From 6f7e055c448ac73068ba6fe38c0ff17fcdeb7e43 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 21:28:14 +0100 Subject: [PATCH 01/48] 85-2-1 --- ld64/APPLE_LICENSE | 367 + ld64/ChangeLog | 542 ++ ld64/FireOpal/APPLE_LICENSE | 367 + ld64/FireOpal/ChangeLog | 542 ++ ld64/FireOpal/doc/man/man1/ld.1 | 676 ++ ld64/FireOpal/doc/man/man1/ld64.1 | 1 + ld64/FireOpal/doc/man/man1/rebase.1 | 39 + ld64/FireOpal/ld64.xcodeproj/project.pbxproj | 788 ++ ld64/FireOpal/src/Architectures.hpp | 88 + ld64/FireOpal/src/ArchiveReader.hpp | 454 + ld64/FireOpal/src/ExecutableFile.h | 70 + ld64/FireOpal/src/FileAbstraction.hpp | 145 + ld64/FireOpal/src/LTOReader.hpp | 684 ++ ld64/FireOpal/src/MachOFileAbstraction.hpp | 925 ++ ld64/FireOpal/src/MachOReaderDylib.hpp | 926 ++ ld64/FireOpal/src/MachOReaderRelocatable.hpp | 4583 +++++++++ ld64/FireOpal/src/MachOWriterExecutable.hpp | 8579 +++++++++++++++++ ld64/FireOpal/src/ObjectDump.cpp | 497 + ld64/FireOpal/src/ObjectFile.h | 349 + ld64/FireOpal/src/OpaqueSection.hpp | 199 + ld64/FireOpal/src/Options.cpp | 3150 ++++++ ld64/FireOpal/src/Options.h | 368 + ld64/FireOpal/src/SectCreate.h | 43 + ld64/FireOpal/src/debugline.c | 546 ++ ld64/FireOpal/src/debugline.h | 109 + ld64/FireOpal/src/dwarf2.h | 85 + ld64/FireOpal/src/ld.cpp | 3778 ++++++++ ld64/FireOpal/src/machochecker.cpp | 965 ++ ld64/FireOpal/src/rebase.cpp | 945 ++ ld64/FireOpal/unit-tests/README | 28 + .../unit-tests/bin/exit-non-zero-pass.pl | 27 + .../unit-tests/bin/fail-if-exit-non-zero.pl | 16 + .../unit-tests/bin/fail-if-exit-zero.pl | 22 + .../unit-tests/bin/fail-if-no-stdin.pl | 22 + ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl | 22 + .../unit-tests/bin/fail-iff-exit-zero.pl | 29 + .../unit-tests/bin/make-recursive-newtest.pl | 127 + .../FireOpal/unit-tests/bin/make-recursive.pl | 123 + ld64/FireOpal/unit-tests/bin/mkld | 73 + .../unit-tests/bin/pass-iff-exit-non-zero.pl | 29 + .../unit-tests/bin/pass-iff-exit-zero.pl | 23 + .../unit-tests/bin/pass-iff-no-stdin.pl | 23 + .../FireOpal/unit-tests/bin/pass-iff-stdin.pl | 24 + ld64/FireOpal/unit-tests/bin/result-filter.pl | 131 + .../unit-tests/bin/rm-stale-test-logs | 36 + ld64/FireOpal/unit-tests/clean-tests | 63 + .../unit-tests/include/common.makefile | 76 + ld64/FireOpal/unit-tests/include/test.h | 35 + ld64/FireOpal/unit-tests/proctor-run | 204 + ld64/FireOpal/unit-tests/run-all-unit-tests | 35 + .../unit-tests/run-all-unit-tests-debug | 26 + ld64/FireOpal/unit-tests/src/Makefile | 9 + .../unit-tests/src/results-to-xml.cpp | 260 + .../unit-tests/src/xmlparser/xmlparser.1 | 79 + .../unit-tests/src/xmlparser/xmlparser.m | 25 + .../xmlparser.xcodeproj/project.pbxproj | 218 + .../src/xmlparser/xmlparser_Prefix.pch | 7 + .../test-cases/16-byte-alignment/Makefile | 44 + .../test-cases/16-byte-alignment/comment.txt | 1 + .../test-cases/16-byte-alignment/tl_test2.c | 43 + .../test-cases/absolute-symbol/Makefile | 40 + .../test-cases/absolute-symbol/abs.s | 3 + .../test-cases/absolute-symbol/main.c | 5 + .../test-cases/alias-command-line/Makefile | 53 + .../test-cases/alias-command-line/aliases.s | 45 + .../test-cases/alias-command-line/aliases.txt | 6 + .../test-cases/alias-objects/Makefile | 44 + .../test-cases/alias-objects/aliases.s | 43 + .../test-cases/align-modulus/Makefile | 40 + .../test-cases/align-modulus/align.s | 36 + .../test-cases/align-modulus/comment.txt | 2 + .../unit-tests/test-cases/align-modulus/foo.c | 32 + .../test-cases/align-modulus/foo.exp | 1 + .../test-cases/allow-stack-execute/Makefile | 46 + .../allow-stack-execute/comment.txt | 1 + .../test-cases/allow-stack-execute/foo.c | 4 + .../test-cases/allowable-client/Makefile | 110 + .../test-cases/allowable-client/bar.c | 6 + .../test-cases/allowable-client/baz.c | 6 + .../test-cases/allowable-client/comment.txt | 1 + .../test-cases/allowable-client/foo.c | 4 + .../test-cases/allowable-client/main.c | 6 + .../test-cases/archive-ObjC/Makefile | 49 + .../unit-tests/test-cases/archive-ObjC/bar.c | 2 + .../unit-tests/test-cases/archive-ObjC/baz.m | 8 + .../unit-tests/test-cases/archive-ObjC/foo.m | 8 + .../unit-tests/test-cases/archive-ObjC/main.c | 31 + .../test-cases/archive-basic/Makefile | 46 + .../unit-tests/test-cases/archive-basic/bar.c | 1 + .../test-cases/archive-basic/comment.txt | 1 + .../unit-tests/test-cases/archive-basic/foo.c | 1 + .../test-cases/archive-basic/main.c | 32 + .../test-cases/archive-duplicate/Makefile | 45 + .../test-cases/archive-duplicate/bar.c | 1 + .../test-cases/archive-duplicate/foo.c | 1 + .../test-cases/archive-duplicate/main.c | 32 + .../test-cases/archive-weak/Makefile | 51 + .../unit-tests/test-cases/archive-weak/bar.c | 1 + .../unit-tests/test-cases/archive-weak/baz.c | 11 + .../test-cases/archive-weak/comment.txt | 7 + .../unit-tests/test-cases/archive-weak/foo.c | 13 + .../unit-tests/test-cases/archive-weak/main.c | 42 + .../unit-tests/test-cases/auto-arch/Makefile | 40 + .../unit-tests/test-cases/auto-arch/hello.c | 29 + .../test-cases/blank-stubs/Makefile | 61 + .../test-cases/blank-stubs/comment.txt | 1 + .../unit-tests/test-cases/blank-stubs/foo.c | 4 + .../unit-tests/test-cases/blank-stubs/main.c | 5 + .../test-cases/branch-islands/Makefile | 47 + .../test-cases/branch-islands/extra.c | 8 + .../test-cases/branch-islands/hello.c | 10 + .../test-cases/branch-islands/space.s | 39 + .../test-cases/bundle_loader/Makefile | 55 + .../unit-tests/test-cases/bundle_loader/bar.c | 31 + .../test-cases/bundle_loader/bundle.c | 31 + .../test-cases/bundle_loader/main.c | 30 + .../test-cases/cfstring-coalesce/Makefile | 52 + .../test-cases/cfstring-coalesce/bar.c | 7 + .../test-cases/cfstring-coalesce/foo.c | 19 + .../test-cases/cfstring-utf16/Makefile | 50 + .../test-cases/cfstring-utf16/bar.m | 7 + .../test-cases/cfstring-utf16/foo.m | 20 + .../test-cases/commons-alignment/Makefile | 37 + .../test-cases/commons-alignment/foo.s | 2 + .../commons-coalesced-dead_strip/Makefile | 42 + .../commons-coalesced-dead_strip/a.c | 4 + .../commons-coalesced-dead_strip/b.c | 4 + .../commons-coalesced-dead_strip/c.c | 3 + .../commons-coalesced-dead_strip/c.h | 4 + .../test-cases/commons-mixed/Makefile | 46 + .../unit-tests/test-cases/commons-mixed/bar.c | 2 + .../unit-tests/test-cases/commons-mixed/foo.c | 2 + .../test-cases/commons-order/Makefile | 40 + .../unit-tests/test-cases/commons-order/bar.c | 3 + .../unit-tests/test-cases/commons-order/baz.c | 3 + .../test-cases/commons-order/expected.order | 8 + .../unit-tests/test-cases/commons-order/foo.c | 3 + .../test-cases/commons-order/main.c | 4 + .../cpu-sub-types-preference/Makefile | 96 + .../test-cases/cpu-sub-types-preference/foo.c | 25 + .../test-cases/cpu-sub-types/Makefile | 157 + .../test-cases/cpu-sub-types/comment.txt | 2 + .../unit-tests/test-cases/cpu-sub-types/foo.c | 3 + .../test-cases/cpu-sub-types/main.c | 10 + .../dead_strip-archive-global/Makefile | 43 + .../dead_strip-archive-global/foo.c | 12 + .../dead_strip-archive-global/main.c | 33 + .../test-cases/dead_strip-archive/Makefile | 43 + .../test-cases/dead_strip-archive/comment.txt | 1 + .../test-cases/dead_strip-archive/foo.c | 7 + .../test-cases/dead_strip-archive/main.c | 37 + .../dead_strip-init-archive/Makefile | 40 + .../test-cases/dead_strip-init-archive/bar.c | 4 + .../test-cases/dead_strip-init-archive/foo.c | 6 + .../unit-tests/test-cases/dead_strip/Makefile | 49 + .../test-cases/dead_strip/comment.txt | 5 + .../test-cases/dead_strip/deadwood.c | 11 + .../unit-tests/test-cases/dead_strip/main.c | 32 + .../unit-tests/test-cases/dead_strip/main.exp | 1 + .../test-cases/dead_strip_dylibs/Makefile | 52 + .../test-cases/dead_strip_dylibs/bar.c | 5 + .../test-cases/dead_strip_dylibs/baz.c | 5 + .../test-cases/dead_strip_dylibs/foo.c | 4 + .../test-cases/dead_strip_dylibs/main.c | 10 + .../dead_strip_section_attribute/Makefile | 40 + .../dead_strip_section_attribute/comment.txt | 2 + .../dead_strip_section_attribute/main.c | 42 + .../dtrace-static-probes-coalescing/Makefile | 59 + .../dtrace-static-probes-coalescing/Number.d | 3 + .../dtrace-static-probes-coalescing/a.cxx | 8 + .../dtrace-static-probes-coalescing/header.h | 11 + .../dtrace-static-probes-coalescing/x.cxx | 6 + .../test-cases/dtrace-static-probes/Makefile | 60 + .../test-cases/dtrace-static-probes/bar.d | 7 + .../dtrace-static-probes/comment.txt | 1 + .../test-cases/dtrace-static-probes/foo.d | 8 + .../test-cases/dtrace-static-probes/main.c | 29 + .../dwarf-archive-all_load/Makefile | 45 + .../test-cases/dwarf-archive-all_load/bar.c | 2 + .../test-cases/dwarf-archive-all_load/baz.c | 1 + .../dwarf-archive-all_load/comment.txt | 2 + .../dwarf-archive-all_load/expected-stabs | 24 + .../test-cases/dwarf-archive-all_load/foo.c | 1 + .../dwarf-archive-all_load/stabs-filter.pl | 25 + .../test-cases/dwarf-debug-notes-r/Makefile | 59 + .../test-cases/dwarf-debug-notes-r/bar.cxx | 4 + .../dwarf-debug-notes-r/comment.txt | 5 + .../dwarf-debug-notes-r/expected-stabs | 24 + .../test-cases/dwarf-debug-notes-r/foo.cxx | 4 + .../test-cases/dwarf-debug-notes-r/main.cxx | 4 + .../dwarf-debug-notes-r/stabs-filter.pl | 25 + .../test-cases/dwarf-debug-notes/Makefile | 50 + .../test-cases/dwarf-debug-notes/comment.txt | 4 + .../dwarf-debug-notes/expected-stabs | 33 + .../test-cases/dwarf-debug-notes/header.h | 8 + .../test-cases/dwarf-debug-notes/hello.cxx | 33 + .../test-cases/dwarf-debug-notes/other.cxx | 27 + .../dwarf-debug-notes/stabs-filter.pl | 25 + .../test-cases/dwarf-ignore/Makefile | 39 + .../test-cases/dwarf-ignore/comment.txt | 1 + .../test-cases/dwarf-ignore/hello.c | 29 + .../test-cases/dwarf-strip/Makefile | 40 + .../test-cases/dwarf-strip/comment.txt | 1 + .../unit-tests/test-cases/dwarf-strip/hello.c | 29 + .../test-cases/dylib-aliases/Makefile | 47 + .../unit-tests/test-cases/dylib-aliases/bar.c | 1 + .../unit-tests/test-cases/dylib-aliases/foo.c | 1 + .../test-cases/dylib-aliases/main.c | 8 + .../test-cases/dylib-re-export-cycle/Makefile | 52 + .../test-cases/dylib-re-export-cycle/bar.c | 1 + .../test-cases/dylib-re-export-cycle/foo.c | 1 + .../test-cases/dylib-re-export-cycle/main.c | 6 + .../test-cases/dylib_file-missing/Makefile | 42 + .../test-cases/dylib_file-missing/bar.c | 13 + .../test-cases/dylib_file-missing/foo.c | 7 + .../test-cases/dylib_file-missing/main.c | 15 + .../unit-tests/test-cases/dylib_file/Makefile | 46 + .../unit-tests/test-cases/dylib_file/bar.c | 13 + .../test-cases/dylib_file/comment.txt | 1 + .../unit-tests/test-cases/dylib_file/foo.c | 7 + .../unit-tests/test-cases/dylib_file/main.c | 15 + .../unit-tests/test-cases/dylib_init/Makefile | 36 + .../unit-tests/test-cases/dylib_init/foo.c | 2 + .../test-cases/eh-coalescing-r/Makefile | 47 + .../test-cases/eh-coalescing-r/bar.cxx | 32 + .../test-cases/eh-coalescing-r/foo.cxx | 32 + .../test-cases/eh-coalescing-r/func.h | 35 + .../test-cases/eh-coalescing/Makefile | 50 + .../test-cases/eh-coalescing/bar.cxx | 31 + .../test-cases/eh-coalescing/foo.cxx | 33 + .../test-cases/eh-coalescing/foo2.cxx | 31 + .../test-cases/eh-coalescing/func.h | 43 + .../test-cases/eh-strip-test/Makefile | 34 + .../test-cases/eh-strip-test/comment.txt | 21 + .../test-cases/eh-strip-test/main.cxx | 6 + .../unit-tests/test-cases/eh_frame/Makefile | 47 + .../unit-tests/test-cases/eh_frame/bar.cxx | 38 + .../unit-tests/test-cases/eh_frame/foo.cxx | 38 + .../test-cases/empty-object/Makefile | 40 + .../unit-tests/test-cases/empty-object/main.c | 1 + .../unit-tests/test-cases/end-label/Makefile | 41 + .../unit-tests/test-cases/end-label/bar.s | 7 + .../unit-tests/test-cases/end-label/foo.s | 11 + .../Makefile | 39 + .../foo.c | 34 + .../exported-symbols-wildcards/Makefile | 78 + .../exported-symbols-wildcards/expect1 | 2 + .../exported-symbols-wildcards/expect2 | 3 + .../exported-symbols-wildcards/expect3 | 4 + .../exported-symbols-wildcards/expect4 | 6 + .../exported-symbols-wildcards/expect5 | 3 + .../exported-symbols-wildcards/expect6 | 4 + .../exported-symbols-wildcards/expect7 | 2 + .../exported-symbols-wildcards/expect8 | 3 + .../exported-symbols-wildcards/foo.c | 55 + .../exported-symbols-wildcards/list5 | 2 + .../exported_symbols_list-eol/Makefile | 41 + .../exported_symbols_list-eol/expected.nm | 2 + .../exported_symbols_list-eol/test.c | 18 + .../exported_symbols_list-eol/test.exp | 1 + .../exported_symbols_list-hidden/Makefile | 41 + .../exported_symbols_list-hidden/test.c | 18 + .../exported_symbols_list-hidden/test.exp | 4 + .../exported_symbols_list-r/Makefile | 55 + .../exported_symbols_list-r/test-bad.exp | 3 + .../test-cases/exported_symbols_list-r/test.c | 18 + .../exported_symbols_list-r/test.exp | 2 + .../external-reloc-sorting/Makefile | 40 + .../test-cases/external-reloc-sorting/foo.c | 5 + .../test-cases/external-reloc-sorting/main.c | 39 + .../unit-tests/test-cases/filelist/Makefile | 47 + .../test-cases/filelist/comment.txt | 1 + .../unit-tests/test-cases/filelist/hello.c | 29 + .../unit-tests/test-cases/flat-dylib/Makefile | 40 + .../unit-tests/test-cases/flat-dylib/main.c | 33 + .../flat-indirect-undefines/Makefile | 49 + .../test-cases/flat-indirect-undefines/bar.c | 4 + .../test-cases/flat-indirect-undefines/foo.c | 8 + .../test-cases/flat-indirect-undefines/main.c | 10 + .../unit-tests/test-cases/flat-main/Makefile | 40 + .../unit-tests/test-cases/flat-main/main.c | 33 + .../test-cases/got-elimination/Makefile | 50 + .../test-cases/got-elimination/bar.c | 28 + .../test-cases/got-elimination/foo.c | 42 + .../unit-tests/test-cases/header-pad/Makefile | 38 + .../test-cases/header-pad/comment.txt | 1 + .../unit-tests/test-cases/header-pad/hello.c | 29 + .../test-cases/hello-world/Makefile | 38 + .../test-cases/hello-world/comment.txt | 1 + .../unit-tests/test-cases/hello-world/hello.c | 29 + .../implicit-common2/Makefile.newtest | 47 + .../test-cases/implicit-common2/a.c | 7 + .../test-cases/implicit-common2/comment.txt | 1 + .../test-cases/implicit-common2/test.c | 26 + .../test-cases/implicit-common3/Makefile | 44 + .../test-cases/implicit-common3/a.c | 8 + .../test-cases/implicit-common3/comment.txt | 1 + .../test-cases/implicit-common3/test.c | 37 + .../implicit-common4/Makefile.newtest | 45 + .../test-cases/implicit-common4/a.c | 7 + .../test-cases/implicit-common4/comment.txt | 1 + .../test-cases/implicit-common4/test.c | 26 + .../implicit-common5/Makefile.newtest | 41 + .../test-cases/implicit-common5/a.c | 7 + .../test-cases/implicit-common5/comment.txt | 1 + .../test-cases/implicit-common5/test.c | 25 + .../test-cases/implicit_dylib/Makefile | 48 + .../test-cases/implicit_dylib/bar.c | 7 + .../test-cases/implicit_dylib/foo.c | 5 + .../test-cases/implicit_dylib/main.c | 11 + .../test-cases/indirect-dylib/Makefile | 46 + .../test-cases/indirect-dylib/bar.c | 31 + .../test-cases/indirect-dylib/comment.txt | 4 + .../test-cases/indirect-dylib/foo.c | 31 + .../test-cases/indirect-dylib/main.c | 33 + .../test-cases/indirect-path-search/Makefile | 106 + .../test-cases/indirect-path-search/bar.c | 5 + .../test-cases/indirect-path-search/baz.c | 5 + .../test-cases/indirect-path-search/foo.c | 4 + .../test-cases/indirect-path-search/main.c | 8 + .../test-cases/interposable_list/Makefile | 47 + .../test-cases/interposable_list/test.c | 57 + .../test-cases/interposable_list/test.exp | 2 + .../unit-tests/test-cases/large-data/Makefile | 50 + .../unit-tests/test-cases/large-data/test1.c | 42 + .../unit-tests/test-cases/large-data/test2.c | 37 + .../unit-tests/test-cases/large-data/test3.c | 37 + .../unit-tests/test-cases/large-data/test4.c | 37 + .../test-cases/late-link-error/Makefile | 41 + .../test-cases/late-link-error/comment.txt | 2 + .../test-cases/late-link-error/link_error.s | 22 + .../test-cases/lazy-dylib-objc/Makefile | 45 + .../test-cases/lazy-dylib-objc/foo.h | 9 + .../test-cases/lazy-dylib-objc/foo.m | 8 + .../test-cases/lazy-dylib-objc/main.m | 12 + .../unit-tests/test-cases/lazy-dylib/Makefile | 46 + .../unit-tests/test-cases/lazy-dylib/bad.c | 12 + .../unit-tests/test-cases/lazy-dylib/bad2.c | 13 + .../unit-tests/test-cases/lazy-dylib/foo.c | 5 + .../unit-tests/test-cases/lazy-dylib/main.c | 18 + .../literals-coalesce-alignment/Makefile | 46 + .../cstring-align-0.s | 26 + .../cstring-align-3.s | 26 + .../literals-coalesce-alignment2/Makefile | 47 + .../literals-coalesce-alignment2/comment.txt | 1 + .../cstring-align-0.s | 27 + .../cstring-align-3.s | 28 + .../literals-coalesce-alignment3/Makefile | 48 + .../literals-coalesce-alignment3/comment.txt | 1 + .../cstring-align-0.s | 27 + .../cstring-align-3.s | 28 + .../test-cases/literals-coalesce/Makefile | 40 + .../test-cases/literals-coalesce/literals.s | 69 + .../literals-coalesce2/Makefile.newtest | 40 + .../test-cases/literals-coalesce2/comment.txt | 1 + .../test-cases/literals-coalesce2/literals.s | 48 + .../test-cases/literals-coalesce2/test.sh | 5 + .../test-cases/llvm-integration/Makefile | 289 + .../test-cases/llvm-integration/a.c | 5 + .../test-cases/llvm-integration/a1.c | 10 + .../test-cases/llvm-integration/a10.c | 5 + .../test-cases/llvm-integration/a11.c | 6 + .../test-cases/llvm-integration/a12.c | 8 + .../test-cases/llvm-integration/a12.h | 8 + .../test-cases/llvm-integration/a13.cc | 3 + .../test-cases/llvm-integration/a13.h | 7 + .../test-cases/llvm-integration/a14.c | 1 + .../test-cases/llvm-integration/a15.c | 3 + .../test-cases/llvm-integration/a17.c | 4 + .../test-cases/llvm-integration/a18.c | 18 + .../test-cases/llvm-integration/a2.c | 6 + .../test-cases/llvm-integration/a20.c | 2 + .../test-cases/llvm-integration/a3.c | 6 + .../test-cases/llvm-integration/a4.c | 6 + .../test-cases/llvm-integration/a5.c | 10 + .../test-cases/llvm-integration/a6.c | 10 + .../test-cases/llvm-integration/a7.c | 11 + .../test-cases/llvm-integration/a8.c | 23 + .../test-cases/llvm-integration/a9.c | 25 + .../test-cases/llvm-integration/a9.list | 3 + .../test-cases/llvm-integration/b.c | 3 + .../test-cases/llvm-integration/b1.c | 4 + .../test-cases/llvm-integration/b10.c | 7 + .../test-cases/llvm-integration/b10.h | 6 + .../test-cases/llvm-integration/b14.c | 7 + .../test-cases/llvm-integration/b15.c | 8 + .../test-cases/llvm-integration/b17.c | 4 + .../test-cases/llvm-integration/b2.c | 9 + .../test-cases/llvm-integration/b20.c | 1 + .../test-cases/llvm-integration/b3.c | 4 + .../test-cases/llvm-integration/b4.c | 13 + .../test-cases/llvm-integration/b5.c | 4 + .../test-cases/llvm-integration/b7.c | 7 + .../test-cases/llvm-integration/c15.c | 9 + .../test-cases/llvm-integration/main.c | 9 + .../test-cases/llvm-integration/main1.c | 13 + .../test-cases/llvm-integration/main10.c | 10 + .../test-cases/llvm-integration/main11.c | 7 + .../test-cases/llvm-integration/main12.c | 7 + .../test-cases/llvm-integration/main13.cc | 8 + .../test-cases/llvm-integration/main16.c | 8 + .../test-cases/llvm-integration/main19.c | 8 + .../test-cases/llvm-integration/main2.c | 9 + .../test-cases/llvm-integration/main20.c | 7 + .../test-cases/llvm-integration/main3.c | 13 + .../test-cases/llvm-integration/main4.c | 9 + .../test-cases/llvm-integration/main5.c | 16 + .../test-cases/llvm-integration/main6.c | 10 + .../test-cases/llvm-integration/main7.c | 10 + .../test-cases/llvm-integration/main8.c | 11 + .../test-cases/llvm-integration/main9.c | 14 + .../test-cases/loader_path/Makefile | 46 + .../unit-tests/test-cases/loader_path/bar.c | 6 + .../unit-tests/test-cases/loader_path/foo.c | 7 + .../unit-tests/test-cases/loader_path/main.c | 8 + .../local-symbol-partial-stripping/Makefile | 75 + .../local-symbol-partial-stripping/a.expect | 2 + .../local-symbol-partial-stripping/a.list | 2 + .../local-symbol-partial-stripping/b.expect | 3 + .../local-symbol-partial-stripping/b.list | 2 + .../local-symbol-partial-stripping/c.list | 1 + .../local-symbol-partial-stripping/foo.c | 11 + .../local-symbol-partial-stripping/main.c | 11 + .../test-cases/lto-llvm-options/Makefile | 45 + .../test-cases/lto-llvm-options/main.c | 15 + .../lto-weak-native-override/Makefile | 45 + .../test-cases/lto-weak-native-override/foo.c | 6 + .../lto-weak-native-override/main.c | 17 + .../test-cases/main-stripped/Makefile | 38 + .../test-cases/main-stripped/main.c | 34 + .../test-cases/main-stripped/main.exp | 1 + .../test-cases/missing-option-args/Makefile | 98 + .../missing-option-args/comment.txt | 1 + .../test-cases/multiple-entry-points/Makefile | 46 + .../multiple-entry-points/comment.txt | 3 + .../test-cases/multiple-entry-points/test.s | 54 + .../no-dynamic-common/Makefile.newtest | 39 + .../test-cases/no-dynamic-common/a.c | 7 + .../test-cases/no-dynamic-common/comment.txt | 1 + .../test-cases/no-dynamic-common/test.c | 25 + .../unit-tests/test-cases/no-uuid/Makefile | 63 + .../unit-tests/test-cases/no-uuid/bar.c | 4 + .../unit-tests/test-cases/no-uuid/comment.txt | 1 + .../unit-tests/test-cases/no-uuid/foo.c | 4 + .../unit-tests/test-cases/non-lazy-r/Makefile | 61 + .../unit-tests/test-cases/non-lazy-r/foo.c | 12 + .../unit-tests/test-cases/non-lazy-r/other.c | 2 + .../objc-category-debug-notes/Makefile | 40 + .../objc-category-debug-notes/test.m | 44 + .../objc-exported_symbols_list/Makefile | 40 + .../objc-exported_symbols_list/foo.exp | 1 + .../objc-exported_symbols_list/foo.m | 18 + .../test-cases/objc-gc-checks/Makefile | 84 + .../test-cases/objc-gc-checks/bar.m | 11 + .../test-cases/objc-gc-checks/comment.txt | 1 + .../test-cases/objc-gc-checks/foo.m | 12 + .../test-cases/objc-gc-checks/runtime.c | 2 + .../test-cases/objc-literal-pointers/Makefile | 47 + .../test-cases/objc-literal-pointers/test.m | 33 + .../test-cases/objc-references/Makefile | 47 + .../test-cases/objc-references/comment.txt | 1 + .../test-cases/objc-references/test.m | 52 + .../objc-selector-coalescing/Makefile | 39 + .../objc-selector-coalescing/main.m | 7 + .../objc-selector-coalescing/other.m | 10 + .../test-cases/operator-new/Makefile | 40 + .../test-cases/operator-new/main.cxx | 47 + .../test-cases/order_file-ans/Makefile | 40 + .../test-cases/order_file-ans/main.cxx | 62 + .../test-cases/order_file-ans/main.expected | 4 + .../test-cases/order_file-ans/main.order | 4 + .../unit-tests/test-cases/order_file/Makefile | 57 + .../unit-tests/test-cases/order_file/extra.s | 24 + .../unit-tests/test-cases/order_file/main.c | 33 + .../test-cases/order_file/main1.expected | 4 + .../test-cases/order_file/main1.order | 4 + .../test-cases/order_file/main2.expected | 11 + .../test-cases/order_file/main2.order | 6 + .../test-cases/order_file/main3.expected | 4 + .../test-cases/order_file/main3.order | 8 + .../test-cases/prebound-main/Makefile | 51 + .../test-cases/prebound-main/main.c | 3 + .../test-cases/prebound-split-seg/Makefile | 39 + .../prebound-split-seg/address_table | 4 + .../test-cases/prebound-split-seg/bar.c | 36 + .../test-cases/private-non-lazy/Makefile | 54 + .../test-cases/private-non-lazy/bar.c | 3 + .../test-cases/private-non-lazy/comment.txt | 1 + .../test-cases/private-non-lazy/foo.c | 7 + .../test-cases/private-non-lazy/hello.c | 31 + .../test-cases/re-export-cases/Makefile | 167 + .../test-cases/re-export-cases/bar.c | 5 + .../test-cases/re-export-cases/baz.c | 5 + .../test-cases/re-export-cases/foo.c | 4 + .../test-cases/re-export-flag/Makefile | 48 + .../test-cases/re-export-flag/bar.c | 5 + .../test-cases/re-export-flag/foo.c | 4 + .../re-export-optimizations/Makefile | 65 + .../test-cases/re-export-optimizations/bar.c | 5 + .../test-cases/re-export-optimizations/foo.c | 4 + .../test-cases/re-export-optimizations/main.c | 10 + .../re-export-relative-paths/Makefile | 49 + .../test-cases/re-export-relative-paths/bar.c | 5 + .../test-cases/re-export-relative-paths/foo.c | 4 + .../re-export-relative-paths/main.c | 11 + .../re-export-relative-paths/wrap.c | 2 + .../test-cases/read-only-relocs/Makefile | 63 + .../test-cases/read-only-relocs/foo.c | 6 + .../test-cases/read-only-relocs/test.c | 34 + .../test-cases/rebase-basic/Makefile | 53 + .../unit-tests/test-cases/rebase-basic/bar.m | 13 + .../test-cases/rebase-basic/comment.txt | 1 + .../unit-tests/test-cases/rebase-basic/foo.c | 14 + .../unit-tests/test-cases/relocs-asm/Makefile | 46 + .../test-cases/relocs-asm/comment.txt | 3 + .../test-cases/relocs-asm/relocs-asm.s | 471 + .../unit-tests/test-cases/relocs-c/Makefile | 57 + .../unit-tests/test-cases/relocs-c/test.c | 76 + .../unit-tests/test-cases/relocs-c2/Makefile | 58 + .../test-cases/relocs-c2/comment.txt | 5 + .../unit-tests/test-cases/relocs-c2/test.c | 76 + .../test-cases/relocs-literals/Makefile | 47 + .../test-cases/relocs-literals/test.c | 54 + .../test-cases/relocs-literals2/Makefile | 50 + .../test-cases/relocs-literals2/test.c | 54 + .../test-cases/relocs-literals3/Makefile | 47 + .../test-cases/relocs-literals3/comment.txt | 3 + .../test-cases/relocs-literals3/test.c | 47 + .../test-cases/relocs-objc/Makefile | 47 + .../test-cases/relocs-objc/comment.txt | 3 + .../unit-tests/test-cases/relocs-objc/test.m | 59 + .../test-cases/segment-order/Makefile | 37 + .../test-cases/segment-order/expected.order | 3 + .../test-cases/segment-order/main.c | 4 + .../test-cases/segment-order/segJJJ.s | 7 + .../test-cases/segment-order/segKKK.s | 7 + .../test-cases/segment-order/segLLL.s | 7 + .../test-cases/slow-x86-stubs/Makefile | 42 + .../test-cases/slow-x86-stubs/hello.c | 7 + .../test-cases/special-labels/Makefile | 41 + .../test-cases/special-labels/extra.s | 9 + .../test-cases/special-labels/main.c | 29 + .../test-cases/stabs-coalesce/Makefile | 52 + .../test-cases/stabs-coalesce/comment.txt | 3 + .../test-cases/stabs-coalesce/header.h | 31 + .../test-cases/stabs-coalesce/hello.cxx | 33 + .../test-cases/stabs-coalesce/other.cxx | 41 + .../test-cases/stabs-directory-slash/Makefile | 39 + .../test-cases/stabs-directory-slash/main.c | 3 + .../stack_addr_no_size/Makefile.newtest | 77 + .../test-cases/stack_addr_no_size/comment.txt | 11 + .../test-cases/stack_addr_no_size/main.c | 29 + .../test-cases/stack_addr_size/Makefile | 57 + .../test-cases/stack_addr_size/comment.txt | 11 + .../test-cases/stack_addr_size/main.c | 29 + .../test-cases/stack_size_no_addr/Makefile | 56 + .../test-cases/stack_size_no_addr/comment.txt | 11 + .../test-cases/stack_size_no_addr/main.c | 37 + .../test-cases/static-executable/Makefile | 35 + .../test-cases/static-executable/test.c | 11 + .../test-cases/static-strip/Makefile.newtest | 40 + .../test-cases/static-strip/comment.txt | 1 + .../unit-tests/test-cases/static-strip/test.c | 28 + .../test-cases/strip-test2/Makefile | 70 + .../test-cases/strip-test2/comment.txt | 21 + .../test-cases/strip-test2/main.cxx | 6 + .../test-cases/strip-test3/Makefile.newtest | 71 + .../test-cases/strip-test3/comment.txt | 21 + .../test-cases/strip-test3/main.cxx | 6 + .../test-cases/strip_local/Makefile | 53 + .../unit-tests/test-cases/strip_local/foo.c | 8 + .../unit-tests/test-cases/strip_local/hello.c | 33 + .../stripped-indirect-symbol-table/Makefile | 57 + .../stripped-indirect-symbol-table/a.c | 7 + .../stripped-indirect-symbol-table/b.c | 12 + .../stripped-indirect-symbol-table/c.c | 11 + .../stripped-indirect-symbol-table/func.c | 1 + .../stripped-indirect-symbol-table/strip.list | 2 + .../test-cases/stub-generation-weak/Makefile | 42 + .../test-cases/stub-generation-weak/foo.c | 5 + .../test-cases/stub-generation-weak/main.c | 40 + .../test-cases/stub-generation/Makefile | 41 + .../test-cases/stub-generation/test.c | 40 + .../test-cases/switch-jump-table/Makefile | 56 + .../switch-jump-table/interpose.exp | 2 + .../test-cases/switch-jump-table/main.c | 4 + .../test-cases/switch-jump-table/switch.s | 49 + .../test-cases/symbol-moving/Makefile | 93 + .../unit-tests/test-cases/symbol-moving/aaa.c | 3 + .../test-cases/symbol-moving/anotb.c | 26 + .../unit-tests/test-cases/symbol-moving/bar.c | 1 + .../unit-tests/test-cases/symbol-moving/bbb.c | 1 + .../test-cases/symbol-moving/bnota.c | 25 + .../unit-tests/test-cases/symbol-moving/foo.c | 3 + .../test-cases/symbol-moving/main.c | 17 + .../test-cases/tentative-and-archive/Makefile | 50 + .../test-cases/tentative-and-archive/foo.c | 6 + .../test-cases/tentative-and-archive/main.c | 8 + .../test-cases/tentative-and-dylib/Makefile | 56 + .../test-cases/tentative-and-dylib/bar.c | 1 + .../test-cases/tentative-and-dylib/foo.c | 2 + .../test-cases/tentative-and-dylib/main.c | 8 + .../tentative-to-real-hidden/Makefile | 52 + .../tentative-to-real-hidden/test.c | 11 + .../test-cases/tentative-to-real/Makefile | 45 + .../test-cases/tentative-to-real/comment.txt | 1 + .../test-cases/tentative-to-real/test.c | 3 + .../unit-tests/test-cases/thumb-blx/Makefile | 54 + .../unit-tests/test-cases/thumb-blx/test.c | 36 + .../undefined-dynamic-lookup/Makefile | 47 + .../undefined-dynamic-lookup/main.c | 32 + .../Makefile | 46 + .../visibility-warning-dylib-v-archive/bar.c | 11 + .../visibility-warning-dylib-v-archive/foo.c | 5 + .../visibility-warning-dylib-v-archive/main.c | 11 + .../test-cases/visibility-warning/Makefile | 57 + .../test-cases/visibility-warning/foo.c | 5 + .../visibility-warning/foo_hidden.c | 5 + .../test-cases/visibility-warning/foo_weak.c | 5 + .../visibility-warning/foo_weak_hidden.c | 5 + .../test-cases/weak-def-ordinal/Makefile | 50 + .../test-cases/weak-def-ordinal/bar.c | 6 + .../test-cases/weak-def-ordinal/foo.c | 11 + .../test-cases/weak-def-ordinal/main.c | 35 + .../unit-tests/test-cases/weak_dylib/Makefile | 49 + .../unit-tests/test-cases/weak_dylib/bar.c | 9 + .../unit-tests/test-cases/weak_dylib/bar.h | 9 + .../unit-tests/test-cases/weak_dylib/foo.c | 9 + .../unit-tests/test-cases/weak_dylib/foo.h | 6 + .../unit-tests/test-cases/weak_dylib/main.c | 22 + .../test-cases/weak_import/Makefile | 62 + .../unit-tests/test-cases/weak_import/foo.c | 17 + .../unit-tests/test-cases/weak_import/foo.h | 16 + .../unit-tests/test-cases/weak_import/main.c | 20 + .../test-cases/weak_import2/Makefile.newtest | 58 + .../test-cases/weak_import2/comment.txt | 1 + .../unit-tests/test-cases/weak_import2/foo.c | 17 + .../unit-tests/test-cases/weak_import2/foo.h | 16 + .../unit-tests/test-cases/weak_import2/foo1.c | 10 + .../unit-tests/test-cases/weak_import2/main.c | 20 + .../test-cases/weak_import3/Makefile | 43 + .../test-cases/weak_import3/comment.txt | 1 + .../unit-tests/test-cases/weak_import3/foo.c | 17 + .../unit-tests/test-cases/weak_import3/foo.h | 16 + .../unit-tests/test-cases/weak_import3/foo1.c | 4 + .../unit-tests/test-cases/weak_import3/main.c | 20 + .../unit-tests/test-cases/why_live/Makefile | 44 + .../unit-tests/test-cases/why_live/bar.c | 1 + .../unit-tests/test-cases/why_live/foo.c | 12 + .../unit-tests/test-cases/why_live/main.c | 7 + .../unit-tests/test-cases/zero-fill/Makefile | 38 + .../unit-tests/test-cases/zero-fill/test.c | 51 + .../unit-tests/test-cases/zero-fill2/Makefile | 38 + .../test-cases/zero-fill2/comment.txt | 1 + .../unit-tests/test-cases/zero-fill2/test.c | 58 + .../unit-tests/test-cases/zero-fill3/Makefile | 50 + .../test-cases/zero-fill3/comment.txt | 1 + .../unit-tests/test-cases/zero-fill3/test.c | 63 + ld64/doc/man/man1/ld.1 | 676 ++ ld64/doc/man/man1/ld64.1 | 1 + ld64/doc/man/man1/rebase.1 | 39 + ld64/ld64.xcodeproj/project.pbxproj | 788 ++ ld64/src/Architectures.hpp | 88 + ld64/src/ArchiveReader.hpp | 454 + ld64/src/ExecutableFile.h | 70 + ld64/src/FileAbstraction.hpp | 145 + ld64/src/LTOReader.hpp | 684 ++ ld64/src/MachOFileAbstraction.hpp | 925 ++ ld64/src/MachOReaderDylib.hpp | 926 ++ ld64/src/MachOReaderRelocatable.hpp | 4583 +++++++++ ld64/src/MachOWriterExecutable.hpp | 8579 +++++++++++++++++ ld64/src/ObjectDump.cpp | 497 + ld64/src/ObjectFile.h | 349 + ld64/src/OpaqueSection.hpp | 199 + ld64/src/Options.cpp | 3150 ++++++ ld64/src/Options.h | 368 + ld64/src/SectCreate.h | 43 + ld64/src/debugline.c | 546 ++ ld64/src/debugline.h | 109 + ld64/src/dwarf2.h | 85 + ld64/src/ld.cpp | 3778 ++++++++ ld64/src/machochecker.cpp | 965 ++ ld64/src/rebase.cpp | 945 ++ ld64/unit-tests/README | 28 + ld64/unit-tests/bin/exit-non-zero-pass.pl | 27 + ld64/unit-tests/bin/fail-if-exit-non-zero.pl | 16 + ld64/unit-tests/bin/fail-if-exit-zero.pl | 22 + ld64/unit-tests/bin/fail-if-no-stdin.pl | 22 + ld64/unit-tests/bin/fail-if-stdin.pl | 22 + ld64/unit-tests/bin/fail-iff-exit-zero.pl | 29 + ld64/unit-tests/bin/make-recursive-newtest.pl | 127 + ld64/unit-tests/bin/make-recursive.pl | 123 + ld64/unit-tests/bin/mkld | 73 + ld64/unit-tests/bin/pass-iff-exit-non-zero.pl | 29 + ld64/unit-tests/bin/pass-iff-exit-zero.pl | 23 + ld64/unit-tests/bin/pass-iff-no-stdin.pl | 23 + ld64/unit-tests/bin/pass-iff-stdin.pl | 24 + ld64/unit-tests/bin/result-filter.pl | 131 + ld64/unit-tests/bin/rm-stale-test-logs | 36 + ld64/unit-tests/clean-tests | 63 + ld64/unit-tests/include/common.makefile | 76 + ld64/unit-tests/include/test.h | 35 + ld64/unit-tests/proctor-run | 204 + ld64/unit-tests/run-all-unit-tests | 35 + ld64/unit-tests/run-all-unit-tests-debug | 26 + ld64/unit-tests/src/Makefile | 9 + ld64/unit-tests/src/results-to-xml.cpp | 260 + ld64/unit-tests/src/xmlparser/xmlparser.1 | 79 + ld64/unit-tests/src/xmlparser/xmlparser.m | 25 + .../xmlparser.xcodeproj/project.pbxproj | 218 + .../src/xmlparser/xmlparser_Prefix.pch | 7 + .../test-cases/16-byte-alignment/Makefile | 44 + .../test-cases/16-byte-alignment/comment.txt | 1 + .../test-cases/16-byte-alignment/tl_test2.c | 43 + .../test-cases/absolute-symbol/Makefile | 40 + .../test-cases/absolute-symbol/abs.s | 3 + .../test-cases/absolute-symbol/main.c | 5 + .../test-cases/alias-command-line/Makefile | 53 + .../test-cases/alias-command-line/aliases.s | 45 + .../test-cases/alias-command-line/aliases.txt | 6 + .../test-cases/alias-objects/Makefile | 44 + .../test-cases/alias-objects/aliases.s | 43 + .../test-cases/align-modulus/Makefile | 40 + .../test-cases/align-modulus/align.s | 36 + .../test-cases/align-modulus/comment.txt | 2 + .../unit-tests/test-cases/align-modulus/foo.c | 32 + .../test-cases/align-modulus/foo.exp | 1 + .../test-cases/allow-stack-execute/Makefile | 46 + .../allow-stack-execute/comment.txt | 1 + .../test-cases/allow-stack-execute/foo.c | 4 + .../test-cases/allowable-client/Makefile | 110 + .../test-cases/allowable-client/bar.c | 6 + .../test-cases/allowable-client/baz.c | 6 + .../test-cases/allowable-client/comment.txt | 1 + .../test-cases/allowable-client/foo.c | 4 + .../test-cases/allowable-client/main.c | 6 + .../test-cases/archive-ObjC/Makefile | 49 + ld64/unit-tests/test-cases/archive-ObjC/bar.c | 2 + ld64/unit-tests/test-cases/archive-ObjC/baz.m | 8 + ld64/unit-tests/test-cases/archive-ObjC/foo.m | 8 + .../unit-tests/test-cases/archive-ObjC/main.c | 31 + .../test-cases/archive-basic/Makefile | 46 + .../unit-tests/test-cases/archive-basic/bar.c | 1 + .../test-cases/archive-basic/comment.txt | 1 + .../unit-tests/test-cases/archive-basic/foo.c | 1 + .../test-cases/archive-basic/main.c | 32 + .../test-cases/archive-duplicate/Makefile | 45 + .../test-cases/archive-duplicate/bar.c | 1 + .../test-cases/archive-duplicate/foo.c | 1 + .../test-cases/archive-duplicate/main.c | 32 + .../test-cases/archive-weak/Makefile | 51 + ld64/unit-tests/test-cases/archive-weak/bar.c | 1 + ld64/unit-tests/test-cases/archive-weak/baz.c | 11 + .../test-cases/archive-weak/comment.txt | 7 + ld64/unit-tests/test-cases/archive-weak/foo.c | 13 + .../unit-tests/test-cases/archive-weak/main.c | 42 + ld64/unit-tests/test-cases/auto-arch/Makefile | 40 + ld64/unit-tests/test-cases/auto-arch/hello.c | 29 + .../test-cases/blank-stubs/Makefile | 61 + .../test-cases/blank-stubs/comment.txt | 1 + ld64/unit-tests/test-cases/blank-stubs/foo.c | 4 + ld64/unit-tests/test-cases/blank-stubs/main.c | 5 + .../test-cases/branch-islands/Makefile | 47 + .../test-cases/branch-islands/extra.c | 8 + .../test-cases/branch-islands/hello.c | 10 + .../test-cases/branch-islands/space.s | 39 + .../test-cases/bundle_loader/Makefile | 55 + .../unit-tests/test-cases/bundle_loader/bar.c | 31 + .../test-cases/bundle_loader/bundle.c | 31 + .../test-cases/bundle_loader/main.c | 30 + .../test-cases/cfstring-coalesce/Makefile | 52 + .../test-cases/cfstring-coalesce/bar.c | 7 + .../test-cases/cfstring-coalesce/foo.c | 19 + .../test-cases/cfstring-utf16/Makefile | 50 + .../test-cases/cfstring-utf16/bar.m | 7 + .../test-cases/cfstring-utf16/foo.m | 20 + .../test-cases/commons-alignment/Makefile | 37 + .../test-cases/commons-alignment/foo.s | 2 + .../commons-coalesced-dead_strip/Makefile | 42 + .../commons-coalesced-dead_strip/a.c | 4 + .../commons-coalesced-dead_strip/b.c | 4 + .../commons-coalesced-dead_strip/c.c | 3 + .../commons-coalesced-dead_strip/c.h | 4 + .../test-cases/commons-mixed/Makefile | 46 + .../unit-tests/test-cases/commons-mixed/bar.c | 2 + .../unit-tests/test-cases/commons-mixed/foo.c | 2 + .../test-cases/commons-order/Makefile | 40 + .../unit-tests/test-cases/commons-order/bar.c | 3 + .../unit-tests/test-cases/commons-order/baz.c | 3 + .../test-cases/commons-order/expected.order | 8 + .../unit-tests/test-cases/commons-order/foo.c | 3 + .../test-cases/commons-order/main.c | 4 + .../cpu-sub-types-preference/Makefile | 96 + .../test-cases/cpu-sub-types-preference/foo.c | 25 + .../test-cases/cpu-sub-types/Makefile | 157 + .../test-cases/cpu-sub-types/comment.txt | 2 + .../unit-tests/test-cases/cpu-sub-types/foo.c | 3 + .../test-cases/cpu-sub-types/main.c | 10 + .../dead_strip-archive-global/Makefile | 43 + .../dead_strip-archive-global/foo.c | 12 + .../dead_strip-archive-global/main.c | 33 + .../test-cases/dead_strip-archive/Makefile | 43 + .../test-cases/dead_strip-archive/comment.txt | 1 + .../test-cases/dead_strip-archive/foo.c | 7 + .../test-cases/dead_strip-archive/main.c | 37 + .../dead_strip-init-archive/Makefile | 40 + .../test-cases/dead_strip-init-archive/bar.c | 4 + .../test-cases/dead_strip-init-archive/foo.c | 6 + .../unit-tests/test-cases/dead_strip/Makefile | 49 + .../test-cases/dead_strip/comment.txt | 5 + .../test-cases/dead_strip/deadwood.c | 11 + ld64/unit-tests/test-cases/dead_strip/main.c | 32 + .../unit-tests/test-cases/dead_strip/main.exp | 1 + .../test-cases/dead_strip_dylibs/Makefile | 52 + .../test-cases/dead_strip_dylibs/bar.c | 5 + .../test-cases/dead_strip_dylibs/baz.c | 5 + .../test-cases/dead_strip_dylibs/foo.c | 4 + .../test-cases/dead_strip_dylibs/main.c | 10 + .../dead_strip_section_attribute/Makefile | 40 + .../dead_strip_section_attribute/comment.txt | 2 + .../dead_strip_section_attribute/main.c | 42 + .../dtrace-static-probes-coalescing/Makefile | 59 + .../dtrace-static-probes-coalescing/Number.d | 3 + .../dtrace-static-probes-coalescing/a.cxx | 8 + .../dtrace-static-probes-coalescing/header.h | 11 + .../dtrace-static-probes-coalescing/x.cxx | 6 + .../test-cases/dtrace-static-probes/Makefile | 60 + .../test-cases/dtrace-static-probes/bar.d | 7 + .../dtrace-static-probes/comment.txt | 1 + .../test-cases/dtrace-static-probes/foo.d | 8 + .../test-cases/dtrace-static-probes/main.c | 29 + .../dwarf-archive-all_load/Makefile | 45 + .../test-cases/dwarf-archive-all_load/bar.c | 2 + .../test-cases/dwarf-archive-all_load/baz.c | 1 + .../dwarf-archive-all_load/comment.txt | 2 + .../dwarf-archive-all_load/expected-stabs | 24 + .../test-cases/dwarf-archive-all_load/foo.c | 1 + .../dwarf-archive-all_load/stabs-filter.pl | 25 + .../test-cases/dwarf-debug-notes-r/Makefile | 59 + .../test-cases/dwarf-debug-notes-r/bar.cxx | 4 + .../dwarf-debug-notes-r/comment.txt | 5 + .../dwarf-debug-notes-r/expected-stabs | 24 + .../test-cases/dwarf-debug-notes-r/foo.cxx | 4 + .../test-cases/dwarf-debug-notes-r/main.cxx | 4 + .../dwarf-debug-notes-r/stabs-filter.pl | 25 + .../test-cases/dwarf-debug-notes/Makefile | 50 + .../test-cases/dwarf-debug-notes/comment.txt | 4 + .../dwarf-debug-notes/expected-stabs | 33 + .../test-cases/dwarf-debug-notes/header.h | 8 + .../test-cases/dwarf-debug-notes/hello.cxx | 33 + .../test-cases/dwarf-debug-notes/other.cxx | 27 + .../dwarf-debug-notes/stabs-filter.pl | 25 + .../test-cases/dwarf-ignore/Makefile | 39 + .../test-cases/dwarf-ignore/comment.txt | 1 + .../test-cases/dwarf-ignore/hello.c | 29 + .../test-cases/dwarf-strip/Makefile | 40 + .../test-cases/dwarf-strip/comment.txt | 1 + .../unit-tests/test-cases/dwarf-strip/hello.c | 29 + .../test-cases/dylib-aliases/Makefile | 47 + .../unit-tests/test-cases/dylib-aliases/bar.c | 1 + .../unit-tests/test-cases/dylib-aliases/foo.c | 1 + .../test-cases/dylib-aliases/main.c | 8 + .../test-cases/dylib-re-export-cycle/Makefile | 52 + .../test-cases/dylib-re-export-cycle/bar.c | 1 + .../test-cases/dylib-re-export-cycle/foo.c | 1 + .../test-cases/dylib-re-export-cycle/main.c | 6 + .../test-cases/dylib_file-missing/Makefile | 42 + .../test-cases/dylib_file-missing/bar.c | 13 + .../test-cases/dylib_file-missing/foo.c | 7 + .../test-cases/dylib_file-missing/main.c | 15 + .../unit-tests/test-cases/dylib_file/Makefile | 46 + ld64/unit-tests/test-cases/dylib_file/bar.c | 13 + .../test-cases/dylib_file/comment.txt | 1 + ld64/unit-tests/test-cases/dylib_file/foo.c | 7 + ld64/unit-tests/test-cases/dylib_file/main.c | 15 + .../unit-tests/test-cases/dylib_init/Makefile | 36 + ld64/unit-tests/test-cases/dylib_init/foo.c | 2 + .../test-cases/eh-coalescing-r/Makefile | 47 + .../test-cases/eh-coalescing-r/bar.cxx | 32 + .../test-cases/eh-coalescing-r/foo.cxx | 32 + .../test-cases/eh-coalescing-r/func.h | 35 + .../test-cases/eh-coalescing/Makefile | 50 + .../test-cases/eh-coalescing/bar.cxx | 31 + .../test-cases/eh-coalescing/foo.cxx | 33 + .../test-cases/eh-coalescing/foo2.cxx | 31 + .../test-cases/eh-coalescing/func.h | 43 + .../test-cases/eh-strip-test/Makefile | 34 + .../test-cases/eh-strip-test/comment.txt | 21 + .../test-cases/eh-strip-test/main.cxx | 6 + ld64/unit-tests/test-cases/eh_frame/Makefile | 47 + ld64/unit-tests/test-cases/eh_frame/bar.cxx | 38 + ld64/unit-tests/test-cases/eh_frame/foo.cxx | 38 + .../test-cases/empty-object/Makefile | 40 + .../unit-tests/test-cases/empty-object/main.c | 1 + ld64/unit-tests/test-cases/end-label/Makefile | 41 + ld64/unit-tests/test-cases/end-label/bar.s | 7 + ld64/unit-tests/test-cases/end-label/foo.s | 11 + .../Makefile | 39 + .../foo.c | 34 + .../exported-symbols-wildcards/Makefile | 78 + .../exported-symbols-wildcards/expect1 | 2 + .../exported-symbols-wildcards/expect2 | 3 + .../exported-symbols-wildcards/expect3 | 4 + .../exported-symbols-wildcards/expect4 | 6 + .../exported-symbols-wildcards/expect5 | 3 + .../exported-symbols-wildcards/expect6 | 4 + .../exported-symbols-wildcards/expect7 | 2 + .../exported-symbols-wildcards/expect8 | 3 + .../exported-symbols-wildcards/foo.c | 55 + .../exported-symbols-wildcards/list5 | 2 + .../exported_symbols_list-eol/Makefile | 41 + .../exported_symbols_list-eol/expected.nm | 2 + .../exported_symbols_list-eol/test.c | 18 + .../exported_symbols_list-eol/test.exp | 1 + .../exported_symbols_list-hidden/Makefile | 41 + .../exported_symbols_list-hidden/test.c | 18 + .../exported_symbols_list-hidden/test.exp | 4 + .../exported_symbols_list-r/Makefile | 55 + .../exported_symbols_list-r/test-bad.exp | 3 + .../test-cases/exported_symbols_list-r/test.c | 18 + .../exported_symbols_list-r/test.exp | 2 + .../external-reloc-sorting/Makefile | 40 + .../test-cases/external-reloc-sorting/foo.c | 5 + .../test-cases/external-reloc-sorting/main.c | 39 + ld64/unit-tests/test-cases/filelist/Makefile | 47 + .../test-cases/filelist/comment.txt | 1 + ld64/unit-tests/test-cases/filelist/hello.c | 29 + .../unit-tests/test-cases/flat-dylib/Makefile | 40 + ld64/unit-tests/test-cases/flat-dylib/main.c | 33 + .../flat-indirect-undefines/Makefile | 49 + .../test-cases/flat-indirect-undefines/bar.c | 4 + .../test-cases/flat-indirect-undefines/foo.c | 8 + .../test-cases/flat-indirect-undefines/main.c | 10 + ld64/unit-tests/test-cases/flat-main/Makefile | 40 + ld64/unit-tests/test-cases/flat-main/main.c | 33 + .../test-cases/got-elimination/Makefile | 50 + .../test-cases/got-elimination/bar.c | 28 + .../test-cases/got-elimination/foo.c | 42 + .../unit-tests/test-cases/header-pad/Makefile | 38 + .../test-cases/header-pad/comment.txt | 1 + ld64/unit-tests/test-cases/header-pad/hello.c | 29 + .../test-cases/hello-world/Makefile | 38 + .../test-cases/hello-world/comment.txt | 1 + .../unit-tests/test-cases/hello-world/hello.c | 29 + .../implicit-common2/Makefile.newtest | 47 + .../test-cases/implicit-common2/a.c | 7 + .../test-cases/implicit-common2/comment.txt | 1 + .../test-cases/implicit-common2/test.c | 26 + .../test-cases/implicit-common3/Makefile | 44 + .../test-cases/implicit-common3/a.c | 8 + .../test-cases/implicit-common3/comment.txt | 1 + .../test-cases/implicit-common3/test.c | 37 + .../implicit-common4/Makefile.newtest | 45 + .../test-cases/implicit-common4/a.c | 7 + .../test-cases/implicit-common4/comment.txt | 1 + .../test-cases/implicit-common4/test.c | 26 + .../implicit-common5/Makefile.newtest | 41 + .../test-cases/implicit-common5/a.c | 7 + .../test-cases/implicit-common5/comment.txt | 1 + .../test-cases/implicit-common5/test.c | 25 + .../test-cases/implicit_dylib/Makefile | 48 + .../test-cases/implicit_dylib/bar.c | 7 + .../test-cases/implicit_dylib/foo.c | 5 + .../test-cases/implicit_dylib/main.c | 11 + .../test-cases/indirect-dylib/Makefile | 46 + .../test-cases/indirect-dylib/bar.c | 31 + .../test-cases/indirect-dylib/comment.txt | 4 + .../test-cases/indirect-dylib/foo.c | 31 + .../test-cases/indirect-dylib/main.c | 33 + .../test-cases/indirect-path-search/Makefile | 106 + .../test-cases/indirect-path-search/bar.c | 5 + .../test-cases/indirect-path-search/baz.c | 5 + .../test-cases/indirect-path-search/foo.c | 4 + .../test-cases/indirect-path-search/main.c | 8 + .../test-cases/interposable_list/Makefile | 47 + .../test-cases/interposable_list/test.c | 57 + .../test-cases/interposable_list/test.exp | 2 + .../unit-tests/test-cases/large-data/Makefile | 50 + ld64/unit-tests/test-cases/large-data/test1.c | 42 + ld64/unit-tests/test-cases/large-data/test2.c | 37 + ld64/unit-tests/test-cases/large-data/test3.c | 37 + ld64/unit-tests/test-cases/large-data/test4.c | 37 + .../test-cases/late-link-error/Makefile | 41 + .../test-cases/late-link-error/comment.txt | 2 + .../test-cases/late-link-error/link_error.s | 22 + .../test-cases/lazy-dylib-objc/Makefile | 45 + .../test-cases/lazy-dylib-objc/foo.h | 9 + .../test-cases/lazy-dylib-objc/foo.m | 8 + .../test-cases/lazy-dylib-objc/main.m | 12 + .../unit-tests/test-cases/lazy-dylib/Makefile | 46 + ld64/unit-tests/test-cases/lazy-dylib/bad.c | 12 + ld64/unit-tests/test-cases/lazy-dylib/bad2.c | 13 + ld64/unit-tests/test-cases/lazy-dylib/foo.c | 5 + ld64/unit-tests/test-cases/lazy-dylib/main.c | 18 + .../literals-coalesce-alignment/Makefile | 46 + .../cstring-align-0.s | 26 + .../cstring-align-3.s | 26 + .../literals-coalesce-alignment2/Makefile | 47 + .../literals-coalesce-alignment2/comment.txt | 1 + .../cstring-align-0.s | 27 + .../cstring-align-3.s | 28 + .../literals-coalesce-alignment3/Makefile | 48 + .../literals-coalesce-alignment3/comment.txt | 1 + .../cstring-align-0.s | 27 + .../cstring-align-3.s | 28 + .../test-cases/literals-coalesce/Makefile | 40 + .../test-cases/literals-coalesce/literals.s | 69 + .../literals-coalesce2/Makefile.newtest | 40 + .../test-cases/literals-coalesce2/comment.txt | 1 + .../test-cases/literals-coalesce2/literals.s | 48 + .../test-cases/literals-coalesce2/test.sh | 5 + .../test-cases/llvm-integration/Makefile | 289 + .../test-cases/llvm-integration/a.c | 5 + .../test-cases/llvm-integration/a1.c | 10 + .../test-cases/llvm-integration/a10.c | 5 + .../test-cases/llvm-integration/a11.c | 6 + .../test-cases/llvm-integration/a12.c | 8 + .../test-cases/llvm-integration/a12.h | 8 + .../test-cases/llvm-integration/a13.cc | 3 + .../test-cases/llvm-integration/a13.h | 7 + .../test-cases/llvm-integration/a14.c | 1 + .../test-cases/llvm-integration/a15.c | 3 + .../test-cases/llvm-integration/a17.c | 4 + .../test-cases/llvm-integration/a18.c | 18 + .../test-cases/llvm-integration/a2.c | 6 + .../test-cases/llvm-integration/a20.c | 2 + .../test-cases/llvm-integration/a3.c | 6 + .../test-cases/llvm-integration/a4.c | 6 + .../test-cases/llvm-integration/a5.c | 10 + .../test-cases/llvm-integration/a6.c | 10 + .../test-cases/llvm-integration/a7.c | 11 + .../test-cases/llvm-integration/a8.c | 23 + .../test-cases/llvm-integration/a9.c | 25 + .../test-cases/llvm-integration/a9.list | 3 + .../test-cases/llvm-integration/b.c | 3 + .../test-cases/llvm-integration/b1.c | 4 + .../test-cases/llvm-integration/b10.c | 7 + .../test-cases/llvm-integration/b10.h | 6 + .../test-cases/llvm-integration/b14.c | 7 + .../test-cases/llvm-integration/b15.c | 8 + .../test-cases/llvm-integration/b17.c | 4 + .../test-cases/llvm-integration/b2.c | 9 + .../test-cases/llvm-integration/b20.c | 1 + .../test-cases/llvm-integration/b3.c | 4 + .../test-cases/llvm-integration/b4.c | 13 + .../test-cases/llvm-integration/b5.c | 4 + .../test-cases/llvm-integration/b7.c | 7 + .../test-cases/llvm-integration/c15.c | 9 + .../test-cases/llvm-integration/main.c | 9 + .../test-cases/llvm-integration/main1.c | 13 + .../test-cases/llvm-integration/main10.c | 10 + .../test-cases/llvm-integration/main11.c | 7 + .../test-cases/llvm-integration/main12.c | 7 + .../test-cases/llvm-integration/main13.cc | 8 + .../test-cases/llvm-integration/main16.c | 8 + .../test-cases/llvm-integration/main19.c | 8 + .../test-cases/llvm-integration/main2.c | 9 + .../test-cases/llvm-integration/main20.c | 7 + .../test-cases/llvm-integration/main3.c | 13 + .../test-cases/llvm-integration/main4.c | 9 + .../test-cases/llvm-integration/main5.c | 16 + .../test-cases/llvm-integration/main6.c | 10 + .../test-cases/llvm-integration/main7.c | 10 + .../test-cases/llvm-integration/main8.c | 11 + .../test-cases/llvm-integration/main9.c | 14 + .../test-cases/loader_path/Makefile | 46 + ld64/unit-tests/test-cases/loader_path/bar.c | 6 + ld64/unit-tests/test-cases/loader_path/foo.c | 7 + ld64/unit-tests/test-cases/loader_path/main.c | 8 + .../local-symbol-partial-stripping/Makefile | 75 + .../local-symbol-partial-stripping/a.expect | 2 + .../local-symbol-partial-stripping/a.list | 2 + .../local-symbol-partial-stripping/b.expect | 3 + .../local-symbol-partial-stripping/b.list | 2 + .../local-symbol-partial-stripping/c.list | 1 + .../local-symbol-partial-stripping/foo.c | 11 + .../local-symbol-partial-stripping/main.c | 11 + .../test-cases/main-stripped/Makefile | 38 + .../test-cases/main-stripped/main.c | 34 + .../test-cases/main-stripped/main.exp | 1 + .../test-cases/missing-option-args/Makefile | 98 + .../missing-option-args/comment.txt | 1 + .../test-cases/multiple-entry-points/Makefile | 46 + .../multiple-entry-points/comment.txt | 3 + .../test-cases/multiple-entry-points/test.s | 54 + .../no-dynamic-common/Makefile.newtest | 39 + .../test-cases/no-dynamic-common/a.c | 7 + .../test-cases/no-dynamic-common/comment.txt | 1 + .../test-cases/no-dynamic-common/test.c | 25 + ld64/unit-tests/test-cases/no-uuid/Makefile | 63 + ld64/unit-tests/test-cases/no-uuid/bar.c | 4 + .../unit-tests/test-cases/no-uuid/comment.txt | 1 + ld64/unit-tests/test-cases/no-uuid/foo.c | 4 + .../unit-tests/test-cases/non-lazy-r/Makefile | 61 + ld64/unit-tests/test-cases/non-lazy-r/foo.c | 12 + ld64/unit-tests/test-cases/non-lazy-r/other.c | 2 + .../objc-category-debug-notes/Makefile | 40 + .../objc-category-debug-notes/test.m | 44 + .../objc-exported_symbols_list/Makefile | 40 + .../objc-exported_symbols_list/foo.exp | 1 + .../objc-exported_symbols_list/foo.m | 18 + .../test-cases/objc-gc-checks/Makefile | 84 + .../test-cases/objc-gc-checks/bar.m | 11 + .../test-cases/objc-gc-checks/comment.txt | 1 + .../test-cases/objc-gc-checks/foo.m | 12 + .../test-cases/objc-gc-checks/runtime.c | 2 + .../test-cases/objc-literal-pointers/Makefile | 47 + .../test-cases/objc-literal-pointers/test.m | 33 + .../test-cases/objc-references/Makefile | 47 + .../test-cases/objc-references/comment.txt | 1 + .../test-cases/objc-references/test.m | 52 + .../objc-selector-coalescing/Makefile | 39 + .../objc-selector-coalescing/main.m | 7 + .../objc-selector-coalescing/other.m | 10 + .../test-cases/operator-new/Makefile | 40 + .../test-cases/operator-new/main.cxx | 47 + .../test-cases/order_file-ans/Makefile | 40 + .../test-cases/order_file-ans/main.cxx | 62 + .../test-cases/order_file-ans/main.expected | 4 + .../test-cases/order_file-ans/main.order | 4 + .../unit-tests/test-cases/order_file/Makefile | 57 + ld64/unit-tests/test-cases/order_file/extra.s | 24 + ld64/unit-tests/test-cases/order_file/main.c | 33 + .../test-cases/order_file/main1.expected | 4 + .../test-cases/order_file/main1.order | 4 + .../test-cases/order_file/main2.expected | 11 + .../test-cases/order_file/main2.order | 6 + .../test-cases/order_file/main3.expected | 4 + .../test-cases/order_file/main3.order | 8 + .../test-cases/prebound-main/Makefile | 51 + .../test-cases/prebound-main/main.c | 3 + .../test-cases/prebound-split-seg/Makefile | 39 + .../prebound-split-seg/address_table | 4 + .../test-cases/prebound-split-seg/bar.c | 36 + .../test-cases/private-non-lazy/Makefile | 54 + .../test-cases/private-non-lazy/bar.c | 3 + .../test-cases/private-non-lazy/comment.txt | 1 + .../test-cases/private-non-lazy/foo.c | 7 + .../test-cases/private-non-lazy/hello.c | 31 + .../test-cases/re-export-cases/Makefile | 167 + .../test-cases/re-export-cases/bar.c | 5 + .../test-cases/re-export-cases/baz.c | 5 + .../test-cases/re-export-cases/foo.c | 4 + .../test-cases/re-export-flag/Makefile | 48 + .../test-cases/re-export-flag/bar.c | 5 + .../test-cases/re-export-flag/foo.c | 4 + .../re-export-optimizations/Makefile | 65 + .../test-cases/re-export-optimizations/bar.c | 5 + .../test-cases/re-export-optimizations/foo.c | 4 + .../test-cases/re-export-optimizations/main.c | 10 + .../re-export-relative-paths/Makefile | 49 + .../test-cases/re-export-relative-paths/bar.c | 5 + .../test-cases/re-export-relative-paths/foo.c | 4 + .../re-export-relative-paths/main.c | 11 + .../re-export-relative-paths/wrap.c | 2 + .../test-cases/read-only-relocs/Makefile | 63 + .../test-cases/read-only-relocs/foo.c | 6 + .../test-cases/read-only-relocs/test.c | 34 + .../test-cases/rebase-basic/Makefile | 53 + ld64/unit-tests/test-cases/rebase-basic/bar.m | 13 + .../test-cases/rebase-basic/comment.txt | 1 + ld64/unit-tests/test-cases/rebase-basic/foo.c | 14 + .../unit-tests/test-cases/relocs-asm/Makefile | 46 + .../test-cases/relocs-asm/comment.txt | 3 + .../test-cases/relocs-asm/relocs-asm.s | 471 + ld64/unit-tests/test-cases/relocs-c/Makefile | 57 + ld64/unit-tests/test-cases/relocs-c/test.c | 76 + ld64/unit-tests/test-cases/relocs-c2/Makefile | 58 + .../test-cases/relocs-c2/comment.txt | 5 + ld64/unit-tests/test-cases/relocs-c2/test.c | 76 + .../test-cases/relocs-literals/Makefile | 47 + .../test-cases/relocs-literals/test.c | 54 + .../test-cases/relocs-literals2/Makefile | 50 + .../test-cases/relocs-literals2/test.c | 54 + .../test-cases/relocs-literals3/Makefile | 47 + .../test-cases/relocs-literals3/comment.txt | 3 + .../test-cases/relocs-literals3/test.c | 47 + .../test-cases/relocs-objc/Makefile | 47 + .../test-cases/relocs-objc/comment.txt | 3 + ld64/unit-tests/test-cases/relocs-objc/test.m | 59 + .../test-cases/segment-order/Makefile | 37 + .../test-cases/segment-order/expected.order | 3 + .../test-cases/segment-order/main.c | 4 + .../test-cases/segment-order/segJJJ.s | 7 + .../test-cases/segment-order/segKKK.s | 7 + .../test-cases/segment-order/segLLL.s | 7 + .../test-cases/slow-x86-stubs/Makefile | 42 + .../test-cases/slow-x86-stubs/hello.c | 7 + .../test-cases/special-labels/Makefile | 41 + .../test-cases/special-labels/extra.s | 9 + .../test-cases/special-labels/main.c | 29 + .../test-cases/stabs-coalesce/Makefile | 52 + .../test-cases/stabs-coalesce/comment.txt | 3 + .../test-cases/stabs-coalesce/header.h | 31 + .../test-cases/stabs-coalesce/hello.cxx | 33 + .../test-cases/stabs-coalesce/other.cxx | 41 + .../test-cases/stabs-directory-slash/Makefile | 39 + .../test-cases/stabs-directory-slash/main.c | 3 + .../stack_addr_no_size/Makefile.newtest | 77 + .../test-cases/stack_addr_no_size/comment.txt | 11 + .../test-cases/stack_addr_no_size/main.c | 29 + .../test-cases/stack_addr_size/Makefile | 57 + .../test-cases/stack_addr_size/comment.txt | 11 + .../test-cases/stack_addr_size/main.c | 29 + .../test-cases/stack_size_no_addr/Makefile | 56 + .../test-cases/stack_size_no_addr/comment.txt | 11 + .../test-cases/stack_size_no_addr/main.c | 37 + .../test-cases/static-executable/Makefile | 35 + .../test-cases/static-executable/test.c | 11 + .../test-cases/static-strip/Makefile.newtest | 40 + .../test-cases/static-strip/comment.txt | 1 + .../unit-tests/test-cases/static-strip/test.c | 28 + .../test-cases/strip-test2/Makefile | 70 + .../test-cases/strip-test2/comment.txt | 21 + .../test-cases/strip-test2/main.cxx | 6 + .../test-cases/strip-test3/Makefile.newtest | 71 + .../test-cases/strip-test3/comment.txt | 21 + .../test-cases/strip-test3/main.cxx | 6 + .../test-cases/strip_local/Makefile | 53 + ld64/unit-tests/test-cases/strip_local/foo.c | 8 + .../unit-tests/test-cases/strip_local/hello.c | 33 + .../stripped-indirect-symbol-table/Makefile | 57 + .../stripped-indirect-symbol-table/a.c | 7 + .../stripped-indirect-symbol-table/b.c | 12 + .../stripped-indirect-symbol-table/c.c | 11 + .../stripped-indirect-symbol-table/func.c | 1 + .../stripped-indirect-symbol-table/strip.list | 2 + .../test-cases/stub-generation-weak/Makefile | 42 + .../test-cases/stub-generation-weak/foo.c | 5 + .../test-cases/stub-generation-weak/main.c | 40 + .../test-cases/stub-generation/Makefile | 41 + .../test-cases/stub-generation/test.c | 40 + .../test-cases/switch-jump-table/Makefile | 56 + .../switch-jump-table/interpose.exp | 2 + .../test-cases/switch-jump-table/main.c | 4 + .../test-cases/switch-jump-table/switch.s | 49 + .../test-cases/symbol-moving/Makefile | 93 + .../unit-tests/test-cases/symbol-moving/aaa.c | 3 + .../test-cases/symbol-moving/anotb.c | 26 + .../unit-tests/test-cases/symbol-moving/bar.c | 1 + .../unit-tests/test-cases/symbol-moving/bbb.c | 1 + .../test-cases/symbol-moving/bnota.c | 25 + .../unit-tests/test-cases/symbol-moving/foo.c | 3 + .../test-cases/symbol-moving/main.c | 17 + .../test-cases/tentative-and-archive/Makefile | 50 + .../test-cases/tentative-and-archive/foo.c | 6 + .../test-cases/tentative-and-archive/main.c | 8 + .../test-cases/tentative-and-dylib/Makefile | 56 + .../test-cases/tentative-and-dylib/bar.c | 1 + .../test-cases/tentative-and-dylib/foo.c | 2 + .../test-cases/tentative-and-dylib/main.c | 8 + .../tentative-to-real-hidden/Makefile | 52 + .../tentative-to-real-hidden/test.c | 11 + .../test-cases/tentative-to-real/Makefile | 45 + .../test-cases/tentative-to-real/comment.txt | 1 + .../test-cases/tentative-to-real/test.c | 3 + ld64/unit-tests/test-cases/thumb-blx/Makefile | 54 + ld64/unit-tests/test-cases/thumb-blx/test.c | 36 + .../undefined-dynamic-lookup/Makefile | 47 + .../undefined-dynamic-lookup/main.c | 32 + .../Makefile | 46 + .../visibility-warning-dylib-v-archive/bar.c | 11 + .../visibility-warning-dylib-v-archive/foo.c | 5 + .../visibility-warning-dylib-v-archive/main.c | 11 + .../test-cases/visibility-warning/Makefile | 57 + .../test-cases/visibility-warning/foo.c | 5 + .../visibility-warning/foo_hidden.c | 5 + .../test-cases/visibility-warning/foo_weak.c | 5 + .../visibility-warning/foo_weak_hidden.c | 5 + .../test-cases/weak-def-ordinal/Makefile | 50 + .../test-cases/weak-def-ordinal/bar.c | 6 + .../test-cases/weak-def-ordinal/foo.c | 11 + .../test-cases/weak-def-ordinal/main.c | 35 + .../unit-tests/test-cases/weak_dylib/Makefile | 49 + ld64/unit-tests/test-cases/weak_dylib/bar.c | 9 + ld64/unit-tests/test-cases/weak_dylib/bar.h | 9 + ld64/unit-tests/test-cases/weak_dylib/foo.c | 9 + ld64/unit-tests/test-cases/weak_dylib/foo.h | 6 + ld64/unit-tests/test-cases/weak_dylib/main.c | 22 + .../test-cases/weak_import/Makefile | 62 + ld64/unit-tests/test-cases/weak_import/foo.c | 17 + ld64/unit-tests/test-cases/weak_import/foo.h | 16 + ld64/unit-tests/test-cases/weak_import/main.c | 20 + .../test-cases/weak_import2/Makefile.newtest | 58 + .../test-cases/weak_import2/comment.txt | 1 + ld64/unit-tests/test-cases/weak_import2/foo.c | 17 + ld64/unit-tests/test-cases/weak_import2/foo.h | 16 + .../unit-tests/test-cases/weak_import2/foo1.c | 10 + .../unit-tests/test-cases/weak_import2/main.c | 20 + .../test-cases/weak_import3/Makefile | 43 + .../test-cases/weak_import3/comment.txt | 1 + ld64/unit-tests/test-cases/weak_import3/foo.c | 17 + ld64/unit-tests/test-cases/weak_import3/foo.h | 16 + .../unit-tests/test-cases/weak_import3/foo1.c | 4 + .../unit-tests/test-cases/weak_import3/main.c | 20 + ld64/unit-tests/test-cases/why_live/Makefile | 44 + ld64/unit-tests/test-cases/why_live/bar.c | 1 + ld64/unit-tests/test-cases/why_live/foo.c | 12 + ld64/unit-tests/test-cases/why_live/main.c | 7 + ld64/unit-tests/test-cases/zero-fill/Makefile | 38 + ld64/unit-tests/test-cases/zero-fill/test.c | 51 + .../unit-tests/test-cases/zero-fill2/Makefile | 38 + .../test-cases/zero-fill2/comment.txt | 1 + ld64/unit-tests/test-cases/zero-fill2/test.c | 58 + .../unit-tests/test-cases/zero-fill3/Makefile | 50 + .../test-cases/zero-fill3/comment.txt | 1 + ld64/unit-tests/test-cases/zero-fill3/test.c | 63 + 1307 files changed, 92226 insertions(+) create mode 100644 ld64/APPLE_LICENSE create mode 100644 ld64/ChangeLog create mode 100644 ld64/FireOpal/APPLE_LICENSE create mode 100644 ld64/FireOpal/ChangeLog create mode 100644 ld64/FireOpal/doc/man/man1/ld.1 create mode 100644 ld64/FireOpal/doc/man/man1/ld64.1 create mode 100644 ld64/FireOpal/doc/man/man1/rebase.1 create mode 100644 ld64/FireOpal/ld64.xcodeproj/project.pbxproj create mode 100644 ld64/FireOpal/src/Architectures.hpp create mode 100644 ld64/FireOpal/src/ArchiveReader.hpp create mode 100644 ld64/FireOpal/src/ExecutableFile.h create mode 100644 ld64/FireOpal/src/FileAbstraction.hpp create mode 100644 ld64/FireOpal/src/LTOReader.hpp create mode 100644 ld64/FireOpal/src/MachOFileAbstraction.hpp create mode 100644 ld64/FireOpal/src/MachOReaderDylib.hpp create mode 100644 ld64/FireOpal/src/MachOReaderRelocatable.hpp create mode 100644 ld64/FireOpal/src/MachOWriterExecutable.hpp create mode 100644 ld64/FireOpal/src/ObjectDump.cpp create mode 100644 ld64/FireOpal/src/ObjectFile.h create mode 100644 ld64/FireOpal/src/OpaqueSection.hpp create mode 100644 ld64/FireOpal/src/Options.cpp create mode 100644 ld64/FireOpal/src/Options.h create mode 100644 ld64/FireOpal/src/SectCreate.h create mode 100644 ld64/FireOpal/src/debugline.c create mode 100644 ld64/FireOpal/src/debugline.h create mode 100644 ld64/FireOpal/src/dwarf2.h create mode 100644 ld64/FireOpal/src/ld.cpp create mode 100644 ld64/FireOpal/src/machochecker.cpp create mode 100644 ld64/FireOpal/src/rebase.cpp create mode 100644 ld64/FireOpal/unit-tests/README create mode 100755 ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl create mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl create mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl create mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl create mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl create mode 100755 ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl create mode 100755 ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl create mode 100755 ld64/FireOpal/unit-tests/bin/make-recursive.pl create mode 100755 ld64/FireOpal/unit-tests/bin/mkld create mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl create mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl create mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl create mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl create mode 100755 ld64/FireOpal/unit-tests/bin/result-filter.pl create mode 100755 ld64/FireOpal/unit-tests/bin/rm-stale-test-logs create mode 100755 ld64/FireOpal/unit-tests/clean-tests create mode 100644 ld64/FireOpal/unit-tests/include/common.makefile create mode 100644 ld64/FireOpal/unit-tests/include/test.h create mode 100755 ld64/FireOpal/unit-tests/proctor-run create mode 100755 ld64/FireOpal/unit-tests/run-all-unit-tests create mode 100755 ld64/FireOpal/unit-tests/run-all-unit-tests-debug create mode 100644 ld64/FireOpal/unit-tests/src/Makefile create mode 100644 ld64/FireOpal/unit-tests/src/results-to-xml.cpp create mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 create mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m create mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj create mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch create mode 100644 ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d create mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/foo.c create mode 100755 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx create mode 100755 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx create mode 100755 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/empty-object/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/end-label/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/end-label/bar.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/end-label/foo.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/filelist/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/filelist/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-main/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test1.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test2.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test3.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test4.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s create mode 100755 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-references/test.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/extra.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main1.order create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main2.order create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected create mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main3.order create mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table create mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m create mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order create mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/special-labels/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/static-executable/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/static-strip/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list create mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp create mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/bar.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/foo.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/main.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt create mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c create mode 100644 ld64/doc/man/man1/ld.1 create mode 100644 ld64/doc/man/man1/ld64.1 create mode 100644 ld64/doc/man/man1/rebase.1 create mode 100644 ld64/ld64.xcodeproj/project.pbxproj create mode 100644 ld64/src/Architectures.hpp create mode 100644 ld64/src/ArchiveReader.hpp create mode 100644 ld64/src/ExecutableFile.h create mode 100644 ld64/src/FileAbstraction.hpp create mode 100644 ld64/src/LTOReader.hpp create mode 100644 ld64/src/MachOFileAbstraction.hpp create mode 100644 ld64/src/MachOReaderDylib.hpp create mode 100644 ld64/src/MachOReaderRelocatable.hpp create mode 100644 ld64/src/MachOWriterExecutable.hpp create mode 100644 ld64/src/ObjectDump.cpp create mode 100644 ld64/src/ObjectFile.h create mode 100644 ld64/src/OpaqueSection.hpp create mode 100644 ld64/src/Options.cpp create mode 100644 ld64/src/Options.h create mode 100644 ld64/src/SectCreate.h create mode 100644 ld64/src/debugline.c create mode 100644 ld64/src/debugline.h create mode 100644 ld64/src/dwarf2.h create mode 100644 ld64/src/ld.cpp create mode 100644 ld64/src/machochecker.cpp create mode 100644 ld64/src/rebase.cpp create mode 100644 ld64/unit-tests/README create mode 100755 ld64/unit-tests/bin/exit-non-zero-pass.pl create mode 100755 ld64/unit-tests/bin/fail-if-exit-non-zero.pl create mode 100755 ld64/unit-tests/bin/fail-if-exit-zero.pl create mode 100755 ld64/unit-tests/bin/fail-if-no-stdin.pl create mode 100755 ld64/unit-tests/bin/fail-if-stdin.pl create mode 100755 ld64/unit-tests/bin/fail-iff-exit-zero.pl create mode 100755 ld64/unit-tests/bin/make-recursive-newtest.pl create mode 100755 ld64/unit-tests/bin/make-recursive.pl create mode 100755 ld64/unit-tests/bin/mkld create mode 100755 ld64/unit-tests/bin/pass-iff-exit-non-zero.pl create mode 100755 ld64/unit-tests/bin/pass-iff-exit-zero.pl create mode 100755 ld64/unit-tests/bin/pass-iff-no-stdin.pl create mode 100755 ld64/unit-tests/bin/pass-iff-stdin.pl create mode 100755 ld64/unit-tests/bin/result-filter.pl create mode 100755 ld64/unit-tests/bin/rm-stale-test-logs create mode 100755 ld64/unit-tests/clean-tests create mode 100644 ld64/unit-tests/include/common.makefile create mode 100644 ld64/unit-tests/include/test.h create mode 100755 ld64/unit-tests/proctor-run create mode 100755 ld64/unit-tests/run-all-unit-tests create mode 100755 ld64/unit-tests/run-all-unit-tests-debug create mode 100644 ld64/unit-tests/src/Makefile create mode 100644 ld64/unit-tests/src/results-to-xml.cpp create mode 100644 ld64/unit-tests/src/xmlparser/xmlparser.1 create mode 100644 ld64/unit-tests/src/xmlparser/xmlparser.m create mode 100644 ld64/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj create mode 100644 ld64/unit-tests/src/xmlparser/xmlparser_Prefix.pch create mode 100644 ld64/unit-tests/test-cases/16-byte-alignment/Makefile create mode 100644 ld64/unit-tests/test-cases/16-byte-alignment/comment.txt create mode 100644 ld64/unit-tests/test-cases/16-byte-alignment/tl_test2.c create mode 100644 ld64/unit-tests/test-cases/absolute-symbol/Makefile create mode 100644 ld64/unit-tests/test-cases/absolute-symbol/abs.s create mode 100644 ld64/unit-tests/test-cases/absolute-symbol/main.c create mode 100644 ld64/unit-tests/test-cases/alias-command-line/Makefile create mode 100644 ld64/unit-tests/test-cases/alias-command-line/aliases.s create mode 100644 ld64/unit-tests/test-cases/alias-command-line/aliases.txt create mode 100644 ld64/unit-tests/test-cases/alias-objects/Makefile create mode 100644 ld64/unit-tests/test-cases/alias-objects/aliases.s create mode 100644 ld64/unit-tests/test-cases/align-modulus/Makefile create mode 100644 ld64/unit-tests/test-cases/align-modulus/align.s create mode 100644 ld64/unit-tests/test-cases/align-modulus/comment.txt create mode 100644 ld64/unit-tests/test-cases/align-modulus/foo.c create mode 100644 ld64/unit-tests/test-cases/align-modulus/foo.exp create mode 100644 ld64/unit-tests/test-cases/allow-stack-execute/Makefile create mode 100644 ld64/unit-tests/test-cases/allow-stack-execute/comment.txt create mode 100644 ld64/unit-tests/test-cases/allow-stack-execute/foo.c create mode 100644 ld64/unit-tests/test-cases/allowable-client/Makefile create mode 100644 ld64/unit-tests/test-cases/allowable-client/bar.c create mode 100644 ld64/unit-tests/test-cases/allowable-client/baz.c create mode 100644 ld64/unit-tests/test-cases/allowable-client/comment.txt create mode 100644 ld64/unit-tests/test-cases/allowable-client/foo.c create mode 100644 ld64/unit-tests/test-cases/allowable-client/main.c create mode 100644 ld64/unit-tests/test-cases/archive-ObjC/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-ObjC/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-ObjC/baz.m create mode 100644 ld64/unit-tests/test-cases/archive-ObjC/foo.m create mode 100644 ld64/unit-tests/test-cases/archive-ObjC/main.c create mode 100644 ld64/unit-tests/test-cases/archive-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-basic/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-basic/comment.txt create mode 100644 ld64/unit-tests/test-cases/archive-basic/foo.c create mode 100644 ld64/unit-tests/test-cases/archive-basic/main.c create mode 100644 ld64/unit-tests/test-cases/archive-duplicate/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-duplicate/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-duplicate/foo.c create mode 100644 ld64/unit-tests/test-cases/archive-duplicate/main.c create mode 100644 ld64/unit-tests/test-cases/archive-weak/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-weak/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-weak/baz.c create mode 100644 ld64/unit-tests/test-cases/archive-weak/comment.txt create mode 100644 ld64/unit-tests/test-cases/archive-weak/foo.c create mode 100644 ld64/unit-tests/test-cases/archive-weak/main.c create mode 100644 ld64/unit-tests/test-cases/auto-arch/Makefile create mode 100644 ld64/unit-tests/test-cases/auto-arch/hello.c create mode 100644 ld64/unit-tests/test-cases/blank-stubs/Makefile create mode 100644 ld64/unit-tests/test-cases/blank-stubs/comment.txt create mode 100644 ld64/unit-tests/test-cases/blank-stubs/foo.c create mode 100644 ld64/unit-tests/test-cases/blank-stubs/main.c create mode 100644 ld64/unit-tests/test-cases/branch-islands/Makefile create mode 100644 ld64/unit-tests/test-cases/branch-islands/extra.c create mode 100644 ld64/unit-tests/test-cases/branch-islands/hello.c create mode 100644 ld64/unit-tests/test-cases/branch-islands/space.s create mode 100644 ld64/unit-tests/test-cases/bundle_loader/Makefile create mode 100644 ld64/unit-tests/test-cases/bundle_loader/bar.c create mode 100644 ld64/unit-tests/test-cases/bundle_loader/bundle.c create mode 100644 ld64/unit-tests/test-cases/bundle_loader/main.c create mode 100644 ld64/unit-tests/test-cases/cfstring-coalesce/Makefile create mode 100644 ld64/unit-tests/test-cases/cfstring-coalesce/bar.c create mode 100644 ld64/unit-tests/test-cases/cfstring-coalesce/foo.c create mode 100644 ld64/unit-tests/test-cases/cfstring-utf16/Makefile create mode 100644 ld64/unit-tests/test-cases/cfstring-utf16/bar.m create mode 100644 ld64/unit-tests/test-cases/cfstring-utf16/foo.m create mode 100644 ld64/unit-tests/test-cases/commons-alignment/Makefile create mode 100644 ld64/unit-tests/test-cases/commons-alignment/foo.s create mode 100644 ld64/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile create mode 100644 ld64/unit-tests/test-cases/commons-coalesced-dead_strip/a.c create mode 100644 ld64/unit-tests/test-cases/commons-coalesced-dead_strip/b.c create mode 100644 ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.c create mode 100644 ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.h create mode 100644 ld64/unit-tests/test-cases/commons-mixed/Makefile create mode 100644 ld64/unit-tests/test-cases/commons-mixed/bar.c create mode 100644 ld64/unit-tests/test-cases/commons-mixed/foo.c create mode 100644 ld64/unit-tests/test-cases/commons-order/Makefile create mode 100644 ld64/unit-tests/test-cases/commons-order/bar.c create mode 100644 ld64/unit-tests/test-cases/commons-order/baz.c create mode 100644 ld64/unit-tests/test-cases/commons-order/expected.order create mode 100644 ld64/unit-tests/test-cases/commons-order/foo.c create mode 100644 ld64/unit-tests/test-cases/commons-order/main.c create mode 100644 ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile create mode 100644 ld64/unit-tests/test-cases/cpu-sub-types-preference/foo.c create mode 100644 ld64/unit-tests/test-cases/cpu-sub-types/Makefile create mode 100644 ld64/unit-tests/test-cases/cpu-sub-types/comment.txt create mode 100644 ld64/unit-tests/test-cases/cpu-sub-types/foo.c create mode 100644 ld64/unit-tests/test-cases/cpu-sub-types/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-global/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-global/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-global/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive/comment.txt create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-init-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-init-archive/bar.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-init-archive/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip/comment.txt create mode 100644 ld64/unit-tests/test-cases/dead_strip/deadwood.c create mode 100644 ld64/unit-tests/test-cases/dead_strip/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip/main.exp create mode 100644 ld64/unit-tests/test-cases/dead_strip_dylibs/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip_dylibs/bar.c create mode 100644 ld64/unit-tests/test-cases/dead_strip_dylibs/baz.c create mode 100644 ld64/unit-tests/test-cases/dead_strip_dylibs/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip_dylibs/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip_section_attribute/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip_section_attribute/comment.txt create mode 100644 ld64/unit-tests/test-cases/dead_strip_section_attribute/main.c create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes/Makefile create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes/bar.d create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes/comment.txt create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes/foo.d create mode 100644 ld64/unit-tests/test-cases/dtrace-static-probes/main.c create mode 100644 ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-archive-all_load/bar.c create mode 100644 ld64/unit-tests/test-cases/dwarf-archive-all_load/baz.c create mode 100644 ld64/unit-tests/test-cases/dwarf-archive-all_load/comment.txt create mode 100644 ld64/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs create mode 100644 ld64/unit-tests/test-cases/dwarf-archive-all_load/foo.c create mode 100755 ld64/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx create mode 100755 ld64/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/comment.txt create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/header.h create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/hello.cxx create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx create mode 100755 ld64/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl create mode 100644 ld64/unit-tests/test-cases/dwarf-ignore/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-ignore/comment.txt create mode 100644 ld64/unit-tests/test-cases/dwarf-ignore/hello.c create mode 100644 ld64/unit-tests/test-cases/dwarf-strip/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-strip/comment.txt create mode 100644 ld64/unit-tests/test-cases/dwarf-strip/hello.c create mode 100644 ld64/unit-tests/test-cases/dylib-aliases/Makefile create mode 100644 ld64/unit-tests/test-cases/dylib-aliases/bar.c create mode 100644 ld64/unit-tests/test-cases/dylib-aliases/foo.c create mode 100644 ld64/unit-tests/test-cases/dylib-aliases/main.c create mode 100644 ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile create mode 100644 ld64/unit-tests/test-cases/dylib-re-export-cycle/bar.c create mode 100644 ld64/unit-tests/test-cases/dylib-re-export-cycle/foo.c create mode 100644 ld64/unit-tests/test-cases/dylib-re-export-cycle/main.c create mode 100644 ld64/unit-tests/test-cases/dylib_file-missing/Makefile create mode 100644 ld64/unit-tests/test-cases/dylib_file-missing/bar.c create mode 100644 ld64/unit-tests/test-cases/dylib_file-missing/foo.c create mode 100644 ld64/unit-tests/test-cases/dylib_file-missing/main.c create mode 100644 ld64/unit-tests/test-cases/dylib_file/Makefile create mode 100644 ld64/unit-tests/test-cases/dylib_file/bar.c create mode 100644 ld64/unit-tests/test-cases/dylib_file/comment.txt create mode 100644 ld64/unit-tests/test-cases/dylib_file/foo.c create mode 100644 ld64/unit-tests/test-cases/dylib_file/main.c create mode 100644 ld64/unit-tests/test-cases/dylib_init/Makefile create mode 100644 ld64/unit-tests/test-cases/dylib_init/foo.c create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-r/Makefile create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-r/bar.cxx create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-r/foo.cxx create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-r/func.h create mode 100644 ld64/unit-tests/test-cases/eh-coalescing/Makefile create mode 100644 ld64/unit-tests/test-cases/eh-coalescing/bar.cxx create mode 100644 ld64/unit-tests/test-cases/eh-coalescing/foo.cxx create mode 100644 ld64/unit-tests/test-cases/eh-coalescing/foo2.cxx create mode 100644 ld64/unit-tests/test-cases/eh-coalescing/func.h create mode 100644 ld64/unit-tests/test-cases/eh-strip-test/Makefile create mode 100644 ld64/unit-tests/test-cases/eh-strip-test/comment.txt create mode 100644 ld64/unit-tests/test-cases/eh-strip-test/main.cxx create mode 100644 ld64/unit-tests/test-cases/eh_frame/Makefile create mode 100644 ld64/unit-tests/test-cases/eh_frame/bar.cxx create mode 100644 ld64/unit-tests/test-cases/eh_frame/foo.cxx create mode 100644 ld64/unit-tests/test-cases/empty-object/Makefile create mode 100644 ld64/unit-tests/test-cases/empty-object/main.c create mode 100644 ld64/unit-tests/test-cases/end-label/Makefile create mode 100644 ld64/unit-tests/test-cases/end-label/bar.s create mode 100644 ld64/unit-tests/test-cases/end-label/foo.s create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect1 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect2 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect3 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect4 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect5 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect6 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect7 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/expect8 create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/foo.c create mode 100644 ld64/unit-tests/test-cases/exported-symbols-wildcards/list5 create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-eol/expected.nm create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-eol/test.c create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-eol/test.exp create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-hidden/Makefile create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.c create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.exp create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-r/Makefile create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-r/test.c create mode 100644 ld64/unit-tests/test-cases/exported_symbols_list-r/test.exp create mode 100644 ld64/unit-tests/test-cases/external-reloc-sorting/Makefile create mode 100644 ld64/unit-tests/test-cases/external-reloc-sorting/foo.c create mode 100644 ld64/unit-tests/test-cases/external-reloc-sorting/main.c create mode 100644 ld64/unit-tests/test-cases/filelist/Makefile create mode 100644 ld64/unit-tests/test-cases/filelist/comment.txt create mode 100644 ld64/unit-tests/test-cases/filelist/hello.c create mode 100644 ld64/unit-tests/test-cases/flat-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/flat-dylib/main.c create mode 100644 ld64/unit-tests/test-cases/flat-indirect-undefines/Makefile create mode 100644 ld64/unit-tests/test-cases/flat-indirect-undefines/bar.c create mode 100644 ld64/unit-tests/test-cases/flat-indirect-undefines/foo.c create mode 100644 ld64/unit-tests/test-cases/flat-indirect-undefines/main.c create mode 100644 ld64/unit-tests/test-cases/flat-main/Makefile create mode 100644 ld64/unit-tests/test-cases/flat-main/main.c create mode 100644 ld64/unit-tests/test-cases/got-elimination/Makefile create mode 100644 ld64/unit-tests/test-cases/got-elimination/bar.c create mode 100644 ld64/unit-tests/test-cases/got-elimination/foo.c create mode 100644 ld64/unit-tests/test-cases/header-pad/Makefile create mode 100644 ld64/unit-tests/test-cases/header-pad/comment.txt create mode 100644 ld64/unit-tests/test-cases/header-pad/hello.c create mode 100644 ld64/unit-tests/test-cases/hello-world/Makefile create mode 100644 ld64/unit-tests/test-cases/hello-world/comment.txt create mode 100644 ld64/unit-tests/test-cases/hello-world/hello.c create mode 100644 ld64/unit-tests/test-cases/implicit-common2/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/implicit-common2/a.c create mode 100644 ld64/unit-tests/test-cases/implicit-common2/comment.txt create mode 100644 ld64/unit-tests/test-cases/implicit-common2/test.c create mode 100644 ld64/unit-tests/test-cases/implicit-common3/Makefile create mode 100644 ld64/unit-tests/test-cases/implicit-common3/a.c create mode 100644 ld64/unit-tests/test-cases/implicit-common3/comment.txt create mode 100644 ld64/unit-tests/test-cases/implicit-common3/test.c create mode 100644 ld64/unit-tests/test-cases/implicit-common4/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/implicit-common4/a.c create mode 100644 ld64/unit-tests/test-cases/implicit-common4/comment.txt create mode 100644 ld64/unit-tests/test-cases/implicit-common4/test.c create mode 100644 ld64/unit-tests/test-cases/implicit-common5/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/implicit-common5/a.c create mode 100644 ld64/unit-tests/test-cases/implicit-common5/comment.txt create mode 100644 ld64/unit-tests/test-cases/implicit-common5/test.c create mode 100644 ld64/unit-tests/test-cases/implicit_dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/implicit_dylib/bar.c create mode 100644 ld64/unit-tests/test-cases/implicit_dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/implicit_dylib/main.c create mode 100644 ld64/unit-tests/test-cases/indirect-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/indirect-dylib/bar.c create mode 100644 ld64/unit-tests/test-cases/indirect-dylib/comment.txt create mode 100644 ld64/unit-tests/test-cases/indirect-dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/indirect-dylib/main.c create mode 100644 ld64/unit-tests/test-cases/indirect-path-search/Makefile create mode 100644 ld64/unit-tests/test-cases/indirect-path-search/bar.c create mode 100644 ld64/unit-tests/test-cases/indirect-path-search/baz.c create mode 100644 ld64/unit-tests/test-cases/indirect-path-search/foo.c create mode 100644 ld64/unit-tests/test-cases/indirect-path-search/main.c create mode 100644 ld64/unit-tests/test-cases/interposable_list/Makefile create mode 100644 ld64/unit-tests/test-cases/interposable_list/test.c create mode 100644 ld64/unit-tests/test-cases/interposable_list/test.exp create mode 100644 ld64/unit-tests/test-cases/large-data/Makefile create mode 100644 ld64/unit-tests/test-cases/large-data/test1.c create mode 100644 ld64/unit-tests/test-cases/large-data/test2.c create mode 100644 ld64/unit-tests/test-cases/large-data/test3.c create mode 100644 ld64/unit-tests/test-cases/large-data/test4.c create mode 100644 ld64/unit-tests/test-cases/late-link-error/Makefile create mode 100644 ld64/unit-tests/test-cases/late-link-error/comment.txt create mode 100644 ld64/unit-tests/test-cases/late-link-error/link_error.s create mode 100644 ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile create mode 100644 ld64/unit-tests/test-cases/lazy-dylib-objc/foo.h create mode 100644 ld64/unit-tests/test-cases/lazy-dylib-objc/foo.m create mode 100644 ld64/unit-tests/test-cases/lazy-dylib-objc/main.m create mode 100644 ld64/unit-tests/test-cases/lazy-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/lazy-dylib/bad.c create mode 100644 ld64/unit-tests/test-cases/lazy-dylib/bad2.c create mode 100644 ld64/unit-tests/test-cases/lazy-dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/lazy-dylib/main.c create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment/Makefile create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment2/Makefile create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment3/Makefile create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce/Makefile create mode 100644 ld64/unit-tests/test-cases/literals-coalesce/literals.s create mode 100644 ld64/unit-tests/test-cases/literals-coalesce2/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/literals-coalesce2/comment.txt create mode 100644 ld64/unit-tests/test-cases/literals-coalesce2/literals.s create mode 100755 ld64/unit-tests/test-cases/literals-coalesce2/test.sh create mode 100644 ld64/unit-tests/test-cases/llvm-integration/Makefile create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a1.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a10.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a11.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a12.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a12.h create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a13.cc create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a13.h create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a14.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a15.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a17.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a18.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a2.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a20.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a3.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a4.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a5.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a6.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a7.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a8.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a9.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/a9.list create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b1.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b10.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b10.h create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b14.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b15.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b17.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b2.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b20.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b3.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b4.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b5.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/b7.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/c15.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main1.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main10.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main11.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main12.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main13.cc create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main16.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main19.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main2.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main20.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main3.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main4.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main5.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main6.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main7.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main8.c create mode 100644 ld64/unit-tests/test-cases/llvm-integration/main9.c create mode 100644 ld64/unit-tests/test-cases/loader_path/Makefile create mode 100644 ld64/unit-tests/test-cases/loader_path/bar.c create mode 100644 ld64/unit-tests/test-cases/loader_path/foo.c create mode 100644 ld64/unit-tests/test-cases/loader_path/main.c create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/Makefile create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.expect create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.list create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.expect create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.list create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/c.list create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/foo.c create mode 100644 ld64/unit-tests/test-cases/local-symbol-partial-stripping/main.c create mode 100644 ld64/unit-tests/test-cases/main-stripped/Makefile create mode 100644 ld64/unit-tests/test-cases/main-stripped/main.c create mode 100644 ld64/unit-tests/test-cases/main-stripped/main.exp create mode 100644 ld64/unit-tests/test-cases/missing-option-args/Makefile create mode 100644 ld64/unit-tests/test-cases/missing-option-args/comment.txt create mode 100644 ld64/unit-tests/test-cases/multiple-entry-points/Makefile create mode 100644 ld64/unit-tests/test-cases/multiple-entry-points/comment.txt create mode 100644 ld64/unit-tests/test-cases/multiple-entry-points/test.s create mode 100644 ld64/unit-tests/test-cases/no-dynamic-common/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/no-dynamic-common/a.c create mode 100644 ld64/unit-tests/test-cases/no-dynamic-common/comment.txt create mode 100644 ld64/unit-tests/test-cases/no-dynamic-common/test.c create mode 100644 ld64/unit-tests/test-cases/no-uuid/Makefile create mode 100644 ld64/unit-tests/test-cases/no-uuid/bar.c create mode 100644 ld64/unit-tests/test-cases/no-uuid/comment.txt create mode 100644 ld64/unit-tests/test-cases/no-uuid/foo.c create mode 100644 ld64/unit-tests/test-cases/non-lazy-r/Makefile create mode 100644 ld64/unit-tests/test-cases/non-lazy-r/foo.c create mode 100644 ld64/unit-tests/test-cases/non-lazy-r/other.c create mode 100644 ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-category-debug-notes/test.m create mode 100644 ld64/unit-tests/test-cases/objc-exported_symbols_list/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.exp create mode 100644 ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.m create mode 100644 ld64/unit-tests/test-cases/objc-gc-checks/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-gc-checks/bar.m create mode 100644 ld64/unit-tests/test-cases/objc-gc-checks/comment.txt create mode 100644 ld64/unit-tests/test-cases/objc-gc-checks/foo.m create mode 100644 ld64/unit-tests/test-cases/objc-gc-checks/runtime.c create mode 100644 ld64/unit-tests/test-cases/objc-literal-pointers/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-literal-pointers/test.m create mode 100644 ld64/unit-tests/test-cases/objc-references/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-references/comment.txt create mode 100644 ld64/unit-tests/test-cases/objc-references/test.m create mode 100644 ld64/unit-tests/test-cases/objc-selector-coalescing/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-selector-coalescing/main.m create mode 100644 ld64/unit-tests/test-cases/objc-selector-coalescing/other.m create mode 100644 ld64/unit-tests/test-cases/operator-new/Makefile create mode 100644 ld64/unit-tests/test-cases/operator-new/main.cxx create mode 100644 ld64/unit-tests/test-cases/order_file-ans/Makefile create mode 100644 ld64/unit-tests/test-cases/order_file-ans/main.cxx create mode 100644 ld64/unit-tests/test-cases/order_file-ans/main.expected create mode 100644 ld64/unit-tests/test-cases/order_file-ans/main.order create mode 100644 ld64/unit-tests/test-cases/order_file/Makefile create mode 100644 ld64/unit-tests/test-cases/order_file/extra.s create mode 100644 ld64/unit-tests/test-cases/order_file/main.c create mode 100644 ld64/unit-tests/test-cases/order_file/main1.expected create mode 100644 ld64/unit-tests/test-cases/order_file/main1.order create mode 100644 ld64/unit-tests/test-cases/order_file/main2.expected create mode 100644 ld64/unit-tests/test-cases/order_file/main2.order create mode 100644 ld64/unit-tests/test-cases/order_file/main3.expected create mode 100644 ld64/unit-tests/test-cases/order_file/main3.order create mode 100644 ld64/unit-tests/test-cases/prebound-main/Makefile create mode 100644 ld64/unit-tests/test-cases/prebound-main/main.c create mode 100644 ld64/unit-tests/test-cases/prebound-split-seg/Makefile create mode 100644 ld64/unit-tests/test-cases/prebound-split-seg/address_table create mode 100644 ld64/unit-tests/test-cases/prebound-split-seg/bar.c create mode 100644 ld64/unit-tests/test-cases/private-non-lazy/Makefile create mode 100644 ld64/unit-tests/test-cases/private-non-lazy/bar.c create mode 100644 ld64/unit-tests/test-cases/private-non-lazy/comment.txt create mode 100644 ld64/unit-tests/test-cases/private-non-lazy/foo.c create mode 100644 ld64/unit-tests/test-cases/private-non-lazy/hello.c create mode 100644 ld64/unit-tests/test-cases/re-export-cases/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-cases/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-cases/baz.c create mode 100644 ld64/unit-tests/test-cases/re-export-cases/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-flag/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-flag/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-flag/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations/main.c create mode 100644 ld64/unit-tests/test-cases/re-export-relative-paths/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-relative-paths/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-relative-paths/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-relative-paths/main.c create mode 100644 ld64/unit-tests/test-cases/re-export-relative-paths/wrap.c create mode 100644 ld64/unit-tests/test-cases/read-only-relocs/Makefile create mode 100644 ld64/unit-tests/test-cases/read-only-relocs/foo.c create mode 100644 ld64/unit-tests/test-cases/read-only-relocs/test.c create mode 100644 ld64/unit-tests/test-cases/rebase-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/rebase-basic/bar.m create mode 100644 ld64/unit-tests/test-cases/rebase-basic/comment.txt create mode 100644 ld64/unit-tests/test-cases/rebase-basic/foo.c create mode 100644 ld64/unit-tests/test-cases/relocs-asm/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-asm/comment.txt create mode 100644 ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s create mode 100644 ld64/unit-tests/test-cases/relocs-c/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-c/test.c create mode 100644 ld64/unit-tests/test-cases/relocs-c2/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-c2/comment.txt create mode 100644 ld64/unit-tests/test-cases/relocs-c2/test.c create mode 100644 ld64/unit-tests/test-cases/relocs-literals/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-literals/test.c create mode 100644 ld64/unit-tests/test-cases/relocs-literals2/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-literals2/test.c create mode 100644 ld64/unit-tests/test-cases/relocs-literals3/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-literals3/comment.txt create mode 100644 ld64/unit-tests/test-cases/relocs-literals3/test.c create mode 100644 ld64/unit-tests/test-cases/relocs-objc/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-objc/comment.txt create mode 100644 ld64/unit-tests/test-cases/relocs-objc/test.m create mode 100644 ld64/unit-tests/test-cases/segment-order/Makefile create mode 100644 ld64/unit-tests/test-cases/segment-order/expected.order create mode 100644 ld64/unit-tests/test-cases/segment-order/main.c create mode 100644 ld64/unit-tests/test-cases/segment-order/segJJJ.s create mode 100644 ld64/unit-tests/test-cases/segment-order/segKKK.s create mode 100644 ld64/unit-tests/test-cases/segment-order/segLLL.s create mode 100644 ld64/unit-tests/test-cases/slow-x86-stubs/Makefile create mode 100644 ld64/unit-tests/test-cases/slow-x86-stubs/hello.c create mode 100644 ld64/unit-tests/test-cases/special-labels/Makefile create mode 100644 ld64/unit-tests/test-cases/special-labels/extra.s create mode 100644 ld64/unit-tests/test-cases/special-labels/main.c create mode 100644 ld64/unit-tests/test-cases/stabs-coalesce/Makefile create mode 100644 ld64/unit-tests/test-cases/stabs-coalesce/comment.txt create mode 100644 ld64/unit-tests/test-cases/stabs-coalesce/header.h create mode 100644 ld64/unit-tests/test-cases/stabs-coalesce/hello.cxx create mode 100644 ld64/unit-tests/test-cases/stabs-coalesce/other.cxx create mode 100644 ld64/unit-tests/test-cases/stabs-directory-slash/Makefile create mode 100644 ld64/unit-tests/test-cases/stabs-directory-slash/main.c create mode 100644 ld64/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/stack_addr_no_size/comment.txt create mode 100644 ld64/unit-tests/test-cases/stack_addr_no_size/main.c create mode 100644 ld64/unit-tests/test-cases/stack_addr_size/Makefile create mode 100644 ld64/unit-tests/test-cases/stack_addr_size/comment.txt create mode 100644 ld64/unit-tests/test-cases/stack_addr_size/main.c create mode 100644 ld64/unit-tests/test-cases/stack_size_no_addr/Makefile create mode 100644 ld64/unit-tests/test-cases/stack_size_no_addr/comment.txt create mode 100644 ld64/unit-tests/test-cases/stack_size_no_addr/main.c create mode 100644 ld64/unit-tests/test-cases/static-executable/Makefile create mode 100644 ld64/unit-tests/test-cases/static-executable/test.c create mode 100644 ld64/unit-tests/test-cases/static-strip/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/static-strip/comment.txt create mode 100644 ld64/unit-tests/test-cases/static-strip/test.c create mode 100644 ld64/unit-tests/test-cases/strip-test2/Makefile create mode 100644 ld64/unit-tests/test-cases/strip-test2/comment.txt create mode 100644 ld64/unit-tests/test-cases/strip-test2/main.cxx create mode 100644 ld64/unit-tests/test-cases/strip-test3/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/strip-test3/comment.txt create mode 100644 ld64/unit-tests/test-cases/strip-test3/main.cxx create mode 100644 ld64/unit-tests/test-cases/strip_local/Makefile create mode 100644 ld64/unit-tests/test-cases/strip_local/foo.c create mode 100644 ld64/unit-tests/test-cases/strip_local/hello.c create mode 100644 ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile create mode 100644 ld64/unit-tests/test-cases/stripped-indirect-symbol-table/a.c create mode 100644 ld64/unit-tests/test-cases/stripped-indirect-symbol-table/b.c create mode 100644 ld64/unit-tests/test-cases/stripped-indirect-symbol-table/c.c create mode 100644 ld64/unit-tests/test-cases/stripped-indirect-symbol-table/func.c create mode 100644 ld64/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list create mode 100644 ld64/unit-tests/test-cases/stub-generation-weak/Makefile create mode 100644 ld64/unit-tests/test-cases/stub-generation-weak/foo.c create mode 100644 ld64/unit-tests/test-cases/stub-generation-weak/main.c create mode 100644 ld64/unit-tests/test-cases/stub-generation/Makefile create mode 100644 ld64/unit-tests/test-cases/stub-generation/test.c create mode 100644 ld64/unit-tests/test-cases/switch-jump-table/Makefile create mode 100644 ld64/unit-tests/test-cases/switch-jump-table/interpose.exp create mode 100644 ld64/unit-tests/test-cases/switch-jump-table/main.c create mode 100644 ld64/unit-tests/test-cases/switch-jump-table/switch.s create mode 100644 ld64/unit-tests/test-cases/symbol-moving/Makefile create mode 100644 ld64/unit-tests/test-cases/symbol-moving/aaa.c create mode 100644 ld64/unit-tests/test-cases/symbol-moving/anotb.c create mode 100644 ld64/unit-tests/test-cases/symbol-moving/bar.c create mode 100644 ld64/unit-tests/test-cases/symbol-moving/bbb.c create mode 100644 ld64/unit-tests/test-cases/symbol-moving/bnota.c create mode 100644 ld64/unit-tests/test-cases/symbol-moving/foo.c create mode 100644 ld64/unit-tests/test-cases/symbol-moving/main.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive/foo.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive/main.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-and-dylib/bar.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-dylib/main.c create mode 100644 ld64/unit-tests/test-cases/tentative-to-real-hidden/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-to-real-hidden/test.c create mode 100644 ld64/unit-tests/test-cases/tentative-to-real/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-to-real/comment.txt create mode 100644 ld64/unit-tests/test-cases/tentative-to-real/test.c create mode 100644 ld64/unit-tests/test-cases/thumb-blx/Makefile create mode 100644 ld64/unit-tests/test-cases/thumb-blx/test.c create mode 100644 ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile create mode 100644 ld64/unit-tests/test-cases/undefined-dynamic-lookup/main.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning/Makefile create mode 100644 ld64/unit-tests/test-cases/visibility-warning/foo.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning/foo_hidden.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning/foo_weak.c create mode 100644 ld64/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c create mode 100644 ld64/unit-tests/test-cases/weak-def-ordinal/Makefile create mode 100644 ld64/unit-tests/test-cases/weak-def-ordinal/bar.c create mode 100644 ld64/unit-tests/test-cases/weak-def-ordinal/foo.c create mode 100644 ld64/unit-tests/test-cases/weak-def-ordinal/main.c create mode 100644 ld64/unit-tests/test-cases/weak_dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_dylib/bar.c create mode 100644 ld64/unit-tests/test-cases/weak_dylib/bar.h create mode 100644 ld64/unit-tests/test-cases/weak_dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_dylib/foo.h create mode 100644 ld64/unit-tests/test-cases/weak_dylib/main.c create mode 100644 ld64/unit-tests/test-cases/weak_import/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import/foo.h create mode 100644 ld64/unit-tests/test-cases/weak_import/main.c create mode 100644 ld64/unit-tests/test-cases/weak_import2/Makefile.newtest create mode 100644 ld64/unit-tests/test-cases/weak_import2/comment.txt create mode 100644 ld64/unit-tests/test-cases/weak_import2/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import2/foo.h create mode 100644 ld64/unit-tests/test-cases/weak_import2/foo1.c create mode 100644 ld64/unit-tests/test-cases/weak_import2/main.c create mode 100644 ld64/unit-tests/test-cases/weak_import3/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import3/comment.txt create mode 100644 ld64/unit-tests/test-cases/weak_import3/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import3/foo.h create mode 100644 ld64/unit-tests/test-cases/weak_import3/foo1.c create mode 100644 ld64/unit-tests/test-cases/weak_import3/main.c create mode 100644 ld64/unit-tests/test-cases/why_live/Makefile create mode 100644 ld64/unit-tests/test-cases/why_live/bar.c create mode 100644 ld64/unit-tests/test-cases/why_live/foo.c create mode 100644 ld64/unit-tests/test-cases/why_live/main.c create mode 100644 ld64/unit-tests/test-cases/zero-fill/Makefile create mode 100644 ld64/unit-tests/test-cases/zero-fill/test.c create mode 100644 ld64/unit-tests/test-cases/zero-fill2/Makefile create mode 100644 ld64/unit-tests/test-cases/zero-fill2/comment.txt create mode 100644 ld64/unit-tests/test-cases/zero-fill2/test.c create mode 100644 ld64/unit-tests/test-cases/zero-fill3/Makefile create mode 100644 ld64/unit-tests/test-cases/zero-fill3/comment.txt create mode 100644 ld64/unit-tests/test-cases/zero-fill3/test.c diff --git a/ld64/APPLE_LICENSE b/ld64/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/ld64/APPLE_LICENSE @@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +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." diff --git a/ld64/ChangeLog b/ld64/ChangeLog new file mode 100644 index 0000000..1a79a07 --- /dev/null +++ b/ld64/ChangeLog @@ -0,0 +1,542 @@ + +2008-07-10 Nick Kledzik + + * src/LTOReader.hpp: improve missing symbol error message + + +2008-07-08 Nick Kledzik + + ld: add support for mllvm LTO options + * src/Options.cpp: support -mllvm option + * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options + * src/ld.cpp: pass llvmOptions to optimize() + * src/Options.h: add fLLVMOptions + * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() + * src/ObjectFile.h: add llvmOptions parameter to optimize() + * unit-tests/test-cases/lto-llvm-options: add test case + + +2008-06-04 Nick Kledzik + + * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + +2008-06-04 Nick Kledzik + + * src/ObjectFile.h: add deadAtoms parameter to optimize() + * src/ld.cpp: ditto + * src/ArchiveReader.hpp: ditto + * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs + * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away + * unit-tests/test-cases/lto-weak-native-override: add test case + + +2008-06-04 Nick Kledzik + + LTO : 176.gcc and 177.mesa build failure at -O4 + * src/LTOReader.hpp: make sure internal is returned by getAtoms() + * unit-tests/test-cases/lto-archive-dylib: update test case + + +2008-05-06 Nick Kledzik + + ARM ld should take W bit off of maxprot for __TEXT segment + * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments + + +2008-05-06 Nick Kledzik + + encryptable images may not be signable + * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section + + +----- Tagged ld64-85 (Xcode 3.1) + +2008-04-29 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include + + +2008-04-29 Nick Kledzik + + ld doesn't honor "rightmost" -syslibroot argument + * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + + +2008-04-29 Nick Kledzik + + GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files + * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment + * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment + + +2008-04-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better cpu subtype support + + +2008-04-14 Nick Kledzik + + ld64 has bad ARM branch island check + * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail + + +2008-04-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs + + +----- Tagged ld64-84.4 + +2008-04-10 Nick Kledzik + + SPEC2000/eon built with -mdynamic-no-pic won't run + * src/Architectures.hpp: added arm::kReadOnlyPointer + * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer + * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer + * src/machochecker.cpp: allow MH_PIE bit + * unit-tests/test-cases/switch-jump-table: added test cases + + +----- Tagged ld64-84.3 + +2008-04-09 Nick Kledzik + + -undefined dynamic_lookup busted + * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates + * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup + + +----- Tagged ld64-84.2 + +2008-04-04 Nick Kledzik + + * src/ld.cpp: don't add .eh symbols to symbol table in -r mode + * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing + + +----- Tagged ld64-84.1 + +2008-03-28 Nick Kledzik + + ld should prefer architecture-specific variant over generic in fat object file + * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture + * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files + * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc + + +----- Tagged ld64-84 + +2008-03-28 Nick Kledzik + + * src/LTOReader.hpp: don't print lto version, if lto is unavailable + + +2008-03-26 Nick Kledzik + + Add LD_WARN_COMMONS to BigBear builds + * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file + + +2008-03-26 Nick Kledzik + + Need encryption tag in mach-o file + linker should adjust arm final linked images so __text is never on the same page as the load commands + * src/MachOFileAbstraction.hpp: add support for encryption_info_command + * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption + * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom + * src/machochecker.cpp: validate LC_ENCRYPTION_INFO + + +2008-03-25 Nick Kledzik + + ld64 does not recognize LLVM bitcode archive files + * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp + * src/ArchiveReader.hpp: sniff each member and instantiate correct reader + * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader + * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp + * unit-tests/test-cases/llvm-integration: added test case + + +2008-03-25 Nick Kledzik + + ld64 should switch to new libLTO.dylib interface + Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc + * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface + * unit-tests/test-cases/llvm-integration: update and comment + * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib + * src/ld.cpp: rework and simplify Linker::optimize() + * src/ObjectDump.cpp: Add -nm option + + +2008-03-25 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem + * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem + + +2008-03-24 Nick Kledzik + + Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 + * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. + + +2008-03-21 Nick Kledzik + + * src/Options.cpp: warn if -seg1addr value is not page aligned + + +2008-03-21 Nick Kledzik + + Move ARM support outside of __OPEN_SOURCE__ + * src/ld.cpp: remove __OPEN_SOURCE__ around arm support + * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support + * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check + * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support + * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h + + +----- Tagged ld64-83.2 + +2008-03-15 Nick Kledzik + + ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results + * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files + * unit-tests/test-cases/objc-exported_symbols_list: added test case + + +----- Tagged ld64-83.1 + +2008-03-14 Nick Kledzik + + -iphone_version_min ==> -iphoneos_version_min + * src/Options.cpp: support -iphoneos_version_min as well + + +----- Tagged ld64-83 + +2008-03-10 Nick Kledzik + + ld needs to strip iphone_version_min option if invoking ld_classic + * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic + + +2008-03-04 Nick Kledzik + + ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) + * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs + * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework + * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools + * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() + * src/ld.cpp: pass lazy helper atom to writer + * doc/man/man1/ld.1: document new options + * unit-tests/test-cases/lazy-dylib-objc: add test case + * unit-tests/test-cases/lazy-dylib: add test case + + +----- Tagged ld64-82.7 + +2008-03-07 Nick Kledzik + + duplicate symbol literal-pointer@__OBJC@__message_refs@... + * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak + * unit-tests/test-cases/objc-selector-coalescing: added test case + + +----- Tagged ld64-82.6 + +2008-03-04 Nick Kledzik + + ld crashes building XsanFS for Snow Leopard Builds + * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() + * unit-tests/test-cases/tentative-and-archive: added test case + +2008-03-04 Nick Kledzik + + ld64 should not force building with gcc 4.0 + * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 + + +2008-02-29 Nick Kledzik + + Simulator frameworks are being build split-seg and not prebound + * src/Options.cpp: only splitseg if prebound + + +2008-02-29 Nick Kledzik + + Linker should not make GSYM debug note for .objc_category_* symbols + * src/ld.cpp: suppress GSYM debug notes for absolute symbols + * unit-tests/test-cases/objc-category-debug-notes: added test case + + +2008-02-29 Nick Kledzik + + non-ASCII CFString support is broken + * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring + * unit-tests/test-cases/cfstring-utf16: add test case + + +2008-02-25 Nick Kledzik + + ld -r -x + * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels + + +----- Tagged ld64-82.5 + +2008-02-12 Nick Kledzik + + x86_64: -stack_size failure when large __bss is used + * src/ld.cpp: only move section already in __DATA segment to new __huge section + * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section + + +----- Tagged ld64-82.4 + +2008-02-06 Nick Kledzik + + comdat warnings with ld -r of C++ .o files + * unit-tests/test-cases/eh-coalescing-r: added test case + * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static + + +2008-02-06 Devang Patel + + LTO of Bom framework with -dead_strip causes ld(1) crash + * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. + * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. + * unit-tests/test-cases/llvm-integration/a15.c: New. + * unit-tests/test-cases/llvm-integration/b15.c: New. + * unit-tests/test-cases/llvm-integration/c15.c: New. + +2008-02-05 Nick Kledzik + + * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used + +----- Tagged ld64-82.3 + +2008-02-04 Nick Kledzik + + ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves + * src/ObjectFile.h: add 10.6 + * src/Options.cpp: add 10.6 support + * src/MachOReaderDylib.hpp: recognize $os10.6$ + + +----- Tagged ld64-82.2 + +2008-01-30 Devang Patel + + Can't build 64-bit Intel binaries with LTO + ld64 fails to build with llvm-gcc-4.2 + * src/LLVMReader.hpp: Fix character count typo in strncmp call. + Use const char * to initialize temp. string. + * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction + instead of hard coding /Developer. + +----- Tagged ld64-82.1 + +2008-01-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs + + +2008-01-22 Nick Kledzik + + ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files + * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs + * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs + * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files + + +----- Tagged ld64-82 + +2008-01-18 Nick Kledzik + + Bad grammar used in ld warning: cannot exported hidden symbol + * src/ld.cpp: fix typo in warning string + + +2008-01-16 Nick Kledzik + + Bundle Loader does not work anymore when loader is a bundle + ld warns of incorrect architecture when linking a bundle to a bundle + * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages + * unit-tests/test-cases/bundle_loader: update test case + + +2008-01-16 Nick Kledzik + + ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) + * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S + + +2008-01-16 Nick Kledzik + + if ld crashes while writing output file, it should delete the half written file + * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete + output file on failure. + + +2008-01-16 Devang Patel + + * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. + + +2008-01-16 Nick Kledzik + + GC-supported library can't be linked into GC-required executable + * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and + allow gc-compatible code to be linked into anything. + * unit-tests/test-cases/objc-gc-checks: update test case + + +2008-01-15 Nick Kledzik + + no debug notes for custom named data + * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore + * unit-tests/test-cases/dwarf-debug-notes: update test case + +----- Tagged ld64-81.5 + +2008-01-14 Devang Patel + + llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 + * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references + after optimization. + * src/ld.cpp: Resolve additional unbounded references after optimization. + + +2008-01-14 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes + * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs + * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs + + +2008-01-11 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" + * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing + * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions + + +2008-01-11 Nick Kledzik + + * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list + + +2008-01-11 Nick Kledzik + + ld64(1) man page uses ambiguous term "suffix" + * doc/man/man1/ld.1: make meaning of "suffix" more explicit + + +2008-01-11 Nick Kledzik + + Obj-C Symbols in Leopard Can't Be Weak Linked + * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines + to dylibs to support Mac OS X 10.3.x dyld + + +2008-01-11 Nick Kledzik + + Unknown error with linker (dyld: unknown external relocation type) + * src/ld.cpp: fix crash when SO stabs are not balanced + + +2008-01-11 Devang Patel + + LTO does not work if expected output is a dynamic library + * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate + visibility info. + +2000-01-10 Nick Kledzik + + __cls_refs section is losing S_LITERAL_POINTERS section type + * src/MachOWriterExecutable.hpp: special case __cls_refs section + * unit-tests/test-cases/objc-literal-pointers: add test case + + +2008-01-03 Nick Kledzik + + wrong EH information might be used + Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom + has kGroupSubordinate references to the other atoms in the group. If the signature atom + is coalesced away, the linker follows kGroupSubordinate references and throws away the + other members of the group. + * unit-tests/test-cases/eh-coalescing: added test case + * src/ld.cpp: added markDead() and use propagate to subordinates + * src/Architectures.hpp: added kGroupSubordinate + * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom + and if used, from .eh atom to its LSDA atom. + * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp + +----- Tagged ld64-81.4.1 + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. + * src/Options.cpp: Use printLLVMVersion() in verbose mode. + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/Options.h: Add verbose() method to check fVerbose flag. + * src/LLVMReader.hpp: Print LLVM version string in verbose mode. + +----- Tagged ld64-81.4 + +2007-12-18 Devang Patel + + * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. + +----- Tagged ld64-81.3 + +2007-12-17 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths + + +2007-12-17 Devang Patel + + * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to + dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. + + +2007-12-14 Nick Kledzik + + gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) + * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs + * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static + + +2007-12-14 Devang Patel + + Enable Link Time Optimization in Opal + * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. + * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. + * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. + * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. + + +2007-12-13 Nick Kledzik + + SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... + * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly + +----- Tagged ld64-81.2 + diff --git a/ld64/FireOpal/APPLE_LICENSE b/ld64/FireOpal/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/ld64/FireOpal/APPLE_LICENSE @@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +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." diff --git a/ld64/FireOpal/ChangeLog b/ld64/FireOpal/ChangeLog new file mode 100644 index 0000000..1a79a07 --- /dev/null +++ b/ld64/FireOpal/ChangeLog @@ -0,0 +1,542 @@ + +2008-07-10 Nick Kledzik + + * src/LTOReader.hpp: improve missing symbol error message + + +2008-07-08 Nick Kledzik + + ld: add support for mllvm LTO options + * src/Options.cpp: support -mllvm option + * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options + * src/ld.cpp: pass llvmOptions to optimize() + * src/Options.h: add fLLVMOptions + * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() + * src/ObjectFile.h: add llvmOptions parameter to optimize() + * unit-tests/test-cases/lto-llvm-options: add test case + + +2008-06-04 Nick Kledzik + + * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + +2008-06-04 Nick Kledzik + + * src/ObjectFile.h: add deadAtoms parameter to optimize() + * src/ld.cpp: ditto + * src/ArchiveReader.hpp: ditto + * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs + * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away + * unit-tests/test-cases/lto-weak-native-override: add test case + + +2008-06-04 Nick Kledzik + + LTO : 176.gcc and 177.mesa build failure at -O4 + * src/LTOReader.hpp: make sure internal is returned by getAtoms() + * unit-tests/test-cases/lto-archive-dylib: update test case + + +2008-05-06 Nick Kledzik + + ARM ld should take W bit off of maxprot for __TEXT segment + * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments + + +2008-05-06 Nick Kledzik + + encryptable images may not be signable + * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section + + +----- Tagged ld64-85 (Xcode 3.1) + +2008-04-29 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include + + +2008-04-29 Nick Kledzik + + ld doesn't honor "rightmost" -syslibroot argument + * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + + +2008-04-29 Nick Kledzik + + GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files + * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment + * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment + + +2008-04-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better cpu subtype support + + +2008-04-14 Nick Kledzik + + ld64 has bad ARM branch island check + * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail + + +2008-04-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs + + +----- Tagged ld64-84.4 + +2008-04-10 Nick Kledzik + + SPEC2000/eon built with -mdynamic-no-pic won't run + * src/Architectures.hpp: added arm::kReadOnlyPointer + * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer + * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer + * src/machochecker.cpp: allow MH_PIE bit + * unit-tests/test-cases/switch-jump-table: added test cases + + +----- Tagged ld64-84.3 + +2008-04-09 Nick Kledzik + + -undefined dynamic_lookup busted + * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates + * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup + + +----- Tagged ld64-84.2 + +2008-04-04 Nick Kledzik + + * src/ld.cpp: don't add .eh symbols to symbol table in -r mode + * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing + + +----- Tagged ld64-84.1 + +2008-03-28 Nick Kledzik + + ld should prefer architecture-specific variant over generic in fat object file + * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture + * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files + * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc + + +----- Tagged ld64-84 + +2008-03-28 Nick Kledzik + + * src/LTOReader.hpp: don't print lto version, if lto is unavailable + + +2008-03-26 Nick Kledzik + + Add LD_WARN_COMMONS to BigBear builds + * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file + + +2008-03-26 Nick Kledzik + + Need encryption tag in mach-o file + linker should adjust arm final linked images so __text is never on the same page as the load commands + * src/MachOFileAbstraction.hpp: add support for encryption_info_command + * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption + * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom + * src/machochecker.cpp: validate LC_ENCRYPTION_INFO + + +2008-03-25 Nick Kledzik + + ld64 does not recognize LLVM bitcode archive files + * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp + * src/ArchiveReader.hpp: sniff each member and instantiate correct reader + * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader + * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp + * unit-tests/test-cases/llvm-integration: added test case + + +2008-03-25 Nick Kledzik + + ld64 should switch to new libLTO.dylib interface + Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc + * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface + * unit-tests/test-cases/llvm-integration: update and comment + * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib + * src/ld.cpp: rework and simplify Linker::optimize() + * src/ObjectDump.cpp: Add -nm option + + +2008-03-25 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem + * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem + + +2008-03-24 Nick Kledzik + + Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 + * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. + + +2008-03-21 Nick Kledzik + + * src/Options.cpp: warn if -seg1addr value is not page aligned + + +2008-03-21 Nick Kledzik + + Move ARM support outside of __OPEN_SOURCE__ + * src/ld.cpp: remove __OPEN_SOURCE__ around arm support + * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support + * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check + * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support + * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h + + +----- Tagged ld64-83.2 + +2008-03-15 Nick Kledzik + + ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results + * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files + * unit-tests/test-cases/objc-exported_symbols_list: added test case + + +----- Tagged ld64-83.1 + +2008-03-14 Nick Kledzik + + -iphone_version_min ==> -iphoneos_version_min + * src/Options.cpp: support -iphoneos_version_min as well + + +----- Tagged ld64-83 + +2008-03-10 Nick Kledzik + + ld needs to strip iphone_version_min option if invoking ld_classic + * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic + + +2008-03-04 Nick Kledzik + + ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) + * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs + * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework + * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools + * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() + * src/ld.cpp: pass lazy helper atom to writer + * doc/man/man1/ld.1: document new options + * unit-tests/test-cases/lazy-dylib-objc: add test case + * unit-tests/test-cases/lazy-dylib: add test case + + +----- Tagged ld64-82.7 + +2008-03-07 Nick Kledzik + + duplicate symbol literal-pointer@__OBJC@__message_refs@... + * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak + * unit-tests/test-cases/objc-selector-coalescing: added test case + + +----- Tagged ld64-82.6 + +2008-03-04 Nick Kledzik + + ld crashes building XsanFS for Snow Leopard Builds + * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() + * unit-tests/test-cases/tentative-and-archive: added test case + +2008-03-04 Nick Kledzik + + ld64 should not force building with gcc 4.0 + * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 + + +2008-02-29 Nick Kledzik + + Simulator frameworks are being build split-seg and not prebound + * src/Options.cpp: only splitseg if prebound + + +2008-02-29 Nick Kledzik + + Linker should not make GSYM debug note for .objc_category_* symbols + * src/ld.cpp: suppress GSYM debug notes for absolute symbols + * unit-tests/test-cases/objc-category-debug-notes: added test case + + +2008-02-29 Nick Kledzik + + non-ASCII CFString support is broken + * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring + * unit-tests/test-cases/cfstring-utf16: add test case + + +2008-02-25 Nick Kledzik + + ld -r -x + * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels + + +----- Tagged ld64-82.5 + +2008-02-12 Nick Kledzik + + x86_64: -stack_size failure when large __bss is used + * src/ld.cpp: only move section already in __DATA segment to new __huge section + * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section + + +----- Tagged ld64-82.4 + +2008-02-06 Nick Kledzik + + comdat warnings with ld -r of C++ .o files + * unit-tests/test-cases/eh-coalescing-r: added test case + * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static + + +2008-02-06 Devang Patel + + LTO of Bom framework with -dead_strip causes ld(1) crash + * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. + * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. + * unit-tests/test-cases/llvm-integration/a15.c: New. + * unit-tests/test-cases/llvm-integration/b15.c: New. + * unit-tests/test-cases/llvm-integration/c15.c: New. + +2008-02-05 Nick Kledzik + + * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used + +----- Tagged ld64-82.3 + +2008-02-04 Nick Kledzik + + ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves + * src/ObjectFile.h: add 10.6 + * src/Options.cpp: add 10.6 support + * src/MachOReaderDylib.hpp: recognize $os10.6$ + + +----- Tagged ld64-82.2 + +2008-01-30 Devang Patel + + Can't build 64-bit Intel binaries with LTO + ld64 fails to build with llvm-gcc-4.2 + * src/LLVMReader.hpp: Fix character count typo in strncmp call. + Use const char * to initialize temp. string. + * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction + instead of hard coding /Developer. + +----- Tagged ld64-82.1 + +2008-01-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs + + +2008-01-22 Nick Kledzik + + ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files + * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs + * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs + * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files + + +----- Tagged ld64-82 + +2008-01-18 Nick Kledzik + + Bad grammar used in ld warning: cannot exported hidden symbol + * src/ld.cpp: fix typo in warning string + + +2008-01-16 Nick Kledzik + + Bundle Loader does not work anymore when loader is a bundle + ld warns of incorrect architecture when linking a bundle to a bundle + * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages + * unit-tests/test-cases/bundle_loader: update test case + + +2008-01-16 Nick Kledzik + + ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) + * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S + + +2008-01-16 Nick Kledzik + + if ld crashes while writing output file, it should delete the half written file + * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete + output file on failure. + + +2008-01-16 Devang Patel + + * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. + + +2008-01-16 Nick Kledzik + + GC-supported library can't be linked into GC-required executable + * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and + allow gc-compatible code to be linked into anything. + * unit-tests/test-cases/objc-gc-checks: update test case + + +2008-01-15 Nick Kledzik + + no debug notes for custom named data + * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore + * unit-tests/test-cases/dwarf-debug-notes: update test case + +----- Tagged ld64-81.5 + +2008-01-14 Devang Patel + + llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 + * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references + after optimization. + * src/ld.cpp: Resolve additional unbounded references after optimization. + + +2008-01-14 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes + * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs + * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs + + +2008-01-11 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" + * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing + * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions + + +2008-01-11 Nick Kledzik + + * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list + + +2008-01-11 Nick Kledzik + + ld64(1) man page uses ambiguous term "suffix" + * doc/man/man1/ld.1: make meaning of "suffix" more explicit + + +2008-01-11 Nick Kledzik + + Obj-C Symbols in Leopard Can't Be Weak Linked + * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines + to dylibs to support Mac OS X 10.3.x dyld + + +2008-01-11 Nick Kledzik + + Unknown error with linker (dyld: unknown external relocation type) + * src/ld.cpp: fix crash when SO stabs are not balanced + + +2008-01-11 Devang Patel + + LTO does not work if expected output is a dynamic library + * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate + visibility info. + +2000-01-10 Nick Kledzik + + __cls_refs section is losing S_LITERAL_POINTERS section type + * src/MachOWriterExecutable.hpp: special case __cls_refs section + * unit-tests/test-cases/objc-literal-pointers: add test case + + +2008-01-03 Nick Kledzik + + wrong EH information might be used + Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom + has kGroupSubordinate references to the other atoms in the group. If the signature atom + is coalesced away, the linker follows kGroupSubordinate references and throws away the + other members of the group. + * unit-tests/test-cases/eh-coalescing: added test case + * src/ld.cpp: added markDead() and use propagate to subordinates + * src/Architectures.hpp: added kGroupSubordinate + * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom + and if used, from .eh atom to its LSDA atom. + * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp + +----- Tagged ld64-81.4.1 + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. + * src/Options.cpp: Use printLLVMVersion() in verbose mode. + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/Options.h: Add verbose() method to check fVerbose flag. + * src/LLVMReader.hpp: Print LLVM version string in verbose mode. + +----- Tagged ld64-81.4 + +2007-12-18 Devang Patel + + * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. + +----- Tagged ld64-81.3 + +2007-12-17 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths + + +2007-12-17 Devang Patel + + * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to + dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. + + +2007-12-14 Nick Kledzik + + gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) + * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs + * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static + + +2007-12-14 Devang Patel + + Enable Link Time Optimization in Opal + * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. + * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. + * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. + * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. + + +2007-12-13 Nick Kledzik + + SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... + * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly + +----- Tagged ld64-81.2 + diff --git a/ld64/FireOpal/doc/man/man1/ld.1 b/ld64/FireOpal/doc/man/man1/ld.1 new file mode 100644 index 0000000..a8b0188 --- /dev/null +++ b/ld64/FireOpal/doc/man/man1/ld.1 @@ -0,0 +1,676 @@ +.Dd December 8, 2006 +.Dt ld 1 +.Os Darwin +.Sh NAME +.Nm ld +.Nd "linker" +.Sh SYNOPSIS +.Nm +files... +.Op options +.Op Fl o Ar outputfile +.Sh DESCRIPTION +The +.Nm ld +command combines several object files and libraries, resolves references, and +produces an ouput file. +.Nm ld +can produce a final linked image (executable, dylib, or bundle), or with the -r +option, produce another object file. If the -o option is not used, the output +file produced is named "a.out". +.Ss Universal +The linker accepts universal (multiple-architecture) input files, but +always creates a "thin" (single-architecture), standard Mach-O output file. +The architecture for the output file is specified using the -arch option. +If this option is not used, +.Nm ld +attempts to determine the output architecture by examining the object +files in command line order. The first "thin" +architecture determines that of the output file. If no input +object file is a "thin" file, the native 32-bit architecture for the host is used. +.Pp +Usually, +.Nm ld +is not used directly. Instead the +.Xr gcc(1) +compiler driver invokes +.Nm ld. +The compiler driver can be passed multiple -arch options and it will create a +universal final linked image by invoking +.Nm ld +multiple times and then running +.Xr lipo(1) +merge the outputs into a universal file. +.Ss Layout +The object files are loaded in the order in which they are specified on the +command line. The segments and the sections in those segments will appear in +the output file in the order they are encountered in the object files being linked. +All zero fill sections will appear after all non-zero fill sections in their segments. +Sections created from files with the -sectcreate option will be laid out at after +sections from .o files. The use of the -order_file option will alter the layout +rules above, and move the symbols specified to start of their section. +.Ss Libraries +A static library (aka static archive) is a collection of .o files with a table of contents +that lists the global symbols in the .o files. +.Nm ld +will only pull .o files out of a static library if needed to resolve some symbol reference. +Unlike traditional linkers, +.Nm ld +will continually search a static library while linking. There is no need to specify a static +library multiple times on the command line. +.Pp +A dynamic library (aka dylib or framework) is a final linked image. Putting a dynamic +library on the command line causes two things: 1) The generated final linked image +will have encoded that it depends on that dynamic library. 2) Exported symbols from the +dynamic library are used to resolve references. +.Pp +Both dynamic and static libraries are searched as they appear on the command line. +.Ss Search paths +.Nm ld +maintains a list of directories to search for a library or framework to use. The default +library search path is /usr/lib then /usr/local/lib. The -L option will add a new library search +path. The default framework search path is /Library/Frameworks then /System/Library/Frameworks. +(Note: previously, /Network/Library/Frameworks was at the end of the default path. If you need +that functionality, you need to explicitly add -F/Network/Library/Frameworks). +The -F option will a new framework search path. The -Z option will remove +the standard search paths. The -syslibroot option will prepend a prefix to all search +paths. +.Ss Two-level namespace +By default all references resolved to a dynamic library record the library to which +they were resolved. At runtime, dyld uses that information to directly resolve +symobls. The alternative is to use the -flat_namespace option. With flat namespace, +the library is not recorded. At runtime, dyld will search each dynamic library in load +order when resolving symbols. This is slower, but more like how other operating systems +resolve symbols. +.Ss Indirect dynamic libraries +If the command line specifies to link against dylib A, and when dylib A was built it linked +against dylib B, then B is considered an indirect dylib. +When linking for two-level namespace, ld does not look at indirect dylibs, except when +re-exported by a direct dylibs. On the other hand when linking for flat namespace, +ld does load all indirect dylibs and uses them to resolve references. +Even though indirect dylibs are specified via a full path, +.Nm ld +first uses the specified search paths to locate each indirect dylib. If one cannot +be found using the search paths, the full path is used. +.Ss Dynamic libraries undefines +When linking for two-level namespace, +.Nm ld +does not verify that undefines in dylibs actually +exist. But when linking for flat namespace, +.Nm ld +does check that all undefines from all loaded dylibs have a matching definition. +This is sometimes used to force selected functions to be loaded from a static library. +.Sh OPTIONS +.Ss Options that control the kind of output +.Bl -tag +.It Fl execute +The default. Produce a mach-o main executable that has file type MH_EXECUTE. +.It Fl dylib +Produce a mach-o shared library that has file type MH_DYLIB. +.It Fl bundle +Produce a mach-o bundle that has file type MH_BUNDLE. +.It Fl r +Merges object files to produce another mach-o object file with file type MH_OBJECT. +.It Fl dylinker +Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when building dyld. +.It Fl dynamic +The default. Implied by -dynamiclib, -bundle, or -execute +.It Fl static +Produces a mach-o file that does not use the dyld. Only used building the kernel. +.It Fl arch Ar arch_name +Specifies which architecture (e.g. ppc, ppc64, i386, x86_64) the output file should be. +.It Fl o Ar path +Specifies the name and location of the output file. If not specified, `a.out' is used. +.El +.Ss Options that control libraries +.Bl -tag +.It Fl l Ns x +This option tells the linker to search for libx.dylib or libx.a in the library search path. +If string x is of the form y.o, then that file is searched for in the same places, but without +prepending `lib' or appending `.a' or `.dylib' to the filename. +.It Fl weak-l Ns Ar x +This is the same as the -lx but forces the library and all references to it to be marked as weak imports. +That is, the library is allowed to be missing at runtime. +.It Fl weak_library Ar path_to_library +This is the same as listing a file name path to a library on the link line except that it forces the +library and all references to it to be marked as weak imports. +.It Fl reexport-l Ns Ar x +This is the same as the -lx but specifies that the all symbols in library x should be available to +clients linking to the library being created. This was previously done with a separate -sub_library option. +.It Fl reexport_library Ar path_to_library +This is the same as listing a file name path to a library on the link line and it specifies that the +all symbols in library path should be available to clients linking to the library being created. +This was previously done with a separate -sub_library option. +.It Fl lazy-l Ns Ar x +This is the same as the -lx but it is only for shared libraries and the linker +will construct glue code so that the shared library is not loaded until +the first function in it is called. +.It Fl lazy_library Ar path_to_library +This is the same as listing a file name path to a shared library on the link line +except that the linker will construct glue code so that the shared library is not +loaded until the first function in it is called. +.It Fl L Ns dir +Add +.Ar dir +to the list of directories in which to search for libraries. +Directories specified with -L are searched in the order they appear on the command line +and before the default search path. +.It Fl Z +Do not search the standard directories when searching for libraries and frameworks. +.It Fl syslibroot Ar rootdir +Prepend +.Ar rootdir +to all search paths when searching for libraries or frameworks. +.It Fl search_paths_first +By default the -lx and -weak-lx options first search for a file of the form `libx.dylib' in each directory +in the library search path, then a file of the form `libx.a' is searched for in the library search paths. +This option changes it so that in each path `libx.dylib' is searched for then `libx.a' before the +next path in the library search path is searched. +.It Fl framework Ar name[,suffix] +This option tells the linker to search for `name.framework/name' the framework search path. +If the optional suffix is specified the framework is first searched for the name with the suffix and then without +(e.g. look for `name.framework/name_suffix' first, if not there try `name.framework/name'). +.It Fl weak_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] but forces the framework and all +references to it to be marked as weak imports. +.It Fl reexport_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] but also specifies that the +all symbols in that framework should be available to clients linking to the library being created. +This was previously done with a separate -sub_umbrella option. +.It Fl lazy_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] except that the linker will +construct glue code so that the framework is not +loaded until the first function in it is called. You cannot directly access +data or Objective-C classes in a frameworked linked this way. +.It Fl F Ns dir +Add +.Ar dir +to the list of directories in which to search for frameworks. +Directories specified with -F are searched in the order they appear on the command line +and before the default search path. +.It Fl all_load +Loads all members of static archive libraries. +.It Fl ObjC +Loads all members of static archive libraries that implement an Objective-C class or category. +.El +.Ss Options that control additional content +.Bl -tag +.It Fl sectcreate Ar segname sectname file +The section +.Ar sectname +in the segment +.Ar segname +is created from the contents of file +.Ar file. +The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname) +from any other input. +.It Fl filelist Ar file[,dirname] +Specifies that the linker should link the files listed in +.Ar file . +This is an alternative to listing the files on the command line. +The file names are listed one per line separated only by newlines. (Spaces and tabs are assumed to be part of the file name.) +If the optional directory name, +.Ar dirname +is specified, it is prepended to each name in the list file. +.It Fl dtrace Ar file +Enables dtrace static probes when producing a final linked image. The file +.Ar file +must be a DTrace script which declares the static probes. +.El +.Ss Options that control optimizations +.Bl -tag +.It Fl dead_strip +Remove functions and data that are unreachable by the entry point or exported symbols. +.It Fl dead_strip_dylibs +Remove dylibs that are unreachable by the entry point or exported symbols. That is, +suppresses the generation of load command commands for dylibs which supplied no +symbols during the link. This option should not be used when linking against a dylib which +is required at runtime for some indirect reason such as the dylib has an important initializer. +.It Fl order_file Ar file +Alters the order in which functions and data are laid out. For each section in the output file, +any symbol in that section that are specified in the order file +.Ar file +is moved to the start of its section and laid out in the same order as in the order file +.Ar file . +Order files are text files with one symbol name per line. Lines starting with a # are comments. +A symbol name may be optionally preceded with its object file leafname and a colon (e.g. foo.o:_foo). +This is useful for static functions/data that occur in multiple files. +A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). +This enables you to have one order file that works for multiple architectures. +Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file. +.It Fl macosx_version_min Ar version +This is set to indicate the oldest Mac OS X version that that the output is to be used on. Specifying +a later version enables the linker to assumes features of that OS in the output file. The format of +.Ar version +is a Mac OS X version number such as 10.4 or 10.5 +.It Fl image_base Ar address +Specifies the perferred load address for a dylib or bundle. The argument +.Ar address +is a hexadecimal number with an optional leading 0x. By choosing non-overlapping address for all +dylibs and bundles that a program loads, launch time can be improved because dyld will not need to +"rebase" the image (that is, adjust pointers within the image to work at the loaded address). +It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs. +It will then choose non-overlapping addresses for the list and rebase them all. +This option is also called -seg1addr for compatibility. +.It Fl no_implicit_dylibs +When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs +that are implicitly linked to make the two-level namespace +encoding more efficient for dyld. For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. +If you link with -framework Cocoa and use a symbol from Foundation, the linker will implicitly add a load +command to load Foundation and encode the symbol as coming from Foundation. If you use this option, +the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then +at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation. +.El +.Ss Options when creating a dynamic library (dylib) +.Bl -tag +.It Fl install_name Ar name +Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library +will record that path as the way dyld should locate this library. If this option is not specified, then +the -o path will be used. This option is also called -dylib_install_name for compatibility. +.It Fl compatibility_version Ar number +Specifies the compatibility version number of the library. When a library is loaded by dyld, the +compatibility version is checked and if the program's version is greater that the library's version, it is an error. +The format of +.Ar number +is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, +and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. +If the compatibility version number is not specified, it has a value of 0 and no checking is done when the library is used. +This option is also called -dylib_compatibility_version for compatibility. +.It Fl current_version Ar number +Specifies the current version number of the library. The current version of the library can be obtained +programmatically by the user of the library so it can determine exactly which version of the library it is using. +The format of +.Ar number +is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, +and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. +If the version number is not specified, it has a value of 0. +This option is also called -dylib_current_version for compatibility. +.El +.Ss Options when creating a main executable +.Bl -tag +.It Fl pie +This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5, the OS +will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled +with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some +security. +.It Fl pagezero_size Ar size +By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence +will cause a bus error if a NULL pointer is dereferenced. The argument +.Ar size +is a hexadecimal number with an optional leading 0x. If +.Ar size +is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size +is 4KB. On 64-bit architectures, the default size if 4GB. The ppc64 architecture has some special cases. Since Mac +OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless +-macosx_version_min is 10.5 or later. Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the +code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero +size is set to 4KB and then a new unredable trailing segment is created after the code, filling up the lower 4GB. +.It Fl stack_size Ar size +Specifies the maximum stack size for the main thread in a program. Without this option a program has a 8MB stack. +The argument +.Ar size +is a hexadecimal number with an optional leading 0x. The +.Ar size +should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. +.It Fl allow_stack_execute +Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. +.El +.Ss Options when creating a bundle +.Bl -tag +.It Fl bundle_loader Ar executable +This specifies the +.Ar executable +that will be loading the bundle output file being linked. +Undefined symbols from the bundle are checked against the specified +.Ar executable +like it was one of the +dynamic libraries the bundle was linked with. +.El +.Ss Options when creating an object file +.Bl -tag +.It Fl keep_private_externs +Don't turn private external (aka visibility=hidden) symbols into static symbols, +but rather leave them as private external in the resulting object file. +.It Fl d +Force definition of common symbols. That is, transform tentative defintions into real definitions. +.El +.Ss Options that control symbol resolution +.Bl -tag +.It Fl exported_symbols_list Ar filename +The specified +.Ar filename +contains a list of global symbol names that will remain as global symbols in the output file. +All other global symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) +and will not be global in the output file. The symbol names listed in filename must be one per line. +Leading and trailing white space are not part of the symbol name. +Lines starting with # are ignored, as are lines with only white space. +Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. +The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches +any single lower case letter from 'a' to 'z'. +.It Fl exported_symbol Ar symbol +The specified +.Ar symbol +is added to the list of global symbols names that will remain as global symbols in the output file. This +option can be used multiple times. For short lists, this can be more convenient than creating a file and using +-exported_symbols_list. +.It Fl unexported_symbols_list Ar file +The specified +.Ar filename +contains a list of global symbol names that will not remain as global symbols in the output file. +The symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) and will not be global +in the output file. The symbol names listed in filename must be one per line. +Leading and trailing white space are not part of the symbol name. +Lines starting with # are ignored, as are lines with only white space. +Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. +The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches +any single lower case letter from 'a' to 'z'. +.It Fl unexported_symbol Ar symbol +The specified +.Ar symbol +is added to the list of global symbols names that will not remain as global symbols in the output file. This +option can be used multiple times. For short lists, this can be more convenient than creating a file and using +-unexported_symbols_list. +.It Fl alias Ar symbol_name Ar alternate_symbol_name +Create an alias named +.Ar alternate_symbol_name +for the symbol +.Ar symbol_name . +By default the alias symbol has global visibility. This option was previous the -idef:indir option. +.It Fl alias_list Ar filename +The specified +.Ar filename +contains a list of aliases. The symbol name and its alias are on one line, separated by whitespace. +Lines starting with # are ignored. +.It Fl flat_namespace +Alters how symbols are resolved at build time and runtime. With -two_levelnamespace (the default), the linker +only searches dylibs on the command line for symbols, and records in which dylib they were found. With -flat_namespace, +the linker searches all dylibs on the command line and all dylibs those original dylibs depend on. The linker +does not record which dylib an external symbol came from, so at runtime dyld again searches all images and uses +the first definition it finds. In addition, any undefines in loaded flat_namespace dylibs must be resolvable +at build time. +.It Fl u Ar symbol_name +Specified that symbol +.Ar symbol_name +must be defined for the link to succeed. This is useful to force selected functions to be loaded +from a static library. +.It Fl U Ar symbol_name +Specified that it is ok for +.Ar symbol_name +to have no definition. With -two_levelnamespace, the resulting symbol will be marked dynamic_lookup which +means dyld will search all loaded images. +.It Fl undefined Ar treatment +Specifies how undefined symbols are to be treated. Options are: error, warning, suppress, or dynamic_lookup. The +default is error. +.It Fl rpath Ar path +Add +.Ar path +to the runpath search path list for image being created. At runtime, dyld uses the runpath when searching +for dylibs whose load path begins with @rpath/. +.It Fl commons Ar treatment +Specifies how commons (aka tentative definitions) are resolved with respect to dylibs. Options are: +ignore_dylibs, use_dylibs, error. The default is ignore_dylibs which means the linker will turn a tentative +definition in an object file into a real definition and not even check dylibs for conflicts. The dylibs +option means the linker should check linked dylibs for definitions and use them to replace tentative definitions +from object files. The error option means the linker should issu an error whenever a tentative definition in an +object file conflicts with an external symbol in a linked dylib. See also -warn_commons. +.El +.Ss Options for introspecting the linker +.Bl -tag +.It Fl why_load +Log why each object file in a static library is loaded. That is, what symbol was needed. Also called -whyload +for compatibility. +.It Fl why_live Ar symbol_name +Logs a chain of references to +.Ar symbol_name . +Only applicable with -dead_strip . +It can help debug why something that you think should be dead strip removed is not removed. +.It Fl print_statistics +Logs information about the amount of memory and time the linker used. +.It Fl t +Logs each file (object, archive, or dylib) the linker loads. Useful for debugging problems with search paths where the wrong library is loaded. +.It Fl whatsloaded +Logs just object files the linker loads. +.It Fl order_file_statistics +Logs information about the processing of a -order_file. +.It Fl map Ar map_file_path +Writes a map file to the specified path which details all symbols and their addresses in the output image. +.El +.Ss Options for controling symbol table optimizations +.Bl -tag +.It Fl S +Do not put debug information (STABS or DWARF) in the output file. +.It Fl x +Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and +getting symbol names in back traces, but are not used at runtime. If -x is used with -r +non-global symbol names are not removed, but instead replaced with a unique, duumy name +that will be automatically removed when linked into a final linked image. This +allows dead code stripping, which uses symbols to break up code and data, to +work properly and provides the security of having source symbol names removed. +.It Fl non_global_symbols_strip_list Ar filename +The specified +.Ar filename +contains a list of non-global symbol names that should be removed from the output file's symbol table. All other +non-global symbol names will remain in the output files symbol table. See -exported_symbols_list for syntax and use +of wildcards. +.It Fl non_global_symbols_no_strip_list Ar filename +The specified +.Ar filename +contains a list of non-global symbol names that should be remain in the output file's symbol table. All other +symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use +of wildcards. +.El +.Ss Rarely used Options +.Bl -tag +.It Fl v +Prints the version of the linker. +.It Fl no_uuid +Do not generate an LC_UUID load command in the output file. +.It Fl root_safe +Sets the MH_ROOT_SAFE bit in the mach header of the output file. +.It Fl setuid_safe +Sets the MH_SETUID_SAFE bit in the mach header of the output file. +.It Fl interposable +Indirects access to all to exported symbols when creating a dynamic library. +.It Fl init Ar symbol_name +The specified symbol_name will be run as the first initializer. Only used when creating a dynamic library. +.It Fl sub_library Ar library_name +The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc. +Only used when creating a dynamic library. +.It Fl sub_umbrella Ar framework_name +The specified framework will be re-exported. Only used when creating a dynamic library. +.It Fl allowable_client Ar name +Restricts what can link against the dynamic library being created. +.It Fl client_name Ar name +Enables a bundle to link against a dylib that was built with -allowable_client. +The name specified must match one of the -allowable_client names specified when the dylib was created. +.It Fl umbrella Ar framework_name +Specifies that the dylib being linked is re-exported through an umbrella framework of the specified name. +.It Fl headerpad Ar size +Specifies the minimum space for future expansion of the load commands. Only useful if intend to run +install_name_tool to alter the load commands later. Size is a hexadecimal number. +.It Fl headerpad_max_install_names +Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. +Only useful if intend to run install_name_tool to alter the load commands later. Size is a hexadecimal number. +.It Fl bind_at_load +Sets a bit in the mach header of the resulting binary which tells dyld to bind all symbols when the binary is loaded, rather than lazily. +.It Fl force_flat_namespace +Sets a bit in the mach header of the resulting binary which tells dyld to not only use flat namespace for the binary, +but force flat namespace binding on all dylibs and bundles loaded in the process. Can only be used when linking main executables. +.It Fl sectalign Ar segname Ar sectname Ar value +The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal +number that must be an integral power of 2. +.It Fl stack_addr Ar address +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to a page boundary. +.It Fl segprot Ar segname Ar max_prot Ar init_prot +Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively. +The values for max and init are any combination of the characters `r' (for read), `w' (for write), `x' (for execute) and `-' (no access). +.It Fl seg_addr_table Ar filename +Specifies a file containing base addresses for dynamic libraries. Each line of the file is a hexadecimal base address +followed by whitespace then the install name of the corresponding dylib. The # character denotes a comment. +.It Fl segs_read_write_addr Ar address +Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address +specified is a hexadecimal number that indicates the base address for the read-write segments. +.It Fl segs_read_only_addr Ar address +Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address +specified is a hexadecimal number that indicates the base address for the read-only segments. +.It Fl segaddr Ar name Ar address +Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number +that is a multiple of 4K page size. +.It Fl dylib_file Ar install_name:file_name +Specifies that a dynamic shared library is in a different location than its standard location. Use this option +when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other +than its default location. install_name specifies the path where the library normally resides. file_name specifies +the path of the library you want to use instead. For example, if you link to a library that depends upon the dynamic +library libsys and you have libsys installed in a nondefault location, you would use this option: +-dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib. +.It Fl prebind +The created output file will be in the prebound format. This was used in Mac OS X 10.3 and earlier to improve launch performance. +.It Fl weak_reference_mismatches Ar treatment +Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another. The valid +treatments are: error, weak, or non-weak. The default is non-weak. +.It Fl read_only_relocs Ar treatment +Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages. The compiler will +normally never generate such code. +.It Fl force_cpusubtype_ALL +The is only applicable with -arch ppc. It tells the linker to ignore the PowerPC cpu requirements (e.g. G3, G4 or G5) encoded +in the object files and mark the resulting binary as runnable on any PowerPC cpu. +.It Fl dylinker_install_name Ar path +Only used when building dyld. +.It Fl no_arch_warnings +Suppresses warning messages about files that have the wrong architecture for the -arch flag +.It Fl arch_errors_fatal +Turns into errors, warnings about files that have the wrong architecture for the -arch flag. +.It Fl e Ar symbol_name +Specifies the entry point of a main executable. By default the entry name is "start" which is found in crt1.o which contains +the glue code need to set up and call main(). +.It Fl w +Suppress all warning messages +.It Fl final_output Ar name +Specifies the install name of a dylib if -install_name is not used. This option is used by gcc driver when it is invoked +with multiple -arch arguments. +.It Fl arch_multiple +Specifes that the linker should augment error and warning messages with the architecture name. This option is used by gcc +driver when it is invoked with multiple -arch arguments. +.It Fl twolevel_namespace_hints +Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the +libraries being linked against have not changed. +.It Fl dot Ar path +Create a file a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. +.It Fl keep_relocs +Add section based relocation records to a final linked image. These relocations are ignored at runtime by dyld. +.It Fl warn_stabs +Print a warning when the linker cannot do a BINCL/EINCL optimzation because the compiler put a bad stab symbol inside +a BINCL/EINCL range. +.It Fl warn_commons +Print a warning whenever the a tentative definition in an object file is found and a external symbol by the same name +is also found in a linked dylib. This often means that the extern keyword is missing from a variable declaration +in a header file. +.It Fl read_only_stubs +[i386 only] Makes the __IMPORT segment of a final linked images read-only. This option makes a program slightly more +secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code. The downside +is the dyld must use mprotect() to temporily make the segment writable while it is binding the stubs. +.It Fl slow_stubs +[i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which +calls through a lazy pointer in the __DATA segment. +.It Fl interposable_list Ar filename +The specified +.Ar filename +contains a list of global symbol names that should always be accessed indirectly. For instance, if libSystem.dylib +is linked such that _malloc is interposable, then calls to malloc() from within libSystem will go through a dyld +stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc +interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed +(not interposed) because they would be direct calls. +.El +.Ss Obsolete Options +.Bl -tag +.It Fl segalign Ar value +All segments must be page aligned. This option is obsolete. +.It Fl seglinkedit +Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. +.It Fl noseglinkedit +This is the default. This option is obsolete. +.It Fl fvmlib +Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. +.It Fl preload +Preload executables (MH_PRELOAD) are no longer supported. This option is obsolete. +.It Fl sectobjectsymbols Ar segname Ar sectname +Adding a local label at a section start is no longer supported. This option is obsolete. +.It Fl nofixprebinding +The MH_NOFIXPREBINDING bit of mach_headers has been ignored since Mac OS X 10.3.9. This option is obsolete. +.It Fl noprebind_all_twolevel_modules +Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. +.It Fl prebind_all_twolevel_modules +Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. +.It Fl prebind_allow_overlap +When using -prebind, the linker allows overlapping by default, so this option is obsolete. +.It Fl noprebind +LD_PREBIND is no longer supported as a way to force on prebinding, so there no longer needs to +be a command line way to override LD_PREBIND. This option is obsolete. +.It Fl sect_diff_relocs Ar treatment +This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into +a main executable, but the false positive rate generated too much noise to make the option useful. +This option is obsolete. +.It Fl run_init_lazily +This option was removed in Mac OS X 10.2. +.It Fl single_module +This is now the default so does not need to be specified. +.It Fl multi_module +Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. +.It Fl no_dead_strip_inits_and_terms +The linker never dead strips initialzation and termination routines. They are considered "roots" of the dead strip graph. +.It Fl A Ar basefile +Obsolete incremental load format. This option is obsolete. +.It Fl b +Used with -A option to strip base file's symbols. This option is obsolete. +..It Fl M +Obsolete option to produce a load map. Use -map option instead. +.It Fl Sn +Don't strip any symbols. This is the default. This option is obsolete. +.It Fl Si +Optimize stabs debug symbols to remove duplicates. This is the default. This option is obsolete. +.It Fl Sp +Write minimal stabs which causes the debugger to open and read the original .o file for full stabs. +This style of debugging is obsolete in Mac OS X 10.5. This option is obsolete. +.It Fl X +Strip local symbols that being the 'L'. This is the default. This option is obsolete. +.It Fl s +Completely strip the output, including removing the symbol table. This file format variant is no longer supported. +This option is obsolete. +.It Fl m +Don't treat multiple definitions as an error. This is no longer supported. This option is obsolete. +.It Fl y Ns symbol +Display each file in which +.Ar symbol +is used. This was previously used to debug where an undefined symbol was used, but the linker now +automatically prints out all usages. The -why_live option can also be used to display what kept +a symbol from being dead striped. This option is obsolete. +.It Fl Y Ar number +Used to control how many occurances of each symbol specifed with -y would be shown. This option is obsolete. +.It Fl nomultidefs +Only used when linking an umbrella framework. Sets the MH_NOMULTIDEFS bit in the mach_header. The MH_NOMULTIDEFS +bit has been obsolete since Mac OS X 10.4. This option is obsolete. +.It Fl multiply_defined_unused Ar treatment +Previously provided a way to warn or error if any of the symbol definitions in the output file matched any +definitions in dynamic library being linked. This option is obsolete. +.It Fl multiply_defined Ar treatment +Previously provided a way to warn or error if any of the symbols used from a dynamic library were also +available in another linked dynamic library. This option is obsolete. +.It Fl private_bundle +Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle +contained a definition that conflicted with a symbol in the main executable. The linker no longer +errors on such conflicts. This option is obsolete. +.It Fl noall_load +This is the default. This option is obsolete. +.It Fl seg_addr_table_filename Ar path +Use +.Ar path +instead of the install name of the library for matching an entry in the seg_addr_table. This option is obsolete. +.It Fl sectorder Ar segname sectname orderfile +Replaced by more general -order_file option. +.It Fl sectorder_detail +Produced extra logging about which entries from a sectorder entries were used. Replaced by -order_file_statistics. +This option is obsolete. +.El +.Sh SEE ALSO +as(1), ar(1), cc(1), nm(1), otool(1) lipo(1), +arch(3), dyld(3), Mach-O(5), strip(1), rebase(1) diff --git a/ld64/FireOpal/doc/man/man1/ld64.1 b/ld64/FireOpal/doc/man/man1/ld64.1 new file mode 100644 index 0000000..615b0e5 --- /dev/null +++ b/ld64/FireOpal/doc/man/man1/ld64.1 @@ -0,0 +1 @@ +.so man1/ld.1 diff --git a/ld64/FireOpal/doc/man/man1/rebase.1 b/ld64/FireOpal/doc/man/man1/rebase.1 new file mode 100644 index 0000000..6743a96 --- /dev/null +++ b/ld64/FireOpal/doc/man/man1/rebase.1 @@ -0,0 +1,39 @@ +.Dd June 6, 2006 +.Dt rebase 1 +.Os Darwin +.Sh NAME +.Nm rebase +.Nd "Changes base address of dylibs and bundles" +.Sh SYNOPSIS +.Nm +.Op Fl low_address Ar addr +.Op Fl high_address Ar addr +.Op Fl arch Ar arch +.Op Fl v +.Ar file(s) +.Sh DESCRIPTION +The base address of an image (dylib or bundle) is the preferred address for it to be loaded. By +default all images are built with a base address of zero. At runtime, if the +preferred memory range is already occupied, dyld will "slide" the image to a new address range. +There is a small cost to the slide, as dyld must do some fix ups. +The rebase tool takes a list of images and adjust their base address to be non-overlapping. If no +low or high address is specified, the a suitable address range is choosen for the architecture. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl low_address Ar addr +Force the base address for the first image to be +.Ar addr +(specified in hex). Each subsequent file gets the next available base address. +.It Fl high_address Ar addr +Force the base address for the last image to be such that when that image is loaded it occupies +memory up to +.Ar addr +(specified in hex). Each preceeding file gets the previous available base address. +.It Fl arch Ar arch +Only rebase the specified architecture. Other architectures in a universal image are left as is. +.It Fl v +Verbose. Print information about rebasing done. +.El +.Sh SEE ALSO +.Xr ld 1 diff --git a/ld64/FireOpal/ld64.xcodeproj/project.pbxproj b/ld64/FireOpal/ld64.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5f8cd7f --- /dev/null +++ b/ld64/FireOpal/ld64.xcodeproj/project.pbxproj @@ -0,0 +1,788 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXAggregateTarget section */ + F96D5368094A2754008E9EE8 /* unit-tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */; + buildPhases = ( + F96D5367094A2754008E9EE8 /* ShellScript */, + ); + dependencies = ( + F96D536A094A275D008E9EE8 /* PBXTargetDependency */, + F96D536C094A275F008E9EE8 /* PBXTargetDependency */, + F96904890A4333AC00B77D2A /* PBXTargetDependency */, + F9EA73970974999B008B4F1D /* PBXTargetDependency */, + ); + name = "unit-tests"; + productName = "unit-tests"; + }; + F9B1A2670A3A567B00DA8FAB /* all */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */; + buildPhases = ( + ); + dependencies = ( + F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, + F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */, + ); + name = all; + productName = all; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; + F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; + F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; + F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; + F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; + F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; + F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; + F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; + F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; + F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9FCC3F10A54A75600CEB866 /* ld64.1 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + F96904880A4333AC00B77D2A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; + remoteInfo = rebase; + }; + F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9023C3806D5A23E001BBF46; + remoteInfo = ld; + }; + F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F971EED206D5ACF60041D381; + remoteInfo = ObjectDump; + }; + F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9023C3806D5A23E001BBF46; + remoteInfo = ld; + }; + F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; + remoteInfo = rebase; + }; + F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EA72CA097454A6008B4F1D; + remoteInfo = machocheck; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F97F5025070D0B6300B9FCD7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */, + F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/LTOReader.hpp; sourceTree = ""; }; + C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ExecutableFile.h; sourceTree = ""; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld.cpp; sourceTree = ""; }; + F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ObjectFile.h; sourceTree = ""; }; + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/FileAbstraction.hpp; sourceTree = ""; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/MachOFileAbstraction.hpp; sourceTree = ""; }; + F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/Architectures.hpp; sourceTree = ""; }; + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/MachOReaderDylib.hpp; sourceTree = ""; }; + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/MachOReaderRelocatable.hpp; sourceTree = ""; }; + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/MachOWriterExecutable.hpp; sourceTree = ""; }; + F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; + F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = ""; }; + F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; + F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/OpaqueSection.hpp; sourceTree = ""; }; + F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ArchiveReader.hpp; sourceTree = SOURCE_ROOT; }; + F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = ""; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = ""; }; + F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; + F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/machochecker.cpp; sourceTree = ""; }; + F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/debugline.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = ""; }; + F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; + F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/rebase.cpp; sourceTree = ""; }; + F9FCC3F10A54A75600CEB866 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F9023C3706D5A23E001BBF46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED106D5ACF60041D381 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EA72C9097454A6008B4F1D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EC77EC0A2F85F6002A3E39 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F9023C2C06D5A227001BBF46 = { + isa = PBXGroup; + children = ( + C02A29DE0953B26E001FB8C1 /* ChangeLog */, + F933DC37092A82480083EAC8 /* Architectures.hpp */, + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */, + F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */, + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */, + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, + 3DA587190ACC53BE0015C432 /* LTOReader.hpp */, + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, + F9023C4106D5A254001BBF46 /* ObjectFile.h */, + F98D26850AA779BD00416316 /* OpaqueSection.hpp */, + F9023C3F06D5A254001BBF46 /* ld.cpp */, + F9C0D48A06DD1E1B001C7193 /* Options.cpp */, + F9C0D48B06DD1E1B001C7193 /* Options.h */, + F9EA7583097882F3008B4F1D /* debugline.h */, + F9EA7582097882F3008B4F1D /* debugline.c */, + F9EA72D4097454FF008B4F1D /* machochecker.cpp */, + F971EED706D5AD240041D381 /* ObjectDump.cpp */, + F9EC78050A2F8674002A3E39 /* rebase.cpp */, + F97F5028070D0BB200B9FCD7 /* ld.1 */, + F9FCC3F10A54A75600CEB866 /* ld64.1 */, + F9B1A2580A3A448800DA8FAB /* rebase.1 */, + F9023C3A06D5A23E001BBF46 /* Products */, + ); + sourceTree = ""; + }; + F9023C3A06D5A23E001BBF46 /* Products */ = { + isa = PBXGroup; + children = ( + F9023C3906D5A23E001BBF46 /* ld */, + F971EED306D5ACF60041D381 /* ObjectDump */, + F9EA72CB097454A6008B4F1D /* machocheck */, + F9EC77EE0A2F85F6002A3E39 /* rebase */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F9023C3806D5A23E001BBF46 /* ld */ = { + isa = PBXNativeTarget; + buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; + buildPhases = ( + 0B12F6A50CE39466008ABCAE /* build configure.h */, + F9023C3606D5A23E001BBF46 /* Sources */, + F9023C3706D5A23E001BBF46 /* Frameworks */, + F97F5025070D0B6300B9FCD7 /* CopyFiles */, + F9FCC3EF0A54A4ED00CEB866 /* Run Script */, + ); + buildRules = ( + F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, + F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = ld; + productName = ld64; + productReference = F9023C3906D5A23E001BBF46 /* ld */; + productType = "com.apple.product-type.tool"; + }; + F971EED206D5ACF60041D381 /* ObjectDump */ = { + isa = PBXNativeTarget; + buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */; + buildPhases = ( + F971EED006D5ACF60041D381 /* Sources */, + F971EED106D5ACF60041D381 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ObjectDump; + productName = ObjectDump; + productReference = F971EED306D5ACF60041D381 /* ObjectDump */; + productType = "com.apple.product-type.tool"; + }; + F9EA72CA097454A6008B4F1D /* machocheck */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; + buildPhases = ( + F9EA72C8097454A6008B4F1D /* Sources */, + F9EA72C9097454A6008B4F1D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = machocheck; + productName = machocheck; + productReference = F9EA72CB097454A6008B4F1D /* machocheck */; + productType = "com.apple.product-type.tool"; + }; + F9EC77ED0A2F85F6002A3E39 /* rebase */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */; + buildPhases = ( + F9EC77EB0A2F85F6002A3E39 /* Sources */, + F9EC77EC0A2F85F6002A3E39 /* Frameworks */, + F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rebase; + productName = rebase; + productReference = F9EC77EE0A2F85F6002A3E39 /* rebase */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F9023C3006D5A227001BBF46 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 0; + mainGroup = F9023C2C06D5A227001BBF46; + productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F9B1A2670A3A567B00DA8FAB /* all */, + F9023C3806D5A23E001BBF46 /* ld */, + F9EC77ED0A2F85F6002A3E39 /* rebase */, + F971EED206D5ACF60041D381 /* ObjectDump */, + F9EA72CA097454A6008B4F1D /* machocheck */, + F96D5368094A2754008E9EE8 /* unit-tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0B12F6A50CE39466008ABCAE /* build configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "build configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\nelse\n\techo \"#undef LTO_SUPPORT\t\" > ${DERIVED_FILE_DIR}/configure.h\nfi\n"; + showEnvVarsInLog = 0; + }; + F96D5367094A2754008E9EE8 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + 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"; + showEnvVarsInLog = 0; + }; + F9FCC3EF0A54A4ED00CEB866 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "cd ${DSTROOT}/usr/bin\nln -s ld ld64"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F9023C3606D5A23E001BBF46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, + F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, + F9EA7584097882F3008B4F1D /* debugline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED006D5ACF60041D381 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */, + F9EA75BC09788857008B4F1D /* debugline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EA72C8097454A6008B4F1D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EC77EB0A2F85F6002A3E39 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F96904890A4333AC00B77D2A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EC77ED0A2F85F6002A3E39 /* rebase */; + targetProxy = F96904880A4333AC00B77D2A /* PBXContainerItemProxy */; + }; + F96D536A094A275D008E9EE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9023C3806D5A23E001BBF46 /* ld */; + targetProxy = F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */; + }; + F96D536C094A275F008E9EE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F971EED206D5ACF60041D381 /* ObjectDump */; + targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */; + }; + F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9023C3806D5A23E001BBF46 /* ld */; + targetProxy = F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */; + }; + F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EC77ED0A2F85F6002A3E39 /* rebase */; + targetProxy = F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */; + }; + F9EA73970974999B008B4F1D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EA72CA097454A6008B4F1D /* machocheck */; + targetProxy = F9EA73960974999B008B4F1D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + F933D91C09291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DEVELOPER_DIR)/usr/local/include", + "$(DEVELOPER_DIR)/usr/include", + ); + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = ""; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + PREBINDING = NO; + PRODUCT_NAME = ld; + SECTORDER_FLAGS = ""; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = Debug; + }; + F933D91D09291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DEVELOPER_DIR)/usr/local/include", + "$(DEVELOPER_DIR)/usr/include", + ); + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + PREBINDING = NO; + PRODUCT_NAME = ld; + SECTORDER_FLAGS = ""; + VALID_ARCHS = "i386 ppc"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = Release; + }; + F933D92009291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/include"; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Debug; + }; + F933D92109291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = s; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F933D92409291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = Debug; + }; + F933D92509291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/SDKs/Extra/usr/include"; + }; + name = Release; + }; + F96D536E094A2773008E9EE8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + PRODUCT_NAME = "unit-tests"; + }; + name = Debug; + }; + F96D536F094A2773008E9EE8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = "unit-tests"; + }; + name = Release; + }; + F9B1A26D0A3A568700DA8FAB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = all; + }; + name = Debug; + }; + F9B1A26E0A3A568700DA8FAB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = all; + ZERO_LINK = NO; + }; + name = Release; + }; + F9EA72D0097454D5008B4F1D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = Debug; + }; + F9EA72D1097454D5008B4F1D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = Release; + }; + F9EC77F10A2F8616002A3E39 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = rebase; + }; + name = Debug; + }; + F9EC77F20A2F8616002A3E39 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + INSTALL_PATH = /usr/bin; + PREBINDING = NO; + PRODUCT_NAME = rebase; + VALID_ARCHS = "i386 ppc"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D91C09291AC90083EAC8 /* Debug */, + F933D91D09291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D92009291AC90083EAC8 /* Debug */, + F933D92109291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D92409291AC90083EAC8 /* Debug */, + F933D92509291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F96D536E094A2773008E9EE8 /* Debug */, + F96D536F094A2773008E9EE8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9B1A26D0A3A568700DA8FAB /* Debug */, + F9B1A26E0A3A568700DA8FAB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9EA72D0097454D5008B4F1D /* Debug */, + F9EA72D1097454D5008B4F1D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9EC77F10A2F8616002A3E39 /* Debug */, + F9EC77F20A2F8616002A3E39 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F9023C3006D5A227001BBF46 /* Project object */; +} diff --git a/ld64/FireOpal/src/Architectures.hpp b/ld64/FireOpal/src/Architectures.hpp new file mode 100644 index 0000000..2546bfe --- /dev/null +++ b/ld64/FireOpal/src/Architectures.hpp @@ -0,0 +1,88 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +struct ppc +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, + kBranch24, kBranch24WeakImport, kBranch14, + kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct ppc64 +{ + typedef Pointer64 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, + kBranch24, kBranch24WeakImport, kBranch14, + kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct x86 +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff16, + kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct x86_64 +{ + typedef Pointer64 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32, + kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, + kBranchPCRel32, kBranchPCRel32WeakImport, + kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, + kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct arm +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kReadOnlyPointer, + kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +#endif // __ARCHITECTURES__ + + diff --git a/ld64/FireOpal/src/ArchiveReader.hpp b/ld64/FireOpal/src/ArchiveReader.hpp new file mode 100644 index 0000000..a195090 --- /dev/null +++ b/ld64/FireOpal/src/ArchiveReader.hpp @@ -0,0 +1,454 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_ARCHIVE__ +#define __OBJECT_FILE_ARCHIVE__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ObjectFile.h" +#include "MachOReaderRelocatable.hpp" +#if LTO_SUPPORT + #include "LTOReader.hpp" +#endif + +namespace archive { + +typedef const struct ranlib* ConstRanLibPtr; + +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength); + Reader(const uint8_t fileContent[], uint64_t fileLength, + const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime(){ return fModTime; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabs() { return NULL; } + virtual void optimize(std::vector&, std::vector&, + std::vector&, const std::set&, + uint32_t, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, int okind, + bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs); + +private: + static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength); + static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength); + static cpu_type_t architecture(); + + + class Entry : ar_hdr + { + public: + const char* getName() const; + time_t getModTime() const; + const uint8_t* getContent() const; + uint32_t getContentSize() const; + const Entry* getNext() const; + private: + bool hasLongName() const; + unsigned int getLongNameSpace() const; + + }; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; + + typedef typename A::P P; + typedef typename A::P::E E; + + const struct ranlib* ranlibHashSearch(const char* name); + ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); + void dumpTableOfContents(); + void buildHashTable(); + + const char* fPath; + time_t fModTime; + const ObjectFile::ReaderOptions& fOptions; + uint32_t fOrdinalBase; + const uint8_t* fFileContent; + uint64_t fFileLength; + const struct ranlib* fTableOfContents; + uint32_t fTableOfContentCount; + const char* fStringPool; + std::vector fAllAtoms; + std::vector fInstantiatedReaders; + std::set fInstantiatedEntries; + std::set fPossibleEntries; + NameToEntryMap fHashTable; + + static std::vector fgEmptyList; +}; + +template +std::vector Reader::fgEmptyList; + + +template +bool Reader::Entry::hasLongName() const +{ + return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); +} + +template +unsigned int Reader::Entry::getLongNameSpace() const +{ + char* endptr; + long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); + return result; +} + +template +const char* Reader::Entry::getName() const +{ + if ( this->hasLongName() ) { + int len = this->getLongNameSpace(); + static char longName[256]; + strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); + longName[len] = '\0'; + return longName; + } + else { + static char shortName[20]; + strncpy(shortName, this->ar_name, 16); + shortName[16] = '\0'; + char* space = strchr(shortName, ' '); + if ( space != NULL ) + *space = '\0'; + return shortName; + } +} + +template +time_t Reader::Entry::getModTime() const +{ + char temp[14]; + strncpy(temp, this->ar_date, 12); + temp[12] = '\0'; + char* endptr; + return (time_t)strtol(temp, &endptr, 10); +} + + +template +const uint8_t* Reader::Entry::getContent() const +{ + if ( this->hasLongName() ) + return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); + else + return ((uint8_t*)this) + sizeof(ar_hdr); +} + + +template +uint32_t Reader::Entry::getContentSize() const +{ + char temp[12]; + strncpy(temp, this->ar_size, 10); + temp[10] = '\0'; + char* endptr; + long size = strtol(temp, &endptr, 10); + // long name is included in ar_size + if ( this->hasLongName() ) + size -= this->getLongNameSpace(); + return size; +} + + +template +const class Reader::Entry* Reader::Entry::getNext() const +{ + const uint8_t* p = this->getContent() + getContentSize(); + p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align + return (class Reader::Entry*)p; +} + + +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_I386; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_X86_64; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_ARM; } + + +template +bool Reader::validMachOFile(const uint8_t* fileContent, uint64_t fileLength) +{ + return mach_o::relocatable::Reader::validFile(fileContent); +} + +template +bool Reader::validLTOFile(const uint8_t* fileContent, uint64_t fileLength) +{ +#if LTO_SUPPORT + return lto::Reader::validFile(fileContent, fileLength, architecture()); +#else + return false; +#endif +} + + + +template +bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength) +{ + // must have valid archive header + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + return false; + + // peak at first .o file and verify it is correct architecture + const Entry* const start = (Entry*)&fileContent[8]; + const Entry* const end = (Entry*)&fileContent[fileLength]; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + // skip option table-of-content member + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + // archive is valid if first .o file is valid + return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize())); + } + // empty archive + return true; +} + +template +Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL), + fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL) +{ + fPath = strdup(path); + fFileContent = fileContent; + fFileLength = fileLength; + + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + throw "not an archive"; + + // write out path for -whatsloaded option + if ( options.fLogAllFiles ) + printf("%s\n", path); + + if ( !options.fFullyLoadArchives ) { + const Entry* const firstMember = (Entry*)&fFileContent[8]; + if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { + const uint8_t* contents = firstMember->getContent(); + uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); + fTableOfContents = (const struct ranlib*)&contents[4]; + fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); + fStringPool = (const char*)&contents[ranlibArrayLen+8]; + if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) + || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) + throw "malformed archive, perhaps wrong architecture"; + this->buildHashTable(); + } + else + throw "archive has no table of contents"; + } +} + + +template +ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) +{ + const char* memberName = member->getName(); + char memberPath[strlen(fPath) + strlen(memberName)+4]; + strcpy(memberPath, fPath); + strcat(memberPath, "("); + strcat(memberPath, memberName); + strcat(memberPath, ")"); + //fprintf(stderr, "using %s from %s\n", memberName, fPath); + try { + // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive + uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent; + if ( validMachOFile(member->getContent(), member->getContentSize()) ) { + return new typename mach_o::relocatable::Reader::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase); + } +#if LTO_SUPPORT + else if ( validLTOFile(member->getContent(), member->getContentSize()) ) { + return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture()); + } +#endif + throw "not a valid archive member"; + } + catch (const char* msg) { + throwf("in %s, %s", memberPath, msg); + } +} + + +template +std::vector& Reader::getAtoms() +{ + if ( fOptions.fFullyLoadArchives ) { + // build vector of all atoms from all .o files in this archive + const Entry* const start = (Entry*)&fFileContent[8]; + const Entry* const end = (Entry*)&fFileContent[fFileLength]; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + if ( fOptions.fWhyLoad ) + printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); + ObjectFile::Reader* r = this->makeObjectReaderForMember(p); + std::vector& atoms = r->getAtoms(); + fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); + fInstantiatedReaders.push_back(r); + } + return fAllAtoms; + } + else if ( fOptions.fLoadAllObjcObjectsFromArchives ) { + // build vector of all atoms from all .o files containing objc classes in this archive + for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) { + if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { + const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)]; + if ( fInstantiatedEntries.count(member) == 0 ) { + if ( fOptions.fWhyLoad ) + printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName()); + // only return these atoms once + fInstantiatedEntries.insert(member); + ObjectFile::Reader* r = makeObjectReaderForMember(member); + std::vector& atoms = r->getAtoms(); + fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); + fInstantiatedReaders.push_back(r); + } + } + } + return fAllAtoms; + } + else { + // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed + return fgEmptyList; + } +} + +template +void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, const std::set& deadAtoms, + uint32_t nextOrdinal, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, int okind, + bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs) +{ + for(std::vector::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) { + (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, nextOrdinal, writer, llvmOptions, + allGlobalsAReDeadStripRoots, okind, verbose, saveTemps, outputFilePath, pie, allowTextRelocs); + } +} + + + +template +ConstRanLibPtr Reader::ranlibHashSearch(const char* name) +{ + class NameToEntryMap::iterator pos = fHashTable.find(name); + if ( pos != fHashTable.end() ) + return pos->second; + else + return NULL; +} + +template +void Reader::buildHashTable() +{ + // walk through list backwards, adding/overwriting entries + // this assures that with duplicates those earliest in the list will be found + for (int i = fTableOfContentCount-1; i >= 0; --i) { + const struct ranlib* entry = &fTableOfContents[i]; + const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; + const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; + //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); + fHashTable[entryName] = entry; + fPossibleEntries.insert(member); + } +} + +template +void Reader::dumpTableOfContents() +{ + for (unsigned int i=0; i < fTableOfContentCount; ++i) { + const struct ranlib* e = &fTableOfContents[i]; + printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); + } +} + +template +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + if ( fOptions.fFullyLoadArchives ) { + return NULL; + } + else { + const struct ranlib* result = NULL; + // do a hash search of table of contents looking for requested symbol + result = ranlibHashSearch(name); + if ( result != NULL ) { + const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; + if ( fInstantiatedEntries.count(member) == 0 ) { + if ( fOptions.fWhyLoad ) + printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); + // only return these atoms once + fInstantiatedEntries.insert(member); + ObjectFile::Reader* r = makeObjectReaderForMember(member); + fInstantiatedReaders.push_back(r); + return new std::vector(r->getAtoms()); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); + return NULL; + } +} + + + + + +}; // namespace archive + + +#endif // __OBJECT_FILE_ARCHIVE__ diff --git a/ld64/FireOpal/src/ExecutableFile.h b/ld64/FireOpal/src/ExecutableFile.h new file mode 100644 index 0000000..b0b760d --- /dev/null +++ b/ld64/FireOpal/src/ExecutableFile.h @@ -0,0 +1,70 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __EXECUTABLEFILE__ +#define __EXECUTABLEFILE__ + +#include +#include + +#include "ObjectFile.h" +#include "Options.h" + + +namespace ExecutableFile { + + struct DyLibUsed + { + ObjectFile::Reader* reader; + DynamicLibraryOptions options; + }; + + class Writer : public ObjectFile::Reader + { + public: + virtual ~Writer() {}; + + virtual const char* getPath() = 0; + virtual std::vector& getAtoms() = 0; + virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; + virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, + bool objcReplacementClasses) = 0; + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; + virtual uint64_t write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool createUUID, bool canScatter, + ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs, + bool overridesDylibWeakDefines) = 0; + + protected: + Writer(std::vector&) {}; + }; + +}; + +#endif // __EXECUTABLEFILE__ diff --git a/ld64/FireOpal/src/FileAbstraction.hpp b/ld64/FireOpal/src/FileAbstraction.hpp new file mode 100644 index 0000000..1f7a629 --- /dev/null +++ b/ld64/FireOpal/src/FileAbstraction.hpp @@ -0,0 +1,145 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/ld64/FireOpal/src/LTOReader.hpp b/ld64/FireOpal/src/LTOReader.hpp new file mode 100644 index 0000000..2736f43 --- /dev/null +++ b/ld64/FireOpal/src/LTOReader.hpp @@ -0,0 +1,684 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LTO_READER_H__ +#define __LTO_READER_H__ + +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ObjectFile.h" +#include "Options.h" + +#include "llvm-c/lto.h" + + +namespace lto { + + +// +// Reference handles Atom references. These references facilitate +// symbol resolution. +// + +class Reference : public ObjectFile::Reference +{ +public: + Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { } + Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { } + + bool isTargetUnbound() const { return fTargetAtom == NULL; } + bool isFromTargetUnbound() const { return true; } + uint8_t getKind() const { return 0; } + uint64_t getFixUpOffset() const { return 0; } + const char * getTargetName() const { return fTargetName; } + ObjectFile::Atom& getTarget() const { return *fTargetAtom; } + uint64_t getTargetOffset() const { return 0; } + bool hasFromTarget() const { return false; } + ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } + const char * getFromTargetName() const { return NULL; } + uint64_t getFromTargetOffset() const { return 0; } + TargetBinding getTargetBinding() const; + TargetBinding getFromTargetBinding() const { return kDontBind; } + void setTarget (ObjectFile::Atom& a, uint64_t offset) + { fTargetAtom = &a; } + void setFromTarget(ObjectFile::Atom &a) { } + const char * getDescription() const; + +private: + const char * fTargetName; + ObjectFile::Atom * fTargetAtom; +}; + + +ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const +{ + if ( fTargetAtom == NULL ) + return kUnboundByName; + else if ( fTargetName == NULL ) + return kBoundDirectly; + else + return kBoundByName; +} + +const char* Reference::getDescription() const +{ + static char temp[256]; + strcpy(temp, "reference to "); + if ( fTargetName != NULL ) + strcat(temp, fTargetName); + else + strcat(temp, fTargetAtom->getDisplayName()); + return temp; +} + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + virtual bool hasFixedAddress() const { return fFixedAddress; } + + static Segment fgBootstrapSegment; + +private: + const char* fName; + const bool fReadable; + const bool fWritable; + const bool fExecutable; + const bool fFixedAddress; +}; + +Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false); + + + + +// +// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, +// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After +// optimization is performed, real Atoms are created for these symobls. However these real Atoms +// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate +// methods to real atom. +// +class Atom : public ObjectFile::Atom +{ +public: + Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom); + + ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; } + bool getTranslationUnitSource (const char **dir, const char **name) const + { return fRealAtom->getTranslationUnitSource(dir, name); } + const char * getName () const { return fName; } + const char * getDisplayName() const { return this->getName(); } + Scope getScope() const { return fScope; } + DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } + SymbolTableInclusion getSymbolTableInclusion() const + { return fRealAtom->getSymbolTableInclusion(); } + bool dontDeadStrip() const { return false; } + bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); } + bool isThumb() const { return false; } + uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); } + std::vector& getReferences() const + { return (fRealAtom ? fRealAtom->getReferences() : (std::vector&)fReferences); } + bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); } + const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); } + // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection. + class ObjectFile::Section * getSection() const { return fSection; } + ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); } + uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); } + ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); } + std::vector* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); } + ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); } + void copyRawContent(uint8_t buffer[]) const + { if (fRealAtom) fRealAtom->copyRawContent(buffer); } + void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; } + + void setRealAtom (ObjectFile::Atom *atom) + { fRealAtom = atom; } + ObjectFile::Atom * getRealAtom() { return fRealAtom; } + void addReference(ObjectFile::Reference *ref) + { fReferences.push_back(ref); } + + void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); } + void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); } + +private: + class Reader& fOwner; + const char* fName; + ObjectFile::Atom::Scope fScope; + ObjectFile::Atom::DefinitionKind fKind; + uint8_t fAlignment; + ObjectFile::Atom* fRealAtom; + std::vector fReferences; +}; + + +Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom) +: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL) +{ + // every Atom references the InternalAtom for its reader + fReferences.push_back(new Reference(internalAtom)); +} + + +// +// ld64 only tracks non-internal symbols from an llvm bitcode file. +// We model this by having an InternalAtom which represent all internal functions and data. +// All non-interal symbols from a bitcode file are represented by a Atom +// and each Atom has a reference to the InternalAtom. The InternalAtom +// also has references to each symbol external to the bitcode file. +// +class InternalAtom : public ObjectFile::Atom +{ +public: + InternalAtom(class Reader& owner) : fOwner(owner) {} + + ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; } + bool getTranslationUnitSource (const char **dir, const char **name) const + { return false; } + const char * getName () const { return "__llvm-internal-atom"; } + const char * getDisplayName() const { return "llvm bitcode"; } + Scope getScope() const { return scopeTranslationUnit; } + DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; } + bool dontDeadStrip() const { return false; } + bool isZeroFill() const { return false; } + bool isThumb() const { return false; } + uint64_t getSize() const { return 0; } + std::vector& getReferences() const { return (std::vector&)fReferences; } + bool mustRemainInSection() const { return false; } + const char * getSectionName() const { return NULL; } + class ObjectFile::Section * getSection() const { return NULL; } + ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; } + uint32_t getOrdinal() const { return 0; } + ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + std::vector* getLineInfo() const { return NULL; } + ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + void copyRawContent(uint8_t buffer[]) const { } + void setScope(Scope s) { } + + void addReference(const char* targetName); + +private: + class Reader& fOwner; + std::vector fReferences; +}; + + +void InternalAtom::addReference(const char* name) +{ + fReferences.push_back(new Reference(name)); +} + + + + +class RemovableAtoms +{ +public: + RemovableAtoms(std::set& iAtoms) : fAtoms(iAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fAtoms.count(atom) != 0 ); + } + +private: + std::set& fAtoms; +}; + + + +// +// LLVM bitcode file reader +// +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); + static bool loaded() { return (::lto_get_version() != NULL); } + Reader(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, + const ObjectFile::ReaderOptions&, cpu_type_t arch); + virtual ~Reader(); + + virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return fModTime; } + virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; } + virtual std::vector* getStabs() { return NULL; } + virtual void optimize(std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, const std::set&, + uint32_t nextInputOrdinal, + ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, + int outputKind, bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs); + +private: + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; + + ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal); + static const char* tripletPrefixForArch(cpu_type_t); + + cpu_type_t fArchitecture; + const char* fPath; + time_t fModTime; + lto_module_t fModule; + std::vector fAtoms; + InternalAtom fInternalAtom; + const ObjectFile::ReaderOptions& fReaderOptions; + static std::set fgReaders; + static bool fgOptimized; +}; + +bool Reader::fgOptimized = false; +std::set Reader::fgReaders; + + +Reader::~Reader() +{ + if ( fModule != NULL ) + ::lto_module_dispose(fModule); +} + +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, cpu_type_t arch) + : fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options) +{ + fgReaders.insert(this); + + fModule = ::lto_module_create_from_memory(fileContent, fileLength); + if ( fModule == NULL ) + throwf("could not parse object file %s: %s", path, lto_get_error_message()); + + fAtoms.push_back(&fInternalAtom); + + uint32_t count = ::lto_module_get_num_symbols(fModule); + for (uint32_t i=0; i < count; ++i) { + const char* name = ::lto_module_get_symbol_name(fModule, i); + lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i); + + ObjectFile::Atom::DefinitionKind kind; + switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { + case LTO_SYMBOL_DEFINITION_REGULAR: + kind = ObjectFile::Atom::kRegularDefinition; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + kind = ObjectFile::Atom::kTentativeDefinition; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + kind = ObjectFile::Atom::kWeakDefinition; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + kind = ObjectFile::Atom::kExternalDefinition; + break; + default: + throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); + } + + // make LLVM atoms for definitions and a reference for undefines + if ( kind != ObjectFile::Atom::kExternalDefinition ) { + ObjectFile::Atom::Scope scope; + switch ( attr & LTO_SYMBOL_SCOPE_MASK) { + case LTO_SYMBOL_SCOPE_INTERNAL: + scope = ObjectFile::Atom::scopeTranslationUnit; + break; + case LTO_SYMBOL_SCOPE_HIDDEN: + scope = ObjectFile::Atom::scopeLinkageUnit; + break; + case LTO_SYMBOL_SCOPE_DEFAULT: + scope = ObjectFile::Atom::scopeGlobal; + break; + default: + throwf("unknown scope for symbol %s in bitcode file %s", name, path); + } + // only make atoms for non-internal symbols + if ( scope == ObjectFile::Atom::scopeTranslationUnit ) + continue; + uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); + // make Atom + fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom)); + } + else { + // add to list of external references + fInternalAtom.addReference(name); + } + } +} + +const char* Reader::tripletPrefixForArch(cpu_type_t arch) +{ + switch (arch) { + case CPU_TYPE_POWERPC: + return "powerpc-"; + case CPU_TYPE_POWERPC64: + return "powerpc64-"; + case CPU_TYPE_I386: + return "i386-"; + case CPU_TYPE_X86_64: + return "x86_64-"; + case CPU_TYPE_ARM: + return "arm-"; + } + return ""; +} + +bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) +{ + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); +} + +void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, const std::set& deadAtoms, + uint32_t nextInputOrdinal, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, + int okind, bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs) +{ + // this method is call on all Readers. We want the first call to trigger optimization + // across all Readers and the subsequent calls to do nothing. + if ( fgOptimized ) + return; + fgOptimized = true; + + Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency + + // print out LTO version string if -v was used + if ( verbose ) + fprintf(stderr, "%s\n", lto_get_version()); + + // create optimizer and add each Reader + lto_code_gen_t generator = ::lto_codegen_create(); + for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { + if ( ::lto_codegen_add_module(generator, (*it)->fModule) ) + throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message()); + } + + // add any -mllvm command line options + for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { + ::lto_codegen_debug_options(generator, *it); + } + + // the linker must preserve all globals in dylibs and flat images + const bool globalsNeedPreserving = allGlobalsAReDeadStripRoots || fReaderOptions.fFlatNamespace; + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringToAtom llvmAtoms; + bool hasNonllvmAtoms = false; + for (std::vector::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + // only look at references come from an atom that is not an llvm atom + if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) { + // remember if we've seen any atoms not from an llvm reader and not from the writer + if ( atom->getFile() != writer ) + hasNonllvmAtoms = true; + std::vector& refs = atom->getReferences(); + for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { + ObjectFile::Reference* ref = *ri; + // add target name to set if target is an llvm atom + if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) { + nonLLVMRefs.insert(ref->getTargetName()); + } + } + } + else { + const char* name = atom->getName(); + if ( name != NULL ) + llvmAtoms[name] = (Atom*)atom; + } + } + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions + // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // atom so that the linker can replace it with the mach-o one later. + CStringToAtom deadllvmAtoms; + for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) { + const char* name = atom->getName(); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)atom; + } + } + + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - globals need preserving and atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( globalsNeedPreserving && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) { + if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { + // HACK, no good way to tell linker we are all done, so just quit + exit(0); + } + warning("could not produce merged bitcode file"); + } + + // if requested, save off merged bitcode file + if ( saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + + // set code-gen model + lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + switch ( outputKind ) { + case Options::kDynamicExecutable: + if ( pie ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // ?? Is this appropriate ? + case Options::kDyld: + if ( allowTextRelocs ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + break; + case Options::kStaticExecutable: + model = LTO_CODEGEN_PIC_MODEL_STATIC; + break; + } + if ( ::lto_codegen_set_pic_model(generator, model) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // run code generator + size_t machOFileLen; + const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + + // if requested, save off temp mach-o file + if ( saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + } + + // parse generated mach-o file into a MachOReader + ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal); + + // sync generated mach-o atoms with existing atoms ld knows about + std::vector machoAtoms = machoReader->getAtoms(); + for (std::vector::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + const char* name = atom->getName(); + if ( name != NULL ) { + CStringToAtom::iterator pos = llvmAtoms.find(name); + if ( pos != llvmAtoms.end() ) { + // turn Atom into a proxy for this mach-o atom + pos->second->setRealAtom(atom); + } + else { + // an atom of this name was not in the allAtoms list the linker gave us + if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { + // this corresponding to an atom that the linker coalesced away. Ignore it + // Make sure there any dependent atoms are also marked dead + std::vector& refs = atom->getReferences(); + for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { + ObjectFile::Reference* ref = *ri; + if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX + ObjectFile::Atom* targ = &ref->getTarget(); + deadllvmAtoms[targ->getName()] = (Atom*)atom; + } + } + } + else + { + // this is something new that lto conjured up, tell ld its new + newAtoms.push_back(atom); + } + } + } + else { + // ld only knew about named atoms, so this one must be new + newAtoms.push_back(atom); + } + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { + ObjectFile::Reference* ref = *rit; + const char* targetName = ref->getTargetName(); + CStringToAtom::iterator pos; + if (targetName != NULL) { + switch ( ref->getTargetBinding() ) { + case ObjectFile::Reference::kUnboundByName: + // accumulate unbounded references so that ld can bound them. + additionalUndefines.push_back(targetName); + break; + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + // If mach-o atom is referencing another mach-o atom then + // reference is not going through Atom proxy. Fix it here to ensure that all + // llvm symbol references always go through Atom proxy. + pos = llvmAtoms.find(targetName); + if ( pos != llvmAtoms.end() ) + ref->setTarget(*pos->second, ref->getTargetOffset()); + break; + case ObjectFile::Reference::kDontBind: + break; + } + } + } + } + + // Remove InternalAtoms from ld + std::set deletedAtoms; + for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { + deletedAtoms.insert(&((*it)->fInternalAtom)); + } + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->getRealAtom() == NULL ) + deletedAtoms.insert(li->second); + } + allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), RemovableAtoms(deletedAtoms)), allAtoms.end()); +} + + +ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal) +{ + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + } + throw "LLVM LTO, file is not of required architecture"; +} + +}; // namespace lto + + +void printLTOVersion(Options &opts) { + const char* vers = lto_get_version(); + if ( vers != NULL ) + fprintf(stderr, "%s\n", vers); +} + + +#endif + diff --git a/ld64/FireOpal/src/MachOFileAbstraction.hpp b/ld64/FireOpal/src/MachOFileAbstraction.hpp new file mode 100644 index 0000000..83a8ee1 --- /dev/null +++ b/ld64/FireOpal/src/MachOFileAbstraction.hpp @@ -0,0 +1,925 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +// stuff that will eventually go away once newer cctools headers are widespread +#ifndef LC_LAZY_LOAD_DYLIB + #define LC_LAZY_LOAD_DYLIB 0x20 +#endif +#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS + #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#endif +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif +#ifndef N_ARM_THUMB_DEF + #define N_ARM_THUMB_DEF 0x0008 +#endif +enum reloc_type_arm +{ + ARM_RELOC_VANILLA, /* generic relocation as discribed above */ + ARM_RELOC_PAIR, /* the second relocation entry of a pair */ + ARM_RELOC_SECTDIFF, /* a PAIR follows with subtract symbol value */ + ARM_RELOC_LOCAL_SECTDIFF, /* like ARM_RELOC_SECTDIFF, but the symbol + referenced was local. */ + ARM_RELOC_PB_LA_PTR,/* prebound lazy pointer */ + ARM_RELOC_BR24, /* 24 bit branch displacement (to a word address) */ + ARM_THUMB_RELOC_BR22, /* 22 bit branch displacement (to a half-word + address) */ +}; + +#ifndef LC_ENCRYPTION_INFO + #define LC_ENCRYPTION_INFO 0x21 + struct encryption_info_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; /* file offset of encrypted range */ + uint32_t cryptsize; /* file size of encrypted range */ + uint32_t cryptid; /* which enryption system, 0 means not-encrypted yet */ + }; +#endif + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

::CMD + }; + + typedef typename P::E E; +private: + macho_segment_content

segment; +}; + + +// +// mach-o section +// +template struct macho_section_content {}; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; + +template +class macho_section { +public: + const char* sectname() const INLINE { return section.fields.sectname; } + void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } + + const char* segname() const INLINE { return section.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } + + uint64_t addr() const INLINE { return P::getP(section.fields.addr); } + void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } + + uint64_t size() const INLINE { return P::getP(section.fields.size); } + void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } + + uint32_t offset() const INLINE { return E::get32(section.fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } + + uint32_t align() const INLINE { return E::get32(section.fields.align); } + void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } + + uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } + void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } + + uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } + void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } + + uint32_t flags() const INLINE { return E::get32(section.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } + + uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } + void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } + + uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } + void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } + + typedef typename P::E E; +private: + macho_section_content

section; +}; + + +// +// mach-o dylib load command +// +template +class macho_dylib_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } + + uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } + void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } + + uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } + void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } + + uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } + void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylib_command fields; +}; + + +// +// mach-o dylinker load command +// +template +class macho_dylinker_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylinker_command fields; +}; + + +// +// mach-o sub_framework load command +// +template +class macho_sub_framework_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } + void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } + + const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } + void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_framework_command fields; +}; + + +// +// mach-o sub_client load command +// +template +class macho_sub_client_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } + void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } + + const char* client() const INLINE { return (const char*)&fields + client_offset(); } + void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_client_command fields; +}; + + +// +// mach-o sub_umbrella load command +// +template +class macho_sub_umbrella_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } + void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } + + const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } + void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_umbrella_command fields; +}; + + +// +// mach-o sub_library load command +// +template +class macho_sub_library_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } + void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } + + const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } + void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_library_command fields; +}; + + +// +// mach-o uuid load command +// +template +class macho_uuid_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); } + + typedef typename P::E E; +private: + uuid_command fields; +}; + + +// +// mach-o routines load command +// +template struct macho_routines_content {}; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; + +template +class macho_routines_command { +public: + uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } + + uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } + void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } + + uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } + void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } + + uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } + void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } + + uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } + void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } + + uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } + void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } + + uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } + void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } + + uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } + void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } + + uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } + void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } + + typedef typename P::E E; + enum { + CMD = macho_routines_content

::CMD + }; +private: + macho_routines_content

routines; +}; + + +// +// mach-o symbol table load command +// +template +class macho_symtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t symoff() const INLINE { return E::get32(fields.symoff); } + void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } + + uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } + void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } + + uint32_t stroff() const INLINE { return E::get32(fields.stroff); } + void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } + + uint32_t strsize() const INLINE { return E::get32(fields.strsize); } + void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } + + + typedef typename P::E E; +private: + symtab_command fields; +}; + + +// +// mach-o dynamic symbol table load command +// +template +class macho_dysymtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } + + uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } + void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } + + uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } + void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } + + uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } + void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } + + uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } + void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } + + uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } + void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } + + uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } + void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } + + uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } + void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } + + uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } + void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } + + uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } + void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } + + uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } + void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } + + uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } + void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } + + uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } + + uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } + void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } + + uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } + void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } + + typedef typename P::E E; +private: + dysymtab_command fields; +}; + + + + +// +// mach-o module table entry (for compatibility with old ld/dyld) +// +template struct macho_dylib_module_content {}; +template <> struct macho_dylib_module_content > { struct dylib_module fields; }; +template <> struct macho_dylib_module_content > { struct dylib_module fields; }; +template <> struct macho_dylib_module_content > { struct dylib_module_64 fields; }; +template <> struct macho_dylib_module_content > { struct dylib_module_64 fields; }; + +template +class macho_dylib_module { +public: + uint32_t module_name() const INLINE { return E::get32(module.fields.module_name); } + void set_module_name(uint32_t value) INLINE { E::set32(module.fields.module_name, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(module.fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(module.fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(module.fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(module.fields.nextdefsym, value); } + + uint32_t irefsym() const INLINE { return E::get32(module.fields.irefsym); } + void set_irefsym(uint32_t value) INLINE { E::set32(module.fields.irefsym, value); } + + uint32_t nrefsym() const INLINE { return E::get32(module.fields.nrefsym); } + void set_nrefsym(uint32_t value) INLINE { E::set32(module.fields.nrefsym, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(module.fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(module.fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(module.fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(module.fields.nlocalsym, value); } + + uint32_t iextrel() const INLINE { return E::get32(module.fields.iextrel); } + void set_iextrel(uint32_t value) INLINE { E::set32(module.fields.iextrel, value); } + + uint32_t nextrel() const INLINE { return E::get32(module.fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(module.fields.nextrel, value); } + + uint16_t iinit() const INLINE { return E::get32(module.fields.iinit_iterm) & 0xFFFF; } + uint16_t iterm() const INLINE { return E::get32(module.fields.iinit_iterm) > 16; } + void set_iinit_iterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.iinit_iterm, (term<<16) | (init &0xFFFF)); } + + uint16_t ninit() const INLINE { return E::get32(module.fields.ninit_nterm) & 0xFFFF; } + uint16_t nterm() const INLINE { return E::get32(module.fields.ninit_nterm) > 16; } + void set_ninit_nterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.ninit_nterm, (term<<16) | (init &0xFFFF)); } + + uint64_t objc_module_info_addr() const INLINE { return P::getP(module.fields.objc_module_info_addr); } + void set_objc_module_info_addr(uint64_t value) INLINE { P::setP(module.fields.objc_module_info_addr, value); } + + uint32_t objc_module_info_size() const INLINE { return E::get32(module.fields.objc_module_info_size); } + void set_objc_module_info_size(uint32_t value) INLINE { E::set32(module.fields.objc_module_info_size, value); } + + + typedef typename P::E E; +private: + macho_dylib_module_content

module; +}; + + +// +// mach-o dylib_reference entry +// +template +class macho_dylib_reference { +public: + uint32_t isym() const INLINE { return E::getBits(fields, 0, 24); } + void set_isym(uint32_t value) INLINE { E::setBits(fields, value, 0, 24); } + + uint8_t flags() const INLINE { return E::getBits(fields, 24, 8); } + void set_flags(uint8_t value) INLINE { E::setBits(fields, value, 24, 8); } + + typedef typename P::E E; +private: + uint32_t fields; +}; + + + +// +// mach-o two-level hints load command +// +template +class macho_dylib_table_of_contents { +public: + uint32_t symbol_index() const INLINE { return E::get32(fields.symbol_index); } + void set_symbol_index(uint32_t value) INLINE { E::set32(fields.symbol_index, value); } + + uint32_t module_index() const INLINE { return E::get32(fields.module_index); } + void set_module_index(uint32_t value) INLINE { E::set32(fields.module_index, value); } + + typedef typename P::E E; +private: + dylib_table_of_contents fields; +}; + + + +// +// mach-o two-level hints load command +// +template +class macho_twolevel_hints_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t offset() const INLINE { return E::get32(fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } + + uint32_t nhints() const INLINE { return E::get32(fields.nhints); } + void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } + + typedef typename P::E E; +private: + twolevel_hints_command fields; +}; + + +// +// mach-o threads load command +// +template +class macho_thread_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t flavor() const INLINE { return E::get32(fields_flavor); } + void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } + + uint32_t count() const INLINE { return E::get32(fields_count); } + void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } + + uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } + void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } + + typedef typename P::E E; + typedef typename P::uint_t pint_t; +private: + struct thread_command fields; + uint32_t fields_flavor; + uint32_t fields_count; + pint_t thread_registers[1]; +}; + + +// +// mach-o misc data +// +template +class macho_linkedit_data_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } + void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } + + uint32_t datasize() const INLINE { return E::get32(fields.datasize); } + void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } + + + typedef typename P::E E; +private: + struct linkedit_data_command fields; +}; + + +// +// mach-o rpath +// +template +class macho_rpath_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t path_offset() const INLINE { return E::get32(fields.path.offset); } + void set_path_offset(uint32_t value) INLINE { E::set32(fields.path.offset, value); } + + const char* path() const INLINE { return (const char*)&fields + path_offset(); } + void set_path_offset() INLINE { set_path_offset(sizeof(fields)); } + + + typedef typename P::E E; +private: + struct rpath_command fields; +}; + + + +// +// mach-o symbol table entry +// +template struct macho_nlist_content {}; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; + +template +class macho_nlist { +public: + uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } + void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } + + uint8_t n_type() const INLINE { return entry.fields.n_type; } + void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } + + uint8_t n_sect() const INLINE { return entry.fields.n_sect; } + void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } + + uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } + void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } + + uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } + void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } + + typedef typename P::E E; +private: + macho_nlist_content

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) { if ( x > 0x00FFFFFF ) throw "scattered reloc r_address too large"; + uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + + +// +// mach-o encyrption info load command +// +template +class macho_encryption_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); } + void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); } + + uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); } + void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); } + + uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); } + void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); } + + typedef typename P::E E; +private: + encryption_info_command fields; +}; + + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/ld64/FireOpal/src/MachOReaderDylib.hpp b/ld64/FireOpal/src/MachOReaderDylib.hpp new file mode 100644 index 0000000..eb8e844 --- /dev/null +++ b/ld64/FireOpal/src/MachOReaderDylib.hpp @@ -0,0 +1,926 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_DYLIB_MACH_O__ +#define __OBJECT_FILE_DYLIB_MACH_O__ + +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ObjectFile.h" + +// +// +// To implement architecture xxx, you must write template specializations for the following method: +// Reader::validFile() +// +// + + + + +namespace mach_o { +namespace dylib { + + +// forward reference +template class Reader; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } +private: + const char* fName; +}; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template +class ExportAtom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const { return fName; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual bool dontDeadStrip() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const {} + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + typedef typename A::P P; + + ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal) + : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {} + virtual ~ExportAtom() {} + + ObjectFile::Reader& fOwner; + const char* fName; + uint32_t fOrdinal; + bool fWeakDefinition; + + static std::vector fgEmptyReferenceList; + static Segment fgImportSegment; +}; + +template +Segment ExportAtom::fgImportSegment("__LINKEDIT"); + +template +std::vector ExportAtom::fgEmptyReferenceList; + + + +class ImportReference : public ObjectFile::Reference +{ +public: + ImportReference(const char* name) + : fTarget(NULL), fTargetName(strdup(name)) {} + virtual ~ImportReference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return 0; } + virtual uint64_t getFixUpOffset() const { return 0; } + virtual const char* getTargetName() const { return fTargetName; } + virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } + virtual uint64_t getTargetOffset() const { return 0; } + virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } + virtual const char* getFromTargetName() const { return NULL; } + virtual uint64_t getFromTargetOffset() const { return 0; } + virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; } + virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } + virtual const char* getDescription() const { return "dylib import reference"; } + +private: + const ObjectFile::Atom* fTarget; + const char* fTargetName; +}; + + +// +// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace +// the imports of all flat dylibs are checked +// +template +class ImportAtom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return "flat-imports"; } + virtual const char* getDisplayName() const { return "flat_namespace undefines"; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const {} + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + typedef typename A::P P; + + ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector& imports) + : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); } + virtual ~ImportAtom() {} + void makeReferences(std::vector& imports) { + for (std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { + fReferences.push_back(new ImportReference(*it)); + } + } + + + ObjectFile::Reader& fOwner; + uint32_t fOrdinal; + std::vector fReferences; + + static Segment fgImportSegment; +}; + +template +Segment ImportAtom::fgImportSegment("__LINKEDIT"); + + + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + const DynamicLibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, + uint32_t ordinalBase); + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabs() { return NULL; } + virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; } + virtual const char* getInstallPath() { return fDylibInstallPath; } + virtual uint32_t getTimestamp() { return fDylibTimeStamp; } + virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } + virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } + virtual void processIndirectLibraries(DylibHander* handler); + virtual void setExplicitlyLinked() { fExplicitlyLinked = true; } + virtual bool explicitlyLinked() { return fExplicitlyLinked; } + virtual bool implicitlyLinked() { return fImplicitlyLinked; } + virtual bool providedExportAtom() { return fProvidedAtom; } + virtual const char* parentUmbrella() { return fParentUmbrella; } + virtual std::vector* getAllowableClients(); + virtual bool hasWeakExternals() { return fHasWeakExports; } + virtual bool isLazyLoadedDylib() { return fLazyLoaded; } + + virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } + +protected: + + struct ReExportChain { ReExportChain* prev; Reader* reader; }; + + void assertNoReExportCycles(ReExportChain*); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + typedef typename NameToAtomMap::iterator NameToAtomMapIterator; + + struct PathAndFlag { const char* path; bool reExport; }; + + bool isPublicLocation(const char* path); + void addSymbol(const char* name, bool weak, uint32_t ordinal); + + const char* fPath; + const char* fParentUmbrella; + std::vector fAllowableClients; + const char* fDylibInstallPath; + uint32_t fDylibTimeStamp; + uint32_t fDylibtCurrentVersion; + uint32_t fDylibCompatibilityVersion; + uint32_t fReExportedOrdinal; + std::vector fDependentLibraryPaths; + NameToAtomMap fAtoms; + NameSet fIgnoreExports; + bool fNoRexports; + bool fHasWeakExports; + const bool fLinkingFlat; + const bool fLinkingMainExecutable; + bool fExplictReExportFound; + bool fExplicitlyLinked; + bool fImplicitlyLinked; + bool fProvidedAtom; + bool fImplicitlyLinkPublicDylibs; + bool fLazyLoaded; + ObjectFile::Reader::ObjcConstraint fObjcContraint; + std::vector fReExportedChildren; + const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin; + std::vector fFlatImports; + + static bool fgLogHashtable; + static std::vector fgEmptyAtomList; +}; + +template +std::vector Reader::fgEmptyAtomList; +template +bool Reader::fgLogHashtable = false; + + +template +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + const DynamicLibraryOptions& dylibOptions, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), + fDylibCompatibilityVersion(0), fLinkingFlat(options.fFlatNamespace), + fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), + fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), + fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), + fObjcContraint(ObjectFile::Reader::kObjcNone), + fDeploymentVersionMin(options.fVersionMin) +{ + // sanity check + if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) + throw "not a valid mach-o object file"; + + fPath = strdup(path); + + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

) + header->sizeofcmds()); + + // write out path for -whatsloaded option + if ( options.fLogAllFiles ) + printf("%s\n", path); + + if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) ) + warning("using -root_safe but linking against %s which is not root safe", path); + + if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) ) + warning("using -setuid_safe but linking against %s which is not setuid safe", path); + + // a "blank" stub has zero load commands + if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { + // no further processing needed + munmap((caddr_t)fileContent, fileLength); + return; + } + + + // optimize the case where we know there is no reason to look at indirect dylibs + fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); + fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); + bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; + + // pass 1 builds list of all dependent libraries + const macho_load_command

* cmd = cmds; + if ( trackDependentLibraries ) { + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_REEXPORT_DYLIB: + fExplictReExportFound = true; + // fall into next case + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + PathAndFlag entry; + entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); + entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + fDependentLibraryPaths.push_back(entry); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); + } + } + + // pass 2 determines re-export info + const macho_dysymtab_command

* dynamicInfo = NULL; + const macho_nlist

* symbolTable = NULL; + const char* strings = NULL; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); + strings = (char*)header + symtab->stroff(); + } + break; + case LC_DYSYMTAB: + dynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_ID_DYLIB: + { + macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; + fDylibInstallPath = strdup(dylibID->name()); + fDylibTimeStamp = dylibID->timestamp(); + fDylibtCurrentVersion = dylibID->current_version(); + fDylibCompatibilityVersion = dylibID->compatibility_version(); + } + break; + case LC_SUB_UMBRELLA: + if ( trackDependentLibraries ) { + const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + } + break; + case LC_SUB_LIBRARY: + if ( trackDependentLibraries) { + const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + } + break; + case LC_SUB_FRAMEWORK: + fParentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + break; + case macho_segment_command

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

*)cmd)->segname(), "__OBJC") == 0 ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strcmp(sect->sectname(), "__image_info") == 0 ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + fObjcContraint = ObjectFile::Reader::kObjcGC; + else if ( (flags & 2) == 2 ) + fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + else + fObjcContraint = ObjectFile::Reader::kObjcRetainRelease; + } + else if ( sect->size() > 0 ) { + warning("can't parse __OBJC/__image_info section in %s", fPath); + } + } + } + } + } + + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); + } + + // Process the rest of the commands here. + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SUB_CLIENT: + const char *temp = strdup(((macho_sub_client_command

*)cmd)->client()); + fAllowableClients.push_back(temp); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + + // validate minimal load commands + if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", path); + if ( symbolTable == NULL ) + throw "binary missing LC_SYMTAB load command"; + if ( dynamicInfo == NULL ) + throw "binary missing LC_DYSYMTAB load command"; + + // if linking flat and this is a flat dylib, create one atom that references all imported symbols + if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { + std::vector importNames; + importNames.reserve(dynamicInfo->nundefsym()); + const macho_nlist

* start = &symbolTable[dynamicInfo->iundefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nundefsym()]; + for (const macho_nlist

* sym=start; sym < end; ++sym) { + importNames.push_back(&strings[sym->n_strx()]); + } + fFlatImports.push_back(new ImportAtom(*this, ordinalBase++, importNames)); + } + + // build hash table + if ( dynamicInfo->tocoff() == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); + const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; + fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + uint32_t index = ordinalBase; + for (const macho_nlist

* sym=start; sym < end; ++sym, ++index) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, index); + } + fReExportedOrdinal = index; + } + else { + int32_t count = dynamicInfo->ntoc(); + fAtoms.resize(count); // set initial bucket count + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, path); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + dynamicInfo->tocoff()); + for (int32_t i = 0; i < count; ++i) { + const uint32_t index = E::get32(toc[i].symbol_index); + const macho_nlist

* sym = &symbolTable[index]; + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, ordinalBase+i); + } + fReExportedOrdinal = ordinalBase + count; + } + + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + + + +template +void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) +{ + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != NULL ) { + ObjectFile::ReaderOptions::VersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinUnset; + if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { + switch ( symCond[6] - '0' ) { + case 0: + case 1: + symVersionCondition = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + symVersionCondition = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + symVersionCondition = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + symVersionCondition = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + symVersionCondition = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + symVersionCondition = ObjectFile::ReaderOptions::k10_6; + break; + } + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weak, ordinal); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } + } + } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol version: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->getPath()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( fIgnoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = weak; + bucket.ordinal = ordinal; + if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); + fAtoms[strdup(name)] = bucket; + } +} + + +template +std::vector& Reader::getAtoms() +{ + return fFlatImports; +} + + +template +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + std::vector* atoms = NULL; + + NameToAtomMapIterator pos = fAtoms.find(name); + if ( pos != fAtoms.end() ) { + if ( pos->second.atom == NULL ) { + // instantiate atom and update hash table + pos->second.atom = new ExportAtom(*this, name, pos->second.weak, pos->second.ordinal); + fProvidedAtom = true; + if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); + } + // return a vector of one atom + atoms = new std::vector; + atoms->push_back(pos->second.atom); + } + else { + if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); + // if not supposed to ignore this export, see if I have it + if ( fIgnoreExports.count(name) == 0 ) { + // look in children that I re-export + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); + std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); + if ( childAtoms != NULL ) { + // make a new atom that says this reader is the owner + bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); + // return a vector of one atom + ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); + fProvidedAtom = true; + atoms = new std::vector; + atoms->push_back(newAtom); + delete childAtoms; + return atoms; + } + } + } + } + return atoms; +} + + + +template +bool Reader::isPublicLocation(const char* path) +{ + // -no_implicit_dylibs disables this optimization + if ( ! fImplicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&path[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &path[27]; + if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void Reader::processIndirectLibraries(DylibHander* handler) +{ + if ( fLinkingFlat ) { + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + handler->findDylib(it->path, this->getPath()); + } + } + else if ( fNoRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + if ( it->reExport ) { + //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); + if ( isPublicLocation(child->getInstallPath()) ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( this->explicitlyLinked() || this->implicitlyLinked() ) { + //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); + ((Reader*)child)->setImplicitlyLinked(); + } + else + fReExportedChildren.push_back(child); + } + else { + // add all child's symbols to me + fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } + } + else if ( !fExplictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); + const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->getPath(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = NULL; + chain.reader = this; + this->assertNoReExportCycles(&chain); +} + +template +void Reader::assertNoReExportCycles(ReExportChain* prev) +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.reader = this; + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + ObjectFile::Reader* child = *it; + // check child is not already in chain + for (ReExportChain* p = prev; p != NULL; p = p->prev) { + if ( p->reader == child ) { + throwf("cycle in dylib re-exports with %s", child->getPath()); + } + } + ((Reader*)(*it))->assertNoReExportCycles(&chain); + } +} + + +template +std::vector* Reader::getAllowableClients() +{ + std::vector* result = new std::vector; + for (typename std::vector::iterator it = fAllowableClients.begin(); + it != fAllowableClients.end(); + it++) { + result->push_back(*it); + } + return (fAllowableClients.size() != 0 ? result : NULL); +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +}; // namespace dylib +}; // namespace mach_o + + +#endif // __OBJECT_FILE_DYLIB_MACH_O__ diff --git a/ld64/FireOpal/src/MachOReaderRelocatable.hpp b/ld64/FireOpal/src/MachOReaderRelocatable.hpp new file mode 100644 index 0000000..73c58e2 --- /dev/null +++ b/ld64/FireOpal/src/MachOReaderRelocatable.hpp @@ -0,0 +1,4583 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_MACH_O__ +#define __OBJECT_FILE_MACH_O__ + +#include +#include +#include +#include + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ObjectFile.h" +#include "dwarf2.h" +#include "debugline.h" + + +// +// +// To implement architecture xxx, you must write template specializations for the following six methods: +// Reader::validFile() +// Reader::validSectionType() +// Reader::addRelocReference() +// Reference::getDescription() +// +// + + + +extern __attribute__((noreturn)) void throwf(const char* format, ...); +extern void warning(const char* format, ...); + +namespace mach_o { +namespace relocatable { + + + +class ReferenceSorter +{ +public: + bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right) + { + return ( left->getFixUpOffset() < right->getFixUpOffset() ); + } +}; + + +// forward reference +template class Reader; + +struct AtomAndOffset +{ + AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {} + AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} + ObjectFile::Atom* atom; + uint32_t offset; +}; + + +template +class Reference : public ObjectFile::Reference +{ +public: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + + Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget); + Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget); + Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset); + + virtual ~Reference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const; + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const; + virtual uint8_t getKind() const { return (uint8_t)fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } + virtual const char* getTargetName() const { return (fToTargetName != NULL) ? fToTargetName : fToTarget.atom->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } + virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); } + virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } + virtual const char* getFromTargetName() const { return (fFromTargetName != NULL) ? fFromTargetName : fFromTarget.atom->getName(); } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } + virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } + virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } + virtual void setFromTargetName(const char* name) { fFromTargetName = name; } + virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } + virtual const char* getDescription() const; + virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } + + static bool fgForFinalLinkedImage; + +private: + pint_t fFixUpOffsetInSrc; + AtomAndOffset fToTarget; + AtomAndOffset fFromTarget; + const char* fToTargetName; + const char* fFromTargetName; + Kinds fKind; + +}; + +template bool Reference::fgForFinalLinkedImage = true; + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) + : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL), + fKind(kind) +{ + // make reference a by-name unless: + // - the reference type is only used with direct references + // - the target is translation unit scoped + // - the target kind is not regular (is weak or tentative) + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) + && (toTarget.atom != at.atom) ) { + fToTargetName = toTarget.atom->getName(); + //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName()); + fToTarget.atom = NULL; + } + ((class BaseAtom*)at.atom)->addReference(this); + //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset); +} + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget) + : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget), + fToTargetName(NULL), fFromTargetName(NULL), fKind(kind) +{ + // make reference a by-name where needed + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) + && (toTarget.atom != at.atom) ) { + fToTargetName = toTarget.atom->getName(); + fToTarget.atom = NULL; + } + ((class BaseAtom*)at.atom)->addReference(this); + //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind, + // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset); +} + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset) + : fFixUpOffsetInSrc(at.offset), + fToTargetName(toName), fFromTargetName(NULL), fKind(kind) +{ + fToTarget.offset = toOffset; + ((class BaseAtom*)at.atom)->addReference(this); +} + +template +ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const +{ + if ( fgForFinalLinkedImage ) { + if ( (fKind == A::kDtraceProbe) || (fKind == A::kDtraceProbeSite) || (fKind == A::kDtraceIsEnabledSite) || (fKind == A::kDtraceTypeReference) ) + return ObjectFile::Reference::kDontBind; + } + if ( fToTarget.atom == NULL ) + return ObjectFile::Reference::kUnboundByName; + if ( fToTargetName == NULL ) + return ObjectFile::Reference::kBoundDirectly; + else + return ObjectFile::Reference::kBoundByName; +} + +template +ObjectFile::Reference::TargetBinding Reference::getFromTargetBinding() const +{ + if ( fFromTarget.atom == NULL ) { + if ( fFromTargetName == NULL ) + return ObjectFile::Reference::kDontBind; + else + return ObjectFile::Reference::kUnboundByName; + } + else { + if ( fFromTargetName == NULL ) + return ObjectFile::Reference::kBoundDirectly; + else + return ObjectFile::Reference::kBoundByName; + } +} + + + +template +class Segment : public ObjectFile::Segment +{ +public: + Segment(const macho_section* sect); + virtual const char* getName() const { return fSection->segname(); } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } +private: + const macho_section* fSection; + bool fWritable; + bool fExecutable; +}; + +template +Segment::Segment(const macho_section* sect) + : fSection(sect), fWritable(true), fExecutable(false) +{ + if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { + fWritable = false; + fExecutable = true; + } + else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { + fWritable = true; + fExecutable = true; + } +} + + +class DataSegment : public ObjectFile::Segment +{ +public: + virtual const char* getName() const { return "__DATA"; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return true; } + virtual bool isContentExecutable() const { return false; } + + static DataSegment fgSingleton; +}; + +DataSegment DataSegment::fgSingleton; + +class LinkEditSegment : public ObjectFile::Segment +{ +public: + virtual const char* getName() const { return "__LINKEDIT"; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } + + static LinkEditSegment fgSingleton; +}; + +LinkEditSegment LinkEditSegment::fgSingleton; + +class BaseAtom : public ObjectFile::Atom +{ +public: + BaseAtom() : fStabsStartIndex(0), fStabsCount(0) {} + + virtual void setSize(uint64_t size) = 0; + virtual void addReference(ObjectFile::Reference* ref) = 0; + virtual void sortReferences() = 0; + virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; + virtual uint64_t getObjectAddress() const = 0; + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual void setOrdinal(uint32_t value) { fOrdinal = value; } + virtual const void* getSectionRecord() const = 0; + virtual bool isAlias() const { return false; } + + uint32_t fStabsStartIndex; + uint32_t fStabsCount; + uint32_t fOrdinal; +}; + +class BaseAtomSorter +{ +public: + bool operator()(const class BaseAtom* left, const class BaseAtom* right) { + if ( left == right ) + return false; + uint64_t leftAddr = left->getObjectAddress(); + uint64_t rightAddr = right->getObjectAddress(); + if ( leftAddr < rightAddr ) { + return true; + } + else if ( leftAddr > rightAddr ) { + return false; + } + else { + // if they have same address, one might be the end of a section and the other the start of the next section + const void* leftSection = left->getSectionRecord(); + const void* rightSection = right->getSectionRecord(); + if ( leftSection != rightSection ) { + return ( leftSection < rightSection ); + } + // if they have same address and section, one might be an alias + bool leftAlias = left->isAlias(); + bool rightAlias = right->isAlias(); + if ( leftAlias && rightAlias ) { + // sort multiple aliases for same address first by scope + ObjectFile::Atom::Scope leftScope = left->getScope(); + ObjectFile::Atom::Scope rightScope = right->getScope(); + if ( leftScope != rightScope ) { + return ( leftScope < rightScope ); + } + // sort multiple aliases for same address then by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + } + else if ( leftAlias ) { + return true; + } + else if ( rightAlias ) { + return false; + } + else { + // they must be tentative defintions + switch ( left->getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + // sort tentative definitions by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + case ObjectFile::Atom::kAbsoluteSymbol: + // sort absolute symbols with same address by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + default: + // hack for rdar://problem/5102873 + if ( !left->isZeroFill() || !right->isZeroFill() ) + warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); + break; + } + } + } + return false; + } +}; + + +// +// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry +// pointing to it. A C function or global variable is represented by one of these atoms. +// +// +template +class SymbolAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) + ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } + virtual bool dontDeadStrip() const; + virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } + virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } + virtual uint64_t getSize() const { return fSize; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual Segment& getSegment() const { return *fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getLineInfo() const { return (std::vector*)&fLineInfo; } + virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size); + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } + virtual uint64_t getObjectAddress() const { return fAddress; } + virtual const void* getSectionRecord() const { return (const void*)fSection; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + SymbolAtom(Reader&, const macho_nlist

*, const macho_section

*); + virtual ~SymbolAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + pint_t fAddress; + pint_t fSize; + const macho_section

* fSection; + Segment* fSegment; + ReferenceVector fReferences; + std::vector fLineInfo; + ObjectFile::Atom::Scope fScope; + SymbolTableInclusion fSymbolTableInclusion; + ObjectFile::Alignment fAlignment; +}; + + +template +SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const macho_section

* section) + : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fAlignment(0) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( (type & N_TYPE) == N_SECT ) { + // real definition + fSegment = new Segment(fSection); + fAddress = fSymbol->n_value(); + pint_t sectionStartAddr = section->addr(); + pint_t sectionEndAddr = section->addr()+section->size(); + if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) { + throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX", + this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(), + (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) ); + } + } + else { + warning("unknown symbol type: %d", type); + } + + //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress); + // support for .o files built with old ld64 + if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) { + const char* name = this->getName(); + const int nameLen = strlen(name); + if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) { + // switch symbol to point at name that does not have trailing $stub + char correctName[nameLen]; + strncpy(correctName, name, nameLen-5); + correctName[nameLen-5] = '\0'; + const macho_nlist

* symbolsStart = fOwner.fSymbols; + const macho_nlist

* symbolsEnd = &symbolsStart[fOwner.fSymbolCount]; + for(const macho_nlist

* s = symbolsStart; s < symbolsEnd; ++s) { + if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) { + fSymbol = s; + break; + } + } + } + } + // support for labeled stubs + switch ( section->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + setSize(section->reserved2()); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + setSize(sizeof(pint_t)); + break; + case S_4BYTE_LITERALS: + setSize(4); + break; + case S_8BYTE_LITERALS: + setSize(8); + break; + case S_16BYTE_LITERALS: + setSize(16); + break; + case S_CSTRING_LITERALS: + setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); + break; + case S_REGULAR: + case S_ZEROFILL: + case S_COALESCED: + // size calculate later after next atom is found + break; + } + + // compute alignment + fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); + + // compute whether this atom needs to be in symbol table + if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; + } + else if ( fOwner.fOptions.fForFinalLinkedImage + && ((section->flags() & SECTION_TYPE) == S_COALESCED) + && ((section->flags() & S_ATTR_NO_TOC) == S_ATTR_NO_TOC) + && ((section->flags() & S_ATTR_STRIP_STATIC_SYMS) == S_ATTR_STRIP_STATIC_SYMS) + && (strcmp(section->sectname(), "__eh_frame") == 0) ) { + // .eh symbols exist so the linker can associate them with functions + // removing them from final linked images is a big space savings rdar://problem/4180168 + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment + fAlignment = ObjectFile::Alignment(0); + } + else if ( fOwner.fOptions.fForFinalLinkedImage + && ((section->flags() & SECTION_TYPE) == S_REGULAR) + && (strncmp(section->sectname(), "__gcc_except_tab", 16) == 0) + && (strncmp(this->getName(), "GCC_except_table", 16) == 0) ) { + // GCC_except_table* symbols don't need to exist in final linked image + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + } + else if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) { + // labels beginning with a lowercase ell are automatically removed in final linked images + // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + } + else { + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } + + // work around malformed icc generated .o files + // if section starts with a symbol and that symbol address does not match section alignment, then force it to + if ( (section->addr() == fAddress) && (fAlignment.modulus != 0) ) + fAlignment.modulus = 0; +} + +template +bool SymbolAtom::dontDeadStrip() const +{ + // the symbol can have a no-dead-strip bit + if ( (fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 ) + return true; + // or the section can have a no-dead-strip bit + return ( fSection->flags() & S_ATTR_NO_DEAD_STRIP ); +} + + +template +const char* SymbolAtom::getSectionName() const +{ + if ( fOwner.fOptions.fForFinalLinkedImage && (strcmp(fSection->sectname(), "__textcoal_nt") == 0) ) + return "__text"; + + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +template +ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const +{ + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return ref->getTarget(); + } + return *((ObjectFile::Atom*)NULL); +} + + +class Beyond +{ +public: + Beyond(uint64_t offset) : fOffset(offset) {} + bool operator()(ObjectFile::Reference* ref) const { + return ( ref->getFixUpOffset() >= fOffset ); + } +private: + uint64_t fOffset; +}; + + +template +void SymbolAtom::setSize(uint64_t size) +{ + // when resizing, any references beyond the new size are tossed + if ( (fSize != 0) && (fReferences.size() > 0) ) + fReferences.erase(std::remove_if(fReferences.begin(), fReferences.end(), Beyond(size)), fReferences.end()); + // set new size + fSize = size; +} + +template +void SymbolAtom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + +// +// A SymbolAliasAtom represents an alternate name for a SymbolAtom +// +// +template +class SymbolAliasAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fAliasOf.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); } + virtual bool dontDeadStrip() const { return fDontDeadStrip; } + virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); } + virtual bool isThumb() const { return fAliasOf.isThumb(); } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const { return fAliasOf.getSectionName(); } + virtual Segment& getSegment() const { return (Segment&)fAliasOf.getSegment(); } + virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return fAliasOf.getAlignment(); } + virtual void copyRawContent(uint8_t buffer[]) const {} + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { } + virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } + virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } + virtual bool isAlias() const { return true; } + +protected: + typedef typename A::P P; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + SymbolAliasAtom(const char* name, const macho_nlist

*, const BaseAtom& ); + virtual ~SymbolAliasAtom() {} + + const char* fName; + const BaseAtom& fAliasOf; + ObjectFile::Atom::Scope fScope; + bool fDontDeadStrip; + ReferenceVector fReferences; +}; + + +template +SymbolAliasAtom::SymbolAliasAtom(const char* name, const macho_nlist

* symbol, const BaseAtom& aliasOf) + : fName(name), fAliasOf(aliasOf) +{ + //fprintf(stderr, "SymbolAliasAtom(%p) %s\n", this, name); + if ( symbol != NULL ) { + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + fDontDeadStrip = ((symbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); + } + else { + // aliases defined on the command line are initially global scope + fScope = ObjectFile::Atom::scopeGlobal; + fDontDeadStrip = false; + } + // add follow-on reference to real atom + new Reference(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf)); +} + + +// +// A TentativeAtom represents a C "common" or "tentative" defintion of data. +// For instance, "int foo;" is neither a declaration or a definition and +// is represented by a TentativeAtom. +// +template +class TentativeAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } + virtual bool isZeroFill() const { return true; } + virtual bool isThumb() const { return false; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) + ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } + virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } + virtual uint64_t getSize() const { return fSymbol->n_value(); } + virtual std::vector& getReferences() const { return fgNoReferences; } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } + virtual void sortReferences() { } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } + virtual const void* getSectionRecord() const { return NULL; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + friend class Reader; + + TentativeAtom(Reader&, const macho_nlist

*); + virtual ~TentativeAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + ObjectFile::Atom::Scope fScope; + static std::vector fgNoReferences; +}; + +template +std::vector TentativeAtom::fgNoReferences; + +template +TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) + : fOwner(owner), fSymbol(symbol) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { + // tentative definition + } + else { + warning("unknown symbol type: %d", type); + } + //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName()); +} + + +template +ObjectFile::Alignment TentativeAtom::getAlignment() const +{ + uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc()); + if ( alignment == 0 ) { + // common symbols align to their size + // that is, a 4-byte common aligns to 4-bytes + // if this size is not a power of two, + // then round up to the next power of two + uint64_t size = this->getSize(); + alignment = 63 - (uint8_t)__builtin_clzll(size); + if ( size != (1ULL << alignment) ) + ++alignment; + } + // limit alignment of extremely large commons to 2^15 bytes (8-page) + if ( alignment < 12 ) + return ObjectFile::Alignment(alignment); + else + return ObjectFile::Alignment(12); +} + +template +const char* TentativeAtom::getSectionName() const +{ + if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal ) + return "__common"; + else + return "._tentdef"; +} + + +template +void TentativeAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +// +// An AnonymousAtom represents compiler generated data that has no name. +// For instance, a literal C-string or a 64-bit floating point constant +// is represented by an AnonymousAtom. +// +template +class AnonymousAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fSynthesizedName; } + virtual const char* getDisplayName() const; + virtual ObjectFile::Atom::Scope getScope() const; + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } + virtual bool dontDeadStrip() const { return fDontDeadStrip; } + virtual bool isZeroFill() const; + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return fSize; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual Segment& getSegment() const { return *fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { fSize = size; } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); } + virtual uint64_t getObjectAddress() const { return fAddress; } + virtual const void* getSectionRecord() const { return (const void*)fSection; } + BaseAtom* redirectTo() { return fRedirect; } + bool isWeakImportStub() { return fWeakImportStub; } + void resolveName(); + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + AnonymousAtom(Reader&, const macho_section

*, pint_t addr, pint_t size); + virtual ~AnonymousAtom() {} + static bool cstringsHaveLabels(); + + Reader& fOwner; + const char* fSynthesizedName; + const char* fDisplayName; + const macho_section

* fSection; + pint_t fAddress; + pint_t fSize; + Segment* fSegment; + ReferenceVector fReferences; + BaseAtom* fRedirect; + bool fDontDeadStrip; + bool fWeakImportStub; + ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; + ObjectFile::Atom::Scope fScope; + ObjectFile::Atom::DefinitionKind fKind; +}; + +template +AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, pint_t addr, pint_t size) + : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), + fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), + fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition) +{ + fSegment = new Segment(fSection); + fRedirect = this; + uint8_t type = fSection->flags() & SECTION_TYPE; + //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath()); + switch ( type ) { + case S_ZEROFILL: + { + asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr); + } + break; + case S_COALESCED: + case S_REGULAR: + if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { + // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only + fSynthesizedName = ".objc_class_name_PENDING"; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + if ( fOwner.fOptions.fForFinalLinkedImage ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + else + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAsAbsolute; + fScope = ObjectFile::Atom::scopeGlobal; + } + else if ( strcmp(fSection->sectname(), "__cstring") == 0 ) { + // handle .o files created by old ld64 -r that are missing cstring section type + const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + } + else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) { + fSynthesizedName = "cfstring-pointer-name-PENDING"; + fScope = ObjectFile::Atom::scopeLinkageUnit; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + fDontDeadStrip = false; + fKind = ObjectFile::Atom::kWeakDefinition; + } + break; + case S_CSTRING_LITERALS: + { + const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } + break; + case S_4BYTE_LITERALS: + { + uint32_t value = E::get32(*(uint32_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + asprintf((char**)&fSynthesizedName, "4-byte-literal=0x%08X", value); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + } + break; + case S_8BYTE_LITERALS: + { + uint64_t value = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + asprintf((char**)&fSynthesizedName, "8-byte-literal=0x%016llX", value); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + } + break; + case S_16BYTE_LITERALS: + { + uint64_t value1 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + uint64_t value2 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 8 - section->addr())); + asprintf((char**)&fSynthesizedName, "16-byte-literal=0x%016llX,%016llX", value1, value2); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + } + break; + case S_LITERAL_POINTERS: + { + //uint32_t literalNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + //const char* str = (char*)(owner.fHeader) + section->offset() + literalNameAddr - section->addr(); + //asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", section->segname(), section->sectname(), str); + fSynthesizedName = "literal-pointer-name-PENDING"; + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + } + break; + case S_MOD_INIT_FUNC_POINTERS: + asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); + break; + case S_MOD_TERM_FUNC_POINTERS: + asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); + break; + case S_SYMBOL_STUBS: + { + uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2(); + index += fSection->reserved1(); + uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); + const macho_nlist

* sym = &fOwner.fSymbols[symbolIndex]; + uint32_t strOffset = sym->n_strx(); + // want name to not have $stub suffix, this is what automatic stub generation expects + fSynthesizedName = &fOwner.fStrings[strOffset]; + // check for weak import + fWeakImportStub = fOwner.isWeakImportSymbol(sym); + // sometimes the compiler gets confused and generates a stub to a static function + // if so, we should redirect any call to the stub to be calls to the real static function atom + if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) { + BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); + if ( staticAtom != NULL ) + fRedirect = staticAtom; + } + fKind = ObjectFile::Atom::kWeakDefinition; + // might be a spurious stub for a static function, make stub static too + if ( (sym->n_type() & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else + fScope = ObjectFile::Atom::scopeLinkageUnit; + } + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + { + fDontDeadStrip = false; + fScope = ObjectFile::Atom::scopeLinkageUnit; + uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); + index += fSection->reserved1(); + uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { + // Silly codegen with non-lazy pointer to a local symbol + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fOwner.fHeader)+fileOffset))); + // All atoms not created yet, so we need to scan symbol table + const macho_nlist

* closestSym = NULL; + const macho_nlist

* end = &fOwner.fSymbols[fOwner.fSymbolCount]; + for (const macho_nlist

* sym = fOwner.fSymbols; sym < end; ++sym) { + if ( ((sym->n_type() & N_TYPE) == N_SECT) + && ((sym->n_type() & N_STAB) == 0) ) { + if ( sym->n_value() == nonLazyPtrValue ) { + const char* name = &fOwner.fStrings[sym->n_strx()]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + // add direct reference to target later, because its atom may not be constructed yet + fOwner.fLocalNonLazys.push_back(this); + fScope = ObjectFile::Atom::scopeTranslationUnit; + return; + } + else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) { + closestSym = sym; + } + } + } + // add direct reference to target later, because its atom may not be constructed yet + if ( closestSym != NULL ) { + const char* name = &fOwner.fStrings[closestSym->n_strx()]; + char* str; + asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value()); + fSynthesizedName = str; + } + else { + fSynthesizedName = "$interior$non_lazy_ptr"; + } + fScope = ObjectFile::Atom::scopeTranslationUnit; + fOwner.fLocalNonLazys.push_back(this); + return; + } + const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; + const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + if ( type == S_LAZY_SYMBOL_POINTERS ) + strcat(str, "$lazy_ptr"); + else + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + + // optimize __IMPORT segment out of i386 dyld or if -slow_stubs is used + if ( (fOwner.fOptions.fForDyld || fOwner.fOptions.fSlowx86Stubs) && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { + macho_section

* dummySection = new macho_section

(*fSection); + dummySection->set_segname("__DATA"); + dummySection->set_sectname("__nl_symbol_ptr"); + fSection = dummySection; + fSegment = new Segment(fSection); + } + + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) + fKind = ObjectFile::Atom::kWeakDefinition; + + if ( (targetSymbol->n_type() & N_EXT) == 0 ) { + // target is translation unit scoped, so add direct reference to target + //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); + new Reference(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value())); + } + else { + if ( fOwner.isWeakImportSymbol(targetSymbol) ) + new Reference(A::kPointerWeakImport, AtomAndOffset(this), name, 0); + else + new Reference(A::kPointer, AtomAndOffset(this), name, 0); + } + } + break; + default: + throwf("section type %d not supported with address=0x%08X", type, addr); + } + //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); +} + +// x86_64 uses L labels on cstrings to allow relocs with addends +template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } +template bool AnonymousAtom::cstringsHaveLabels() { return false; } + + +template +void AnonymousAtom::resolveName() +{ + if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) { + std::vector& references = this->getReferences(); + // references are not yet sorted, so scan the vector + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { + const char* superStr = (*rit)->getTargetName(); + if ( strncmp(superStr, "cstring=", 8) == 0 ) { + const char* superClassName; + asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]); + new Reference(A::kNoFixUp, AtomAndOffset(this), superClassName, 0); + } + break; + } + } + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { + const char* classStr = (*rit)->getTargetName(); + if ( strncmp(classStr, "cstring=", 8) == 0 ) { + asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]); + } + break; + } + } + } + else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) { + std::vector& references = this->getReferences(); + if ( references.size() < 1 ) + throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname()); + ObjectFile::Reference* ref = references[0]; + const char* str = ref->getTargetName(); + if ( strncmp(str, "cstring=", 8) == 0 ) { + asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]); + } + } + else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { + // references are not yet sorted, so scan the vector + std::vector& references = this->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { + const char* superStr = (*rit)->getTargetName(); + if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) { + asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]); + } + else { + // compiled with -fwritable-strings or a non-ASCII string + ObjectFile::Atom& stringDataAtom = (*rit)->getTarget(); + uint8_t buffer[stringDataAtom.getSize()]; + stringDataAtom.copyRawContent(buffer); + fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable + fScope = ObjectFile::Atom::scopeTranslationUnit; + fSynthesizedName = "cfstring-not-coalesable"; + } + break; + } + } + } +} + + +template +const char* AnonymousAtom::getDisplayName() const +{ + if ( fSynthesizedName != NULL ) + return fSynthesizedName; + + if ( fDisplayName != NULL ) + return fDisplayName; + + if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); + } + else { + asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); + } + return fDisplayName; +} + + +template +ObjectFile::Atom::Scope AnonymousAtom::getScope() const +{ + return fScope; +} + + +template +bool AnonymousAtom::isZeroFill() const +{ + return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL ); +} + + +template +const char* AnonymousAtom::getSectionName() const +{ + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +template +ObjectFile::Alignment AnonymousAtom::getAlignment() const +{ + switch ( fSection->flags() & SECTION_TYPE ) { + case S_4BYTE_LITERALS: + return ObjectFile::Alignment(2); + case S_8BYTE_LITERALS: + return ObjectFile::Alignment(3); + case S_16BYTE_LITERALS: + return ObjectFile::Alignment(4); + case S_NON_LAZY_SYMBOL_POINTERS: + return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t))); + case S_CSTRING_LITERALS: + if ( ! fOwner.fOptions.fForFinalLinkedImage ) + return ObjectFile::Alignment(fSection->align()); + default: + return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); + } +} + + +template +ObjectFile::Atom& AnonymousAtom::getFollowOnAtom() const +{ + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return ref->getTarget(); + } + return *((ObjectFile::Atom*)NULL); +} + +template +void AnonymousAtom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + + +// +// An AbsoluteAtom represents an N_ABS symbol which can only be created in +// assembly language and usable by static executables such as the kernel/ +// +template +class AbsoluteAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; } + virtual bool dontDeadStrip() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgNoReferences; } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const { return "._absolute"; } + virtual ObjectFile::Segment& getSegment() const { return LinkEditSegment::fgSingleton; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } + virtual void sortReferences() { } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } + virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } + virtual const void* getSectionRecord() const { return NULL; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + friend class Reader; + + AbsoluteAtom(Reader&, const macho_nlist

*); + virtual ~AbsoluteAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + ObjectFile::Atom::Scope fScope; + static std::vector fgNoReferences; +}; + +template +std::vector AbsoluteAtom::fgNoReferences; + +template +AbsoluteAtom::AbsoluteAtom(Reader& owner, const macho_nlist

* symbol) + : fOwner(owner), fSymbol(symbol) +{ + // store absolute adress in fSectionOffset + fSectionOffset = symbol->n_value(); + // compute scope + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName()); +} + + + +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent); + Reader(const uint8_t* fileContent, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return fModTime; } + virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; } + virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return &fStabs; } + virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; } + virtual uint32_t updateCpuConstraint(uint32_t current); + virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); } + virtual bool objcReplacementClasses(){ return fReplacementClasses; } + virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; } + + bool getTranslationUnitSource(const char** dir, const char** name) const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + //typedef typename std::vector*> AtomVector; + //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser + typedef typename A::ReferenceKinds Kinds; + friend class AnonymousAtom; + friend class TentativeAtom; + friend class AbsoluteAtom; + friend class SymbolAtom; + typedef std::map AddrToAtomMap; + + void addReferencesForSection(const macho_section

* sect); + bool addRelocReference(const macho_section

* sect, const macho_relocation_info

* reloc); + bool addRelocReference_powerpc(const macho_section

* sect, const macho_relocation_info

* reloc); + bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); + static bool isWeakImportSymbol(const macho_nlist

* sym); + static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); + static const char* assureFullPath(const char* path); + AtomAndOffset findAtomAndOffset(pint_t addr); + AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr); + Reference* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr); + Reference* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr); + Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr); + Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr); + Reference* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset); + Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); + Reference* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset); + void validSectionType(uint8_t type); + void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName); + void setCpuConstraint(uint32_t cpusubtype); + + BaseAtom* findAtomByName(const char*); + + const char* fPath; + time_t fModTime; + uint32_t fOrdinalBase; + const ObjectFile::ReaderOptions& fOptions; + const macho_header

* fHeader; + const char* fStrings; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const macho_segment_command

* fSegment; + const uint32_t* fIndirectTable; + std::vector fAtoms; + AddrToAtomMap fAddrToAtom; + AddrToAtomMap fAddrToAbsoluteAtom; + std::vector*> fLocalNonLazys; + std::vector*> fAtomsPendingAName; + std::set*> fSectionsWithAtomsPendingAName; + std::vector fDtraceProviderInfo; + ObjectFile::Reader::DebugInfoKind fDebugInfo; + bool fHasUUID; + const macho_section

* fDwarfDebugInfoSect; + const macho_section

* fDwarfDebugAbbrevSect; + const macho_section

* fDwarfDebugLineSect; + const char* fDwarfTranslationUnitDir; + const char* fDwarfTranslationUnitFile; + std::map fDwarfIndexToFile; + std::vector fStabs; + bool fAppleObjc; + bool fHasDTraceProbes; + bool fHaveIndirectSymbols; + bool fReplacementClasses; + bool fHasLongBranchStubs; + ObjectFile::Reader::ObjcConstraint fObjConstraint; + uint32_t fCpuConstraint; +}; + +template +Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header

*)fileContent), + fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), + fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), + fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), + fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), + fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a valid mach-o object file"; + + Reference::fgForFinalLinkedImage = options.fForFinalLinkedImage; + + // write out path for -t or -whatsloaded option + if ( options.fLogObjectFiles || options.fLogAllFiles ) + printf("%s\n", path); + + // cache intersting pointers + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; + uint32_t undefinedStartIndex = 0; + uint32_t undefinedEndIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)header + symtab->symoff()); + fStrings = (char*)header + symtab->stroff(); + if ( undefinedEndIndex == 0 ) { + undefinedStartIndex = 0; + undefinedEndIndex = symtab->nsyms(); + } + } + break; + case LC_DYSYMTAB: + { + const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); + undefinedStartIndex = dsymtab->iundefsym(); + undefinedEndIndex = undefinedStartIndex + dsymtab->nundefsym(); + } + break; + case LC_UUID: + fHasUUID = true; + break; + + default: + if ( cmd->cmd() == macho_segment_command

::CMD ) { + fSegment = (macho_segment_command

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

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); + } + + // if there are no load commands, then this file has no content, so no atoms + if ( header->ncmds() < 1 ) + return; + + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[fSegment->nsects()]; + + // inital guess for number of atoms + fAtoms.reserve(fSymbolCount); + + // add all atoms that have entries in symbol table + const macho_section

* sections = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + for (int i=fSymbolCount-1; i >= 0 ; --i) { + // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the reaal name + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + uint8_t type = (sym.n_type() & N_TYPE); + if ( type == N_SECT ) { + const macho_section

* section = §ions[sym.n_sect()-1]; + pint_t sectionEndAddr = section->addr() + section->size(); + bool suppress = false; + // ignore atoms in debugger sections + if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { + if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) { + // ignore dtrace probe labels + fHasDTraceProbes = true; + } + else if ( fStrings[sym.n_strx()] == 'L' ) { + // ignore L labels, + } + else { + // ignore labels for atoms in other sections + switch ( section->flags() & SECTION_TYPE ) { + case S_REGULAR: + if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 ) + suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS + case S_ZEROFILL: + case S_COALESCED: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_16BYTE_LITERALS: + case S_CSTRING_LITERALS: + { + BaseAtom* newAtom; + typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value()); + if ( (pos != fAddrToAtom.end()) && (strcmp(pos->second->getSectionName(), section->sectname())==0) ) { + // another label to an existing address in the same section, make this an alias + newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *pos->second); + } + else { + // make SymbolAtom atom for this address + newAtom = new SymbolAtom(*this, &sym, section); + // don't add symbols at end of section to addr->atom map + if ( sym.n_value() != sectionEndAddr ) + fAddrToAtom[newAtom->getObjectAddress()] = newAtom; + } + if ( ! suppress ) + fAtoms.push_back(newAtom); + } + break; + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + // ignore symboled stubs produces by old ld64 + break; + default: + warning("symbol %s found in unsupported section in %s", + &fStrings[sym.n_strx()], this->getPath()); + } + } + } + } + else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { + fAtoms.push_back(new TentativeAtom(*this, &sym)); + } + else if ( type == N_ABS ) { + const char* symName = &fStrings[sym.n_strx()]; + if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) { + // ignore .objc_class_name_* symbols + fAppleObjc = true; + } + else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) { + // ignore empty *.eh symbols + } + else { + BaseAtom* abAtom = new AbsoluteAtom(*this, &sym); + fAtoms.push_back(abAtom); + fAddrToAbsoluteAtom[sym.n_value()] = abAtom; + } + } + else if ( type == N_INDR ) { + fHaveIndirectSymbols = true; + } + } + } + + // add all fixed size anonymous atoms from special sections + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + pint_t atomSize = 0; + uint8_t type (sect->flags() & SECTION_TYPE); + validSectionType(type); + bool suppress = false; + switch ( type ) { + case S_SYMBOL_STUBS: + suppress = true; + atomSize = sect->reserved2(); + break; + case S_LAZY_SYMBOL_POINTERS: + suppress = true; + atomSize = sizeof(pint_t); + break; + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LITERAL_POINTERS: + case S_MOD_INIT_FUNC_POINTERS: + case S_MOD_TERM_FUNC_POINTERS: + atomSize = sizeof(pint_t); + break; + case S_INTERPOSING: + atomSize = sizeof(pint_t)*2; + break; + case S_4BYTE_LITERALS: + atomSize = 4; + break; + case S_8BYTE_LITERALS: + atomSize = 8; + break; + case S_16BYTE_LITERALS: + atomSize = 16; + break; + case S_REGULAR: + // special case ObjC classes to synthesize .objc_class_name_* symbols + if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) && fAppleObjc ) { + // gcc sometimes over aligns class structure + uint32_t align = 1 << sect->align(); + atomSize = ((12 * sizeof(pint_t)) + align-1) & (-align); + } + // get objc Garbage Collection info + else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) + || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset()); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + fObjConstraint = ObjectFile::Reader::kObjcGC; + else if ( (flags & 2) == 2 ) + fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + else + fObjConstraint = ObjectFile::Reader::kObjcRetainRelease; + if ( (flags & 1) == 1 ) + fReplacementClasses = true; + // don't make atom for this section + atomSize = sect->size(); + suppress = true; + } + else { + warning("can't parse __OBJC/__image_info section in %s", fPath); + } + } + // special case constant NS/CFString literals and make an atom out of each one + else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { + atomSize = 4 * sizeof(pint_t); + } + break; + } + if ( atomSize != 0 ) { + for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { + pint_t atomAddr = sect->addr() + sectOffset; + // add if not already an atom at that address + if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) { + AnonymousAtom* newAtom = new AnonymousAtom(*this, sect, atomAddr, atomSize); + if ( !suppress ) + fAtoms.push_back(newAtom); + fAddrToAtom[atomAddr] = newAtom->redirectTo(); + } + } + } + } + + // add all c-string anonymous atoms + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { + uint32_t stringLen; + pint_t stringAddr; + BaseAtom* mostAlignedEmptyString = NULL; + uint32_t mostAlignedEmptyStringTrailingZeros = 0; + std::vector > emptyStrings; + for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { + stringAddr = sect->addr() + sectOffset; + stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; + // add if not already an atom at that address + if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) { + BaseAtom* newAtom = new AnonymousAtom(*this, sect, stringAddr, stringLen); + if ( stringLen == 1 ) { + // because of padding it may look like there are lots of empty strings, keep track of all + emptyStrings.push_back(std::make_pair(stringAddr, newAtom)); + // record empty string with greatest alignment requirement + uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr); + if ( (mostAlignedEmptyString == NULL) + || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) { + mostAlignedEmptyString = newAtom; + mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros; + } + } + else { + fAtoms.push_back(newAtom); + fAddrToAtom[stringAddr] = newAtom; + } + } + } + // map all uses of empty strings to the most aligned one + if ( mostAlignedEmptyString != NULL ) { + // make most aligned atom a real atom + fAtoms.push_back(mostAlignedEmptyString); + // map all other empty atoms to this one + for (typename std::vector >::iterator it=emptyStrings.begin(); it != emptyStrings.end(); it++) { + fAddrToAtom[it->first] = mostAlignedEmptyString; + } + } + } + } + + // sort all atoms so far by address and section + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + + //fprintf(stderr, "sorted atoms:\n"); + //for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) + // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName()); + + // create atoms to cover any non-debug ranges not handled above + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + pint_t sectionStartAddr = sect->addr(); + pint_t sectionEndAddr = sect->addr() + sect->size(); + const bool setFollowOnAtom = ! this->canScatterAtoms(); + if ( sect->size() != 0 ) { + // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change + if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { + fDebugInfo = kDebugInfoDwarf; + if ( strcmp(sect->sectname(), "__debug_info") == 0 ) + fDwarfDebugInfoSect = sect; + else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) + fDwarfDebugAbbrevSect = sect; + else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) + fDwarfDebugLineSect = sect; + } + else { + if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { + throw "object file contains old DWARF debug info - rebuild with newer compiler"; + } + uint8_t type (sect->flags() & SECTION_TYPE); + switch ( type ) { + case S_REGULAR: + case S_ZEROFILL: + case S_COALESCED: + // if there is not an atom already at the start of this section, add an anonymous one + pint_t previousAtomAddr = 0; + BaseAtom* previousAtom = NULL; + if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { + BaseAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr(), 0); + fAddrToAtom[sect->addr()] = newAtom; + fAtoms.push_back(newAtom); + previousAtomAddr = sectionStartAddr; + previousAtom = newAtom; + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + } + // calculate size of all atoms in this section and add follow-on references + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + pint_t atomAddr = atom->getObjectAddress(); + if ( atom->getSectionRecord() == sect ) { + //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName()); + if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) { + previousAtom->setSize(atomAddr - previousAtomAddr); + if ( setFollowOnAtom && (atom != previousAtom) ) + new Reference(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom)); + } + previousAtomAddr = atomAddr; + previousAtom = atom; + } + } + if ( previousAtom != NULL ) { + // set last atom in section + previousAtom->setSize(sectionEndAddr - previousAtomAddr); + } + break; + } + } + } + } + + // check for object file that defines no objc classes, but uses objc classes + // check for dtrace provider info + for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( (sym.n_type() & N_TYPE) == N_UNDF ) { + const char* undefinedName = &fStrings[sym.n_strx()]; + if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) { + fAppleObjc = true; + } + else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) { + if ( strchr(undefinedName, '$') != NULL ) { + if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) { + // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* + // is extra provider info + fDtraceProviderInfo.push_back(undefinedName); + } + } + } + } + } + } + + // add relocation based references to sections that have atoms with pending names + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( fSectionsWithAtomsPendingAName.count(sect) != 0 ) + addReferencesForSection(sect); + } + + // update any anonymous atoms that need references built in order to name themselves + for (typename std::vector*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) { + (*it)->resolveName(); + } + + // add relocation based references to other sections + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( fSectionsWithAtomsPendingAName.count(sect) == 0 ) + addReferencesForSection(sect); + } + + // add objective-c references + if ( fAppleObjc ) { + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { + for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { + AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset); + ObjectFile::Reference* classRef = ao.atom->getReferences()[0]; + if ( classRef->getFixUpOffset() == 0 ) { + const char* classStr = classRef->getTargetName(); + if ( strncmp(classStr, "cstring=", 8) == 0 ) { + const char* className; + asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]); + new Reference(A::kNoFixUp, ao, className, 0); + } + } + } + } + } + } + + // add direct references to local non-lazy-pointers, can do this now that all atoms are constructed + for (typename std::vector*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) { + AnonymousAtom* localNonLazy = *it; + uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress; + pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset))); + makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); + } + + // add implicit direct reference from each C++ function to its eh info + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_COALESCED) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { + for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // note: this algorithm depens on the map iterator returning entries in address order + if ( (it->first >= sect->addr()) && (it->first < sect->addr()+sect->size()) ) { + pint_t ehAtomAddress = it->first; + BaseAtom* ehAtom = it->second; + const char* ehName = ehAtom->getName(); + if ( (ehName != NULL) && (strcmp(&ehName[strlen(ehName)-3], ".eh") == 0) ) { + makeReferenceToEH(ehName, ehAtomAddress, sect); + // make EH symbol static so linker does not try to coalesce + if ( fOptions.fForFinalLinkedImage ) + ehAtom->setScope(ObjectFile::Atom::scopeTranslationUnit); + // if it has a reference to a LSDA, add a group reference + std::vector& ehrefs = ehAtom->getReferences(); + // all FDE's have at least 2 references (to CIE and to function) + if ( ehrefs.size() > 2 ) { + // a third reference means there is a LSDA + ObjectFile::Atom* lsdaAtom = NULL; + for (std::vector::iterator rit=ehrefs.begin(); rit != ehrefs.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getFixUpOffset() ) { + case 4: + case 8: + // these are CIE and function references + break; + default: + // this is LSDA reference + lsdaAtom = &ref->getTarget(); + } + } + if ( lsdaAtom != NULL ) { + new Reference(A::kGroupSubordinate, AtomAndOffset(ehAtom), AtomAndOffset(lsdaAtom)); + } + } + } + } + } + } + } + + // add command line aliases + for(std::vector::const_iterator it = fOptions.fAliases.begin(); it != fOptions.fAliases.end(); ++it) { + BaseAtom* target = this->findAtomByName(it->realName); + if ( (target != NULL) && target->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn ) + fAtoms.push_back(new SymbolAliasAtom(it->alias, NULL, *target)); + } + + // add dtrace probe locations + if ( fHasDTraceProbes ) { + for (uint32_t i=0; i < fSymbolCount; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( (sym.n_type() & N_TYPE) == N_SECT ) { + const char* symbolName = &fStrings[sym.n_strx()]; + if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) { + //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName); + makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0); + } + } + } + } + } + + // turn indirect symbols int SymbolAliasAtom + if ( fHaveIndirectSymbols ) { + for (uint32_t i=0; i < fSymbolCount; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( (sym.n_type() & N_TYPE) == N_INDR ) { + const char* aliasName = &fStrings[sym.n_strx()]; + const char* targetName = &fStrings[sym.n_value()]; + //fprintf(stderr, "found alias %s for %s\n", aliasName, targetName); + BaseAtom* target = this->findAtomByName(targetName); + // only currently support N_INDR based aliases to something in the same .o file + if ( target != NULL ) { + fAtoms.push_back(new SymbolAliasAtom(aliasName, &sym, *target)); + //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName); + } + } + } + } + } + + //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName()); + //} + + // add translation unit info from dwarf + uint64_t stmtList; + if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // compiler sometimes emits emtpty dwarf sections when there is no debug info, skip those + if ( (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { + if ( !read_comp_unit(&fDwarfTranslationUnitFile, &fDwarfTranslationUnitDir, &stmtList) ) { + // if can't parse dwarf, warn and give up + fDwarfTranslationUnitFile = NULL; + fDwarfTranslationUnitDir = NULL; + warning("can't parse dwarf compilation unit info in %s", this->getPath()); + fDebugInfo = kDebugInfoNone; + } + } + } + + // add line number info to atoms from dwarf + if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // file with just data will have no __debug_line info + if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0) + && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { + // validate stmt_list + if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) { + const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset(); + if ( debug_line != NULL ) { + struct line_reader_data* lines = line_open(&debug_line[stmtList], + fDwarfDebugLineSect->size() - stmtList, E::little_endian); + struct line_info result; + ObjectFile::Atom* curAtom = NULL; + uint32_t curAtomOffset = 0; + uint32_t curAtomAddress = 0; + uint32_t curAtomSize = 0; + while ( line_next (lines, &result, line_stop_pc) ) { + //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", + // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); + // work around weird debug line table compiler generates if no functions in __text section + if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) + continue; + // for performance, see if in next pc is in current atom + if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or pc at end of current atom + else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + else { + // do slow look up of atom by address + AtomAndOffset ao = this->findAtomAndOffset(result.pc); + curAtom = ao.atom; + if ( curAtom == NULL ) + break; // file has line info but no functions + if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { + // a one line function can be returned by line_next() as one entry with pc at end of blob + // look for alt atom starting at end of previous atom + uint32_t previousEnd = curAtomAddress+curAtomSize; + AtomAndOffset alt = this->findAtomAndOffset(previousEnd); + if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { + curAtom = alt.atom; + curAtomOffset = alt.offset; + curAtomAddress = previousEnd - alt.offset; + curAtomSize = curAtom->getSize(); + } + else { + curAtomOffset = ao.offset; + curAtomAddress = result.pc - ao.offset; + curAtomSize = curAtom->getSize(); + } + } + else { + curAtomOffset = ao.offset; + curAtomAddress = result.pc - ao.offset; + curAtomSize = curAtom->getSize(); + } + } + const char* filename; + std::map::iterator pos = fDwarfIndexToFile.find(result.file); + if ( pos == fDwarfIndexToFile.end() ) { + filename = line_file(lines, result.file); + fDwarfIndexToFile[result.file] = filename; + } + else { + filename = pos->second; + } + ObjectFile::LineInfo info; + info.atomOffset = curAtomOffset; + info.fileName = filename; + info.lineNumber = result.line; + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", + // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); + ((BaseAtom*)curAtom)->addLineInfo(info); + if ( result.end_of_sequence ) { + curAtom = NULL; + } + } + line_free(lines); + } + else { + warning("could not parse dwarf line number info in %s", this->getPath()); + } + } + } + } + + // if no dwarf, try processing stabs debugging info + if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // scan symbol table for stabs entries + fStabs.reserve(fSymbolCount); // reduce re-allocations + BaseAtom* currentAtom = NULL; + pint_t currentAtomAddress = 0; + enum { start, inBeginEnd, inFun } state = start; + for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { + const macho_nlist

* sym = &fSymbols[symbolIndex]; + bool useStab = true; + uint8_t type = sym->n_type(); + const char* symString = (sym->n_strx() != 0) ? &fStrings[sym->n_strx()] : NULL; + if ( (type & N_STAB) != 0 ) { + fDebugInfo = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs); + Stab stab; + stab.atom = NULL; + stab.type = type; + stab.other = sym->n_sect(); + stab.desc = sym->n_desc(); + stab.value = sym->n_value(); + stab.string = NULL; + switch (state) { + case start: + switch (type) { + case N_BNSYM: + // beginning of function block + state = inBeginEnd; + // fall into case to lookup atom by addresss + case N_LCSYM: + case N_STSYM: + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", + (uint64_t)sym->n_value(), path); + } + break; + case N_SO: + case N_OSO: + case N_OPT: + case N_LSYM: + case N_RSYM: + case N_PSYM: + // not associated with an atom, just copy + stab.string = symString; + break; + case N_GSYM: + { + // n_value field is NOT atom address ;-( + // need to find atom by name match + const char* colon = strchr(symString, ':'); + if ( colon != NULL ) { + // build underscore leading name + int nameLen = colon - symString; + char symName[nameLen+2]; + strlcpy(&symName[1], symString, nameLen+1); + symName[0] = '_'; + symName[nameLen+1] = '\0'; + currentAtom = findAtomByName(symName); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + else { + // might be a debug-note without trailing :G() + currentAtom = findAtomByName(symString); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + if ( stab.atom == NULL ) { + warning("can't find atom for N_GSYM stabs %s in %s", symString, path); + useStab = false; + } + break; + } + case N_FUN: + // old style stabs without BNSYM + state = inFun; + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, path); + } + break; + case N_SOL: + case N_SLINE: + stab.string = symString; + // old stabs + break; + case N_BINCL: + case N_EINCL: + case N_EXCL: + stab.string = symString; + // -gfull built .o file + break; + default: + warning("unknown stabs type 0x%X in %s", type, path); + } + break; + case inBeginEnd: + stab.atom = currentAtom; + switch (type) { + case N_ENSYM: + state = start; + currentAtom = NULL; + break; + case N_LCSYM: + case N_STSYM: + { + BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; + if ( nestedAtom != NULL ) { + stab.atom = nestedAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs 0x%X at %08llX in %s", + type, (uint64_t)sym->n_value(), path); + } + break; + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + default: + stab.string = symString; + break; + } + break; + case inFun: + switch (type) { + case N_FUN: + if ( sym->n_sect() != 0 ) { + // found another start stab, must be really old stabs... + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, path); + } + } + else { + // found ending stab, switch back to start state + stab.string = symString; + stab.atom = currentAtom; + state = start; + currentAtom = NULL; + } + break; + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + stab.atom = currentAtom; + break; + case N_SO: + stab.string = symString; + state = start; + break; + default: + stab.atom = currentAtom; + stab.string = symString; + break; + } + break; + } + // add to list of stabs for this .o file + if ( useStab ) + fStabs.push_back(stab); + } + } + } + +#if 0 + // special case precompiled header .o file (which has no content) to have one empty atom + if ( fAtoms.size() == 0 ) { + int pathLen = strlen(path); + if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) { + ObjectFile::Atom* phony = new AnonymousAtom(*this, (uint32_t)0); + //phony->fSynthesizedName = ".gch.o"; + fAtoms.push_back(phony); + } + } +#endif + + // sort all atoms by address + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + + // set ordinal and sort references in each atom + uint32_t index = fOrdinalBase; + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + atom->setOrdinal(index++); + atom->sortReferences(); + } + +} + + +template <> +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + switch (cpusubtype) { + case CPU_SUBTYPE_POWERPC_ALL: + case CPU_SUBTYPE_POWERPC_750: + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + case CPU_SUBTYPE_POWERPC_970: + fCpuConstraint = cpusubtype; + break; + default: + warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); + fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL; + break; + } +} + +template <> +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + switch (cpusubtype) { + case CPU_SUBTYPE_ARM_ALL: + case CPU_SUBTYPE_ARM_V4T: + case CPU_SUBTYPE_ARM_V5TEJ: + case CPU_SUBTYPE_ARM_V6: + case CPU_SUBTYPE_ARM_XSCALE: + case CPU_SUBTYPE_ARM_V7: + fCpuConstraint = cpusubtype; + break; + default: + warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); + fCpuConstraint = CPU_SUBTYPE_ARM_ALL; + break; + } +} + +template +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + // no cpu sub types for this architecture +} + +template <> +uint32_t Reader::updateCpuConstraint(uint32_t previous) +{ + switch ( previous ) { + case CPU_SUBTYPE_POWERPC_ALL: + return fCpuConstraint; + break; + case CPU_SUBTYPE_POWERPC_750: + if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 || + fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 || + fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_POWERPC_970: + // G5 can run everything + break; + default: + throw "Unhandled PPC cpu subtype!"; + break; + } + return previous; +} + + + +template <> +uint32_t Reader::updateCpuConstraint(uint32_t previous) +{ + switch (previous) { + case CPU_SUBTYPE_ARM_ALL: + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_V5TEJ: + // v6, v7, and xscale are more constrained than previous file (v5), so use it + if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6) + || (fCpuConstraint == CPU_SUBTYPE_ARM_V7) + || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_V4T: + // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it + if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7) + || (fCpuConstraint == CPU_SUBTYPE_ARM_V6) + || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ) + || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_V6: + // v6 can run everything except xscale and v7 + if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) + throw "can't mix xscale and v6 code"; + if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_XSCALE: + // xscale can run everything except v6 and v7 + if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 ) + throw "can't mix xscale and v6 code"; + if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) + throw "can't mix xscale and v7 code"; + break; + case CPU_SUBTYPE_ARM_V7: + // v7 can run everything except xscale + if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) + throw "can't mix xscale and v7 code"; + break; + default: + throw "Unhandled ARM cpu subtype!"; + } + return previous; +} + +template +uint32_t Reader::updateCpuConstraint(uint32_t current) +{ + // no cpu sub types for this architecture + return current; +} + +template +void Reader::addDtraceExtraInfos(uint32_t probeAddr, const char* providerName) +{ + // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with + // a matching provider name, add a by-name kDtraceTypeReference at probe site + const char* dollar = strchr(providerName, '$'); + if ( dollar != NULL ) { + int providerNameLen = dollar-providerName+1; + for ( std::vector::iterator it = fDtraceProviderInfo.begin(); it != fDtraceProviderInfo.end(); ++it) { + const char* typeDollar = strchr(*it, '$'); + if ( typeDollar != NULL ) { + if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) { + makeByNameReference(A::kDtraceTypeReference, probeAddr, *it, 0); + } + } + } + } +} + + +template <> +void Reader::validSectionType(uint8_t type) +{ + switch ( type ) { + case S_SYMBOL_STUBS: + throw "symbol_stub sections not valid in x86_64 object files"; + case S_LAZY_SYMBOL_POINTERS: + throw "lazy pointer sections not valid in x86_64 object files"; + case S_NON_LAZY_SYMBOL_POINTERS: + throw "non lazy pointer sections not valid in x86_64 object files"; + } +} + +template +void Reader::validSectionType(uint8_t type) +{ +} + +template +bool Reader::getTranslationUnitSource(const char** dir, const char** name) const +{ + if ( fDebugInfo == kDebugInfoDwarf ) { + *dir = fDwarfTranslationUnitDir; + *name = fDwarfTranslationUnitFile; + return (fDwarfTranslationUnitFile != NULL); + } + return false; +} + +template +BaseAtom* Reader::findAtomByName(const char* name) +{ + // first search the more important atoms + for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + const char* atomName = it->second->getName(); + if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { + return it->second; + } + } + // try all atoms, because this might have been a tentative definition + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + const char* atomName = atom->getName(); + if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { + return atom; + } + } + return NULL; +} + +template +Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr)); +} + +template +Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr)); +} + +template +Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr)); +} + +template +Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr)); +} + +template +Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) +{ + return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); +} + +template +Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +{ + // add a direct reference from function atom to its eh frame atom + const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); + int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function + pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8; + return makeReference(A::kGroupSubordinate, funcAddr, ehAtomAddress); +} + + +template <> +Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) +{ + // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references + // instead check scope of target + BaseAtom* target = findAtomByName(toName); + if ( (target != NULL) && (target->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) + return new Reference(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset)); + else + return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); +} + +template <> +Reference* Reader::makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset) +{ + // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references + // instead check scope of target + const char* symbolName = &fStrings[toSymbol->n_strx()]; + if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toSymbol->n_value(), toSymbol->n_value()+toOffset)); + else + return new Reference(kind, findAtomAndOffset(atAddr), symbolName, toOffset); +} + + +template <> +Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +{ + // add a direct reference from function atom to its eh frame atom + // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target + uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + ehSect->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { + pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); + return makeReference(x86_64::kGroupSubordinate, funcAddr, ehAtomAddress); + } + } + warning("can't find matching function for eh symbol %s", ehName); + return NULL; +} + + +template +AtomAndOffset Reader::findAtomAndOffset(pint_t addr) +{ + // STL has no built-in for "find largest key that is same or less than" + typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr); + // if no atoms up to this address return none found + if ( it == fAddrToAtom.begin() ) + return AtomAndOffset(NULL); + // otherwise upper_bound gets us next key, so we back up one + --it; + AtomAndOffset result; + result.atom = it->second; + result.offset = addr - it->first; + //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n", + // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize()); + return result; +} + +// "scattered" relocations enable you to offset into an atom past the end of it +// baseAddr is the address of the target atom, +// realAddr is the points into it +template +AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) +{ + typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr); + if ( it != fAddrToAtom.end() ) { + AtomAndOffset result; + result.atom = it->second; + result.offset = realAddr - it->first; + //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); + return result; + } + // getting here means we have a scattered relocation to an address without a label + // we should never get here... + // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section + return findAtomAndOffset(realAddr); +} + + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (const uint8_t ** offset, const uint8_t * end) +{ + while (*offset != end && **offset >= 0x80) + (*offset)++; + if (*offset != end) + (*offset)++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (const uint8_t ** offset, const uint8_t * end) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (*offset == end) + return (uint64_t) -1; + + b = **offset & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*(*offset)++ >= 0x80); + return result; +} + + +/* Skip over a DWARF attribute of form FORM. */ +template +bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, + uint8_t addr_size, bool dwarf64) +{ + int64_t sz=0; + + switch (form) + { + case DW_FORM_addr: + sz = addr_size; + break; + + case DW_FORM_block2: + if (end - *offset < 2) + return false; + sz = 2 + A::P::E::get16(*(uint16_t*)offset); + break; + + case DW_FORM_block4: + if (end - *offset < 4) + return false; + sz = 2 + A::P::E::get32(*(uint32_t*)offset); + break; + + case DW_FORM_data2: + case DW_FORM_ref2: + sz = 2; + break; + + case DW_FORM_data4: + case DW_FORM_ref4: + sz = 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + sz = 8; + break; + + case DW_FORM_string: + while (*offset != end && **offset) + ++*offset; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + sz = 1; + break; + + case DW_FORM_block: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_block1: + if (*offset == end) + return false; + sz = 1 + **offset; + break; + + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + skip_leb128 (offset, end); + return true; + + case DW_FORM_strp: + case DW_FORM_ref_addr: + sz = dwarf64 ? 8 : 4; + break; + + default: + return false; + } + if (end - *offset < sz) + return false; + *offset += sz; + return true; +} + +// Look at the compilation unit DIE and determine +// its NAME, compilation directory (in COMP_DIR) and its +// line number information offset (in STMT_LIST). NAME and COMP_DIR +// may be NULL (especially COMP_DIR) if they are not in the .o file; +// STMT_LIST will be (uint64_t) -1. +// +// At present this assumes that there's only one compilation unit DIE. +// +template +bool Reader::read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list) +{ + const uint8_t * debug_info; + const uint8_t * debug_abbrev; + const uint8_t * di; + const uint8_t * da; + const uint8_t * end; + const uint8_t * enda; + uint64_t sz; + uint16_t vers; + uint64_t abbrev_base; + uint64_t abbrev; + uint8_t address_size; + bool dwarf64; + + *name = NULL; + *comp_dir = NULL; + *stmt_list = (uint64_t) -1; + + if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) ) + return false; + + debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset(); + debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset(); + di = debug_info; + + if (fDwarfDebugInfoSect->size() < 12) + /* Too small to be a real debug_info section. */ + return false; + sz = A::P::E::get32(*(uint32_t*)di); + di += 4; + dwarf64 = sz == 0xffffffff; + if (dwarf64) + sz = A::P::E::get64(*(uint64_t*)di), di += 8; + else if (sz > 0xffffff00) + /* Unknown dwarf format. */ + return false; + + /* Verify claimed size. */ + if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + return false; + + vers = A::P::E::get16(*(uint16_t*)di); + if (vers < 2 || vers > 3) + /* DWARF version wrong for this code. + Chances are we could continue anyway, but we don't know for sure. */ + return false; + di += 2; + + /* Find the debug_abbrev section. */ + abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); + di += dwarf64 ? 8 : 4; + + if (abbrev_base > fDwarfDebugAbbrevSect->size()) + return false; + da = debug_abbrev + abbrev_base; + enda = debug_abbrev + fDwarfDebugAbbrevSect->size(); + + address_size = *di++; + + /* Find the abbrev number we're looking for. */ + end = di + sz; + abbrev = read_uleb128 (&di, end); + if (abbrev == (uint64_t) -1) + return false; + + /* Skip through the debug_abbrev section looking for that abbrev. */ + for (;;) + { + uint64_t this_abbrev = read_uleb128 (&da, enda); + uint64_t attr; + + if (this_abbrev == abbrev) + /* This is almost always taken. */ + break; + skip_leb128 (&da, enda); /* Skip the tag. */ + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + do { + attr = read_uleb128 (&da, enda); + skip_leb128 (&da, enda); + } while (attr != 0 && attr != (uint64_t) -1); + if (attr != 0) + return false; + } + + /* Check that the abbrev is one for a DW_TAG_compile_unit. */ + if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) + return false; + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + /* Now, go through the DIE looking for DW_AT_name, + DW_AT_comp_dir, and DW_AT_stmt_list. */ + for (;;) + { + uint64_t attr = read_uleb128 (&da, enda); + uint64_t form = read_uleb128 (&da, enda); + + if (attr == (uint64_t) -1) + return false; + else if (attr == 0) + return true; + + if (form == DW_FORM_indirect) + form = read_uleb128 (&di, end); + + if (attr == DW_AT_name && form == DW_FORM_string) + *name = (const char *) di; + else if (attr == DW_AT_comp_dir && form == DW_FORM_string) + *comp_dir = (const char *) di; + /* Really we should support DW_FORM_strp here, too, but + there's usually no reason for the producer to use that form + for the DW_AT_name and DW_AT_comp_dir attributes. */ + else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) + *stmt_list = A::P::E::get32(*(uint32_t*)di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) + *stmt_list = A::P::E::get64(*(uint64_t*)di); + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } +} + +template +const char* Reader::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + + +// +// +// To implement architecture xxx, you must write template specializations for the following six methods: +// Reader::validFile() +// Reader::addRelocReference() +// Reference::getDescription() +// +// + + +template <> +bool Reader::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; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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_I386 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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_X86_64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template +bool Reader::isWeakImportSymbol(const macho_nlist

* sym) +{ + return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) ); +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + return addRelocReference_powerpc(sect, reloc); +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + return addRelocReference_powerpc(sect, reloc); +} + + +// +// ppc and ppc64 both use the same relocations, so process them in one common routine +// +template +bool Reader::addRelocReference_powerpc(const macho_section* sect, + const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + uint32_t offsetInTarget; + int16_t lowBits; + bool result = false; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + const macho_relocation_info

* nextReloc = &reloc[1]; + const char* targetName = NULL; + bool weakImport = false; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != PPC_RELOC_PAIR ) + instruction = BigEndian::get32(*fixUpPtr); + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + weakImport = this->isWeakImportSymbol(targetSymbol); + } + switch ( reloc->r_type() ) { + case PPC_RELOC_BR24: + { + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + } + else { + printf("bad instruction for BR24 reloc"); + } + if ( reloc->r_extern() ) { + offsetInTarget = srcAddr + displacement; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( weakImport ) + makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(A::kBranch24WeakImport, srcAddr, dstAddr); + else + makeReference(A::kBranch24, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_BR14: + { + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + if ( reloc->r_extern() ) { + offsetInTarget = srcAddr + displacement; + makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + makeReference(A::kBranch14, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_LO16 missing following pair"); + break; + } + result = true; + lowBits = (instruction & 0xFFFF); + if ( reloc->r_extern() ) { + offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsLow16, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsLow16, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_LO14: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_LO14 missing following pair"); + break; + } + result = true; + lowBits = (instruction & 0xFFFC); + if ( reloc->r_extern() ) { + offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsLow14, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsLow14, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_HI16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_HI16 missing following pair"); + break; + } + result = true; + if ( reloc->r_extern() ) { + offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsHigh16, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsHigh16, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_HA16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_HA16 missing following pair"); + break; + } + result = true; + lowBits = (nextReloc->r_address() & 0x0000FFFF); + if ( reloc->r_extern() ) { + offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_VANILLA: + { + pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + if ( weakImport ) + makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue); + else + makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); + } + else { + makeReference(A::kPointer, srcAddr, pointerValue); + } + } + break; + case PPC_RELOC_JBSR: + // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_JBSR missing following pair"); + break; + } + fHasLongBranchStubs = true; + result = true; + makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + } + else { + fprintf(stderr, "bad instruction for BR24 reloc"); + } + if ( reloc->r_extern() ) { + fprintf(stderr, "PPC_RELOC_JBSR should not be using an external relocation"); + } + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + uint32_t betterDstAddr; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch (sreloc->r_type()) { + case PPC_RELOC_VANILLA: + { + betterDstAddr = P::getP(*(pint_t*)fixUpPtr); + //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_BR14: + { + instruction = BigEndian::get32(*fixUpPtr); + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + betterDstAddr = srcAddr+displacement; + //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement); + makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_BR24: + { + instruction = BigEndian::get32(*fixUpPtr); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + betterDstAddr = srcAddr+displacement; + makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr); + } + } + break; + case PPC_RELOC_LO16_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO16_SECTDIFF missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFF); + displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_LO14_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO14_SECTDIFF missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFC); + displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_HA16_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_HA16_SECTDIFF missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0x0000FFFF); + displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_LO14: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO14 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFC); + betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_LO16: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO16 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFF); + betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_HA16: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_HA16 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0xFFFF); + betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_HI16: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_HI16 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0xFFFF); + betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_SECTDIFF missing following pair"); + break; + } + Kinds kind = A::kPointerDiff32;; + uint32_t contentAddr = 0; + switch ( sreloc->r_length() ) { + case 0: + throw "bad diff relocations r_length (0) for ppc architecture"; + case 1: + kind = A::kPointerDiff16; + contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + kind = A::kPointerDiff32; + contentAddr = BigEndian::get32(*fixUpPtr); + break; + case 3: + kind = A::kPointerDiff64; + contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr)); + break; + } + AtomAndOffset srcao = findAtomAndOffset(srcAddr); + AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); + AtomAndOffset toao = findAtomAndOffset(dstAddr); + // check for addend encoded in the section content + //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", + // dstAddr, nextRelocValue, contentAddr); + if ( (dstAddr - nextRelocValue) != contentAddr ) { + if ( toao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else if ( fromao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else + fromao.offset += (dstAddr - contentAddr) - nextRelocValue; + } + //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", + // srcao.atom->getDisplayName(), srcao.offset, + // fromao.atom->getDisplayName(), fromao.offset, + // toao.atom->getDisplayName(), toao.offset); + new Reference(kind, srcao, fromao, toao); + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + bool result = false; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + switch ( reloc->r_type() ) { + case GENERIC_RELOC_VANILLA: + { + x86::ReferenceKinds kind = x86::kPointer; + uint32_t pointerValue = E::get32(*fixUpPtr); + if ( reloc->r_pcrel() ) { + switch( reloc->r_length() ) { + case 0: + kind = x86::kPCRel8; + pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t); + break; + case 1: + kind = x86::kPCRel16; + pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t); + break; + case 2: + kind = x86::kPCRel32; + pointerValue += srcAddr + sizeof(uint32_t); + break; + case 3: + throw "bad pc-rel vanilla relocation length"; + } + } + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { + kind = x86::kAbsolute32; + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + } + else { + kind = x86::kPointer; + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + } + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + if ( this->isWeakImportSymbol(targetSymbol) ) { + if ( reloc->r_pcrel() ) + kind = x86::kPCRel32WeakImport; + else + kind = x86::kPointerWeakImport; + } + const char* targetName = &fStrings[targetSymbol->n_strx()]; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else + makeByNameReference(kind, srcAddr, targetName, pointerValue); + } + else { + // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol + ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom; + const char* targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue); + else if ( reloc->r_symbolnum() != R_ABS ) + makeReference(kind, srcAddr, pointerValue); + else { + // find absolute symbol that corresponds to pointerValue + AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(kind, srcAddr, pos->second->getName(), 0); + else + throwf("R_ABS reloc but no absolute symbol at target address"); + } + } + } + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + pint_t betterDstAddr; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + } + } + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + betterDstAddr = LittleEndian::get32(*fixUpPtr); + //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( sreloc->r_pcrel() ) { + betterDstAddr += srcAddr + 4; + makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); + } + else { + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); + else + makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); + } + break; + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + { + if ( !nextRelocIsPair ) { + warning("GENERIC_RELOC_SECTDIFF missing following pair"); + break; + } + x86::ReferenceKinds kind = x86::kPointerDiff; + uint32_t contentAddr = 0; + switch ( sreloc->r_length() ) { + case 0: + case 3: + throw "bad length for GENERIC_RELOC_SECTDIFF"; + case 1: + kind = x86::kPointerDiff16; + contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + kind = x86::kPointerDiff; + contentAddr = LittleEndian::get32(*fixUpPtr); + break; + } + AtomAndOffset srcao = findAtomAndOffset(srcAddr); + AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); + AtomAndOffset toao = findAtomAndOffset(dstAddr); + // check for addend encoded in the section content + //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", + // dstAddr, nextRelocValue, contentAddr); + if ( (dstAddr - nextRelocValue) != contentAddr ) { + if ( toao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else if ( fromao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else + fromao.offset += (dstAddr - contentAddr) - nextRelocValue; + } + //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", + // srcao.atom->getDisplayName(), srcao.offset, + // fromao.atom->getDisplayName(), fromao.offset, + // toao.atom->getDisplayName(), toao.offset); + new Reference(kind, srcao, fromao, toao); + } + break; + case GENERIC_RELOC_PAIR: + // do nothing, already used via a look ahead + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint64_t srcAddr; + uint64_t dstAddr = 0; + uint64_t addend; + uint32_t* fixUpPtr; + x86_64::ReferenceKinds kind = x86_64::kNoFixUp; + bool result = false; + const macho_nlist

* targetSymbol = NULL; + const char* targetName = NULL; + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + //fprintf(stderr, "addReloc type=%d\n", reloc->r_type()); + if ( reloc->r_extern() ) { + targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + } + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; + if ( reloc->r_length() != 3 ) + throw "length < 3 and X86_64_RELOC_UNSIGNED not supported"; + dstAddr = E::get64(*((uint64_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kPointer, srcAddr, dstAddr); + // verify that dstAddr is in the section being targeted + int sectNum = reloc->r_symbolnum(); + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const targetSection = §ionsStart[sectNum-1]; + if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { + throwf("local relocation for address 0x%08llX in section %s does not target section %s", + srcAddr, sect->sectname(), targetSection->sectname()); + } + } + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( reloc->r_extern() ) { + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + kind = x86_64::kPCRel32; + // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created + if ( addend == (uint64_t)(-1) ) { + addend = 0; + kind = x86_64::kPCRel32_1; + } + else if ( addend == (uint64_t)(-2) ) { + addend = 0; + kind = x86_64::kPCRel32_2; + } + else if ( addend == (uint64_t)(-4) ) { + addend = 0; + kind = x86_64::kPCRel32_4; + } + break; + // end support for old .o files before X86_64_RELOC_SIGNED_1 was created + case X86_64_RELOC_SIGNED_1: + kind = x86_64::kPCRel32_1; + addend += 1; + break; + case X86_64_RELOC_SIGNED_2: + kind = x86_64::kPCRel32_2; + addend += 2; + break; + case X86_64_RELOC_SIGNED_4: + kind = x86_64::kPCRel32_4; + addend += 4; + break; + } + makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend); + } + else { + uint64_t ripRelativeOffset = addend; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + dstAddr = srcAddr + 4 + ripRelativeOffset; + kind = x86_64::kPCRel32; + break; + case X86_64_RELOC_SIGNED_1: + dstAddr = srcAddr + 5 + ripRelativeOffset; + kind = x86_64::kPCRel32_1; + break; + case X86_64_RELOC_SIGNED_2: + dstAddr = srcAddr + 6 + ripRelativeOffset; + kind = x86_64::kPCRel32_2; + break; + case X86_64_RELOC_SIGNED_4: + dstAddr = srcAddr + 8 + ripRelativeOffset; + kind = x86_64::kPCRel32_4; + break; + } + makeReference(kind, srcAddr, dstAddr); + // verify that dstAddr is in the section being targeted + int sectNum = reloc->r_symbolnum(); + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const targetSection = §ionsStart[sectNum-1]; + if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { + throwf("local relocation for address 0x%08llX in section %s does not target section %s", + srcAddr, sect->sectname(), targetSection->sectname()); + } + } + break; + case X86_64_RELOC_BRANCH: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_BRANCH not supported"; + if ( reloc->r_length() == 2 ) { + dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( reloc->r_extern() ) { + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr); + else + makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr); + } + } + else if ( reloc->r_length() == 0 ) { + dstAddr = *((int8_t*)fixUpPtr); + if ( reloc->r_extern() ) { + makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr); + } + } + else { + throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());; + } + break; + case X86_64_RELOC_GOT: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend); + else + makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend); + break; + case X86_64_RELOC_GOT_LOAD: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend); + else + makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend); + break; + case X86_64_RELOC_SUBTRACTOR: + { + if ( reloc->r_pcrel() ) + throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; + Reference* ref; + bool negativeAddend; + if ( reloc->r_length() == 2 ) { + kind = x86_64::kPointerDiff32; + dstAddr = E::get32(*fixUpPtr); // addend is in content + negativeAddend = ((dstAddr & 0x80000000) != 0); + } + else { + kind = x86_64::kPointerDiff; + dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content + negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); + } + ObjectFile::Atom* inAtom = this->findAtomAndOffset(srcAddr).atom; + // create reference with "to" target + if ( nextReloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0); + // if "to" is in this atom, change by-name to a direct reference + if ( strcmp(targetName, inAtom->getName()) == 0 ) + ref->setTarget(*inAtom, 0); + } + else { + ref = makeReference(kind, srcAddr, dstAddr); + } + // add in "from" target + if ( reloc->r_extern() ) { + const macho_nlist

* targetFromSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()]; + if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) { + // from target is translation unit scoped, so use a direct reference + ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom)); + } + else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) { + // if "from" is in this atom, change by-name to a direct reference + ref->setFromTarget(*inAtom); + } + else { + // some non-static other atom + ref->setFromTargetName(fromTargetName); + } + } + // addend goes in from side iff negative + if ( negativeAddend ) + ref->setFromTargetOffset(-dstAddr); + else + ref->setToTargetOffset(dstAddr); + break; + } + default: + warning("unknown relocation type %d", reloc->r_type()); + } + return result; +} + + +/// Reader::addRelocReference - +/// turns arm relocation entries into references. Returns true if the next +/// relocation should be skipped, false otherwise. +template <> +bool Reader::addRelocReference(const macho_section* sect, + const macho_relocation_info* reloc) +{ + uint32_t * fixUpPtr; + int32_t displacement; + uint32_t instruction = 0; + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t pointerValue; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + // non-scattered relocation + const char* targetName = NULL; + bool weakImport = false; + + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != ARM_RELOC_PAIR ) + instruction = LittleEndian::get32(*fixUpPtr); + + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + weakImport = this->isWeakImportSymbol(targetSymbol); + } + + switch ( reloc->r_type() ) { + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + + if ( reloc->r_extern() ) { + uint32_t offsetInTarget = srcAddr + displacement; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( weakImport ) + makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + // check for dtrace probes and weak_import stubs + const char* targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr); + else if ( reloc->r_symbolnum() != R_ABS ) + makeReference(arm::kBranch24, srcAddr, dstAddr); + else { + // find absolute symbol that corresponds to pointerValue + AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0); + else + throwf("R_ABS reloc but no absolute symbol at target address"); + } + } + break; + + case ARM_THUMB_RELOC_BR22: + // First instruction has upper 11 bits of the displacement. + displacement = (instruction & 0x7FF) << 12; + if ( (displacement & 0x400000) != 0 ) + displacement |= 0xFF800000; + // Second instruction has lower eleven bits of the displacement. + displacement += ((instruction >> 16) & 0x7FF) << 1; + // The pc added will be +4 from the pc + displacement += 4; + // If the instruction was blx, force the low 2 bits to be clear + dstAddr = srcAddr + displacement; + if ((instruction & 0xF8000000) == 0xE8000000) + dstAddr &= 0xFFFFFFFC; + + if ( reloc->r_extern() ) { + uint32_t offsetInTarget = dstAddr; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( weakImport ) + makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget); + } + else { + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + // check for dtrace probes and weak_import stubs + const char* targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr); + else if ( reloc->r_symbolnum() != R_ABS ) + makeReference(arm::kThumbBranch22, srcAddr, dstAddr); + else { + // find absolute symbol that corresponds to pointerValue + AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0); + else + throwf("R_ABS reloc but no absolute symbol at target address"); + } + } + break; + + case ARM_RELOC_VANILLA: + if ( reloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + + pointerValue = instruction; + if ( reloc->r_extern() ) { + if ( weakImport ) + makeByNameReference(arm::kPointerWeakImport, srcAddr, targetName, pointerValue); + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeByNameReference(arm::kReadOnlyPointer, srcAddr, targetName, pointerValue); + else + makeByNameReference(arm::kPointer, srcAddr, targetName, pointerValue); + } + else { + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReference(arm::kReadOnlyPointer, srcAddr, pointerValue); + else + makeReference(arm::kPointer, srcAddr, pointerValue); + } + break; + + default: + warning("unexpected relocation type %u", reloc->r_type()); + break; + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + uint32_t betterDstAddr; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + instruction = LittleEndian::get32(*fixUpPtr); + + // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF} + // relocation types, and it is an error to see one otherwise. + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + + switch (sreloc->r_type()) { + case ARM_RELOC_VANILLA: + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + + betterDstAddr = LittleEndian::get32(*fixUpPtr); + //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReferenceWithToBase(arm::kReadOnlyPointer, srcAddr, betterDstAddr, dstAddr); + else + makeReferenceWithToBase(arm::kPointer, srcAddr, betterDstAddr, dstAddr); + break; + + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + betterDstAddr = srcAddr+displacement; + makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr); + break; + + case ARM_THUMB_RELOC_BR22: + // First instruction has upper 11 bits of the displacement. + displacement = (instruction & 0x7FF) << 12; + if ( (displacement & 0x400000) != 0 ) + displacement |= 0xFF800000; + // Second instruction has lower eleven bits of the displacement. + displacement += ((instruction >> 16) & 0x7FF) << 1; + // The pc added will be +4 from the pc + displacement += 4; + betterDstAddr = srcAddr+displacement; + // If the instruction was blx, force the low 2 bits to be clear + if ((instruction & 0xF8000000) == 0xE8000000) + betterDstAddr &= 0xFFFFFFFC; + makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr); + break; + + case ARM_RELOC_SECTDIFF: + case ARM_RELOC_LOCAL_SECTDIFF: + if ( !nextRelocIsPair ) { + warning("ARM_RELOC_SECTDIFF missing following pair"); + break; + } + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_SECTDIFF"; + { + AtomAndOffset srcao = findAtomAndOffset(srcAddr); + AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); + AtomAndOffset toao = findAtomAndOffset(dstAddr); + // check for addend encoded in the section content + pointerValue = LittleEndian::get32(*fixUpPtr); + if ( (dstAddr - nextRelocValue) != pointerValue ) { + if ( toao.atom == srcao.atom ) + toao.offset += (pointerValue + nextRelocValue) - dstAddr; + else if ( fromao.atom == srcao.atom ) + toao.offset += (pointerValue + nextRelocValue) - dstAddr; + else + fromao.offset += (dstAddr - pointerValue) - nextRelocValue; + } + new Reference(arm::kPointerDiff, srcao, fromao, toao); + } + break; + + default: + warning("unexpected srelocation type %u", sreloc->r_type()); + break; + } + } + return result; +} + +template +void Reader::addReferencesForSection(const macho_section

* sect) +{ + // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change + if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) { + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + // we ignore compiler generated stubs, so ignore those relocs too + break; + default: + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); + for (uint32_t r = 0; r < relocCount; ++r) { + try { + if ( addRelocReference(sect, &relocs[r]) ) + ++r; // skip next + } + catch (const char* msg) { + throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); + } + } + } + } +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case x86::kNoFixUp: + sprintf(temp, "reference to "); + break; + case x86::kFollowOn: + sprintf(temp, "followed by "); + break; + case x86::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case x86::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case x86::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case x86::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86::kPointerDiff16: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86::kPCRel32WeakImport: + sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc); + break; + case x86::kPCRel32: + sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86::kPCRel16: + sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc); + break; + case x86::kPCRel8: + sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc); + break; + case x86::kAbsolute32: + sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); + break; + case x86::kDtraceProbe: + sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); + break; + case x86::kDtraceProbeSite: + sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); + break; + case x86::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case x86::kDtraceTypeReference: + sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case ppc::kNoFixUp: + sprintf(temp, "reference to "); + break; + case ppc::kFollowOn: + sprintf(temp, "followed by "); + break; + case ppc::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case ppc::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case ppc::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case ppc::kPointerDiff16: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc::kPointerDiff32: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc::kPointerDiff64: + throw "unsupported refrence kind"; + break; + case ppc::kBranch24WeakImport: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case ppc::kBranch24: + case ppc::kBranch14: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case ppc::kPICBaseLow16: + sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); + break; + case ppc::kPICBaseLow14: + sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); + break; + case ppc::kPICBaseHigh16: + sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); + break; + case ppc::kAbsLow16: + sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsLow14: + sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsHigh16: + sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsHigh16AddLow: + sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kDtraceProbe: + sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); + break; + case ppc::kDtraceProbeSite: + sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); + break; + case ppc::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case ppc::kDtraceTypeReference: + sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case ppc64::kNoFixUp: + sprintf(temp, "reference to "); + break; + case ppc64::kFollowOn: + sprintf(temp, "followed by "); + break; + case ppc64::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case ppc64::kPointerWeakImport: + sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); + break; + case ppc64::kPointer: + sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); + break; + case ppc64::kPointerDiff64: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kPointerDiff32: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kPointerDiff16: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kBranch24WeakImport: + sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case ppc64::kBranch24: + case ppc64::kBranch14: + sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case ppc64::kPICBaseLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kPICBaseLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kPICBaseHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kAbsLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsHigh16AddLow: + sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceProbe: + sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceProbeSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceTypeReference: + sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); + + return temp; +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case x86_64::kNoFixUp: + sprintf(temp, "reference to "); + break; + case x86_64::kFollowOn: + sprintf(temp, "followed by "); + break; + case x86_64::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case x86_64::kPointerWeakImport: + sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); + break; + case x86_64::kPointer: + sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); + break; + case x86_64::kPointerDiff32: + case x86_64::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; + sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, size, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86_64::kPCRel32: + sprintf(temp, "offset 0x%04llX, rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_1: + sprintf(temp, "offset 0x%04llX, rel32-1 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_2: + sprintf(temp, "offset 0x%04llX, rel32-2 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_4: + sprintf(temp, "offset 0x%04llX, rel32-4 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel32: + sprintf(temp, "offset 0x%04llX, branch rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel32WeakImport: + sprintf(temp, "offset 0x%04llX, branch rel32 reference to weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOT: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTWeakImport: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTLoad: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTLoadWeakImport: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel8: + sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceProbe: + sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceProbeSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceTypeReference: + sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); + + return temp; +} + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case arm::kNoFixUp: + sprintf(temp, "reference to "); + break; + case arm::kFollowOn: + sprintf(temp, "followed by "); + break; + case arm::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case arm::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case arm::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case arm::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case arm::kReadOnlyPointer: + sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc); + break; + case arm::kBranch24: + case arm::kThumbBranch22: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case arm::kBranch24WeakImport: + case arm::kThumbBranch22WeakImport: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case arm::kDtraceProbe: + sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); + break; + case arm::kDtraceProbeSite: + sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); + break; + case arm::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case arm::kDtraceTypeReference: + sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + +}; // namespace relocatable +}; // namespace mach_o + +#endif // __OBJECT_FILE_MACH_O__ diff --git a/ld64/FireOpal/src/MachOWriterExecutable.hpp b/ld64/FireOpal/src/MachOWriterExecutable.hpp new file mode 100644 index 0000000..8667ae4 --- /dev/null +++ b/ld64/FireOpal/src/MachOWriterExecutable.hpp @@ -0,0 +1,8579 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __EXECUTABLE_MACH_O__ +#define __EXECUTABLE_MACH_O__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ObjectFile.h" +#include "ExecutableFile.h" +#include "Options.h" + +#include "MachOFileAbstraction.hpp" + + +// +// +// To implement architecture xxx, you must write template specializations for the following methods: +// MachHeaderAtom::setHeaderInfo() +// ThreadsLoadCommandsAtom::getSize() +// ThreadsLoadCommandsAtom::copyRawContent() +// Writer::addObjectRelocs() +// Writer::fixUpReferenceRelocatable() +// Writer::fixUpReferenceFinal() +// Writer::stubableReference() +// Writer::weakImportReferenceKind() +// Writer::GOTReferenceKind() +// + + +namespace mach_o { +namespace executable { + +// forward references +template class WriterAtom; +template class PageZeroAtom; +template class CustomStackAtom; +template class MachHeaderAtom; +template class SegmentLoadCommandsAtom; +template class EncryptionLoadCommandsAtom; +template class SymbolTableLoadCommandsAtom; +template class ThreadsLoadCommandsAtom; +template class DylibIDLoadCommandsAtom; +template class RoutinesLoadCommandsAtom; +template class DyldLoadCommandsAtom; +template class UUIDLoadCommandAtom; +template class LinkEditAtom; +template class SectionRelocationsLinkEditAtom; +template class LocalRelocationsLinkEditAtom; +template class ExternalRelocationsLinkEditAtom; +template class SymbolTableLinkEditAtom; +template class SegmentSplitInfoLoadCommandsAtom; +template class SegmentSplitInfoContentAtom; +template class IndirectTableLinkEditAtom; +template class ModuleInfoLinkEditAtom; +template class StringsLinkEditAtom; +template class LoadCommandsPaddingAtom; +template class StubAtom; +template class StubHelperAtom; +template class LazyPointerAtom; +template class NonLazyPointerAtom; +template class DylibLoadCommandsAtom; + + +// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes +class SectionInfo : public ObjectFile::Section { +public: + SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), + fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false), + fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false), + fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false), + fHasTextLocalRelocs(false), fHasTextExternalRelocs(false) + { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } + void setIndex(unsigned int index) { fIndex=index; } + std::vector fAtoms; + char fSegmentName[20]; + char fSectionName[20]; + uint64_t fFileOffset; + uint64_t fSize; + uint32_t fRelocCount; + uint32_t fRelocOffset; + uint32_t fIndirectSymbolOffset; + uint8_t fAlignment; + bool fAllLazyPointers; + bool fAllLazyDylibPointers; + bool fAllNonLazyPointers; + bool fAllStubs; + bool fAllSelfModifyingStubs; + bool fAllZeroFill; + bool fVirtualSection; + bool fHasTextLocalRelocs; + bool fHasTextExternalRelocs; +}; + +// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes +class SegmentInfo +{ +public: + SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), + fBaseAddress(0), fSize(0), fFixedAddress(false), + fIndependentAddress(false) { fName[0] = '\0'; } + std::vector fSections; + char fName[20]; + uint32_t fInitProtection; + uint32_t fMaxProtection; + uint64_t fFileOffset; + uint64_t fFileSize; + uint64_t fBaseAddress; + uint64_t fSize; + bool fFixedAddress; + bool fIndependentAddress; +}; + +template +class Writer : public ExecutableFile::Writer +{ +public: + Writer(const char* path, Options& options, std::vector& dynamicLibraries); + virtual ~Writer(); + + virtual const char* getPath() { return fFilePath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms() { return fWriterSynthesizedAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return NULL; } + + virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, + bool objcReplacementClasses); + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); + virtual uint64_t write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool createUUID, bool canScatter, + ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs, bool overridesDylibWeakDefines); + +private: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; + + void assignFileOffsets(); + void synthesizeStubs(); + void insertDummyStubs(); + void partitionIntoSections(); + bool addBranchIslands(); + bool addPPCBranchIslands(); + bool isBranch24Reference(uint8_t kind); + void adjustLoadCommandsAndPadding(); + void createDynamicLinkerCommand(); + void createDylibCommands(); + void buildLinkEdit(); + const char* getArchString(); + void writeMap(); + uint64_t writeAtoms(); + void writeNoOps(int fd, uint32_t from, uint32_t to); + void copyNoOps(uint8_t* from, uint8_t* to); + bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to); + void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref); + void collectExportedAndImportedAndLocalAtoms(); + void setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count); + void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); + void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); + void buildSymbolTable(); + const char* symbolTableName(const ObjectFile::Atom* atom); + void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void copyNlistRange(const std::vector >& entries, uint32_t startIndex); + uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); + uint8_t ordinalForLibrary(ObjectFile::Reader* file); + bool shouldExport(const ObjectFile::Atom& atom) const; + void buildFixups(); + void adjustLinkEditSections(); + void buildObjectFileFixups(); + void buildExecutableFixups(); + bool preboundLazyPointerType(uint8_t* type); + uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; + void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; + void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; + void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, + uint8_t buffer[], bool finalLinkedImage) const; + uint32_t symbolIndex(ObjectFile::Atom& atom); + bool makesExternalRelocatableReference(ObjectFile::Atom& target) const; + uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + uint8_t getRelocPointerSize(); + uint64_t maxAddress(); + bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref); + bool GOTReferenceKind(uint8_t kind); + bool optimizableGOTReferenceKind(uint8_t kind); + bool weakImportReferenceKind(uint8_t kind); + unsigned int collectStabs(); + uint64_t valueForStab(const ObjectFile::Reader::Stab& stab); + uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab); + uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab); + void addStabs(uint32_t startIndex); + RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const; + bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&); + bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); + bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); + bool mightNeedPadSegment(); + void scanForAbsoluteReferences(); + bool needsModuleTable(); + void optimizeDylibReferences(); + bool indirectSymbolIsLocal(const ObjectFile::Reference* ref) const; + + struct DirectLibrary { + class ObjectFile::Reader* fLibrary; + bool fWeak; + bool fReExport; + }; + + friend class WriterAtom; + friend class PageZeroAtom; + friend class CustomStackAtom; + friend class MachHeaderAtom; + friend class SegmentLoadCommandsAtom; + friend class EncryptionLoadCommandsAtom; + friend class SymbolTableLoadCommandsAtom; + friend class ThreadsLoadCommandsAtom; + friend class DylibIDLoadCommandsAtom; + friend class RoutinesLoadCommandsAtom; + friend class DyldLoadCommandsAtom; + friend class UUIDLoadCommandAtom; + friend class LinkEditAtom; + friend class SectionRelocationsLinkEditAtom; + friend class LocalRelocationsLinkEditAtom; + friend class ExternalRelocationsLinkEditAtom; + friend class SymbolTableLinkEditAtom; + friend class SegmentSplitInfoLoadCommandsAtom; + friend class SegmentSplitInfoContentAtom; +// friend class IndirectTableLinkEditAtom; + friend class ModuleInfoLinkEditAtom; + friend class StringsLinkEditAtom; + friend class LoadCommandsPaddingAtom; + friend class StubAtom; + friend class StubHelperAtom; + friend class LazyPointerAtom; + friend class NonLazyPointerAtom; + friend class DylibLoadCommandsAtom; + + const char* fFilePath; + Options& fOptions; + std::vector* fAllAtoms; + std::vector* fStabs; + class SectionInfo* fLoadCommandsSection; + class SegmentInfo* fLoadCommandsSegment; + class EncryptionLoadCommandsAtom* fEncryptionLoadCommand; + class SegmentLoadCommandsAtom* fSegmentCommands; + class SymbolTableLoadCommandsAtom* fSymbolTableCommands; + class LoadCommandsPaddingAtom* fHeaderPadding; + class UUIDLoadCommandAtom* fUUIDAtom; + std::vector fWriterSynthesizedAtoms; + std::vector fSegmentInfos; + class SegmentInfo* fPadSegmentInfo; + class ObjectFile::Atom* fEntryPoint; + class ObjectFile::Atom* fDyldHelper; + class ObjectFile::Atom* fDyldLazyDylibHelper; + std::map*> fLibraryToLoadCommand; + std::map fLibraryToOrdinal; + std::map fLibraryAliases; + std::vector fExportedAtoms; + std::vector fImportedAtoms; + std::vector fLocalSymbolAtoms; + std::vector > fLocalExtraLabels; + std::vector > fGlobalExtraLabels; + class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; + class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; + class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; + class SymbolTableLinkEditAtom* fSymbolTableAtom; + class SegmentSplitInfoContentAtom* fSplitCodeToDataContentAtom; + class IndirectTableLinkEditAtom* fIndirectTableAtom; + class ModuleInfoLinkEditAtom* fModuleInfoAtom; + class StringsLinkEditAtom* fStringsAtom; + class PageZeroAtom* fPageZeroAtom; + macho_nlist

* fSymbolTable; + std::vector > fSectionRelocs; + std::vector > fInternalRelocs; + std::vector > fExternalRelocs; + std::map fStubsMap; + std::map fGOTMap; + std::vector*> fAllSynthesizedStubs; + std::vector fAllSynthesizedStubHelpers; + std::vector*> fAllSynthesizedLazyPointers; + std::vector*> fAllSynthesizedLazyDylibPointers; + std::vector*> fAllSynthesizedNonLazyPointers; + uint32_t fSymbolTableCount; + uint32_t fSymbolTableStabsCount; + uint32_t fSymbolTableStabsStartIndex; + uint32_t fSymbolTableLocalCount; + uint32_t fSymbolTableLocalStartIndex; + uint32_t fSymbolTableExportCount; + uint32_t fSymbolTableExportStartIndex; + uint32_t fSymbolTableImportCount; + uint32_t fSymbolTableImportStartIndex; + uint32_t fLargestAtomSize; + bool fEmitVirtualSections; + bool fHasWeakExports; + bool fReferencesWeakImports; + bool fCanScatter; + bool fWritableSegmentPastFirst4GB; + bool fNoReExportedDylibs; + bool fBiggerThanTwoGigs; + bool fSlideable; + std::map fWeakImportMap; + std::set fDylibReadersWithNonWeakImports; + std::set fDylibReadersWithWeakImports; + SegmentInfo* fFirstWritableSegment; + ObjectFile::Reader::CpuConstraint fCpuConstraint; + uint32_t fAnonNameIndex; +}; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + virtual bool hasFixedAddress() const { return fFixedAddress; } + + static Segment fgTextSegment; + static Segment fgPageZeroSegment; + static Segment fgLinkEditSegment; + static Segment fgStackSegment; + static Segment fgImportSegment; + static Segment fgROImportSegment; + static Segment fgDataSegment; + static Segment fgObjCSegment; + + +private: + const char* fName; + const bool fReadable; + const bool fWritable; + const bool fExecutable; + const bool fFixedAddress; +}; + +Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true); +Segment Segment::fgTextSegment("__TEXT", true, false, true, false); +Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false); +Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true); +Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); +Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false); +Segment Segment::fgDataSegment("__DATA", true, true, false, false); +Segment Segment::fgObjCSegment("__OBJC", true, true, false, false); + + +template +class WriterAtom : public ObjectFile::Atom +{ +public: + enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; + WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { } + + virtual ObjectFile::Reader* getFile() const { return &fWriter; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return NULL; } + virtual const char* getDisplayName() const { return this->getName(); } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return true; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return true; } + virtual ObjectFile::Segment& getSegment() const { return fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return 0; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); } + virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } + virtual void setScope(Scope) { } + + +protected: + virtual ~WriterAtom() {} + typedef typename A::P P; + typedef typename A::P::E E; + + static std::vector fgEmptyReferenceList; + + Writer& fWriter; + Segment& fSegment; +}; + +template std::vector WriterAtom::fgEmptyReferenceList; + + +template +class PageZeroAtom : public WriterAtom +{ +public: + PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment), + fSize(fWriter.fOptions.zeroPageSize()) {} + virtual const char* getDisplayName() const { return "page zero content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fSize; } + virtual const char* getSectionName() const { return "._zeropage"; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } + void setSize(uint64_t size) { fSize = size; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint64_t fSize; +}; + + +template +class DsoHandleAtom : public WriterAtom +{ +public: + DsoHandleAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + virtual const char* getName() const { return "___dso_handle"; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual uint64_t getSize() const { return 0; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual void copyRawContent(uint8_t buffer[]) const {} +}; + + +template +class MachHeaderAtom : public WriterAtom +{ +public: + MachHeaderAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + virtual const char* getName() const; + virtual const char* getDisplayName() const; + virtual ObjectFile::Atom::Scope getScope() const; + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; + virtual uint64_t getSize() const { return sizeof(macho_header); } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual uint32_t getOrdinal() const { return 1; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + void setHeaderInfo(macho_header& header) const; +}; + +template +class CustomStackAtom : public WriterAtom +{ +public: + CustomStackAtom(Writer& writer); + virtual const char* getDisplayName() const { return "custom stack content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } + virtual const char* getSectionName() const { return "._stack"; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + static bool stackGrowsDown(); +}; + +template +class LoadCommandAtom : public WriterAtom +{ +protected: + LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment), fOrdinal(fgCurrentOrdinal++) {} + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual uint32_t getOrdinal() const { return fOrdinal; } + static uint64_t alignedSize(uint64_t size); +protected: + uint32_t fOrdinal; + static uint32_t fgCurrentOrdinal; +}; + +template uint32_t LoadCommandAtom::fgCurrentOrdinal = 0; + +template +class SegmentLoadCommandsAtom : public LoadCommandAtom +{ +public: + SegmentLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0) + { writer.fSegmentCommands = this; } + virtual const char* getDisplayName() const { return "segment load commands"; } + virtual uint64_t getSize() const { return fSize; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void computeSize(); + void setup(); + unsigned int commandCount() { return fCommandCount; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + unsigned int fCommandCount; + uint32_t fSize; +}; + + +template +class SymbolTableLoadCommandsAtom : public LoadCommandAtom +{ +public: + SymbolTableLoadCommandsAtom(Writer&); + virtual const char* getDisplayName() const { return "symbol table load commands"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; + unsigned int commandCount(); + void needDynamicTable(); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + bool fNeedsDynamicSymbolTable; + macho_symtab_command fSymbolTable; + macho_dysymtab_command fDynamicSymbolTable; +}; + +template +class ThreadsLoadCommandsAtom : public LoadCommandAtom +{ +public: + ThreadsLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "thread load commands"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint8_t* fBuffer; + uint32_t fBufferSize; +}; + +template +class DyldLoadCommandsAtom : public LoadCommandAtom +{ +public: + DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "dyld load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom +{ +public: + SegmentSplitInfoLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "segment split info load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class AllowableClientLoadCommandsAtom : public LoadCommandAtom +{ +public: + AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : + LoadCommandAtom(writer, Segment::fgTextSegment), clientString(client) {} + virtual const char* getDisplayName() const { return "allowable_client load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* clientString; +}; + +template +class DylibLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) + : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info), + fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; } + virtual const char* getDisplayName() const { return "dylib load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void optimizeAway() { fOptimizedAway = true; } + bool linkedWeak() { return fInfo.options.fWeakImport; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + ExecutableFile::DyLibUsed fInfo; + bool fOptimizedAway; +}; + +template +class DylibIDLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "dylib ID load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class RoutinesLoadCommandsAtom : public LoadCommandAtom +{ +public: + RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "routines load command"; } + virtual uint64_t getSize() const { return sizeof(macho_routines_command); } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "sub-umbrella load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + typedef typename A::P P; + const char* fName; +}; + +template +class SubLibraryLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) + : LoadCommandAtom(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} + virtual const char* getDisplayName() const { return "sub-library load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fNameStart; + int fNameLength; +}; + +template +class UmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + UmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "umbrella load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class UUIDLoadCommandAtom : public LoadCommandAtom +{ +public: + UUIDLoadCommandAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) {} + virtual const char* getDisplayName() const { return "uuid load command"; } + virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void generate(); + void setContent(const uint8_t uuid[16]); + const uint8_t* getUUID() { return fUUID; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uuid_t fUUID; + bool fEmit; +}; + + +template +class RPathLoadCommandsAtom : public LoadCommandAtom +{ +public: + RPathLoadCommandsAtom(Writer& writer, const char* path) + : LoadCommandAtom(writer, Segment::fgTextSegment), fPath(path) {} + virtual const char* getDisplayName() const { return "rpath load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fPath; +}; + +template +class EncryptionLoadCommandsAtom : public LoadCommandAtom +{ +public: + EncryptionLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fStartOffset(0), + fEndOffset(0) {} + virtual const char* getDisplayName() const { return "encryption info load command"; } + virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command); } + virtual void copyRawContent(uint8_t buffer[]) const; + void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; } + void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint32_t fStartOffset; + uint32_t fEndOffset; +}; + +template +class LoadCommandsPaddingAtom : public WriterAtom +{ +public: + LoadCommandsPaddingAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment), fSize(0) {} + virtual const char* getDisplayName() const { return "header padding"; } + virtual uint64_t getSize() const { return fSize; } + virtual const char* getSectionName() const { return "._load_cmds_pad"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void setSize(uint64_t newSize); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint64_t fSize; +}; + +template +class LinkEditAtom : public WriterAtom +{ +public: + LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment), fOrdinal(fgCurrentOrdinal++) {} + uint64_t getFileOffset() const; + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } + virtual uint32_t getOrdinal() const { return fOrdinal; } +private: + uint32_t fOrdinal; + static uint32_t fgCurrentOrdinal; +private: + typedef typename A::P P; +}; + +template uint32_t LinkEditAtom::fgCurrentOrdinal = 0; + +template +class SectionRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "section relocations"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._section_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class LocalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "local relocations"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._local_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SymbolTableLinkEditAtom : public LinkEditAtom +{ +public: + SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "symbol table"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._symbol_table"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class ExternalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "external relocations"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._extern_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +struct IndirectEntry { + uint32_t indirectIndex; + uint32_t symbolIndex; +}; + + +template +class SegmentSplitInfoContentAtom : public LinkEditAtom +{ +public: + SegmentSplitInfoContentAtom(Writer& writer) : LinkEditAtom(writer), fCantEncode(false) { } + virtual const char* getDisplayName() const { return "split segment info"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._split_info"; } + virtual void copyRawContent(uint8_t buffer[]) const; + bool canEncode() { return !fCantEncode; } + void setCantEncode() { fCantEncode = true; } + void add32bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind1Locations.push_back(AtomAndOffset(atom, offset)); } + void add64bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind2Locations.push_back(AtomAndOffset(atom, offset)); } + void addPPCHi16Location(const ObjectFile::Atom* atom, uint32_t offset) { fKind3Locations.push_back(AtomAndOffset(atom, offset)); } + void add32bitImportLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind4Locations.push_back(AtomAndOffset(atom, offset)); } + void encode(); + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + struct AtomAndOffset { + AtomAndOffset(const ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} + const ObjectFile::Atom* atom; + uint32_t offset; + }; + void uleb128EncodeAddresses(const std::vector& locations); + + std::vector fKind1Locations; + std::vector fKind2Locations; + std::vector fKind3Locations; + std::vector fKind4Locations; + std::vector fEncodedData; + bool fCantEncode; +}; + +template +class IndirectTableLinkEditAtom : public LinkEditAtom +{ +public: + IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "indirect symbol table"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._indirect_syms"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + std::vector fTable; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class ModuleInfoLinkEditAtom : public LinkEditAtom +{ +public: + ModuleInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer), fModuleNameOffset(0) { } + virtual const char* getDisplayName() const { return "module table"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._module_info"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); } + uint32_t getTableOfContentsFileOffset() const; + uint32_t getModuleTableFileOffset() const; + uint32_t getReferencesFileOffset() const; + uint32_t getReferencesCount() const; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint32_t fModuleNameOffset; +}; + + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +template +class StringsLinkEditAtom : public LinkEditAtom +{ +public: + StringsLinkEditAtom(Writer& writer); + virtual const char* getDisplayName() const { return "string pool"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._string_pool"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + int32_t add(const char* name); + int32_t addUnique(const char* name); + int32_t emptyString() { return 1; } + const char* stringForIndex(int32_t) const; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + enum { kBufferSize = 0x01000000 }; + typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; + + std::vector fFullBuffers; + char* fCurrentBuffer; + uint32_t fCurrentBufferUsed; + StringToOffset fUniqueStrings; +}; + + + +template +class UndefinedSymbolProxyAtom : public WriterAtom +{ +public: + UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, Segment::fgLinkEditSegment), fName(name) {} + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual uint64_t getSize() const { return 0; } + virtual const char* getSectionName() const { return "._imports"; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class BranchIslandAtom : public WriterAtom +{ +public: + BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__text"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + uint32_t fTargetOffset; +}; + +template +class StubAtom : public WriterAtom +{ +public: + StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual ObjectFile::Alignment getAlignment() const; + virtual const char* getSectionName() const { return "__symbol_stub1"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + static const char* stubName(const char* importName); + bool pic() const { return fWriter.fSlideable; } + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; + bool fForLazyDylib; +}; + +template +class StubHelperAtom : public WriterAtom +{ +public: + StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer, bool forLazyDylib); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__stub_helper"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + static const char* stubName(const char* importName); + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + +template +class LazyPointerAtom : public WriterAtom +{ +public: + LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, + StubAtom& stub, bool forLazyDylib); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } + virtual const char* getSectionName() const { return fForLazyDylib ? "__ld_symbol_ptr" : "__la_symbol_ptr"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fExternalTarget; } +private: + using WriterAtom::fWriter; + static const char* lazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + ObjectFile::Atom& fExternalTarget; + std::vector fReferences; + bool fForLazyDylib; +}; + + +template +class NonLazyPointerAtom : public WriterAtom +{ +public: + NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } + virtual const char* getSectionName() const { return "__nl_symbol_ptr"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + using WriterAtom::fWriter; + static const char* nonlazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class ObjCInfoAtom : public WriterAtom +{ +public: + ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcContraint, + bool objcReplacementClasses); + virtual const char* getName() const { return "objc$info"; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return 8; } + virtual const char* getSectionName() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + Segment& getInfoSegment() const; + uint32_t fContent[2]; +}; + + +template +class WriterReference : public ObjectFile::Reference +{ +public: + typedef typename A::ReferenceKinds Kinds; + + WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, + uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) + : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), + fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} + + virtual ~WriterReference() {} + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return (uint8_t)fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } + virtual const char* getTargetName() const { return fTarget->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *fTarget; } + virtual uint64_t getTargetOffset() const { return fTargetOffset; } + virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } + virtual const char* getFromTargetName() const { return fFromTarget->getName(); } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; } + virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } + virtual void setFromTargetName(const char* name) { } + virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; } + virtual const char* getDescription() const { return "writer reference"; } + virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } + +private: + Kinds fKind; + uint32_t fFixUpOffsetInSrc; + ObjectFile::Atom* fTarget; + uint32_t fTargetOffset; + ObjectFile::Atom* fFromTarget; + uint32_t fFromTargetOffset; +}; + + + +template <> +StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, + ObjectFile::Atom& lazyPointer, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubHelpers.push_back(this); + + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldLazyDylibHelper)); + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); + } +} + +template <> +uint64_t StubHelperAtom::getSize() const +{ + return 12; +} + +template <> +void StubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0xE9; // jmp dyld_stub_binding_helper + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; +} + + +template +const char* StubHelperAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stubHelper", name); + return buf; +} + + +// specialize lazy pointer for x86_64 to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + StubHelperAtom* helper = new StubHelperAtom(writer, target, *this, forLazyDylib); + fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); +} + +// specialize lazy pointer for x86 to initially pointer to second half of stub +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + // helper part of stub is 14 or 6 bytes into stub + fReferences.push_back(new WriterReference(0, x86::kPointer, &stub, writer.fSlideable ? 14 : 6)); +} + +template +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + + + +template +const char* LazyPointerAtom::lazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$lazy_pointer", name); + return buf; +} + +template +void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +template +NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedNonLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + +template +const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$non_lazy_pointer", name); + return buf; +} + +template +void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + + +template <> +bool StubAtom::pic() const +{ + // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB. + // Usually that only happens if page zero is very large + return ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ); +} + + +template <> +bool StubAtom::pic() const +{ + return fWriter.fSlideable; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), + fTarget(target), fForLazyDylib(forLazyDylib) +{ + writer.fAllSynthesizedStubs.push_back(this); + LazyPointerAtom* lp; + if ( fWriter.fOptions.prebind() ) { + // for prebound ppc, lazy pointer starts out pointing to target symbol's address + // if target is a weak definition within this linkage unit or zero if in some dylib + lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + } + else { + // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); + } + } + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, this, 8)); + fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, this, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc::kAbsLow16, lp)); + } +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), + fTarget(target), fForLazyDylib(forLazyDylib) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); + } + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, this, 8)); + fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, this, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc64::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc64::kAbsLow14, lp)); + } +} + +// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, (writer.fOptions.slowx86Stubs() || forLazyDylib) ? Segment::fgTextSegment : + ( writer.fOptions.readOnlyx86Stubs() ? Segment::fgROImportSegment : Segment::fgImportSegment)), + fTarget(target), fForLazyDylib(forLazyDylib) +{ + if ( writer.fOptions.slowx86Stubs() || forLazyDylib ) { + fName = stubName(target.getName()); + writer.fAllSynthesizedStubs.push_back(this); + LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + ObjectFile::Atom* helper; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + helper = writer.fDyldLazyDylibHelper; + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + helper = writer.fDyldHelper; + } + if ( pic() ) { + // picbase is 5 bytes into atom + fReferences.push_back(new WriterReference(8, x86::kPointerDiff, lp, 0, this, 5)); + fReferences.push_back(new WriterReference(16, x86::kPCRel32, helper)); + } + else { + fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); + fReferences.push_back(new WriterReference(7, x86::kAbsolute32, lp)); + fReferences.push_back(new WriterReference(12, x86::kPCRel32, helper)); + } + } + else { + if ( &target == NULL ) + fName = "cache-line-crossing-stub"; + else { + fName = stubName(target.getName()); + writer.fAllSynthesizedStubs.push_back(this); + } + } +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, lp)); +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp; + if ( fWriter.fOptions.prebind() && !forLazyDylib ) { + // for prebound arm, lazy pointer starts out pointing to target symbol's address + // if target is a weak definition within this linkage unit or zero if in some dylib + lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + } + else { + // for non-prebound arm, lazy pointer starts out pointing to dyld_stub_binding_helper glue code + ObjectFile::Atom* helper; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + helper = writer.fDyldLazyDylibHelper; + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + helper = writer.fDyldHelper; + } + lp = new LazyPointerAtom(writer, *helper, *this, forLazyDylib); + } + if ( pic() ) + fReferences.push_back(new WriterReference(12, arm::kPointerDiff, lp, 0, this, 12)); + else + fReferences.push_back(new WriterReference(8, arm::kPointer, lp)); +} + +template +const char* StubAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stub", name); + return buf; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 16 : 12 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { + if ( pic() ) + return 20; + else + return 16; + } + return 5; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return 6; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) + return 2; + else + return 0; // special case x86 fast stubs to be byte aligned +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + else { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) + OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + else { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) + OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { + if ( pic() ) { + buffer[0] = 0xE8; // call picbase + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x58; // pop eax + buffer[6] = 0x8D; // lea foo$lazy_pointer-picbase(eax),eax + buffer[7] = 0x80; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0xFF; // jmp *(eax) + buffer[13] = 0x20; + buffer[14] = 0x50; // push eax + buffer[15] = 0xE9; // jump dyld_stub_binding_helper + buffer[16] = 0x00; + buffer[17] = 0x00; + buffer[18] = 0x00; + buffer[19] = 0x00; + } + else { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x68; // pushl $foo$lazy_pointer + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0xE9; // jump dyld_stub_binding_helper + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x00; + } + } + else { + if ( fWriter.fOptions.prebind() ) { + uint32_t address = this->getAddress(); + int32_t rel32 = 0 - (address+5); + buffer[0] = 0xE9; + buffer[1] = rel32 & 0xFF; + buffer[2] = (rel32 >> 8) & 0xFF; + buffer[3] = (rel32 >> 16) & 0xFF; + buffer[4] = (rel32 >> 24) & 0xFF; + } + else { + buffer[0] = 0xF4; + buffer[1] = 0xF4; + buffer[2] = 0xF4; + buffer[3] = 0xF4; + buffer[4] = 0xF4; + } + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + } + else { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + } +} + +// x86_64 stubs are 7 bytes and need no alignment +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 0; +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub4" : "__symbol_stub4"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { + if ( pic() ) + return "__picsymbol_stub"; + else + return "__symbol_stub"; + } + return "__jump_table"; +} + + + + +struct AtomByNameSorter +{ + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) + { + return (strcmp(left->getName(), right->getName()) < 0); + } +}; + +template +struct ExternalRelocSorter +{ + bool operator()(const macho_relocation_info

& left, const macho_relocation_info

& right) + { + // sort first by symbol number + if ( left.r_symbolnum() != right.r_symbolnum() ) + return (left.r_symbolnum() < right.r_symbolnum()); + // then sort all uses of the same symbol by address + return (left.r_address() < right.r_address()); + } +}; + + +template +Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) + : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), + fAllAtoms(NULL), fStabs(NULL), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), + fSymbolTableCommands(NULL), fHeaderPadding(NULL), + fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), fDyldHelper(NULL), fDyldLazyDylibHelper(NULL), + fSectionRelocationsAtom(NULL), fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), + fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL), + fStringsAtom(NULL), fPageZeroAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), fSymbolTableStabsCount(0), + fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), + fLargestAtomSize(1), + fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), + fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), + fBiggerThanTwoGigs(false), fSlideable(false), + fFirstWritableSegment(NULL), fAnonNameIndex(1000) +{ + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + if ( fOptions.zeroPageSize() != 0 ) + fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + if ( fOptions.hasCustomStack() ) + fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + // fall through + case Options::kObjectFile: + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); + if ( fOptions.initFunctionName() != NULL ) + fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.sharedRegionEligible() ) + fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + if ( fOptions.sharedRegionEligible() ) { + fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + if ( this->needsModuleTable() ) + fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDyld: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + } + + // add extra commmands + bool hasReExports = false; + uint32_t ordinal = 1; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + if ( fOptions.makeEncryptable() ) { + fEncryptionLoadCommand = new EncryptionLoadCommandsAtom(*this); + fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand); + } + // fall through + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + { + // add dylib load command atoms for all dynamic libraries + const unsigned int libCount = dynamicLibraries.size(); + for (unsigned int i=0; i < libCount; ++i) { + ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; + //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() ); + + if ( dylibInfo.options.fReExport ) { + hasReExports = true; + } + else { + const char* parentUmbrella = dylibInfo.reader->parentUmbrella(); + if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) { + const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/'); + if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) ) + hasReExports = true; + } + } + + if ( dylibInfo.options.fBundleLoader ) { + fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL; + } + else { + // see if a DylibLoadCommandsAtom has already been created for this install path + bool newDylib = true; + const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); + for (unsigned int seenLib=0; seenLib < i; ++seenLib) { + ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; + if ( !seenDylibInfo.options.fBundleLoader ) { + const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); + if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { + fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; + fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader]; + fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader; + newDylib = false; + break; + } + } + } + + if ( newDylib ) { + // assign new ordinal and check for other paired load commands + fLibraryToOrdinal[dylibInfo.reader] = ordinal++; + DylibLoadCommandsAtom* dyliblc = new DylibLoadCommandsAtom(*this, dylibInfo); + fLibraryToLoadCommand[dylibInfo.reader] = dyliblc; + fWriterSynthesizedAtoms.push_back(dyliblc); + if ( dylibInfo.options.fReExport + && (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) + && (fOptions.outputKind() == Options::kDynamicLibrary) ) { + // see if child has sub-framework that is this + bool isSubFramework = false; + const char* childInUmbrella = dylibInfo.reader->parentUmbrella(); + if ( childInUmbrella != NULL ) { + const char* myLeaf = strrchr(fOptions.installPath(), '/'); + if ( myLeaf != NULL ) { + if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) + isSubFramework = true; + } + } + // LC_SUB_FRAMEWORK is in child, so do nothing in parent + if ( ! isSubFramework ) { + // this dylib also needs a sub_x load command + bool isFrameworkReExport = false; + const char* lastSlash = strrchr(dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + char frameworkName[strlen(lastSlash)+20]; + sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); + isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); + } + if ( isFrameworkReExport ) { + // needs a LC_SUB_UMBRELLA command + fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1])); + } + else { + // needs a LC_SUB_LIBRARY command + const char* nameStart = &lastSlash[1]; + if ( lastSlash == NULL ) + nameStart = dylibInstallPath; + int len = strlen(nameStart); + const char* dot = strchr(nameStart, '.'); + if ( dot != NULL ) + len = dot - nameStart; + fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len)); + } + } + } + } + } + } + // add umbrella command if needed + if ( fOptions.umbrellaName() != NULL ) { + fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); + } + // add allowable client commands if used + std::vector& allowableClients = fOptions.allowableClients(); + for (std::vector::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it) + fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom(*this, *it)); + } + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + break; + } + fNoReExportedDylibs = !hasReExports; + + // add any rpath load commands + for(std::vector::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) { + fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom(*this, *it)); + } + + // set up fSlideable + switch ( fOptions.outputKind() ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + fSlideable = false; + break; + case Options::kDynamicExecutable: + fSlideable = fOptions.positionIndependentExecutable(); + break; + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fSlideable = true; + break; + } + + //fprintf(stderr, "ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); + //} +} + +template +Writer::~Writer() +{ + if ( fFilePath != NULL ) + free((void*)fFilePath); + if ( fSymbolTable != NULL ) + delete [] fSymbolTable; +} + + +// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments +template <>bool Writer::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); } +template bool Writer::mightNeedPadSegment() { return false; } + + +template +ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) +{ + if ( fOptions.outputKind() == Options::kObjectFile ) { + // when doing -r -exported_symbols_list, don't creat proxy for a symbol + // that is supposed to be exported. We want an error instead + // ld does not report error when -r is used and exported symbols are not defined. + if ( fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) + return NULL; + else + return new UndefinedSymbolProxyAtom(*this, name); + } + else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) ) + return new UndefinedSymbolProxyAtom(*this, name); + else + return NULL; +} + +template +uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) +{ + // flat namespace images use zero for all ordinals + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) + return 0; + + // is an UndefinedSymbolProxyAtom + if ( lib == this ) + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) + return DYNAMIC_LOOKUP_ORDINAL; + + std::map::iterator pos = fLibraryToOrdinal.find(lib); + if ( pos != fLibraryToOrdinal.end() ) + return pos->second; + + throw "can't find ordinal for imported symbol"; +} + +template +ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) +{ + return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses)); +} + + +template +uint64_t Writer::write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs, bool overridesDylibWeakDefines) +{ + fAllAtoms = &atoms; + fStabs = &stabs; + fEntryPoint = entryPointAtom; + fDyldHelper = dyldHelperAtom; + fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; + fCanScatter = canScatter; + fCpuConstraint = cpuConstraint; + fBiggerThanTwoGigs = biggerThanTwoGigs; + fHasWeakExports = overridesDylibWeakDefines; // dyld needs to search this image as if it had weak exports + + try { + // Set for create UUID + if (createUUID) + fUUIDAtom->generate(); + + // remove uneeded dylib load commands + optimizeDylibReferences(); + + // check for mdynamic-no-pic codegen + scanForAbsoluteReferences(); + + // create inter-library stubs + synthesizeStubs(); + + // create SegmentInfo and SectionInfo objects and assign all atoms to a section + partitionIntoSections(); + + // segment load command can now be sized and padding can be set + adjustLoadCommandsAndPadding(); + + // assign each section a file offset + assignFileOffsets(); + + // if need to add branch islands, reassign file offsets + if ( addBranchIslands() ) + assignFileOffsets(); + + // build symbol table and relocations + buildLinkEdit(); + + // write map file if requested + writeMap(); + + // write everything + return writeAtoms(); + } catch (...) { + // clean up if any errors + (void)unlink(fFilePath); + throw; + } +} + +template +void Writer::buildLinkEdit() +{ + this->collectExportedAndImportedAndLocalAtoms(); + this->buildSymbolTable(); + this->buildFixups(); + this->adjustLinkEditSections(); +} + + + +template +uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) +{ + return atom->getAddress(); +// SectionInfo* info = (SectionInfo*)atom->getSection(); +// return info->getBaseAddress() + atom->getSectionOffset(); +} + + +template <> +const char* Writer::symbolTableName(const ObjectFile::Atom* atom) +{ + static unsigned int counter = 0; + const char* name = atom->getName(); + if ( strncmp(name, "cstring=", 8) == 0 ) + asprintf((char**)&name, "LC%u", counter++); + return name; +} + +template +const char* Writer::symbolTableName(const ObjectFile::Atom* atom) +{ + return atom->getName(); +} + +template +void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_strx + entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom))); + + // set n_type + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { + entry->set_n_type(N_EXT | N_ABS); + } + else { + entry->set_n_type(N_EXT | N_SECT); + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { + if ( fOptions.keepPrivateExterns() ) + entry->set_n_type(N_EXT | N_SECT | N_PEXT); + } + } + + // set n_sect (section number of implementation ) + uint8_t sectionIndex = atom->getSection()->getIndex(); + entry->set_n_sect(sectionIndex); + + // the __mh_execute_header is magic and must be an absolute symbol + if ( (sectionIndex==0) + && (fOptions.outputKind() == Options::kDynamicExecutable) + && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) + entry->set_n_type(N_EXT | N_ABS); + + // set n_desc + uint16_t desc = 0; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + desc |= N_WEAK_DEF; + fHasWeakExports = true; + } + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) + entry->set_n_value(atom->getSectionOffset()); + else + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + +template +void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_strx + entry->set_n_strx(this->fStringsAtom->add(atom->getName())); + + // set n_type + if ( (fOptions.outputKind() == Options::kObjectFile) + && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) + && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) + entry->set_n_type(N_UNDF | N_EXT | N_PEXT); + else if ( fOptions.prebind() ) + entry->set_n_type(N_PBUD | N_EXT); + else + entry->set_n_type(N_UNDF | N_EXT); + + // set n_sect + entry->set_n_sect(0); + + uint16_t desc = 0; + if ( fOptions.outputKind() != Options::kObjectFile ) { + // set n_desc ( high byte is library ordinal, low byte is reference type ) + std::map::iterator pos = fStubsMap.find(atom); + if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) + desc = REFERENCE_FLAG_UNDEFINED_LAZY; + else + desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY; + try { + uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); + //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); + SET_LIBRARY_ORDINAL(desc, ordinal); + } + catch (const char* msg) { + throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) { + uint8_t align = atom->getAlignment().powerOf2; + // always record custom alignment of common symbols to match what compiler does + SET_COMM_ALIGN(desc, align); + } + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + desc |= N_REF_TO_WEAK; + fReferencesWeakImports = true; + } + // set weak_import attribute + if ( fWeakImportMap[atom] ) + desc |= N_WEAK_REF; + entry->set_n_desc(desc); + + // set n_value, zero for import proxy and size for tentative definition + entry->set_n_value(atom->getSize()); +} + + +template +void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_strx + const char* symbolName = this->symbolTableName(atom); + char anonName[32]; + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { + sprintf(anonName, "l%u", fAnonNameIndex++); + symbolName = anonName; + } + entry->set_n_strx(this->fStringsAtom->add(symbolName)); + + // set n_type + uint8_t type = N_SECT; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) + type = N_ABS; + if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) + type |= N_PEXT; + entry->set_n_type(type); + + // set n_sect (section number of implementation ) + uint8_t sectIndex = atom->getSection()->getIndex(); + if ( sectIndex == 0 ) { + // see synthesized lable for mach_header needs special section number... + if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) + sectIndex = 1; + } + entry->set_n_sect(sectIndex); + + // set n_desc + uint16_t desc = 0; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + desc |= N_WEAK_DEF; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) + entry->set_n_value(atom->getSectionOffset()); + else + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + + +template +void Writer::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) +{ + macho_nlist

entry; + + // set n_strx + entry.set_n_strx(fStringsAtom->add(name)); + + // set n_type + entry.set_n_type(N_SECT); + + // set n_sect (section number of implementation ) + entry.set_n_sect(atom.getSection()->getIndex()); + + // set n_desc + entry.set_n_desc(0); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); + + // add + fLocalExtraLabels.push_back(entry); +} + + + +template +void Writer::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) +{ + macho_nlist

entry; + + // set n_strx + entry.set_n_strx(fStringsAtom->add(name)); + + // set n_type + entry.set_n_type(N_SECT|N_EXT); + + // set n_sect (section number of implementation ) + entry.set_n_sect(atom.getSection()->getIndex()); + + // set n_desc + entry.set_n_desc(0); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); + + // add + fGlobalExtraLabels.push_back(entry); +} + +template +void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for (uint32_t i=0; i < count; ++i, ++entry) { + ObjectFile::Atom* atom = atoms[i]; + if ( &atoms == &fExportedAtoms ) { + this->setExportNlist(atom, entry); + } + else if ( &atoms == &fImportedAtoms ) { + this->setImportNlist(atom, entry); + } + else { + this->setLocalNlist(atom, entry); + } + } +} + +template +void Writer::copyNlistRange(const std::vector >& entries, uint32_t startIndex) +{ + for ( typename std::vector >::const_iterator it = entries.begin(); it != entries.end(); ++it) + fSymbolTable[startIndex++] = *it; +} + + +template +struct NListNameSorter +{ + NListNameSorter(StringsLinkEditAtom* pool) : fStringPool(pool) {} + + bool operator()(const macho_nlist& left, const macho_nlist& right) + { + return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0); + } +private: + StringsLinkEditAtom* fStringPool; +}; + + +template +void Writer::buildSymbolTable() +{ + fSymbolTableStabsStartIndex = 0; + fSymbolTableStabsCount = fStabs->size(); + fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; + fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size(); + fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; + fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size(); + fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; + fSymbolTableImportCount = fImportedAtoms.size(); + + // allocate symbol table + fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; + fSymbolTable = new macho_nlist

[fSymbolTableCount]; + + // fill in symbol table and string pool (do stabs last so strings are at end of pool) + setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size()); + if ( fLocalExtraLabels.size() != 0 ) + copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size()); + setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size()); + if ( fGlobalExtraLabels.size() != 0 ) { + copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size()); + // re-sort combined range + std::sort( &fSymbolTable[fSymbolTableExportStartIndex], + &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount], + NListNameSorter(fStringsAtom) ); + } + setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); + addStabs(fSymbolTableStabsStartIndex); + + // set up module table + if ( fModuleInfoAtom != NULL ) + fModuleInfoAtom->setName(); +} + + + +template +bool Writer::shouldExport(const ObjectFile::Atom& atom) const +{ + switch ( atom.getSymbolTableInclusion() ) { + case ObjectFile::Atom::kSymbolTableNotIn: + return false; + case ObjectFile::Atom::kSymbolTableInAndNeverStrip: + return true; + case ObjectFile::Atom::kSymbolTableInAsAbsolute: + case ObjectFile::Atom::kSymbolTableIn: + switch ( atom.getScope() ) { + case ObjectFile::Atom::scopeGlobal: + return true; + case ObjectFile::Atom::scopeLinkageUnit: + return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); + default: + return false; + } + break; + } + return false; +} + +template +void Writer::collectExportedAndImportedAndLocalAtoms() +{ + const int atomCount = fAllAtoms->size(); + // guess at sizes of each bucket to minimize re-allocations + fImportedAtoms.reserve(100); + fExportedAtoms.reserve(atomCount/2); + fLocalSymbolAtoms.reserve(atomCount); + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + // only named atoms go in symbol table + if ( atom->getName() != NULL ) { + // put atom into correct bucket: imports, exports, locals + //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + fImportedAtoms.push_back(atom); + break; + case ObjectFile::Atom::kTentativeDefinition: + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { + fImportedAtoms.push_back(atom); + break; + } + // else fall into + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + if ( this->shouldExport(*atom) ) + fExportedAtoms.push_back(atom); + else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) + && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) + fLocalSymbolAtoms.push_back(atom); + break; + } + } + // when geneating a .o file, dtrace static probes become local labels + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == A::kDtraceProbe ) { + // dtrace probe points to be add back into generated .o file + this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + } + } + } + // when linking kernel, old style dtrace static probes become global labels + else if ( fOptions.readerOptions().fForStatic ) { + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == A::kDtraceProbe ) { + // dtrace probe points to be add back into generated .o file + this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + } + } + } + } + + // sort exported atoms by name + std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter()); + // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable) + std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter()); +} + + +template +uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) +{ + switch ( stab.type ) { + case N_FUN: + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of function N_FUN has size + return stab.atom->getSize(); + } + else { + // start of function N_FUN has address + return getAtomLoadAddress(stab.atom); + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + if ( stab.atom == NULL ) + // some weird assembly files have slines not associated with a function + return stab.value; + else + // all these stab types need their value changed from an offset in the atom to an address + return getAtomLoadAddress(stab.atom) + stab.value; + case N_STSYM: + case N_LCSYM: + case N_BNSYM: + // all these need address of atom + return getAtomLoadAddress(stab.atom);; + case N_ENSYM: + return stab.atom->getSize(); + case N_SO: + if ( stab.atom == NULL ) { + return 0; + } + else { + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of translation unit N_SO has address of end of last atom + return getAtomLoadAddress(stab.atom) + stab.atom->getSize(); + } + else { + // start of translation unit N_SO has address of end of first atom + return getAtomLoadAddress(stab.atom); + } + } + break; + default: + return stab.value; + } +} + +template +uint32_t Writer::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) +{ + switch (stab.type) { + case N_SO: + if ( (stab.string == NULL) || stab.string[0] == '\0' ) { + return this->fStringsAtom->emptyString(); + break; + } + // fall into uniquing case + case N_SOL: + case N_BINCL: + case N_EXCL: + return this->fStringsAtom->addUnique(stab.string); + break; + default: + if ( stab.string == NULL ) + return 0; + else if ( stab.string[0] == '\0' ) + return this->fStringsAtom->emptyString(); + else + return this->fStringsAtom->add(stab.string); + } + return 0; +} + +template +uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) +{ + // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN + if ( stab.type == N_FUN ) + return stab.other; + else if ( stab.atom != NULL ) + return stab.atom->getSection()->getIndex(); + else + return stab.other; +} + +template +void Writer::addStabs(uint32_t startIndex) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for(std::vector::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) { + const ObjectFile::Reader::Stab& stab = *it; + entry->set_n_type(stab.type); + entry->set_n_sect(sectionIndexForStab(stab)); + entry->set_n_desc(stab.desc); + entry->set_n_value(valueForStab(stab)); + entry->set_n_strx(stringOffsetForStab(stab)); + } +} + + + +template +uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) +{ + // search imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableImportStartIndex; + ++i; + } + + // search locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableLocalStartIndex; + ++i; + } + + // search exports + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableExportStartIndex; + ++i; + } + + throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); +} + + +template <> +bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const +{ + switch ( target.getSymbolTableInclusion() ) { + case ObjectFile::Atom::kSymbolTableNotIn: + return false; + case ObjectFile::Atom::kSymbolTableInAsAbsolute: + case ObjectFile::Atom::kSymbolTableIn: + case ObjectFile::Atom::kSymbolTableInAndNeverStrip: + return true; + }; + return false; +} + +template +bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const +{ + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + case ObjectFile::Atom::kTentativeDefinition: + if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal ) + return false; + else + return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit); + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + return shouldExport(target); + } + return false; +} + +template +void Writer::buildFixups() +{ + if ( fOptions.outputKind() == Options::kObjectFile ) { + this->buildObjectFileFixups(); + } + else { + if ( fOptions.keepRelocations() ) + this->buildObjectFileFixups(); + this->buildExecutableFixups(); + } +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool external = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + case x86_64::kGroupSubordinate: + return 0; + + case x86_64::kPointer: + case x86_64::kPointerWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPointerDiff32: + case x86_64::kPointerDiff: + { + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex(); + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolIndex); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + fSectionRelocs.push_back(reloc1); + fSectionRelocs.push_back(reloc2); + return 2; + } + + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kDtraceProbeSite: + case x86_64::kDtraceIsEnabledSite: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32_1: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_1); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32_2: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_2); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32_4: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_4); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kBranchPCRel8: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(0); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // generates no relocs + return 0; + } + return 0; +} + + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); + + if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) + warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", + target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); + + + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + case x86::kGroupSubordinate: + return 0; + + case x86::kPointer: + case x86::kPointerWeakImport: + case x86::kAbsolute32: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case x86::kPointerDiff16: + case x86::kPointerDiff: + { + //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n", + // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(), + // ref->getFromTarget().getAddress(), ref->getFromTargetOffset()); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); + sreloc2->set_r_type(GENERIC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case x86::kPCRel32WeakImport: + case x86::kPCRel32: + case x86::kPCRel16: + case x86::kPCRel8: + case x86::kDtraceProbeSite: + case x86::kDtraceIsEnabledSite: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case x86::kDtraceTypeReference: + case x86::kDtraceProbe: + // generates no relocs + return 0; + + } + return 0; +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind(); + + if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) + warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", + target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); + + + switch ( kind ) { + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + return 0; + + case arm::kPointer: + case arm::kReadOnlyPointer: + case arm::kPointerWeakImport: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(ARM_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case arm::kPointerDiff: + { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(ARM_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(ARM_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case arm::kBranch24WeakImport: + case arm::kBranch24: + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(ARM_RELOC_BR24); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case arm::kThumbBranch22WeakImport: + case arm::kThumbBranch22: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(ARM_THUMB_RELOC_BR22); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case arm::kDtraceTypeReference: + case arm::kDtraceProbe: + // generates no relocs + return 0; + + } + return 0; +} + +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 2; +} + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 3; +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +// +// addObjectRelocs and addObjectRelocs are almost exactly the same, so +// they use a common addObjectRelocs_powerpc() method. +// +template +uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case A::kNoFixUp: + case A::kFollowOn: + case A::kGroupSubordinate: + return 0; + + case A::kPointer: + case A::kPointerWeakImport: + if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) { + // use scattered reloc is target offset is outside target + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(getRelocPointerSize()); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(getRelocPointerSize()); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case A::kPointerDiff16: + case A::kPointerDiff32: + case A::kPointerDiff64: + { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(sreloc1->r_length()); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kBranch24WeakImport: + case A::kBranch24: + case A::kDtraceProbeSite: + case A::kDtraceIsEnabledSite: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR24); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case A::kBranch14: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR14); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case A::kPICBaseLow16: + case A::kPICBaseLow14: + { + pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kPICBaseHigh16: + { + pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kAbsLow14: + case A::kAbsLow16: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() >> 16); + else + reloc2.set_r_address(toAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kAbsHigh16: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HI16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kAbsHigh16AddLow: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + uint32_t overflow = 0; + if ( (toAddr & 0x00008000) != 0 ) + overflow = 0x10000; + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HA16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kDtraceTypeReference: + case A::kDtraceProbe: + // generates no relocs + return 0; + } + return 0; +} + + + +// +// There are cases when an entry in the indirect symbol table is the magic value +// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens +// the content of the corresponding part of the __nl_symbol_pointer section +// must also change. +// +template +bool Writer::indirectSymbolIsLocal(const ObjectFile::Reference* ref) const +{ + // use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend + return ( !this->shouldExport(ref->getTarget()) || (ref->getTargetOffset() != 0) ); +} + + +template +void Writer::buildObjectFileFixups() +{ + uint32_t relocIndex = 0; + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName); + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers + || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + curSection->fRelocOffset = relocIndex; + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + //fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName()); + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers + || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) { + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / atom->getSize(); + uint32_t undefinedSymbolIndex; + if ( curSection->fAllStubs ) { + ObjectFile::Atom& stubTarget =ref->getTarget(); + ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget(); + undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); + //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); + } + else if ( curSection->fAllNonLazyPointers) { + // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend + if ( this->indirectSymbolIsLocal(ref) ) + undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + else + undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); + } + else { + // should never get here, fAllLazyPointers not used in generated .o files + undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + } + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + if ( curSection->fAllLazyPointers ) { + ObjectFile::Atom& target = ref->getTarget(); + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + if ( &fromTarget == NULL ) { + warning("lazy pointer %s missing initial binding", atom->getDisplayName()); + } + else { + bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition)) + && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ); + macho_relocation_info

reloc1; + reloc1.set_r_address(atom->getSectionOffset()); + reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + fSectionRelocs.push_back(reloc1); + ++relocIndex; + } + } + else if ( curSection->fAllStubs ) { + relocIndex += this->addObjectRelocs(atom, ref); + } + } + else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) { + relocIndex += this->addObjectRelocs(atom, ref); + } + } + } + curSection->fRelocCount = relocIndex - curSection->fRelocOffset; + } + } + } + + // reverse the relocs + std::reverse(fSectionRelocs.begin(), fSectionRelocs.end()); + + // now reverse section reloc offsets + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; + } + } + +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + switch ( ref.getKind() ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( fSlideable ) + return true; + } + return false; +} + + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + switch ( ref.getKind() ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( fSlideable ) + return true; + } + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // illegal in dylibs/bundles, until we support TEXT relocs + return fSlideable; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // illegal until we support TEXT relocs + return true; + case ObjectFile::Atom::kAbsoluteSymbol: + // absolute symbbols only allowed in static executables + return ( fOptions.outputKind() != Options::kStaticExecutable); + } + } + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + if ( ref.getKind() == arm::kReadOnlyPointer ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // illegal in dylibs/bundles, until we support TEXT relocs + return fSlideable; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // illegal until we support TEXT relocs + return true; + case ObjectFile::Atom::kAbsoluteSymbol: + // absolute symbbols only allowed in static executables + return ( fOptions.outputKind() != Options::kStaticExecutable); + } + } + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( fSlideable ) { + macho_relocation_info

reloc; + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(sectInfo->getIndex()); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(false); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(reloc); + atomSection->fHasTextLocalRelocs = true; + return true; + } + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + } + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + switch ( ref.getKind() ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( fSlideable ) { + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); + reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc1.set_r_symbolnum(sectInfo->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + reloc2.set_r_address(targetAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.push_back(reloc1); + fInternalRelocs.push_back(reloc2); + atomSection->fHasTextLocalRelocs = true; + return true; + } + break; + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( fSlideable ) { + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); + reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc1.set_r_symbolnum(sectInfo->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); + reloc2.set_r_address(targetAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.push_back(reloc1); + fInternalRelocs.push_back(reloc2); + atomSection->fHasTextLocalRelocs = true; + return true; + } + } + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( ref.getKind() == arm::kReadOnlyPointer ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( fSlideable ) { + macho_relocation_info

reloc; + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(sectInfo->getIndex()); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(false); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(reloc); + atomSection->fHasTextLocalRelocs = true; + return true; + } + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + } + return false; +} + + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) +{ + // text relocs not supported (usually never needed because of RIP addressing) + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) +{ + // text relocs not supported + return false; +} + +template <> +bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + macho_relocation_info

reloc; + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // a reference to the absolute address of something in another linkage unit can be + // encoded as an external text reloc in a dylib or bundle + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(true); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + fExternalRelocs.push_back(reloc); + atomSection->fHasTextExternalRelocs = true; + return true; + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + } + return false; +} + +template +bool Writer::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) +{ + return false; +} + + + + +template +typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const +{ + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + // in main executables, the only way regular symbols are indirected is if -interposable is used + if ( fOptions.outputKind() == Options::kDynamicExecutable ) { + if ( this->shouldExport(target) && fOptions.interposable(target.getName()) ) + return kRelocExternal; + else if ( fSlideable ) + return kRelocInternal; + else + return kRelocNone; + } + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + else if ( this->shouldExport(target) && + ((fOptions.nameSpace() == Options::kFlatNameSpace) + || (fOptions.nameSpace() == Options::kForceFlatNameSpace) + || fOptions.interposable(target.getName())) + && (target.getName() != NULL) + && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // + return kRelocExternal; + else if ( fSlideable ) + return kRelocInternal; + else + return kRelocNone; + case ObjectFile::Atom::kWeakDefinition: + // all calls to global weak definitions get indirected + if ( this->shouldExport(target) ) + return kRelocExternal; + else if ( fSlideable ) + return kRelocInternal; + else + return kRelocNone; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + return kRelocExternal; + case ObjectFile::Atom::kAbsoluteSymbol: + return kRelocNone; + } + return kRelocNone; +} + +template +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for 32-bit architectures, the r_address field in relocs + // for final linked images is the offset from the first segment + uint64_t result = address - fSegmentInfos[0]->fBaseAddress; + // or the offset from the first writable segment if built split-seg + if ( fOptions.splitSeg() ) + result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0x7FFFFFFF ) { + throwf("image too large: address can't fit in 31-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for x86_64, the r_address field in relocs for final linked images + // is the offset from the start address of the first writable segment + uint64_t result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address. + // the 10.5 dyld, iterprets the r_address as: + // 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise + // 2) an offset from the base address of the first writable segment + // For dyld, r_address is always the offset from the base address + uint64_t result; + bool badFor10_4 = false; + if ( fWritableSegmentPastFirst4GB ) { + if ( fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5 ) + badFor10_4 = true; + result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + result = address - fSegmentInfos[0]->fBaseAddress; + if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) && (result > 0x7FFFFFFF) ) + badFor10_4 = true; + } + if ( badFor10_4 ) { + throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + + +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; } + +template +void Writer::buildExecutableFixups() +{ + fIndirectTableAtom->fTable.reserve(50); // minimize reallocations + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + //fprintf(stderr, "starting section %s\n", curSection->fSectionName); + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers + || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol + if ( atom->getSize() != sizeof(pint_t) ) { + warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath()); + } + ObjectFile::Atom* pointerTarget = &(ref->getTarget()); + if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + pointerTarget = ((LazyPointerAtom*)atom)->getTarget(); + } + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(pint_t); + uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) + undefinedSymbolIndex = this->symbolIndex(*pointerTarget); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X), pointerTarget=%s\n", + // indirectTableIndex, undefinedSymbolIndex, pointerTarget->getDisplayName()); + fIndirectTableAtom->fTable.push_back(entry); + if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + uint8_t preboundLazyType; + if ( fOptions.prebind() && (fDyldHelper != NULL) + && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) { + // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid + macho_scattered_relocation_info

pblaReloc; + pblaReloc.set_r_scattered(true); + pblaReloc.set_r_pcrel(false); + pblaReloc.set_r_length(); + pblaReloc.set_r_type(preboundLazyType); + pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); + pblaReloc.set_r_value(fDyldHelper->getAddress()); + fInternalRelocs.push_back(*((macho_relocation_info

*)&pblaReloc)); + } + else if ( fSlideable ) { + // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides + macho_relocation_info

dyldHelperReloc; + uint32_t sectionNum = 1; + if ( fDyldHelper != NULL ) + sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex(); + //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); + dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); + dyldHelperReloc.set_r_symbolnum(sectionNum); + dyldHelperReloc.set_r_pcrel(false); + dyldHelperReloc.set_r_length(); + dyldHelperReloc.set_r_extern(false); + dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(dyldHelperReloc); + } + } + } + else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) { + if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { + throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { + case kRelocNone: + // no reloc needed + break; + case kRelocInternal: + { + macho_relocation_info

internalReloc; + SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); + uint32_t sectionNum = sectInfo->getIndex(); + // special case _mh_dylib_header and friends which are not in any real section + if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) + sectionNum = 1; + internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); + internalReloc.set_r_symbolnum(sectionNum); + internalReloc.set_r_pcrel(false); + internalReloc.set_r_length(); + internalReloc.set_r_extern(false); + internalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(internalReloc); + } + break; + case kRelocExternal: + { + macho_relocation_info

externalReloc; + externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); + externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); + externalReloc.set_r_pcrel(false); + externalReloc.set_r_length(); + externalReloc.set_r_extern(true); + externalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fExternalRelocs.push_back(externalReloc); + } + break; + } + } + else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { + if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { + if ( fOptions.warnAboutTextRelocs() ) + warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); + if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { + // relocs added to fInternalRelocs + } + else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) { + // relocs added to fExternalRelocs + } + else { + throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. " + "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath()); + } + } + } + if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { + ObjectFile::Atom* stubTarget = ((StubAtom*)atom)->getTarget(); + uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS; + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / atom->getSize(); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + } + } + } + } + } + if ( fSplitCodeToDataContentAtom != NULL ) + fSplitCodeToDataContentAtom->encode(); +} + + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (ppc::ReferenceKinds)ref->getKind() ) { + case ppc::kPICBaseHigh16: + fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); + break; + case ppc::kPointerDiff32: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc::kPointerDiff64: + fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc::kNoFixUp: + case ppc::kGroupSubordinate: + case ppc::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + // ignore + break; + default: + warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (ppc64::ReferenceKinds)ref->getKind() ) { + case ppc64::kPICBaseHigh16: + fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); + break; + case ppc64::kPointerDiff32: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc64::kPointerDiff64: + fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc64::kNoFixUp: + case ppc64::kGroupSubordinate: + case ppc64::kPointer: + case ppc64::kPointerWeakImport: + case ppc64::kPICBaseLow16: + case ppc64::kPICBaseLow14: + // ignore + break; + default: + warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (x86::ReferenceKinds)ref->getKind() ) { + case x86::kPointerDiff: + if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 ) + fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); + else + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case x86::kNoFixUp: + case x86::kGroupSubordinate: + case x86::kPointer: + case x86::kPointerWeakImport: + // ignore + break; + case x86::kPCRel32: + case x86::kPCRel32WeakImport: + if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment) + || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) { + fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); + break; + } + // fall into warning case + default: + warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (x86_64::ReferenceKinds)ref->getKind() ) { + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPointerDiff32: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case x86_64::kPointerDiff: + fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case x86_64::kNoFixUp: + case x86_64::kGroupSubordinate: + case x86_64::kPointer: + // ignore + break; + default: + warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (arm::ReferenceKinds)ref->getKind() ) { + case arm::kPointerDiff: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case arm::kNoFixUp: + case arm::kGroupSubordinate: + case arm::kPointer: + case arm::kPointerWeakImport: + case arm::kReadOnlyPointer: + // ignore + break; + default: + warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template +bool Writer::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to) +{ + switch ( to.getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // segments with same permissions slide together + return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable()) + || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) ); + } + throw "ld64 internal error"; +} + + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fd, &ppcNop, 4, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fd, &ppcNop, 4, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint8_t x86Nop = 0x90; + for (uint32_t p=from; p < to; ++p) + ::pwrite(fd, &x86Nop, 1, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint8_t x86Nop = 0x90; + for (uint32_t p=from; p < to; ++p) + ::pwrite(fd, &x86Nop, 1, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + // FIXME: need thumb nop? + uint32_t armNop; + OSWriteLittleInt32(&armNop, 0, 0xe1a00000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fd, &armNop, 4, p); +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; ++p) + *p = 0x90; +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; ++p) + *p = 0x90; +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + // fixme: need thumb nop? + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); +} + +static const char* stringName(const char* str) +{ + if ( strncmp(str, "cstring=", 8) == 0) { + static char buffer[1024]; + char* t = buffer; + *t++ = '\"'; + for(const char*s = &str[8]; *s != '\0'; ++s) { + switch(*s) { + case '\n': + *t++ = '\\'; + *t++ = 'n'; + break; + case '\t': + *t++ = '\\'; + *t++ = 't'; + break; + default: + *t++ = *s; + break; + } + if ( t > &buffer[1020] ) { + *t++= '\"'; + *t++= '.'; + *t++= '.'; + *t++= '.'; + *t++= '\0'; + return buffer; + } + } + *t++= '\"'; + *t++= '\0'; + return buffer; + } + else { + return str; + } +} + + +template <> const char* Writer::getArchString() { return "ppc"; } +template <> const char* Writer::getArchString() { return "ppc64"; } +template <> const char* Writer::getArchString() { return "i386"; } +template <> const char* Writer::getArchString() { return "x86_64"; } +template <> const char* Writer::getArchString() { return "arm"; } + +template +void Writer::writeMap() +{ + if ( fOptions.generatedMapPath() != NULL ) { + FILE* mapFile = fopen(fOptions.generatedMapPath(), "w"); + if ( mapFile != NULL ) { + // write output path + fprintf(mapFile, "# Path: %s\n", fFilePath); + // write output architecure + fprintf(mapFile, "# Arch: %s\n", getArchString()); + // write UUID + if ( fUUIDAtom != NULL ) { + const uint8_t* uuid = fUUIDAtom->getUUID(); + fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + } + // write table of object files + std::map readerToOrdinal; + std::map ordinalToReader; + std::map readerToFileOrdinal; + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + if ( ! (*secit)->fVirtualSection ) { + std::vector& sectionAtoms = (*secit)->fAtoms; + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Reader* reader = (*ait)->getFile(); + uint32_t readerOrdinal = (*ait)->getOrdinal(); + std::map::iterator pos = readerToOrdinal.find(reader); + if ( pos == readerToOrdinal.end() ) { + readerToOrdinal[reader] = readerOrdinal; + ordinalToReader[readerOrdinal] = reader; + } + } + } + } + } + fprintf(mapFile, "# Object files:\n"); + fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); + uint32_t fileIndex = 0; + readerToFileOrdinal[this] = fileIndex++; + for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first != 0 ) { + fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->getPath()); + readerToFileOrdinal[it->second] = fileIndex++; + } + } + // write table of sections + fprintf(mapFile, "# Sections:\n"); + fprintf(mapFile, "# Address\tSize \tSegment\tSection\n"); + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + if ( ! (*secit)->fVirtualSection ) { + SectionInfo* sect = *secit; + fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->getBaseAddress(), sect->fSize, + (*segit)->fName, sect->fSectionName); + } + } + } + // write table of symbols + fprintf(mapFile, "# Symbols:\n"); + fprintf(mapFile, "# Address\tSize \tFile Name\n"); + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + if ( ! (*secit)->fVirtualSection ) { + std::vector& sectionAtoms = (*secit)->fAtoms; + bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0); + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Atom* atom = *ait; + fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(), + readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName()); + } + } + } + } + fclose(mapFile); + } + else { + warning("could not write map file: %s\n", fOptions.generatedMapPath()); + } + } +} + +static const char* sCleanupFile = NULL; +static void cleanup(int sig) +{ + ::signal(sig, SIG_DFL); + if ( sCleanupFile != NULL ) { + ::unlink(sCleanupFile); + } + if ( sig == SIGINT ) + ::exit(1); +} + + +template +uint64_t Writer::writeAtoms() +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) ) + throwf("can't write output file: %s", fFilePath); + + int permissions = 0777; + if ( fOptions.outputKind() == Options::kObjectFile ) + permissions = 0666; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where fFilePath file is not writable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + (void)unlink(fFilePath); + + // try to allocate buffer for entire output file content + int fd = -1; + SectionInfo* lastSection = fSegmentInfos.back()->fSections.back(); + uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096); + uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1); + uint8_t* atomBuffer = NULL; + bool streaming = false; + if ( wholeBuffer == NULL ) { + fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); + atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; + streaming = true; + // install signal handlers to delete output file if program is killed + sCleanupFile = fFilePath; + ::signal(SIGINT, cleanup); + ::signal(SIGBUS, cleanup); + ::signal(SIGSEGV, cleanup); + } + uint32_t size = 0; + uint32_t end = 0; + try { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + bool isTextSeg = (strcmp(curSegment->fName, "__TEXT") == 0); + std::vector& sectionInfos = curSegment->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + SectionInfo* curSection = *secit; + std::vector& sectionAtoms = curSection->fAtoms; + //printf("writing with max atom size 0x%X\n", fLargestAtomSize); + //fprintf(stderr, "writing %lu atoms for section %s\n", sectionAtoms.size(), curSection->fSectionName); + if ( ! curSection->fAllZeroFill ) { + end = curSection->fFileOffset; + bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0); + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Atom* atom = *ait; + if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) + && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) + && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) { + uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset(); + if ( fileOffset != end ) { + if ( needsNops ) { + // fill gaps with no-ops + if ( streaming ) + writeNoOps(fd, end, fileOffset); + else + copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]); + } + else if ( streaming ) { + // zero fill gaps + if ( (fileOffset-end) == 4 ) { + uint32_t zero = 0; + ::pwrite(fd, &zero, 4, end); + } + else { + uint8_t zero = 0x00; + for (uint32_t p=end; p < fileOffset; ++p) + ::pwrite(fd, &zero, 1, p); + } + } + } + uint64_t atomSize = atom->getSize(); + if ( streaming ) { + if ( atomSize > fLargestAtomSize ) + throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX", + atom->getDisplayName(), atomSize, fLargestAtomSize); + } + else { + if ( fileOffset > fileBufferSize ) + throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX", + atom->getDisplayName(), fileOffset, fileBufferSize); + } + uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset]; + end = fileOffset+atomSize; + // copy raw bytes + atom->copyRawContent(buffer); + // apply any fix-ups + try { + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( fOptions.outputKind() == Options::kObjectFile ) { + // doing ld -r + // skip fix-ups for undefined targets + if ( &(ref->getTarget()) != NULL ) + this->fixUpReferenceRelocatable(ref, atom, buffer); + } + else { + // producing final linked image + this->fixUpReferenceFinal(ref, atom, buffer); + } + } + } + catch (const char* msg) { + throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n", + // fileOffset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); + if ( streaming ) { + // write out + ::pwrite(fd, buffer, atomSize, fileOffset); + } + else { + if ( (fileOffset + atomSize) > size ) + size = fileOffset + atomSize; + } + } + } + } + } + } + + // update content based UUID + if ( fOptions.getUUIDMode() == Options::kUUIDContent ) { + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + if ( streaming ) { + // if output file file did not fit in memory, re-read file to generate md5 hash + uint32_t kMD5BufferSize = 16*1024; + uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize); + if ( md5Buffer != NULL ) { + CC_MD5_CTX md5State; + CC_MD5_Init(&md5State); + ::lseek(fd, 0, SEEK_SET); + ssize_t len; + while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 ) + CC_MD5_Update(&md5State, md5Buffer, len); + CC_MD5_Final(digest, &md5State); + ::free(md5Buffer); + } + else { + // if malloc fails, fall back to random uuid + ::uuid_generate_random(digest); + } + fUUIDAtom->setContent(digest); + uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); + fUUIDAtom->copyRawContent(atomBuffer); + ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset); + } + else { + // if output file fit in memory, just genrate an md5 hash in memory + #if 1 + // temp hack for building on Tiger + CC_MD5_CTX md5State; + CC_MD5_Init(&md5State); + CC_MD5_Update(&md5State, wholeBuffer, size); + CC_MD5_Final(digest, &md5State); + #else + CC_MD5(wholeBuffer, size, digest); + #endif + fUUIDAtom->setContent(digest); + uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); + fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]); + } + } + } + catch (...) { + if ( sCleanupFile != NULL ) + ::unlink(sCleanupFile); + throw; + } + + // finish up + if ( streaming ) { + delete [] atomBuffer; + close(fd); + // restore default signal handlers + sCleanupFile = NULL; + ::signal(SIGINT, SIG_DFL); + ::signal(SIGBUS, SIG_DFL); + ::signal(SIGSEGV, SIG_DFL); + } + else { + // write whole output file in one chunk + fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); + ::pwrite(fd, wholeBuffer, size, 0); + close(fd); + delete [] wholeBuffer; + } + + return end; +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + int64_t displacement; + int64_t baseAddr; + uint32_t instruction; + uint32_t newInstruction; + uint64_t targetAddr = 0; + uint32_t firstDisp; + uint32_t nextDisp; + uint32_t opcode; + bool relocateableExternal = false; + bool is_bl; + bool is_blx; + bool targetIsThumb; + + if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { + targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); + } + + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + switch ( (arm::ReferenceKinds)(ref->getKind()) ) { + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + // do nothing + break; + case arm::kPointerWeakImport: + case arm::kPointer: + // If this is the lazy pointers section, then set all lazy pointers to + // point to the dyld stub binding helper. + if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers + || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound lazy pointer to another dylib ==> pointer contains zero + LittleEndian::set32(*fixUp, 0); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + // prebound lazy pointer to withing this dylib ==> pointer contains address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + break; + } + } + else if ( relocateableExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + else { + // pointer contains target address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + } + break; + case arm::kPointerDiff: + LittleEndian::set32(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case arm::kReadOnlyPointer: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); + break; + } + break; + case arm::kBranch24WeakImport: + case arm::kBranch24: + displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + // The pc added will be +8 from the pc + displacement -= 8; + // fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); + // max positive displacement is 0x007FFFFF << 2 + // max negative displacement is 0xFF800000 << 2 + if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { + throwf("b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + instruction = LittleEndian::get32(*fixUp); + // Make sure we are calling arm with bl, thumb with blx + is_bl = ((instruction & 0xFF000000) == 0xEB000000); + is_blx = ((instruction & 0xFE000000) == 0xFA000000); + if ( is_bl && ref->getTarget().isThumb() ) { + uint32_t opcode = 0xFA000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; + newInstruction = opcode | h_bit | disp; + } + else if ( is_blx && !ref->getTarget().isThumb() ) { + uint32_t opcode = 0xEB000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + newInstruction = opcode | disp; + } + else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { + throwf("don't know how to convert instruction %x referencing %s to thumb", + instruction, ref->getTarget().getDisplayName()); + } + else { + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); + } + LittleEndian::set32(*fixUp, newInstruction); + break; + case arm::kThumbBranch22WeakImport: + case arm::kThumbBranch22: + instruction = LittleEndian::get32(*fixUp); + is_bl = ((instruction & 0xF8000000) == 0xF8000000); + is_blx = ((instruction & 0xF8000000) == 0xE8000000); + targetIsThumb = ref->getTarget().isThumb(); + + // The pc added will be +4 from the pc + baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; + // If the target is not thumb, we will be generating a blx instruction + // Since blx cannot have the low bit set, set bit[1] of the target to + // bit[1] of the base address, so that the difference is a multiple of + // 4 bytes. + if ( !targetIsThumb ) { + targetAddr &= -3ULL; + targetAddr |= (baseAddr & 2LL); + } + displacement = targetAddr - baseAddr; + + // max positive displacement is 0x003FFFFE + // max negative displacement is 0xFFC00000 + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the first + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the next + // 11 bits of the displacement, as well as differentiating bl and blx. + { + firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; + nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; + if ( is_bl && !targetIsThumb ) { + opcode = 0xE800F000; + } + else if ( is_blx && targetIsThumb ) { + opcode = 0xF800F000; + } + else if ( !is_bl && !is_blx && !targetIsThumb ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, ref->getTarget().getDisplayName()); + } + else { + opcode = instruction & 0xF800F800; + } + newInstruction = opcode | (nextDisp << 16) | firstDisp; + LittleEndian::set32(*fixUp, newInstruction); + } + break; + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + if ( inAtom->isThumb() ) { + // change 32-bit blx call site to two thumb NOPs + LittleEndian::set32(*fixUp, 0x46C046C0); + } + else { + // change call site to a NOP + LittleEndian::set32(*fixUp, 0xE1A00000); + } + break; + case arm::kDtraceTypeReference: + case arm::kDtraceProbe: + // nothing to fix up + break; + default: + throw "boom shaka laka"; + } +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + int64_t displacement; + uint32_t instruction; + uint32_t newInstruction; + uint64_t targetAddr = 0; + int64_t baseAddr; + uint32_t firstDisp; + uint32_t nextDisp; + uint32_t opcode; + bool relocateableExternal = false; + bool is_bl; + bool is_blx; + bool targetIsThumb; + + if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { + targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); + } + + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + switch ( (arm::ReferenceKinds)(ref->getKind()) ) { + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + // do nothing + break; + case arm::kPointer: + case arm::kReadOnlyPointer: + case arm::kPointerWeakImport: + { + if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content + if ( this->indirectSymbolIsLocal(ref) ) + LittleEndian::set32(*fixUp, targetAddr); + else + LittleEndian::set32(*fixUp, 0); + } + else if ( relocateableExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + LittleEndian::set32(*fixUp, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + } + else { + // internal relocation + if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { + // pointer contains target address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + } + else { + // pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + } + break; + case arm::kPointerDiff: + LittleEndian::set32(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + case arm::kBranch24WeakImport: + case arm::kBranch24: + displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + // The pc added will be +8 from the pc + displacement -= 8; + // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + // max positive displacement is 0x007FFFFF << 2 + // max negative displacement is 0xFF800000 << 2 + if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { + throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + } + instruction = LittleEndian::get32(*fixUp); + // Make sure we are calling arm with bl, thumb with blx + is_bl = ((instruction & 0xFF000000) == 0xEB000000); + is_blx = ((instruction & 0xFE000000) == 0xFA000000); + if ( is_bl && ref->getTarget().isThumb() ) { + uint32_t opcode = 0xFA000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; + newInstruction = opcode | h_bit | disp; + } + else if ( is_blx && !ref->getTarget().isThumb() ) { + uint32_t opcode = 0xEB000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + newInstruction = opcode | disp; + } + else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { + throwf("don't know how to convert instruction %x referencing %s to thumb", + instruction, ref->getTarget().getDisplayName()); + } + else { + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); + } + LittleEndian::set32(*fixUp, newInstruction); + break; + case arm::kThumbBranch22WeakImport: + case arm::kThumbBranch22: + instruction = LittleEndian::get32(*fixUp); + is_bl = ((instruction & 0xF8000000) == 0xF8000000); + is_blx = ((instruction & 0xF8000000) == 0xE8000000); + targetIsThumb = ref->getTarget().isThumb(); + + // The pc added will be +4 from the pc + baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; + // If the target is not thumb, we will be generating a blx instruction + // Since blx cannot have the low bit set, set bit[1] of the target to + // bit[1] of the base address, so that the difference is a multiple of + // 4 bytes. + if (!targetIsThumb) { + targetAddr &= -3ULL; + targetAddr |= (baseAddr & 2LL); + } + displacement = targetAddr - baseAddr; + + //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + // max positive displacement is 0x003FFFFE + // max negative displacement is 0xFFC00000 + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the first + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the next + // 11 bits of the displacement, as well as differentiating bl and blx. + firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; + nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; + if ( is_bl && !targetIsThumb ) { + opcode = 0xE800F000; + } + else if ( is_blx && targetIsThumb ) { + opcode = 0xF800F000; + } + else if ( !is_bl && !is_blx && !targetIsThumb ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, ref->getTarget().getDisplayName()); + } + else { + opcode = instruction & 0xF800F800; + } + newInstruction = opcode | (nextDisp << 16) | firstDisp; + LittleEndian::set32(*fixUp, newInstruction); + break; + case arm::kDtraceProbe: + case arm::kDtraceTypeReference: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + uint8_t* dtraceProbeSite; + const int64_t kTwoGigLimit = 0x7FFFFFFF; + const int64_t kSixtyFourKiloLimit = 0x7FFF; + const int64_t kOneTwentyEightLimit = 0x7F; + int64_t displacement; + x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + case x86::kGroupSubordinate: + // do nothing + break; + case x86::kPointerWeakImport: + case x86::kPointer: + { + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + else { + // pointer contains target address + //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86::kPointerDiff: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + LittleEndian::set32(*fixUp, (uint32_t)displacement); + break; + case x86::kPointerDiff16: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) + throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); + LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); + break; + case x86::kDtraceProbeSite: + // change call site to a NOP + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x90; // 1-byte nop + dtraceProbeSite[0] = 0x0F; // 4-byte nop + dtraceProbeSite[1] = 0x1F; + dtraceProbeSite[2] = 0x40; + dtraceProbeSite[3] = 0x00; + break; + case x86::kDtraceIsEnabledSite: + // change call site to a clear eax + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x33; // xorl eax,eax + dtraceProbeSite[0] = 0xC0; + dtraceProbeSite[1] = 0x90; // 1-byte nop + dtraceProbeSite[2] = 0x90; // 1-byte nop + dtraceProbeSite[3] = 0x90; // 1-byte nop + break; + case x86::kPCRel32WeakImport: + case x86::kPCRel32: + case x86::kPCRel16: + case x86::kPCRel8: + displacement = 0; + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throw "codegen problem, can't use rel32 to external symbol"; + case ObjectFile::Atom::kTentativeDefinition: + displacement = 0; + break; + case ObjectFile::Atom::kAbsoluteSymbol: + displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + } + if ( kind == x86::kPCRel8 ) { + if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel8 out of range in %s", inAtom->getDisplayName()); + } + *(int8_t*)fixUp = (int8_t)displacement; + } + else if ( kind == x86::kPCRel16 ) { + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel16 out of range in %s", inAtom->getDisplayName()); + } + LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); + } + else { + if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel32 out of range in %s", inAtom->getDisplayName()); + } + LittleEndian::set32(*fixUp, (int32_t)displacement); + } + break; + case x86::kAbsolute32: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); + break; + } + break; + case x86::kDtraceTypeReference: + case x86::kDtraceProbe: + // nothing to fix up + break; + } +} + + + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t kTwoGigLimit = 0x7FFFFFFF; + const int64_t kSixtyFourKiloLimit = 0x7FFF; + const int64_t kOneTwentyEightLimit = 0x7F; + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + bool isExtern = this->makesExternalRelocatableReference(ref->getTarget()); + int64_t displacement; + x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + case x86::kGroupSubordinate: + // do nothing + break; + case x86::kPointer: + case x86::kPointerWeakImport: + case x86::kAbsolute32: + { + if ( isExtern ) { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + else if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero + if ( this->indirectSymbolIsLocal(ref) ) + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + else + LittleEndian::set32(*fixUp, 0); + } + else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { + // internal relocation => pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + else { + // internal relocation to tentative ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + break; + case x86::kPointerDiff: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + LittleEndian::set32(*fixUp, (uint32_t)displacement); + break; + case x86::kPointerDiff16: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) + throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); + LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); + break; + case x86::kPCRel8: + case x86::kPCRel16: + case x86::kPCRel32: + case x86::kPCRel32WeakImport: + case x86::kDtraceProbeSite: + case x86::kDtraceIsEnabledSite: + { + if ( isExtern ) + displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + else + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + if ( kind == x86::kPCRel8 ) { + displacement += 3; + if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName()); + } + int8_t byte = (int8_t)displacement; + *((int8_t*)fixUp) = byte; + } + else if ( kind == x86::kPCRel16 ) { + displacement += 2; + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel16 out of range in %s", inAtom->getDisplayName()); + } + int16_t word = (int16_t)displacement; + LittleEndian::set16(*((uint16_t*)fixUp), word); + } + else { + if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { + //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement, + // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + throwf("rel32 out of range in %s", inAtom->getDisplayName()); + } + LittleEndian::set32(*fixUp, (int32_t)displacement); + } + } + break; + case x86::kDtraceProbe: + case x86::kDtraceTypeReference: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; + uint8_t* dtraceProbeSite; + int64_t displacement = 0; + switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + case x86_64::kGroupSubordinate: + // do nothing + break; + case x86_64::kPointerWeakImport: + case x86_64::kPointer: + { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { + // external relocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation + // pointer contains target address + //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86_64::kPointerDiff32: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) + throw "32-bit pointer difference out of range"; + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); + break; + case x86_64::kPointerDiff: + LittleEndian::set64(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + // if GOT entry was optimized away, change movq instruction to a leaq + if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) { + //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName()); + uint8_t* opcodes = (uint8_t*)fixUp; + if ( opcodes[-2] != 0x8B ) + throw "GOT load reloc does not point to a movq instruction"; + opcodes[-2] = 0x8D; + } + // fall into general rel32 case + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel8: + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + displacement = (ref->getTarget().getSectionOffset() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throw "codegen problem, can't use rel32 to external symbol"; + break; + } + switch ( ref->getKind() ) { + case x86_64::kPCRel32_1: + displacement -= 1; + break; + case x86_64::kPCRel32_2: + displacement -= 2; + break; + case x86_64::kPCRel32_4: + displacement -= 4; + break; + case x86_64::kBranchPCRel8: + displacement += 3; + break; + } + if ( ref->getKind() == x86_64::kBranchPCRel8 ) { + if ( (displacement > 127) || (displacement < (-128)) ) { + fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n", + inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); + throw "rel8 out of range"; + } + *((int8_t*)fixUp) = (int8_t)displacement; + } + else { + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + fprintf(stderr, "call out of range from %s (%llX) in %s to %s (%llX) in %s\n", + inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + } + break; + case x86_64::kDtraceProbeSite: + // change call site to a NOP + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x90; // 1-byte nop + dtraceProbeSite[0] = 0x0F; // 4-byte nop + dtraceProbeSite[1] = 0x1F; + dtraceProbeSite[2] = 0x40; + dtraceProbeSite[3] = 0x00; + break; + case x86_64::kDtraceIsEnabledSite: + // change call site to a clear eax + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x48; // xorq eax,eax + dtraceProbeSite[0] = 0x33; + dtraceProbeSite[1] = 0xC0; + dtraceProbeSite[2] = 0x90; // 1-byte nop + dtraceProbeSite[3] = 0x90; // 1-byte nop + break; + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + bool external = this->makesExternalRelocatableReference(ref->getTarget()); + uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; + int64_t displacement = 0; + int32_t temp32; + switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + case x86_64::kGroupSubordinate: + // do nothing + break; + case x86_64::kPointer: + case x86_64::kPointerWeakImport: + { + if ( external ) { + // external relocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation ==> pointer contains target address + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86_64::kPointerDiff32: + // addend in content + LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset() - ref->getFromTargetOffset() ); + break; + case x86_64::kPointerDiff: + // addend in content + LittleEndian::set64(*fixUp, ref->getTargetOffset() - ref->getFromTargetOffset() ); + break; + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kDtraceProbeSite: + case x86_64::kDtraceIsEnabledSite: + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had + temp32 = ref->getTargetOffset(); + if ( external ) { + // extern relocation contains addend + displacement = temp32; + } + else { + // internal relocations contain delta to target address + displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + } + switch ( ref->getKind() ) { + case x86_64::kPCRel32_1: + displacement -= 1; + break; + case x86_64::kPCRel32_2: + displacement -= 2; + break; + case x86_64::kPCRel32_4: + displacement -= 4; + break; + } + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + break; + case x86_64::kBranchPCRel8: + // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had + temp32 = ref->getTargetOffset(); + if ( external ) { + // extern relocation contains addend + displacement = temp32; + } + else { + // internal relocations contain delta to target address + displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 1); + } + if ( (displacement > 127) || (displacement < (-128)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel8 out of range"; + } + *((int8_t*)fixUp) = (int8_t)displacement; + break; + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoadWeakImport: + // contains addend (usually zero) + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); + break; + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +// +// ppc and ppc64 are mostly the same, so they share a template specialzation +// +template +void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const +{ + uint32_t instruction; + uint32_t newInstruction; + int64_t displacement; + uint64_t targetAddr = 0; + uint64_t picBaseAddr; + uint16_t instructionLowHalf; + uint16_t instructionHighHalf; + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()]; + bool relocateableExternal = false; + const int64_t picbase_twoGigLimit = 0x80000000; + + if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { + targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + if ( finalLinkedImage ) + relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); + else + relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); + } + + switch ( (typename A::ReferenceKinds)(ref->getKind()) ) { + case A::kNoFixUp: + case A::kFollowOn: + case A::kGroupSubordinate: + // do nothing + break; + case A::kPointerWeakImport: + case A::kPointer: + { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers + || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound lazy pointer to another dylib ==> pointer contains zero + P::setP(*fixUpPointer, 0); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + // prebound lazy pointer to withing this dylib ==> pointer contains address + P::setP(*fixUpPointer, targetAddr); + break; + } + } + else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero + if ( this->indirectSymbolIsLocal(ref) ) + P::setP(*fixUpPointer, targetAddr); + else + P::setP(*fixUpPointer, 0); + } + else if ( relocateableExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + P::setP(*fixUpPointer, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // external relocation ==> pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + } + } + else { + // internal relocation + if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { + // pointer contains target address + //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr); + P::setP(*fixUpPointer, targetAddr); + } + else { + // pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + } + } + } + break; + case A::kPointerDiff64: + P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kPointerDiff32: + P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kPointerDiff16: + P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kDtraceProbeSite: + if ( finalLinkedImage ) { + // change call site to a NOP + BigEndian::set32(*fixUp, 0x60000000); + } + else { + // set bl instuction to branch to address zero in .o file + int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kDtraceIsEnabledSite: + if ( finalLinkedImage ) { + // change call site to a li r3,0 + BigEndian::set32(*fixUp, 0x38600000); + } + else { + // set bl instuction to branch to address zero in .o file + int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kBranch24WeakImport: + case A::kBranch24: + { + //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); + int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + const int64_t bl_eightMegLimit = 0x00FFFFFF; + if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s", + displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath()); + } + } + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kBranch14: + { + int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; + if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + + //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC); + //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kPICBaseLow16: + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + instructionLowHalf = (displacement & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kPICBaseLow14: + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + if ( (displacement & 0x3) != 0 ) + throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement); + instructionLowHalf = (displacement & 0xFFFC); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kPICBaseHigh16: + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + instructionLowHalf = displacement >> 16; + if ( (displacement & 0x00008000) != 0 ) + ++instructionLowHalf; + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsLow16: + if ( relocateableExternal && !finalLinkedImage ) + targetAddr -= ref->getTarget().getAddress(); + instructionLowHalf = (targetAddr & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsLow14: + if ( relocateableExternal && !finalLinkedImage ) + targetAddr -= ref->getTarget().getAddress(); + if ( (targetAddr & 0x3) != 0 ) + throw "bad address for absolute lo14 instruction fix-up"; + instructionLowHalf = (targetAddr & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsHigh16: + if ( relocateableExternal ) { + if ( finalLinkedImage ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // use target address + break; + case ObjectFile::Atom::kAbsoluteSymbol: + targetAddr = ref->getTarget().getSectionOffset(); + break; + } + } + else { + targetAddr -= ref->getTarget().getAddress(); + } + } + instructionHighHalf = (targetAddr >> 16); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsHigh16AddLow: + if ( relocateableExternal ) { + if ( finalLinkedImage ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // use target address + break; + case ObjectFile::Atom::kAbsoluteSymbol: + targetAddr = ref->getTarget().getSectionOffset(); + break; + } + } + else { + targetAddr -= ref->getTarget().getAddress(); + } + } + if ( targetAddr & 0x00008000 ) + targetAddr += 0x00010000; + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16); + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kDtraceTypeReference: + case A::kDtraceProbe: + // nothing to fix up + break; + } +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (ppc::ReferenceKinds)kind ) { + case ppc::kNoFixUp: + case ppc::kFollowOn: + case ppc::kGroupSubordinate: + case ppc::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPointerDiff16: + case ppc::kPointerDiff32: + case ppc::kPointerDiff64: + case ppc::kDtraceProbe: + case ppc::kDtraceProbeSite: + case ppc::kDtraceIsEnabledSite: + case ppc::kDtraceTypeReference: + // these are never used to call external functions + return false; + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + case ppc::kBranch14: + // these are used to call external functions + return true; + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + case ppc::kPICBaseHigh16: + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + // these are only used to call external functions + // in -mlong-branch stubs + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // if the .o file this atom came from has long-branch stubs, + // then assume these instructions in a stub. + // Otherwise, these are a direct reference to something (maybe a runtime text reloc) + return ( inAtom->getFile()->hasLongBranchStubs() ); + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + break; + } + return false; +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (arm::ReferenceKinds)kind ) { + case arm::kBranch24: + case arm::kBranch24WeakImport: + case arm::kThumbBranch22: + case arm::kThumbBranch22WeakImport: + return true; + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + case arm::kPointer: + case arm::kReadOnlyPointer: + case arm::kPointerWeakImport: + case arm::kPointerDiff: + case arm::kDtraceProbe: + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + case arm::kDtraceTypeReference: + return false; + } + return false; +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (ppc64::ReferenceKinds)kind ) { + case ppc::kNoFixUp: + case ppc::kFollowOn: + case ppc::kGroupSubordinate: + case ppc::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPointerDiff16: + case ppc::kPointerDiff32: + case ppc::kPointerDiff64: + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + case ppc::kPICBaseHigh16: + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + case ppc::kDtraceProbe: + case ppc::kDtraceProbeSite: + case ppc::kDtraceIsEnabledSite: + case ppc::kDtraceTypeReference: + // these are never used to call external functions + return false; + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + case ppc::kBranch14: + // these are used to call external functions + return true; + } + return false; +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport); +} + + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPointerWeakImport: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport || + kind == arm::kPointerWeakImport); +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity +template bool Writer::needsModuleTable() {return fOptions.needsModuleTable(); } +template <> bool Writer::needsModuleTable() { return false; } +template <> bool Writer::needsModuleTable() { return false; } + + +template +void Writer::optimizeDylibReferences() +{ + //fprintf(stderr, "original ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); + //} + // find unused dylibs that can be removed + std::map ordinalToReader; + std::map readerAliases; + for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + ObjectFile::Reader* reader = it->first; + std::map::iterator aliasPos = fLibraryAliases.find(reader); + if ( aliasPos != fLibraryAliases.end() ) { + // already noticed that this reader has same install name as another reader + readerAliases[reader] = aliasPos->second; + } + else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || fOptions.deadStripDylibs()) ) { + // this reader can be optimized away + it->second = 0xFFFFFFFF; + typename std::map* >::iterator pos = fLibraryToLoadCommand.find(reader); + if ( pos != fLibraryToLoadCommand.end() ) + pos->second->optimizeAway(); + } + else { + // mark this reader as using it ordinal + std::map::iterator pos = ordinalToReader.find(it->second); + if ( pos == ordinalToReader.end() ) + ordinalToReader[it->second] = reader; + else + readerAliases[reader] = pos->second; + } + } + // renumber ordinals (depends on iterator walking in ordinal order) + // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals + uint32_t newOrdinal = 0; + for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first <= fLibraryToOrdinal.size() ) { + if ( ! it->second->isLazyLoadedDylib() ) + fLibraryToOrdinal[it->second] = ++newOrdinal; + } + } + for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first <= fLibraryToOrdinal.size() ) { + if ( it->second->isLazyLoadedDylib() ) { + fLibraryToOrdinal[it->second] = ++newOrdinal; + } + } + } + + // linker does not error when dylib ordinal exceeds 250 + if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) ) + throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal); + + // add aliases (e.g. -lm points to libSystem.dylib) + for (std::map::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) { + fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second]; + } + + //fprintf(stderr, "new ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); + //} +} + + +template <> +void Writer::scanForAbsoluteReferences() +{ + // arm codegen never has absolute references. FIXME: Is this correct? +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // x86_64 codegen never has absolute references +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used + if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch (ref->getKind()) { + case x86::kAbsolute32: + throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); + return; + } + } + } + } +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used + if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch (ref->getKind()) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); + return; + } + } + } + } +} + + +// for ppc64 look for any -mdynamic-no-pic codegen +template <> +void Writer::scanForAbsoluteReferences() +{ + // only do this for main executable + if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch (ref->getKind()) { + case ppc64::kAbsLow16: + case ppc64::kAbsLow14: + case ppc64::kAbsHigh16: + case ppc64::kAbsHigh16AddLow: + //fprintf(stderr, "found -mdynamic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + // shrink page-zero and add pad segment to compensate + fPadSegmentInfo = new SegmentInfo(); + strcpy(fPadSegmentInfo->fName, "__4GBFILL"); + fPageZeroAtom->setSize(0x1000); + return; + } + } + } + } +} + + +template +void Writer::insertDummyStubs() +{ + // only needed for x86 +} + +template <> +void Writer::insertDummyStubs() +{ + // any 5-byte stubs that cross a 32-byte cache line may update incorrectly + std::vector*> betterStubs; + for (std::vector*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) { + switch (betterStubs.size() % 64 ) { + case 12:// stub would occupy 0x3C->0x41 + case 25:// stub would occupy 0x7D->0x82 + case 38:// stub would occupy 0xBE->0xC3 + case 51:// stub would occupy 0xFF->0x04 + betterStubs.push_back(new StubAtom(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub + break; + } + betterStubs.push_back(*it); + } + // replace + fAllSynthesizedStubs.clear(); + fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end()); +} + +template +void Writer::synthesizeStubs() +{ + switch ( fOptions.outputKind() ) { + case Options::kObjectFile: + // these output kinds never have stubs + return; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDynamicExecutable: + // try to synthesize stubs for these + break; + } + + // walk every atom and reference + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getTargetBinding()) { + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + break; + case ObjectFile::Reference::kBoundByName: + case ObjectFile::Reference::kBoundDirectly: + ObjectFile::Atom& target = ref->getTarget(); + // build map of which symbols need weak importing + if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + bool weakImport = this->weakImportReferenceKind(ref->getKind()); + // Obj-C Symbols in Leopard Can't Be Weak Linked + // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols + // in dylibs that are weakly linked. + if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) { + typename std::map* >::iterator pos; + pos = fLibraryToLoadCommand.find(target.getFile()); + if ( pos != fLibraryToLoadCommand.end() ) { + if ( pos->second->linkedWeak() ) + weakImport = true; + } + } + std::map::iterator pos = fWeakImportMap.find(&target); + if ( pos == fWeakImportMap.end() ) { + // target not in fWeakImportMap, so add + fWeakImportMap[&target] = weakImport; + } + else { + // target in fWeakImportMap, check for weakness mismatch + if ( pos->second != weakImport ) { + // found mismatch + switch ( fOptions.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", target.getName()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + // update if we use a weak_import or a strong import from this dylib + if ( fWeakImportMap[&target] ) + fDylibReadersWithWeakImports.insert(target.getFile()); + else + fDylibReadersWithNonWeakImports.insert(target.getFile()); + } + // create stubs as needed + if ( this->stubableReference(atom, ref) + && (ref->getTargetOffset() == 0) + && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { + ObjectFile::Atom* stub = NULL; + std::map::iterator pos = fStubsMap.find(&target); + if ( pos == fStubsMap.end() ) { + bool forLazyDylib = false; + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + case ObjectFile::Atom::kTentativeDefinition: + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + if ( target.getFile()->isLazyLoadedDylib() ) + forLazyDylib = true; + break; + } + stub = new StubAtom(*this, target, forLazyDylib); + fStubsMap[&target] = stub; + } + else { + stub = pos->second; + } + // alter reference to use stub instead + ref->setTarget(*stub, 0); + } + else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) { + throwf("illegal reference to %s in lazy loaded dylib from %s in %s", + target.getDisplayName(), atom->getDisplayName(), + atom->getFile()->getPath()); + } + // create GOT slots (non-lazy pointers) as needed + else if ( this->GOTReferenceKind(ref->getKind()) ) { + // + bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); + bool useGOT; + if ( fBiggerThanTwoGigs ) { + // in big images use GOT for all zero fill atoms + // this is just a heuristic and may need to be re-examined + useGOT = mustUseGOT || ref->getTarget().isZeroFill(); + } + else { + // < 2GB image so remove all GOT entries that we can + useGOT = mustUseGOT; + } + // if this GOT usage cannot be optimized away then make a GOT enry + if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) + useGOT = true; + if ( useGOT ) { + ObjectFile::Atom* nlp = NULL; + std::map::iterator pos = fGOTMap.find(&target); + if ( pos == fGOTMap.end() ) { + nlp = new NonLazyPointerAtom(*this, target); + fGOTMap[&target] = nlp; + } + else { + nlp = pos->second; + } + // alter reference to use non lazy pointer instead + ref->setTarget(*nlp, ref->getTargetOffset()); + } + } + } + } + } + + // sort stubs + std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); + + // add dummy fast stubs (x86 only) + if ( !fOptions.slowx86Stubs() ) + this->insertDummyStubs(); + + // sort lazy pointers + std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); + std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter()); + + + // add stubs to fAllAtoms + if ( fAllSynthesizedStubs.size() != 0 ) { + std::vector textStubs; + std::vector importStubs; + for (typename std::vector*>::iterator sit=fAllSynthesizedStubs.begin(); sit != fAllSynthesizedStubs.end(); ++sit) { + ObjectFile::Atom* stubAtom = *sit; + if ( strcmp(stubAtom->getSegment().getName(), "__TEXT") == 0 ) + textStubs.push_back(stubAtom); + else + importStubs.push_back(stubAtom); + } + // any helper stubs go right after regular stubs + if ( fAllSynthesizedStubHelpers.size() != 0 ) + textStubs.insert(textStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); + // insert text stubs right after __text section + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) { + // found end of __text section, insert stubs here + fAllAtoms->insert(it, textStubs.begin(), textStubs.end()); + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( importStubs.size() != 0 ) { + // insert __IMPORTS stubs right before __LINKEDIT + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + // for i386 where stubs are not in __TEXT segment + if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0)) + || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) { + // insert stubs at end of __IMPORT segment, or before __LINKEDIT + fAllAtoms->insert(it, importStubs.begin(), importStubs.end()); + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + } + } + + + // add lazy dylib pointers to fAllAtoms + if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert lazy pointers, __dyld section not found"; + } + } + + // add lazy pointers to fAllAtoms + if ( fAllSynthesizedLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert lazy pointers, __dyld section not found"; + } + } + + // add non-lazy pointers to fAllAtoms + if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) + && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0) + || ((strcmp(prevAtom->getSectionName(), "__data") == 0) && + ((fOptions.outputKind() == Options::kDyld) || (fOptions.outputKind() == Options::kStaticExecutable))) ) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert non-lazy pointers, __dyld section not found"; + } + } + + // build LC_SEGMENT_SPLIT_INFO content now that all atoms exist + if ( fSplitCodeToDataContentAtom != NULL ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getTargetBinding()) { + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + break; + case ObjectFile::Reference::kBoundByName: + case ObjectFile::Reference::kBoundDirectly: + if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) { + this->addCrossSegmentRef(atom, ref); + } + break; + } + } + } + } + +} + + +template +void Writer::partitionIntoSections() +{ + const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); + + // for every atom, set its sectionInfo object and section offset + // build up fSegmentInfos along the way + ObjectFile::Section* curSection = NULL; + SectionInfo* currentSectionInfo = NULL; + SegmentInfo* currentSegmentInfo = NULL; + SectionInfo* cstringSectionInfo = NULL; + unsigned int sectionIndex = 1; + fSegmentInfos.reserve(8); + for (unsigned int i=0; i < fAllAtoms->size(); ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) { + if ( oneSegmentCommand ) { + if ( currentSegmentInfo == NULL ) { + currentSegmentInfo = new SegmentInfo(); + currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) ) + cstringSectionInfo = currentSectionInfo; + } + else { + if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { + currentSegmentInfo = new SegmentInfo(); + strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); + uint32_t initprot = 0; + if ( atom->getSegment().isContentReadable() ) + initprot |= VM_PROT_READ; + if ( atom->getSegment().isContentWritable() ) + initprot |= VM_PROT_WRITE; + if ( atom->getSegment().isContentExecutable() ) + initprot |= VM_PROT_EXECUTE; + if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) ) + initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker + currentSegmentInfo->fInitProtection = initprot; + if ( initprot == 0 ) + currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 + else if ( fOptions.architecture() == CPU_TYPE_ARM ) + currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init + else + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + std::vector& customSegProtections = fOptions.customSegmentProtections(); + for(std::vector::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) { + if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) { + currentSegmentInfo->fInitProtection = it->init; + currentSegmentInfo->fMaxProtection = it->max; + } + } + currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); + currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); + if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) ) + currentSegmentInfo->fIndependentAddress = true; + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; + // check for -sectalign override + std::vector& alignmentOverrides = fOptions.sectionAlignments(); + for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { + if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) + currentSectionInfo->fAlignment = it->alignment; + } + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { + fLoadCommandsSection = currentSectionInfo; + fLoadCommandsSegment = currentSegmentInfo; + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__ld_symbol_ptr") == 0) ) + currentSectionInfo->fAllLazyDylibPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (fOptions.outputKind() == Options::kDyld) && (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub4") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub4") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { + currentSectionInfo->fAllSelfModifyingStubs = true; + currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) ) + currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned + curSection = atom->getSection(); + if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers + || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) { + fSymbolTableCommands->needDynamicTable(); + } + } + // any non-zero fill atoms make whole section marked not-zero-fill + if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) + currentSectionInfo->fAllZeroFill = false; + // change section object to be Writer's SectionInfo object + atom->setSection(currentSectionInfo); + // section alignment is that of a contained atom with the greatest alignment + uint8_t atomAlign = atom->getAlignment().powerOf2; + if ( currentSectionInfo->fAlignment < atomAlign ) + currentSectionInfo->fAlignment = atomAlign; + // calculate section offset for this atom + uint64_t offset = currentSectionInfo->fSize; + uint64_t alignment = 1 << atomAlign; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->getAlignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + atom->setSectionOffset(offset); + uint64_t curAtomSize = atom->getSize(); + currentSectionInfo->fSize = offset + curAtomSize; + // add atom to section vector + currentSectionInfo->fAtoms.push_back(atom); + // update largest size + if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) + fLargestAtomSize = curAtomSize; + } + if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) { + // when merging cstring sections in .o files, all strings need to use the max alignment + uint64_t offset = 0; + uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment; + for (std::vector::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) { + offset = (offset + (cstringAlignment-1)) & (-cstringAlignment); + ObjectFile::Atom* atom = *it; + atom->setSectionOffset(offset); + offset += atom->getSize(); + } + cstringSectionInfo->fSize = offset; + } +} + + +struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; +class TargetAndOffsetComparor +{ +public: + bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const + { + if ( left.atom != right.atom ) + return ( left.atom < right.atom ); + return ( left.offset < right.offset ); + } +}; + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G address space, so no need for branch islands + return false; +} + +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G size of largest image + return false; +} + +template <> +bool Writer::addBranchIslands() +{ + // arm branch islands not (yet) supported + // you can instead compile with -mlong-call + return false; +} + +template <> +bool Writer::isBranch24Reference(uint8_t kind) +{ + switch (kind) { + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + return true; + } + return false; +} + +template <> +bool Writer::isBranch24Reference(uint8_t kind) +{ + switch (kind) { + case ppc64::kBranch24: + case ppc64::kBranch24WeakImport: + return true; + } + return false; +} + +// +// PowerPC can do PC relative branches as far as +/-16MB. +// If a branch target is >16MB then we insert one or more +// "branch islands" between the branch and its target that +// allows island hoping to the target. +// +// Branch Island Algorithm +// +// If the __TEXT segment < 16MB, then no branch islands needed +// Otherwise, every 15MB into the __TEXT segment is region is +// added which can contain branch islands. Every out of range +// bl instruction is checked. If it crosses a region, an island +// is added to that region with the same target and the bl is +// adjusted to target the island instead. +// +// In theory, if too many islands are added to one region, it +// could grow the __TEXT enough that other previously in-range +// bl branches could be pushed out of range. We reduce the +// probability this could happen by placing the ranges every +// 15MB which means the region would have to be 1MB (256K islands) +// before any branches could be pushed out of range. +// +template +bool Writer::addPPCBranchIslands() +{ + bool log = false; + bool result = false; + // Can only possibly need branch islands if __TEXT segment > 16M + if ( fLoadCommandsSegment->fSize > 16000000 ) { + if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); + const uint32_t kBetweenRegions = 15*1024*1024; // place regions of islands every 15MB in __text section + SectionInfo* textSection = NULL; + for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { + if ( strcmp((*it)->fSectionName, "__text") == 0 ) { + textSection = *it; + if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize); + break; + } + } + const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions; + typedef std::map AtomToIsland; + AtomToIsland regionsMap[kIslandRegionsCount]; + std::vector regionsIslands[kIslandRegionsCount]; + unsigned int islandCount = 0; + if ( log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + + // create islands for branch references that are out of range + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( this->isBranch24Reference(ref->getKind()) ) { + ObjectFile::Atom& target = ref->getTarget(); + int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); + int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); + int64_t displacement = dstAddr - srcAddr; + TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; + const int64_t kFifteenMegLimit = kBetweenRegions; + if ( displacement > kFifteenMegLimit ) { + // create forward branch chain + ObjectFile::Atom* nextTarget = ⌖ + uint64_t nextTargetOffset = ref->getTargetOffset(); + for (int i=kIslandRegionsCount-1; i >=0 ; --i) { + AtomToIsland* region = ®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress(); + if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset); + island->setSection(textSection); + (*region)[finalTargetAndOffset] = island; + if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); + regionsIslands[i].push_back(island); + ++islandCount; + nextTarget = island; + nextTargetOffset = 0; + } + else { + nextTarget = pos->second; + nextTargetOffset = 0; + } + } + } + if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName()); + ref->setTarget(*nextTarget, nextTargetOffset); + } + else if ( displacement < (-kFifteenMegLimit) ) { + // create back branching chain + ObjectFile::Atom* prevTarget = ⌖ + uint64_t prevTargetOffset = ref->getTargetOffset(); + for (int i=0; i < kIslandRegionsCount ; ++i) { + AtomToIsland* region = ®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset); + island->setSection(textSection); + (*region)[finalTargetAndOffset] = island; + if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); + regionsIslands[i].push_back(island); + ++islandCount; + prevTarget = island; + prevTargetOffset = 0; + } + else { + prevTarget = pos->second; + prevTargetOffset = 0; + } + } + } + if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); + ref->setTarget(*prevTarget, prevTargetOffset); + } + } + } + } + + // insert islands into __text section and adjust section offsets + if ( islandCount > 0 ) { + if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); + std::vector newAtomList; + newAtomList.reserve(textSection->fAtoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress(); + uint64_t textSectionAlignment = (1 << textSection->fAlignment); + int regionIndex = 0; + uint64_t atomSlide = 0; + uint64_t sectionOffset = 0; + for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getAddress() > islandRegionAddr ) { + uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide; + sectionOffset = islandStartOffset; + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + ++regionIndex; + islandRegionAddr += kBetweenRegions; + uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment; + atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment); + } + newAtomList.push_back(atom); + if ( atomSlide != 0 ) + atom->setSectionOffset(atom->getSectionOffset()+atomSlide); + } + sectionOffset = textSection->fSize+atomSlide; + // put any remaining islands at end of __text section + if ( regionIndex < kIslandRegionsCount ) { + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + } + + textSection->fAtoms = newAtomList; + textSection->fSize = sectionOffset; + result = true; + } + + } + return result; +} + + +template +void Writer::adjustLoadCommandsAndPadding() +{ + fSegmentCommands->computeSize(); + + // recompute load command section offsets + uint64_t offset = 0; + std::vector& loadCommandAtoms = fLoadCommandsSection->fAtoms; + const unsigned int atomCount = loadCommandAtoms.size(); + for (unsigned int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = loadCommandAtoms[i]; + uint64_t alignment = 1 << atom->getAlignment().powerOf2; + offset = ( (offset+alignment-1) & (-alignment) ); + atom->setSectionOffset(offset); + uint32_t atomSize = atom->getSize(); + if ( atomSize > fLargestAtomSize ) + fLargestAtomSize = atomSize; + offset += atomSize; + fLoadCommandsSection->fSize = offset; + } + + std::vector& sectionInfos = fLoadCommandsSegment->fSections; + const int sectionCount = sectionInfos.size(); + uint32_t totalSizeOfHeaderAndLoadCommands = 0; + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + totalSizeOfHeaderAndLoadCommands += curSection->fSize; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + break; + } + uint64_t paddingSize = 0; + if ( fOptions.outputKind() == Options::kDyld ) { + // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address + paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); + } + else if ( fOptions.outputKind() == Options::kObjectFile ) { + // mach-o .o files need no padding between load commands and first section + paddingSize = 0; + } + else if ( fOptions.makeEncryptable() ) { + // want load commands to end on a page boundary, so __text starts on page boundary + paddingSize = 4096 - ((totalSizeOfHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); + fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfHeaderAndLoadCommands+paddingSize); + } + else { + // work backwards from end of segment and lay out sections so that extra room goes to padding atom + uint64_t addr = 0; + for(int j=sectionCount-1; j >=0; --j) { + SectionInfo* curSection = sectionInfos[j]; + addr -= curSection->fSize; + addr = addr & (0 - (1 << curSection->fAlignment)); + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { + addr -= totalSizeOfHeaderAndLoadCommands; + paddingSize = addr % 4096; + break; + } + } + + // if command line requires more padding than this + uint32_t minPad = fOptions.minimumHeaderPad(); + if ( fOptions.maxMminimumHeaderPad() ) { + // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes + uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN; + if ( fOptions.outputKind() == Options::kDynamicLibrary ) + altMin += MAXPATHLEN; + if ( altMin > minPad ) + minPad = altMin; + } + if ( paddingSize < minPad ) { + int extraPages = (minPad - paddingSize + 4095)/4096; + paddingSize += extraPages * 4096; + } + } + + // adjust atom size and update section size + fHeaderPadding->setSize(paddingSize); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + curSection->fSize = paddingSize; + } +} + +// assign file offsets and logical address to all segments +template +void Writer::assignFileOffsets() +{ + bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile); + bool haveFixedSegments = false; + uint64_t fileOffset = 0; + uint64_t nextContiguousAddress = fOptions.baseAddress(); + uint64_t nextReadOnlyAddress = fOptions.baseAddress(); + uint64_t nextWritableAddress = fOptions.baseWritableAddress(); + + // process segments with fixed addresses (-segaddr) + for (std::vector::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( strcmp(curSegment->fName, it->name) == 0 ) { + curSegment->fBaseAddress = it->address; + curSegment->fFixedAddress = true; + break; + } + } + } + + // Run through the segments and each segment's sections to assign addresses + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + + if ( fOptions.splitSeg() ) { + if ( curSegment->fInitProtection & VM_PROT_WRITE ) + nextContiguousAddress = nextWritableAddress; + else + nextContiguousAddress = nextReadOnlyAddress; + } + + fileOffset = (fileOffset+4095) & (-4096); + curSegment->fFileOffset = fileOffset; + + // Set the segment base address + if ( curSegment->fFixedAddress ) + haveFixedSegments = true; + else + curSegment->fBaseAddress = nextContiguousAddress; + + // We've set the segment address, now run through each section. + uint64_t address = curSegment->fBaseAddress; + SectionInfo* firstZeroFillSection = NULL; + SectionInfo* prevSection = NULL; + + std::vector& sectionInfos = curSegment->fSections; + + for (std::vector::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) { + SectionInfo* curSection = *it; + + // adjust section address based on alignment + uint64_t alignment = 1 << curSection->fAlignment; + address = ( (address+alignment-1) & (-alignment) ); + + // adjust file offset to match address + if ( prevSection != NULL ) { + if ( finalLinkedImage || !prevSection->fVirtualSection ) + fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; + else + fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); + } + + // update section info + curSection->fFileOffset = fileOffset; + curSection->setBaseAddress(address); + //fprintf(stderr, "%s %s %llX\n", curSegment->fName, curSection->fSectionName, address); + + // keep track of trailing zero fill sections + if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) + firstZeroFillSection = curSection; + if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage ) + throwf("zero-fill section %s not at end of segment", curSection->fSectionName); + + // update running pointers + if ( finalLinkedImage || !curSection->fVirtualSection ) + address += curSection->fSize; + fileOffset += curSection->fSize; + + // sanity check size of 32-bit binaries + if ( address > maxAddress() ) + throwf("section %s exceeds 4GB limit", curSection->fSectionName); + + // update segment info + curSegment->fFileSize = fileOffset - curSegment->fFileOffset; + curSegment->fSize = curSegment->fFileSize; + prevSection = curSection; + } + + if ( fOptions.outputKind() == Options::kObjectFile ) { + // don't page align .o files + } + else { + // optimize trailing zero-fill sections to not occupy disk space + if ( firstZeroFillSection != NULL ) { + curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset; + fileOffset = firstZeroFillSection->fFileOffset; + } + // page align segment size + curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); + curSegment->fSize = (curSegment->fSize+4095) & (-4096); + if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { + nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); + if ( curSegment->fInitProtection & VM_PROT_WRITE ) + nextWritableAddress = nextContiguousAddress; + else + nextReadOnlyAddress = nextContiguousAddress; + } + } + } + + // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) + if ( haveFixedSegments ) { + int segCount = fSegmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* segment1 = fSegmentInfos[i]; + + for(int j=0; j < segCount; ++j) { + if ( i != j ) { + SegmentInfo* segment2 = fSegmentInfos[j]; + + if ( segment1->fBaseAddress < segment2->fBaseAddress ) { + if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { + if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) { + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + } + } + } + } + + // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) { + if ( fFirstWritableSegment == NULL ) + fFirstWritableSegment = curSegment; + if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL ) + fWritableSegmentPastFirst4GB = true; + } + } + + // record size of encrypted part of __TEXT segment + if ( fOptions.makeEncryptable() ) { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( strcmp(curSegment->fName, "__TEXT") == 0 ) { + fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize); + break; + } + } + } + +} + +template +void Writer::adjustLinkEditSections() +{ + // link edit content is always in last segment + SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; + unsigned int firstLinkEditSectionIndex = 0; + while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) + ++firstLinkEditSectionIndex; + + const unsigned int linkEditSectionCount = lastSeg->fSections.size(); + uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; + uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); + if ( fPadSegmentInfo != NULL ) { + // insert __4GBFILL segment into segments vector before LINKEDIT + for(std::vector::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) { + if ( *it == lastSeg ) { + fSegmentInfos.insert(it, fPadSegmentInfo); + break; + } + } + // adjust __4GBFILL segment to span from end of last segment to zeroPageSize + fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address; + fPadSegmentInfo->fBaseAddress = address; + // adjust LINKEDIT to start at zeroPageSize + address = fOptions.zeroPageSize(); + lastSeg->fBaseAddress = fOptions.zeroPageSize(); + } + for (unsigned int i=firstLinkEditSectionIndex; i < linkEditSectionCount; ++i) { + std::vector& atoms = lastSeg->fSections[i]->fAtoms; + // adjust section address based on alignment + uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment; + uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address; + address += pad; + fileOffset += pad; // adjust file offset to match address + lastSeg->fSections[i]->setBaseAddress(address); + if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 ) + lastSeg->fSections[i]->setBaseAddress(0); + lastSeg->fSections[i]->fFileOffset = fileOffset; + uint64_t sectionOffset = 0; + for (unsigned int j=0; j < atoms.size(); ++j) { + ObjectFile::Atom* atom = atoms[j]; + uint64_t alignment = 1 << atom->getAlignment().powerOf2; + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + atom->setSectionOffset(sectionOffset); + uint64_t size = atom->getSize(); + sectionOffset += size; + if ( size > fLargestAtomSize ) + fLargestAtomSize = size; + } + //fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset); + lastSeg->fSections[i]->fSize = sectionOffset; + fileOffset += sectionOffset; + address += sectionOffset; + } + if ( fOptions.outputKind() == Options::kObjectFile ) { + //lastSeg->fBaseAddress = 0; + //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> + //lastSeg->fFileOffset = 0; + //lastSeg->fFileSize = + } + else { + lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; + lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); + } +} + + +template +ObjectFile::Atom::Scope MachHeaderAtom::getScope() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return ObjectFile::Atom::scopeGlobal; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kObjectFile: + return ObjectFile::Atom::scopeLinkageUnit; + } + throw "unknown header type"; +} + +template +ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusion() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + return ObjectFile::Atom::kSymbolTableInAndNeverStrip; + case Options::kStaticExecutable: + return ObjectFile::Atom::kSymbolTableInAsAbsolute; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return ObjectFile::Atom::kSymbolTableIn; + case Options::kObjectFile: + return ObjectFile::Atom::kSymbolTableNotIn; + } + throw "unknown header type"; +} + +template +const char* MachHeaderAtom::getName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return "__mh_execute_header"; + case Options::kDynamicLibrary: + return "__mh_dylib_header"; + case Options::kDynamicBundle: + return "__mh_bundle_header"; + case Options::kObjectFile: + return NULL; + case Options::kDyld: + return "__mh_dylinker_header"; + } + throw "unknown header type"; +} + +template +const char* MachHeaderAtom::getDisplayName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return this->getName(); + case Options::kObjectFile: + return "mach header"; + } + throw "unknown header type"; +} + +template +void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const +{ + // get file type + uint32_t fileType = 0; + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fileType = MH_EXECUTE; + break; + case Options::kDynamicLibrary: + fileType = MH_DYLIB; + break; + case Options::kDynamicBundle: + fileType = MH_BUNDLE; + break; + case Options::kObjectFile: + fileType = MH_OBJECT; + break; + case Options::kDyld: + fileType = MH_DYLINKER; + break; + } + + // get flags + uint32_t flags = 0; + if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { + if ( fWriter.fCanScatter ) + flags = MH_SUBSECTIONS_VIA_SYMBOLS; + } + else { + if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { + flags |= MH_NOUNDEFS; + } + else { + flags = MH_DYLDLINK; + if ( fWriter.fOptions.bindAtLoad() ) + flags |= MH_BINDATLOAD; + switch ( fWriter.fOptions.nameSpace() ) { + case Options::kTwoLevelNameSpace: + flags |= MH_TWOLEVEL | MH_NOUNDEFS; + break; + case Options::kFlatNameSpace: + break; + case Options::kForceFlatNameSpace: + flags |= MH_FORCE_FLAT; + break; + } + if ( fWriter.fHasWeakExports ) + flags |= MH_WEAK_DEFINES; + if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) + flags |= MH_BINDS_TO_WEAK; + if ( fWriter.fOptions.prebind() ) + flags |= MH_PREBOUND; + if ( fWriter.fOptions.splitSeg() ) + flags |= MH_SPLIT_SEGS; + if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs ) + flags |= MH_NO_REEXPORTED_DYLIBS; + if ( fWriter.fOptions.positionIndependentExecutable() ) + flags |= MH_PIE; + } + if ( fWriter.fOptions.hasExecutableStack() ) + flags |= MH_ALLOW_STACK_EXECUTION; + if ( fWriter.fOptions.readerOptions().fRootSafe ) + flags |= MH_ROOT_SAFE; + if ( fWriter.fOptions.readerOptions().fSetuidSafe ) + flags |= MH_SETUID_SAFE; + } + + // get commands info + uint32_t commandsSize = 0; + uint32_t commandsCount = 0; + + std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; + for (std::vector::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + commandsSize += atom->getSize(); + // segment and symbol table atoms can contain more than one load command + if ( atom == fWriter.fSegmentCommands ) + commandsCount += fWriter.fSegmentCommands->commandCount(); + else if ( atom == fWriter.fSymbolTableCommands ) + commandsCount += fWriter.fSymbolTableCommands->commandCount(); + else if ( atom->getSize() != 0 ) + ++commandsCount; + } + + // fill out mach_header + macho_header* mh = (macho_header*)buffer; + setHeaderInfo(*mh); + mh->set_filetype(fileType); + mh->set_ncmds(commandsCount); + mh->set_sizeofcmds(commandsSize); + mh->set_flags(flags); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_POWERPC); + header.set_cpusubtype(fWriter.fCpuConstraint); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC_64); + header.set_cputype(CPU_TYPE_POWERPC64); + if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000); + else + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); + header.set_reserved(0); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_I386); + header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC_64); + header.set_cputype(CPU_TYPE_X86_64); + if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000); + else + header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); + header.set_reserved(0); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_ARM); + header.set_cpusubtype(fWriter.fCpuConstraint); +} + +template +CustomStackAtom::CustomStackAtom(Writer& writer) + : WriterAtom(writer, Segment::fgStackSegment) +{ + if ( stackGrowsDown() ) + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); + else + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); +} + + +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } + +template +void SegmentLoadCommandsAtom::computeSize() +{ + uint64_t size = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + size += sizeof(macho_segment_command

); + std::vector& sectionInfos = segmentInfos[i]->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) + size += sizeof(macho_section

); + } + } + fSize = size; + fCommandCount = segCount; + if ( fWriter.fPadSegmentInfo != NULL ) { + ++fCommandCount; + fSize += sizeof(macho_segment_command

); + } +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template +void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); + bzero(buffer, size); + uint8_t* p = buffer; + typename std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* segInfo = segmentInfos[i]; + const int sectionCount = segInfo->fSections.size(); + macho_segment_command

* cmd = (macho_segment_command

*)p; + cmd->set_cmd(macho_segment_command

::CMD); + cmd->set_segname(segInfo->fName); + cmd->set_vmaddr(segInfo->fBaseAddress); + cmd->set_vmsize(segInfo->fSize); + cmd->set_fileoff(segInfo->fFileOffset); + cmd->set_filesize(segInfo->fFileSize); + cmd->set_maxprot(segInfo->fMaxProtection); + cmd->set_initprot(segInfo->fInitProtection); + // add sections array + macho_section

* const sections = (macho_section

*)&p[sizeof(macho_segment_command

)]; + unsigned int sectionsEmitted = 0; + for (int j=0; j < sectionCount; ++j) { + SectionInfo* sectInfo = segInfo->fSections[j]; + if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { + macho_section

* sect = §ions[sectionsEmitted++]; + if ( oneSegment ) { + // .o file segment does not cover load commands, so recalc at first real section + if ( sectionsEmitted == 1 ) { + cmd->set_vmaddr(sectInfo->getBaseAddress()); + cmd->set_fileoff(sectInfo->fFileOffset); + } + cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); + cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); + } + sect->set_sectname(sectInfo->fSectionName); + sect->set_segname(sectInfo->fSegmentName); + sect->set_addr(sectInfo->getBaseAddress()); + sect->set_size(sectInfo->fSize); + sect->set_offset(sectInfo->fFileOffset); + sect->set_align(sectInfo->fAlignment); + if ( sectInfo->fRelocCount != 0 ) { + sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info

) + fWriter.fSectionRelocationsAtom->getFileOffset()); + sect->set_nreloc(sectInfo->fRelocCount); + } + if ( sectInfo->fAllZeroFill ) { + sect->set_flags(S_ZEROFILL); + sect->set_offset(0); + } + else if ( sectInfo->fAllLazyPointers ) { + sect->set_flags(S_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllLazyDylibPointers ) { + sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllNonLazyPointers ) { + sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllStubs ) { + sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + } + else if ( sectInfo->fAllSelfModifyingStubs ) { + sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_INIT_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_TERM_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); + } + else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_INTERPOSING); + } + else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_CSTRING_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_4BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_8BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_16BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } + else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_DTRACE_DOF); + } + else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_DTRACE_DOF); + } + else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + if ( sectInfo->fHasTextLocalRelocs ) + sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); + if ( sectInfo->fHasTextExternalRelocs ) + sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC); + } + } + } + p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; + cmd->set_cmdsize(sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)); + cmd->set_nsects(sectionsEmitted); + } +} + + +template +SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment) +{ + bzero(&fSymbolTable, sizeof(macho_symtab_command

)); + bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command

)); + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + fNeedsDynamicSymbolTable = true; + break; + case Options::kObjectFile: + case Options::kStaticExecutable: + fNeedsDynamicSymbolTable = false; + break; + } + writer.fSymbolTableCommands = this; +} + + + +template +void SymbolTableLoadCommandsAtom::needDynamicTable() +{ + fNeedsDynamicSymbolTable = true; +} + + +template +uint64_t SymbolTableLoadCommandsAtom::getSize() const +{ + if ( fNeedsDynamicSymbolTable ) + return this->alignedSize(sizeof(macho_symtab_command

) + sizeof(macho_dysymtab_command

)); + else + return this->alignedSize(sizeof(macho_symtab_command

)); +} + +template +void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + // build LC_DYSYMTAB command + macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)buffer; + bzero(symbolTableCmd, sizeof(macho_symtab_command

)); + symbolTableCmd->set_cmd(LC_SYMTAB); + symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); + symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); + symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset()); + symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset()); + symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); + + // build LC_DYSYMTAB command + if ( fNeedsDynamicSymbolTable ) { + macho_dysymtab_command

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)&buffer[sizeof(macho_symtab_command

)]; + bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command

)); + dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); + dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); + dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); + dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); + dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount); + dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex); + dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount); + if ( fWriter.fModuleInfoAtom != NULL ) { + dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); + dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); + dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); + dynamicSymbolTableCmd->set_nmodtab(1); + dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); + dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); + } + dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size()); + if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { + dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); + dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); + } + } +} + + +template +unsigned int SymbolTableLoadCommandsAtom::commandCount() +{ + return fNeedsDynamicSymbolTable ? 2 : 1; +} + +template +uint64_t DyldLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylinker_command

) + strlen("/usr/lib/dyld") + 1); +} + +template +void DyldLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_dylinker_command

* cmd = (macho_dylinker_command

*)buffer; + if ( fWriter.fOptions.outputKind() == Options::kDyld ) + cmd->set_cmd(LC_ID_DYLINKER); + else + cmd->set_cmd(LC_LOAD_DYLINKER); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strcpy((char*)&buffer[sizeof(macho_dylinker_command

)], "/usr/lib/dyld"); +} + +template +uint64_t AllowableClientLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_client_command

) + strlen(this->clientString) + 1); +} + +template +void AllowableClientLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + + bzero(buffer, size); + macho_sub_client_command

* cmd = (macho_sub_client_command

*)buffer; + cmd->set_cmd(LC_SUB_CLIENT); + cmd->set_cmdsize(size); + cmd->set_client_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_client_command

)], this->clientString); + +} + +template +uint64_t DylibLoadCommandsAtom::getSize() const +{ + if ( fOptimizedAway ) { + return 0; + } + else { + const char* path = fInfo.reader->getInstallPath(); + return this->alignedSize(sizeof(macho_dylib_command

) + strlen(path) + 1); + } +} + +template +void DylibLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( fOptimizedAway ) + return; + uint64_t size = this->getSize(); + bzero(buffer, size); + const char* path = fInfo.reader->getInstallPath(); + macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; + // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) + && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); + if ( fInfo.options.fLazyLoad ) + cmd->set_cmd(LC_LAZY_LOAD_DYLIB); + else if ( fInfo.options.fWeakImport || autoWeakLoadDylib ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); + else if ( fInfo.options.fReExport && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + cmd->set_cmd(LC_REEXPORT_DYLIB); + else + cmd->set_cmd(LC_LOAD_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses + cmd->set_current_version(fInfo.reader->getCurrentVersion()); + cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); + cmd->set_name_offset(); + strcpy((char*)&buffer[sizeof(macho_dylib_command

)], path); +} + + + +template +uint64_t DylibIDLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylib_command

) + strlen(fWriter.fOptions.installPath()) + 1); +} + +template +void DylibIDLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; + cmd->set_cmd(LC_ID_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses + cmd->set_current_version(fWriter.fOptions.currentVersion()); + cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); + strcpy((char*)&buffer[sizeof(macho_dylib_command

)], fWriter.fOptions.installPath()); +} + + +template +void RoutinesLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + if (fWriter.fEntryPoint->isThumb()) + initAddr |= 1ULL; + bzero(buffer, sizeof(macho_routines_command

)); + macho_routines_command

* cmd = (macho_routines_command

*)buffer; + cmd->set_cmd(macho_routines_command

::CMD); + cmd->set_cmdsize(this->getSize()); + cmd->set_init_address(initAddr); +} + + +template +uint64_t SubUmbrellaLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(fName) + 1); +} + +template +void SubUmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_umbrella_command

* cmd = (macho_sub_umbrella_command

*)buffer; + cmd->set_cmd(LC_SUB_UMBRELLA); + cmd->set_cmdsize(this->getSize()); + cmd->set_sub_umbrella_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command

)], fName); +} + +template +void UUIDLoadCommandAtom::generate() +{ + switch ( fWriter.fOptions.getUUIDMode() ) { + case Options::kUUIDNone: + fEmit = false; + break; + case Options::kUUIDRandom: + ::uuid_generate_random(fUUID); + fEmit = true; + break; + case Options::kUUIDContent: + bzero(fUUID, 16); + fEmit = true; + break; + } +} + +template +void UUIDLoadCommandAtom::setContent(const uint8_t uuid[16]) +{ + memcpy(fUUID, uuid, 16); +} + +template +void UUIDLoadCommandAtom::copyRawContent(uint8_t buffer[]) const +{ + if (fEmit) { + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_uuid_command

* cmd = (macho_uuid_command

*)buffer; + cmd->set_cmd(LC_UUID); + cmd->set_cmdsize(this->getSize()); + cmd->set_uuid((uint8_t*)fUUID); + } +} + + +template +uint64_t SubLibraryLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_library_command

) + fNameLength + 1); +} + +template +void SubLibraryLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_library_command

* cmd = (macho_sub_library_command

*)buffer; + cmd->set_cmd(LC_SUB_LIBRARY); + cmd->set_cmdsize(this->getSize()); + cmd->set_sub_library_offset(); + strncpy((char*)&buffer[sizeof(macho_sub_library_command

)], fNameStart, fNameLength); + buffer[sizeof(macho_sub_library_command

)+fNameLength] = '\0'; +} + +template +uint64_t UmbrellaLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_framework_command

) + strlen(fName) + 1); +} + +template +void UmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_framework_command

* cmd = (macho_sub_framework_command

*)buffer; + cmd->set_cmd(LC_SUB_FRAMEWORK); + cmd->set_cmdsize(this->getSize()); + cmd->set_umbrella_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_framework_command

)], fName); +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); +} + +// We should be picking it up from a header +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_thread_register(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 +} + + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_thread_register(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); // i386_THREAD_STATE + cmd->set_count(16); // i386_THREAD_STATE_COUNT; + cmd->set_thread_register(10, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(x86_THREAD_STATE64); + cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_thread_register(16, start); // rip + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); + cmd->set_count(17); + cmd->set_thread_register(15, start); // pc + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp? +} + +template +uint64_t RPathLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_rpath_command

) + strlen(fPath) + 1); +} + +template +void RPathLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_rpath_command

* cmd = (macho_rpath_command

*)buffer; + cmd->set_cmd(LC_RPATH); + cmd->set_cmdsize(this->getSize()); + cmd->set_path_offset(); + strcpy((char*)&buffer[sizeof(macho_rpath_command

)], fPath); +} + + + +template +void EncryptionLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)buffer; + cmd->set_cmd(LC_ENCRYPTION_INFO); + cmd->set_cmdsize(this->getSize()); + cmd->set_cryptoff(fStartOffset); + cmd->set_cryptsize(fEndOffset-fStartOffset); + cmd->set_cryptid(0); +} + + + +template +void LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, fSize); +} + +template +void LoadCommandsPaddingAtom::setSize(uint64_t newSize) +{ + fSize = newSize; + // this resizing by-passes the way fLargestAtomSize is set, so re-check here + if ( fWriter.fLargestAtomSize < newSize ) + fWriter.fLargestAtomSize = newSize; +} + +template +uint64_t LinkEditAtom::getFileOffset() const +{ + return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); +} + + +template +uint64_t SectionRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void SectionRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize()); +} + + +template +uint64_t LocalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void LocalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize()); +} + + + +template +uint64_t SymbolTableLinkEditAtom::getSize() const +{ + return fWriter.fSymbolTableCount * sizeof(macho_nlist

); +} + +template +void SymbolTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fWriter.fSymbolTable, this->getSize()); +} + +template +uint64_t ExternalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter

()); + memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); +} + + + +template +uint64_t IndirectTableLinkEditAtom::getSize() const +{ + return fTable.size() * sizeof(uint32_t); +} + +template +void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + const uint32_t indirectTableSize = fTable.size(); + uint32_t* indirectTable = (uint32_t*)buffer; + for(std::vector::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { + if ( it->indirectIndex < indirectTableSize ) { + A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); + } + else { + throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); + } + } +} + + + +template +uint64_t ModuleInfoLinkEditAtom::getSize() const +{ + return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

) + + sizeof(macho_dylib_module

) + + this->getReferencesCount()*sizeof(uint32_t); +} + +template +uint32_t ModuleInfoLinkEditAtom::getTableOfContentsFileOffset() const +{ + return this->getFileOffset(); +} + +template +uint32_t ModuleInfoLinkEditAtom::getModuleTableFileOffset() const +{ + return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

); +} + +template +uint32_t ModuleInfoLinkEditAtom::getReferencesFileOffset() const +{ + return this->getModuleTableFileOffset() + sizeof(macho_dylib_module

); +} + +template +uint32_t ModuleInfoLinkEditAtom::getReferencesCount() const +{ + return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount; +} + +template +void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + // create toc. The symbols are already sorted, they are all in the smae module + macho_dylib_table_of_contents

* p = (macho_dylib_table_of_contents

*)buffer; + for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) { + p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i); + p->set_module_index(0); + } + // create module table (one entry) + uint16_t numInits = 0; + uint16_t numTerms = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + for (std::vector::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) { + if ( strcmp((*segit)->fName, "__DATA") == 0 ) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { + if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 ) + numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t); + else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 ) + numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t); + } + } + } + macho_dylib_module

* module = (macho_dylib_module

*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

)]; + module->set_module_name(fModuleNameOffset); + module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); + module->set_nextdefsym(fWriter.fSymbolTableExportCount); + module->set_irefsym(0); + module->set_nrefsym(this->getReferencesCount()); + module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); + module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount); + module->set_iextrel(0); + module->set_nextrel(fWriter.fExternalRelocs.size()); + module->set_iinit_iterm(0,0); + module->set_ninit_nterm(numInits,numTerms); + module->set_objc_module_info_addr(0); // Not used by ld_classic, and not used by objc runtime for many years + module->set_objc_module_info_size(0); // Not used by ld_classic, and not used by objc runtime for many years + // create reference table + macho_dylib_reference

* ref = (macho_dylib_reference

*)((uint8_t*)module + sizeof(macho_dylib_module

)); + for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) { + ref->set_isym(fWriter.fSymbolTableExportStartIndex+i); + ref->set_flags(REFERENCE_FLAG_DEFINED); + } + for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) { + ref->set_isym(fWriter.fSymbolTableImportStartIndex+i); + std::map::iterator pos = fWriter.fStubsMap.find(fWriter.fImportedAtoms[i]); + if ( pos != fWriter.fStubsMap.end() ) + ref->set_flags(REFERENCE_FLAG_UNDEFINED_LAZY); + else + ref->set_flags(REFERENCE_FLAG_UNDEFINED_NON_LAZY); + } +} + + + +template +StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) + : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) +{ + fCurrentBuffer = new char[kBufferSize]; + // burn first byte of string pool (so zero is never a valid string offset) + fCurrentBuffer[fCurrentBufferUsed++] = ' '; + // make offset 1 always point to an empty string + fCurrentBuffer[fCurrentBufferUsed++] = '\0'; +} + +template +uint64_t StringsLinkEditAtom::getSize() const +{ + // align size + return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t)); +} + +template +void StringsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t offset = 0; + for (unsigned int i=0; i < fFullBuffers.size(); ++i) { + memcpy(&buffer[offset], fFullBuffers[i], kBufferSize); + offset += kBufferSize; + } + memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed); + // zero fill end to align + offset += fCurrentBufferUsed; + while ( (offset % sizeof(typename A::P::uint_t)) != 0 ) + buffer[offset++] = 0; +} + +template +int32_t StringsLinkEditAtom::add(const char* name) +{ + int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; + int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1; + if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) { + fCurrentBufferUsed += lenNeeded; + } + else { + int copied = kBufferSize-fCurrentBufferUsed-1; + // change trailing '\0' that strlcpy added to real char + fCurrentBuffer[kBufferSize-1] = name[copied]; + // alloc next buffer + fFullBuffers.push_back(fCurrentBuffer); + fCurrentBuffer = new char[kBufferSize]; + fCurrentBufferUsed = 0; + // append rest of string + this->add(&name[copied+1]); + } + return offset; +} + + +template +int32_t StringsLinkEditAtom::addUnique(const char* name) +{ + StringToOffset::iterator pos = fUniqueStrings.find(name); + if ( pos != fUniqueStrings.end() ) { + return pos->second; + } + else { + int32_t offset = this->add(name); + fUniqueStrings[name] = offset; + return offset; + } +} + + +template +const char* StringsLinkEditAtom::stringForIndex(int32_t index) const +{ + int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size(); + int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed; + // check for out of bounds + if ( index > maxIndex ) + return ""; + // check for index in fCurrentBuffer + if ( index > currentBufferStartIndex ) + return &fCurrentBuffer[index-currentBufferStartIndex]; + // otherwise index is in a full buffer + uint32_t fullBufferIndex = index/kBufferSize; + return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; +} + + + +template +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) + : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset) +{ + char* buf = new char[strlen(name)+32]; + if ( targetOffset == 0 ) { + if ( islandRegion == 0 ) + sprintf(buf, "%s$island", name); + else + sprintf(buf, "%s$island_%d", name, islandRegion); + } + else { + sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); + } + fName = buf; +} + + +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); +} + +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + + + +template +uint64_t SegmentSplitInfoLoadCommandsAtom::getSize() const +{ + if ( fWriter.fSplitCodeToDataContentAtom->canEncode() ) + return this->alignedSize(sizeof(macho_linkedit_data_command

)); + else + return 0; // a zero size causes the load command to be suppressed +} + +template +void SegmentSplitInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)buffer; + cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); + cmd->set_cmdsize(size); + cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset()); + cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize()); +} + + +template +uint64_t SegmentSplitInfoContentAtom::getSize() const +{ + return fEncodedData.size(); +} + +template +void SegmentSplitInfoContentAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fEncodedData[0], fEncodedData.size()); +} + + +template +void SegmentSplitInfoContentAtom::uleb128EncodeAddresses(const std::vector::AtomAndOffset>& locations) +{ + pint_t addr = fWriter.fOptions.baseAddress(); + for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { + pint_t nextAddr = it->atom->getAddress() + it->offset; + //fprintf(stderr, "\t0x%0llX\n", (uint64_t)nextAddr); + uint64_t delta = nextAddr - addr; + if ( delta == 0 ) + throw "double split seg info for same address"; + // uleb128 encode + uint8_t byte; + do { + byte = delta & 0x7F; + delta &= ~0x7F; + if ( delta != 0 ) + byte |= 0x80; + fEncodedData.push_back(byte); + delta = delta >> 7; + } + while( byte >= 0x80 ); + addr = nextAddr; + } +} + +template +void SegmentSplitInfoContentAtom::encode() +{ + if ( ! fCantEncode ) { + fEncodedData.reserve(8192); + + if ( fKind1Locations.size() != 0 ) { + fEncodedData.push_back(1); + //fprintf(stderr, "type 1:\n"); + this->uleb128EncodeAddresses(fKind1Locations); + fEncodedData.push_back(0); + } + + if ( fKind2Locations.size() != 0 ) { + fEncodedData.push_back(2); + //fprintf(stderr, "type 2:\n"); + this->uleb128EncodeAddresses(fKind2Locations); + fEncodedData.push_back(0); + } + + if ( fKind3Locations.size() != 0 ) { + fEncodedData.push_back(3); + //fprintf(stderr, "type 3:\n"); + this->uleb128EncodeAddresses(fKind3Locations); + fEncodedData.push_back(0); + } + + if ( fKind4Locations.size() != 0 ) { + fEncodedData.push_back(4); + //fprintf(stderr, "type 4:\n"); + this->uleb128EncodeAddresses(fKind4Locations); + fEncodedData.push_back(0); + } + + // always add zero byte to mark end + fEncodedData.push_back(0); + + // add zeros to end to align size + while ( (fEncodedData.size() % sizeof(pint_t)) != 0 ) + fEncodedData.push_back(0); + } +} + + +template +ObjCInfoAtom::ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, bool objcReplacementClasses) + : WriterAtom(writer, getInfoSegment()) +{ + fContent[0] = 0; + uint32_t value = 0; + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + if ( objcReplacementClasses ) + value = 1; + switch ( objcConstraint ) { + case ObjectFile::Reader::kObjcNone: + case ObjectFile::Reader::kObjcRetainRelease: + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + value |= 2; + break; + case ObjectFile::Reader::kObjcGC: + value |= 6; + break; + } + A::P::E::set32(fContent[1], value); +} + +template +void ObjCInfoAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fContent[0], 8); +} + + +// objc info section is in a different segment and section for 32 vs 64 bit runtimes +template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } + +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } + + +}; // namespace executable +}; // namespace mach_o + + +#endif // __EXECUTABLE_MACH_O__ diff --git a/ld64/FireOpal/src/ObjectDump.cpp b/ld64/FireOpal/src/ObjectDump.cpp new file mode 100644 index 0000000..a06c7a2 --- /dev/null +++ b/ld64/FireOpal/src/ObjectDump.cpp @@ -0,0 +1,497 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +#include "MachOReaderRelocatable.hpp" + +#define LTO_SUPPORT 0 + +#if LTO_SUPPORT + #include "LTOReader.hpp" +#endif + +static bool sDumpContent= true; +static bool sDumpStabs = false; +static bool sSort = true; +static bool sNMmode = false; +static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; +static const char* sMatchName; +static int sPrintRestrict; +static int sPrintAlign; +static int sPrintName; + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + +void warning(const char* format, ...) +{ + va_list list; + fprintf(stderr, "warning: "); + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} + +static void dumpStabs(std::vector* stabs) +{ + // debug info + printf("stabs: (%lu)\n", stabs->size()); + for (std::vector::iterator it = stabs->begin(); it != stabs->end(); ++it ) { + ObjectFile::Reader::Stab& stab = *it; + const char* code = "?????"; + switch (stab.type) { + case N_GSYM: + code = " GSYM"; + break; + case N_FNAME: + code = "FNAME"; + break; + case N_FUN: + code = " FUN"; + break; + case N_STSYM: + code = "STSYM"; + break; + case N_LCSYM: + code = "LCSYM"; + break; + case N_BNSYM: + code = "BNSYM"; + break; + case N_OPT: + code = " OPT"; + break; + case N_RSYM: + code = " RSYM"; + break; + case N_SLINE: + code = "SLINE"; + break; + case N_ENSYM: + code = "ENSYM"; + break; + case N_SSYM: + code = " SSYM"; + break; + case N_SO: + code = " SO"; + break; + case N_OSO: + code = " OSO"; + break; + case N_LSYM: + code = " LSYM"; + break; + case N_BINCL: + code = "BINCL"; + break; + case N_SOL: + code = " SOL"; + break; + case N_PARAMS: + code = "PARMS"; + break; + case N_VERSION: + code = " VERS"; + break; + case N_OLEVEL: + code = "OLEVL"; + break; + case N_PSYM: + code = " PSYM"; + break; + case N_EINCL: + code = "EINCL"; + break; + case N_ENTRY: + code = "ENTRY"; + break; + case N_LBRAC: + code = "LBRAC"; + break; + case N_EXCL: + code = " EXCL"; + break; + case N_RBRAC: + code = "RBRAC"; + break; + case N_BCOMM: + code = "BCOMM"; + break; + case N_ECOMM: + code = "ECOMM"; + break; + case N_LENG: + code = "LENG"; + break; + } + printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string); + } +} + + +static void dumpAtomLikeNM(ObjectFile::Atom* atom) +{ + uint32_t size = atom->getSize(); + + const char* visibility; + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + visibility = "internal"; + break; + case ObjectFile::Atom::scopeLinkageUnit: + visibility = "hidden "; + break; + case ObjectFile::Atom::scopeGlobal: + visibility = "global "; + break; + default: + visibility = " "; + break; + } + + const char* kind; + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + kind = "regular "; + break; + case ObjectFile::Atom::kTentativeDefinition: + kind = "tentative"; + break; + case ObjectFile::Atom::kWeakDefinition: + kind = "weak "; + break; + case ObjectFile::Atom::kAbsoluteSymbol: + kind = "absolute "; + break; + default: + kind = " "; + break; + } + + printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName()); +} + + +static void dumpAtom(ObjectFile::Atom* atom) +{ + if(sMatchName && strcmp(sMatchName, atom->getDisplayName())) + return; + + //printf("atom: %p\n", atom); + + // name + if(!sPrintRestrict || sPrintName) + printf("name: %s\n", atom->getDisplayName()); + + // scope + if(!sPrintRestrict) + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + printf("scope: translation unit\n"); + break; + case ObjectFile::Atom::scopeLinkageUnit: + printf("scope: linkage unit\n"); + break; + case ObjectFile::Atom::scopeGlobal: + printf("scope: global\n"); + break; + default: + printf("scope: unknown\n"); + } + + // kind + if(!sPrintRestrict) + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + printf("kind: regular\n"); + break; + case ObjectFile::Atom::kWeakDefinition: + printf("kind: weak\n"); + break; + case ObjectFile::Atom::kTentativeDefinition: + printf("kind: tentative\n"); + break; + case ObjectFile::Atom::kExternalDefinition: + printf("kind: import\n"); + break; + case ObjectFile::Atom::kExternalWeakDefinition: + printf("kind: weak import\n"); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + printf("kind: absolute symbol\n"); + break; + default: + printf("kind: unknown\n"); + } + + // segment and section + if(!sPrintRestrict && (atom->getSectionName() != NULL) ) + printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + + // attributes + if(!sPrintRestrict) { + printf("attrs: "); + if ( atom->dontDeadStrip() ) + printf("dont-dead-strip "); + if ( atom->isZeroFill() ) + printf("zero-fill "); + if ( atom->isThumb() ) + printf("thumb "); + printf("\n"); + } + + // size + if(!sPrintRestrict) + printf("size: 0x%012llX\n", atom->getSize()); + + // alignment + if(!sPrintRestrict || sPrintAlign) + printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); + + // content + if (!sPrintRestrict && sDumpContent ) { + uint64_t size = atom->getSize(); + if ( size < 4096 ) { + uint8_t content[size]; + atom->copyRawContent(content); + printf("content: "); + if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + printf("\""); + for (unsigned int i=0; i < size; ++i) { + if(content[i]<'!' || content[i]>=127) + printf("\\%o", content[i]); + else + printf("%c", content[i]); + } + printf("\""); + } + else { + for (unsigned int i=0; i < size; ++i) + printf("%02X ", content[i]); + } + } + printf("\n"); + } + + // references + if(!sPrintRestrict) { + std::vector& references = atom->getReferences(); + const int refCount = references.size(); + printf("references: (%u)\n", refCount); + for (int i=0; i < refCount; ++i) { + ObjectFile::Reference* ref = references[i]; + printf(" %s\n", ref->getDescription()); + } + } + + // line info + if(!sPrintRestrict) { + std::vector* lineInfo = atom->getLineInfo(); + if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) { + printf("line info: (%lu)\n", lineInfo->size()); + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); + } + } + } + + if(!sPrintRestrict) + printf("\n"); +} + +struct AtomSorter +{ + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) + { + if ( left == right ) + return false; + return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); + } +}; + + +static void dumpFile(ObjectFile::Reader* reader) +{ + // stabs debug info + if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) { + std::vector* stabs = reader->getStabs(); + if ( stabs != NULL ) + dumpStabs(stabs); + } + + // get all atoms + std::vector atoms = reader->getAtoms(); + + // make copy of vector and sort (so output is canonical) + std::vector sortedAtoms(atoms); + if ( sSort ) + std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter()); + + for(std::vector::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) { + if ( sNMmode ) + dumpAtomLikeNM(*it); + else + dumpAtom(*it); + } +} + + +static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) +{ + struct stat stat_buf; + + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("cannot open file: %s", path); + ::fstat(fd, &stat_buf); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { + p = p + OSSwapBigToHostInt32(archs[i].offset); + mh = (struct mach_header*)p; + } + } + } + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); +#if LTO_SUPPORT + if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) { + return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0); + } +#endif + + throwf("not a mach-o object file: %s", path); +} + +static +void +usage() +{ + fprintf(stderr, "ObjectDump options:\n" + "\t-no_content\tdon't dump contents\n" + "\t-stabs\t\tdump stabs\n" + "\t-arch aaa\tonly dump info about arch aaa\n" + "\t-only sym\tonly dump info about sym\n" + "\t-align\t\tonly print alignment info\n" + "\t-name\t\tonly print symbol names\n" + ); +} + +int main(int argc, const char* argv[]) +{ + if(argc<2) { + usage(); + return 0; + } + + ObjectFile::ReaderOptions options; + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-no_content") == 0 ) { + sDumpContent = false; + } + else if ( strcmp(arg, "-nm") == 0 ) { + sNMmode = true; + } + else if ( strcmp(arg, "-stabs") == 0 ) { + sDumpStabs = true; + } + else if ( strcmp(arg, "-no_sort") == 0 ) { + sSort = false; + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = ++i +#include +#include +#include + + + +// +// These classes represent the abstract Atoms and References that are the basis of the linker. +// An Atom and a Reference correspond to a Node and Edge in graph theory. +// +// A Reader is a class which parses an object file and presents it as Atoms and References. +// All linking operations are done on Atoms and References. This makes the linker file +// format independent. +// +// A Writer takes a vector of Atoms with all References resolved and produces an executable file. +// +// + + + +namespace ObjectFile { + + +struct LineInfo +{ + uint32_t atomOffset; + const char* fileName; + uint32_t lineNumber; +}; + + +class ReaderOptions +{ +public: + ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), + fLinkingMainExecutable(false), fSlowx86Stubs(false), + fForFinalLinkedImage(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), + fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), + fImplicitlyLinkPublicDylibs(true), fLogObjectFiles(false), fLogAllFiles(false), + fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), + fTraceOutputFile(NULL), fVersionMin(kMinUnset) {} + enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; + enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; + + struct AliasPair { + const char* realName; + const char* alias; + }; + + bool fFullyLoadArchives; + bool fLoadAllObjcObjectsFromArchives; + bool fFlatNamespace; + bool fLinkingMainExecutable; + bool fSlowx86Stubs; + bool fForFinalLinkedImage; + bool fForStatic; + bool fForDyld; + bool fMakeTentativeDefinitionsReal; + bool fWhyLoad; + bool fRootSafe; + bool fSetuidSafe; + DebugInfoStripping fDebugInfoStripping; + bool fImplicitlyLinkPublicDylibs; + bool fLogObjectFiles; + bool fLogAllFiles; + bool fTraceDylibs; + bool fTraceIndirectDylibs; + bool fTraceArchives; + const char* fTraceOutputFile; + VersionMin fVersionMin; + std::vector fAliases; +}; + + +class Reader +{ +public: + enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + struct Stab + { + class Atom* atom; + uint8_t type; + uint8_t other; + uint16_t desc; + uint32_t value; + const char* string; + }; + enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC }; + enum CpuConstraint { kCpuAny = 0 }; + + class DylibHander + { + public: + virtual ~DylibHander() {} + virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0; + }; + + + static Reader* createReader(const char* path, const ReaderOptions& options); + + virtual const char* getPath() = 0; + virtual time_t getModificationTime() = 0; + virtual DebugInfoKind getDebugInfoKind() = 0; + virtual std::vector& getAtoms() = 0; + virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; + virtual std::vector* getStabs() = 0; + virtual ObjcConstraint getObjCConstraint() { return kObjcNone; } + virtual uint32_t updateCpuConstraint(uint32_t current) { return current; } + virtual bool objcReplacementClasses() { return false; } + + // For relocatable object files only + virtual bool canScatterAtoms() { return true; } + virtual void optimize(std::vector&, std::vector&, + std::vector&, const std::set&, + uint32_t, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, int okind, + bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs) { } + virtual bool hasLongBranchStubs() { return false; } + + // For Dynamic Libraries only + virtual const char* getInstallPath() { return NULL; } + virtual uint32_t getTimestamp() { return 0; } + virtual uint32_t getCurrentVersion() { return 0; } + virtual uint32_t getCompatibilityVersion() { return 0; } + virtual void processIndirectLibraries(DylibHander* handler) { } + virtual void setExplicitlyLinked() { } + virtual bool explicitlyLinked() { return false; } + virtual bool implicitlyLinked() { return false; } + virtual bool providedExportAtom() { return false; } + virtual const char* parentUmbrella() { return NULL; } + virtual std::vector* getAllowableClients() { return NULL; } + virtual bool hasWeakExternals() { return false; } + virtual bool isLazyLoadedDylib() { return false; } + +protected: + Reader() {} + virtual ~Reader() {} +}; + +class Segment +{ +public: + virtual const char* getName() const = 0; + virtual bool isContentReadable() const = 0; + virtual bool isContentWritable() const = 0; + virtual bool isContentExecutable() const = 0; + + uint64_t getBaseAddress() const { return fBaseAddress; } + void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + virtual bool hasFixedAddress() const { return false; } + +protected: + Segment() : fBaseAddress(0) {} + virtual ~Segment() {} + uint64_t fBaseAddress; +}; + +class Reference; + +class Section +{ +public: + unsigned int getIndex() { return fIndex; } + uint64_t getBaseAddress() { return fBaseAddress; } + void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + void* fOther; + +protected: + Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} + uint64_t fBaseAddress; + unsigned int fIndex; +}; + + +struct Alignment +{ + Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} + uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } + uint16_t powerOf2; + uint16_t modulus; +}; + +// +// An atom is the fundamental unit of linking. A C function or global variable is an atom. +// An atom has content and some attributes. The content of a function atom is the instructions +// that implement the function. The content of a global variable atom is its initial bits. +// +// Name: +// The name of an atom is the label name generated by the compiler. A C compiler names foo() +// as _foo. A C++ compiler names foo() as __Z3foov. +// The name refers to the first byte of the content. An atom cannot have multiple entry points. +// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. +// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. +// +// Scope: +// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond +// to the C visibility of static, hidden, default. +// +// DefinitionKind: +// An atom is one of five defintion kinds: +// regular Most atoms. +// weak C++ compiler makes some functions weak if there might be multiple copies +// that the linker needs to coalesce. +// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. +// It could be a prototype or it could be a definition. +// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists +// so that all References can be resolved. +// external-weak Same as external, but the definition in the dylib is weak. +// +// SymbolTableInclusion: +// An atom may or may not be in the symbol table in an object file. +// in Most atoms for functions or global data +// not-in Anonymous atoms such literal c-strings, or other compiler generated data +// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) +// +// Ordinal: +// When a reader is created it is given a base ordinal number. All atoms created by the reader +// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal +// values are used by the linker to sort the atom graph when producing the output file. +// +class Atom +{ +public: + enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; + enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; + enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; + + virtual Reader* getFile() const = 0; + virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; + virtual const char* getName() const = 0; + virtual const char* getDisplayName() const = 0; + virtual Scope getScope() const = 0; + virtual DefinitionKind getDefinitionKind() const = 0; + virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; + virtual bool dontDeadStrip() const = 0; + virtual bool isZeroFill() const = 0; + virtual bool isThumb() const = 0; + virtual uint64_t getSize() const = 0; + virtual std::vector& getReferences() const = 0; + virtual bool mustRemainInSection() const = 0; + virtual const char* getSectionName() const = 0; + virtual Segment& getSegment() const = 0; + virtual Atom& getFollowOnAtom() const = 0; + virtual uint32_t getOrdinal() const = 0; + virtual std::vector* getLineInfo() const = 0; + virtual Alignment getAlignment() const = 0; + virtual void copyRawContent(uint8_t buffer[]) const = 0; + virtual void setScope(Scope) = 0; + + + uint64_t getSectionOffset() const { return fSectionOffset; } + uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } + class Section* getSection() const { return fSection; } + + virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } + virtual void setSection(class Section* sect) { fSection = sect; } + +protected: + Atom() : fSectionOffset(0), fSection(NULL) {} + virtual ~Atom() {} + + uint64_t fSectionOffset; + class Section* fSection; +}; + + +// +// A Reference is a directed edge to another Atom. When an instruction in +// the content of an Atom refers to another Atom, that is represented by a +// Reference. +// +// There are two kinds of references: direct and by-name. With a direct Reference, +// the target is bound by the Reader that created it. For instance a reference to a +// static would produce a direct reference. A by-name reference requires the linker +// to find the target Atom with the required name in order to be bound. +// +// For a link to succeed all References must be bound. +// +// A Reference has an optional "from" target. This is used when the content to fix-up +// is the difference of two Atom address. For instance, if a pointer sized data Atom +// is to contain A - B, then the Atom would have on Reference with a target of "A" and +// a from-target of "B". +// +// A Reference also has a fix-up-offset. This is the offset into the content of the +// Atom holding the reference where the fix-up (relocation) will be applied. +// +// +// +class Reference +{ +public: + enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind }; + + virtual TargetBinding getTargetBinding() const = 0; + virtual TargetBinding getFromTargetBinding() const = 0; + virtual uint8_t getKind() const = 0; + virtual uint64_t getFixUpOffset() const = 0; + virtual const char* getTargetName() const = 0; + virtual Atom& getTarget() const = 0; + virtual uint64_t getTargetOffset() const = 0; + virtual Atom& getFromTarget() const = 0; + virtual const char* getFromTargetName() const = 0; + virtual uint64_t getFromTargetOffset() const = 0; + + virtual void setTarget(Atom&, uint64_t offset) = 0; + virtual void setFromTarget(Atom&) = 0; + virtual const char* getDescription() const = 0; + +protected: + Reference() {} + virtual ~Reference() {} +}; + + +}; // namespace ObjectFile + + +#endif // __OBJECTFILE__ diff --git a/ld64/FireOpal/src/OpaqueSection.hpp b/ld64/FireOpal/src/OpaqueSection.hpp new file mode 100644 index 0000000..cddd45c --- /dev/null +++ b/ld64/FireOpal/src/OpaqueSection.hpp @@ -0,0 +1,199 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OPAQUE_SECTION__ +#define __OPAQUE_SECTION__ + + +#include + +#include "ObjectFile.h" + +namespace opaque_section { + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); } +private: + const char* fName; +}; + + +class Reader : public ObjectFile::Reader +{ +public: + Reader(const char* segmentName, const char* sectionName, const char* path, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL); + virtual ~Reader(); + + void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, + uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0); + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms() { return fAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return NULL; } + +private: + const char* fPath; + std::vector fAtoms; +}; + +class Reference : public ObjectFile::Reference +{ +public: + Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset, + const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0) + : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind), + fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {} + virtual ~Reference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffset; } + virtual const char* getTargetName() const { return fTarget->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } + virtual uint64_t getTargetOffset() const { return fTargetOffset; } + virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); } + virtual const char* getFromTargetName() const { return fFromTarget->getName(); } + virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } + virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; } + virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } + virtual const char* getDescription() const { return "opaque section reference"; } + +private: + uint64_t fFixUpOffset; + const ObjectFile::Atom* fTarget; + uint64_t fTargetOffset; + uint8_t fKind; + const ObjectFile::Atom* fFromTarget; + uint64_t fFromTargetOffset; +}; + + +class Atom : public ObjectFile::Atom { +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const; + virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return true; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return fFileLength; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return fSectionName; } + virtual Segment& getSegment() const { return fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); } + virtual void copyRawContent(uint8_t buffer[]) const; + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + + Atom(Reader& owner, Segment& segment, const char* sectionName, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName); + virtual ~Atom() {} + + Reader& fOwner; + Segment& fSegment; + const char* fName; + const char* fSectionName; + const uint8_t* fFileContent; + uint32_t fOrdinal; + uint64_t fFileLength; + std::vector fReferences; +}; + + + +Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName) + : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength) +{ + if ( symbolName != NULL ) + fName = strdup(symbolName); + else + asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName); +} + + +Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], + uint64_t fileLength, uint32_t ordinal, const char* symbolName) + : fPath(path) +{ + fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName)); +} + +Reader::~Reader() +{ +} + +void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, + uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget) +{ + fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget)); +} + + +const char* Atom::getDisplayName() const +{ + static char name[64]; + sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName); + return name; +} + + +void Atom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fFileContent, fFileLength); +} + + + +}; + + + +#endif // __OPAQUE_SECTION__ + + + diff --git a/ld64/FireOpal/src/Options.cpp b/ld64/FireOpal/src/Options.cpp new file mode 100644 index 0000000..eb244ed --- /dev/null +++ b/ld64/FireOpal/src/Options.cpp @@ -0,0 +1,3150 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +#include "configure.h" +#include "Options.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +extern void printLTOVersion(Options &opts); + + +static bool sEmitWarnings = true; +static const char* sWarningsSideFilePath = NULL; +static FILE* sWarningsSideFile = NULL; + +void warning(const char* format, ...) +{ + if ( sEmitWarnings ) { + va_list list; + if ( sWarningsSideFilePath != NULL ) { + if ( sWarningsSideFile == NULL ) + sWarningsSideFile = fopen(sWarningsSideFilePath, "a"); + } + va_start(list, format); + fprintf(stderr, "ld warning: "); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); + if ( sWarningsSideFile != NULL ) { + fprintf(sWarningsSideFile, "ld warning: "); + vfprintf(sWarningsSideFile, format, list); + fprintf(sWarningsSideFile, "\n"); + fflush(sWarningsSideFile); + } + va_end(list); + } +} + +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + +Options::Options(int argc, const char* argv[]) + : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable), + fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), + fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), + fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0), + fBaseWritableAddress(0), fSplitSegs(false), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), + fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), + fClientName(NULL), + fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), + fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), + fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(32), + fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), + fVerbose(false), fKeepRelocations(false), fWarnStabs(false), + fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), + fSharedRegionEligible(false), fPrintOrderFileStatistics(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), + fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), + fUsingLazyDylibLinking(false), fEncryptable(true), fSaveTempFiles(false) +{ + this->checkForClassic(argc, argv); + this->parsePreCommandLineEnvironmentSettings(); + this->parse(argc, argv); + this->parsePostCommandLineEnvironmentSettings(); + this->reconfigureDefaults(); + this->checkIllegalOptionCombinations(); +} + +Options::~Options() +{ +} + +const ObjectFile::ReaderOptions& Options::readerOptions() +{ + return fReaderOptions; +} + + +const char* Options::getOutputFilePath() +{ + return fOutputFile; +} + +std::vector& Options::getInputFiles() +{ + return fInputFiles; +} + +Options::OutputKind Options::outputKind() +{ + return fOutputKind; +} + +bool Options::bindAtLoad() +{ + return fBindAtLoad; +} + +bool Options::prebind() +{ + return fPrebind; +} + +bool Options::fullyLoadArchives() +{ + return fReaderOptions.fFullyLoadArchives; +} + +Options::NameSpace Options::nameSpace() +{ + return fNameSpace; +} + +const char* Options::installPath() +{ + if ( fDylibInstallName != NULL ) + return fDylibInstallName; + else if ( fFinalName != NULL ) + return fFinalName; + else + return fOutputFile; +} + +uint32_t Options::currentVersion() +{ + return fDylibCurrentVersion; +} + +uint32_t Options::compatibilityVersion() +{ + return fDylibCompatVersion; +} + +const char* Options::entryName() +{ + return fEntryName; +} + +uint64_t Options::baseAddress() +{ + return fBaseAddress; +} + +bool Options::keepPrivateExterns() +{ + return fKeepPrivateExterns; +} + +bool Options::interposable(const char* name) +{ + switch ( fInterposeMode ) { + case kInterposeNone: + return false; + case kInterposeAllExternal: + return true; + case kInterposeSome: + return fInterposeList.contains(name); + } + throw "internal error"; +} + +bool Options::needsModuleTable() +{ + return fNeedsModuleTable; +} + +bool Options::ignoreOtherArchInputFiles() +{ + return fIgnoreOtherArchFiles; +} + +bool Options::forceCpuSubtypeAll() +{ + return fForceSubtypeAll; +} + +bool Options::traceDylibs() +{ + return fReaderOptions.fTraceDylibs; +} + +bool Options::traceArchives() +{ + return fReaderOptions.fTraceArchives; +} + +Options::UndefinedTreatment Options::undefinedTreatment() +{ + return fUndefinedTreatment; +} + +ObjectFile::ReaderOptions::VersionMin Options::macosxVersionMin() +{ + return fReaderOptions.fVersionMin; +} + +Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() +{ + return fWeakReferenceMismatchTreatment; +} + +const char* Options::umbrellaName() +{ + return fUmbrellaName; +} + +std::vector& Options::allowableClients() +{ + return fAllowableClients; +} + +const char* Options::clientName() +{ + return fClientName; +} + +uint64_t Options::zeroPageSize() +{ + return fZeroPageSize; +} + +bool Options::hasCustomStack() +{ + return (fStackSize != 0); +} + +uint64_t Options::customStackSize() +{ + return fStackSize; +} + +uint64_t Options::customStackAddr() +{ + return fStackAddr; +} + +bool Options::hasExecutableStack() +{ + return fExecutableStack; +} + +std::vector& Options::initialUndefines() +{ + return fInitialUndefines; +} + +bool Options::printWhyLive(const char* symbolName) +{ + return ( fWhyLive.find(symbolName) != fWhyLive.end() ); +} + + +const char* Options::initFunctionName() +{ + return fInitFunctionName; +} + +const char* Options::dotOutputFile() +{ + return fDotOutputFile; +} + +bool Options::hasExportRestrictList() +{ + return (fExportMode != kExportDefault); +} + +bool Options::hasExportMaskList() +{ + return (fExportMode == kExportSome); +} + + +bool Options::hasWildCardExportRestrictList() +{ + // has -exported_symbols_list which contains some wildcards + return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); +} + + +bool Options::allGlobalsAreDeadStripRoots() +{ + // -exported_symbols_list means globals are not exported by default + if ( fExportMode == kExportSome ) + return false; + // + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // by default unused globals in a main executable are stripped + return false; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + return true; + } + return false; +} + +uint32_t Options::minimumHeaderPad() +{ + return fMinimumHeaderPad; +} + +std::vector& Options::extraSections() +{ + return fExtraSections; +} + +std::vector& Options::sectionAlignments() +{ + return fSectionAlignments; +} + +Options::CommonsMode Options::commonsMode() +{ + return fCommonsMode; +} + +bool Options::warnCommons() +{ + return fWarnCommons; +} + +bool Options::keepRelocations() +{ + return fKeepRelocations; +} + +bool Options::warnStabs() +{ + return fWarnStabs; +} + +const char* Options::executablePath() +{ + return fExecutablePath; +} + +Options::DeadStripMode Options::deadStrip() +{ + return fDeadStrip; +} + +bool Options::shouldExport(const char* symbolName) +{ + switch (fExportMode) { + case kExportSome: + return fExportSymbols.contains(symbolName); + case kDontExportSome: + return ! fDontExportSymbols.contains(symbolName); + case kExportDefault: + return true; + } + throw "internal error"; +} + +bool Options::keepLocalSymbol(const char* symbolName) +{ + switch (fLocalSymbolHandling) { + case kLocalSymbolsAll: + return true; + case kLocalSymbolsNone: + return false; + case kLocalSymbolsSelectiveInclude: + return fLocalSymbolsIncluded.contains(symbolName); + case kLocalSymbolsSelectiveExclude: + return ! fLocalSymbolsExcluded.contains(symbolName); + } + throw "internal error"; +} + +void Options::parseArch(const char* architecture) +{ + if ( architecture == NULL ) + throw "-arch must be followed by an architecture string"; + if ( strcmp(architecture, "ppc") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; + } + else if ( strcmp(architecture, "ppc64") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC64; + fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; + } + else if ( strcmp(architecture, "i386") == 0 ) { + fArchitecture = CPU_TYPE_I386; + fSubArchitecture = CPU_SUBTYPE_I386_ALL; + } + else if ( strcmp(architecture, "x86_64") == 0 ) { + fArchitecture = CPU_TYPE_X86_64; + fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; + } + else if ( strcmp(architecture, "arm") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_ALL; + } + // compatibility support for cpu-sub-types + else if ( strcmp(architecture, "ppc750") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_750; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "ppc7400") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "ppc7450") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "ppc970") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_970; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv6") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V6; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv5") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv4t") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V4T; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "xscale") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv7") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V7; + fHasPreferredSubType = true; + } + else + throwf("unknown/unsupported architecture name for: -arch %s", architecture); +} + +bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) +{ + struct stat statBuffer; + char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; + sprintf(possiblePath, format, dir, rootName); + bool found = (stat(possiblePath, &statBuffer) == 0); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); + if ( found ) { + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return true; + } + return false; +} + + +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) +{ + FileInfo result; + const int rootNameLen = strlen(rootName); + // if rootName ends in .o there is no .a vs .dylib choice + if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/%s", dir, rootName, result) ) + return result; + } + } + else { + bool lookForDylibs = ( fOutputKind != Options::kDyld); + switch ( fLibrarySearchMode ) { + case kSearchAllDirsForDylibsThenAllDirsForArchives: + // first look in all directories for just for dylibs + if ( lookForDylibs ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + return result; + } + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.so", dir, rootName, result) ) + return result; + } + } + // next look in all directories for just for archives + if ( !dylibsOnly ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + } + break; + + case kSearchDylibAndArchiveInEachDir: + // look in each directory for just for a dylib then for an archive + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + return result; + if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) + return result; + if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + break; + } + } + throwf("library not found for -l%s", rootName); +} + +Options::FileInfo Options::findFramework(const char* frameworkName) +{ + if ( frameworkName == NULL ) + throw "-framework missing next argument"; + char temp[strlen(frameworkName)+1]; + strcpy(temp, frameworkName); + const char* name = temp; + const char* suffix = NULL; + char* comma = strchr(temp, ','); + if ( comma != NULL ) { + *comma = '\0'; + suffix = &comma[1]; + } + return findFramework(name, suffix); +} + +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) +{ + struct stat statBuffer; + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) { + // ??? Shouldn't we be using String here and just initializing it? + // ??? Use str.c_str () to pull out the string for the stat call. + const char* dir = *it; + char possiblePath[PATH_MAX]; + strcpy(possiblePath, dir); + strcat(possiblePath, "/"); + strcat(possiblePath, rootName); + strcat(possiblePath, ".framework/"); + strcat(possiblePath, rootName); + if ( suffix != NULL ) { + char realPath[PATH_MAX]; + // no symlink in framework to suffix variants, so follow main symlink + if ( realpath(possiblePath, realPath) != NULL ) { + strcpy(possiblePath, realPath); + strcat(possiblePath, suffix); + } + } + bool found = (stat(possiblePath, &statBuffer) == 0); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound framework: '%s'\n", + (found ? " " : " not "), possiblePath); + if ( found ) { + FileInfo result; + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + // try without suffix + if ( suffix != NULL ) + return findFramework(rootName, NULL); + else + throwf("framework not found %s", rootName); +} + +Options::FileInfo Options::findFile(const char* path) +{ + FileInfo result; + struct stat statBuffer; + + // if absolute path and not a .o file, the use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { + const int pathLen = strlen(path); + for (std::vector::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { + // ??? Shouldn't we be using String here? + const char* sdkPathDir = *it; + const int sdkPathDirLen = strlen(sdkPathDir); + char possiblePath[sdkPathDirLen+pathLen+4]; + strcpy(possiblePath, sdkPathDir); + if ( possiblePath[sdkPathDirLen-1] == '/' ) + possiblePath[sdkPathDirLen-1] = '\0'; + strcat(possiblePath, path); + if ( stat(possiblePath, &statBuffer) == 0 ) { + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + } + // try raw path + if ( stat(path, &statBuffer) == 0 ) { + result.path = strdup(path); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + + // try @executable_path substitution + if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { + char newPath[strlen(fExecutablePath) + strlen(path)]; + strcpy(newPath, fExecutablePath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newPath, &path[17]); + if ( stat(newPath, &statBuffer) == 0 ) { + result.path = strdup(newPath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + + // not found + throwf("file not found: %s", path); +} + +Options::FileInfo Options::findFileUsingPaths(const char* path) +{ + FileInfo result; + + const char* lastSlash = strrchr(path, '/'); + const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1]; + + // Is this in a framework? + // /path/Foo.framework/Foo ==> true (Foo) + // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) + // /path/Foo.framework/Resources/Bar ==> false + bool isFramework = false; + if ( lastSlash != NULL ) { + char frameworkDir[strlen(leafName) + 20]; + strcpy(frameworkDir, "/"); + strcat(frameworkDir, leafName); + strcat(frameworkDir, ".framework/"); + if ( strstr(path, frameworkDir) != NULL ) + isFramework = true; + } + + // These are abbreviated versions of the routines findFramework and findLibrary above + // because we already know the final name of the file that we're looking for and so + // don't need to try variations, just paths. We do need to add the additional bits + // onto the framework path though. + if ( isFramework ) { + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) { + const char* dir = *it; + char possiblePath[PATH_MAX]; + strcpy(possiblePath, dir); + strcat(possiblePath, "/"); + strcat(possiblePath, leafName); + strcat(possiblePath, ".framework"); + + //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName); + if ( checkForFile("%s/%s", possiblePath, leafName, result) ) + return result; + } + } + else { + // if this is a .dylib inside a framework, do not search -L paths + // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + int leafLen = strlen(leafName); + bool embeddedDylib = ( (leafLen > 6) + && (strcmp(&leafName[leafLen-6], ".dylib") == 0) + && (strstr(path, ".framework/") != NULL) ); + if ( !embeddedDylib ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); + if ( checkForFile("%s/%s", dir, leafName, result) ) + return result; + } + } + } + + // If we didn't find it fall back to findFile. + return findFile(path); +} + + +void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath) +{ + FILE* file = fopen(segAddrPath, "r"); + if ( file == NULL ) { + warning("-seg_addr_table file cannot be read: %s", segAddrPath); + return; + } + + char path[PATH_MAX]; + uint64_t firstColumAddress = 0; + uint64_t secondColumAddress = 0; + bool hasSecondColumn = false; + while ( fgets(path, PATH_MAX, file) != NULL ) { + path[PATH_MAX-1] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + // ignore lines not starting with 0x number + if ( (path[0] == '0') && (path[1] == 'x') ) { + char* p; + firstColumAddress = strtoull(path, &p, 16); + while ( isspace(*p) ) + ++p; + // see if second column is a number + if ( (p[0] == '0') && (p[1] == 'x') ) { + secondColumAddress = strtoull(p, &p, 16); + hasSecondColumn = true; + while ( isspace(*p) ) + ++p; + } + while ( isspace(*p) ) + ++p; + if ( p[0] == '/' ) { + // remove any trailing whitespace + for(char* end = eol-1; (end > p) && isspace(*end); --end) + *end = '\0'; + // see if this line is for the dylib being linked + if ( strcmp(p, installPath) == 0 ) { + fBaseAddress = firstColumAddress; + if ( hasSecondColumn ) { + fBaseWritableAddress = secondColumAddress; + fSplitSegs = true; + } + break; // out of while loop + } + } + } + } + + fclose(file); +} + +void Options::loadFileList(const char* fileOfPaths) +{ + FILE* file; + const char* comma = strrchr(fileOfPaths, ','); + const char* prefix = NULL; + if ( comma != NULL ) { + prefix = comma+1; + int realFileOfPathsLen = comma-fileOfPaths; + char realFileOfPaths[realFileOfPathsLen+1]; + strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); + realFileOfPaths[realFileOfPathsLen] = '\0'; + file = fopen(realFileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", realFileOfPaths); + } + else { + file = fopen(fileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", fileOfPaths); + } + + char path[PATH_MAX]; + while ( fgets(path, PATH_MAX, file) != NULL ) { + path[PATH_MAX-1] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + if ( prefix != NULL ) { + char builtPath[strlen(prefix)+strlen(path)+2]; + strcpy(builtPath, prefix); + strcat(builtPath, "/"); + strcat(builtPath, path); + fInputFiles.push_back(findFile(builtPath)); + } + else { + fInputFiles.push_back(findFile(path)); + } + } + fclose(file); +} + +bool Options::SetWithWildcards::hasWildCards(const char* symbol) +{ + // an exported symbol name containing *, ?, or [ requires wildcard matching + return ( strpbrk(symbol, "*?[") != NULL ); +} + +void Options::SetWithWildcards::insert(const char* symbol) +{ + if ( hasWildCards(symbol) ) + fWildCard.push_back(symbol); + else + fRegular.insert(symbol); +} + +bool Options::SetWithWildcards::contains(const char* symbol) +{ + // first look at hash table on non-wildcard symbols + if ( fRegular.find(symbol) != fRegular.end() ) + return true; + // next walk list of wild card symbols looking for a match + for(std::vector::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { + if ( wildCardMatch(*it, symbol) ) + return true; + } + return false; +} + + +bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) +{ + ++p; // find end + const char* b = p; + while ( *p != '\0' ) { + if ( *p == ']') { + const char* e = p; + // found beginining [ and ending ] + unsigned char last = '\0'; + for ( const char* s = b; s < e; ++s ) { + if ( *s == '-' ) { + unsigned char next = *(++s); + if ( (last <= c) && (c <= next) ) + return true; + ++s; + } + else { + if ( *s == c ) + return true; + last = *s; + } + } + return false; + } + ++p; + } + return false; +} + +bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) +{ + const char* s = symbol; + for (const char* p = pattern; *p != '\0'; ++p) { + switch ( *p ) { + case '*': + if ( p[1] == '\0' ) + return true; + for (const char* t = s; *t != '\0'; ++t) { + if ( wildCardMatch(&p[1], t) ) + return true; + } + return false; + case '?': + if ( *s == '\0' ) + return false; + ++s; + break; + case '[': + if ( ! inCharRange(p, *s) ) + return false; + ++s; + break; + default: + if ( *s != *p ) + return false; + ++s; + } + } + return (*s == '\0'); +} + + +void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) +{ + // read in whole file + int fd = ::open(fileOfExports, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open %s file: %s", option, fileOfExports); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process %s file: %s", option, fileOfExports); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read %s file: %s", option, fileOfExports); + + ::close(fd); + + // parse into symbols and add to hash_set + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (*s == '\r') ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(symbolStart); + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( (*s == '\n') || (*s == '\r') ) + state = lineStart; + break; + } + } + if ( state == inSymbol ) { + warning("missing line-end at end of file \"%s\"", fileOfExports); + int len = end-symbolStart+1; + char* temp = new char[len]; + strlcpy(temp, symbolStart, len); + + // remove any trailing spaces + char* last = &temp[len-2]; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(temp); + } + + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table +} + +void Options::parseAliasFile(const char* fileOfAliases) +{ + // read in whole file + int fd = ::open(fileOfAliases, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open alias file: %s", fileOfAliases); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size+1); + if ( p == NULL ) + throwf("can't process alias file: %s", fileOfAliases); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read alias file: %s", fileOfAliases); + p[stat_buf.st_size] = '\n'; + ::close(fd); + + // parse into symbols and add to fAliases + ObjectFile::ReaderOptions::AliasPair pair; + char * const end = &p[stat_buf.st_size+1]; + enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; + int lineNumber = 1; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inRealName; + pair.realName = s; + } + break; + case inRealName: + if ( *s == '\n' ) { + warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( isspace(*s) ) { + *s = '\0'; + state = inBetween; + } + break; + case inBetween: + if ( *s == '\n' ) { + warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( ! isspace(*s) ) { + state = inAliasName; + pair.alias = s; + } + break; + case inAliasName: + if ( *s =='#' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + fReaderOptions.fAliases.push_back(pair); + state = inComment; + } + else if ( *s == '\n' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + fReaderOptions.fAliases.push_back(pair); + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + + // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases +} + + + +void Options::setUndefinedTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; + + if ( strcmp(treatment, "warning") == 0 ) + fUndefinedTreatment = kUndefinedWarning; + else if ( strcmp(treatment, "error") == 0 ) + fUndefinedTreatment = kUndefinedError; + else if ( strcmp(treatment, "suppress") == 0 ) + fUndefinedTreatment = kUndefinedSuppress; + else if ( strcmp(treatment, "dynamic_lookup") == 0 ) + fUndefinedTreatment = kUndefinedDynamicLookup; + else + throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; +} + +Options::Treatment Options::parseTreatment(const char* treatment) +{ + if ( treatment == NULL ) + return kNULL; + + if ( strcmp(treatment, "warning") == 0 ) + return kWarning; + else if ( strcmp(treatment, "error") == 0 ) + return kError; + else if ( strcmp(treatment, "suppress") == 0 ) + return kSuppress; + else + return kInvalid; +} + +void Options::setMacOSXVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-macosx_version_min argument missing"; + + if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { + int num = version[3] - '0'; + switch ( num ) { + case 0: + case 1: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; + break; + default: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; + break; + } + } + else { + warning("unknown option to -macosx_version_min, not 10.x"); + } +} + +void Options::setIPhoneVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-iphoneos_version_min argument missing"; + + if ( ((strncmp(version, "1.", 2) == 0) || (strncmp(version, "2.", 2) == 0)) && isdigit(version[2]) ) { + int num = version[2] - '0'; + switch ( num ) { + case 2: + // TODO: store deployment version + break; + default: + break; + } + } + else { + warning("unknown option to -iphoneos_version_min, not 1.x or 2.x"); + } +} + +void Options::setWeakReferenceMismatchTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; + + if ( strcmp(treatment, "error") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; + else if ( strcmp(treatment, "weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; + else if ( strcmp(treatment, "non-weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; + else + throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; +} + +Options::CommonsMode Options::parseCommonsTreatment(const char* mode) +{ + if ( mode == NULL ) + throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; + + if ( strcmp(mode, "ignore_dylibs") == 0 ) + return kCommonsIgnoreDylibs; + else if ( strcmp(mode, "use_dylibs") == 0 ) + return kCommonsOverriddenByDylibs; + else if ( strcmp(mode, "error") == 0 ) + return kCommonsConflictsDylibsError; + else + throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; +} + +void Options::addDylibOverride(const char* paths) +{ + if ( paths == NULL ) + throw "-dylib_file must followed by two colon separated paths"; + const char* colon = strchr(paths, ':'); + if ( colon == NULL ) + throw "-dylib_file must followed by two colon separated paths"; + int len = colon-paths; + char* target = new char[len+2]; + strncpy(target, paths, len); + target[len] = '\0'; + DylibOverride entry; + entry.installName = target; + entry.useInstead = &colon[1]; + fDylibOverrides.push_back(entry); +} + +uint64_t Options::parseAddress(const char* addr) +{ + char* endptr; + uint64_t result = strtoull(addr, &endptr, 16); + return result; +} + +uint32_t Options::parseProtection(const char* prot) +{ + uint32_t result = 0; + for(const char* p = prot; *p != '\0'; ++p) { + switch(tolower(*p)) { + case 'r': + result |= VM_PROT_READ; + break; + case 'w': + result |= VM_PROT_WRITE; + break; + case 'x': + result |= VM_PROT_EXECUTE; + break; + case '-': + break; + default: + throwf("unknown -segprot lettter in %s", prot); + } + } + return result; +} + + + +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +// +uint32_t Options::parseVersionNumber(const char* versionString) +{ + unsigned long x = 0; + unsigned long y = 0; + unsigned long z = 0; + char* end; + x = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed version number: %s", versionString); + + return (x << 16) | ( y << 8 ) | z; +} + +static const char* cstringSymbolName(const char* orderFileString) +{ + char* result; + asprintf(&result, "cstring=%s", orderFileString); + // convert escaped characters + char* d = result; + for(const char* s=result; *s != '\0'; ++s, ++d) { + if ( *s == '\\' ) { + ++s; + switch ( *s ) { + case 'n': + *d = '\n'; + break; + case 't': + *d = '\t'; + break; + case 'v': + *d = '\v'; + break; + case 'b': + *d = '\b'; + break; + case 'r': + *d = '\r'; + break; + case 'f': + *d = '\f'; + break; + case 'a': + *d = '\a'; + break; + case '\\': + *d = '\\'; + break; + case '?': + *d = '\?'; + break; + case '\'': + *d = '\r'; + break; + case '\"': + *d = '\"'; + break; + case 'x': + // hexadecimal value of char + { + ++s; + char value = 0; + while ( isxdigit(*s) ) { + value *= 16; + if ( isdigit(*s) ) + value += (*s-'0'); + else + value += ((toupper(*s)-'A') + 10); + ++s; + } + *d = value; + } + break; + default: + if ( isdigit(*s) ) { + // octal value of char + char value = 0; + while ( isdigit(*s) ) { + value = (value << 3) + (*s-'0'); + ++s; + } + *d = value; + } + } + } + else { + *d = *s; + } + } + *d = '\0'; + return result; +} + +void Options::parseOrderFile(const char* path, bool cstring) +{ + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open order file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size+1); + if ( p == NULL ) + throwf("can't process order file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read order file: %s", path); + ::close(fd); + p[stat_buf.st_size] = '\n'; + + // parse into vector of pairs + char * const end = &p[stat_buf.st_size+1]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) || cstring ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (!cstring && (*s == '#')) ) { + bool wasComment = (*s == '#'); + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { + if ( fArchitecture == CPU_TYPE_POWERPC ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; + } + // if there is an architecture prefix, only use this symbol it if matches current arch + else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_POWERPC64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { + if ( fArchitecture == CPU_TYPE_I386 ) + symbolStart = &symbolStart[5]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) { + if ( fArchitecture == CPU_TYPE_X86_64 ) + symbolStart = &symbolStart[7]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "arm:", 4) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; + } + if ( symbolStart != NULL ) { + char* objFileName = NULL; + char* colon = strstr(symbolStart, ".o:"); + if ( colon != NULL ) { + colon[2] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[3]; + } + // trim leading spaces + while ( isspace(*symbolStart) ) + ++symbolStart; + Options::OrderedSymbol pair; + if ( cstring ) + pair.symbolName = cstringSymbolName(symbolStart); + else + pair.symbolName = symbolStart; + pair.objectFileName = objFileName; + fOrderedSymbols.push_back(pair); + } + symbolStart = NULL; + if ( wasComment ) + state = inComment; + else + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols +} + +void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) +{ + if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) { + parseOrderFile(path, true); + } + else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) { + warning("sorting of __literal[4,8,16] sections not supported"); + } + else { + // ignore section information and append all symbol names to global order file + parseOrderFile(path, false); + } +} + +void Options::addSection(const char* segment, const char* section, const char* path) +{ + if ( strlen(segment) > 16 ) + throw "-seccreate segment name max 16 chars"; + if ( strlen(section) > 16 ) { + char* tmp = strdup(section); + tmp[16] = '\0'; + warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp); + section = tmp; + } + + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open -sectcreate file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process -sectcreate file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read -sectcreate file: %s", path); + ::close(fd); + + // record section to create + ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; + fExtraSections.push_back(info); +} + +void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) +{ + if ( strlen(segment) > 16 ) + throw "-sectalign segment name max 16 chars"; + if ( strlen(section) > 16 ) + throw "-sectalign section name max 16 chars"; + + // argument to -sectalign is a hexadecimal number + char* endptr; + unsigned long value = strtoul(alignmentStr, &endptr, 16); + if ( *endptr != '\0') + throw "argument for -sectalign is not a hexadecimal number"; + if ( value > 0x8000 ) + throw "argument for -sectalign must be less than or equal to 0x8000"; + if ( value == 0 ) { + warning("zero is not a valid -sectalign"); + value = 1; + } + + // alignment is power of 2 (e.g. page alignment = 12) + uint8_t alignment = (uint8_t)__builtin_ctz(value); + if ( (unsigned long)(1 << alignment) != value ) { + warning("alignment for -sectalign %s %s is not a power of two, using 0x%X", + segment, section, 1 << alignment); + } + + SectionAlignment info = { segment, section, alignment }; + fSectionAlignments.push_back(info); +} + +void Options::addLibrary(const FileInfo& info) +{ + // if this library has already been added, don't add again (archives are automatically repeatedly searched) + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + if ( strcmp(info.path, fit->path) == 0 ) { + // if dylib is specified again but weak, record that it should be weak + if ( info.options.fWeakImport ) + fit->options.fWeakImport = true; + return; + } + } + // add to list + fInputFiles.push_back(info); +} + +void Options::warnObsolete(const char* arg) +{ + warning("option %s is obsolete and being ignored", arg); +} + + + + +// +// Process all command line arguments. +// +// The only error checking done here is that each option is valid and if it has arguments +// that they too are valid. +// +// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified, +// whichever was last on the command line is used. +// +// Error check for invalid combinations of options is done in checkIllegalOptionCombinations() +// +void Options::parse(int argc, const char* argv[]) +{ + // pass one builds search list from -L and -F options + this->buildSearchPaths(argc, argv); + + // reduce re-allocations + fInputFiles.reserve(32); + + // pass two parse all other options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + + if ( arg[0] == '-' ) { + + // Since we don't care about the files passed, just the option names, we do this here. + if (fPrintOptions) + fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); + + if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + // previously handled by buildSearchPaths() + } + // The one gnu style option we have to keep compatibility + // with gcc. Might as well have the single hyphen one as well. + else if ( (strcmp(arg, "--help") == 0) + || (strcmp(arg, "-help") == 0)) { + fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n"); + exit (0); + } + else if ( strcmp(arg, "-arch") == 0 ) { + parseArch(argv[++i]); + } + else if ( strcmp(arg, "-dynamic") == 0 ) { + // default + } + else if ( strcmp(arg, "-static") == 0 ) { + if ( fOutputKind != kObjectFile ) + fOutputKind = kStaticExecutable; + fReaderOptions.fForStatic = true; + } + else if ( strcmp(arg, "-dylib") == 0 ) { + fOutputKind = kDynamicLibrary; + } + else if ( strcmp(arg, "-bundle") == 0 ) { + fOutputKind = kDynamicBundle; + } + else if ( strcmp(arg, "-dylinker") == 0 ) { + fOutputKind = kDyld; + } + else if ( strcmp(arg, "-execute") == 0 ) { + if ( fOutputKind != kStaticExecutable ) + fOutputKind = kDynamicExecutable; + } + else if ( strcmp(arg, "-r") == 0 ) { + fOutputKind = kObjectFile; + } + else if ( strcmp(arg, "-o") == 0 ) { + fOutputFile = argv[++i]; + } + else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { + addLibrary(findLibrary(&arg[2])); + } + // This causes a dylib to be weakly bound at + // link time. This corresponds to weak_import. + else if ( strncmp(arg, "-weak-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7]); + info.options.fWeakImport = true; + addLibrary(info); + } + else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7], true); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } + // Avoid lazy binding. + // ??? Deprecate. + else if ( strcmp(arg, "-bind_at_load") == 0 ) { + fBindAtLoad = true; + } + else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { + fNameSpace = kTwoLevelNameSpace; + } + else if ( strcmp(arg, "-flat_namespace") == 0 ) { + fNameSpace = kFlatNameSpace; + } + // Also sets a bit to ensure dyld causes everything + // in the namespace to be flat. + // ??? Deprecate + else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { + fNameSpace = kForceFlatNameSpace; + } + // Similar to --whole-archive. + else if ( strcmp(arg, "-all_load") == 0 ) { + fReaderOptions.fFullyLoadArchives = true; + } + else if ( strcmp(arg, "-noall_load") == 0) { + warnObsolete(arg); + } + // Similar to -all_load + else if ( strcmp(arg, "-ObjC") == 0 ) { + fReaderOptions.fLoadAllObjcObjectsFromArchives = true; + } + // Library versioning. + else if ( (strcmp(arg, "-dylib_compatibility_version") == 0) + || (strcmp(arg, "-compatibility_version") == 0)) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-dylib_compatibility_version missing "; + fDylibCompatVersion = parseVersionNumber(vers); + } + else if ( (strcmp(arg, "-dylib_current_version") == 0) + || (strcmp(arg, "-current_version") == 0)) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-dylib_current_version missing "; + fDylibCurrentVersion = parseVersionNumber(vers); + } + else if ( strcmp(arg, "-sectorder") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectorder missing

"; + parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( strcmp(arg, "-order_file") == 0 ) { + parseOrderFile(argv[++i], false); + } + else if ( strcmp(arg, "-order_file_statistics") == 0 ) { + fPrintOrderFileStatistics = true; + } + // ??? Deprecate segcreate. + // -sectcreate puts whole files into a section in the output. + else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectcreate missing
"; + addSection(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + // Since we have a full path in binary/library names we need to be able to override it. + else if ( (strcmp(arg, "-dylib_install_name") == 0) + || (strcmp(arg, "-dylinker_install_name") == 0) + || (strcmp(arg, "-install_name") == 0)) { + fDylibInstallName = argv[++i]; + if ( fDylibInstallName == NULL ) + throw "-install_name missing "; + } + // Sets the base address of the output. + else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) { + const char* address = argv[++i]; + if ( address == NULL ) + throwf("%s missing
", arg); + fBaseAddress = parseAddress(address); + uint64_t temp = (fBaseAddress+4095) & (-4096); // page align + if ( fBaseAddress != temp ) { + warning("-seg1addr not page aligned, rounding up"); + fBaseAddress = temp; + } + } + else if ( strcmp(arg, "-e") == 0 ) { + fEntryName = argv[++i]; + } + // Same as -@ from the FSF linker. + else if ( strcmp(arg, "-filelist") == 0 ) { + const char* path = argv[++i]; + if ( (path == NULL) || (path[0] == '-') ) + throw "-filelist missing "; + loadFileList(path); + } + else if ( strcmp(arg, "-keep_private_externs") == 0 ) { + fKeepPrivateExterns = true; + } + else if ( strcmp(arg, "-final_output") == 0 ) { + fFinalName = argv[++i]; + } + // Ensure that all calls to exported symbols go through lazy pointers. Multi-module + // just ensures that this happens for cross object file boundaries. + else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { + switch ( fInterposeMode ) { + case kInterposeNone: + case kInterposeAllExternal: + fInterposeMode = kInterposeAllExternal; + break; + case kInterposeSome: + // do nothing, -interposable_list overrides -interposable" + break; + } + } + else if ( strcmp(arg, "-interposable_list") == 0 ) { + fInterposeMode = kInterposeSome; + loadExportFile(argv[++i], "-interposable_list", fInterposeList); + } + // Default for -interposable/-multi_module/-single_module. + else if ( strcmp(arg, "-single_module") == 0 ) { + fInterposeMode = kInterposeNone; + } + else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbols_list and -unexported_symbols_list"; + fExportMode = kExportSome; + loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); + } + else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -unexported_symbols_list and -exported_symbols_list"; + fExportMode = kDontExportSome; + loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); + } + else if ( strcmp(arg, "-exported_symbol") == 0 ) { + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbol and -unexported_symbols"; + fExportMode = kExportSome; + fExportSymbols.insert(argv[++i]); + } + else if ( strcmp(arg, "-unexported_symbol") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -unexported_symbol and -exported_symbol"; + fExportMode = kDontExportSome; + fDontExportSymbols.insert(argv[++i]); + } + else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { + if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude ) + throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; + fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; + loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); + } + else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { + if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude ) + throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; + fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; + loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); + } + // ??? Deprecate + else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { + fIgnoreOtherArchFiles = true; + } + else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { + fForceSubtypeAll = true; + } + // Similar to -weak-l but uses the absolute path name to the library. + else if ( strcmp(arg, "-weak_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fWeakImport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-lazy_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } + else if ( strcmp(arg, "-framework") == 0 ) { + addLibrary(findFramework(argv[++i])); + } + else if ( strcmp(arg, "-weak_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fWeakImport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-lazy_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } + else if ( strcmp(arg, "-search_paths_first") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-undefined") == 0 ) { + setUndefinedTreatment(argv[++i]); + } + // Debugging output flag. + else if ( strcmp(arg, "-arch_multiple") == 0 ) { + fMessagesPrefixedWithArchitecture = true; + } + // Specify what to do with relocations in read only + // sections like .text. Could be errors, warnings, + // or suppressed. Currently we do nothing with the + // flag. + else if ( strcmp(arg, "-read_only_relocs") == 0 ) { + switch ( parseTreatment(argv[++i]) ) { + case kNULL: + case kInvalid: + throw "-read_only_relocs missing [ warning | error | suppress ]"; + case kWarning: + fWarnTextRelocs = true; + fAllowTextRelocs = true; + break; + case kSuppress: + fWarnTextRelocs = false; + fAllowTextRelocs = true; + break; + case kError: + fWarnTextRelocs = false; + fAllowTextRelocs = false; + break; + } + } + else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { + warnObsolete(arg); + ++i; + } + // Warn, error or make strong a mismatch between weak + // and non-weak references. + else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { + setWeakReferenceMismatchTreatment(argv[++i]); + } + // For a deployment target of 10.3 and earlier ld64 will + // prebind an executable with 0s in all addresses that + // are prebound. This can then be fixed up by update_prebinding + // later. Prebinding is less useful on 10.4 and greater. + else if ( strcmp(arg, "-prebind") == 0 ) { + fPrebind = true; + } + else if ( strcmp(arg, "-noprebind") == 0 ) { + warnObsolete(arg); + fPrebind = false; + } + else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-nofixprebinding") == 0 ) { + warnObsolete(arg); + } + // This should probably be deprecated when we respect -L and -F + // when searching for libraries. + else if ( strcmp(arg, "-dylib_file") == 0 ) { + addDylibOverride(argv[++i]); + } + // What to expand @executable_path to if found in dependent dylibs + else if ( strcmp(arg, "-executable_path") == 0 ) { + fExecutablePath = argv[++i]; + if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') ) + throw "-executable_path missing "; + // if a directory was passed, add / to end + // ld64 can't find @executable _path relative dylibs from our umbrella frameworks + struct stat statBuffer; + if ( stat(fExecutablePath, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) { + char* pathWithSlash = new char[strlen(fExecutablePath)+2]; + strcpy(pathWithSlash, fExecutablePath); + strcat(pathWithSlash, "/"); + fExecutablePath = pathWithSlash; + } + } + } + // Aligns all segments to the power of 2 boundary specified. + else if ( strcmp(arg, "-segalign") == 0 ) { + warnObsolete(arg); + ++i; + } + // Puts a specified segment at a particular address that must + // be a multiple of the segment alignment. + else if ( strcmp(arg, "-segaddr") == 0 ) { + SegmentStart seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) ) + throw "-segaddr missing segName Adddress"; + seg.address = parseAddress(argv[++i]); + uint64_t temp = seg.address & (-4096); // page align + if ( (seg.address != temp) ) + warning("-segaddr %s not page aligned, rounding down", seg.name); + fCustomSegmentAddresses.push_back(seg); + } + // ??? Deprecate when we deprecate split-seg. + else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { + fBaseAddress = parseAddress(argv[++i]); + } + // ??? Deprecate when we deprecate split-seg. + else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { + fBaseWritableAddress = parseAddress(argv[++i]); + fSplitSegs = true; + } + // ??? Deprecate when we get rid of basing at build time. + else if ( strcmp(arg, "-seg_addr_table") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-seg_addr_table missing argument"; + fSegAddrTablePath = name; + } + else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-segprot") == 0 ) { + SegmentProtect seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) ) + throw "-segprot missing segName max-prot init-prot"; + seg.max = parseProtection(argv[++i]); + seg.init = parseProtection(argv[++i]); + fCustomSegmentProtections.push_back(seg); + } + else if ( strcmp(arg, "-pagezero_size") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-pagezero_size missing "; + fZeroPageSize = parseAddress(size); + uint64_t temp = fZeroPageSize & (-4096); // page align + if ( (fZeroPageSize != temp) ) + warning("-pagezero_size not page aligned, rounding down"); + fZeroPageSize = temp; + } + else if ( strcmp(arg, "-stack_addr") == 0 ) { + const char* address = argv[++i]; + if ( address == NULL ) + throw "-stack_addr missing
"; + fStackAddr = parseAddress(address); + } + else if ( strcmp(arg, "-stack_size") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-stack_size missing
"; + fStackSize = parseAddress(size); + uint64_t temp = fStackSize & (-4096); // page align + if ( (fStackSize != temp) ) + warning("-stack_size not page aligned, rounding down"); + } + else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { + fExecutableStack = true; + } + else if ( strcmp(arg, "-sectalign") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectalign missing
"; + addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( strcmp(arg, "-sectorder_detail") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { + warnObsolete(arg); + i += 2; + } + else if ( strcmp(arg, "-bundle_loader") == 0 ) { + fBundleLoader = argv[++i]; + if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) + throw "-bundle_loader missing "; + FileInfo info = findFile(fBundleLoader); + info.options.fBundleLoader = true; + fInputFiles.push_back(info); + } + else if ( strcmp(arg, "-private_bundle") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { + // FIX FIX + } + // Use this flag to set default behavior for deployement targets. + else if ( strcmp(arg, "-macosx_version_min") == 0 ) { + setMacOSXVersionMin(argv[++i]); + } + else if ( (strcmp(arg, "-aspen_version_min") == 0) || (strcmp(arg, "-iphone_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { + setIPhoneVersionMin(argv[++i]); + } + else if ( strcmp(arg, "-multiply_defined") == 0 ) { + //warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-nomultidefs") == 0 ) { + warnObsolete(arg); + } + // Display each file in which the argument symbol appears and whether + // the file defines or references it. This option takes an argument + // as -y note that there is no space. + else if ( strncmp(arg, "-y", 2) == 0 ) { + warnObsolete("-y"); + } + // Same output as -y, but output number of undefined symbols only. + else if ( strcmp(arg, "-Y") == 0 ) { + //warnObsolete(arg); + ++i; + } + // This option affects all objects linked into the final result. + else if ( strcmp(arg, "-m") == 0 ) { + warnObsolete(arg); + } + else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { + fReaderOptions.fWhyLoad = true; + } + else if ( strcmp(arg, "-why_live") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-why_live missing symbol name argument"; + fWhyLive.insert(name); + } + else if ( strcmp(arg, "-u") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-u missing argument"; + fInitialUndefines.push_back(name); + } + else if ( strcmp(arg, "-U") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-U missing argument"; + fAllowedUndefined.insert(name); + } + else if ( strcmp(arg, "-s") == 0 ) { + warnObsolete(arg); + fLocalSymbolHandling = kLocalSymbolsNone; + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + } + else if ( strcmp(arg, "-x") == 0 ) { + fLocalSymbolHandling = kLocalSymbolsNone; + } + else if ( strcmp(arg, "-S") == 0 ) { + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + } + else if ( strcmp(arg, "-X") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-Si") == 0 ) { + warnObsolete(arg); + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + } + else if ( strcmp(arg, "-b") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-Sn") == 0 ) { + warnObsolete(arg); + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + } + else if ( strcmp(arg, "-Sp") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-dead_strip") == 0 ) { + fDeadStrip = kDeadStripOnPlusUnusedInits; + } + else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { + fDeadStrip = kDeadStripOn; + } + else if ( strcmp(arg, "-w") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-M") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-headerpad") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-headerpad missing argument"; + fMinimumHeaderPad = parseAddress(size); + } + else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { + fMaxMinimumHeaderPad = true; + } + else if ( strcmp(arg, "-t") == 0 ) { + fReaderOptions.fLogAllFiles = true; + } + else if ( strcmp(arg, "-whatsloaded") == 0 ) { + fReaderOptions.fLogObjectFiles = true; + } + else if ( strcmp(arg, "-A") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-umbrella missing argument"; + fUmbrellaName = name; + } + else if ( strcmp(arg, "-allowable_client") == 0 ) { + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-allowable_client missing argument"; + + fAllowableClients.push_back(name); + } + else if ( strcmp(arg, "-client_name") == 0 ) { + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-client_name missing argument"; + + fClientName = name; + } + else if ( strcmp(arg, "-sub_umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_umbrella missing argument"; + fSubUmbellas.push_back(name); + } + else if ( strcmp(arg, "-sub_library") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_library missing argument"; + fSubLibraries.push_back(name); + } + else if ( strcmp(arg, "-init") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-init missing argument"; + fInitFunctionName = name; + } + else if ( strcmp(arg, "-dot") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dot missing argument"; + fDotOutputFile = name; + } + else if ( strcmp(arg, "-warn_commons") == 0 ) { + fWarnCommons = true; + } + else if ( strcmp(arg, "-commons") == 0 ) { + fCommonsMode = parseCommonsTreatment(argv[++i]); + } + else if ( strcmp(arg, "-keep_relocs") == 0 ) { + fKeepRelocations = true; + } + else if ( strcmp(arg, "-warn_stabs") == 0 ) { + fWarnStabs = true; + } + else if ( strcmp(arg, "-pause") == 0 ) { + fPause = true; + } + else if ( strcmp(arg, "-print_statistics") == 0 ) { + fStatistics = true; + } + else if ( strcmp(arg, "-d") == 0 ) { + fReaderOptions.fMakeTentativeDefinitionsReal = true; + } + else if ( strcmp(arg, "-v") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-Z") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-syslibroot") == 0 ) { + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-no_uuid") == 0 ) { + fUUIDMode = kUUIDNone; + } + else if ( strcmp(arg, "-random_uuid") == 0 ) { + fUUIDMode = kUUIDRandom; + } + else if ( strcmp(arg, "-dtrace") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dtrace missing argument"; + fDtraceScriptName = name; + } + else if ( strcmp(arg, "-root_safe") == 0 ) { + fReaderOptions.fRootSafe = true; + } + else if ( strcmp(arg, "-setuid_safe") == 0 ) { + fReaderOptions.fSetuidSafe = true; + } + else if ( strcmp(arg, "-alias") == 0 ) { + ObjectFile::ReaderOptions::AliasPair pair; + pair.realName = argv[++i]; + if ( pair.realName == NULL ) + throw "missing argument to -alias"; + pair.alias = argv[++i]; + if ( pair.alias == NULL ) + throw "missing argument to -alias"; + fReaderOptions.fAliases.push_back(pair); + } + else if ( strcmp(arg, "-alias_list") == 0 ) { + parseAliasFile(argv[++i]); + } + // put this last so that it does not interfer with other options starting with 'i' + else if ( strncmp(arg, "-i", 2) == 0 ) { + const char* colon = strchr(arg, ':'); + if ( colon == NULL ) + throwf("unknown option: %s", arg); + ObjectFile::ReaderOptions::AliasPair pair; + char* temp = new char[colon-arg]; + strlcpy(temp, &arg[2], colon-arg-1); + pair.realName = &colon[1]; + pair.alias = temp; + fReaderOptions.fAliases.push_back(pair); + } + else if ( strcmp(arg, "-save-temps") == 0 ) { + fSaveTempFiles = true; + } + else if ( strcmp(arg, "-rpath") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "missing argument to -rpath"; + fRPaths.push_back(path); + } + else if ( strcmp(arg, "-read_only_stubs") == 0 ) { + fReadOnlyx86Stubs = true; + } + else if ( strcmp(arg, "-slow_stubs") == 0 ) { + fReaderOptions.fSlowx86Stubs = true; + } + else if ( strcmp(arg, "-map") == 0 ) { + fMapPath = argv[++i]; + if ( fMapPath == NULL ) + throw "missing argument to -map"; + } + else if ( strcmp(arg, "-pie") == 0 ) { + fPositionIndependentExecutable = true; + } + else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { + FileInfo info = findLibrary(&arg[11], true); + info.options.fReExport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-reexport_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fReExport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-reexport_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fReExport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { + fDeadStripDylibs = true; + } + else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { + fReaderOptions.fImplicitlyLinkPublicDylibs = false; + } + else if ( strcmp(arg, "-new_linker") == 0 ) { + // ignore + } + else if ( strcmp(arg, "-no_encryption") == 0 ) { + fEncryptable = false; + } + else if ( strcmp(arg, "-mllvm") == 0 ) { + const char* opts = argv[++i]; + if ( opts == NULL ) + throw "missing argument to -mllvm"; + fLLVMOptions.push_back(opts); + } + else { + throwf("unknown option: %s", arg); + } + } + else { + FileInfo info = findFile(arg); + if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 ) + addLibrary(info); + else + fInputFiles.push_back(info); + } + } + + // if a -lazy option was used, implicitly link in lazydylib1.o + if ( fUsingLazyDylibLinking ) { + addLibrary(findLibrary("lazydylib1.o")); + } +} + + + +// +// -syslibroot is used for SDK support. +// The rule is that all search paths (both explicit and default) are +// checked to see if they exist in the SDK. If so, that path is +// replaced with the sdk prefixed path. If not, that search path +// is used as is. If multiple -syslibroot options are specified +// their directory structures are logically overlayed and files +// from sdks specified earlier on the command line used before later ones. + +void Options::buildSearchPaths(int argc, const char* argv[]) +{ + bool addStandardLibraryDirectories = true; + std::vector libraryPaths; + std::vector frameworkPaths; + libraryPaths.reserve(10); + frameworkPaths.reserve(10); + // scan through argv looking for -L, -F, -Z, and -syslibroot options + for(int i=0; i < argc; ++i) { + if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) + libraryPaths.push_back(&argv[i][2]); + else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) + frameworkPaths.push_back(&argv[i][2]); + else if ( strcmp(argv[i], "-Z") == 0 ) + addStandardLibraryDirectories = false; + else if ( strcmp(argv[i], "-v") == 0 ) { + fVerbose = true; + extern const char ldVersionString[]; + fprintf(stderr, "%s", ldVersionString); + // if only -v specified, exit cleanly + if ( argc == 2 ) { +#if LTO_SUPPORT + printLTOVersion(*this); +#endif + exit(0); + } + } + else if ( strcmp(argv[i], "-syslibroot") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-syslibroot missing argument"; + fSDKPaths.push_back(path); + } + else if ( strcmp(argv[i], "-search_paths_first") == 0 ) { + // ??? Deprecate when we get -Bstatic/-Bdynamic. + fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; + } + else if ( strcmp(argv[i], "-w") == 0 ) { + sEmitWarnings = false; + } + } + if ( addStandardLibraryDirectories ) { + libraryPaths.push_back("/usr/lib"); + libraryPaths.push_back("/usr/local/lib"); + + frameworkPaths.push_back("/Library/Frameworks/"); + frameworkPaths.push_back("/System/Library/Frameworks/"); + // remove /Network from default search path + //frameworkPaths.push_back("/Network/Library/Frameworks/"); + } + + // Support for configure based hacks + // if last -syslibroot is /, then ignore all syslibroots + if ( fSDKPaths.size() > 0 ) { + if ( strcmp(fSDKPaths.back(), "/") == 0 ) { + fSDKPaths.clear(); + } + } + + // now merge sdk and library paths to make real search paths + fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1)); + for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { + const char* libDir = *it; + bool sdkOverride = false; + if ( libDir[0] == '/' ) { + char betterLibDir[PATH_MAX]; + if ( strstr(libDir, "/..") != NULL ) { + if ( realpath(libDir, betterLibDir) != NULL ) + libDir = strdup(betterLibDir); + } + const int libDirLen = strlen(libDir); + for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + // ??? Should be using string here. + const char* sdkDir = *sdkit; + const int sdkDirLen = strlen(sdkDir); + char newPath[libDirLen + sdkDirLen+4]; + strcpy(newPath, sdkDir); + if ( newPath[sdkDirLen-1] == '/' ) + newPath[sdkDirLen-1] = '\0'; + strcat(newPath, libDir); + struct stat statBuffer; + if ( stat(newPath, &statBuffer) == 0 ) { + fLibrarySearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } + } + } + if ( !sdkOverride ) + fLibrarySearchPaths.push_back(libDir); + } + + // now merge sdk and framework paths to make real search paths + fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); + for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { + const char* frameworkDir = *it; + bool sdkOverride = false; + if ( frameworkDir[0] == '/' ) { + char betterFrameworkDir[PATH_MAX]; + if ( strstr(frameworkDir, "/..") != NULL ) { + if ( realpath(frameworkDir, betterFrameworkDir) != NULL ) + frameworkDir = strdup(betterFrameworkDir); + } + const int frameworkDirLen = strlen(frameworkDir); + for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + // ??? Should be using string here + const char* sdkDir = *sdkit; + const int sdkDirLen = strlen(sdkDir); + char newPath[frameworkDirLen + sdkDirLen+4]; + strcpy(newPath, sdkDir); + if ( newPath[sdkDirLen-1] == '/' ) + newPath[sdkDirLen-1] = '\0'; + strcat(newPath, frameworkDir); + struct stat statBuffer; + if ( stat(newPath, &statBuffer) == 0 ) { + fFrameworkSearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } + } + } + if ( !sdkOverride ) + fFrameworkSearchPaths.push_back(frameworkDir); + } + + if ( fVerbose ) { + fprintf(stderr,"Library search paths:\n"); + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) + fprintf(stderr,"\t%s\n", *it); + fprintf(stderr,"Framework search paths:\n"); + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) + fprintf(stderr,"\t%s\n", *it); + } +} + +// this is run before the command line is parsed +void Options::parsePreCommandLineEnvironmentSettings() +{ + if ((getenv("LD_TRACE_ARCHIVES") != NULL) + || (getenv("RC_TRACE_ARCHIVES") != NULL)) + fReaderOptions.fTraceArchives = true; + + if ((getenv("LD_TRACE_DYLIBS") != NULL) + || (getenv("RC_TRACE_DYLIBS") != NULL)) { + fReaderOptions.fTraceDylibs = true; + fReaderOptions.fTraceIndirectDylibs = true; + } + + if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { + fTraceDylibSearching = true; + } + + if (getenv("LD_PRINT_OPTIONS") != NULL) + fPrintOptions = true; + + if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) + fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); + + if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) + fPrintOrderFileStatistics = true; + + if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL) + fSplitSegs = true; + + if (getenv("LD_NO_ENCRYPT") != NULL) + fEncryptable = false; + + sWarningsSideFilePath = getenv("LD_WARN_FILE"); +} + + +// this is run after the command line is parsed +void Options::parsePostCommandLineEnvironmentSettings() +{ + // when building a dynamic main executable, default any use of @executable_path to output path + if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) { + fExecutablePath = fOutputFile; + } + + // allow build system to set default seg_addr_table + if ( fSegAddrTablePath == NULL ) + fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE"); + + // allow build system to turn on prebinding + if ( !fPrebind ) { + fPrebind = ( getenv("LD_PREBIND") != NULL ); + } + + // allow build system to force on dead-code-stripping + if ( fDeadStrip == kDeadStripOff ) { + if ( getenv("LD_DEAD_STRIP") != NULL ) { + switch (fOutputKind) { + case Options::kDynamicLibrary: + case Options::kDynamicExecutable: + case Options::kDynamicBundle: + fDeadStrip = kDeadStripOn; + break; + case Options::kObjectFile: + case Options::kDyld: + case Options::kStaticExecutable: + break; + } + } + } + + // allow build system to force on -warn_commons + if ( getenv("LD_WARN_COMMONS") != NULL ) + fWarnCommons = true; +} + +void Options::reconfigureDefaults() +{ + // sync reader options + switch ( fOutputKind ) { + case Options::kObjectFile: + fReaderOptions.fForFinalLinkedImage = false; + break; + case Options::kDyld: + fReaderOptions.fForDyld = true; + fReaderOptions.fForFinalLinkedImage = true; + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fReaderOptions.fForFinalLinkedImage = true; + break; + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fReaderOptions.fLinkingMainExecutable = true; + fReaderOptions.fForFinalLinkedImage = true; + break; + } + + // set default min OS version + if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) { + // if -macosx_version_min not used, try environment variable + const char* envVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + if ( envVers != NULL ) + setMacOSXVersionMin(envVers); + // if -macosx_version_min and environment variable not used assume current OS version + if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; // FIX FIX, this really should be a check of the OS version the linker is running on + } + + // adjust min based on architecture + switch ( fArchitecture ) { + case CPU_TYPE_I386: + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for i386"); + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + } + break; + case CPU_TYPE_POWERPC64: + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for ppc64"); + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + } + break; + case CPU_TYPE_X86_64: + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for x86_64"); + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + } + break; + } + + // disable implicit dylibs when targetting 10.3 + // add option to disable implicit load commands for indirectly used public dylibs + if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_3 ) + fReaderOptions.fImplicitlyLinkPublicDylibs = false; + + + // determine if info for shared region should be added + if ( fOutputKind == Options::kDynamicLibrary ) { + if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + if ( fArchitecture != CPU_TYPE_ARM ) + fSharedRegionEligible = true; + } + + // allow build system to force linker to ignore seg_addr_table + if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL ) + fSegAddrTablePath = NULL; + + // check for base address specified externally + if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) { + parseSegAddrTable(fSegAddrTablePath, this->installPath()); + // HACK to support seg_addr_table entries that are physical paths instead of install paths + if ( fBaseAddress == 0 ) { + if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib"); + + else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib"); + + else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib"); + } + } + + // split segs only allowed for dylibs + if ( fSplitSegs ) { + // split seg only supported for ppc, i386, and arm. + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + if ( fOutputKind != Options::kDynamicLibrary ) + fSplitSegs = false; + // make sure read and write segments are proper distance apart + if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) ) + fBaseWritableAddress = fBaseAddress + 0x10000000; + break; + case CPU_TYPE_ARM: + if ( fOutputKind != Options::kDynamicLibrary ) { + fSplitSegs = false; + } + else { + // make sure read and write segments are proper distance apart + if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) ) + fBaseWritableAddress = fBaseAddress + 0x08000000; + } + break; + default: + fSplitSegs = false; + fBaseAddress = 0; + fBaseWritableAddress = 0; + } + } + + // disable prebinding depending on arch and min OS version + if ( fPrebind ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::k10_4 ) { + // in 10.4 only split seg dylibs are prebound + if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) + fPrebind = false; + } + else if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { + // in 10.5 nothing is prebound + fPrebind = false; + } + else { + // in 10.3 and earlier only dylibs and main executables could be prebound + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + // only main executables and dylibs can be prebound + break; + case Options::kStaticExecutable: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + // disable prebinding for everything else + fPrebind = false; + break; + } + } + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + fPrebind = false; + break; + case CPU_TYPE_ARM: + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + // only main executables and dylibs can be prebound + break; + case Options::kStaticExecutable: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + // disable prebinding for everything else + fPrebind = false; + break; + } + break; + } + } + + // only prebound images can be split-seg + if ( fSplitSegs && !fPrebind ) + fSplitSegs = false; + + // figure out if module table is needed for compatibility with old ld/dyld + if ( fOutputKind == Options::kDynamicLibrary ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table + case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table + if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_5 ) + fNeedsModuleTable = true; + break; + case CPU_TYPE_ARM: + fNeedsModuleTable = true; // redo_prebinding requires a module table + break; + } + } + + // -r -x implies -S + if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + + // only ARM main executables can be encrypted + if ( fOutputKind != Options::kDynamicExecutable ) + fEncryptable = false; + if ( fArchitecture != CPU_TYPE_ARM ) + fEncryptable = false; +} + +void Options::checkIllegalOptionCombinations() +{ + // check -undefined setting + switch ( fUndefinedTreatment ) { + case kUndefinedError: + case kUndefinedDynamicLookup: + // always legal + break; + case kUndefinedWarning: + case kUndefinedSuppress: + // requires flat namespace + if ( fNameSpace == kTwoLevelNameSpace ) + throw "can't use -undefined warning or suppress with -twolevel_namespace"; + break; + } + + // unify -sub_umbrella with dylibs + for (std::vector::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) { + const char* subUmbrella = *it; + bool found = false; + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + Options::FileInfo& info = *fit; + const char* lastSlash = strrchr(info.path, '/'); + if ( lastSlash == NULL ) + lastSlash = info.path - 1; + if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { + info.options.fReExport = true; + found = true; + break; + } + } + if ( ! found ) + warning("-sub_umbrella %s does not match a supplied dylib", subUmbrella); + } + + // unify -sub_library with dylibs + for (std::vector::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) { + const char* subLibrary = *it; + bool found = false; + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + Options::FileInfo& info = *fit; + const char* lastSlash = strrchr(info.path, '/'); + if ( lastSlash == NULL ) + lastSlash = info.path - 1; + const char* dot = strchr(&lastSlash[1], '.'); + if ( dot == NULL ) + dot = &lastSlash[strlen(lastSlash)]; + if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { + info.options.fReExport = true; + found = true; + break; + } + } + if ( ! found ) + warning("-sub_library %s does not match a supplied dylib", subLibrary); + } + + // sync reader options + if ( fNameSpace != kTwoLevelNameSpace ) + fReaderOptions.fFlatNamespace = true; + + // check -stack_addr + if ( fStackAddr != 0 ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + case CPU_TYPE_ARM: + if ( fStackAddr > 0xFFFFFFFF ) + throw "-stack_addr must be < 4G for 32-bit processes"; + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + break; + } + if ( (fStackAddr & -4096) != fStackAddr ) + throw "-stack_addr must be multiples of 4K"; + if ( fStackSize == 0 ) + throw "-stack_addr must be used with -stack_size"; + } + + // check -stack_size + if ( fStackSize != 0 ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackAddr == 0 ) { + fStackAddr = 0xC0000000; + } + if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) + warning("custom stack placement overlaps and will disable shared region"); + break; + case CPU_TYPE_ARM: + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackAddr == 0 ) + fStackAddr = 0x30000000; + if ( fStackAddr > 0x40000000) + throw "-stack_addr must be < 1G for arm"; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + if ( fStackAddr == 0 ) { + fStackAddr = 0x00007FFF5C000000LL; + } + break; + } + if ( (fStackSize & -4096) != fStackSize ) + throw "-stack_size must be multiples of 4K"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // custom stack size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-stack_size option can only be used when linking a main executable"; + } + } + + // check that -allow_stack_execute is only used with main executables + if ( fExecutableStack ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // -allow_stack_execute size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-allow_stack_execute option can only be used when linking a main executable"; + } + } + + // check -client_name is only used when making a bundle or main executable + if ( fClientName != NULL ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicBundle: + break; + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kObjectFile: + case Options::kDyld: + throw "-client_name can only be used with -bundle"; + } + } + + // check -init is only used when building a dylib + if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) + throw "-init can only be used with -dynamiclib"; + + // check -bundle_loader only used with -bundle + if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) ) + throw "-bundle_loader can only be used with -bundle"; + + // check -dtrace not used with -r + if ( (fDtraceScriptName != NULL) && (fOutputKind == Options::kObjectFile) ) + throw "-dtrace can only be used when creating final linked images"; + + // check -d can only be used with -r + if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) + throw "-d can only be used with -r"; + + // check that -root_safe is not used with -r + if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) ) + throw "-root_safe cannot be used with -r"; + + // check that -setuid_safe is not used with -r + if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) ) + throw "-setuid_safe cannot be used with -r"; + + // make sure all required exported symbols exist + std::vector impliedExports; + for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) { + const char* name = *it; + // never export .eh symbols + const int len = strlen(name); + if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) + warning("ignoring %s in export list", name); + else + fInitialUndefines.push_back(name); + if ( strncmp(name, ".objc_class_name_", 17) == 0 ) { + // rdar://problem/4718189 map ObjC class names to new runtime names + switch (fArchitecture) { + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: + char* temp; + asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + break; + } + } + } + for (std::vector::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) { + const char* name = *it; + fExportSymbols.insert(name); + fInitialUndefines.push_back(name); + } + + // make sure that -init symbol exist + if ( fInitFunctionName != NULL ) + fInitialUndefines.push_back(fInitFunctionName); + + // check custom segments + if ( fCustomSegmentAddresses.size() != 0 ) { + // verify no segment is in zero page + if ( fZeroPageSize != ULLONG_MAX ) { + for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( (it->address >= 0) && (it->address < fZeroPageSize) ) + throwf("-segaddr %s 0x%X conflicts with -pagezero_size", it->name, it->address); + } + } + // verify no duplicates + for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + for (std::vector::iterator it2 = fCustomSegmentAddresses.begin(); it2 != fCustomSegmentAddresses.end(); ++it2) { + if ( (it->address == it2->address) && (it != it2) ) + throwf("duplicate -segaddr addresses for %s and %s", it->name, it2->name); + } + // a custom segment address of zero will disable the use of a zero page + if ( it->address == 0 ) + fZeroPageSize = 0; + } + } + + if ( fZeroPageSize == ULLONG_MAX ) { + // zero page size not specified on command line, set default + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + case CPU_TYPE_ARM: + // first 4KB for 32-bit architectures + fZeroPageSize = 0x1000; + break; + case CPU_TYPE_POWERPC64: + // first 4GB for ppc64 on 10.5 + if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + fZeroPageSize = 0x100000000ULL; + else + fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page + break; + case CPU_TYPE_X86_64: + // first 4GB for x86_64 on all OS's + fZeroPageSize = 0x100000000ULL; + break; + default: + // if -arch not used, default to 4K zero-page + fZeroPageSize = 0x1000; + } + } + else { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // -pagezero_size size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + if ( fZeroPageSize != 0 ) + throw "-pagezero_size option can only be used when linking a main executable"; + } + } + + // -dead_strip and -r are incompatible + if ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) ) + throw "-r and -dead_strip cannot be used together"; + + // can't use -rpath unless targeting 10.5 or later + if ( fRPaths.size() > 0 ) { + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) + throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + throw "-rpath can only be used when creating a dynamic final linked image"; + } + } + + // check -pie is only used when building a dynamic main executable for 10.5 + if ( fPositionIndependentExecutable ) { + if ( fOutputKind != Options::kDynamicExecutable ) + throw "-pie can only be used when linking a main executable"; + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) + throw "-pie can only be used when targeting Mac OS X 10.5 or later"; + } +} + + + +void Options::checkForClassic(int argc, const char* argv[]) +{ + // scan options + bool archFound = false; + bool staticFound = false; + bool dtraceFound = false; + bool rFound = false; + bool creatingMachKernel = false; + bool newLinker = false; + + for(int i=0; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-arch") == 0 ) { + parseArch(argv[++i]); + archFound = true; + } + else if ( strcmp(arg, "-static") == 0 ) { + staticFound = true; + } + else if ( strcmp(arg, "-dtrace") == 0 ) { + dtraceFound = true; + } + else if ( strcmp(arg, "-r") == 0 ) { + rFound = true; + } + else if ( strcmp(arg, "-new_linker") == 0 ) { + newLinker = true; + } + else if ( strcmp(arg, "-classic_linker") == 0 ) { + // ld_classic does not understand this option, so remove it + for(int j=i; j < argc; ++j) + argv[j] = argv[j+1]; + this->gotoClassicLinker(argc-1, argv); + } + else if ( strcmp(arg, "-o") == 0 ) { + const char* outfile = argv[++i]; + if ( (outfile != NULL) && (strstr(outfile, "/mach_kernel") != NULL) ) + creatingMachKernel = true; + } + } + } + + // -dtrace only supported by new linker + if( dtraceFound ) + return; + + if( archFound ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + case CPU_TYPE_ARM: +// if ( staticFound && (rFound || !creatingMachKernel) ) { + if ( staticFound && !newLinker ) { + // this environment variable will disable use of ld_classic for -static links + if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { + // ld_classic does not support -aspen_version_min, so change + for(int j=0; j < argc; ++j) { + if ( (strcmp(argv[j], "-aspen_version_min") == 0) + || (strcmp(argv[j], "-iphone_version_min") == 0) + || (strcmp(argv[j], "-iphoneos_version_min") == 0) ) { + argv[j] = "-macosx_version_min"; + if ( j < argc-1 ) + argv[j+1] = "10.5"; + break; + } + } + this->gotoClassicLinker(argc, argv); + } + } + break; + } + } + else { + // work around for VSPTool + if ( staticFound ) + this->gotoClassicLinker(argc, argv); + } + +} + +void Options::gotoClassicLinker(int argc, const char* argv[]) +{ + argv[0] = "ld_classic"; + execvp(argv[0], (char**)argv); + fprintf(stderr, "can't exec ld_classic\n"); + exit(1); +} diff --git a/ld64/FireOpal/src/Options.h b/ld64/FireOpal/src/Options.h new file mode 100644 index 0000000..4bbcffe --- /dev/null +++ b/ld64/FireOpal/src/Options.h @@ -0,0 +1,368 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OPTIONS__ +#define __OPTIONS__ + + +#include +#include + +#include +#include + +#include "ObjectFile.h" + +extern void throwf (const char* format, ...) __attribute__ ((noreturn)); +extern void warning(const char* format, ...); + +class DynamicLibraryOptions +{ +public: + DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false) {} + + bool fWeakImport; + bool fReExport; + bool fBundleLoader; + bool fLazyLoad; +}; + +// +// The public interface to the Options class is the abstract representation of what work the linker +// should do. +// +// This abstraction layer will make it easier to support a future where the linker is a shared library +// invoked directly from Xcode. The target settings in Xcode would be used to directly construct an Options +// object (without building a command line which is then parsed). +// +// +class Options +{ +public: + Options(int argc, const char* argv[]); + ~Options(); + + enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld }; + enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace }; + // Standard treatment for many options. + enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid }; + enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup }; + enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, + kWeakReferenceMismatchNonWeak }; + enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; + enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; + enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; + enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; + + struct FileInfo { + const char* path; + uint64_t fileLen; + time_t modTime; + DynamicLibraryOptions options; + }; + + struct ExtraSection { + const char* segmentName; + const char* sectionName; + const char* path; + const uint8_t* data; + uint64_t dataLen; + }; + + struct SectionAlignment { + const char* segmentName; + const char* sectionName; + uint8_t alignment; + }; + + struct OrderedSymbol { + const char* symbolName; + const char* objectFileName; + }; + + struct SegmentStart { + const char* name; + uint64_t address; + }; + + struct SegmentProtect { + const char* name; + uint32_t max; + uint32_t init; + }; + + struct DylibOverride { + const char* installName; + const char* useInstead; + }; + + + const ObjectFile::ReaderOptions& readerOptions(); + const char* getOutputFilePath(); + std::vector& getInputFiles(); + + cpu_type_t architecture() { return fArchitecture; } + bool preferSubArchitecture() { return fHasPreferredSubType; } + cpu_subtype_t subArchitecture() { return fSubArchitecture; } + OutputKind outputKind(); + bool prebind(); + bool bindAtLoad(); + bool fullyLoadArchives(); + NameSpace nameSpace(); + const char* installPath(); // only for kDynamicLibrary + uint32_t currentVersion(); // only for kDynamicLibrary + uint32_t compatibilityVersion(); // only for kDynamicLibrary + const char* entryName(); // only for kDynamicExecutable or kStaticExecutable + const char* executablePath(); + uint64_t baseAddress(); + bool keepPrivateExterns(); // only for kObjectFile + bool needsModuleTable(); // only for kDynamicLibrary + bool interposable(const char* name); + bool hasExportRestrictList(); + bool hasExportMaskList(); + bool hasWildCardExportRestrictList(); + bool allGlobalsAreDeadStripRoots(); + bool shouldExport(const char*); + bool ignoreOtherArchInputFiles(); + bool forceCpuSubtypeAll(); + bool traceDylibs(); + bool traceArchives(); + DeadStripMode deadStrip(); + UndefinedTreatment undefinedTreatment(); + ObjectFile::ReaderOptions::VersionMin macosxVersionMin(); + bool messagesPrefixedWithArchitecture(); + Treatment picTreatment(); + WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); + const char* umbrellaName(); + std::vector& allowableClients(); + const char* clientName(); + const char* initFunctionName(); // only for kDynamicLibrary + const char* dotOutputFile(); + uint64_t zeroPageSize(); + bool hasCustomStack(); + uint64_t customStackSize(); + uint64_t customStackAddr(); + bool hasExecutableStack(); + std::vector& initialUndefines(); + bool printWhyLive(const char* name); + uint32_t minimumHeaderPad(); + bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; } + std::vector& extraSections(); + std::vector& sectionAlignments(); + CommonsMode commonsMode(); + bool warnCommons(); + bool keepRelocations(); + FileInfo findFile(const char* path); + UUIDMode getUUIDMode() { return fUUIDMode; } + bool warnStabs(); + bool pauseAtEnd() { return fPause; } + bool printStatistics() { return fStatistics; } + bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } + void gotoClassicLinker(int argc, const char* argv[]); + bool sharedRegionEligible() { return fSharedRegionEligible; } + bool printOrderFileStatistics() { return fPrintOrderFileStatistics; } + const char* dTraceScriptName() { return fDtraceScriptName; } + bool dTrace() { return (fDtraceScriptName != NULL); } + std::vector& orderedSymbols() { return fOrderedSymbols; } + bool splitSeg() { return fSplitSegs; } + uint64_t baseWritableAddress() { return fBaseWritableAddress; } + std::vector& customSegmentAddresses() { return fCustomSegmentAddresses; } + std::vector& customSegmentProtections() { return fCustomSegmentProtections; } + bool saveTempFiles() { return fSaveTempFiles; } + const std::vector& rpaths() { return fRPaths; } + bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; } + bool slowx86Stubs() { return fReaderOptions.fSlowx86Stubs; } + std::vector& dylibOverrides() { return fDylibOverrides; } + const char* generatedMapPath() { return fMapPath; } + bool positionIndependentExecutable() { return fPositionIndependentExecutable; } + Options::FileInfo findFileUsingPaths(const char* path); + bool deadStripDylibs() { return fDeadStripDylibs; } + bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } + bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); } + LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; } + bool keepLocalSymbol(const char* symbolName); + bool allowTextRelocs() { return fAllowTextRelocs; } + bool warnAboutTextRelocs() { return fWarnTextRelocs; } + bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; } + bool verbose() { return fVerbose; } + bool makeEncryptable() { return fEncryptable; } + std::vector& llvmOptions() { return fLLVMOptions; } + +private: + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; + enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; + enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome }; + + class SetWithWildcards { + public: + void insert(const char*); + bool contains(const char*); + bool hasWildCards() { return !fWildCard.empty(); } + NameSet::iterator regularBegin() { return fRegular.begin(); } + NameSet::iterator regularEnd() { return fRegular.end(); } + private: + static bool hasWildCards(const char*); + bool wildCardMatch(const char* pattern, const char* candidate); + bool inCharRange(const char*& range, unsigned char c); + + NameSet fRegular; + std::vector fWildCard; + }; + + + void parse(int argc, const char* argv[]); + void checkIllegalOptionCombinations(); + void buildSearchPaths(int argc, const char* argv[]); + void parseArch(const char* architecture); + FileInfo findLibrary(const char* rootName, bool dylibsOnly=false); + FileInfo findFramework(const char* frameworkName); + FileInfo findFramework(const char* rootName, const char* suffix); + bool checkForFile(const char* format, const char* dir, const char* rootName, + FileInfo& result); + uint32_t parseVersionNumber(const char*); + void parseSectionOrderFile(const char* segment, const char* section, const char* path); + void parseOrderFile(const char* path, bool cstring); + void addSection(const char* segment, const char* section, const char* path); + void addSubLibrary(const char* name); + void loadFileList(const char* fileOfPaths); + uint64_t parseAddress(const char* addr); + void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set); + void parseAliasFile(const char* fileOfAliases); + void parsePreCommandLineEnvironmentSettings(); + void parsePostCommandLineEnvironmentSettings(); + void setUndefinedTreatment(const char* treatment); + void setMacOSXVersionMin(const char* version); + void setIPhoneVersionMin(const char* version); + void setWeakReferenceMismatchTreatment(const char* treatment); + void addDylibOverride(const char* paths); + void addSectionAlignment(const char* segment, const char* section, const char* alignment); + CommonsMode parseCommonsTreatment(const char* mode); + Treatment parseTreatment(const char* treatment); + void reconfigureDefaults(); + void checkForClassic(int argc, const char* argv[]); + void parseSegAddrTable(const char* segAddrPath, const char* installPath); + void addLibrary(const FileInfo& info); + void warnObsolete(const char* arg); + uint32_t parseProtection(const char* prot); + + + ObjectFile::ReaderOptions fReaderOptions; + const char* fOutputFile; + std::vector fInputFiles; + cpu_type_t fArchitecture; + cpu_subtype_t fSubArchitecture; + OutputKind fOutputKind; + bool fHasPreferredSubType; + bool fPrebind; + bool fBindAtLoad; + bool fKeepPrivateExterns; + bool fNeedsModuleTable; + bool fIgnoreOtherArchFiles; + bool fForceSubtypeAll; + InterposeMode fInterposeMode; + DeadStripMode fDeadStrip; + NameSpace fNameSpace; + uint32_t fDylibCompatVersion; + uint32_t fDylibCurrentVersion; + const char* fDylibInstallName; + const char* fFinalName; + const char* fEntryName; + uint64_t fBaseAddress; + uint64_t fBaseWritableAddress; + bool fSplitSegs; + SetWithWildcards fExportSymbols; + SetWithWildcards fDontExportSymbols; + SetWithWildcards fInterposeList; + ExportMode fExportMode; + LibrarySearchMode fLibrarySearchMode; + UndefinedTreatment fUndefinedTreatment; + bool fMessagesPrefixedWithArchitecture; + WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment; + std::vector fSubUmbellas; + std::vector fSubLibraries; + std::vector fAllowableClients; + std::vector fRPaths; + const char* fClientName; + const char* fUmbrellaName; + const char* fInitFunctionName; + const char* fDotOutputFile; + const char* fExecutablePath; + const char* fBundleLoader; + const char* fDtraceScriptName; + const char* fSegAddrTablePath; + const char* fMapPath; + uint64_t fZeroPageSize; + uint64_t fStackSize; + uint64_t fStackAddr; + bool fExecutableStack; + uint32_t fMinimumHeaderPad; + CommonsMode fCommonsMode; + UUIDMode fUUIDMode; + SetWithWildcards fLocalSymbolsIncluded; + SetWithWildcards fLocalSymbolsExcluded; + LocalSymbolHandling fLocalSymbolHandling; + bool fWarnCommons; + bool fVerbose; + bool fKeepRelocations; + bool fWarnStabs; + bool fTraceDylibSearching; + bool fPause; + bool fStatistics; + bool fPrintOptions; + bool fSharedRegionEligible; + bool fPrintOrderFileStatistics; + bool fReadOnlyx86Stubs; + bool fPositionIndependentExecutable; + bool fMaxMinimumHeaderPad; + bool fDeadStripDylibs; + bool fAllowTextRelocs; + bool fWarnTextRelocs; + bool fUsingLazyDylibLinking; + bool fEncryptable; + std::vector fInitialUndefines; + NameSet fAllowedUndefined; + NameSet fWhyLive; + std::vector fExtraSections; + std::vector fSectionAlignments; + std::vector fOrderedSymbols; + std::vector fCustomSegmentAddresses; + std::vector fCustomSegmentProtections; + std::vector fDylibOverrides; + std::vector fLLVMOptions; + std::vector fLibrarySearchPaths; + std::vector fFrameworkSearchPaths; + std::vector fSDKPaths; + bool fSaveTempFiles; +}; + + + +#endif // __OPTIONS__ diff --git a/ld64/FireOpal/src/SectCreate.h b/ld64/FireOpal/src/SectCreate.h new file mode 100644 index 0000000..d5c7f4e --- /dev/null +++ b/ld64/FireOpal/src/SectCreate.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __SECTCREATE__ +#define __SECTCREATE__ + + +#include "ObjectFile.h" + +namespace SectCreate { + + extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); + +}; + + +#endif + + + + diff --git a/ld64/FireOpal/src/debugline.c b/ld64/FireOpal/src/debugline.c new file mode 100644 index 0000000..ff0e1d9 --- /dev/null +++ b/ld64/FireOpal/src/debugline.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef KLD +#include +#include +#include +#include +#include "dwarf2.h" +#include "debugline.h" + +struct line_reader_data +{ + bool little_endian; + + /* From the line number information header. */ + uint8_t minimum_instruction_length; + int8_t line_base; + uint8_t line_range; + uint8_t opcode_base; + const uint8_t * standard_opcode_lengths; + size_t numdir; + const uint8_t * * dirnames; + size_t numfile_orig; + size_t numfile; /* As updated during execution of the table. */ + const uint8_t * * filenames; + + /* Current position in the line table. */ + const uint8_t * cpos; + /* End of this part of the line table. */ + const uint8_t * end; + /* Start of the line table. */ + const uint8_t * init; + + struct line_info cur; +}; + +/* Read in a word of fixed size, which may be unaligned, in the + appropriate endianness. */ +#define read_16(p) (lnd->little_endian \ + ? ((p)[1] << 8 | (p)[0]) \ + : ((p)[0] << 8 | (p)[1])) +#define read_32(p) (lnd->little_endian \ + ? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \ + : ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])) +#define read_64(p) (lnd->little_endian \ + ? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \ + | (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \ + | (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \ + | (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \ + : ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \ + | (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \ + | (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \ + | (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7])) + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (struct line_reader_data * leb) +{ + while (leb->cpos != leb->end && *leb->cpos >= 0x80) + leb->cpos++; + if (leb->cpos != leb->end) + leb->cpos++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (struct line_reader_data * leb) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (leb->cpos == leb->end) + return (uint64_t) -1; + + b = *leb->cpos & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*leb->cpos++ >= 0x80); + return result; +} + + +/* Read a SLEB128 into a 64-bit word. Return 0 on overflow or error + (which is not very helpful). On overflow, skip past the rest of + the SLEB128. For negative numbers, this actually overflows when + under -2^62, but since this is used for line numbers that ought to + be OK... */ +static int64_t +read_sleb128 (struct line_reader_data * leb) +{ + const uint8_t * start_pos = leb->cpos; + uint64_t v = read_uleb128 (leb); + uint64_t signbit; + + if (v >= 1ull << 63) + return 0; + if (leb->cpos - start_pos > 9) + return v; + + signbit = 1ull << ((leb->cpos - start_pos) * 7 - 1); + + return v | -(v & signbit); +} + +/* Free a line_reader_data structure. */ +void +line_free (struct line_reader_data * lnd) +{ + if (! lnd) + return; + if (lnd->dirnames) + free (lnd->dirnames); + if (lnd->filenames) + free (lnd->filenames); + free (lnd); +} + +/* Return the pathname of the file in S, or NULL on error. + The result will have been allocated with malloc. */ + +char * +line_file (struct line_reader_data *lnd, uint64_t n) +{ + const uint8_t * prev_pos = lnd->cpos; + size_t filelen, dirlen; + uint64_t dir; + char * result; + + /* I'm not sure if this is actually an error. */ + if (n == 0 + || n > lnd->numfile) + return NULL; + + filelen = strlen ((const char *)lnd->filenames[n - 1]); + lnd->cpos = lnd->filenames[n - 1] + filelen + 1; + dir = read_uleb128 (lnd); + lnd->cpos = prev_pos; + if (dir == 0 + || lnd->filenames[n - 1][0] == '/') + return strdup ((const char *)lnd->filenames[n - 1]); + else if (dir > lnd->numdir) + return NULL; + + dirlen = strlen ((const char *) lnd->dirnames[dir - 1]); + result = malloc (dirlen + filelen + 2); + memcpy (result, lnd->dirnames[dir - 1], dirlen); + result[dirlen] = '/'; + memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen); + result[dirlen + 1 + filelen] = '\0'; + return result; +} + +/* Initialize a state S. Return FALSE on error. */ + +static void +init_state (struct line_info *s) +{ + s->file = 1; + s->line = 1; + s->col = 0; + s->pc = 0; + s->end_of_sequence = false; +} + +/* Read a debug_line section. */ + +struct line_reader_data * +line_open (const uint8_t * debug_line, size_t debug_line_size, + int little_endian) +{ + struct line_reader_data * lnd = NULL; + bool dwarf_size_64; + + uint64_t lnd_length, header_length; + const uint8_t * table_start; + + if (debug_line_size < 12) + return NULL; + + lnd = malloc (sizeof (struct line_reader_data)); + if (! lnd) + goto error; + + lnd->little_endian = little_endian; + lnd->cpos = debug_line; + + lnd_length = read_32 (lnd->cpos); + lnd->cpos += 4; + if (lnd_length == 0xffffffff) + { + lnd_length = read_64 (lnd->cpos); + lnd->cpos += 8; + dwarf_size_64 = true; + } + else if (lnd_length > 0xfffffff0) + /* Not a format we understand. */ + goto error; + else + dwarf_size_64 = false; + + if (debug_line_size < lnd_length + (dwarf_size_64 ? 12 : 4) + || lnd_length < (dwarf_size_64 ? 15 : 11)) + /* Too small. */ + goto error; + + if (read_16 (lnd->cpos) != 2) + /* Unknown line number format. */ + goto error; + lnd->cpos += 2; + + header_length = dwarf_size_64 ? (uint64_t)read_64(lnd->cpos) : (uint64_t)read_32(lnd->cpos); + lnd->cpos += dwarf_size_64 ? 8 : 4; + if (lnd_length < header_length + (lnd->cpos - debug_line) + || header_length < 7) + goto error; + + lnd->minimum_instruction_length = lnd->cpos[0]; + /* Ignore default_is_stmt. */ + lnd->line_base = lnd->cpos[2]; + lnd->line_range = lnd->cpos[3]; + lnd->opcode_base = lnd->cpos[4]; + + if (lnd->opcode_base == 0) + /* Every valid line number program must use at least opcode 0 + for DW_LNE_end_sequence. */ + goto error; + + lnd->standard_opcode_lengths = lnd->cpos + 5; + if (header_length < (uint64_t)(5 + (lnd->opcode_base - 1))) + /* Header not long enough. */ + goto error; + lnd->cpos += 5 + lnd->opcode_base - 1; + lnd->end = debug_line + header_length + (dwarf_size_64 ? 22 : 10); + + /* Make table of offsets to directory names. */ + table_start = lnd->cpos; + lnd->numdir = 0; + while (lnd->cpos != lnd->end && *lnd->cpos) + { + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); + if (! lnd->cpos) + goto error; + lnd->cpos++; + lnd->numdir++; + } + if (lnd->cpos == lnd->end) + goto error; + lnd->dirnames = malloc (lnd->numdir * sizeof (const uint8_t *)); + if (! lnd->dirnames) + goto error; + lnd->numdir = 0; + lnd->cpos = table_start; + while (*lnd->cpos) + { + lnd->dirnames[lnd->numdir++] = lnd->cpos; + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; + } + lnd->cpos++; + + /* Make table of offsets to file entries. */ + table_start = lnd->cpos; + lnd->numfile = 0; + while (lnd->cpos != lnd->end && *lnd->cpos) + { + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); + if (! lnd->cpos) + goto error; + lnd->cpos++; + skip_leb128 (lnd); + skip_leb128 (lnd); + skip_leb128 (lnd); + lnd->numfile++; + } + if (lnd->cpos == lnd->end) + goto error; + lnd->filenames = malloc (lnd->numfile * sizeof (const uint8_t *)); + if (! lnd->filenames) + goto error; + lnd->numfile = 0; + lnd->cpos = table_start; + while (*lnd->cpos) + { + lnd->filenames[lnd->numfile++] = lnd->cpos; + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; + skip_leb128 (lnd); + skip_leb128 (lnd); + skip_leb128 (lnd); + } + lnd->cpos++; + + lnd->numfile_orig = lnd->numfile; + lnd->cpos = lnd->init = lnd->end; + lnd->end = debug_line + lnd_length + (dwarf_size_64 ? 12 : 4); + + init_state (&lnd->cur); + + return lnd; + + error: + line_free (lnd); + return NULL; +} + +/* Reset back to the beginning. */ +void +line_reset (struct line_reader_data * lnd) +{ + lnd->cpos = lnd->init; + lnd->numfile = lnd->numfile_orig; + init_state (&lnd->cur); +} + +/* Is there no more line data available? */ +int +line_at_eof (struct line_reader_data * lnd) +{ + return lnd->cpos == lnd->end; +} + +static bool +next_state (struct line_reader_data *lnd) +{ + if (lnd->cur.end_of_sequence) + init_state (&lnd->cur); + + for (;;) + { + uint8_t op; + uint64_t tmp; + + if (lnd->cpos == lnd->end) + return false; + op = *lnd->cpos++; + if (op >= lnd->opcode_base) + { + op -= lnd->opcode_base; + + lnd->cur.line += op % lnd->line_range + lnd->line_base; + lnd->cur.pc += (op / lnd->line_range + * lnd->minimum_instruction_length); + return true; + } + else switch (op) + { + case DW_LNS_extended_op: + { + uint64_t sz = read_uleb128 (lnd); + const uint8_t * op = lnd->cpos; + + if ((uint64_t)(lnd->end - op) < sz || sz == 0) + return false; + lnd->cpos += sz; + switch (*op++) + { + case DW_LNE_end_sequence: + lnd->cur.end_of_sequence = true; + return true; + + case DW_LNE_set_address: + if (sz == 9) + lnd->cur.pc = read_64 (op); + else if (sz == 5) + lnd->cur.pc = read_32 (op); + else + return false; + break; + + case DW_LNE_define_file: + { + const uint8_t * * filenames; + filenames = realloc + (lnd->filenames, + (lnd->numfile + 1) * sizeof (const uint8_t *)); + if (! filenames) + return false; + /* Check for zero-termination. */ + if (! memchr (op, 0, lnd->cpos - op)) + return false; + filenames[lnd->numfile++] = op; + lnd->filenames = filenames; + + /* There's other data here, like file sizes and modification + times, but we don't need to read it so skip it. */ + } + break; + + default: + /* Don't understand it, so skip it. */ + break; + } + break; + } + + case DW_LNS_copy: + //fprintf(stderr, "DW_LNS_copy\n"); + return true; + case DW_LNS_advance_pc: + //fprintf(stderr, "DW_LNS_advance_pc\n"); + tmp = read_uleb128 (lnd); + if (tmp == (uint64_t) -1) + return false; + lnd->cur.pc += tmp * lnd->minimum_instruction_length; + break; + case DW_LNS_advance_line: + //fprintf(stderr, "DW_LNS_advance_line\n"); + lnd->cur.line += read_sleb128 (lnd); + break; + case DW_LNS_set_file: + //fprintf(stderr, "DW_LNS_set_file\n"); + lnd->cur.file = read_uleb128 (lnd); + break; + case DW_LNS_set_column: + //fprintf(stderr, "DW_LNS_set_column\n"); + lnd->cur.col = read_uleb128 (lnd); + break; + case DW_LNS_const_add_pc: + //fprintf(stderr, "DW_LNS_const_add_pc\n"); + lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range + * lnd->minimum_instruction_length); + break; + case DW_LNS_fixed_advance_pc: + //fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); + if (lnd->end - lnd->cpos < 2) + return false; + lnd->cur.pc += read_16 (lnd->cpos); + lnd->cpos += 2; + break; + default: + { + /* Don't know what it is, so skip it. */ + int i; + for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++) + skip_leb128 (lnd); + break; + } + } + } +} + + +/* Set RESULT to the next 'interesting' line state, as indicated + by STOP, or return FALSE on error. The final (end-of-sequence) + line state is always considered interesting. */ +int +line_next (struct line_reader_data * lnd, + struct line_info * result, + enum line_stop_constants stop) +{ + for (;;) + { + struct line_info prev = lnd->cur; + + if (! next_state (lnd)) + return false; + + if (lnd->cur.end_of_sequence) + break; + if (stop == line_stop_always) + break; + if ((stop & line_stop_pc) && lnd->cur.pc != prev.pc) + break; + if ((stop & line_stop_pos_mask) && lnd->cur.file != prev.file) + break; + if ((stop & line_stop_pos_mask) >= line_stop_line + && lnd->cur.line != prev.line) + break; + if ((stop & line_stop_pos_mask) >= line_stop_col + && lnd->cur.col != prev.col) + break; + } + *result = lnd->cur; + return true; +} + +/* Find the region (START->pc through END->pc) in the debug_line + information which contains PC. This routine starts searching at + the current position (which is returned as END), and will go all + the way around the debug_line information. It will return false if + an error occurs or if there is no matching region; these may be + distinguished by looking at START->end_of_sequence, which will be + false on error and true if there was no matching region. + You could write this routine using line_next, but this version + will be slightly more efficient, and of course more convenient. */ + +int +line_find_addr (struct line_reader_data * lnd, + struct line_info * start, + struct line_info * end, + uint64_t pc) +{ + const uint8_t * startpos; + struct line_info prev; + + if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) + line_reset (lnd); + + startpos = lnd->cpos; + + do { + prev = lnd->cur; + if (! next_state (lnd)) + { + start->end_of_sequence = false; + return false; + } + if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) + line_reset (lnd); + if (lnd->cpos == startpos) + { + start->end_of_sequence = true; + return false; + } + } while (lnd->cur.pc <= pc || prev.pc > pc || prev.end_of_sequence); + *start = prev; + *end = lnd->cur; + return true; +} +#endif /* ! KLD */ + diff --git a/ld64/FireOpal/src/debugline.h b/ld64/FireOpal/src/debugline.h new file mode 100644 index 0000000..51d585e --- /dev/null +++ b/ld64/FireOpal/src/debugline.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Information about a line. + DIRECTORY is to be ignored if FILENAME is absolute. + PC will be relative to the file the debug_line section is in. */ +struct line_info +{ + uint64_t file; + int64_t line; + uint64_t col; + uint64_t pc; + int end_of_sequence; +}; + +/* Opaque status structure for the line readers. */ +struct line_reader_data; + +/* Create a line_reader_data, given address and size of the debug_line section. + SIZE may be (size_t)-1 if unknown, although this suppresses checking + for an incorrectly large size in the debug_line section. + LITTLE_ENDIAN is set if the debug_line section is for a little-endian + machine. + Returns NULL on error. */ +struct line_reader_data * line_open (const uint8_t * debug_line, + size_t debug_line_size, + int little_endian); + +/* The STOP parameter to line_next is one of line_stop_{file,line,col}, + perhaps ORed with line_stop_pc; or line_stop_atend, or line_stop_always. */ +enum line_stop_constants { + line_stop_atend = 0, /* Stop only at the end of a sequence. */ + line_stop_file = 1, /* Stop if DIRECTORY or FILENAME change. */ + line_stop_line = 2, /* Stop if LINE, DIRECTORY, or FILENAME change. */ + line_stop_col = 3, /* Stop if COL, LINE, DIRECTORY, or FILENAME change. */ + line_stop_pos_mask = 3, + line_stop_pc = 4, /* Stop if PC changes. */ + line_stop_always = 8 /* Stop always. */ +}; + +/* Either return FALSE on an error, in which case the line_reader_data + may be invalid and should be passed immediately to line_free; or + fill RESULT with the first 'interesting' line, as determined by STOP. + The last line data in a sequence is always considered 'interesting'. */ +int line_next (struct line_reader_data * lnd, + struct line_info * result, + enum line_stop_constants stop); + +/* Find the region (START->pc through END->pc) in the debug_line + information which contains PC. This routine starts searching at + the current position (which is returned as END), and will go all + the way around the debug_line information. It will return false if + an error occurs or if there is no matching region; these may be + distinguished by looking at START->end_of_sequence, which will be + false on error and true if there was no matching region. + You could write this routine using line_next, but this version + will be slightly more efficient, and of course more convenient. */ + +int line_find_addr (struct line_reader_data * lnd, + struct line_info * start, + struct line_info * end, + uint64_t pc); + +/* Return TRUE if there is more line data to be fetched. + If line_next has not been called or it has been called but did not + set END_OF_SEQUENCE, you can assume there is more line data, + but it's safe to call this routine anyway. */ +int line_at_eof (struct line_reader_data * lnd); + +/* Return the pathname of the file in S, or NULL on error. + The result will have been allocated with malloc. */ +char * line_file (struct line_reader_data *lnd, uint64_t file); + +/* Reset the line_reader_data: go back to the beginning. */ +void line_reset (struct line_reader_data * lnd); + +/* Free a line_reader_data structure. */ +void line_free (struct line_reader_data * lnd); + +#ifdef __cplusplus +} +#endif + diff --git a/ld64/FireOpal/src/dwarf2.h b/ld64/FireOpal/src/dwarf2.h new file mode 100644 index 0000000..530b465 --- /dev/null +++ b/ld64/FireOpal/src/dwarf2.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. */ + +/* This is not a complete list. */ +enum { + DW_TAG_compile_unit = 17, + DW_TAG_partial_unit = 60 +}; + +/* This is not a complete list. */ +enum { + DW_AT_sibling = 1, + DW_AT_name = 3, + DW_AT_stmt_list = 16, + DW_AT_comp_dir = 27 +}; + +enum { + DW_FORM_addr = 1, + DW_FORM_block2 = 3, + DW_FORM_block4, + DW_FORM_data2, + DW_FORM_data4, + DW_FORM_data8, + DW_FORM_string, + DW_FORM_block, + DW_FORM_block1, + DW_FORM_data1, + DW_FORM_flag, + DW_FORM_sdata, + DW_FORM_strp, + DW_FORM_udata, + DW_FORM_ref_addr, + DW_FORM_ref1, + DW_FORM_ref2, + DW_FORM_ref4, + DW_FORM_ref8, + DW_FORM_ref_udata, + DW_FORM_indirect /* 22 */ +}; + +enum { + DW_LNS_extended_op = 0, + DW_LNS_copy, + DW_LNS_advance_pc, + DW_LNS_advance_line, + DW_LNS_set_file, + DW_LNS_set_column, + DW_LNS_negate_stmt, + DW_LNS_set_basic_block, + DW_LNS_const_add_pc, + DW_LNS_fixed_advance_pc, + DW_LNS_set_prologue_end, + DW_LNS_set_epilogue_begin, + DW_LNS_set_isa +}; + +enum { + DW_LNE_end_sequence = 1, + DW_LNE_set_address, + DW_LNE_define_file +}; diff --git a/ld64/FireOpal/src/ld.cpp b/ld64/FireOpal/src/ld.cpp new file mode 100644 index 0000000..acd18c8 --- /dev/null +++ b/ld64/FireOpal/src/ld.cpp @@ -0,0 +1,3778 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// start temp HACK for cross builds +extern "C" double log2 ( double ); +#define __MATH__ +// end temp HACK for cross builds + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configure.h" +#include "Options.h" + +#include "ObjectFile.h" + +#include "MachOReaderRelocatable.hpp" +#include "ArchiveReader.hpp" +#include "MachOReaderDylib.hpp" +#include "MachOWriterExecutable.hpp" + + +#if LTO_SUPPORT +#include "LTOReader.hpp" +#endif + + +#include "OpaqueSection.hpp" + + +class CStringComparor +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } +}; + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +class Section : public ObjectFile::Section +{ +public: + static Section* find(const char* sectionName, const char* segmentName, bool zeroFill); + static void assignIndexes(); + const char* getName() { return fSectionName; } +private: + Section(const char* sectionName, const char* segmentName, bool zeroFill); + + struct Sorter { + static int segmentOrdinal(const char* segName); + bool operator()(Section* left, Section* right); + }; + + typedef __gnu_cxx::hash_map, CStringEquals> NameToOrdinal; + typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; + //typedef std::map NameToSection; + + const char* fSectionName; + const char* fSegmentName; + bool fZeroFill; + + static NameToSection fgMapping; + static std::vector fgSections; + static NameToOrdinal fgSegmentDiscoverOrder; +}; + +Section::NameToSection Section::fgMapping; +std::vector Section::fgSections; +Section::NameToOrdinal Section::fgSegmentDiscoverOrder; + +Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) + : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill) +{ + this->fIndex = fgSections.size(); + //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); +} + +Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) +{ + NameToSection::iterator pos = fgMapping.find(sectionName); + if ( pos != fgMapping.end() ) { + if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) + return pos->second; + // otherwise same section name is used in different segments, look slow way + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) + return *it; + } + } + + // does not exist, so make a new one + Section* sect = new Section(sectionName, segmentName, zeroFill); + fgMapping[sectionName] = sect; + fgSections.push_back(sect); + + if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { + // special case __textcoal_nt to be right after __text + find("__textcoal_nt", "__TEXT", false); + } + + // remember segment discovery order + if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) + fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); + + return sect; +} + +int Section::Sorter::segmentOrdinal(const char* segName) +{ + if ( strcmp(segName, "__PAGEZERO") == 0 ) + return 1; + if ( strcmp(segName, "__TEXT") == 0 ) + return 2; + if ( strcmp(segName, "__DATA") == 0 ) + return 3; + if ( strcmp(segName, "__OBJC") == 0 ) + return 4; + if ( strcmp(segName, "__OBJC2") == 0 ) + return 5; + if ( strcmp(segName, "__LINKEDIT") == 0 ) + return INT_MAX; // linkedit segment should always sort last + else + return fgSegmentDiscoverOrder[segName]+6; +} + + +bool Section::Sorter::operator()(Section* left, Section* right) +{ + // Segment is primary sort key + int leftSegOrdinal = segmentOrdinal(left->fSegmentName); + int rightSegOrdinal = segmentOrdinal(right->fSegmentName); + if ( leftSegOrdinal < rightSegOrdinal ) + return true; + if ( leftSegOrdinal > rightSegOrdinal ) + return false; + + // zerofill section sort to the end + if ( !left->fZeroFill && right->fZeroFill ) + return true; + if ( left->fZeroFill && !right->fZeroFill ) + return false; + + // section discovery order is last sort key + return left->fIndex < right->fIndex; +} + +void Section::assignIndexes() +{ + //printf("unsorted sections:\n"); + //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); + //} + + // sort it + std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); + + // assign correct section ordering to each Section object + unsigned int newOrder = 1; + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) + (*it)->fIndex = newOrder++; + + //printf("sorted sections:\n"); + //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); + //} +} + +class Linker : public ObjectFile::Reader::DylibHander { +public: + Linker(int argc, const char* argv[]); + + const char* getArchPrefix(); + const char* architectureName(); + bool showArchitectureInErrors(); + bool isInferredArchitecture(); + void createReaders(); + void createWriter(); + void addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& ); + void setOutputFile(ExecutableFile::Writer* writer); + void link(); + void optimize(); + + // implemenation from ObjectFile::Reader::DylibHander + virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath); + +private: + struct WhyLiveBackChain + { + WhyLiveBackChain* previous; + const char* name; + }; + + ObjectFile::Reader* createReader(const Options::FileInfo&); + void addAtom(ObjectFile::Atom& atom); + void addAtoms(std::vector& atoms); + void buildAtomList(); + void processDylibs(); + void markDead(ObjectFile::Atom* atom); + void updateConstraints(ObjectFile::Reader* reader); + void loadAndResolve(); + void processDTrace(); + void checkObjC(); + void loadUndefines(); + void checkUndefines(); + void addWeakAtomOverrides(); + void resolveReferences(); + void deadStripResolve(); + void addLiveRoot(const char* name); + ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); + void logArchive(ObjectFile::Reader* reader); + void sortSections(); + void sortAtoms(); + void tweakLayout(); + void writeDotOutput(); + static bool minimizeStab(ObjectFile::Reader::Stab& stab); + static const char* truncateStabString(const char* str); + void collectDebugInfo(); + void writeOutput(); + ObjectFile::Atom* entryPoint(bool orInit); + ObjectFile::Atom* dyldHelper(); + ObjectFile::Atom* dyldLazyLibraryHelper(); + const char* assureFullPath(const char* path); + void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); + void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); + void synthesizeDebugNotes(std::vector& allAtomsByReader); + void printStatistics(); + void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); + char* commatize(uint64_t in, char* out); + void getVMInfo(vm_statistics_data_t& info); + cpu_type_t inferArchitecture(); + void addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName); + void checkDylibClientRestrictions(ObjectFile::Reader* reader); + void logDylib(ObjectFile::Reader* reader, bool indirect); + + void resolve(ObjectFile::Reference* reference); + void resolveFrom(ObjectFile::Reference* reference); + std::vector* addJustInTimeAtoms(const char* name, bool dylibsOnly=false); + void addJustInTimeAtomsAndMarkLive(const char* name); + + ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + + void logTraceInfo(const char* format, ...); + + + class SymbolTable + { + public: + typedef __gnu_cxx::hash_map, CStringEquals> Mapper; + + SymbolTable(Linker&); + void require(const char* name); + bool add(ObjectFile::Atom& atom); + ObjectFile::Atom* find(const char* name); + unsigned int getRequireCount() { return fRequireCount; } + void getNeededNames(bool andWeakDefintions, std::vector& undefines); + bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } + bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } + void setHasExternalWeakDefinitions() { fHasExternalWeakDefinitions = true; } + Mapper::iterator begin() { return fTable.begin(); } + Mapper::iterator end() { return fTable.end(); } + + private: + Linker& fOwner; + Mapper fTable; + unsigned int fRequireCount; + bool fHasExternalTentativeDefinitions; + bool fHasExternalWeakDefinitions; + }; + + class AtomSorter + { + public: + AtomSorter(std::map* map) : fOverriddenOrdinalMap(map) {} + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); + private: + std::map* fOverriddenOrdinalMap; + }; + + typedef std::map SectionOrder; + + struct DTraceProbeInfo { + DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} + const ObjectFile::Atom* atom; + uint32_t offset; + const char* probeName; + }; + typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToReader; + + struct IndirectLibrary { + const char* path; + uint64_t fileLen; + ObjectFile::Reader* reader; + std::set parents; + ObjectFile::Reader* reExportedViaDirectLibrary; + }; + + ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib); + + Options fOptions; + SymbolTable fGlobalSymbolTable; + uint32_t fNextInputOrdinal; + std::vector fInputFiles; + ExecutableFile::Writer* fOutputFile; + InstallNameToReader fDylibMap; + std::map fDylibOptionsMap; + std::set fDylibsProcessed; + ObjectFile::Reader* fBundleLoaderReader; + std::vector fReadersThatHaveSuppliedAtoms; + std::vector fAllAtoms; + std::set fArchiveReaders; + std::set fArchiveReadersLogged; + std::set fDeadAtoms; + std::set fLiveAtoms; + std::set fLiveRootAtoms; + std::vector fStabs; + std::vector fAtomsWithUnresolvedReferences; + std::vector fDtraceProbes; + std::vector fDtraceProbeSites; + std::vector fDtraceIsEnabledSites; + std::map fDtraceAtomToTypes; + bool fCreateUUID; + bool fCanScatter; + SectionOrder fSectionOrder; + cpu_type_t fArchitecture; + const char* fArchitectureName; + bool fArchitectureInferred; + bool fDirectLibrariesComplete; + bool fBiggerThanTwoGigOutput; + uint64_t fOutputFileSize; + uint64_t fTotalZeroFillSize; + uint64_t fTotalSize; + uint64_t fStartTime; + uint64_t fStartCreateReadersTime; + uint64_t fStartCreateWriterTime; + uint64_t fStartBuildAtomsTime; + uint64_t fStartLoadAndResolveTime; + uint64_t fStartSortTime; + uint64_t fStartDebugTime; + uint64_t fStartWriteTime; + uint64_t fEndTime; + uint64_t fTotalObjectSize; + uint64_t fTotalArchiveSize; + uint32_t fTotalObjectLoaded; + uint32_t fTotalArchivesLoaded; + uint32_t fTotalDylibsLoaded; + vm_statistics_data_t fStartVMInfo; + ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint; + ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint; + bool fObjcReplacmentClasses; + bool fAllDirectDylibsLoaded; +}; + + +Linker::Linker(int argc, const char* argv[]) + : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), + fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), + fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), + fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), + fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), + fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny), + fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false) +{ + fStartTime = mach_absolute_time(); + if ( fOptions.printStatistics() ) + getVMInfo(fStartVMInfo); + + fArchitecture = fOptions.architecture(); + if ( fArchitecture == 0 ) { + // -arch not specified, scan .o files to figure out what it should be + fArchitecture = inferArchitecture(); + fArchitectureInferred = true; + } + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + fArchitectureName = "ppc"; + break; + case CPU_TYPE_POWERPC64: + fArchitectureName = "ppc64"; + break; + case CPU_TYPE_I386: + fArchitectureName = "i386"; + break; + case CPU_TYPE_X86_64: + fArchitectureName = "x86_64"; + break; + case CPU_TYPE_ARM: + fArchitectureName = "arm"; + break; + default: + fArchitectureName = "unknown architecture"; + break; + } +} + +const char* Linker::architectureName() +{ + return fArchitectureName; +} + +bool Linker::showArchitectureInErrors() +{ + return fOptions.printArchPrefix(); +} + +bool Linker::isInferredArchitecture() +{ + return fArchitectureInferred; +} + +cpu_type_t Linker::inferArchitecture() +{ + // scan all input files, looking for a thin .o file. + // the first one found is presumably the architecture to link + uint8_t buffer[sizeof(mach_header_64)]; + std::vector& files = fOptions.getInputFiles(); + for (std::vector::iterator it = files.begin(); it != files.end(); ++it) { + int fd = ::open(it->path, O_RDONLY, 0); + if ( fd != -1 ) { + ssize_t amount = read(fd, buffer, sizeof(buffer)); + ::close(fd); + if ( amount >= (ssize_t)sizeof(buffer) ) { + if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch ppc based on %s", it->path); + return CPU_TYPE_POWERPC; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch ppc64 based on %s", it->path); + return CPU_TYPE_POWERPC64; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch i386 based on %s", it->path); + return CPU_TYPE_I386; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch x86_64 based on %s", it->path); + return CPU_TYPE_X86_64; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch arm based on %s", it->path); + return CPU_TYPE_ARM; + } + } + } + } + + // no thin .o files found, so default to same architecture this was built as + warning("-arch not specified"); +#if __ppc__ + return CPU_TYPE_POWERPC; +#elif __i386__ + return CPU_TYPE_I386; +#elif __ppc64__ + return CPU_TYPE_POWERPC64; +#elif __x86_64__ + return CPU_TYPE_X86_64; +#elif __arm__ + return CPU_TYPE_ARM; +#else + #error unknown default architecture +#endif +} + + +void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) +{ + fInputFiles.push_back(reader); + fDylibOptionsMap[reader] = info.options; +} + +void Linker::setOutputFile(ExecutableFile::Writer* writer) +{ + fOutputFile = writer; +} + +class InSet +{ +public: + InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fDeadAtoms.count(atom) != 0 ); + } + +private: + std::set& fDeadAtoms; +}; + +void Linker::loadAndResolve() +{ + fStartLoadAndResolveTime = mach_absolute_time(); + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // without dead-code-stripping: + // find atoms to resolve all undefines + this->loadUndefines(); + // verify nothing is missing + this->checkUndefines(); + // once all undefines fulfill, then bind all references + this->resolveReferences(); + // remove atoms weak atoms that have been overridden + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + } + else { + // with dead code stripping: + // start binding references from roots, + this->deadStripResolve(); + // verify nothing is missing + this->checkUndefines(); + } +} + +void Linker::optimize() +{ + // give each reader a chance to do any optimizations + std::vector newAtoms; + std::vector additionalUndefines; + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, fNextInputOrdinal, fOutputFile, + fOptions.llvmOptions(), + fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(), + fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(), + fOptions.allowTextRelocs()); + } + + // add all newly created atoms to fAllAtoms and update symbol table + this->addAtoms(newAtoms); + + // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could + // not have their section set until now. + for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { + ObjectFile::Atom *atom = *itr; + if ( atom->getSection() == NULL ) + atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); + } + + // resolve new undefines + for(std::vector::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { + const char *targetName = *riter; + //fprintf(stderr, "LTO additional undefine: %s\n", targetName); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL) { + // mark that this symbol is needed + fGlobalSymbolTable.require(targetName); + // try to find it in some library + this->addJustInTimeAtoms(targetName); + } + } + + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + fLiveAtoms.clear(); + this->deadStripResolve(); + } + else { + this->checkUndefines(); + this->resolveReferences(); + } +} + +void Linker::link() +{ + this->buildAtomList(); + this->loadAndResolve(); + this->optimize(); + this->checkObjC(); + this->processDTrace(); + this->tweakLayout(); + this->sortSections(); + this->sortAtoms(); + this->writeDotOutput(); + this->collectDebugInfo(); + this->writeOutput(); + this->printStatistics(); + + if ( fOptions.pauseAtEnd() ) + sleep(10); +} + +void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; + //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); + } + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; + uint32_t milliSeconds = milliSecondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + +char* Linker::commatize(uint64_t in, char* out) +{ + char* result = out; + char rawNum[30]; + sprintf(rawNum, "%llu", in); + const int rawNumLen = strlen(rawNum); + for(int i=0; i < rawNumLen-1; ++i) { + *out++ = rawNum[i]; + if ( ((rawNumLen-i) % 3) == 1 ) + *out++ = ','; + } + *out++ = rawNum[rawNumLen-1]; + *out = '\0'; + return result; +} + +void Linker::getVMInfo(vm_statistics_data_t& info) +{ + mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); + kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count); + if (error != KERN_SUCCESS) { + bzero(&info, sizeof(vm_statistics_data_t)); + } +} + +void Linker::printStatistics() +{ + fEndTime = mach_absolute_time(); + if ( fOptions.printStatistics() ) { + vm_statistics_data_t endVMInfo; + getVMInfo(endVMInfo); + + uint64_t totalTime = fEndTime - fStartTime; + printTime("ld total time", totalTime, totalTime); + printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); + printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); + printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); + printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime); + printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime); + printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); + printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); + printTime(" write output", fEndTime - fStartWriteTime, totalTime); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, + endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); + char temp[40]; + fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp)); + fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp)); + fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded); + fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp)); + } +} + +inline void Linker::addAtom(ObjectFile::Atom& atom) +{ + // add to list of all atoms + fAllAtoms.push_back(&atom); + + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // not dead-stripping code, so add atom's references's names to symbol table as to-be-resolved-later + std::vector& references = atom.getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) + fGlobalSymbolTable.require(reference->getTargetName()); + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) + fGlobalSymbolTable.require(reference->getFromTargetName()); + if ( reference->getTargetBinding() == ObjectFile::Reference::kDontBind ) + addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); + } + // update total size info (except for __ZEROPAGE atom) + if ( atom.getSegment().isContentReadable() ) { + fTotalSize += atom.getSize(); + if ( atom.isZeroFill() ) + fTotalZeroFillSize += atom.getSize(); + } + } + else { + if ( atom.dontDeadStrip() ) + fLiveRootAtoms.insert(&atom); + } + + // if in global namespace, add atom itself to symbol table + ObjectFile::Atom::Scope scope = atom.getScope(); + const char* name = atom.getName(); + if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { + // update scope based on export list + if ( fOptions.hasExportRestrictList() ) { + if ( scope == ObjectFile::Atom::scopeGlobal ) { + // check for globals that are downgraded to hidden + bool doExport = fOptions.shouldExport(name); + if ( !doExport ) { + atom.setScope(ObjectFile::Atom::scopeLinkageUnit); + } + } + else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { + // check for hiddens that were requested to be exported + if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { + warning("cannot export hidden symbol %s from %s", name, atom.getFile()->getPath()); + } + } + } + // add to symbol table + if ( fOptions.outputKind() == Options::kObjectFile ) { + // in ld -r mode don't add .eh symbols to symbol table + // instead kGroupSubordinate references will keep them paired + // with their functions. + const char* sectionName = atom.getSectionName(); + if ( (sectionName != NULL) && (strcmp(sectionName, "__eh_frame") != 0) ) + fGlobalSymbolTable.add(atom); + } + else { + fGlobalSymbolTable.add(atom); + } + } + + // record section orders so output file can have same order + if (atom.getSectionName()) + atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); +} + + +void Linker::markDead(ObjectFile::Atom* atom) +{ + fDeadAtoms.insert(atom); + // + // The kGroupSubordinate reference kind is used to model group comdat. + // The "signature" atom in the group has a kGroupSubordinate reference to + // all other members of the group. So, if the signature atom is + // coalesced away, all other atoms in the group should also be removed. + // + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX + ObjectFile::Atom* targetAtom = &(ref->getTarget()); + if ( targetAtom == NULL ) { + warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName()); + } + else { + if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) { + // ok for .eh symbols to be not static in -r mode + if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) ) + warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName()); + } + this->markDead(targetAtom); + } + } + } +} + +void Linker::updateConstraints(ObjectFile::Reader* reader) +{ + // check objc objects were compiled compatibly + ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); + if ( reader->getInstallPath() == NULL ) { + // adding a .o file + switch ( objcAddition ) { + case ObjectFile::Reader::kObjcNone: + break; + case ObjectFile::Reader::kObjcRetainRelease: + if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); + fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease; + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone ) + fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + break; + case ObjectFile::Reader::kObjcGC: + if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); + fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC; + break; + } + } + if ( reader->objcReplacementClasses() ) + fObjcReplacmentClasses = true; + + // check cpu sub-types for stricter sub-type + fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint); +} + +inline void Linker::addAtoms(std::vector& atoms) +{ + bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; + bool first = true; + for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { + // usually we only need to get the first atom's reader, but + // with -all_load all atoms from all .o files come come back together + // so we need to scan all atoms + if ( first || scanAll ) { + // update fReadersThatHaveSuppliedAtoms + ObjectFile::Reader* reader = (*it)->getFile(); + if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) + == fReadersThatHaveSuppliedAtoms.end() ) { + fReadersThatHaveSuppliedAtoms.push_back(reader); + updateConstraints(reader); + } + } + this->addAtom(**it); + first = false; + } +} + +void Linker::logArchive(ObjectFile::Reader* reader) +{ + if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) { + fArchiveReadersLogged.insert(reader); + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + } +} + + +void Linker::buildAtomList() +{ + fStartBuildAtomsTime = mach_absolute_time(); + // add initial undefines from -u option + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { + fGlobalSymbolTable.require(*it); + } + + // writer can contribute atoms + this->addAtoms(fOutputFile->getAtoms()); + + // each reader contributes atoms + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + std::vector& atoms = reader->getAtoms(); + this->addAtoms(atoms); + if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) ) + logArchive(reader); + } + + // extra command line section always at end + std::vector& extraSections = fOptions.extraSections(); + for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { + this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); + fNextInputOrdinal += it->dataLen; + } +} + +static const char* pathLeafName(const char* path) +{ + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + return path; + else + return &shortPath[1]; +} + +void Linker::loadUndefines() +{ + // keep looping until no more undefines were added in last loop + unsigned int undefineCount = 0xFFFFFFFF; + while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { + undefineCount = fGlobalSymbolTable.getRequireCount(); + std::vector undefineNames; + fGlobalSymbolTable.getNeededNames(false, undefineNames); + for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* name = *it; + ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); + if ( (possibleAtom == NULL) + || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) + && (fOptions.outputKind() != Options::kObjectFile) + && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) { + std::vector* atoms = this->addJustInTimeAtoms(name); + if ( atoms != NULL ) + delete atoms; + } + } + } +} + +// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names +class ExportedObjcClass +{ +public: + ExportedObjcClass(Options& opt) : fOptions(opt) {} + + bool operator()(const char* name) const { + if ( fOptions.shouldExport(name) ) { + if ( strncmp(name, ".objc_class_name_", 17) == 0 ) + return true; + if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 ) + return true; + if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 ) + return true; + } + //fprintf(stderr, "%s is not exported\n", name); + return false; + } +private: + Options& fOptions; +}; + + +void Linker::checkUndefines() +{ + // error out on any remaining undefines + bool doPrint = true; + bool doError = true; + switch ( fOptions.undefinedTreatment() ) { + case Options::kUndefinedError: + break; + case Options::kUndefinedDynamicLookup: + doError = false; + break; + case Options::kUndefinedWarning: + doError = false; + break; + case Options::kUndefinedSuppress: + doError = false; + doPrint = false; + break; + } + std::vector unresolvableUndefines; + fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); + + // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names + // ignore unresolved references to Objc class names that are listed in -exported_symbols_list + if ( fOptions.hasExportRestrictList() ) + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end()); + + const int unresolvableCount = unresolvableUndefines.size(); + int unresolvableExportsCount = 0; + if ( unresolvableCount != 0 ) { + if ( doPrint ) { + if ( fOptions.printArchPrefix() ) + fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); + else + fprintf(stderr, "Undefined symbols:\n"); + for (int i=0; i < unresolvableCount; ++i) { + const char* name = unresolvableUndefines[i]; + fprintf(stderr, " \"%s\", referenced from:\n", name); + // scan all atoms for references + bool foundAtomReference = false; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + if ( strcmp(reference->getTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); + foundAtomReference = true; + } + } + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + if ( strcmp(reference->getFromTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); + foundAtomReference = true; + } + } + } + } + // scan command line options + if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbols_list command line option\n"); + ++unresolvableExportsCount; + } + } + } + if ( doError ) + throw "symbol(s) not found"; + } + + // for each tentative definition in symbol table look for dylib that exports same symbol name + if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { + for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { + ObjectFile::Atom* atom = it->second; + if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition) + && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { + // look for dylibs that export same name as used by global tentative definition + addJustInTimeAtoms(atom->getName(), true); + } + } + } + + // if we have no weak symbols, see if we override some weak symbol in some dylib + if ( !fGlobalSymbolTable.hasExternalWeakDefinitions() ) { + bool done = false; + for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); !done && (it != fGlobalSymbolTable.end()); ++it) { + ObjectFile::Atom* atom = it->second; + if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) + && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { + const char* name = atom->getName(); + //fprintf(stderr, "looking for dylibs with a weak %s\n", name); + // look for dylibs with weak exports of the same name + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + ObjectFile::Reader* reader = it->second; + if ( reader->hasWeakExternals() ) { + std::vector* atoms = reader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + // if this is a weak definition in a dylib + if ( (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fGlobalSymbolTable.setHasExternalWeakDefinitions(); + done = true; + break; + } + } + } + } + } + } + } + +} + + + +std::vector* Linker::addJustInTimeAtoms(const char* name, bool dylibsOnly) +{ + // when creating final linked image, writer gets first chance + if ( fOptions.outputKind() != Options::kObjectFile ) { + std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); + return atoms; // found a definition, no need to search anymore + } + } + + // give readers a chance + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); + bool isDylibReader = (reader->getInstallPath() != NULL); + if ( !dylibsOnly || isDylibReader ) { + std::vector* atoms = reader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) { + logArchive(reader); + } + // if this is a weak definition in a dylib + if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + // keep looking for a non-weak definition + } + else { + // found a definition, no need to search anymore + return atoms; + } + } + } + } + } + + // for two level namesapce, give all implicitly link dylibs a chance + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->implicitlyLinked() ) { + //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() ); + std::vector* atoms = it->second->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + // if this is a weak definition in a dylib + if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + // keep looking for a non-weak definition + } + else { + // found a definition, no need to search anymore + return atoms; + } + } + } + } + } + + // for flat namespace, give indirect dylibs + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) { + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( ! it->second->explicitlyLinked() ) { + std::vector* atoms = it->second->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + return atoms; // found a definition, no need to search anymore + } + } + } + } + + // writer creates a proxy in two cases: + // 1) ld -r is being used to create a .o file + // 2) -undefined dynamic_lookup is being used + // 3) -U _foo is being used + if ( (fOptions.outputKind() == Options::kObjectFile) + || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && !dylibsOnly) + || (fOptions.someAllowedUndefines() && !dylibsOnly) ) { + ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); + if ( atom != NULL ) { + this->addAtom(*atom); + return NULL; + } + } + //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); + return NULL; +} + +void Linker::resolve(ObjectFile::Reference* reference) +{ + // look in global symbol table + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + fprintf(stderr, "Undefined symbol: %s\n", targetName); + } + reference->setTarget(*target, reference->getTargetOffset()); +} + +void Linker::resolveFrom(ObjectFile::Reference* reference) +{ + // handle references that have two (from and to) targets + const char* fromTargetName = reference->getFromTargetName(); + ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); + if ( fromTarget == NULL ) { + fprintf(stderr, "Undefined symbol: %s\n", fromTargetName); + } + reference->setFromTarget(*fromTarget); +} + + +void Linker::resolveReferences() +{ + // note: the atom list may grow during this loop as libraries supply needed atoms + for (unsigned int j=0; j < fAllAtoms.size(); ++j) { + ObjectFile::Atom* atom = fAllAtoms[j]; + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) + this->resolve(reference); + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) + this->resolveFrom(reference); + } + } +} + + +// used to remove stabs associated with atoms that won't be in output file +class NotInSet +{ +public: + NotInSet(std::set& theSet) : fSet(theSet) {} + + bool operator()(const ObjectFile::Reader::Stab& stab) const { + if ( stab.atom == NULL ) + return false; // leave stabs that are not associated with any atome + else + return ( fSet.count(stab.atom) == 0 ); + } + +private: + std::set& fSet; +}; + + +class NotLive +{ +public: + NotLive(std::set& set) : fLiveAtoms(set) {} + + bool operator()(ObjectFile::Atom*& atom) const { + //if ( fLiveAtoms.count(atom) == 0 ) + // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); + return ( fLiveAtoms.count(atom) == 0 ); + } +private: + std::set& fLiveAtoms; +}; + + +void Linker::addJustInTimeAtomsAndMarkLive(const char* name) +{ + std::vector* atoms = this->addJustInTimeAtoms(name); + if ( atoms != NULL ) { + if ( fOptions.allGlobalsAreDeadStripRoots() ) { + for (std::vector::iterator it=atoms->begin(); it != atoms->end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = atom->getDisplayName(); + this->markLive(*atom, &rootChain); + } + } + } + delete atoms; + } +} + +void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) +{ + if ( fLiveAtoms.count(&atom) == 0 ) { + // if -whylive cares about this symbol, then dump chain + if ( (previous->name != NULL) && fOptions.printWhyLive(previous->name) ) { + int depth = 0; + for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { + for(int i=depth; i > 0; --i) + fprintf(stderr, " "); + fprintf(stderr, "%s\n", p->name); + } + } + // set up next chain + WhyLiveBackChain thisChain; + thisChain.previous = previous; + // this atom is live + fLiveAtoms.insert(&atom); + // update total size info (except for __ZEROPAGE atom) + if ( atom.getSegment().isContentReadable() ) { + fTotalSize += atom.getSize(); + if ( atom.isZeroFill() ) + fTotalZeroFillSize += atom.getSize(); + } + // and all atoms it references + std::vector& references = atom.getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + // look in global symbol table + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtomsAndMarkLive(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + } + else { + // mark as undefined, for later error processing + fAtomsWithUnresolvedReferences.push_back(&atom); + fGlobalSymbolTable.require(targetName); + } + } + switch ( reference->getTargetBinding() ) { + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + thisChain.name = reference->getTargetName(); + markLive(reference->getTarget(), &thisChain); + break; + case ObjectFile::Reference::kDontBind: + addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); + break; + case ObjectFile::Reference::kUnboundByName: + // do nothing + break; + } + // do the same as above, for "from target" + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + // look in global symbol table + const char* targetName = reference->getFromTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtomsAndMarkLive(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setFromTarget(*target); + } + else { + // mark as undefined, for later error processing + fGlobalSymbolTable.require(targetName); + } + } + switch ( reference->getFromTargetBinding() ) { + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + thisChain.name = reference->getFromTargetName(); + markLive(reference->getFromTarget(), &thisChain); + break; + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + // do nothing + break; + } + } + } +} + + +void Linker::addLiveRoot(const char* name) +{ + ObjectFile::Atom* target = fGlobalSymbolTable.find(name); + if ( target == NULL ) { + this->addJustInTimeAtomsAndMarkLive(name); + target = fGlobalSymbolTable.find(name); + } + if ( target != NULL ) + fLiveRootAtoms.insert(target); +} + + +void Linker::deadStripResolve() +{ + // add main() to live roots + ObjectFile::Atom* entryPoint = this->entryPoint(false); + if ( entryPoint != NULL ) + fLiveRootAtoms.insert(entryPoint); + + // add dyld_stub_binding_helper() to live roots + ObjectFile::Atom* dyldHelper = this->dyldHelper(); + if ( dyldHelper != NULL ) + fLiveRootAtoms.insert(dyldHelper); + + // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots + if ( fOptions.usingLazyDylibLinking() ) { + ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper(); + if ( dyldLazyDylibHelper != NULL ) + fLiveRootAtoms.insert(dyldLazyDylibHelper); + } + + // add -exported_symbols_list, -init, and -u entries to live roots + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) + addLiveRoot(*it); + + // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots + // + if ( fOptions.hasWildCardExportRestrictList() ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) + && (fDeadAtoms.count(atom) == 0) + && fOptions.shouldExport(atom->getName()) ) + fLiveRootAtoms.insert(atom); + } + } + + // in some cases, every global scope atom in initial .o files is a root + if ( fOptions.allGlobalsAreDeadStripRoots() ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) + fLiveRootAtoms.insert(atom); + } + } + + // mark all roots as live, and all atoms they reference + for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = (*it)->getDisplayName(); + markLive(**it, &rootChain); + } + + // it is possible that there are unresolved references that can be resolved now + // this can happen if the first reference to a common symbol in an archive. + // common symbols are not in the archive TOC, but the .o could have been pulled in later. + // ld64 while linking cc1 [ when dead_strip is ON] + for (std::vector::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { + std::vector& references = (*it)->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + warning("internal error %s is not a tentative definition", target->getDisplayName()); + } + } + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); + if ( target != NULL ) { + reference->setFromTarget(*target); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + warning("internal error %s is not a tentative definition", target->getDisplayName()); + } + } + } + } + + // now remove all non-live atoms from fAllAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); +} + +void Linker::checkObjC() +{ + // check dylibs + switch ( fCurrentObjCConstraint ) { + case ObjectFile::Reader::kObjcNone: + // can link against any dylib + break; + case ObjectFile::Reader::kObjcRetainRelease: + // cannot link against GC-only dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->explicitlyLinked() ) { + if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC ) + throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath()); + } + } + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + // can link against GC or RR dylibs + break; + case ObjectFile::Reader::kObjcGC: + // cannot link against RR-only dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->explicitlyLinked() ) { + if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease ) + throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath()); + } + } + break; + } + + // synthesize __OBJC __image_info atom if needed + if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) { + this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses)); + } +} + +void Linker::addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName) +{ + if ( probeName != NULL ) { + if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) + fDtraceProbeSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) + fDtraceIsEnabledSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) + fDtraceAtomToTypes[&atom].insert(probeName); + else if ( fOptions.dTrace() && (strncmp(probeName, "__dtrace_probe$", 15) == 0) ) + fDtraceProbes.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + } +} + +static uint8_t pointerKind(cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + return ppc::kPointer; + case CPU_TYPE_POWERPC64: + return ppc64::kPointer; + case CPU_TYPE_I386: + return x86::kPointer; + case CPU_TYPE_X86_64: + return x86_64::kPointer; + case CPU_TYPE_ARM: + return arm::kPointer; + } + throw "uknown architecture"; +} + +static uint8_t pcRelKind(cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + return ppc::kPointerDiff32; + case CPU_TYPE_POWERPC64: + return ppc64::kPointerDiff32; + case CPU_TYPE_I386: + return x86::kPointerDiff; + case CPU_TYPE_X86_64: + return x86_64::kPointerDiff32; + case CPU_TYPE_ARM: + return arm::kPointerDiff; + } + throw "uknown architecture"; +} + +typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); +typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); + + +void Linker::processDTrace() +{ + // handle dtrace 2.0 static probes + if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) { + // partition probes by provider name + // The symbol names looks like: + // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] + // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] + ProviderToProbes providerToProbes; + std::vector emptyList; + for(std::vector::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { + const char* providerStart = &it->probeName[16]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + } + for(std::vector::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { + const char* providerStart = &it->probeName[20]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + } + + // create a DOF section for each provider + int dofIndex=1; + CStringSet sectionNamesUsed; + for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { + const char* providerName = pit->first; + const std::vector& probes = pit->second; + + // open library and find dtrace_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); + createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); + // build list of typedefs/stability infos for this provider + CStringSet types; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + std::map::iterator pos = fDtraceAtomToTypes.find(it->atom); + if ( pos != fDtraceAtomToTypes.end() ) { + for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { + const char* providerStart = strchr(*sit, '$')+1; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char aProviderName[providerEnd-providerStart+1]; + strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); + if ( strcmp(aProviderName, providerName) == 0 ) + types.insert(*sit); + } + } + } + } + int typeCount = types.size(); + const char* typeNames[typeCount]; + //fprintf(stderr, "types for %s:\n", providerName); + uint32_t index = 0; + for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { + typeNames[index] = *it; + //fprintf(stderr, "\t%s\n", *it); + ++index; + } + + // build list of probe/isenabled sites + const uint32_t probeCount = probes.size(); + const char* probeNames[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + index = 0; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + probeNames[index] = it->probeName; + funtionNames[index] = it->atom->getName(); + offsetsInDOF[index] = 0; + ++index; + } + //fprintf(stderr, "calling libtrace to create DOF\n"); + //for(uint32_t i=0; i < probeCount; ++i) + // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); + // call dtrace library to create DOF section + size_t dofSectionSize; + uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + char sectionName[18]; + strcpy(sectionName, "__dof_"); + strlcpy(§ionName[6], providerName, 10); + // create unique section name so each DOF is in its own section + if ( sectionNamesUsed.count(sectionName) != 0 ) { + sectionName[15] = '0'; + sectionName[16] = '\0'; + while ( sectionNamesUsed.count(sectionName) != 0 ) + ++sectionName[15]; + } + sectionNamesUsed.insert(sectionName); + char symbolName[strlen(providerName)+64]; + sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); + opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, + "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); + fNextInputOrdinal += dofSectionSize; + // add references + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); + } + this->addAtoms(reader->getAtoms()); + } + else { + throw "error creating dtrace DOF section"; + } + } + } + // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files + else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) { + const uint32_t probeCount = fDtraceProbes.size(); + const char* labels[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + + // open libray and find dtrace_ld64_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror()); + oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror()); + + // build argument list + uint32_t index = 0; + for(std::vector::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) { + labels[index] = it->probeName; + funtionNames[index] = it->atom->getName(); + offsetsInDOF[index] = 0; + ++index; + } + size_t dofSectionSize; + // call dtrace library to create DOF section + uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal); + fNextInputOrdinal += dofSectionSize; + // add references + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX", i, offset, dofSectionSize); + reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset); + } + this->addAtoms(reader->getAtoms()); + } + else { + throw "error created dtrace DOF section"; + } + } +} + + +static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName) +{ + if ( objectFileLeafName == NULL ) + return true; + const char* atomFullPath = atom->getFile()->getPath(); + const char* lastSlash = strrchr(atomFullPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) + return true; + } + else { + if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) + return true; + } + return false; +} + + +static bool usesAnonymousNamespace(const char* symbol) +{ + return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) ); +} + + +// +// convert: +// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv +// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv +// +static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[]) +{ + const char* globPtr = strstr(inSymbol, "_GLOBAL__N_"); + while ( isdigit(*(--globPtr)) ) + ; // loop + char* endptr; + unsigned long length = strtoul(globPtr+1, &endptr, 10); + const char* globEndPtr = endptr + length; + int startLen = globPtr-inSymbol+1; + memcpy(outSymbol, inSymbol, startLen); + outSymbol[startLen] = '-'; + strcpy(&outSymbol[startLen+1], globEndPtr); +} + + +ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) +{ + ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); + if ( atom != NULL ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) + return atom; + } + else { + // slow case. The requested symbol is not in symbol table, so might be static function + static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; + static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace; + static bool built = false; + // build a hash_map the first time + if ( !built ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + const char* name = atom->getName(); + if ( name != NULL) { + if ( usesAnonymousNamespace(name) ) { + // symbol that uses anonymous namespace + char canonicalName[strlen(name)+2]; + canonicalizeAnonymousName(name, canonicalName); + const char* hashName = strdup(canonicalName); + SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName); + if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() ) + hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom; + else + hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL + } + else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // static function or data + SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); + if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) + hashTableOfTranslationUnitScopedSymbols[name] = atom; + else + hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL + } + } + } + //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size()); + built = true; + } + + // look for name in hashTableOfTranslationUnitScopedSymbols + SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName); + if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); + return pos->second; + } + if ( pos->second == NULL ) + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + const char* name = atom->getName(); + if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( fOptions.printOrderFileStatistics() ) + warning("%s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + + // look for name in hashTableOfSymbolsWithAnonymousNamespace + if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) { + // symbol that uses anonymous namespace + char canonicalName[strlen(orderedSymbol.symbolName)+2]; + canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName); + SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName); + if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName); + return pos->second; + } + if ( pos->second == NULL ) + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + const char* name = atom->getName(); + if ( (name != NULL) && usesAnonymousNamespace(name) ) { + char canonicalAtomName[strlen(name)+2]; + canonicalizeAnonymousName(name, canonicalAtomName); + if ( strcmp(canonicalAtomName, canonicalName) == 0 ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( fOptions.printOrderFileStatistics() ) + warning("%s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + } + } + return NULL; +} + + +void Linker::sortSections() +{ + Section::assignIndexes(); +} + + +// +// Linker::sortAtoms() +// +// The purpose of this method is to take the graph of all Atoms and produce an ordered +// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must +// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified +// in an order_file are seqenced as in the order_file and before Atoms not specified, +// 4) Atoms in the same section from the same .o file should be contiguous and sequenced +// in the same order they were in the .o file, 5) Atoms in the same Section but which came +// from different .o files should be sequenced in the same order that the .o files +// were passed to the linker (i.e. command line order). +// +// The way this is implemented is that the linker passes a "base ordinal" to each Reader +// as it is constructed. The reader should construct it Atoms so that calling getOrdinal() +// on its atoms returns a contiguous range of values starting at the base ordinal. Then +// sorting is just sorting by section, then by ordinal. +// +// If an order_file is specified, it gets more complicated. First, an override-ordinal map +// is created. It causes the sort routine to ignore the value returned by getOrdinal() and +// use the override value instead. Next some Atoms must be layed out consecutively +// (e.g. hand written assembly that does not end with return, but rather falls into +// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of +// kFollowOn refernces produces "clusters" of atoms that must stay together. +// If an order_file tries to move one atom, it may need to move a whole cluster. The +// algorithm to do this models clusters using two maps. The "starts" maps maps any +// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a +// cluster to the next Atom in the cluster. With this in place, while processing an +// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is +// given ordinal overrides. +// +void Linker::sortAtoms() +{ + fStartSortTime = mach_absolute_time(); + // if -order_file is used, build map of atom ordinal overrides + std::map* ordinalOverrideMap = NULL; + std::map theOrdinalOverrideMap; + const bool log = false; + if ( fOptions.orderedSymbols().size() != 0 ) { + // first make a pass to find all follow-on references and build start/next maps + // which are a way to represent clusters of atoms that must layout together + std::map followOnStarts; + std::map followOnNexts; + for (std::vector::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) { + ObjectFile::Atom* atom = *ait; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == 1 ) { // FIX FIX + ObjectFile::Atom* targetAtom = &ref->getTarget(); + if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName()); + std::map::iterator startFrom = followOnStarts.find(atom); + std::map::iterator startTo = followOnStarts.find(targetAtom); + if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) { + // this is first time we've seen either atom, make simple cluster of the two + if ( log ) fprintf(stderr, " new cluster\n"); + followOnStarts[atom] = atom; + followOnStarts[targetAtom] = atom; + followOnNexts[atom] = targetAtom; + followOnNexts[targetAtom] = NULL; + } + else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) { + // atom is at end of an existing cluster, so append target to end of cluster + if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName()); + followOnNexts[atom] = targetAtom; + followOnNexts[targetAtom] = NULL; + followOnStarts[targetAtom] = followOnStarts[atom]; + } + else { + // gerneral case of inserting into an existing cluster + if ( followOnNexts[atom] != NULL ) { + // an atom with two follow-ons is illegal + warning("can't order %s because both %s and %s must follow it", + atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName()); + } + else { + // there already exists an atom that says target must be its follow-on + const ObjectFile::Atom* originalStart = startTo->second; + const ObjectFile::Atom* originalPrevious = originalStart; + while ( followOnNexts[originalPrevious] != targetAtom ) + originalPrevious = followOnNexts[originalPrevious]; + bool otherIsAlias = (originalPrevious->getSize() == 0); + bool thisIsAlias = (atom->getSize() == 0); + if ( !otherIsAlias && !thisIsAlias ) { + warning("can't order %s because both %s and %s must preceed it", + targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName()); + } + else if ( otherIsAlias ) { + if ( originalPrevious == originalStart ) { + // other is alias at start of cluster, make this the new start of cluster + if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName()); + followOnNexts[atom] = originalPrevious; + for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) + followOnStarts[nextAtom] = atom; + } + else { + // other is alias in middle of cluster, insert new atom before it + if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); + followOnStarts[atom] = originalStart; + followOnNexts[atom] = originalPrevious; + for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) { + if ( followOnNexts[a] == originalPrevious ) { + followOnNexts[a] = atom; + break; + } + } + } + } + else { + // this is alias, so it can go inbetween originalPrevious and targetAtom + if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); + followOnStarts[atom] = originalStart; + followOnNexts[atom] = followOnNexts[originalPrevious]; + followOnNexts[originalPrevious] = atom; + } + } + } + } + } + } + + if ( log ) { + for(std::map::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it) + fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName()); + + for(std::map::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it) + fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null"); + } + + // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals + ordinalOverrideMap = &theOrdinalOverrideMap; + uint32_t index = 0; + uint32_t matchCount = 0; + std::vector& orderedSymbols = fOptions.orderedSymbols(); + for(std::vector::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) { + ObjectFile::Atom* atom = this->findAtom(*it); + if ( atom != NULL ) { + std::map::iterator start = followOnStarts.find(atom); + if ( start != followOnStarts.end() ) { + // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together + for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) { + std::map::iterator pos = theOrdinalOverrideMap.find(nextAtom); + if ( pos == theOrdinalOverrideMap.end() ) { + theOrdinalOverrideMap[nextAtom] = index++; + if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath()); + } + else { + if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", + atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] ); + } + } + } + else { + theOrdinalOverrideMap[atom] = index; + if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + ++matchCount; + //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName); + } + ++index; + } + if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) { + warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() ); + } + } + + // sort atoms + std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap)); + + //fprintf(stderr, "Sorted atoms:\n"); + //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName()); + //} +} + + +// make sure given addresses are within reach of branches, etc +void Linker::tweakLayout() +{ + // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB + if ( fTotalSize > 0x7F000000 ) { + fBiggerThanTwoGigOutput = true; + + if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 ) + throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); + + // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment + Section* hugeZeroFills = Section::find("__huge", "__DATA", true); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) ) + atom->setSection(hugeZeroFills); + } + } +} + + +void Linker::writeDotOutput() +{ + const char* dotOutFilePath = fOptions.dotOutputFile(); + if ( dotOutFilePath != NULL ) { + FILE* out = fopen(dotOutFilePath, "w"); + if ( out != NULL ) { + // print header + fprintf(out, "digraph dg\n{\n"); + fprintf(out, "\tconcentrate = true;\n"); + fprintf(out, "\trankdir = LR;\n"); + + // print each atom as a node + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() != fOutputFile ) { + const char* name = atom->getDisplayName(); + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); + } + else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + char cstring[atom->getSize()+2]; + atom->copyRawContent((uint8_t*)cstring); + fprintf(out, "\taddr%p [ label = \"string: '", atom); + for (const char* s=cstring; *s != '\0'; ++s) { + if ( *s == '\n' ) + fprintf(out, "\\\\n"); + else + fputc(*s, out); + } + fprintf(out, "'\" ];\n"); + } + else { + fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name); + } + } + } + fprintf(out, "\n"); + + // print each reference as an edge + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* fromAtom = *it; + if ( fromAtom->getFile() != fOutputFile ) { + std::vector& references = fromAtom->getReferences(); + std::set seenTargets; + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + ObjectFile::Atom* toAtom = &(reference->getTarget()); + if ( seenTargets.count(toAtom) == 0 ) { + seenTargets.insert(toAtom); + fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); + } + } + } + } + fprintf(out, "\n"); + + // push all imports to bottom of graph + fprintf(out, "{ rank = same; "); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() != fOutputFile ) + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fprintf(out, "addr%p; ", atom); + } + } + fprintf(out, "};\n "); + + // print footer + fprintf(out, "}\n"); + fclose(out); + } + else { + warning("could not write dot output file: %s", dotOutFilePath); + } + } +} + +ObjectFile::Atom* Linker::entryPoint(bool orInit) +{ + // if main executable, find entry point atom + ObjectFile::Atom* entryPoint = NULL; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + if ( entryPoint == NULL ) { + throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); + } + break; + case Options::kDynamicLibrary: + if ( orInit && (fOptions.initFunctionName() != NULL) ) { + entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); + if ( entryPoint == NULL ) { + throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); + } + } + break; + case Options::kObjectFile: + case Options::kDynamicBundle: + entryPoint = NULL; + break; + } + return entryPoint; +} + +ObjectFile::Atom* Linker::dyldHelper() +{ + return fGlobalSymbolTable.find("dyld_stub_binding_helper"); +} + +ObjectFile::Atom* Linker::dyldLazyLibraryHelper() +{ + return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper"); +} + +const char* Linker::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + + +// +// The stab strings are of the form: +// ':' +// but the contain a colon. +// For C++ may contain a double colon (e.g. std::string:f(0,1) ) +// For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) +// +const char* Linker::truncateStabString(const char* str) +{ + enum { start, inObjc } state = start; + for (const char* s = str; *s != 0; ++s) { + char c = *s; + switch (state) { + case start: + if ( c == '[' ) { + state = inObjc; + } + else { + if ( c == ':' ) { + if ( s[1] == ':' ) { + ++s; + } + else { + // found colon + // Duplicate strndup behavior here. + int trunStrLen = s-str+2; + char* temp = new char[trunStrLen+1]; + memcpy(temp, str, trunStrLen); + temp[trunStrLen] = '\0'; + return temp; + } + } + } + break; + case inObjc: + if ( c == ']' ) { + state = start; + } + break; + } + } + // malformed + return str; +} + + +bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) +{ + switch(stab.type){ + case N_GSYM: + case N_STSYM: + case N_LCSYM: + case N_FUN: + // these all need truncated strings + stab.string = truncateStabString(stab.string); + return true; + case N_SO: + case N_OSO: + case N_OPT: + case N_SOL: + // these are included in the minimal stabs, but they keep their full string + return true; + default: + return false; + } +} + + +struct HeaderRange { + std::vector::iterator begin; + std::vector::iterator end; + int parentRangeIndex; + uint32_t sum; + bool sumPrecomputed; + bool useEXCL; + bool cannotEXCL; // because of SLINE, etc stabs +}; + + +typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> PathToSums; + +// hash table that maps header path to a vector of known checksums for that path +static PathToSums sKnownBINCLs; + + +void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) +{ + const bool log = false; + bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); + std::vector* readerStabs = reader->getStabs(); + if ( readerStabs == NULL ) + return; + + if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); + std::vector ranges; + int curRangeIndex = -1; + int count = 0; + ObjectFile::Atom* atomWithLowestOrdinal = NULL; + ObjectFile::Atom* atomWithHighestOrdinal = NULL; + uint32_t highestOrdinal = 0; + uint32_t lowestOrdinal = UINT_MAX; + std::vector > soRanges; + // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums + // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + ++count; + switch ( it->type ) { + case N_BINCL: + { + HeaderRange range; + range.begin = it; + range.end = readerStabs->end(); + range.parentRangeIndex = curRangeIndex; + range.sum = it->value; + range.sumPrecomputed = (range.sum != 0); + range.useEXCL = false; + range.cannotEXCL = false; + curRangeIndex = ranges.size(); + if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string); + ranges.push_back(range); + } + break; + case N_EINCL: + if ( curRangeIndex == -1 ) { + warning("EINCL missing BINCL in %s", reader->getPath()); + } + else { + ranges[curRangeIndex].end = it+1; + if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); + curRangeIndex = ranges[curRangeIndex].parentRangeIndex; + } + break; + case N_FUN: + { + std::map::iterator pos = atomOrdinals.find(it->atom); + if ( pos != atomOrdinals.end() ) { + uint32_t ordinal = pos->second; + if ( ordinal > highestOrdinal ) { + highestOrdinal = ordinal; + atomWithHighestOrdinal = it->atom; + } + if ( ordinal < lowestOrdinal ) { + lowestOrdinal = ordinal; + atomWithLowestOrdinal = it->atom; + } + } + } + // fall through + case N_BNSYM: + case N_ENSYM: + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + case N_STSYM: + case N_LCSYM: + if ( curRangeIndex != -1 ) { + ranges[curRangeIndex].cannotEXCL = true; + if ( fOptions.warnStabs() ) + warning("cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + } + break; + case N_SO: + if ( (it->string != NULL) && (strlen(it->string) > 0) ) { + // start SO, reset hi/low FUN tracking + atomWithLowestOrdinal = NULL; + atomWithHighestOrdinal = NULL; + highestOrdinal = 0; + lowestOrdinal = UINT_MAX; + } + else { + // end SO, record hi/low atoms for this SO range + soRanges.push_back(std::make_pair(atomWithLowestOrdinal, atomWithHighestOrdinal)); + } + // fall through + default: + if ( curRangeIndex != -1 ) { + if ( ! ranges[curRangeIndex].sumPrecomputed ) { + uint32_t sum = 0; + const char* s = it->string; + char c; + while ( (c = *s++) != 0 ) { + sum += c; + // don't checkusm first number (file index) after open paren in string + if ( c == '(' ) { + while(isdigit(*s)) + ++s; + } + } + ranges[curRangeIndex].sum += sum; + } + } + + } + } + if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); + if ( curRangeIndex != -1 ) + warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath()); + + // if no BINCLs + if ( ranges.size() == 0 ) { + unsigned int soIndex = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + // copy minimal or all stabs + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( soIndex < soRanges.size() ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + } + fStabs.push_back(stab); + } + } + return; + } + + //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); + //for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { + // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); + //} + + // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL + for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { + if ( ! it->cannotEXCL ) { + const char* header = it->begin->string; + uint32_t sum = it->sum; + PathToSums::iterator pos = sKnownBINCLs.find(header); + if ( pos != sKnownBINCLs.end() ) { + std::vector& sums = pos->second; + for(std::vector::iterator sit=sums.begin(); sit != sums.end(); ++sit) { + if (*sit == sum) { + //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath()); + it->useEXCL = true; + break; + } + } + if ( ! it->useEXCL ) { + // have seen this path, but not this checksum + //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header); + sums.push_back(sum); + } + } + else { + // have not seen this path, so add to known BINCLs + std::vector empty; + sKnownBINCLs[header] = empty; + sKnownBINCLs[header].push_back(sum); + //fprintf(stderr, "registering checksum %08X for %s\n", sum, header); + } + } + } + + // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs + curRangeIndex = -1; + const int maxRangeIndex = ranges.size(); + int soIndex = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + switch ( it->type ) { + case N_BINCL: + for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) { + if ( ranges[i].begin == it ) { + curRangeIndex = i; + HeaderRange& range = ranges[curRangeIndex]; + ObjectFile::Reader::Stab stab = *it; + stab.value = range.sum; // BINCL and EXCL have n_value set to checksum + if ( range.useEXCL ) + stab.type = N_EXCL; // transform BINCL into EXCL + if ( !minimal ) + fStabs.push_back(stab); + break; + } + } + break; + case N_EINCL: + if ( curRangeIndex != -1 ) { + if ( !ranges[curRangeIndex].useEXCL && !minimal ) + fStabs.push_back(*it); + curRangeIndex = ranges[curRangeIndex].parentRangeIndex; + } + break; + default: + if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + fStabs.push_back(stab); + } + } + } + } + +} + + +// used to prune out atoms that don't need debug notes generated +class NoDebugNoteAtom +{ +public: + NoDebugNoteAtom(const std::map& readersWithDwarfOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} + + bool operator()(const ObjectFile::Atom* atom) const { + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + return true; + if ( atom->getName() == NULL ) + return true; + if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) + return true; + return false; + } + +private: + const std::map& fReadersWithDwarfOrdinals; +}; + +// used to sort atoms with debug notes +class ReadersWithDwarfSorter +{ +public: + ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, + const std::map& atomOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} + + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const + { + // first sort by reader + unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; + unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; + if ( leftReaderIndex != rightReaderIndex ) + return (leftReaderIndex < rightReaderIndex); + + // then sort by atom ordinal + unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; + unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; + return leftAtomIndex < rightAtomIndex; + } + +private: + const std::map& fReadersWithDwarfOrdinals; + const std::map& fAtomOrdinals; +}; + + + + + +void Linker::synthesizeDebugNotes(std::vector& allAtomsByReader) +{ + // synthesize "debug notes" and add them to master stabs vector + const char* dirPath = NULL; + const char* filename = NULL; + bool wroteStartSO = false; + bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL); + __gnu_cxx::hash_set, CStringEquals> seenFiles; + for (std::vector::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { + ObjectFile::Atom* atom = *it; + const char* newDirPath; + const char* newFilename; + //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( filename != NULL ) { + // translation unit change, emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ObjectFile::Reader::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + fStabs.push_back(dirPathStab); + ObjectFile::Reader::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + fStabs.push_back(fileStab); + // Synthesize OSO for start of file + ObjectFile::Reader::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + objStab.other = 0; + objStab.desc = 1; + objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); + objStab.string = assureFullPath(atom->getFile()->getPath()); + fStabs.push_back(objStab); + wroteStartSO = true; + // add the source file path to seenFiles so it does not show up in SOLs + seenFiles.insert(newFilename); + } + filename = newFilename; + dirPath = newDirPath; + if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { + // Synthesize BNSYM and start FUN stabs + ObjectFile::Reader::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + fStabs.push_back(beginSym); + ObjectFile::Reader::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = atom->getName(); + fStabs.push_back(startFun); + // Synthesize any SOL stabs needed + std::vector* lineInfo = atom->getLineInfo(); + if ( lineInfo != NULL ) { + const char* curFile = NULL; + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + if ( it->fileName != curFile ) { + if ( seenFiles.count(it->fileName) == 0 ) { + seenFiles.insert(it->fileName); + ObjectFile::Reader::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = it->fileName; + fStabs.push_back(sol); + } + curFile = it->fileName; + } + } + } + // Synthesize end FUN and ENSYM stabs + ObjectFile::Reader::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + fStabs.push_back(endFun); + ObjectFile::Reader::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + fStabs.push_back(endSym); + } + else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) { + // no stabs for atoms that would not be in the symbol table + } + else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { + // no stabs for absolute symbols + } + else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) { + // no stabs for .eh atoms + } + else { + ObjectFile::Reader::Stab globalsStab; + const char* name = atom->getName(); + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + else { + // Synthesize GSYM stab for other globals + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + } + } + } + + if ( wroteStartSO ) { + // emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } +} + + + + +void Linker::collectDebugInfo() +{ + std::map atomOrdinals; + fStartDebugTime = mach_absolute_time(); + if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { + + // determine mixture of stabs and dwarf + bool someStabs = false; + bool someDwarf = false; + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoNone: + break; + case ObjectFile::Reader::kDebugInfoStabs: + someStabs = true; + break; + case ObjectFile::Reader::kDebugInfoDwarf: + someDwarf = true; + fCreateUUID = true; + break; + case ObjectFile::Reader::kDebugInfoStabsUUID: + someStabs = true; + fCreateUUID = true; + break; + default: + throw "Unhandled type of debug information"; + } + } + } + + if ( someDwarf || someStabs ) { + // try to minimize re-allocations + fStabs.reserve(1024); + + // make mapping from atoms to ordinal + uint32_t ordinal = 1; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atomOrdinals[*it] = ordinal++; + } + } + + // process all dwarf .o files as a batch + if ( someDwarf ) { + // make mapping from readers with dwarf to ordinal + std::map readersWithDwarfOrdinals; + uint32_t readerOrdinal = 1; + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { + readersWithDwarfOrdinals[reader] = readerOrdinal++; + } + } + + // make a vector of atoms + std::vector allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); + // remove those not from a reader that has dwarf + allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), + NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); + // sort by reader then atom ordinal + std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); + // add debug notes for each atom + this->synthesizeDebugNotes(allAtomsByReader); + } + + // process all stabs .o files one by one + if ( someStabs ) { + // get stabs from each reader, in command line order + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoDwarf: + case ObjectFile::Reader::kDebugInfoNone: + // do nothing + break; + case ObjectFile::Reader::kDebugInfoStabs: + case ObjectFile::Reader::kDebugInfoStabsUUID: + collectStabs(reader, atomOrdinals); + break; + default: + throw "Unhandled type of debug information"; + } + } + } + // remove stabs associated with atoms that won't be in output + std::set allAtomsSet; + allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); + fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + } + } +} + +void Linker::writeOutput() +{ + if ( fOptions.forceCpuSubtypeAll() ) + fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; + + fStartWriteTime = mach_absolute_time(); + // tell writer about each segment's atoms + fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), + this->dyldHelper(), this->dyldLazyLibraryHelper(), + fCreateUUID, fCanScatter, + fCurrentCpuConstraint, fBiggerThanTwoGigOutput, + fGlobalSymbolTable.hasExternalWeakDefinitions()); +} + +ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) +{ + // map in whole file + uint64_t len = info.fileLen; + int fd = ::open(info.path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + if ( info.fileLen < 20 ) + throw "file too small"; + + uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file, errno=%d", errno); + + // if fat file, skip to architecture we want + // Note: fat header is always big-endian + const fat_header* fh = (fat_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + uint32_t sliceToUse; + bool sliceFound = false; + if ( fOptions.preferSubArchitecture() ) { + // first try to find a slice that match cpu-type and cpu-sub-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture) + && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( !sliceFound ) { + // look for any slice that matches just cpu-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( sliceFound ) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); + len = OSSwapBigToHostInt32(archs[sliceToUse].size); + // if requested architecture is page aligned within fat file, then remap just that portion of file + if ( (fileOffset & 0x00000FFF) == 0 ) { + // unmap whole file + munmap((caddr_t)p, info.fileLen); + // re-map just part we need + p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); + if ( p == (uint8_t*)(-1) ) + throwf("can't re-map file, errno=%d", errno); + } + else { + p = &p[fileOffset]; + } + } + } + ::close(fd); + + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + break; + } + +#if LTO_SUPPORT + if ( lto::Reader::validFile(p, len, fArchitecture) ) { + return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); + } + else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) { + throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded"; + } +#endif + // error handling + if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + throwf("missing required architecture %s in file", fArchitectureName); + } + else { + throw "file is not of required architecture"; + } +} + +void Linker::logDylib(ObjectFile::Reader* reader, bool indirect) +{ + if ( fOptions.readerOptions().fTraceDylibs ) { + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + if ( indirect ) + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + else + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } +} + + + +ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath) +{ + //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); + InstallNameToReader::iterator pos = fDylibMap.find(installPath); + if ( pos != fDylibMap.end() ) { + return pos->second; + } + else { + // allow -dylib_path option to override indirect library to use + for (std::vector::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) { + if ( strcmp(dit->installName,installPath) == 0 ) {\ + try { + Options::FileInfo info = fOptions.findFile(dit->useInstead); + ObjectFile::Reader* reader = this->createReader(info); + fDylibMap[strdup(installPath)] = reader; + this->logDylib(reader, true); + return reader; + } + catch (const char* msg) { + warning("ignoring -dylib_file option, %s", msg); + } + } + } + char newPath[MAXPATHLEN]; + // handle @loader_path + if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { + strcpy(newPath, fromPath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &installPath[13]); + else + strcpy(newPath, &installPath[13]); + installPath = newPath; + } + // note: @executable_path case is handled inside findFileUsingPaths() + // search for dylib using -F and -L paths + Options::FileInfo info = fOptions.findFileUsingPaths(installPath); + try { + ObjectFile::Reader* reader = this->createReader(info); + fDylibMap[strdup(installPath)] = reader; + this->logDylib(reader, true); + return reader; + } + catch (const char* msg) { + throwf("in %s, %s", info.path, msg); + } + } +} + + +void Linker::processDylibs() +{ + fAllDirectDylibsLoaded = true; + + // mark all dylibs initially specified as required and check if they can be used + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + it->second->setExplicitlyLinked(); + this->checkDylibClientRestrictions(it->second); + } + + // keep processing dylibs until no more dylibs are added + unsigned long lastMapSize = 0; + while ( lastMapSize != fDylibMap.size() ) { + lastMapSize = fDylibMap.size(); + // can't iterator fDylibMap while modifying it, so use temp buffer + std::vector currentUnprocessedReaders; + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( fDylibsProcessed.count(it->second) == 0 ) + currentUnprocessedReaders.push_back(it->second); + } + for (std::vector::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) { + fDylibsProcessed.insert(*it); + (*it)->processIndirectLibraries(this); + } + } + + // go back over original dylibs and mark sub frameworks as re-exported + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + const char* myLeaf = strrchr(fOptions.installPath(), '/'); + if ( myLeaf != NULL ) { + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + const char* childParent = reader->parentUmbrella(); + if ( childParent != NULL ) { + if ( strcmp(childParent, &myLeaf[1]) == 0 ) { + // set re-export bit of info + std::map::iterator pos = fDylibOptionsMap.find(reader); + if ( pos != fDylibOptionsMap.end() ) { + pos->second.fReExport = true; + } + } + } + } + } + } + +} + + + +void Linker::createReaders() +{ + fStartCreateReadersTime = mach_absolute_time(); + std::vector& files = fOptions.getInputFiles(); + const int count = files.size(); + if ( count == 0 ) + throw "no object files specified"; + // add all direct object, archives, and dylibs + for (int i=0; i < count; ++i) { + Options::FileInfo& entry = files[i]; + // ignore /usr/lib/dyld on command line in crt.o build + if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { + try { + this->addInputFile(this->createReader(entry), entry); + } + catch (const char* msg) { + if ( strstr(msg, "architecture") != NULL ) { + if ( fOptions.ignoreOtherArchInputFiles() ) { + // ignore, because this is about an architecture not in use + } + else { + warning("in %s, %s", entry.path, msg); + } + } + else { + throwf("in %s, %s", entry.path, msg); + } + } + } + } + + this->processDylibs(); +} + + + +ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + fNextInputOrdinal += mappedLen; + // remember which readers are archives because they are logged differently + fArchiveReaders.insert(reader); + + // update stats + fTotalArchiveSize += mappedLen; + ++fTotalArchivesLoaded; + return reader; +} + +ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + fNextInputOrdinal += mappedLen; + // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't + if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() ) + fCanScatter = false; + + // update stats + fTotalObjectSize += mappedLen; + ++fTotalObjectLoaded; + return reader; +} + + +void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) +{ + // Check for any restrictions on who can link with this dylib + const char* readerParentName = reader->parentUmbrella() ; + std::vector* clients = reader->getAllowableClients(); + if ( (readerParentName != NULL) || (clients != NULL) ) { + // only dylibs that are in an umbrella or have a client list need verification + const char* installName = fOptions.installPath(); + const char* installNameLastSlash = strrchr(installName, '/'); + bool isParent = false; + bool isSibling = false; + bool isAllowableClient = false; + // There are three cases: + if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) { + // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella + isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 ); + + // hack to support umbrella variants that encode the variant name in the install name + // e.g. CoreServices_profile + if ( !isParent ) { + const char* underscore = strchr(&installNameLastSlash[1], '_'); + if ( underscore != NULL ) { + isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 ); + } + } + + // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent + isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); + } + + if ( !isParent && !isSibling && (clients != NULL) ) { + // case 3) the dylib has a list of allowable clients, and we are creating one of them + const char* clientName = fOptions.clientName(); + int clientNameLen = 0; + if ( clientName != NULL ) { + // use client name as specified on command line + clientNameLen = strlen(clientName); + } + else { + // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) + clientName = installName; + clientNameLen = strlen(clientName); + // starts after last slash + if ( installNameLastSlash != NULL ) + clientName = &installNameLastSlash[1]; + if ( strncmp(clientName, "lib", 3) == 0 ) + clientName = &clientName[3]; + // up to first dot + const char* firstDot = strchr(clientName, '.'); + if ( firstDot != NULL ) + clientNameLen = firstDot - clientName; + // up to first underscore + const char* firstUnderscore = strchr(clientName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) + clientNameLen = firstUnderscore - clientName; + } + + // Use clientName to check if this dylib is able to link against the allowable clients. + for (std::vector::iterator it = clients->begin(); it != clients->end(); it++) { + if ( strncmp(*it, clientName, clientNameLen) == 0 ) + isAllowableClient = true; + } + } + + if ( !isParent && !isSibling && !isAllowableClient ) { + if ( readerParentName != NULL ) { + throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", + reader->getPath(), readerParentName); + } + else { + throwf("cannot link directly with %s", reader->getPath()); + } + } + } + + +} + +ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + fNextInputOrdinal += mappedLen; + if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { + // this is a "blank" stub + // silently ignore it + return reader; + } + // add to map of loaded dylibs + const char* installPath = reader->getInstallPath(); + if ( installPath != NULL ) { + InstallNameToReader::iterator pos = fDylibMap.find(installPath); + if ( pos == fDylibMap.end() ) { + fDylibMap[strdup(installPath)] = reader; + } + else { + InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); + if ( pos2 == fDylibMap.end() ) + fDylibMap[strdup(reader->getPath())] = reader; + else + warning("duplicate dylib %s", reader->getPath()); + } + } + else if ( info.options.fBundleLoader ) + fBundleLoaderReader = reader; + + // log direct readers + if ( !fAllDirectDylibsLoaded ) + this->logDylib(reader, false); + + // update stats + ++fTotalDylibsLoaded; + + return reader; +} + + +void Linker::logTraceInfo (const char* format, ...) +{ + static int trace_file = -1; + char trace_buffer[MAXPATHLEN * 2]; + char *buffer_ptr; + int length; + ssize_t amount_written; + const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile; + + if(trace_file == -1) { + if(trace_file_path != NULL) { + trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); + if(trace_file == -1) + throwf("Could not open or create trace file: %s", trace_file_path); + } + else { + trace_file = fileno(stderr); + } + } + + va_list ap; + va_start(ap, format); + length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); + va_end(ap); + buffer_ptr = trace_buffer; + + while(length > 0) { + amount_written = write(trace_file, buffer_ptr, length); + if(amount_written == -1) + /* Failure to write shouldn't fail the build. */ + return; + buffer_ptr += amount_written; + length -= amount_written; + } +} + + + +void Linker::createWriter() +{ + fStartCreateWriterTime = mach_absolute_time(); + + // make a vector out of all required dylibs in fDylibMap + std::vector dynamicLibraries; + // need to preserve command line order + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) { + if ( reader == mit->second ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = reader; + dylibInfo.options = fDylibOptionsMap[reader]; + dynamicLibraries.push_back(dylibInfo); + break; + } + } + } + // then add any other dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->implicitlyLinked() ) { + // if not already in dynamicLibraries + bool alreadyInDynamicLibraries = false; + for (std::vector::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) { + if ( dit->reader == it->second ) { + alreadyInDynamicLibraries = true; + break; + } + } + if ( ! alreadyInDynamicLibraries ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = it->second; + std::map::iterator pos = fDylibOptionsMap.find(it->second); + if ( pos != fDylibOptionsMap.end() ) { + dylibInfo.options = pos->second; + } + else { + dylibInfo.options.fWeakImport = false; // FIX ME + dylibInfo.options.fReExport = false; + dylibInfo.options.fBundleLoader = false; + } + dynamicLibraries.push_back(dylibInfo); + } + } + } + if ( fBundleLoaderReader != NULL ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = fBundleLoaderReader; + dylibInfo.options.fWeakImport = false; + dylibInfo.options.fReExport = false; + dylibInfo.options.fBundleLoader = true; + dynamicLibraries.push_back(dylibInfo); + } + + const char* path = fOptions.getOutputFilePath(); + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_POWERPC64: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_I386: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_X86_64: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_ARM: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + default: + throw "unknown architecture"; + } +} + + +Linker::SymbolTable::SymbolTable(Linker& owner) + : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false) +{ +} + +void Linker::SymbolTable::require(const char* name) +{ + //fprintf(stderr, "require(%s)\n", name); + Mapper::iterator pos = fTable.find(name); + if ( pos == fTable.end() ) { + fTable[name] = NULL; + ++fRequireCount; + } +} + +// convenience labels for 2-dimensional switch statement +enum AllDefinitionCombinations { + kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition, + kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition, + kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition, + kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition, + kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol +}; + +bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) +{ + bool useNew = true; + bool checkVisibilityMismatch = false; + const char* name = newAtom.getName(); + if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { + switch ( newAtom.getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + fHasExternalTentativeDefinitions = true; + break; + case ObjectFile::Atom::kWeakDefinition: + fHasExternalWeakDefinitions = true; + break; + default: + break; + } + } + //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); + Mapper::iterator pos = fTable.find(name); + ObjectFile::Atom* existingAtom = NULL; + if ( pos != fTable.end() ) + existingAtom = pos->second; + if ( existingAtom != NULL ) { + // already have atom with same name in symbol table + switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { + case kRegAndReg: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kRegAndWeak: + // ignore new weak atom, because we already have a non-weak one + useNew = false; + break; + case kRegAndTent: + // ignore new tentative atom, because we already have a regular one + useNew = false; + checkVisibilityMismatch = true; + if ( newAtom.getSize() > existingAtom->getSize() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "is smaller than the real definition of size %llu from %s", + newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), + existingAtom->getSize(), existingAtom->getFile()->getPath()); + } + break; + case kRegAndExtern: + // ignore external atom, because we already have a one + useNew = false; + break; + case kRegAndExternWeak: + // ignore external atom, because we already have a one + useNew = false; + break; + case kRegAndAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case kWeakAndReg: + // replace existing weak atom with regular one + break; + case kWeakAndWeak: + // have another weak atom, use whichever has largest alignment requirement + // because codegen of some client may require alignment + useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); + checkVisibilityMismatch = true; + break; + case kWeakAndTent: + // replace existing weak atom with tentative one ??? + break; + case kWeakAndExtern: + // keep weak atom, at runtime external one may override + useNew = false; + break; + case kWeakAndExternWeak: + // keep weak atom, at runtime external one may override + useNew = false; + break; + case kWeakAndAbsolute: + // replace existing weak atom with absolute one + break; + case kTentAndReg: + // replace existing tentative atom with regular one + checkVisibilityMismatch = true; + if ( newAtom.getSize() < existingAtom->getSize() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s", + newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), + newAtom.getSize(), newAtom.getFile()->getPath()); + } + break; + case kTentAndWeak: + // replace existing tentative atom with weak one ??? + break; + case kTentAndTent: + // use largest + checkVisibilityMismatch = true; + if ( newAtom.getSize() < existingAtom->getSize() ) { + useNew = false; + } + else { + if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) + warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName()); + } + break; + case kTentAndExtern: + case kTentAndExternWeak: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("replacing common symbol %s from %s with true definition from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + } + break; + case kTentAndAbsolute: + // replace tentative with absolute (can't size check because absolutes have no size) + break; + case kExternAndReg: + // replace external atom with regular one + break; + case kExternAndWeak: + // replace external atom with weak one + break; + case kExternAndTent: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("replacing defintion of %s from dylib %s with common symbol from %s", + newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + } + break; + case kExternAndExtern: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kExternAndExternWeak: + // keep strong dylib atom, ignore weak one + useNew = false; + break; + case kExternAndAbsolute: + // replace external atom with absolute one + break; + case kExternWeakAndReg: + // replace existing weak external with regular + break; + case kExternWeakAndWeak: + // replace existing weak external with weak (let dyld decide at runtime which to use) + break; + case kExternWeakAndTent: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("replacing defintion of %s from dylib %s with common symbol from %s", + newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + } + break; + case kExternWeakAndExtern: + // replace existing weak external with external + break; + case kExternWeakAndExternWeak: + // keep existing external weak + useNew = false; + break; + case kExternWeakAndAbsolute: + // replace existing weak external with absolute + break; + case kAbsoluteAndReg: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kAbsoluteAndWeak: + // ignore new weak atom, because we already have a non-weak one + useNew = false; + break; + case kAbsoluteAndTent: + // ignore new tentative atom, because we already have a regular one + useNew = false; + break; + case kAbsoluteAndExtern: + // ignore external atom, because we already have a one + useNew = false; + break; + case kAbsoluteAndExternWeak: + // ignore external atom, because we already have a one + useNew = false; + break; + case kAbsoluteAndAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + } + } + if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) { + warning("%s has different visibility (%s) in %s and (%s) in %s", + newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath()); + } + if ( useNew ) { + fTable[name] = &newAtom; + if ( existingAtom != NULL ) + fOwner.markDead(existingAtom); + } + else { + fOwner.markDead(&newAtom); + } + return useNew; +} + + + +ObjectFile::Atom* Linker::SymbolTable::find(const char* name) +{ + Mapper::iterator pos = fTable.find(name); + if ( pos != fTable.end() ) { + return pos->second; + } + return NULL; +} + + +void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector& undefines) +{ + for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { + if ( (it->second == NULL) || (andWeakDefintions && (it->second->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)) ) { + undefines.push_back(it->first); + } + } +} + + + +bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) +{ + if ( left == right ) + return false; + + // first sort by section order (which is already sorted by segment) + unsigned int leftSectionIndex = left->getSection()->getIndex(); + unsigned int rightSectionIndex = right->getSection()->getIndex(); + if ( leftSectionIndex != rightSectionIndex) + return (leftSectionIndex < rightSectionIndex); + + // if a -order_file is specified, then sorting is altered to sort those symbols first + if ( fOverriddenOrdinalMap != NULL ) { + std::map::iterator leftPos = fOverriddenOrdinalMap->find(left); + std::map::iterator rightPos = fOverriddenOrdinalMap->find(right); + std::map::iterator end = fOverriddenOrdinalMap->end(); + if ( leftPos != end ) { + if ( rightPos != end ) { + // both left and right are overridden, so compare overridden ordinals + return leftPos->second < rightPos->second; + } + else { + // left is overridden and right is not, so left < right + return true; + } + } + else { + if ( rightPos != end ) { + // right is overridden and left is not, so right < left + return false; + } + else { + // neither are overridden, do default sort + // fall into default sorting below + } + } + } + + // the __common section can have real or tentative definitions + // we want the real ones to sort before tentative ones + bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); + bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); + if ( leftIsTent != rightIsTent ) + return rightIsTent; + + // lastly sort by atom ordinal. this is already sorted by .o order + return left->getOrdinal() < right->getOrdinal(); +} + + +int main(int argc, const char* argv[]) +{ + const char* archName = NULL; + bool showArch = false; + bool archInferred = false; + try { + // create linker object given command line arguments + Linker ld(argc, argv); + + // save error message prefix + archName = ld.architectureName(); + archInferred = ld.isInferredArchitecture(); + showArch = ld.showArchitectureInErrors(); + + // open all input files + ld.createReaders(); + + // open output file + ld.createWriter(); + + // do linking + ld.link(); + } + catch (const char* msg) { + if ( archInferred ) + fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); + else if ( showArch ) + fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); + else + fprintf(stderr, "ld: %s\n", msg); + return 1; + } + + return 0; +} diff --git a/ld64/FireOpal/src/machochecker.cpp b/ld64/FireOpal/src/machochecker.cpp new file mode 100644 index 0000000..311809b --- /dev/null +++ b/ld64/FireOpal/src/machochecker.cpp @@ -0,0 +1,965 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +template +class MachOChecker +{ +public: + static bool validFile(const uint8_t* fileContent); + static MachOChecker* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) + { return new MachOChecker(fileContent, fileLength, path); } + virtual ~MachOChecker() {} + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + + MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); + void checkMachHeader(); + void checkLoadCommands(); + void checkSection(const macho_segment_command

* segCmd, const macho_section

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

* reloc); + void checkLocalReloation(const macho_relocation_info

* reloc); + pint_t relocBase(); + bool addressInWritableSegment(pint_t address); + + const char* fPath; + const macho_header

* fHeader; + uint32_t fLength; + const char* fStrings; + const char* fStringsEnd; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const macho_dysymtab_command

* fDynamicSymbolTable; + const uint32_t* fIndirectTable; + uint32_t fIndirectTableCount; + const macho_relocation_info

* fLocalRelocations; + uint32_t fLocalRelocationsCount; + const macho_relocation_info

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

* fFirstSegment; + const macho_segment_command

* fFirstWritableSegment; +}; + + + +template <> +bool MachOChecker::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 MachOChecker::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 MachOChecker::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_I386 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool MachOChecker::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_X86_64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool MachOChecker::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: + return true; + } + return false; +} + +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } + +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) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a mach-o file that can be checked"; + + fPath = strdup(path); + fHeader = (const macho_header

*)fileContent; + + // sanity check header + checkMachHeader(); + + // check load commands + checkLoadCommands(); + + checkIndirectSymbolTable(); + + checkRelocations(); + + checkSymbolTable(); +} + + +template +void MachOChecker::checkMachHeader() +{ + if ( (fHeader->sizeofcmds() + sizeof(macho_header

)) > fLength ) + throw "sizeofcmds in mach_header is larger than file"; + + uint32_t flags = fHeader->flags(); + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000; + if ( flags & invalidBits ) + throw "invalid bits in mach_header flags"; + if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) + throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs"; +} + +template +void MachOChecker::checkLoadCommands() +{ + // check that all load commands fit within the load command space file + const macho_encryption_info_command

* encryption_info = 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(); + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & this->loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > endOfLoadCommands ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + switch ( cmd->cmd() ) { + case macho_segment_command

::CMD: + case LC_SYMTAB: + case LC_UNIXTHREAD: + case LC_DYSYMTAB: + case LC_LOAD_DYLIB: + case LC_ID_DYLIB: + case LC_LOAD_DYLINKER: + case LC_ID_DYLINKER: + case macho_routines_command

::CMD: + case LC_SUB_FRAMEWORK: + case LC_SUB_CLIENT: + case LC_TWOLEVEL_HINTS: + case LC_PREBIND_CKSUM: + case LC_LOAD_WEAK_DYLIB: + case LC_LAZY_LOAD_DYLIB: + case LC_UUID: + case LC_REEXPORT_DYLIB: + case LC_SEGMENT_SPLIT_INFO: + case LC_CODE_SIGNATURE: + break; + case LC_ENCRYPTION_INFO: + encryption_info = (macho_encryption_info_command

*)cmd; + break; + case LC_SUB_UMBRELLA: + case LC_SUB_LIBRARY: + if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS ) + throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; + break; + default: + throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd()); + } + cmd = (const macho_load_command

*)endOfCmd; + } + + // check segments + cmd = cmds; + std::vector > segmentAddressRanges; + std::vector > segmentFileOffsetRanges; + const macho_segment_command

* linkEditSegment = 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; + if ( segCmd->cmdsize() != (sizeof(macho_segment_command

) + segCmd->nsects() * sizeof(macho_section_content

)) ) + throw "invalid segment load command size"; + + // see if this overlaps another segment address range + uint64_t startAddr = segCmd->vmaddr(); + uint64_t endAddr = startAddr + segCmd->vmsize(); + for (typename std::vector >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) { + if ( it->first < startAddr ) { + if ( it->second > startAddr ) + throw "overlapping segment vm addresses"; + } + else if ( it->first > startAddr ) { + if ( it->first < endAddr ) + throw "overlapping segment vm addresses"; + } + else { + throw "overlapping segment vm addresses"; + } + segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); + } + // see if this overlaps another segment file offset range + uint64_t startOffset = segCmd->fileoff(); + uint64_t endOffset = startOffset + segCmd->filesize(); + for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) { + if ( it->first < startOffset ) { + if ( it->second > startOffset ) + throw "overlapping segment file data"; + } + else if ( it->first > startOffset ) { + if ( it->first < endOffset ) + throw "overlapping segment file data"; + } + else { + throw "overlapping segment file data"; + } + segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); + // check is within file bounds + if ( (startOffset > fLength) || (endOffset > fLength) ) + throw "segment file data is past end of file"; + } + // verify it fits in file + if ( startOffset > fLength ) + throw "segment fileoff does not fit in file"; + if ( endOffset > fLength ) + throw "segment fileoff+filesize does not fit in file"; + + // keep LINKEDIT segment + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + linkEditSegment = segCmd; + + // cache interesting segments + if ( fFirstSegment == NULL ) + fFirstSegment = segCmd; + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { + if ( fFirstWritableSegment == NULL ) + fFirstWritableSegment = segCmd; + if ( segCmd->vmaddr() > 0x100000000ULL ) + fWriteableSegmentWithAddrOver4G = true; + } + + // check section ranges + const macho_section

* 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 + if ( sect->addr() < startAddr ) + throwf("section %s vm address not within segment", sect->sectname()); + if ( (sect->addr()+sect->size()) > endAddr ) + throwf("section %s vm address not within segment", sect->sectname()); + if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) { + if ( sect->offset() < startOffset ) + throwf("section %s file offset not within segment", sect->sectname()); + if ( (sect->offset()+sect->size()) > endOffset ) + throwf("section %s file offset not within segment", sect->sectname()); + } + checkSection(segCmd, sect); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // verify there was a LINKEDIT segment + if ( linkEditSegment == NULL ) + throw "no __LINKEDIT segment"; + + // checks for executables + bool isStaticExecutable = false; + if ( fHeader->filetype() == MH_EXECUTE ) { + isStaticExecutable = true; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_LOAD_DYLINKER: + // the existence of a dyld load command makes a executable dynamic + isStaticExecutable = false; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( isStaticExecutable ) { + if ( fHeader->flags() != MH_NOUNDEFS ) + throw "invalid bits in mach_header flags for static executable"; + } + } + + // verify encryption info + if ( encryption_info != NULL ) { + if ( fHeader->filetype() != MH_EXECUTE ) + throw "LC_ENCRYPTION_INFO load command is only legal in main executables"; + if ( encryption_info->cryptoff() < (sizeof(macho_header

) + fHeader->sizeofcmds()) ) + throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands"; + if ( (encryption_info->cryptoff() % 4096) != 0 ) + throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned"; + if ( (encryption_info->cryptsize() % 4096) != 0 ) + throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized"; + for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); + it != segmentFileOffsetRanges.end(); ++it) { + if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) { + if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second ) + throw "LC_ENCRYPTION_INFO load command is not contained within one segment"; + } + } + } + + // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO + cmd = cmds; + bool foundDynamicSymTab = false; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); + if ( symtab->symoff() < linkEditSegment->fileoff() ) + throw "symbol table not in __LINKEDIT"; + if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist

*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "symbol table end not in __LINKEDIT"; + if ( (symtab->symoff() % sizeof(pint_t)) != 0 ) + throw "symbol table start not pointer aligned"; + fStrings = (char*)fHeader + symtab->stroff(); + fStringsEnd = fStrings + symtab->strsize(); + if ( symtab->stroff() < linkEditSegment->fileoff() ) + throw "string pool not in __LINKEDIT"; + if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "string pool extends beyond __LINKEDIT"; + if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed + throw "string pool start not pointer aligned"; + if ( (symtab->strsize() % sizeof(pint_t)) != 0 ) + throw "string pool size not a multiple of pointer size"; + } + break; + case LC_DYSYMTAB: + { + if ( isStaticExecutable ) + throw "LC_DYSYMTAB should not be used in static executable"; + foundDynamicSymTab = true; + fDynamicSymbolTable = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); + fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); + if ( fIndirectTableCount != 0 ) { + if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() ) + throw "indirect symbol table not in __LINKEDIT"; + if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "indirect symbol table not in __LINKEDIT"; + if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 ) + throw "indirect symbol table not pointer aligned"; + } + fLocalRelocationsCount = fDynamicSymbolTable->nlocrel(); + if ( fLocalRelocationsCount != 0 ) { + fLocalRelocations = (const macho_relocation_info

*)((char*)fHeader + fDynamicSymbolTable->locreloff()); + if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() ) + throw "local relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "local relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 ) + throw "local relocations table not pointer aligned"; + } + fExternalRelocationsCount = fDynamicSymbolTable->nextrel(); + if ( fExternalRelocationsCount != 0 ) { + fExternalRelocations = (const macho_relocation_info

*)((char*)fHeader + fDynamicSymbolTable->extreloff()); + if ( fDynamicSymbolTable->extreloff() < linkEditSegment->fileoff() ) + throw "external relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "external relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->extreloff() % sizeof(pint_t)) != 0 ) + throw "external relocations table not pointer aligned"; + } + } + break; + case LC_SEGMENT_SPLIT_INFO: + { + if ( isStaticExecutable ) + throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; + const macho_linkedit_data_command

* info = (struct macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "split seg info not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "split seg info not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "split seg info table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "split seg info size not a multiple of pointer size"; + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( !isStaticExecutable && !foundDynamicSymTab ) + throw "missing dynamic symbol table"; + if ( fStrings == NULL ) + throw "missing symbol table"; + +} + +template +void MachOChecker::checkSection(const macho_segment_command

* segCmd, const macho_section

* sect) +{ + uint8_t sectionType = (sect->flags() & SECTION_TYPE); + if ( sectionType == S_ZEROFILL ) { + if ( sect->offset() != 0 ) + throwf("section offset should be zero for zero-fill section %s", sect->sectname()); + } + + // more section tests here +} + +template +void MachOChecker::checkIndirectSymbolTable() +{ + 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 start = 0; + uint32_t elementSize = 0; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + elementSize = sect->reserved2(); + start = sect->reserved1(); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + elementSize = sizeof(pint_t); + start = sect->reserved1(); + break; + } + if ( elementSize != 0 ) { + uint32_t count = sect->size() / elementSize; + if ( (count*elementSize) != sect->size() ) + throwf("%s section size is not an even multiple of element size", sect->sectname()); + if ( (start+count) > fIndirectTableCount ) + throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount ); + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void MachOChecker::checkSymbolTable() +{ + // verify no duplicate external symbol names + if ( fDynamicSymbolTable != NULL ) { + StringSet externalNames; + const macho_nlist

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

* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()]; + for(const macho_nlist

* p = exportedStart; p < exportedEnd; ++p) { + const char* symName = &fStrings[p->n_strx()]; + if ( externalNames.find(symName) != externalNames.end() ) + throwf("duplicate external symbol: %s", symName); + externalNames.insert(symName); + } + } +} + + +template <> +ppc::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +ppc64::P::uint_t MachOChecker::relocBase() +{ + if ( fWriteableSegmentWithAddrOver4G ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86_64::P::uint_t MachOChecker::relocBase() +{ + // check for split-seg + return fFirstWritableSegment->vmaddr(); +} + +template <> +arm::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + + +template +bool MachOChecker::addressInWritableSegment(pint_t address) +{ + 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; + if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) { + // if segment is writable, we are fine + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) + return true; + // could be a text reloc, make sure section bit is set + const macho_section

* 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 ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) { + // found section for this address, if has relocs we are fine + return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 ); + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return false; +} + + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 2 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 2 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad external relocation length"; + if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "exernal relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 2 ) + throw "bad external relocation length"; + if ( reloc->r_type() != ARM_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_address() & R_SCATTERED ) { + // scattered + const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; + // FIX + + } + else { + // ignore pair relocs + if ( reloc->r_type() == PPC_RELOC_PAIR ) + return; + // FIX + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); + } +} + + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + // FIX +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_address() & R_SCATTERED ) { + // scattered + const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_length() != 2 ) + throw "bad local scattered relocation length"; + if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR ) + throw "bad local scattered relocation type"; + } + else { + if ( reloc->r_length() != 2 ) + throw "bad local relocation length"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; + } +} + +template +void MachOChecker::checkRelocations() +{ + // external relocations should be sorted to minimize dyld symbol lookups + // therefore every reloc with the same r_symbolnum value should be contiguous + std::set previouslySeenSymbolIndexes; + uint32_t lastSymbolIndex = 0xFFFFFFFF; + const macho_relocation_info

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

* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { + this->checkExternalReloation(reloc); + if ( reloc->r_symbolnum() != lastSymbolIndex ) { + if ( previouslySeenSymbolIndexes.count(reloc->r_symbolnum()) != 0 ) + throw "external relocations not sorted"; + previouslySeenSymbolIndexes.insert(lastSymbolIndex); + lastSymbolIndex = reloc->r_symbolnum(); + } + } + + const macho_relocation_info

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

* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { + this->checkLocalReloation(reloc); + } +} + + +static void check(const char* path) +{ + struct stat stat_buf; + + try { + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throw "cannot open file"; + ::fstat(fd, &stat_buf); + uint32_t length = stat_buf.st_size; + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == ((uint8_t*)(-1)) ) + throw "cannot map file"; + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + size_t offset = OSSwapBigToHostInt32(archs[i].offset); + size_t size = OSSwapBigToHostInt32(archs[i].size); + unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); + + switch(cputype) { + case CPU_TYPE_POWERPC: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, ppc slice does not contain ppc mach-o"; + break; + case CPU_TYPE_I386: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, i386 slice does not contain i386 mach-o"; + break; + case CPU_TYPE_POWERPC64: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; + break; + case CPU_TYPE_X86_64: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; + break; + case CPU_TYPE_ARM: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, arm slice does not contain arm mach-o"; + break; + default: + throwf("in universal file, unknown architecture slice 0x%x\n", cputype); + } + } + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else { + throw "not a known file type"; + } + } + catch (const char* msg) { + throwf("%s in %s", msg, path); + } +} + + +int main(int argc, const char* argv[]) +{ + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-no_content") == 0 ) { + + } + else { + throwf("unknown option: %s\n", arg); + } + } + else { + check(arg); + } + } + } + catch (const char* msg) { + fprintf(stderr, "machocheck failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/ld64/FireOpal/src/rebase.cpp b/ld64/FireOpal/src/rebase.cpp new file mode 100644 index 0000000..ad9b905 --- /dev/null +++ b/ld64/FireOpal/src/rebase.cpp @@ -0,0 +1,945 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + +static bool verbose = false; + +__attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +class AbstractRebaser +{ +public: + virtual cpu_type_t getArchitecture() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual void setBaseAddress(uint64_t) = 0; +}; + + +template +class Rebaser : public AbstractRebaser +{ +public: + Rebaser(const void* machHeader); + virtual ~Rebaser() {} + + virtual cpu_type_t getArchitecture() const; + virtual uint64_t getBaseAddress() const; + virtual uint64_t getVMSize() const; + virtual void setBaseAddress(uint64_t); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; }; + + void setRelocBase(); + void buildSectionTable(); + void adjustLoadCommands(); + void adjustSymbolTable(); + void adjustDATA(); + void doLocalRelocation(const macho_relocation_info

* reloc); + pint_t* mappedAddressForVMAddress(uint32_t vmaddress); + + const macho_header

* fHeader; + pint_t fOrignalVMRelocBaseAddress; + pint_t fSlide; + pint_t fRelocBase; + std::vector fVMMApping; +}; + + + +class MultiArchRebaser +{ +public: + MultiArchRebaser(const char* path, bool writable=false); + ~MultiArchRebaser(); + + const std::vector& getArchs() const { return fRebasers; } + void commit(); + +private: + std::vector fRebasers; + void* fMappingAddress; + uint64_t fFileSize; +}; + + + +MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) + : fMappingAddress(0), fFileSize(0) +{ + // map in whole file + int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small %s", path); + const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file %s, errno=%d", path, errno); + ::close(fd); + + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); + try { + switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { + case CPU_TYPE_POWERPC: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_POWERPC64: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_I386: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_X86_64: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_ARM: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + default: + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + } + else { + try { + if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { + fRebasers.push_back(new Rebaser(mh)); + } + else { + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + + fMappingAddress = p; + fFileSize = stat_buf.st_size; +} + + +MultiArchRebaser::~MultiArchRebaser() +{ + ::munmap(fMappingAddress, fFileSize); +} + +void MultiArchRebaser::commit() +{ + ::msync(fMappingAddress, fFileSize, MS_ASYNC); +} + + + +template +Rebaser::Rebaser(const void* machHeader) + : fHeader((const macho_header

*)machHeader) +{ + switch ( fHeader->filetype() ) { + case MH_DYLIB: + if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 ) + throw "split-seg dylibs cannot be rebased"; + break; + case MH_BUNDLE: + break; + default: + throw "file is not a dylib or bundle"; + } + +} + +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM; } + +template +uint64_t Rebaser::getBaseAddress() const +{ + uint64_t lowestSegmentAddress = LLONG_MAX; + 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; + if ( segCmd->vmaddr() < lowestSegmentAddress ) { + lowestSegmentAddress = segCmd->vmaddr(); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return lowestSegmentAddress; +} + +template +uint64_t Rebaser::getVMSize() const +{ + const macho_segment_command

* highestSegmentCmd = NULL; + 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; + if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) { + highestSegmentCmd = segCmd; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096)); +} + + +template +void Rebaser::setBaseAddress(uint64_t addr) +{ + // calculate slide + fSlide = addr - this->getBaseAddress(); + + // compute base address for relocations + this->setRelocBase(); + + // build cache of section index to section + this->buildSectionTable(); + + // update load commands + this->adjustLoadCommands(); + + // update symbol table + this->adjustSymbolTable(); + + // update writable segments that have internal pointers + this->adjustDATA(); +} + +template +void Rebaser::adjustLoadCommands() +{ + const macho_segment_command

* highestSegmentCmd = NULL; + 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) { + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear timestamp so that any prebound clients are invalidated + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(1); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear expected timestamps so that this image will load with invalid prebinding + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(2); + } + break; + case macho_routines_command

::CMD: + // update -init command + { + struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; + routines->set_init_address(routines->init_address() + fSlide); + } + break; + case macho_segment_command

::CMD: + // update segment commands + { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + seg->set_vmaddr(seg->vmaddr() + fSlide); + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + fSlide); + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Rebaser::buildSectionTable() +{ + // build vector of sections + 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

* seg = (macho_segment_command

*)cmd; + vmmap mapping; + mapping.vmaddr = seg->vmaddr(); + mapping.vmsize = seg->vmsize(); + mapping.fileoff = seg->fileoff(); + fVMMApping.push_back(mapping); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Rebaser::adjustSymbolTable() +{ + const macho_dysymtab_command

* dysymtab = NULL; + macho_nlist

* symbolTable = NULL; + + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (macho_nlist

*)(((uint8_t*)fHeader) + symtab->symoff()); + } + break; + case LC_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all exports and slide their n_value + macho_nlist

* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()]; + for (macho_nlist

* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) == N_SECT ) + entry->set_n_value(entry->n_value() + fSlide); + } + + // walk all local symbols and slide their n_value + macho_nlist

* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; + for (macho_nlist

* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { + if ( entry->n_sect() != NO_SECT ) + entry->set_n_value(entry->n_value() + fSlide); + } + + // FIXME ¥¥¥ adjust dylib_module if it exists +} + +template +void Rebaser::adjustDATA() +{ + const macho_dysymtab_command

* dysymtab = NULL; + + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all local relocations and slide every pointer + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + dysymtab->locreloff()); + const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + this->doLocalRelocation(reloc); + } + + // walk non-lazy-pointers and slide the ones that are LOCAL + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); + for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + uint32_t pointerCount = sect->size() / sizeof(pint_t); + pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); + for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { + if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { + P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + +} + + +template +typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(uint32_t vmaddress) +{ + for(typename std::vector::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) { + //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize); + if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) { + return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader); + } + } + throwf("reloc address 0x%08X not found", vmaddress); +} + + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info* reloc) +{ + if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + else { + throw "invalid relocation type"; + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == ARM_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == ARM_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } +} + + +template +void Rebaser::setRelocBase() +{ + // reloc addresses are from the start of the mapped file (base address) + fRelocBase = (pint_t)fHeader; + fOrignalVMRelocBaseAddress = this->getBaseAddress(); + //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress); +} + +template <> +void Rebaser::setRelocBase() +{ + // reloc addresses either: + // 1) from the base address if no writable segment is > 4GB from base address + // 2) from start of first writable segment + const macho_load_command

* 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; + if ( segCmd->initprot() & VM_PROT_WRITE ) { + if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) { + // found writable segment with address > 4GB past base address + fRelocBase = segCmd->fileoff() + (pint_t)fHeader; + fOrignalVMRelocBaseAddress = segCmd->vmaddr(); + return; + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // just use base address + fRelocBase = (pint_t)fHeader; + fOrignalVMRelocBaseAddress = this->getBaseAddress(); +} + +template <> +void Rebaser::setRelocBase() +{ + // reloc addresses are always based from the start of the first writable segment + 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; + if ( segCmd->initprot() & VM_PROT_WRITE ) { + fRelocBase = segCmd->fileoff() + (pint_t)fHeader; + fOrignalVMRelocBaseAddress = segCmd->vmaddr(); + return; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + throw "no writable segment"; +} + + +static void copyFile(const char* srcFile, const char* dstFile) +{ + // open files + int src = open(srcFile, O_RDONLY); + if ( src == -1 ) + throwf("can't open file %s, errno=%d", srcFile, errno); + struct stat stat_buf; + if ( fstat(src, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", srcFile, errno); + + // create new file with all same permissions to hold copy of dylib + ::unlink(dstFile); + int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); + if ( dst == -1 ) + throwf("can't create temp file %s, errnor=%d", dstFile, errno); + + // mark source as "don't cache" + (void)fcntl(src, F_NOCACHE, 1); + // we want to cache the dst because we are about to map it in and modify it + + // copy permission bits + if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 ) + throwf("can't chmod temp file %s, errno=%d", dstFile, errno); + if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1) + throwf("can't chown temp file %s, errno=%d", dstFile, errno); + + // copy contents + ssize_t len; + const uint32_t kBufferSize = 128*1024; + static uint8_t* buffer = NULL; + if ( buffer == NULL ) { + vm_address_t addr = 0; + if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS ) + buffer = (uint8_t*)addr; + else + throw "can't allcoate copy buffer"; + } + while ( (len = read(src, buffer, kBufferSize)) > 0 ) { + if ( write(dst, buffer, len) == -1 ) + throwf("write failure copying feil %s, errno=%d", dstFile, errno); + } + + // close files + int result1 = close(dst); + int result2 = close(src); + if ( (result1 != 0) || (result2 != 0) ) + throw "can't close file"; +} + + +// scan dylibs and collect size info +// calculate new base address for each dylib +// rebase each file +// copy to temp and mmap +// update content +// unmap/flush +// rename + +struct archInfo { + cpu_type_t arch; + uint64_t vmSize; + uint64_t orgBase; + uint64_t newBase; +}; + +struct fileInfo +{ + fileInfo(const char* p) : path(p) {} + + const char* path; + std::vector archs; +}; + +// +// add archInfos to fileInfo for every slice of a fat file +// for ppc, there may be duplicate architectures (with different sub-types) +// +static void setSizes(fileInfo& info, const std::set& onlyArchs) +{ + const MultiArchRebaser mar(info.path); + const std::vector& rebasers = mar.getArchs(); + for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { + AbstractRebaser* rebaser = *rit; + if ( rebaser->getArchitecture() == *ait ) { + archInfo ai; + ai.arch = *ait; + ai.vmSize = rebaser->getVMSize(); + ai.orgBase = rebaser->getBaseAddress(); + ai.newBase = 0; + //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize); + info.archs.push_back(ai); + } + } + } +} + +static const char* nameForArch(cpu_type_t arch) +{ + switch( arch ) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_POWERPC64: + return "ppca64"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + return "arm"; + } + return "unknown"; +} + +static void rebase(const fileInfo& info) +{ + // generate temp file name + char realFilePath[PATH_MAX]; + if ( realpath(info.path, realFilePath) == NULL ) { + throwf("realpath() failed on %s, errno=%d", info.path, errno); + } + const char* tempPath; + asprintf((char**)&tempPath, "%s_rebase", realFilePath); + + // copy whole file to temp file + copyFile(info.path, tempPath); + + try { + // rebase temp file + MultiArchRebaser mar(tempPath, true); + const std::vector& rebasers = mar.getArchs(); + for(std::vector::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) { + for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { + if ( (*rit)->getArchitecture() == fait->arch ) { + (*rit)->setBaseAddress(fait->newBase); + if ( verbose ) + printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path); + } + } + } + + // flush temp file out to disk + mar.commit(); + + // rename + int result = rename(tempPath, info.path); + if ( result != 0 ) { + throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno); + } + + // make sure every really gets out to disk + ::sync(); + } + catch (const char* msg) { + // delete temp file + ::unlink(tempPath); + + // throw exception with file name added + const char* newMsg; + asprintf((char**)&newMsg, "%s for file %s", msg, info.path); + throw newMsg; + } +} + +static uint64_t totalVMSize(cpu_type_t arch, std::vector& files) +{ + uint64_t totalSize = 0; + for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { + fileInfo& fi = *fit; + for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { + if ( fait->arch == arch ) + totalSize += fait->vmSize; + } + } + return totalSize; +} + +static uint64_t startAddress(cpu_type_t arch, std::vector& files, uint64_t lowAddress, uint64_t highAddress) +{ + if ( lowAddress != 0 ) + return lowAddress; + else if ( highAddress != 0 ) { + uint64_t totalSize = totalVMSize(arch, files); + if ( highAddress < totalSize ) + throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize); + return highAddress - totalSize; + } + else { + if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) { + // place dylibs below dyld + uint64_t topAddr = 0x8FE00000; + uint64_t totalSize = totalVMSize(arch, files); + if ( totalSize > topAddr ) + throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize); + return topAddr - totalSize; + } + else if ( arch == CPU_TYPE_POWERPC64 ) { + return 0x200000000ULL; + } + else if ( arch == CPU_TYPE_X86_64 ) { + return 0x200000000ULL; + } + else if ( arch == CPU_TYPE_ARM ) { + // place dylibs below dyld + uint64_t topAddr = 0x2FE00000; + uint64_t totalSize = totalVMSize(arch, files); + if ( totalSize > topAddr ) + throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize); + return topAddr - totalSize; + } + else + throw "unknown architecture"; + } +} + +static void usage() +{ + fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch ] files...\n"); +} + + +int main(int argc, const char* argv[]) +{ + std::vector files; + std::set onlyArchs; + uint64_t lowAddress = 0; + uint64_t highAddress = 0; + + try { + // parse command line options + char* endptr; + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-v") == 0 ) { + verbose = true; + } + else if ( strcmp(arg, "-low_address") == 0 ) { + lowAddress = strtoull(argv[++i], &endptr, 16); + } + else if ( strcmp(arg, "-high_address") == 0 ) { + highAddress = strtoull(argv[++i], &endptr, 16); + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = argv[++i]; + if ( strcmp(arch, "ppc") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC); + else if ( strcmp(arch, "ppc64") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC64); + else if ( strcmp(arch, "i386") == 0 ) + onlyArchs.insert(CPU_TYPE_I386); + else if ( strcmp(arch, "x86_64") == 0 ) + onlyArchs.insert(CPU_TYPE_X86_64); + else if ( strcmp(arch, "arm") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM); + else if ( strcmp(arch, "armv6") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM); + else + throwf("unknown architecture %s", arch); + } + else { + usage(); + throwf("unknown option: %s\n", arg); + } + } + else { + files.push_back(fileInfo(arg)); + } + } + + if ( files.size() == 0 ) + throw "no files specified"; + + // use all architectures if no restrictions specified + if ( onlyArchs.size() == 0 ) { + onlyArchs.insert(CPU_TYPE_POWERPC); + onlyArchs.insert(CPU_TYPE_POWERPC64); + onlyArchs.insert(CPU_TYPE_I386); + onlyArchs.insert(CPU_TYPE_X86_64); + onlyArchs.insert(CPU_TYPE_ARM); + } + + // scan files and collect sizes + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + setSizes(*it, onlyArchs); + } + + // assign new base address for each arch + for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + cpu_type_t arch = *ait; + uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress); + for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { + fileInfo& fi = *fit; + for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { + if ( fait->arch == arch ) { + fait->newBase = baseAddress; + baseAddress += fait->vmSize; + baseAddress = (baseAddress + 4095) & (-4096); // page align + } + } + } + } + + // rebase each file if it contains something rebaseable + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + fileInfo& fi = *it; + if ( fi.archs.size() > 0 ) + rebase(fi); + } + + } + catch (const char* msg) { + fprintf(stderr, "rebase failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/ld64/FireOpal/unit-tests/README b/ld64/FireOpal/unit-tests/README new file mode 100644 index 0000000..a0fd0a2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/README @@ -0,0 +1,28 @@ + +The easy way to run all tests is within Xcode. Just select "unit-tests" as the target and click Build. + +When run from within Xcode, the just built linker will be used. If you cd into a test case and run it, the +installed linker (e.g. /usr/bin/ld) will be used. + +Each test case is a directory with a Makefile. The Makefile default target should do whatever work is necessary +to perform the test. If successful is should print "PASS xxx" where xxx is the name of the test case. Otherwise +it should print "FAIL xxx reason". If nothing is printed (for instance a tool crashed), the harness will +automatically print that it failed. The harness will always pass ARCH to the Makefile to specify which +architecture to test. The Makefile should also have a "clean" target which removes and generated files. + + +There are some utility functions for use in Makefiles for generating the PASS/FAIL strings: + + ${PASS_IFF} can be put in front of the last command in the make rule and it will print PASS + if the command returned 0 or FAIL otherwise. Example: + ${PASS_IFF} ${CC} foo.c -o foo + Will print PASS if and only if the compilation succeeded + + ${PASS_IFF_EMPTY} can have data piped into it. It prints PASS if there is no data, otherwise FAIL. + Example: + otool -hv foo.o | grep SUBSECTIONS_VIA_SYMBOLS | ${PASS_IFF_EMPTY} + Will print PASS if and only if the output of otool does not contain SUBSECTIONS_VIA_SYMBOLS + + + + diff --git a/ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl b/ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl new file mode 100755 index 0000000..fcc65eb --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_UNLESS} "test name" command +# + +use strict; + +my $string = shift @ARGV; +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $string\n"); +} +else +{ + printf("PASS $string\n"); +} + +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl b/ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl new file mode 100755 index 0000000..7fb54b2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl -w + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(system(@ARGV) != 0) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl b/ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl new file mode 100755 index 0000000..7859888 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl b/ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl new file mode 100755 index 0000000..a44f5d3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${FAIL_IF_EMPTY} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl b/ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl new file mode 100755 index 0000000..c0bf290 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${FAIL_IF_STDIN} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + exit 0; +} + +printf("FAIL $test_name\n"); +exit 1; diff --git a/ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl b/ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl new file mode 100755 index 0000000..6d49a25 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${FALL_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl b/ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl new file mode 100755 index 0000000..031a45d --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl @@ -0,0 +1,127 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; +$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, + 'makefile.newtest' => undef, + 'Makefile.newtest' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + push @$cmd, "-f"; + push @$cmd, $makefile; + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/ld64/FireOpal/unit-tests/bin/make-recursive.pl b/ld64/FireOpal/unit-tests/bin/make-recursive.pl new file mode 100755 index 0000000..f860985 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/make-recursive.pl @@ -0,0 +1,123 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; +$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/ld64/FireOpal/unit-tests/bin/mkld b/ld64/FireOpal/unit-tests/bin/mkld new file mode 100755 index 0000000..33aacc1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/mkld @@ -0,0 +1,73 @@ +#!/bin/sh + +hide() +{ + $PROCTOR set_hidden $1 1 >/dev/null +} + +if [ -z "$1" ] + then echo "Usage: mkld HOST [ DBPATH ]" >&2 + exit 1 +fi + +if [ -z "$PROCTOR" ] + then PROCTOR=proctor +fi + +DBNAME="$2" +[ -z "$DBNAME" ] && DBNAME=ld +PROCTOR="$PROCTOR $1 $DBNAME" + +$PROCTOR tools gcc g++ objc obj-c++ libstdc++ ld ld ld_classic cctools +$PROCTOR sysattrs \ + ld64="ld64" \ + ld="ld (ld_classic)" \ + gcc="GCC" \ + cctools="cctools" \ + os="OS Build" \ + processor=Processor \ + platform=Platform \ + hostname="Hostname" \ + gcc_opts="gcc options" \ + g++_opts="g++ options" \ + objc_opts="objc options" \ + obj-c++_opts="obj-c++ options" \ + libstdc++_opts="libstdc++ options" \ + LANG="LANG environment variable" \ + LC_CTYPE="LC_CTYPE environment variable" \ + LC_MESSAGES="LC_MESSAGES environment variable" \ + LC_ALL="LC_ALL environment variable" \ + TMPDIR="TMPDIR environment variable" \ + GCC_EXEC_PREFIX="GCC_EXEC_PREFIX environment variable" \ + COMPILER_PATH="COMPILER_PATH environment variable" \ + LIBRARY_PATH="LIBRARY_PATH environment variable" \ + LANG="LANG environment variable" \ + CPATH="CPATH environment variable" \ + C_INCLUDE_PATH="C_INCLUDE_PATH environment variable" \ + CPLUS_INCLUDE_PATH="CPLUS_INCLUDE_PATH environment variable" \ + OBJC_INCLUDE_PATH="OBJC_INCLUDE_PATH environment variable" \ + DEPENDENCIES_OUTPUT="DEPENDENCIES_OUTPUT environment variable" \ + SUNPRO_DEPENDENCIES="SUNPRO_DEPENDENCIES environment variable" \ + +for TOOL in gcc g++ objc obj-c++ libstdc++ + do hide ${TOOL}_opts +done + +hide LANG +hide LC_CTYPE +hide LC_MESSAGES +hide LC_ALL +hide TMPDIR +hide GCC_EXEC_PREFIX +hide COMPILER_PATH +hide LIBRARY_PATH +hide LANG +hide CPATH +hide C_INCLUDE_PATH +hide CPLUS_INCLUDE_PATH +hide OBJC_INCLUDE_PATH +hide DEPENDENCIES_OUTPUT +hide SUNPRO_DEPENDENCIES + +$PROCTOR results PASS=1 XFAIL=1 KFAIL=1 FAIL=0 XPASS=0 KPASS=0 UNRESOLVED=0 TIMEDOUT=0 UNSUPPORTED=0 UNTESTED=0 +$PROCTOR severities logline NOTE WARNING ERROR diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl new file mode 100755 index 0000000..beb76a9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl new file mode 100755 index 0000000..07854b5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 != system(@ARGV)) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl new file mode 100755 index 0000000..19b98b4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${PASS_IFF_EMPTY} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("PASS $test_name\n"); + exit 0; +} + +printf("FAIL $test_name\n"); +exit 1; diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl new file mode 100755 index 0000000..d5ee99f --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${PASS_IFF_STDIN} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("FAIL $test_name\n"); + exit 1 +} + +printf("PASS $test_name\n"); +exit 0; + diff --git a/ld64/FireOpal/unit-tests/bin/result-filter.pl b/ld64/FireOpal/unit-tests/bin/result-filter.pl new file mode 100755 index 0000000..8168e79 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/result-filter.pl @@ -0,0 +1,131 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Find; +use Cwd; + +$Data::Dumper::Terse = 1; + +my $root = undef; +my $entry = ''; +my $pass_count = 0; +my $total_count = 0; + +# first match "root: " + +# a line starting with "cwd:" marks the beginning of a new test case +# call process_entry() on each test case +while(<>) +{ + if(m/^root:\s+(.*?)$/) + { + $root = $1; + } + elsif(m/^cwd:\s+(.*?)$/) + { + if(length($entry)) + { + &process_entry($root, $entry); + $entry = ''; + } + $entry .= $_; + } + else + { + $entry .= $_; + } +} +# don't forget last test case (no cwd: to mark end) +if(length($entry)) +{ + &process_entry($root, $entry); +} + +# show totals +my $percentage = $pass_count * 100 / $total_count; +printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; + + +sub process_entry +{ + my ($root, $lines) = @_; + + # build an associative array of keys to value(s) + my $lines_seq = [split /\n/, $lines]; + #print Dumper($lines_seq); + my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; + my $line; + foreach $line (@$lines_seq) + { + if($line =~ m/^(\w+):\s+(.*)$/) + { + my $key = $1; + my $val = $2; + if(!exists($$tbl{$key})) + { $$tbl{$key} = ''; } + + if($key eq 'stdout' || $key eq 'stderr') # if type is @array + { + push @{$$tbl{$key}}, $val; + } + else + { + $$tbl{$key} .= $val; + } + } + else + { + print "ERROR: $line"; + } + } + #print Dumper($tbl); + #return; + + my $test_name = $$tbl{cwd}; + if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) + { + $test_name = $1; + } + + #if make failed (exit was non-zero), mark this as a failure + if(0 ne $$tbl{exit}) + { + printf "%-40s FAIL Makefile failure\n", $test_name; + $total_count++; + return; + } + + #if there was any output to stderr, mark this as a failure + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + return; + } + + # scan all stdout looking for lines that start with PASS or FAIL + my $seen_result = 0; + foreach $line (@{$$tbl{stdout}}) + { + if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) + { + $total_count++; + if($line =~ m/^PASS.+/) + { + $pass_count++; + } + else + { + # only print failure lines + printf "%-40s %s\n", $test_name, $line; + } + $seen_result = 1; + } + } + if(!$seen_result) + { + printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; + $total_count++; + } +} diff --git a/ld64/FireOpal/unit-tests/bin/rm-stale-test-logs b/ld64/FireOpal/unit-tests/bin/rm-stale-test-logs new file mode 100755 index 0000000..936dec1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/bin/rm-stale-test-logs @@ -0,0 +1,36 @@ +#!/bin/sh + +usage() { + echo Usage: $0 number-of-tests-logs-to-keep + echo where number-of-tests-logs-to-keep must be a non-zero integer + exit +} + +# Usage: if no arguments +[ -z "$1" ] && usage + +# Check if requesting 0 tests to remain! +[ "$1" -ne 0 ] + +# don't test directly--use the result value +# because the command can fail for badly formed integers +[ $? -ne 0 ] && usage + +# get the dir names of all tests in date order +ls -1dtr /tmp/proctor*>/tmp/all$$ 2>/dev/null + +# select the last few to keep +tail -$1 /tmp/all$$>/tmp/keep$$ + +# get a list of the others +DELLIST=`diff /tmp/all$$ /tmp/keep$$|grep '^<'|sed -e 's/^< //'` + +# any work to do? +if [ "$DELLIST" ] +then + echo rm -rf $DELLIST + rm -rf $DELLIST +fi + +# rm the temps +rm /tmp/all$$ /tmp/keep$$ diff --git a/ld64/FireOpal/unit-tests/clean-tests b/ld64/FireOpal/unit-tests/clean-tests new file mode 100755 index 0000000..4c38b4e --- /dev/null +++ b/ld64/FireOpal/unit-tests/clean-tests @@ -0,0 +1,63 @@ +find_makefile() +{ + local j + + MF="" + + if [ ! -d $1 ] + then + return 1 + fi + + for j in Makefile makefile Makefile.newtest makefile.newtest + do + [ -f $1/$j ] && MF=$j + done + + [ "$MF" ] && return 0 + return 1 +} + +find_path_to_test_dir() +{ + # FIND THE PATH TO THE TEST DIR + # SO THAT WE CAN ADD THE BIN DIR INTO + # THE SEARCH PATH + + # remember the top level execution dir + chmod +x "$0" # just in case + + #add path to $0 into search + savedir=$PWD + DIRNAME=`dirname $0` + [ -d "$DIRNAME" ] && cd "$DIRNAME" + PATH=$PATH:$PWD + cd "$savedir" + + chmod +x "$0" # just in case + EXECNAME=`which $0` + DIRNAME=`dirname "$EXECNAME"` + if [ -d "$DIRNAME" ] + then + TEST_HOME_DIR="$DIRNAME" + else + TEST_HOME_DIR="$savedir" # Give up and assume current dir + fi + + PATH="$PATH":"$TEST_HOME_DIR"/bin:"$savedir" +} + +find_path_to_test_dir + +cd "$TEST_HOME_DIR/test-cases" + +for i in * +do + [ -d "$i" ] && + ( + if find_makefile $i + then + make -C $i -f $MF -s -k clean >/dev/null 2>/dev/null + fi + ) +done diff --git a/ld64/FireOpal/unit-tests/include/common.makefile b/ld64/FireOpal/unit-tests/include/common.makefile new file mode 100644 index 0000000..70e05f8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/include/common.makefile @@ -0,0 +1,76 @@ +# stuff to include in every test Makefile + +SHELL = /bin/sh + +# set default to be host +ARCH ?= $(shell arch) + +# set default to be all +VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6" + +MYDIR=$(shell cd ../../bin;pwd) + +# if run within Xcode, add the just built tools to the command path +ifdef BUILT_PRODUCTS_DIR + PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH} +else + ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" + RELEASEDIR=$(shell cd ../../../build/Release;pwd) + PATH := ${RELEASEDIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${RELEASEDIR}:${MYDIR}:${COMPILER_PATH} + else + PATH := ${MYDIR}:${PATH}: + COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: + endif +endif +export PATH +export COMPILER_PATH + +LD = ld +OBJECTDUMP = ObjectDump +MACHOCHECK = machocheck +OTOOL = otool + +CC = gcc-4.0 -arch ${ARCH} +CCFLAGS = -Wall -std=c99 +ASMFLAGS = + +CXX = g++-4.0 -arch ${ARCH} +CXXFLAGS = -Wall + +ifeq ($(ARCH),armv6) + LDFLAGS := -syslibroot /Developer/SDKs/Purple + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +ifeq ($(ARCH),thumb) + LDFLAGS := -syslibroot /Developer/SDKs/Purple + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv6 + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +RM = rm +RMFLAGS = -rf + +# utilites for Makefiles +PASS_IFF = pass-iff-exit-zero.pl +PASS_IFF_SUCCESS = ${PASS_IFF} +PASS_IFF_EMPTY = pass-iff-no-stdin.pl +PASS_IFF_STDIN = pass-iff-stdin.pl +FAIL_IFF = fail-iff-exit-zero.pl +FAIL_IFF_SUCCESS = ${FAIL_IFF} +PASS_IFF_ERROR = pass-iff-exit-non-zero.pl +FAIL_IF_ERROR = fail-if-exit-non-zero.pl +FAIL_IF_SUCCESS = fail-if-exit-zero.pl +FAIL_IF_EMPTY = fail-if-no-stdin.pl +FAIL_IF_STDIN = fail-if-stdin.pl +PASS_IFF_GOOD_MACHO = ${PASS_IFF} ${MACHOCHECK} +FAIL_IF_BAD_MACHO = ${FAIL_IF_ERROR} ${MACHOCHECK} +FAIL_IF_BAD_OBJ = ${FAIL_IF_ERROR} ${OBJECTDUMP} >/dev/null diff --git a/ld64/FireOpal/unit-tests/include/test.h b/ld64/FireOpal/unit-tests/include/test.h new file mode 100644 index 0000000..faca714 --- /dev/null +++ b/ld64/FireOpal/unit-tests/include/test.h @@ -0,0 +1,35 @@ +#include +#include + +#define DEFINE_TEST_FUNC(name) \ + static \ + inline \ + void \ + name(const char *format, ...) \ + { \ + va_list args; \ + va_start(args, format); \ + common(stdout, #name, format, args); \ + va_end(args); \ + return; \ + } + +static +inline +void +common(FILE *file, const char *prefix, const char *format, va_list args) +{ + fprintf(file, "%s \"", prefix); + vfprintf(file, format, args); + fprintf(file, "\"\n"); // should check for trailing newline + return; +} + +DEFINE_TEST_FUNC(PASS); +DEFINE_TEST_FUNC(XPASS); +DEFINE_TEST_FUNC(FAIL); +DEFINE_TEST_FUNC(XFAIL); + +DEFINE_TEST_FUNC(UNTESTED); +DEFINE_TEST_FUNC(UNSUPPORTED); +DEFINE_TEST_FUNC(UNRESOLVED); diff --git a/ld64/FireOpal/unit-tests/proctor-run b/ld64/FireOpal/unit-tests/proctor-run new file mode 100755 index 0000000..e7bdea4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/proctor-run @@ -0,0 +1,204 @@ +#!/bin/sh + +all_archs="ppc ppc64 i386 x86_64" + +sysattr() +{ + echo " " +} + +doresults() +{ + local ver + + echo "" + + echo " " + sysattr cctools "`as&1 |sed 's/.*cctools-//;s/,.*//'`" + sysattr hostname "`hostname`" + sysattr os "`uname -r`" + sysattr platform "`uname -m`" + sysattr ld64 "`ld64 -v 2>&1|sed 's/.*PROJECT://;s/ .*//'`" + sysattr ld "`ld_classic -v 2>&1|sed 's/.*cctools-//;s/ .*//'`" + sysattr gcc "`gcc --version|head -1`" + sysattr processor "`uname -p`" + sysattr LANG "$LANG" + sysattr LC_CTYPE "$LC_CTYPE" + sysattr LC_MESSAGES "$LC_MESSAGES" + sysattr LC_ALL "$LC_ALL" + sysattr TMPDIR "$TMPDIR" + sysattr GCC_EXEC_PREFIX "$GCC_EXEC_PREFIX" + sysattr COMPILER_PATH "$COMPILER_PATH" + sysattr LIBRARY_PATH "$LIBRARY_PATH" + sysattr LANG "$LANG" + sysattr CPATH "$CPATH" + sysattr C_INCLUDE_PATH "$C_INCLUDE_PATH" + sysattr CPLUS_INCLUDE_PATH "$CPLUS_INCLUDE_PATH" + sysattr OBJC_INCLUDE_PATH "$OBJC_INCLUDE_PATH" + sysattr DEPENDENCIES_OUTPUT "$DEPENDENCIES_OUTPUT" + sysattr SUNPRO_DEPENDENCIES "$SUNPRO_DEPENDENCIES" + echo " " + + echo "" + echo "" + echo " " + for i in $* + do + echo " " + cat $i + echo " " + done + + echo " " + echo "" + echo "" + echo "" + + #rm $* +} + +find_path_to_test_dir() +{ + # FIND THE PATH TO THE TEST DIR + # SO THAT WE CAN ADD THE BIN DIR INTO + # THE SEARCH PATH + + # remember the top level execution dir + chmod +x "$0" # just in case + + #add path to $0 into search + local savedir + savedir=$PWD + DIRNAME=`dirname $0` + [ -d "$DIRNAME" ] && cd "$DIRNAME" + PATH=$PATH:$PWD + cd "$savedir" + + chmod +x "$0" # just in case + EXECNAME=`which $0` + DIRNAME=`dirname "$EXECNAME"` + if [ -d "$DIRNAME" ] + then + TEST_HOME_DIR=`cd "$DIRNAME";pwd` + fi + + if [ -z "$TEST_HOME_DIR" ] + then + TEST_HOME_DIR="$savedir" # Give up and assume current dir + fi + + cd "$TEST_HOME_DIR" + cd ../build/Release + + PATH="$PWD":"$TEST_HOME_DIR/bin":"$PATH" + cd "$savedir" +} + +start_time=`date +%s` + +find_path_to_test_dir + +# Execute from the location of the script; or if not found the current loc +[ -d $TEST_HOME_DIR/test-cases ] && cd $TEST_HOME_DIR/test-cases || cd test-cases + +rm-stale-test-logs 3 >/dev/null & + +make -C ../src # make sure the binaries are available + +DATEFORMAT=`date +%F-%H%M | sed -e 's/ //'` +tmpdir=/tmp/proctor$DATEFORMAT + +if ! mkdir $tmpdir >/dev/null 2>/dev/null +then + rm -rf $tmpdir + mkdir $tmpdir +fi + + +linestart=0 +if [ x$1 = x-comment ] +then + shift + comment="$1" + shift +fi + +find_makefile() +{ + local j + + MF="" + + if [ ! -d $1 ] + then + return 1 + fi + + for j in Makefile makefile + do + [ -f $1/$j ] && MF=$j + done + + if [ "$NEWTEST" ] + then + for j in Makefile.newtest makefile.newtest + do + [ -f $1/$j ] && MF=$j + done + fi + + [ "$MF" ] && return 0 + return 1 +} + +one_test() +{ + echo cwd: $1 + echo cmd: $1 ARCH="$arch" + make -f "$MF" -C $1 ARCH="$arch" 2>$tmpdir/stderr >$tmpdir/stdout + result=$? + sed 's/^/stdout: /'<$tmpdir/stdout + sed 's/^/stderr: /'<$tmpdir/stderr + echo exit: $? +} + +if [ "$1" ] +then + i="$1" + for arch in $all_archs + do + rm -f $tmpdir/$arch + if find_makefile $i + then + one_test $i + fi + #fi | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch + linestart=`expr $linestart + 10000` + done | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch +else + for arch in $all_archs + do + rm -f $tmpdir/$arch + for i in * + do + if find_makefile $i + then + one_test $i + fi + done | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch + linestart=`expr $linestart + 10000` + done +fi + +(cd $tmpdir; doresults $all_archs)>$tmpdir/o.xml +../bin/xmlparser $tmpdir/o.xml >/dev/null +if [ $? = 0 ] +then + if ! proctor localhost ld import $tmpdir/o.xml + then + proctor database load failed! + fi +else + echo Test results not loaded: internal xml error! + exit 1 +fi diff --git a/ld64/FireOpal/unit-tests/run-all-unit-tests b/ld64/FireOpal/unit-tests/run-all-unit-tests new file mode 100755 index 0000000..e21c2b3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/run-all-unit-tests @@ -0,0 +1,35 @@ +#!/bin/sh + +unset RC_TRACE_DYLIBS +unset RC_TRACE_ARCHIVES +unset LD_TRACE_DYLIBS +unset LD_TRACE_ARCHIVES + +export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +[ "$PROCTORRUN" ] && exec ../proctor-run + +all_archs="x86_64 armv6 thumb ppc ppc64 i386 " +valid_archs="x86_64 armv6 ppc ppc64 i386 " + +# clean first +../bin/make-recursive.pl clean > /dev/null + +mkdir /tmp/$$ +for arch in $all_archs +do + echo "" + echo " * * * Running all unit tests for architecture $arch * * *" + + # build architecture + [ "$NEWTEST" ] && NT=-newtest + + ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$valid_archs" | ../bin/result-filter.pl + + # clean up so svn is happy + ../bin/make-recursive.pl ARCH=$arch clean > /dev/null + + echo "" +done diff --git a/ld64/FireOpal/unit-tests/run-all-unit-tests-debug b/ld64/FireOpal/unit-tests/run-all-unit-tests-debug new file mode 100755 index 0000000..a239748 --- /dev/null +++ b/ld64/FireOpal/unit-tests/run-all-unit-tests-debug @@ -0,0 +1,26 @@ +#!/bin/sh + +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +[ "$PROCTORRUN" ] && exec ../proctor-run + +all_archs="ppc ppc64 i386 x86_64" + +mkdir /tmp/$$ +for arch in $all_archs +do + echo "" + echo " * * * Running all unit tests for architecture $arch * * *" + + # build architecture + [ "$NEWTEST" ] && NT=-newtest + + mkdir /tmp/$$ + ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$all_archs" | tee /tmp/$$/raw | ../bin/result-filter.pl | tee /tmp/$$/sum + + # clean up so svn is happy + ../bin/make-recursive.pl ARCH=$arch clean > /dev/null + + echo "" +done diff --git a/ld64/FireOpal/unit-tests/src/Makefile b/ld64/FireOpal/unit-tests/src/Makefile new file mode 100644 index 0000000..d3bdeab --- /dev/null +++ b/ld64/FireOpal/unit-tests/src/Makefile @@ -0,0 +1,9 @@ + +all: ../bin/results-to-xml ../bin/xmlparser + +../bin/results-to-xml: results-to-xml.cpp + g++ -g -O -Wall $< -o ../bin/results-to-xml + +../bin/xmlparser: + cd xmlparser; xcodebuild -alltargets + cp -p xmlparser/build/Release/xmlparser ../bin/. diff --git a/ld64/FireOpal/unit-tests/src/results-to-xml.cpp b/ld64/FireOpal/unit-tests/src/results-to-xml.cpp new file mode 100644 index 0000000..a305347 --- /dev/null +++ b/ld64/FireOpal/unit-tests/src/results-to-xml.cpp @@ -0,0 +1,260 @@ +#include +#include +#include + +using namespace std; + +#define NELEMENTS(a) (sizeof (a)/sizeof *(a)) + +#define NO_RESULT (-1) +#define PASS 1 +#define FAIL 0 + +#define DBG bhole + +void +bhole(...) +{ +} + +class line_category +{ +public: + const char *key; + char line[99999]; + void (*test)(line_category &); + int test_result; +} lc; + +void +deft(line_category &l) +{ + l.test_result = PASS; +} + +void +pass_fail(line_category &l) +{ + if(FAIL!=l.test_result) + l.test_result = strnstr(l.line, "FAIL", 4)? FAIL: PASS; +} + +void +stderr_output(line_category &l) +{ + if(FAIL==l.test_result) + return; + + if(l.line[0]) + l.test_result = FAIL; +} + +void +exit_test(line_category &l) +{ + if(!atoi(l.line)) { + DBG("exit_test(%s)==%d\n", l.line, atoi(l.line)); + l.test_result = PASS; + } +} + +#define STDOUT "stdout: " +#define STDERR "stderr: " +#define CWD "cwd: " +#define CMD "cmd: " +#define SEXIT "exit: " +line_category line_categories[] = { + { CWD, "" , deft, NO_RESULT}, /* must be first */ + { CMD, "", deft, NO_RESULT}, + { STDOUT, "", pass_fail, NO_RESULT}, + { STDERR, "", stderr_output, NO_RESULT }, + { SEXIT, "", exit_test, NO_RESULT }, +}; + +static line_category no_line_category = { "none", "no test", deft, NO_RESULT }; + +line_category & +retrieve(line_category &l, const char *s) +{ + unsigned j; + line_category *lp = &l; + //int final_result = PASS; + + for(j=0; jkey, s)) { + char *p; + + for(p=(char *)lp->line; *p; ++p) { + switch(*p) { + case '\0': + break; + case '"': + case '<': + *p = ' '; + // fall thru + default: + continue; + } + } +DBG("FOUND line_categories[j].line==%s\n", lp->line); + return line_categories[j]; + } + } + + return no_line_category; +} + +void +xml_string_print(FILE *strm, const char *s) +{ + fputc('"', strm); + for( ; ; ++s) { + switch(*s) { + case '\0': + break; + case '&': + fputs("&", strm); + continue; + default: + fputc(*s, strm); + continue; + } + break; + } + fputc('"', strm); +} + +// +// FAIL if stderr non-zero +// FAIL if stdout=="FAIL" +// UNRESOLVED if make exit non-zero +// PASS otherwise +// + +static int cnt; +void +dump_test(void) +{ + unsigned j; + int final_result = PASS; + + for(j=0; j\n"); + + printf(" \n"); + s = retrieve(line_categories[0], STDERR).line; + if(*s) { + printf(" \n"); + } +#if 1 + s = retrieve(line_categories[0], STDOUT).line; + if(*s) { + printf(" \n"); + } +#endif + printf(" \n"); + printf("\n\n"); + + for(j=0; j1) + cnt = atoi(argv[1]); + + for(;;) { + char line[99999]; + int i; + + line[0] = '\0'; + fgets(line, sizeof line, stdin); + if(feof(stdin)) { + dump_test(); + break; + } + + for(i=0; ; ++i) { + size_t len = strlen(line_categories[i].key); + + //DBG("strnstr(%s, %s, %u)\n", line, line_categories[i].key, len); + if(strnstr(line, line_categories[i].key, len)) { + if(firsttime) + firsttime = 0; + else if(0==i) + dump_test(); + + char *lp = &line[len]; + //DBG("%s%s", line_categories[i].key, lp); + strncpy(line_categories[i].line, lp, sizeof line_categories[i].line); + line_categories[i].test(line_categories[i]); + break; + } + + if(i==NELEMENTS(line_categories)-1) { + DBG("BADLINE:%s", line); + break; + } + } + } + return 0; +} diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 new file mode 100644 index 0000000..6eab06a --- /dev/null +++ b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 @@ -0,0 +1,79 @@ +.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. +.\"See Also: +.\"man mdoc.samples for a complete listing of options +.\"man mdoc for the short list of editing options +.\"/usr/share/misc/mdoc.template +.Dd 9/18/06 \" DATE +.Dt xmlparser 1 \" Program name and manual section number +.Os Darwin +.Sh NAME \" Section Header - required - don't modify +.Nm xmlparser, +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm Other_name_for_same_program(), +.Nm Yet another name for the same program. +.\" Use .Nm macro to designate other names for the documented program. +.Nd This line parsed for whatis database. +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm +.Op Fl abcd \" [-abcd] +.Op Fl a Ar path \" [-a path] +.Op Ar file \" [file] +.Op Ar \" [file ...] +.Ar arg0 \" Underlined argument - use .Ar anywhere to underline +arg2 ... \" Arguments +.Sh DESCRIPTION \" Section Header - required - don't modify +Use the .Nm macro to refer to your program throughout the man page like such: +.Nm +Underlining is accomplished with the .Ar macro like this: +.Ar underlined text . +.Pp \" Inserts a space +A list of items with descriptions: +.Bl -tag -width -indent \" Begins a tagged list +.It item a \" Each item preceded by .It macro +Description of item a +.It item b +Description of item b +.El \" Ends the list +.Pp +A list of flags and their descriptions: +.Bl -tag -width -indent \" Differs from above in tag removed +.It Fl a \"-a flag as a list item +Description of -a flag +.It Fl b +Description of -b flag +.El \" Ends the list +.Pp +.\" .Sh ENVIRONMENT \" May not be needed +.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 +.\" .It Ev ENV_VAR_1 +.\" Description of ENV_VAR_1 +.\" .It Ev ENV_VAR_2 +.\" Description of ENV_VAR_2 +.\" .El +.Sh FILES \" File used or created by the topic of the man page +.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact +.It Pa /usr/share/file_name +FILE_1 description +.It Pa /Users/joeuser/Library/really_long_file_name +FILE_2 description +.El \" Ends the list +.\" .Sh DIAGNOSTICS \" May not be needed +.\" .Bl -diag +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .El +.Sh SEE ALSO +.\" List links in ascending order by section, alphabetically within a section. +.\" Please do not reference files that do not exist without filing a bug report +.Xr a 1 , +.Xr b 1 , +.Xr c 1 , +.Xr a 2 , +.Xr b 2 , +.Xr a 3 , +.Xr b 3 +.\" .Sh BUGS \" Document known, unremedied bugs +.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m new file mode 100644 index 0000000..a9c82fc --- /dev/null +++ b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m @@ -0,0 +1,25 @@ +#import + +int main(int argc, char *argv[]) { + [[NSAutoreleasePool alloc] init]; + + if(argc != 2) { + NSLog(@"Usage: %s path-to-XML\n", argv[0]); + return 1; + } + NSString *path = [NSString stringWithUTF8String:argv[1]]; + + NSError *err = nil; + NSXMLDocument *doc = [[NSXMLDocument alloc] + initWithContentsOfURL:[NSURL + fileURLWithPath:path] + options:0 + error:&err]; + if(err) { + NSLog(@"ERROR: %@", err); + return 1; + } else { + NSLog(@"Parsed!"); + return 0; + } +} diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9492293 --- /dev/null +++ b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj @@ -0,0 +1,218 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 8DD76F9A0486AA7600D96B5E /* xmlparser.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* xmlparser.m */; settings = {ATTRIBUTES = (); }; }; + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; + 8DD76F9F0486AA7600D96B5E /* xmlparser.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* xmlparser.1 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 8DD76F9F0486AA7600D96B5E /* xmlparser.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 08FB7796FE84155DC02AAC07 /* xmlparser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = xmlparser.m; sourceTree = ""; }; + 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32A70AAB03705E1F00C91783 /* xmlparser_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmlparser_Prefix.pch; sourceTree = ""; }; + 8DD76FA10486AA7600D96B5E /* xmlparser */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xmlparser; sourceTree = BUILT_PRODUCTS_DIR; }; + C6859EA3029092ED04C91782 /* xmlparser.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = xmlparser.1; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* xmlparser */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + C6859EA2029092E104C91782 /* Documentation */, + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = xmlparser; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 32A70AAB03705E1F00C91783 /* xmlparser_Prefix.pch */, + 08FB7796FE84155DC02AAC07 /* xmlparser.m */, + ); + name = Source; + sourceTree = ""; + }; + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 08FB779EFE84155DC02AAC07 /* Foundation.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FA10486AA7600D96B5E /* xmlparser */, + ); + name = Products; + sourceTree = ""; + }; + C6859EA2029092E104C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + C6859EA3029092ED04C91782 /* xmlparser.1 */, + ); + name = Documentation; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F960486AA7600D96B5E /* xmlparser */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "xmlparser" */; + buildPhases = ( + 8DD76F990486AA7600D96B5E /* Sources */, + 8DD76F9B0486AA7600D96B5E /* Frameworks */, + 8DD76F9E0486AA7600D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = xmlparser; + productInstallPath = "$(HOME)/bin"; + productName = xmlparser; + productReference = 8DD76FA10486AA7600D96B5E /* xmlparser */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "xmlparser" */; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* xmlparser */; + projectDirPath = ""; + targets = ( + 8DD76F960486AA7600D96B5E /* xmlparser */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F990486AA7600D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9A0486AA7600D96B5E /* xmlparser.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB927508733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = xmlparser_Prefix.pch; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = xmlparser; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB927608733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = xmlparser_Prefix.pch; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = xmlparser; + }; + name = Release; + }; + 1DEB927908733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB927A08733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "xmlparser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927508733DD40010E9CD /* Debug */, + 1DEB927608733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "xmlparser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927908733DD40010E9CD /* Debug */, + 1DEB927A08733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch new file mode 100644 index 0000000..530e057 --- /dev/null +++ b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'xmlparser' target in the 'xmlparser' project. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile new file mode 100644 index 0000000..a3a256f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -O2 optimization fails when trying to make a long 16-byte aligned. +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o + + # verify that the alignment is correct in the .o + ObjectDump -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null + + # now verify the executable + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH} + ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai\>' >/dev/null" + ${PASS_IFF_GOOD_MACHO} tl_test2-${ARCH} + +clean: + rm -rf tl_test2-* diff --git a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt new file mode 100644 index 0000000..eb9eaf5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt @@ -0,0 +1 @@ +Test 16 byte alignment with -O2 optimization. Radar #4662185 diff --git a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c new file mode 100644 index 0000000..ff27fec --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +int i = 1; +int ai __attribute__ ((aligned (16))) = 1; + +int main() { + + long addr = (long)&ai; + i = ai; + + if (addr & 0xf) { + printf("failed: ai = %p\n", (void *)addr); + return 1; + } + + printf("passed: ai = %p\n", (void *)addr); + return 0; + +} diff --git a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile new file mode 100644 index 0000000..806c64e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker processing of absolute symbols +# + +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 + nm -m all.o | grep _myAbs | grep absolute | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker + ${PASS_IFF_GOOD_MACHO} main2 + +clean: + rm -rf main.o abs.o all.o main main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s new file mode 100644 index 0000000..216867c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s @@ -0,0 +1,3 @@ + + .globl _myAbs + .set _myAbs, 0xfe000000 diff --git a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c new file mode 100644 index 0000000..eec3f61 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c @@ -0,0 +1,5 @@ + +extern int* myAbs; + +int main() { return *myAbs; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile new file mode 100644 index 0000000..9e87329 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that added aliases to a .o +# file via the command line is the same as doing so in the sources. +# The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} aliases.s -DALIASES=1 -c -o aliases.source.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.source.${ARCH}.o > aliases.source.${ARCH}.o.dump + + ${CC} ${ASMFLAGS} aliases.s -c -o aliases.tmp.${ARCH}.o + ${FAIL_IF_BAD_OBJ} aliases.tmp.${ARCH}.o + + ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -o aliases.cmdline.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.cmdline.${ARCH}.o > aliases.cmdline.${ARCH}.o.dump + ${FAIL_IF_ERROR} diff aliases.source.${ARCH}.o.dump aliases.cmdline.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.file.${ARCH}.o > aliases.file.${ARCH}.o.dump + ${PASS_IFF} diff aliases.source.${ARCH}.o.dump aliases.file.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s new file mode 100644 index 0000000..ffab4a9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .text + +_temp: nop + nop + + .globl _foo +_foo: nop + nop + +#if ALIASES + .globl _fooalt + .globl _fooalt2 +/* this should make an alias "_fooalt" for "_foo" */ +_fooalt = _foo +_fooalt2 = _foo +#endif + +_bar: nop + nop + + + .subsections_via_symbols \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt new file mode 100644 index 0000000..291f2f7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt @@ -0,0 +1,6 @@ +_foo _fooalt +# comment +_foo _fooalt2 + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile b/ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile new file mode 100644 index 0000000..f4bfdc8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify a .o file with aliases +# can round trip through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} aliases.s -c -o aliases.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.${ARCH}.o -o aliases-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.${ARCH}.o > aliases.${ARCH}.o.dump + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.${ARCH}.o > aliases-r.${ARCH}.o.dump + ${PASS_IFF} diff aliases.${ARCH}.o.dump aliases-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s b/ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s new file mode 100644 index 0000000..b21f8c7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .text + +_temp: nop + nop + + .globl _foo +_foo: nop + nop + + .globl _fooalt + .globl _fooalt2 +/* this should make an alias "_fooalt" for "_foo" */ +_fooalt = _foo +_fooalt2 = _foo + +_bar: nop + nop + + + .subsections_via_symbols \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile b/ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile new file mode 100644 index 0000000..6b6a50f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that the modules of +# symbol _b is maintained. The address for _b must be +# 3 mod 16. Therefore the last hexdigit of the address +# must be 3. + +run: all + +all: + ${CC} ${ASMFLAGS} -dynamiclib -single_module -dead_strip foo.c align.s -exported_symbols_list foo.exp -o foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + nm foo.${ARCH}.dylib | grep "3 d _b" | ${PASS_IFF_STDIN} + +clean: + rm -rf *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s b/ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s new file mode 100644 index 0000000..a288960 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .data + .align 0 +_a: .byte 3 + .byte 3 + .byte 3 + .globl _b +_b: .byte 4 ;# address is 3 + .align 4 +L1: .quad 0 +_c: .long 8 + + .subsections_via_symbols + diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt b/ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt new file mode 100644 index 0000000..240c171 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt @@ -0,0 +1,2 @@ +The point of this test is to verify that the modules of symbol _b is maintained. The address for _b must be +3 mod 16. Therefore the last hexdigit of the address must be 3. diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c b/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c new file mode 100644 index 0000000..0b88dff --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern char b; +int my = 2; + +char foo() +{ + return my+b; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp b/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp new file mode 100644 index 0000000..70eefe9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp @@ -0,0 +1 @@ +_foo diff --git a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile new file mode 100644 index 0000000..6e0be3f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set the stack execution bit properly. +# + +run: all + +all: + +# Test with the flag + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -Wl,-allow_stack_execute + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${FAIL_IF_EMPTY} + rm -f foo-${ARCH} + +# Test without the flag + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${PASS_IFF_EMPTY} + +clean: + rm -rf foo-* diff --git a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt new file mode 100644 index 0000000..525ab2b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt @@ -0,0 +1 @@ +Test the we set the stack execution bit properly. diff --git a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c new file mode 100644 index 0000000..57ed6ba --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c @@ -0,0 +1,4 @@ +int main (void) +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile b/ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile new file mode 100644 index 0000000..ef293a6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile @@ -0,0 +1,110 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that the -allowable_client and -client options +# work when linking against subframeworks. +# + +run: all + +all: +# build with two allowable_clients + ${CC} ${CCFLAGS} -dynamiclib -umbrella foo -allowable_client bar -allowable_client baz foo.c -o foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + +# test that -o works + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libbar.${ARCH}.dylib + +# test that a framework style output works + mkdir -p bar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.framework/bar foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} bar.framework/bar + +# test that second -o works + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + +# test that -o and -install_name works with install_name as an allowable + ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib + +# test that -install_name works with variant name + ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar_profile foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib + +# test that -o and -install_name fails with install_name different than allowable + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.${ARCH}.dylib -install_name /tmp/fail.${ARCH}.dylib foo.${ARCH}.dylib >& fail.log + +# test that a bundle and no client_name fails + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -bundle bar.c -o temp.${ARCH}.bundle foo.${ARCH}.dylib >& fail.log + +# test that a bundle and an allowable client_name passes + ${CC} ${CCFLAGS} -bundle bar.c -client_name bar -o bar.${ARCH}.bundle foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} bar.${ARCH}.bundle + +# test umbrella can link against subs + mkdir -p foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo + ${FAIL_IF_BAD_MACHO} foo.framework/foo + +# test umbrella variant can link against subs + mkdir -p foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo_debug -install_name /path/foo.framework/foo_debug + ${FAIL_IF_BAD_MACHO} foo.framework/foo_debug + +# test sibling in umbrella can link against subs + ${CC} ${CCFLAGS} -dynamiclib main.c -umbrella foo foo.${ARCH}.dylib -o ./main.dylib + ${FAIL_IF_BAD_MACHO} main.dylib + +# test anyone can link against umbrella + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -framework foo -F. + ${FAIL_IF_BAD_MACHO} main.${ARCH} + +# test that an executable and no client_name fails + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} foo.${ARCH}.dylib >& fail.log + +# test that an executable and an allowable client_name passes + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name bar foo.${ARCH}.dylib + ${PASS_IFF_GOOD_MACHO} main.${ARCH} + +# test that a regular dylib can be made unlinkable by using -allowable_client + ${CC} ${CCFLAGS} -dynamiclib foo.c -allowable_client '!' -o unlinkable_foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} unlinkable_foo.${ARCH}.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} unlinkable_foo.${ARCH}.dylib >& fail.log + +# test that a regular dylib can be made linkable by only specially named clients + ${CC} ${CCFLAGS} -dynamiclib foo.c -allowable_client special -o restrictive_foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} restrictive_foo.${ARCH}.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} restrictive_foo.${ARCH}.dylib >& fail.log + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name special restrictive_foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} main.${ARCH} + +# print final pass + ${PASS_IFF_GOOD_MACHO} foo.${ARCH}.dylib + +clean: + rm -rf *.dylib *.bundle main.???* fail.log *.framework diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c new file mode 100644 index 0000000..dbaeef8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c @@ -0,0 +1,6 @@ +extern int foo (); + +int bar (void) +{ + return foo(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c new file mode 100644 index 0000000..dbaeef8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c @@ -0,0 +1,6 @@ +extern int foo (); + +int bar (void) +{ + return foo(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt b/ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt new file mode 100644 index 0000000..0202b5f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt @@ -0,0 +1 @@ +Test that the -allowable_client and -client options work when linking against subframeworks. diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/foo.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allowable-client/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c new file mode 100644 index 0000000..7b76173 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c @@ -0,0 +1,6 @@ +extern int foo (); + +int main (void) +{ + return foo(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile new file mode 100644 index 0000000..892043d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that -ObjC loads all (and only) +# .o files that contain Objective-C code. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + ${CC} ${CCFLAGS} baz.m -c -o baz-${ARCH}.o + ${FAIL_IF_BAD_OBJ} baz-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobarbaz-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobarbaz-${ARCH} -L. -o main-${ARCH} -ObjC -framework Foundation -framework CoreFoundation + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm main-${ARCH} | grep "_bar" | ${FAIL_IF_STDIN} + nm main-${ARCH} | grep "_Foo" | ${FAIL_IF_EMPTY} + nm main-${ARCH} | grep "_Baz" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c new file mode 100644 index 0000000..d9b468b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c @@ -0,0 +1,2 @@ + +int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m new file mode 100644 index 0000000..90ae0a1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m @@ -0,0 +1,8 @@ +#include + +@interface Baz : NSObject +@end + +@implementation Baz +@end + diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c new file mode 100644 index 0000000..dc2fbef --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile new file mode 100644 index 0000000..39c0ef9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that .o files +# can be found in archives. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm main-${ARCH} | grep "_bar" | ${PASS_IFF_EMPTY} + ${CC} ${CCFLAGS} main.c -all_load -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt b/ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt new file mode 100644 index 0000000..0d34170 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that .o files can be found in archives. diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c b/ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c new file mode 100644 index 0000000..57c4c68 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + return foo(); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile new file mode 100644 index 0000000..61f4bc7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ia an archive +# is listed multiple times, the extras are ignored. +# This is done in some makefiles because the traditional linker +# semantics is to only search an archive once. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -lfoobar-${ARCH} -L. -o main-${ARCH} -all_load + ${FAIL_IF_BAD_MACHO} main-${ARCH} + ${CC} ${CCFLAGS} main.c ./libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} -all_load + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c new file mode 100644 index 0000000..57c4c68 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + return foo(); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile new file mode 100644 index 0000000..2d53734 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test if the linker already has a weak definition +# it does not try to find another copy in an archive +# +# There are two case to test: +# 1) both the main .o files and the archive have the same weak symbol (_foo) +# 2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) +# In both cases the linker should ignore the archive. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + ${CC} ${CCFLAGS} baz.c -c -o baz-${ARCH}.o + ${FAIL_IF_BAD_OBJ} baz-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c foo.c -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -m main-${ARCH} | grep _baz | grep weak | ${PASS_IFF_STDIN} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c new file mode 100644 index 0000000..387ccc1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c @@ -0,0 +1,11 @@ + + + +// intentionally not-weak +int baz() +{ + return 1; +} + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt b/ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt new file mode 100644 index 0000000..902d22c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt @@ -0,0 +1,7 @@ +The point of this test if the linker already has a weak definition +it does not try to find another copy in an archive + +There are two case to test: +1) both the main .o files and the archive have the same weak symbol (_foo) +2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) +In both cases the linker should ignore the archive. diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c new file mode 100644 index 0000000..d7003fa --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c @@ -0,0 +1,13 @@ + + +void collisionChecker() { } + + +int __attribute__((weak)) foo() +{ + collisionChecker(); + return 1; +} + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c new file mode 100644 index 0000000..d1ce4a9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); +extern int bar(); + +// intentionally weak +int __attribute__((weak)) baz() +{ + return 1; +} + +int main() +{ + fprintf(stdout, "hello\n"); + return foo() + bar() + baz(); +} + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile b/ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile new file mode 100644 index 0000000..3b1cc7c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Check that ld can figure out architecture from .o files without -arch option +# + +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} + +clean: + rm -rf hello.o hello diff --git a/ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c b/ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile new file mode 100644 index 0000000..346e2b7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(VALID_ARCHS)) + +# +# Test that blank stubs are handled properly +# + +run: all + +all: +# build example fully fat dylib + + gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + # handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo + if [ x${ARCH} != xppc ]; \ + then \ + SUB_ARCH=${ARCH}; \ + else \ + SUB_ARCH=`lipo -info libfoo.dylib | sed 's/.*://;s/ppc64 //;s/.* \(ppc[^ ]*\).*/\1/'`; \ + echo SUB_ARCH $$SUB_ARCH; \ + if [ x$$SUB_ARCH = xALL ]; \ + then \ + SUB_ARCH=ppc; \ + fi \ + fi; \ + lipo libfoo.dylib -remove $$SUB_ARCH -output libfoo.dylib + + lipo -create libfoo.dylib -arch_blank ${ARCH} -output libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${OTOOL} -L main | grep libfoo | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libfoo.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt new file mode 100644 index 0000000..77a8764 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt @@ -0,0 +1 @@ +Test that blank stubs are handled properly diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/foo.c b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c new file mode 100644 index 0000000..6c9f6a4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c @@ -0,0 +1,5 @@ + +int main (void) +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile b/ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile new file mode 100644 index 0000000..c85f382 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ifeq ($(ARCH),armv6) + ARCH_FLAGS = -mlong-branch +else + ARCH_FLAGS = +endif + + + +# +# Simple test for branch islands +# + +run: all + + + +all: + ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} + ${PASS_IFF_GOOD_MACHO} hello + +clean: + rm hello diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c b/ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c new file mode 100644 index 0000000..a1991fe --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c @@ -0,0 +1,8 @@ +#include + + +void foo() +{ + fprintf(stdout, "foo\n"); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c b/ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c new file mode 100644 index 0000000..89caa9d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + foo(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s b/ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s new file mode 100644 index 0000000..dd28637 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s @@ -0,0 +1,39 @@ + +#if __ppc__ + + .text + +_prejunk: + mr r3,r5 + mr r3,r4 + blr + + +_space1: + .space 15*1024*1024 + 2 + + .align 5 +_junk: + mr r3,r5 + mr r3,r4 + blr + + +_space2: + .space 2*1024*1024 + +#endif + + +#if __arm__ + + +_space1: + .space 32*1024*1024 + 2 + + +#endif + + + .subsections_via_symbols + \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile new file mode 100644 index 0000000..96d83b8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Ccheck that ld -bundle_loader works with bundles and executable, +# and _bar is found in the loader and not in libbar.dylib +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + # test with loader as main executable + ${CC} ${CCFLAGS} main.c bar.c -o main + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader main libbar.dylib -o bundle.bundle + ${FAIL_IF_BAD_MACHO} bundle.bundle + nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} + # test with loader as another bundle + ${CC} ${CCFLAGS} -bundle main.c bar.c -o mainbundle + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader mainbundle libbar.dylib -o bundle.bundle + ${FAIL_IF_BAD_MACHO} bundle.bundle + nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} + # verify error message when linking with raw bundle + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c bar.c -o main bundle.bundle 2> error.log + grep "link with bundle" error.log | ${PASS_IFF_STDIN} + +clean: + rm *.dylib main bundle.bundle mainbundle error.log diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c new file mode 100644 index 0000000..b737953 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// funcation called by a loaded bundle +int bar() +{ + return 1; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c new file mode 100644 index 0000000..ba78c28 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c new file mode 100644 index 0000000..c9e53f9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c @@ -0,0 +1,30 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile new file mode 100644 index 0000000..c1ca816 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that cfstring literals are coalesced and dead stripped +# There is 3 CFSTR in foo.c and 1 CFSTR in bar.c +# After coalescing and dead stripping there should be only one CFSTR in the output +# + +ifeq (,${findstring 64,$(ARCH)}) + CFSTRING_SIZE = 16 + CFSTRING_SIZE_WRITABLE = 32 +else + CFSTRING_SIZE = 32 + CFSTRING_SIZE_WRITABLE = 64 +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip + size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} + # now try with -fwritable-strings + ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings + size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE_WRITABLE}" | ${PASS_IFF_STDIN} + +clean: + rm -rf foo foo_writable diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c new file mode 100644 index 0000000..9a84abd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c @@ -0,0 +1,7 @@ +#include + + +void bar() +{ + CFStringGetLength(CFSTR("live")); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c new file mode 100644 index 0000000..fe1b30e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c @@ -0,0 +1,19 @@ +#include + +extern void bar(); + +void foo() +{ + CFStringGetLength(CFSTR("hello")); + CFStringGetLength(CFSTR("world")); +} + + +int main() +{ + CFStringGetLength(CFSTR("live")); + bar(); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile new file mode 100644 index 0000000..3375aae --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that utf16 cfstring literals are not coalesced. +# There is 3 CFSTR in foo.m and 1 CFSTR in bar.m +# After coalescing and dead stripping there should be only one CFSTR in the output +# + +ifeq (,${findstring 64,$(ARCH)}) + CFSTRING_SIZE = 48 +else + CFSTRING_SIZE = 96 +endif + USTRING_SIZE = 37 + + + +run: all + +all: + ${CC} ${CCFLAGS} foo.m bar.m -o foo -framework CoreFoundation -dead_strip + size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} + size -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf foo diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m new file mode 100644 index 0000000..bdf5168 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m @@ -0,0 +1,7 @@ +#include + + +void bar() +{ + CFStringGetLength(CFSTR("über")); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m new file mode 100644 index 0000000..6e4c268 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m @@ -0,0 +1,20 @@ +#include + +extern void bar(); + +void foo() +{ + CFStringGetLength(CFSTR("hello")); + CFStringGetLength(CFSTR("überhund")); +} + + +int main() +{ + CFStringGetLength(CFSTR("über")); + CFStringGetLength(CFSTR("überhund")); + bar(); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile new file mode 100644 index 0000000..d073c0e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker preserves commons with custom alignment +# + +all: + ${CC} ${CCFLAGS} foo.s -c -o foo.o + nm -m foo.o | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY} + ${LD} foo.o -r -o foo2.o + nm -m foo2.o | grep '(alignment 2^6)' | ${PASS_IFF_STDIN} + +clean: + rm -rf foo.o foo2.o diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s b/ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s new file mode 100644 index 0000000..03c28fc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s @@ -0,0 +1,2 @@ + + .comm _mycomm64aligned,15,6 diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile new file mode 100644 index 0000000..5823a61 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify if a header file is missing an extern that there won't be +# duplicates definitions when using -dead_strip. +# + +run: all + +all: + ${CC} ${CCFLAGS} a.c -c -o a.o + ${CC} ${CCFLAGS} b.c -c -o b.o + ${CC} ${CCFLAGS} c.c -c -o c.o + ${CC} -arch ${ARCH} -dynamiclib a.o b.o c.o -o libabc.dylib -dead_strip + ${PASS_IFF_GOOD_MACHO} libabc.dylib + +clean: + rm -rf a.o b.o c.o libabc.dylib one abc.bar.count diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c new file mode 100644 index 0000000..fea7234 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c @@ -0,0 +1,4 @@ +#include "c.h" + +float aa() { return bar; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c new file mode 100644 index 0000000..12e46e8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c @@ -0,0 +1,4 @@ +#include "c.h" + +float bb() { return bar; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c new file mode 100644 index 0000000..165d5e4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c @@ -0,0 +1,3 @@ + +const float bar = 1.0; + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h new file mode 100644 index 0000000..86a61ae --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h @@ -0,0 +1,4 @@ + +// missing extern +const float bar; + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile new file mode 100644 index 0000000..a59a858 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o built with +# commons and one built without commons can be merged. +# +# problem merging .o files built with and without -fno-common +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -fno-common -o bar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs foo.${ARCH}.o bar.${ARCH}.o -o foobar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foobar.${ARCH}.o + nm -m foobar.${ARCH}.o | grep bar | grep __common | ${PASS_IFF_STDIN} + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c new file mode 100644 index 0000000..771802c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c @@ -0,0 +1,2 @@ + +int bar; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c new file mode 100644 index 0000000..42e0a8e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c @@ -0,0 +1,2 @@ + +int foo; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile new file mode 100644 index 0000000..68d809c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker puts commons in the correct order. +# -fno-commons come first, followed by all other commons +# in .o order and alphabetically within each .o +# + +all: + ${CC} ${CCFLAGS} baz.c -fno-common -c -o baz.o + ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main -mmacosx-version-min=10.5 + nm -j -n main | grep _common > symbol.order + ${FAIL_IF_ERROR} diff symbol.order expected.order + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main baz.o symbol.order diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c new file mode 100644 index 0000000..2d258fc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c @@ -0,0 +1,3 @@ +int ddd_common; +int iii_common[4]; +int bbb_common[4]; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c new file mode 100644 index 0000000..6497d29 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c @@ -0,0 +1,3 @@ +int fff_common; +int iii_common[4]; +int ttt_common; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order b/ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order new file mode 100644 index 0000000..4738fc2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order @@ -0,0 +1,8 @@ +_fff_common +_iii_common +_ttt_common +_aaa_common +_eee_common +_ggg_common +_bbb_common +_ddd_common diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c new file mode 100644 index 0000000..ca80a59 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c @@ -0,0 +1,3 @@ +int aaa_common; +int ggg_common[4]; +int eee_common; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/main.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/commons-order/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile new file mode 100644 index 0000000..7586a51 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile @@ -0,0 +1,96 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate cpu subtype preference +# + +test: test-${ARCH} + +test-ppc64: + ${PASS_IFF} true + +test-i386: + ${PASS_IFF} true + +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 + + # check -arch armv4t pulls out V4 slice + ${LD} -r -arch armv4t foo.o -o fooa.o + otool -hv fooa.o | grep V4T | ${FAIL_IF_EMPTY} + nm fooa.o | grep _aaa | ${FAIL_IF_EMPTY} + + # check -arch armv5 pulls out V5 slice + ${LD} -r -arch armv5 foo.o -o foob.o + otool -hv foob.o | grep V5 | ${FAIL_IF_EMPTY} + nm foob.o | grep _bbb | ${FAIL_IF_EMPTY} + + # check -arch armv6 pulls out V6 slice + ${LD} -r -arch armv6 foo.o -o fooc.o + 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} + + ${PASS_IFF} true + + +test-ppc: + gcc foo.c -arch ppc750 -c -DAAA=1 -o foog3.o + gcc foo.c -arch ppc7400 -c -DBBB=1 -o foog4.o + gcc foo.c -arch ppc970 -c -DCCC=1 -o foog5.o + lipo foog3.o foog4.o foog5.o -create -output foo.o + + # check -arch ppc750 pulls out g3 slice + ${LD} -r -arch ppc750 foo.o -o fooa.o + otool -hv fooa.o | grep ppc750 | ${FAIL_IF_EMPTY} + nm fooa.o | grep _aaa | ${FAIL_IF_EMPTY} + + # check -arch ppc7400 pulls out g4 slice + ${LD} -r -arch ppc7400 foo.o -o foob.o + otool -hv foob.o | grep ppc7400 | ${FAIL_IF_EMPTY} + nm foob.o | grep _bbb | ${FAIL_IF_EMPTY} + + # check -arch ppc970 pulls out g5 slice + ${LD} -r -arch ppc970 foo.o -o fooc.o + otool -hv fooc.o | grep ppc970 | ${FAIL_IF_EMPTY} + nm fooc.o | grep _ccc | ${FAIL_IF_EMPTY} + + ${PASS_IFF} true + + +clean: + rm -f *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c new file mode 100644 index 0000000..983ed81 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c @@ -0,0 +1,25 @@ + + +#if AAA + void aaa() {} +#endif + +#if BBB + void bbb() {} +#endif + +#if CCC + void ccc() {} +#endif + +#if DDD + void ddd() {} +#endif + +#if EEE + void eee() {} +#endif + +#if FFFF + void fff() {} +#endif diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile new file mode 100644 index 0000000..695ffba --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile @@ -0,0 +1,157 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate cpu subtypes processing +# + +test: test-${ARCH} + +test-ppc64: + ${PASS_IFF} true + +test-i386: + ${PASS_IFF} true + +test-x86_64: + ${PASS_IFF} true + +test-armv6: + gcc 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 + ${FAIL_IF_BAD_OBJ} foo-v5.o + gcc 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 + ${FAIL_IF_BAD_OBJ} foo-xscale.o + gcc 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 + ${FAIL_IF_BAD_OBJ} main-v5.o + gcc 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 + ${FAIL_IF_BAD_OBJ} main-xscale.o + + # check V4+V4 -> V4 + ${LD} -r main-v4.o foo-v4.o -o all.o + ${FAIL_IF_BAD_OBJ} all.o + 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} + + # 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} + + # 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} + + # check V5+V5 -> V5 + ${LD} -r main-v5.o foo-v5.o -o all.o + ${FAIL_IF_BAD_OBJ} all.o + 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} + + # 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} + + # 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 + ${FAIL_IF_BAD_OBJ} all.o + otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} + + ${PASS_IFF} true + + +test-ppc: + gcc foo.c -arch ppc -mmacosx-version-min=10.4 -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + gcc foo.c -arch ppc750 -c -o foo-G3.o + ${FAIL_IF_BAD_OBJ} foo-G3.o + gcc foo.c -arch ppc7400 -c -o foo-G4.o + ${FAIL_IF_BAD_OBJ} foo-G4.o + gcc foo.c -arch ppc970 -c -o foo-G5.o + ${FAIL_IF_BAD_OBJ} foo-G5.o + gcc main.c -arch ppc -mmacosx-version-min=10.4 -c -o main.o + ${FAIL_IF_BAD_OBJ} main.o + gcc main.c -arch ppc970 -c -o main-G5.o + ${FAIL_IF_BAD_OBJ} main-G5.o + + # check ALL+ALL -> ALL + ${LD} -r main.o foo.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ALL | ${FAIL_IF_EMPTY} + + # check G3+ALL -> G3 + ${LD} -r main.o foo-G3.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc750 | ${FAIL_IF_EMPTY} + + # check G4+ALL -> G4 + ${LD} -r main.o foo-G4.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc7400 | ${FAIL_IF_EMPTY} + + # check G5+ALL -> G5 + ${LD} -r main.o foo-G5.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} + + # check G5+G4 -> G5 + ${LD} -r main-G5.o foo-G4.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} + + # check G4+G5 -> G5 + ${LD} -r foo-G4.o main-G5.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} + + # check -force_cpusubtype_ALL + ${LD} -r main.o foo-G5.o -o main-r.o -force_cpusubtype_ALL + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ALL | ${PASS_IFF_STDIN} + +clean: + rm -f *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt new file mode 100644 index 0000000..5e47ece --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt @@ -0,0 +1,2 @@ +The point of this test is validate cpu subtypes processsing for PowerPC + diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c new file mode 100644 index 0000000..b48076d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile new file mode 100644 index 0000000..ef985b7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a global symbol in an archive will stil be exported (and not dead stripped). +# + +run: all + +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 -dynamiclib -Os libfoo.a -dead_strip -o libmain.dylib + nm libmain.dylib | grep _bar | ${FAIL_IF_EMPTY} + nm libmain.dylib | grep _baz | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libmain.dylib + + +clean: + rm -rf *.dylib *.a *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c new file mode 100644 index 0000000..41478e8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c @@ -0,0 +1,12 @@ + +static int foo_count = 0; +static int bar_count = 0; +static int baz_count = 0; + + +void foo() { ++foo_count; } + +void bar() { ++bar_count; } + +void __attribute__((visibility("hidden"))) + baz() { ++baz_count; } diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c new file mode 100644 index 0000000..da2b2fd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile new file mode 100644 index 0000000..1542d78 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a common symbol can be used from an archive with -dead_strip. The tricky +# part is that common symbols are not in the table of contents for archives. +# If the linker seens a need for my_common, that won't trigger pulling in the .o +# file from the archive. But the later use of foo will. +# + +run: all + +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 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.a *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt new file mode 100644 index 0000000..8dde1c5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt @@ -0,0 +1 @@ +The point of this test that -dead_strip removes unreference code/data from archives diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c new file mode 100644 index 0000000..be1b438 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c @@ -0,0 +1,7 @@ + + +void foo() {} + + +int my_common; + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c new file mode 100644 index 0000000..f6a7d5a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern void foo(); +extern int my_common; + +int main() +{ + // the reference to the common symbol has to be first + my_common += 1; + // refrence to foo is next + foo(); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile new file mode 100644 index 0000000..4b4f912 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a -init function can be pulled from an archive when using -dead_strip. +# + +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 -dynamiclib -Os libfoo.a -dead_strip -o libbar.dylib -init _foo + ${PASS_IFF_GOOD_MACHO} libbar.dylib + +clean: + rm -rf libbar.dylib libfoo.a foo.o + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c new file mode 100644 index 0000000..95ec91c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c @@ -0,0 +1,6 @@ + + +void foo() {} + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile new file mode 100644 index 0000000..32ac1c5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check -dead_strip +# +# 1) in a main executable, dead globals are removed +# 2) in a dylib/bundle with -exported_symbols_list, dead globals are removed +# 3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} + ${FAIL_IF_BAD_MACHO} dylib-${ARCH} + nm -j dylib-${ARCH} | grep dead | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -o dylib2-${ARCH} + ${FAIL_IF_BAD_MACHO} dylib2-${ARCH} + nm -j dylib2-${ARCH} | grep dead_door_knob | ${FAIL_IF_EMPTY} + nm -j dylib2-${ARCH} | grep deadwood | ${PASS_IFF_EMPTY} + +clean: + rm -rf main-* dylib-* dylib2-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt new file mode 100644 index 0000000..90a289c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt @@ -0,0 +1,5 @@ +The point of this test is a sanity check -dead_strip + +1) in a main executable, dead globals are removed +2) in a dylib/bundle with -exported_symbols_list, dead globals are removed +3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c new file mode 100644 index 0000000..176ec78 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c @@ -0,0 +1,11 @@ + + +// deadwood() is local to its linkage unit and is unsed, +// so reference to undef() is ok + +extern void undef(); + +void dead_wood() __attribute__((visibility("hidden"))); +void dead_wood() { undef(); } + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c new file mode 100644 index 0000000..029aed9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} + + + +void dead_door_knob() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp b/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp new file mode 100644 index 0000000..4eb9e89 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp @@ -0,0 +1 @@ +_main diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile new file mode 100644 index 0000000..309655e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test -dead_strip_dylibs +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib libbaz.dylib -o main + ${FAIL_IF_BAD_MACHO} main + otool -L main | grep libfoo.dylib | ${FAIL_IF_EMPTY} + otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY} + otool -L main | grep libbaz.dylib | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libbar.dylib libbaz.dylib -o main -Wl,-dead_strip_dylibs + ${FAIL_IF_BAD_MACHO} main + otool -L main | grep libfoo.dylib | ${FAIL_IF_STDIN} + otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY} + otool -L main | grep libbaz.dylib | ${FAIL_IF_STDIN} + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libbar.dylib libfoo.dylib libbaz.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/bar.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/baz.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c new file mode 100644 index 0000000..2b85b0e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c @@ -0,0 +1,10 @@ + +extern void bar(); + +int main() +{ +#if CALL_BAR + bar(); +#endif + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile new file mode 100644 index 0000000..e6471f7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that -dead_strip does not remove +# atoms in sections with the S_ATTR_NO_DEAD_STRIP bit set +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -dead_strip -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -j main-${ARCH} | grep _bar | ${FAIL_IF_STDIN} + nm -j main-${ARCH} | grep _foo | ${PASS_IFF_STDIN} + +clean: + rm -rf main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt new file mode 100644 index 0000000..6ca56e4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt @@ -0,0 +1,2 @@ +The point of this test is to check that -dead_strip does not remove +atoms in sections with the S_ATTR_NO_DEAD_STRIP bit set diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c new file mode 100644 index 0000000..5fc1fad --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} + + +// foo should not be dead stripped +void __attribute__ ((section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo() +{ + +} + +// bar should be dead stripped +void __attribute__ ((section ("__DATA,__text2"))) bar() +{ + +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile new file mode 100644 index 0000000..533169f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that C++ coalescing throws away static probes associated +# with weak functions that are coalesced away. +# Build two programs that both should have the same number of probes +# and hence the same DOF section size +# + +all: simp coal + otool -lv coal | grep -A3 __dof_Number | grep size > coal-dof-size + otool -lv simp | grep -A3 __dof_Number | grep size > simp-dof-size + ${PASS_IFF_SUCCESS} diff coal-dof-size simp-dof-size + +Number.h: Number.d + dtrace -h -s Number.d + +a.o : a.cxx Number.h header.h + ${CXX} ${CXXFLAGS} -c -DFUNC=a a.cxx -o a.o + +b.o : a.cxx Number.h header.h + ${CXX} ${CXXFLAGS} -c -DFUNC=b a.cxx -o b.o + +c.o : a.cxx Number.h header.h + ${CXX} ${CXXFLAGS} -c -DFUNC=c a.cxx -o c.o + +coal : a.o b.o c.o + ${CXX} -dynamiclib a.o b.o c.o -o coal + +simp : x.cxx Number.h + ${CXX} -dynamiclib x.cxx -o simp + + + +clean: + rm -rf coal simp a.o b.o c.o Number.h coal-dof-size simp-dof-size diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d new file mode 100644 index 0000000..9383fe4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d @@ -0,0 +1,3 @@ +provider Number { + probe hit(int value); +}; diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx new file mode 100644 index 0000000..8e7e3c6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx @@ -0,0 +1,8 @@ + +#include + +#include "header.h" + + +void FUNC() { foo(); } + diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h new file mode 100644 index 0000000..162419b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h @@ -0,0 +1,11 @@ + +#include "Number.h" + +#define LOTS_O_PROBES { NUMBER_HIT(1); NUMBER_HIT(2); NUMBER_HIT(3); NUMBER_HIT(4); } + + +inline void foo() { + LOTS_O_PROBES +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx new file mode 100644 index 0000000..b756394 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx @@ -0,0 +1,6 @@ + +#include "header.h" + +void f() { LOTS_O_PROBES } + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile new file mode 100644 index 0000000..b59760f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -0,0 +1,60 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a progam with dtrace static probes +# + +all: run + +run: main main-r main-dead_strip libmain.dylib + ${FAIL_IF_BAD_MACHO} main + ${PASS_IFF_GOOD_MACHO} main-r + +main: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -o main + +main-dead_strip: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -o main-dead_strip -dead_strip + +main-r: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -c -o main.o + #${FAIL_IF_BAD_OBJ} main.o + ${LD} -r main.o -o main-r.o + #${FAIL_IF_BAD_OBJ} main-r.o + ${CC} ${CCFLAGS} main-r.o -o main-r + +libmain.dylib: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib + +foo.h: foo.d + dtrace -h -s foo.d + +bar.h: bar.d + dtrace -h -s bar.d + +clean: + rm -rf main libmain.dylib main-r main-dead_strip foo.h bar.h *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d new file mode 100644 index 0000000..fab36ea --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d @@ -0,0 +1,7 @@ +typedef int weirdType; + +provider Bar { + probe count1(weirdType); +}; + +#pragma D attributes Evolving/Evolving/Common provider Bar args diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt new file mode 100644 index 0000000..b02ac8b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a progam with dtrace static probes diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d new file mode 100644 index 0000000..03f1a19 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d @@ -0,0 +1,8 @@ +typedef int weirdType2; + +provider Foo { + probe count1(weirdType2); +}; + + +#pragma D attributes Evolving/Evolving/Common provider Foo args diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c new file mode 100644 index 0000000..8a417e9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c @@ -0,0 +1,29 @@ + +#include + +typedef int weirdType; +typedef int weirdType2; + +#include "foo.h" +#include "bar.h" + + +int deadwood() +{ + BAR_COUNT1(2); + return 0; +} + + +int main() { + int a = 1; + + while(a) { + FOO_COUNT1(1); + printf("test\n"); + BAR_COUNT1(2); + sleep(1); + } + + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile new file mode 100644 index 0000000..8d38d99 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that using -all_load to pull all .o files out of an archive +# proeduces good "debug notes". +# + +run: + ${CC} ${CCFLAGS} -gdwarf-2 foo.c -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + ${CC} ${CCFLAGS} -gdwarf-2 bar.c -c -o bar.o + ${FAIL_IF_BAD_OBJ} bar.o + ${CC} ${CCFLAGS} -gdwarf-2 baz.c -c -o baz.o + ${FAIL_IF_BAD_OBJ} baz.o + libtool -static bar.o baz.o foo.o -o liball.a + ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs + ${FAIL_IF_BAD_MACHO} liball.dylib + nm -fap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs + ${PASS_IFF} diff liball.dylib.stabs expected-stabs + +clean: + rm -rf *.o liball.a liball.dylib liball.dylib.stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c new file mode 100644 index 0000000..06752f3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c @@ -0,0 +1,2 @@ +void bar() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c new file mode 100644 index 0000000..256a0e3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c @@ -0,0 +1 @@ +void baz() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt new file mode 100644 index 0000000..6992acd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt @@ -0,0 +1,2 @@ +Test that using -all_load to pull all .o files out of an archive +proeduces good "debug notes". diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs new file mode 100644 index 0000000..0071455 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs @@ -0,0 +1,24 @@ +0000 SO CWD/ +0000 SO bar.c +0001 OSO CWD/liball.a(bar.o) +0000 BNSYM +0000 FUN _bar +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO baz.c +0001 OSO CWD/liball.a(baz.o) +0000 BNSYM +0000 FUN _baz +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO foo.c +0001 OSO CWD/liball.a(foo.o) +0000 BNSYM +0000 FUN _foo +0000 FUN +0000 ENSYM +0000 SO diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/foo.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile new file mode 100644 index 0000000..1eb6310 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# produces good "debug notes" stabs from dwarf .o files after +# some of the .o files are merged with ld -r. +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: foobar.o main.o + ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-test-${ARCH} + nm -ap dwarf-test-${ARCH} | ./stabs-filter.pl > dwarf-test-${ARCH}.stabs + ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs + +foobar.o : foo.o bar.o + ${LD} -r -arch ${ARCH} foo.o bar.o -o foobar.o + ${FAIL_IF_BAD_OBJ} foobar.o + +foo.o : foo.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +bar.o : bar.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +main.o : main.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +clean: + rm -rf dwarf-test-* *.o *.stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx new file mode 100644 index 0000000..c3f6a18 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx @@ -0,0 +1,4 @@ +int bar() +{ + return 10; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt new file mode 100644 index 0000000..0f1d0b1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt @@ -0,0 +1,5 @@ +The point of this test is a sanity check that ld +produces good "debug notes" stabs from dwarf .o files after +some of the .o files are merged with ld -r. +Running nm through stabs-filter.pl produces connonical stabs +that can be diffed against a checked in know good set of stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs new file mode 100644 index 0000000..f8abc12 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs @@ -0,0 +1,24 @@ +0000 SO CWD/ +0000 SO main.cxx +0001 OSO CWD/main.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO foo.cxx +0001 OSO CWD/foo.o +0000 BNSYM +0000 FUN __Z3foov +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO bar.cxx +0001 OSO CWD/bar.o +0000 BNSYM +0000 FUN __Z3barv +0000 FUN +0000 ENSYM +0000 SO diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx new file mode 100644 index 0000000..1f46ef4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx @@ -0,0 +1,4 @@ +int foo() +{ + return 10; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile new file mode 100644 index 0000000..42f1cb7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# produces good "debug notes" stabs from dwarf .o files +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: hello.o other.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + nm -ap dwarf-hello-${ARCH} | ./stabs-filter.pl > dwarf-hello-${ARCH}.stabs + ${PASS_IFF} diff dwarf-hello-${ARCH}.stabs expected-stabs + +hello.o : hello.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +other.o : other.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +clean: + rm -rf dwarf-hello-* *.o *.stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt new file mode 100644 index 0000000..5caf129 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt @@ -0,0 +1,4 @@ +The point of this test is a sanity check that ld +produces good "debug notes" stabs from dwarf .o files +Running nm through stabs-filter.pl produces connonical stabs +that can be diffed against a checked in know good set of stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs new file mode 100644 index 0000000..4ab634d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -0,0 +1,33 @@ +0000 SO CWD/ +0000 SO hello.cxx +0001 OSO CWD/hello.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 BNSYM +0000 FUN __Z3fooi +0000 SOL header.h +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO other.cxx +0001 OSO CWD/other.o +0000 BNSYM +0000 FUN __Z3bari +0000 FUN +0000 ENSYM +0000 BNSYM +0000 FUN .my_non_standard_function_name +0000 FUN +0000 ENSYM +0000 GSYM _init +0000 STSYM .my_non_standard_name +0000 STSYM .my_non_standard_name_static +0000 STSYM __ZZ3bariE8bar_init +0000 GSYM _uninit +0000 STSYM _sinit +0000 STSYM _suninit +0000 STSYM __ZZ3bariE10bar_uninit +0000 SO diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h new file mode 100644 index 0000000..aa960dd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h @@ -0,0 +1,8 @@ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx new file mode 100644 index 0000000..0d508e1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx new file mode 100644 index 0000000..b1b0e77 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx @@ -0,0 +1,27 @@ + +#include "header.h" + +int uninit; +int init = 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; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + scustominit = x; + custom = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + +extern void disappear() _asm("lbegone"); +void disappear() {} + +extern void foo() _asm(".my_non_standard_function_name"); +void foo() { disappear(); } + diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile new file mode 100644 index 0000000..94b026c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# strips out the dwarf segment by default +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} + +clean: + rm -rf dwarf-hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt new file mode 100644 index 0000000..06822b2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld strips out the dwarf segment by default diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c new file mode 100644 index 0000000..e2f0fe9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile new file mode 100644 index 0000000..947afa7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld -S +# produces no debug notes (stabs) +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o dwarf-hello-${ARCH} + #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + nm -ap dwarf-hello-${ARCH} | grep -e " - " | ${PASS_IFF_EMPTY} + +clean: + rm -rf dwarf-hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt new file mode 100644 index 0000000..5a23827 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld -S produces no debug notes (stabs) diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c new file mode 100644 index 0000000..ddca819 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile new file mode 100644 index 0000000..1cf9fa0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that libfoo.dylib can be aliases with a symlink or +# as another dylib and still link +# + +run: all + +all: libfoo.dylib libbar.dylib libbaz.dylib + ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. + ${PASS_IFF_GOOD_MACHO} main + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name libfoo.dylib + +libbaz.dylib : libfoo.dylib + ln -s libfoo.dylib libbaz.dylib + +clean: + rm -rf *.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c new file mode 100644 index 0000000..2fb55ee --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c @@ -0,0 +1 @@ +void foo() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c new file mode 100644 index 0000000..03c4b39 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c @@ -0,0 +1,8 @@ +extern void foo(); +extern void bar(); + +int main() { + foo(); + bar(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile new file mode 100644 index 0000000..617fbfc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Test that searches for indirect libraries does not cause a cycle +# OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found +# + +run: all + +all: libfoo.dylib + ${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true + grep "cycle" errmsg | ${PASS_IFF_STDIN} + +libfoo.dylib : foo.c libbar.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 + +libbar.dylib : bar.c other/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 + +other/libfoo.dylib : foo.c + mkdir -p other + ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 + + + +clean: + rm -rf other libfoo.dylib libbar.dylib main errmsg diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c new file mode 100644 index 0000000..2fb55ee --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c @@ -0,0 +1 @@ +void foo() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c new file mode 100644 index 0000000..8273541 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c @@ -0,0 +1,6 @@ +extern void unfindable(); + +int main() { + unfindable(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile new file mode 100644 index 0000000..f6371af --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) +SHELL = bash # use bash shell so we can redirect just stderr + + +# Verify that if -dylib_file option points to a missing, file the link does not fail + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} foo.c "${PWD}/libbar.dylib" -dynamiclib -o libfoo.dylib -sub_library libbar + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -dylib_file "${PWD}/libbar.dylib:libbar2.dylib" 2>warnings.log + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.dylib main warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c new file mode 100644 index 0000000..2668f9a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c @@ -0,0 +1,13 @@ + + + +void bar() +{ +} + +#if BAR_EXTRA +void bar_extra() +{ +} +#endif + diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c new file mode 100644 index 0000000..aa31241 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c @@ -0,0 +1,7 @@ + +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c new file mode 100644 index 0000000..3f109f2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c @@ -0,0 +1,15 @@ + +extern void foo(); +extern void bar(); +extern void bar_extra(); + +int main() +{ + foo(); + bar(); +#if BAR_EXTRA + bar_extra(); +#endif + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile new file mode 100644 index 0000000..181eee5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) +SHELL = bash # use bash shell so we can redirect just stderr + + +# Verify that -dylib_file option allows you to replace an indirect dylib + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} bar.c -DBAR_EXTRA -dynamiclib -o libbar2.dylib -install_name "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} foo.c "${PWD}/libbar.dylib" -dynamiclib -o libfoo.dylib -sub_library libbar + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + # verify that if main needs bar_extra, it fails + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DBAR_EXTRA libfoo.dylib -o main 2> fail.log + # verify that if main needs bar_extra, it works with -dylib_file option + ${CC} ${CCFLAGS} main.c -DBAR_EXTRA libfoo.dylib -o main -dylib_file "${PWD}/libbar.dylib:libbar2.dylib" + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.dylib main fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c new file mode 100644 index 0000000..2668f9a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c @@ -0,0 +1,13 @@ + + + +void bar() +{ +} + +#if BAR_EXTRA +void bar_extra() +{ +} +#endif + diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt new file mode 100644 index 0000000..e3bfbb7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt @@ -0,0 +1 @@ +Verify that -dylib_file option allows you to replace an indirect dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c new file mode 100644 index 0000000..aa31241 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c @@ -0,0 +1,7 @@ + +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c new file mode 100644 index 0000000..3f109f2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c @@ -0,0 +1,15 @@ + +extern void foo(); +extern void bar(); +extern void bar_extra(); + +int main() +{ + foo(); + bar(); +#if BAR_EXTRA + bar_extra(); +#endif + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile new file mode 100644 index 0000000..3ccbc85 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile @@ -0,0 +1,36 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Verify -init option creates a LC_ROUTINES load command + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -init __init + otool -lv libfoo.dylib | grep LC_ROUTINES | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c new file mode 100644 index 0000000..9ba156e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c @@ -0,0 +1,2 @@ +void _init() { +} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile new file mode 100644 index 0000000..3fb8a25 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# +# comdat warnings in ld -r +# +# also use -falign-functions to force an out of order coalesing +# +run: all + +all: + ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o + ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32 + ${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o + ${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log + grep warning warnings.log | ${PASS_IFF_EMPTY} + + +clean: + rm foo.o bar.o baz.o foobarbaz.o warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx new file mode 100644 index 0000000..b48923c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "func.h" + +void bar() +{ + func(); +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx new file mode 100644 index 0000000..0a8d99a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "func.h" + + +void test() +{ + func(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h new file mode 100644 index 0000000..4505225 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h @@ -0,0 +1,35 @@ +/* -*- 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@ + */ + +extern int global; +extern void foo(int); + + +// this weak func() will have unwind info and a LSDA +inline int func() +{ + global = 1; + return global; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile new file mode 100644 index 0000000..0c7fb32 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# There are two copies of func(). The one from +# foo.o is weak and has an FDE (.eh) and LSDA. +# The one from bar.o is not weak and does not have +# exception info. Verify that since linker uses func() +# from bar.o that it does not use exception info +# from foo.o +# +# wrong EH information might be used +# + +run: all + +all: + ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o + ${CXX} ${CCXXFLAGS} foo2.cxx -c -o foo2.o + ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -fno-exceptions + ${CXX} ${CCXXFLAGS} -dynamiclib foo.o foo2.o bar.o -o libfoobar.dylib -Wl,-map,libfoobar.map + # verify .eh symbol is missing or is from bar.o (file 3) + grep '\[ 2\] __Z4funcv.eh' libfoobar.map | ${FAIL_IF_STDIN} + # verify no LSDA + size -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY} + +clean: + rm foo.o foo2.o bar.o libfoobar.map libfoobar.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx new file mode 100644 index 0000000..83d1845 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// this non-seak func() will have no unwind info and no LSDA +int func() { return 0; } + +void foo(int x) {} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx new file mode 100644 index 0000000..ce9939f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "func.h" + +int global; + +void test() +{ + func(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx new file mode 100644 index 0000000..4440aed --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "func.h" + +void test2() +{ + func(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h new file mode 100644 index 0000000..5bb7c28 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h @@ -0,0 +1,43 @@ +/* -*- 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@ + */ + +extern int global; +extern void foo(int); + + +// this weak func() will have unwind info and a LSDA +inline int func() +{ + global = 1; + try { + foo(1); + global = 2; + } + catch (int x) { + foo(2); + global = 3; + } + return global; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile new file mode 100644 index 0000000..0b8fcff --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile @@ -0,0 +1,34 @@ +## +# 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 + +run: all + + +all: + ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_ERROR} nm -j main | grep '\.eh$$'| ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main +clean: + rm -rf *.o main main-* *.nm diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt @@ -0,0 +1,21 @@ +Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) + +__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +__ZN9__gnu_cxx13new_allocatorIiED2Ev +__ZNSt6vectorIiSaIiEEC1ERKS0_ +__ZNSaIiEC1ERKS_ +__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +__ZNSaIiED2Ev +__ZNSt12_Vector_baseIiSaIiEED2Ev +__ZNSaIiEC1Ev +__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +__ZNSt6vectorIiSaIiEED1Ev +__ZNSaIiED1Ev +__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +__ZN9__gnu_cxx13new_allocatorIiEC2Ev +__ZNSaIiEC2ERKS_ +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile new file mode 100644 index 0000000..6216d8e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# strips .eh symbols out of final linked images, +# even when an intermediate ld -r was used. +# + +run: all + +all: + ${CXX} ${CCXXFLAGS} foo.cxx -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + nm libfoo.dylib | grep '.eh' | ${FAIL_IF_STDIN} + ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + ${LD} -r foo.o -o foo2.o + ${FAIL_IF_BAD_OBJ} foo2.o + ${CXX} ${CCXXFLAGS} foo2.o bar.cxx -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + nm libfoobar.dylib | grep '.eh' | ${PASS_IFF_EMPTY} + +clean: + rm *.dylib *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx new file mode 100644 index 0000000..9623da1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +static void bar1() +{ + fprintf(stderr, "hello\n"); +} + +void bar2() +{ + bar1(); + fprintf(stderr, "world\n"); +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx new file mode 100644 index 0000000..d1e2dd1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +static void foo1() +{ + fprintf(stderr, "hello\n"); +} + +void foo2() +{ + foo1(); + fprintf(stderr, "world\n"); +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile b/ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile new file mode 100644 index 0000000..4142b8c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can handle an empty (no load commands) .o file +# + +run: all + +all: + touch empty.s + as -arch ${ARCH} -n empty.s -o empty.o + ${CC} ${CCFLAGS} main.c empty.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main empty.s empty.o diff --git a/ld64/FireOpal/unit-tests/test-cases/empty-object/main.c b/ld64/FireOpal/unit-tests/test-cases/empty-object/main.c new file mode 100644 index 0000000..76e8197 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/empty-object/main.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/end-label/Makefile b/ld64/FireOpal/unit-tests/test-cases/end-label/Makefile new file mode 100644 index 0000000..69f51a7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/end-label/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld maintains two symbols with the same address and in different sections +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.s -c -o foo.o + ${CC} ${CCFLAGS} bar.s -c -o bar.o + ${LD} -r foo.o bar.o -o foobar.o + nm -m foobar.o | grep _next | grep __other | ${FAIL_IF_EMPTY} + ${LD} -r foobar.o -o foobar2.o + nm -m foobar2.o | grep _next | grep __other | ${PASS_IFF_STDIN} + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/end-label/bar.s b/ld64/FireOpal/unit-tests/test-cases/end-label/bar.s new file mode 100644 index 0000000..db389c4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/end-label/bar.s @@ -0,0 +1,7 @@ + + .section __DATA,__other,regular + + .globl _next +_next: + nop + \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/end-label/foo.s b/ld64/FireOpal/unit-tests/test-cases/end-label/foo.s new file mode 100644 index 0000000..888af5f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/end-label/foo.s @@ -0,0 +1,11 @@ + + .data + + .globl _start + .globl _end +_start: + .long 0 + .long 0 +_end: + + diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile new file mode 100644 index 0000000..cf300a3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests the use of wildcards in exported symbol lists and dead stripping +# + +run: all + +all: + ${CC} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_AB*' -dead_strip + nm -j -f libfoo.dylib | grep _good | ${FAIL_IF_EMPTY} + nm -j -f libfoo.dylib | grep _bad | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c new file mode 100644 index 0000000..6bb8d95 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void good() {} +void bad() {} + + +void ABC() {} +void ABD() { good(); } +void DEF() {} +void DEG() { bad(); } + diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile new file mode 100644 index 0000000..10d1eb1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile @@ -0,0 +1,78 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests the use of wildcards in exported symbol lists +# + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*bar' + nm -j -g -f libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f?o' + nm -j -g -f libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*' + nm -j -g -f libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f*o*' + nm -j -g -f libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,_foo -Wl,-exported_symbol -Wl,'_*bar' + nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list list5 + nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-unexported_symbol -Wl,'_*2*' + nm -j -g -f libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[abcdef]o' + nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-f]o' + nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-z]o' + nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-fnop]o' + nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 new file mode 100644 index 0000000..75aadb3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 @@ -0,0 +1,2 @@ +_foo2bar +_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 new file mode 100644 index 0000000..09883f8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 @@ -0,0 +1,3 @@ +_fao +_ffo +_foo diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 new file mode 100644 index 0000000..478bf69 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 @@ -0,0 +1,4 @@ +_foo +_foo2 +_foo2bar +_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 new file mode 100644 index 0000000..b78c206 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 @@ -0,0 +1,6 @@ +_fao +_ffo +_foo +_foo2 +_foo2bar +_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 new file mode 100644 index 0000000..cc935a4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 @@ -0,0 +1,3 @@ +_foo +_foo2bar +_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 new file mode 100644 index 0000000..bdf1d31 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 @@ -0,0 +1,4 @@ +_fao +_ffo +_foo +_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 new file mode 100644 index 0000000..c691c40 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 @@ -0,0 +1,2 @@ +_fao +_ffo diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 new file mode 100644 index 0000000..09883f8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 @@ -0,0 +1,3 @@ +_fao +_ffo +_foo diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c new file mode 100644 index 0000000..bc5be8e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c @@ -0,0 +1,55 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int foo() +{ + return 1; +} + +int foo2() +{ + return 1; +} + +int foobar() +{ + return 1; +} + +int foo2bar() +{ + return 1; +} + +int fao() +{ + return 1; +} + +int ffo() +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 new file mode 100644 index 0000000..a6776d2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 @@ -0,0 +1,2 @@ +_foo +_*bar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile new file mode 100644 index 0000000..25a50b4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -exported_symbols_list can be used with a file with Mac (0x0D) line endings +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib + nm -jg libtest.dylib | grep _ > test.nm + diff test.nm expected.nm + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib test.nm diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm new file mode 100644 index 0000000..b21cbbf --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm @@ -0,0 +1,2 @@ +_common_global2 +_func_global2 diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp new file mode 100644 index 0000000..6ab1532 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp @@ -0,0 +1 @@ +_func_global2 _common_global2 \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile new file mode 100644 index 0000000..c27287d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -exported_symbols_list of a hidden symbol causes a warning +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib 2>warning.log + grep _func_hidden1 warning.log | ${FAIL_IF_EMPTY} + grep _common_hidden1 warning.log | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib warning.log diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp new file mode 100644 index 0000000..f45858b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp @@ -0,0 +1,4 @@ +_func_global1 +_func_hidden1 +_common_global1 +_common_hidden1 diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile new file mode 100644 index 0000000..96ef763 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -exported_symbols_list can be used with -r +# to reduce visibility of symbols and any missing symbols +# causes an error +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${FAIL_IF_BAD_OBJ} test.o + ${LD} -arch ${ARCH} -r -keep_private_externs test.o -exported_symbols_list test.exp -o test-r.o + ${FAIL_IF_BAD_OBJ} test-r.o + # verify common not in export-list got demoted to private extern + nm -m test-r.o | grep "private external _common_global1" | ${FAIL_IF_EMPTY} + # verify only _common_global1 and _func_global1 changed + nm -m test.o | egrep -v '_common_global1|_func_global1|__eh_frame|__data' > test.nm + nm -m test-r.o | egrep -v '_common_global1|_func_global1|__eh_frame|__data' > test-r.nm + diff test.nm test-r.nm + # verify without -keep_private_externs that commons stay private extern + ${LD} -arch ${ARCH} -r test.o -exported_symbols_list test.exp -o test-rr.o + nm -m test-rr.o | grep _common_hidden | grep ') external' | ${FAIL_IF_STDIN} + nm -m test-rr.o | grep _common_global1 | grep ') external' | ${FAIL_IF_STDIN} + # should error out if told to export unavailable symbol + ${FAIL_IFF_SUCCESS} ${LD} -arch ${ARCH} -r test.o -exported_symbols_list test-bad.exp -o test2.o 2>/dev/null + +clean: + rm -rf test.o test-r.o test-rr.o test.nm test-r.nm test2.o diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp new file mode 100644 index 0000000..73154ba --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp @@ -0,0 +1,3 @@ +_bar +_baz +_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp new file mode 100644 index 0000000..93e7e17 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp @@ -0,0 +1,2 @@ +_func_global2 +_common_global2 diff --git a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile new file mode 100644 index 0000000..d9b92d5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to see that external relocations +# are sorted so that dyld only has to look up symbols once. +# The machochecker tool verifies that the relocs are sorted. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c new file mode 100644 index 0000000..bc28725 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c @@ -0,0 +1,5 @@ + +int foo = 1; +int bar = 2; +int baz = 3; + diff --git a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c new file mode 100644 index 0000000..6c68c4c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// in libfoo.dylib +extern int foo; +extern int bar; +extern int baz; + +// this initialilzed data will result in external relocations +// alternating the values, will create relocs that need sorting +int* array[] = { &foo, &bar, &baz, &foo, &bar, &baz, &foo, &bar, &baz }; + + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/filelist/Makefile b/ld64/FireOpal/unit-tests/test-cases/filelist/Makefile new file mode 100644 index 0000000..0b1a743 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/filelist/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +PWD = $(shell pwd) + +# +# The point of this test is to check the two forms of the +# -filelist option +# + +run: all + +all: + ${CC} ${CCFLAGS} -c hello.c -o hello-${ARCH}.o + ${FAIL_IF_BAD_OBJ} hello-${ARCH}.o + echo "${PWD}/hello-${ARCH}.o" > "${PWD}/filelist1" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}" + ${FAIL_IF_BAD_MACHO} hello-${ARCH} + echo "hello-${ARCH}.o" > "${PWD}/filelist2" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-* *.o filelist1 filelist2 diff --git a/ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt b/ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt new file mode 100644 index 0000000..f193b11 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt @@ -0,0 +1 @@ +The point of this test is to check the two forms of the -filelist option diff --git a/ld64/FireOpal/unit-tests/test-cases/filelist/hello.c b/ld64/FireOpal/unit-tests/test-cases/filelist/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/filelist/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile new file mode 100644 index 0000000..b1015b2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a small dylib -flat_namespace and +# indirect internal references. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib -flat_namespace + otool -Iv libmain.dylib | grep _foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libmain.dylib + +clean: + rm *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c new file mode 100644 index 0000000..6014829 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void foo() {} + + +int main() +{ + foo(); + fprintf(stdout, "hello\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile new file mode 100644 index 0000000..a3715e0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that when linking a main executable for flat-namespace +# that undefines in loaded flat-namespace dylibs are resolved. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -flat_namespace -dynamiclib -o libfoo.dylib -undefined suppress + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static bar.o -o libbar.a + # test that linking main executable -twolevel_namespace does not pull in bar() + ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.a -o main + nm -mn main | grep _bar | ${FAIL_IF_STDIN} + # test that linking dylib -flat_namespace does not pull in bar() + ${CC} ${CCFLAGS} main.c -flat_namespace libfoo.dylib libbar.a -dynamiclib -o main.dylib + nm -mn main.dylib | grep _bar | ${FAIL_IF_STDIN} + # test that linking main executable -flat_namespace pulls in bar() + ${CC} ${CCFLAGS} main.c -flat_namespace libfoo.dylib libbar.a -o main_flat + nm -mn main_flat | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main_flat + +clean: + rm libfoo.dylib libbar.a bar.o main main_flat main.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c new file mode 100644 index 0000000..39df2ea --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c @@ -0,0 +1,8 @@ + +extern void bar(); + +void foo() +{ + bar(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c new file mode 100644 index 0000000..246fed4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); + + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile b/ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile new file mode 100644 index 0000000..d31b7ab --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program -flat_namespace and +# does not indirect internal references. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main-${ARCH} -flat_namespace + otool -Iv main-${ARCH} | grep _foo | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + +clean: + rm main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-main/main.c b/ld64/FireOpal/unit-tests/test-cases/flat-main/main.c new file mode 100644 index 0000000..6014829 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/flat-main/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void foo() {} + + +int main() +{ + foo(); + fprintf(stdout, "hello\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile b/ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile new file mode 100644 index 0000000..3f70e74 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld can remove non-lazy pointers for x86_64 +# + +all: all-${ARCH} + +all-armv6: all-true + +all-ppc: all-true + +all-ppc64: all-true + +all-i386: all-true + +all-true: + ${PASS_IFF} true + +all-x86_64: + ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib + otool -Iv libfoobar.dylib | grep 0x | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib -flat_namespace + otool -Iv libfoobar.dylib | grep 0x | ${PASS_IFF_STDIN} + +clean: + rm -rf libfoobar.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c b/ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c new file mode 100644 index 0000000..781c6fd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c @@ -0,0 +1,28 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bar1 = 1; +int bar2 = 2; +int bar3 = 3; + diff --git a/ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c b/ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c new file mode 100644 index 0000000..45675a3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int bar1; +extern int bar2; // just under 2GB array +extern int bar3; + +int getbar1() +{ + return bar1; +} + +int getbar2() +{ + return bar2; +} + +int getbar3() +{ + return bar3; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile b/ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile new file mode 100644 index 0000000..d8cdd51 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} -Wl,-headerpad -Wl,0x3000 + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt b/ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt new file mode 100644 index 0000000..79114eb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a hello-world program with no errors (or crashes) diff --git a/ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c b/ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile b/ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile new file mode 100644 index 0000000..a1ade02 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt b/ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt new file mode 100644 index 0000000..79114eb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a hello-world program with no errors (or crashes) diff --git a/ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c b/ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c new file mode 100644 index 0000000..e2f0fe9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest new file mode 100644 index 0000000..af93da1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null + #ranlib libtest-${ARCH}.a + #${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + #${PASS_IFF_GOOD_MACHO} a-${ARCH} + + ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o a-${ARCH}.o 2>/dev/null + ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a + ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c new file mode 100644 index 0000000..94578c6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c @@ -0,0 +1,26 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; +extern int main(); diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile new file mode 100644 index 0000000..6785960 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_BAD_OBJ} test-${ARCH}.o + ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_BAD_OBJ} a-${ARCH}.o + ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null + ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a + ${CC} ${CCFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a a-* diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c new file mode 100644 index 0000000..110842f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c @@ -0,0 +1,8 @@ +extern int common_var; +int *fn(); + +int +main(int argc, char **argv) +{ + return 0!=&common_var; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c new file mode 100644 index 0000000..e0fd7e8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +struct abc { + int a; + int b; + int c; +} struct_var; + +int common_var; +extern const int defined_var; + +int *fn() +{ + return &common_var; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest new file mode 100644 index 0000000..d70c634 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o + #${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + #${PASS_IFF_GOOD_MACHO} a-${ARCH} + + ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o a-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c new file mode 100644 index 0000000..94578c6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c @@ -0,0 +1,26 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; +extern int main(); diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest new file mode 100644 index 0000000..d5b6d73 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *a diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c new file mode 100644 index 0000000..f34267a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c @@ -0,0 +1,25 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile new file mode 100644 index 0000000..c982885 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + + +# Verify -no_implicit_dylibs option +# add option to disable implicit load commands for indirectly used public dylibs +# + + +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 + # 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} + # verify that -no_implicit_dylibs causes main to get bar from libfoo + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-no_implicit_dylibs -L. + nm -m main | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c new file mode 100644 index 0000000..e2164a2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c @@ -0,0 +1,7 @@ + + + +void bar() +{ +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c new file mode 100644 index 0000000..1624757 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c new file mode 100644 index 0000000..1f50353 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + bar(); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile new file mode 100644 index 0000000..e3056a5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a sanity check that an indirect +# library is not accidentally searched for symbols. +# +# wrong error message when symbol is found in unused indirect library# +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main libfoo.dylib 2> fail.log + grep ordinal fail.log | ${PASS_IFF_EMPTY} + +clean: + rm *.dylib main fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c new file mode 100644 index 0000000..f39ee21 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// function called by a loaded bundle +int bar() +{ + return 1; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt new file mode 100644 index 0000000..311aa79 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt @@ -0,0 +1,4 @@ +The point of this test is a sanity check that an indirect +library is not accidentally searched for symbols. + + wrong error message when symbol is found in unused indirect library# diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c new file mode 100644 index 0000000..ba78c28 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c new file mode 100644 index 0000000..13f57d7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void bar(); + +int main() +{ + bar(); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile new file mode 100644 index 0000000..e79eb0f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile @@ -0,0 +1,106 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -F and -L work when finding indirect libraries +# + + +run: all + +all: + +# build foo that re-exports bar + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar + ${FAIL_IF_BAD_MACHO} libfoo.dylib + +# build an alternate libbar that also has baz + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/libbar.dylib -install_name libbar.dylib + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + +# build an executable that depends on a symbol in the alternate bar to validate that -L is used for indirect dylibs + ${CC} ${CCFLAGS} main.c -o main -lfoo -Lhide -L. + ${FAIL_IF_BAD_MACHO} main + + + +# build Foo.framework that re-exports Bar.framework + mkdir -p Bar.framework Foo.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" + ${FAIL_IF_BAD_MACHO} Bar.framework/Bar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar + ${FAIL_IF_BAD_MACHO} Foo.framework/Foo + +# build an alternate Bar.framework that also has baz + mkdir -p hide/Bar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" + ${FAIL_IF_BAD_MACHO} hide/Bar.framework/Bar + +# build an executable that depends on a symbol in the alternate Bar.framework to validate that -F is used for indirect dylibs + ${CC} ${CCFLAGS} main.c -o main -Fhide -F. -framework Foo + ${FAIL_IF_BAD_MACHO} main + + + +# build foo that links against bar + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + +# build an alternate libbar that also has baz + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/libbar.dylib -install_name libbar.dylib + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + +# build a flat executable that depends on a symbol in the alternate bar to validate that -L is used for indirect dylibs + ${CC} ${CCFLAGS} -flat_namespace main.c -o main -lfoo -Lhide -L. + ${FAIL_IF_BAD_MACHO} main + + + +# build Foo.framework that re-exports libbar.dylib embedded in framework + mkdir -p Foo.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o Foo.framework/libbar.dylib -install_name "`pwd`/Foo.framework/libbar.dylib" + ${FAIL_IF_BAD_MACHO} Foo.framework/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. Foo.framework/libbar.dylib -sub_library libbar + ${FAIL_IF_BAD_MACHO} Foo.framework/Foo + +# build an alternate libbar.dylib that does not have baz + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + +# build an executable that depends on a symbol not in the alternate libbar.dylib to validate dylibs embedded in frameworks are not searched for + ${CC} ${CCFLAGS} main.c -o main -Lhide -F. -framework Foo + ${PASS_IFF_GOOD_MACHO} main + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework main diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/bar.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c new file mode 100644 index 0000000..f02701a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c @@ -0,0 +1,8 @@ +extern int foo (); +extern int bar (); +extern int baz (); + +int main (void) +{ + return foo() + bar() + baz(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile b/ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile new file mode 100644 index 0000000..2fe99a8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -interposable and -interposable_list work +# + +run: all + +all: + # by default, no test* functions should go through stubs + ${CC} ${CCFLAGS} test.c -dynamiclib -o libtest.dylib + # -interposable should make all four test* functions go through stubs + ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable -o libtest.dylib + otool -Iv libtest.dylib | grep '4 entries' | ${FAIL_IF_EMPTY} + otool -Iv libtest.dylib | grep '_test' | ${FAIL_IF_EMPTY} + # -interposable_list should make just two test* functions go through stubs + ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable_list test.exp -o libtest.dylib + otool -Iv libtest.dylib | grep '2 entries' | ${FAIL_IF_EMPTY} + otool -Iv libtest.dylib | grep '_test3' | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + + +clean: + rm libtest.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c b/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c new file mode 100644 index 0000000..937420e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c @@ -0,0 +1,57 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +const char kMyStr[] = "hello"; + +int test1() +{ + return 10; +} + +int test2() +{ + return 10; +} + +int test3() +{ + return 10; +} + +int test4() +{ + return 10; +} + +const char* getstr() +{ + test1(); + test2(); + test3(); + test4(); + return kMyStr; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp b/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp new file mode 100644 index 0000000..8f104f1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp @@ -0,0 +1,2 @@ +_test1 +_test2 diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/Makefile b/ld64/FireOpal/unit-tests/test-cases/large-data/Makefile new file mode 100644 index 0000000..408a2da --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/large-data/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld can link > 4GB zero fill section +# + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq (,${findstring 64,$(ARCH)}) + 32BIT_SHOULD_FAIL = ${FAIL_IF_SUCCESS} +else + 32BIT_SHOULD_FAIL = +endif + + +run: all + +all: + ${CC} ${CCFLAGS} test1.c -c -o test1.o + ${CC} ${CCFLAGS} test2.c -c -o test2.o + ${CC} ${CCFLAGS} test3.c -c -o test3.o + ${CC} ${CCFLAGS} test4.c -c -o test4.o + ${32BIT_SHOULD_FAIL} ${CC} ${CCFLAGS} test1.o test2.o test3.o test4.o -dynamiclib -o libtest.dylib 2> fail.log + ${PASS_IFF} true + +clean: + rm -rf test*.o libtest.dylib fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test1.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test1.c new file mode 100644 index 0000000..2d9ec94 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/large-data/test1.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int mediumarray1[1000]; +int bigarray1[500000000]; // just under 2GB array +int small1; + +int getbig1() +{ + return bigarray1[0]; +} + +int getmedium1() +{ + return mediumarray1[0]; +} + +int getsmall1() +{ + return small1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test2.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test2.c new file mode 100644 index 0000000..d97bed1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/large-data/test2.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bigarray2[500000000]; // just under 2GB array +int small2; + +int getbig2() +{ + return bigarray2[0]; +} + + +int getsmall2() +{ + return small2; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test3.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test3.c new file mode 100644 index 0000000..b7ca398 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/large-data/test3.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bigarray3[500000000]; // just under 2GB array +int small3; + +int getbig3() +{ + return bigarray3[0]; +} + + +int getsmall3() +{ + return small3; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test4.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test4.c new file mode 100644 index 0000000..d879c5c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/large-data/test4.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bigarray4[500000000]; // just under 2GB array +int small4; + +int getbig4() +{ + return bigarray4[0]; +} + + +int getsmall4() +{ + return small4; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile b/ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile new file mode 100644 index 0000000..e3a0d65 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# The point of this test is a sanity check that if +# ld errors out during linking, that no output file is remaining +# + +run: all + +all: + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} link_error.s -dynamiclib -o link_error-${ARCH} 2> fail.log + ${FAIL_IFF} cat link_error-${ARCH} 2> fail.log + +clean: + rm link_error-* fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt b/ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt new file mode 100644 index 0000000..716686d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt @@ -0,0 +1,2 @@ +The point of this test is a sanity check that if +ld errors out during linking, that no output file is remaining diff --git a/ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s b/ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s new file mode 100644 index 0000000..05d80f1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s @@ -0,0 +1,22 @@ + + +#if __ppc__ || __ppc64__ + ; illegal absolute load +_foo: lis r2,ha16(_strcmp) +#endif + +#if __i386__ + // illegal absolute load +_foo: movl _strcmp, %eax +#endif + + +#if __x86_64__ + // illegal external load +_foo: movl _strcmp(%rip), %eax +#endif + +#if __arm__ + ; illegal absolute load +_foo: ldr r2, _strcmp +#endif diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile new file mode 100644 index 0000000..975ad29 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Verify that -lazy_library fails if an objc class is referenced +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -dynamiclib -o libfoo.dylib -framework Foundation + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.m -Wl,-lazy_library,libfoo.dylib -o main -framework Foundation 2> fail.log + ${CC} ${CCFLAGS} main.m libfoo.dylib -o main -framework Foundation + ${PASS_IFF_GOOD_MACHO} main + + + +clean: + rm libfoo.dylib main rm fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h new file mode 100644 index 0000000..eaa591f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h @@ -0,0 +1,9 @@ + +#include + + +@interface Foo : NSObject + + + +@end diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m new file mode 100644 index 0000000..35a63b1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m @@ -0,0 +1,8 @@ + +#include "foo.h" + +@implementation Foo + + + +@end diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m new file mode 100644 index 0000000..0f11f68 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m @@ -0,0 +1,12 @@ +#include +#include + +#include "foo.h" + + +int main() +{ + [[Foo alloc] init]; + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile new file mode 100644 index 0000000..e3d0d6a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -lazy_library works for function calls +# but fails for data references +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad.c -Wl,-lazy_library,libfoo.dylib -o bad 2> fail.log + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad2.c -Wl,-lazy_library,libfoo.dylib -o bad2 2> fail.log + + + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libfoo.dylib main bad bad2 fail.log + diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c new file mode 100644 index 0000000..59a13fb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c @@ -0,0 +1,12 @@ +#include +#include + + +extern int data; + +static int* pd = &data; + +int main() +{ + return *pd; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c new file mode 100644 index 0000000..ffd2ce1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c @@ -0,0 +1,13 @@ +#include +#include + + +extern int foo(); + +int main() +{ + int (*func)() = foo; + if ( func != NULL ) + (*func)(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c new file mode 100644 index 0000000..c23c9dc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c @@ -0,0 +1,5 @@ + +int data = 5; + +int foo() { return 1; } +int bar() { return 1; } diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c new file mode 100644 index 0000000..8546854 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c @@ -0,0 +1,18 @@ +#include +#include + + +extern int foo(); +extern int bar(); + +int main() +{ + // two regular external function calls + void* x = malloc(16); + free(x); + // two lazy dylib external function calls + int result = foo(); + fprintf(stderr, "foo() returned %d\n", result); + bar(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile new file mode 100644 index 0000000..15f3307 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o + + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-align-3-${ARCH}.o | grep 'align:' > align-3 + + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-r-${ARCH}.o | grep 'align:' > align-r + + ${PASS_IFF} diff align-3 align-r + +clean: + rm -rf *.o align-3 align-r diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s new file mode 100644 index 0000000..0dacbad --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L21: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s new file mode 100644 index 0000000..d2661dc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 3 +L21: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile new file mode 100644 index 0000000..c3c8c80 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +LD=ld + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o + + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-align-3-${ARCH}.o > align-3 + + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-r-${ARCH}.o > align-r + ${PASS_IFF} diff -C 6 align-3 align-r + +clean: + rm -rf *.o align-3 align-r diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt new file mode 100644 index 0000000..3b2e3c7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that when two cstrings are coalesced that the one with greater alignment is used. diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s new file mode 100644 index 0000000..1a55cf6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L20: .asciz "XXX" +L22: .ascii "hell\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s new file mode 100644 index 0000000..211aa72 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 2 +L21: .ascii "hell\0" + .align 13 +L99: .ascii "\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile new file mode 100644 index 0000000..cac20b9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +#LD=ld64 + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o + + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-align-3-${ARCH}.o > align-3 + + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-r-${ARCH}.o > align-r + + ${PASS_IFF} diff -C 6 align-3 align-r + +clean: + rm -rf *.o align-3 align-r diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt new file mode 100644 index 0000000..3b2e3c7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that when two cstrings are coalesced that the one with greater alignment is used. diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s new file mode 100644 index 0000000..1a55cf6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L20: .asciz "XXX" +L22: .ascii "hell\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s new file mode 100644 index 0000000..211aa72 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 2 +L21: .ascii "hell\0" + .align 13 +L99: .ascii "\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile new file mode 100644 index 0000000..dcf1dbe --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that literals are uniqued. +# After running ld -r all duplicates should be removed. +# + +run: all + +all: + ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-${ARCH}.o | grep 'name:'| uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} + ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-r-${ARCH}.o | grep 'name:' | uniq -d | ${PASS_IFF_EMPTY} + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s new file mode 100644 index 0000000..6e5febc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s @@ -0,0 +1,69 @@ +/* + * 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@ + */ + + .literal16 +L01:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654321 + +L02:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654322 + +L03:.long 22345678 + .long 87654321 + .long 12345678 + .long 87654321 + +L04:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654321 + + + .literal8 +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +L3: .long 22345678 + .long 87654321 + +L4: .long 12345678 + .long 87654321 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +L13:.long 22345678 +L14:.long 12345678 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +L23: .ascii "there\0" +L24: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest new file mode 100644 index 0000000..0f9d1b5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that literals are uniqued. +# After running ld -r all duplicates should be removed. +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -only literals-${ARCH}.o | uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} + ${FAIL_IF_ERROR} ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o + ${PASS_IFF} ./test.sh literals-r-${ARCH}.o + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt new file mode 100644 index 0000000..56cea21 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that literals are uniqued. After running ld -r all duplicates should be removed. diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s new file mode 100644 index 0000000..b8d4354 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s @@ -0,0 +1,48 @@ +/* + * 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@ + */ + + .literal8 + +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +L3: .long 22345678 + .long 87654321 + +L4: .long 12345678 + .long 87654321 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +L13:.long 22345678 +L14:.long 12345678 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +L23: .ascii "there\0" +L24: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh new file mode 100755 index 0000000..57a36b7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +SZ=`size "$1" | tail -n 1 | sed 's,\([0-9]*\).*,\1,'` +[ "$SZ" ] && [ "$SZ" = 54 ] && exit 0 +exit 1 diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile new file mode 100644 index 0000000..42c71e4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile @@ -0,0 +1,289 @@ +## +# Copyright (c) 2006-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. + +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 + +# +# Test the we set the stack execution bit properly. + +run: + if [ -f /Developer/usr/bin/llvm-gcc-4.2 ] ; then \ + $(MAKE) all ; \ + else \ + ${PASS_IFF} /usr/bin/true ; \ + fi + +all: zero one two three four five six seven eight nine ten \ + eleven twelve thirteen fourteen fifteen sixteen seventeen \ + eighteen nineteen twenty + + +zero: + # + # llvm : a.c : Dfoo3 + # llvm : b.c : Dfoo2 + # 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 + ${PASS_IFF_GOOD_MACHO} main.exe + +one: + # + # llvm : a1.c : Dfoo3, Ufoo4 + # llvm : b1.c : Dfoo2, Ufoo4 + # 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 + ${PASS_IFF_GOOD_MACHO} main1.exe + +two: + # + # llvm : a2.c : Dfoo3, Ufoo4 + # llvm : b2.c : Dfoo2, Dfoo4 + # 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 + ${PASS_IFF_GOOD_MACHO} main2.exe + +three: + # + # llvm : a3.c : Dfoo1, Dbar + # llvm : b3.c : Dfoo2, Ubar + # 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 + ${PASS_IFF_GOOD_MACHO} main3.exe + +four: + # + # llvm : a4.c : Dfoo3, Ufoo4 + # llvm : b4.c : Dfoo2, DLmyfoo, Ufoo4 + # 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 + ${PASS_IFF_GOOD_MACHO} main4.exe + +five: + # + # llvm : a5.c : Dfoo1, Ufoo2, Ufoo3 + # llvm : b5.c : Dfoo2 + # MachO : main5.c : Dfoo3, Ufoo1 + # + #echo "Five..." + ${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 + ${OTOOL} -tV main5.exe | grep foo3 | ${PASS_IFF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main5.exe + +six: + # + # llvm : a6.c : Dfoo1, Dfoo2 + # 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 + ${PASS_IFF_GOOD_MACHO} main6.exe + ${OTOOL} -tV main6.exe | grep foo2 | ${PASS_IFF_EMPTY} + +seven: + # + # llvm : a7.c : Dfoo1, Dfoo2, Ufoo3 + # llvm : b7.c : Dfoo3, ufoo2 + # 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 + ${PASS_IFF_GOOD_MACHO} main7.exe + +eight: + # + # llvm : a8.c : Dfoo1, Dfoo2 + # 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 + ${OTOOL} -tV main8.exe | grep foo2 | ${PASS_IFF_EMPTY} + ${OTOOL} -tV main8.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY} + +nine: + # + # llvm : a9.c : Dfoo1, Dfoo2, Dfoo3, Ufoo3, Ufoo4 + # 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 + ${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} + +ten: + # + # llvm : a10.c + # llvm : b10.c + # 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 + ${PASS_IFF_GOOD_MACHO} main10.exe + +eleven: + # + # llvm : a11.c + # 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 + ${PASS_IFF_GOOD_MACHO} main11.exe + +twelve: + # + # llvm : a12.c + # MachO : main12.c + # + #echo "Tweleve..." + ${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 + ${PASS_IFF_GOOD_MACHO} main12.exe + +thirteen: + # + # llvm : a13.cc + # 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 + +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 + ${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 + ${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 + ${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 + ${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 + ${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 b17.o + ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o + file ab17.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 + ${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o + ObjectDump -nm a18-rkpe.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} + ObjectDump -nm a18-rkpe.o | grep _func_hidden2 | grep " hidden" | ${FAIL_IF_EMPTY} + #echo verify ld -r makes hidden symbols internal (except for commons) + ${LD} -arch ${ARCH} -r a18.o -o a18-r.o + #ObjectDump -nm a18-r.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} + #ObjectDump -nm a18-r.o | grep _func_hidden2 | grep " internal" | ${FAIL_IF_EMPTY} + +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 + 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 + ar cru lib20.a a20.o b20.o + ${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe + nm main20.exe | grep _foo | ${PASS_IFF_STDIN} + + + + +clean: + rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c new file mode 100644 index 0000000..0c96178 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c @@ -0,0 +1,5 @@ +int foo3() +{ + return 21; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c new file mode 100644 index 0000000..f9fb403 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c @@ -0,0 +1,10 @@ +#include +#include +#include +extern int foo4(); +int foo3() +{ +/* printf ("%s\n",strerror(errno)); */ + return foo4(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c new file mode 100644 index 0000000..0dc181e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c @@ -0,0 +1,5 @@ +extern void foo(void); + +void foo(void) +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c new file mode 100644 index 0000000..e95dc40 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c @@ -0,0 +1,6 @@ +#include +void foo3(void) +{ + fputc ('x', stderr); + printf ("\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c new file mode 100644 index 0000000..80bc1e8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c @@ -0,0 +1,8 @@ +#include "a12.h" + +enum E e[1000]; +void foo(void) +{ + e[1] = ONE; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h new file mode 100644 index 0000000..be43955 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h @@ -0,0 +1,8 @@ +enum E + { + ZERO, + ONE + }; + +extern enum E e[1000]; +extern void foo(void); diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc new file mode 100644 index 0000000..edc938a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc @@ -0,0 +1,3 @@ +#include "a13.h" + +A::~A() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h new file mode 100644 index 0000000..bc00448 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h @@ -0,0 +1,7 @@ +#include + +class A { + public: + virtual ~A(); + void foo() { printf ("Hi\n"); } +}; diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c new file mode 100644 index 0000000..8f4ea09 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c @@ -0,0 +1 @@ +int X __attribute__((visibility("hidden"))) = 14; diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c new file mode 100644 index 0000000..a25431b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c @@ -0,0 +1,3 @@ +void __attribute__((visibility("hidden"))) foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c new file mode 100644 index 0000000..3cd06fd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c @@ -0,0 +1,4 @@ + +int a = 0; +int func_a() { return a; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c new file mode 100644 index 0000000..0eb20c5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c @@ -0,0 +1,6 @@ +extern int foo4(void); +int foo3() +{ + return foo4(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c new file mode 100644 index 0000000..cd52955 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c @@ -0,0 +1,2 @@ +void foo() {} +void bar() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c new file mode 100644 index 0000000..040ca5f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c @@ -0,0 +1,6 @@ +int bar; +int foo1() +{ + return bar; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c new file mode 100644 index 0000000..0eb20c5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c @@ -0,0 +1,6 @@ +extern int foo4(void); +int foo3() +{ + return foo4(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c new file mode 100644 index 0000000..bcb22d9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c @@ -0,0 +1,10 @@ +extern int foo2(void); +extern int foo3(void); + +int foo1() +{ + int i = 42; + if (foo2()) + i = foo3(); + return i; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c new file mode 100644 index 0000000..b621453 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c @@ -0,0 +1,10 @@ + +int foo1() +{ + return 42; +} + +int foo2() +{ + return 21; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c new file mode 100644 index 0000000..560919a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c @@ -0,0 +1,11 @@ +extern int foo3(void); + +int foo1(void) +{ + return foo3(); +} + +int foo2(void) +{ + return 42; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c new file mode 100644 index 0000000..47352a7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c @@ -0,0 +1,23 @@ + +static signed int i = 0; +extern int foo1(void); +extern void foo2(void); + +void foo2(void) { + + i = -1; + +} + +static int foo3() { + return 10; +} + +int foo1(void) +{ + int data = 0; + if (i < 0) + data = foo3(); + data += 42; + return data; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c new file mode 100644 index 0000000..da2c8fa --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c @@ -0,0 +1,25 @@ + +static signed int i = 0; +extern int foo1(void); +extern void foo2(void); +extern void foo4(void); + +void foo2(void) { + + i = -1; + +} + +static int foo3() { + foo4(); + return 10; +} + +int foo1(void) +{ + int data = 0; + if (i < 0) + data = foo3(); + data += 42; + return data; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list new file mode 100644 index 0000000..583bc2f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list @@ -0,0 +1,3 @@ +_foo1 +_main +_bar diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c new file mode 100644 index 0000000..61c92dc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c @@ -0,0 +1,3 @@ +int foo2() { + return 21; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c new file mode 100644 index 0000000..5158abe --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c @@ -0,0 +1,4 @@ +extern int foo4(); +int foo2() { + return foo4(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c new file mode 100644 index 0000000..d899aa9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c @@ -0,0 +1,7 @@ +#include "b10.h" +extern void foo(void); + +struct my_struct my_hooks = { + foo +}; + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h new file mode 100644 index 0000000..fcb50d2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h @@ -0,0 +1,6 @@ +struct my_struct +{ + void (*f)(void); +}; + +extern struct my_struct my_hooks; diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c new file mode 100644 index 0000000..59c5120 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c @@ -0,0 +1,7 @@ +#include + +int Y; +extern int X __attribute__((visibility("hidden"))); +void foo() { + printf ("%d\n", X); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c new file mode 100644 index 0000000..13c88d7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c @@ -0,0 +1,8 @@ +extern void foo(); +void bar() { + foo(); +} + +void __attribute__((visibility("hidden"))) f2() +{} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c new file mode 100644 index 0000000..47d4cc3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c @@ -0,0 +1,4 @@ +int b = 0; +int func_b() { return b; } + + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c new file mode 100644 index 0000000..a20f6e3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c @@ -0,0 +1,9 @@ +extern int foo4(void); + +int foo4(void) +{ + return 21; +} +int foo2() { + return foo4(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c new file mode 100644 index 0000000..f6c0ed3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c @@ -0,0 +1 @@ +void frob() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c new file mode 100644 index 0000000..31e7ee8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c @@ -0,0 +1,4 @@ +extern int bar; +int foo2() { + return bar; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c new file mode 100644 index 0000000..9437ad0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c @@ -0,0 +1,13 @@ +extern int foo4(void); + +int foo4(void) +{ + return 21; +} +static int myfoo() +{ + return foo4(); +} +int foo2() { + return myfoo(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c new file mode 100644 index 0000000..a105df9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c @@ -0,0 +1,4 @@ +int foo2(void) +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c new file mode 100644 index 0000000..d34f91a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c @@ -0,0 +1,7 @@ +extern int foo2(void); +extern int foo3(void); + +int foo3(void) +{ + return foo2(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c new file mode 100644 index 0000000..0089d64 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c @@ -0,0 +1,9 @@ +extern void foo(); +int main() { + foo(); + return 0; +} + +void __attribute__((visibility("hidden"))) f2() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c @@ -0,0 +1,9 @@ +extern int foo2(); +extern int foo3(); +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c new file mode 100644 index 0000000..ccad10d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c @@ -0,0 +1,13 @@ +extern int foo2(); +extern int foo3(); +int foo4() +{ + return 21; +} +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c new file mode 100644 index 0000000..dabcdcf --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c @@ -0,0 +1,10 @@ +#include "b10.h" + +int main() +{ + struct my_struct *mh = &my_hooks; + + mh->f(); + + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c new file mode 100644 index 0000000..82ef7ff --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c @@ -0,0 +1,7 @@ + +extern void foo3(void); +int main() +{ + foo3(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c new file mode 100644 index 0000000..4de8725 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c @@ -0,0 +1,7 @@ +#include "a12.h" +int main() +{ + e[0] = ZERO; + foo(); + return e[0]; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc new file mode 100644 index 0000000..697d81b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc @@ -0,0 +1,8 @@ +#include "a13.h" + +int main() +{ + A a; + a.foo(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c new file mode 100644 index 0000000..67112aa --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c @@ -0,0 +1,8 @@ + +int tent; +int global = 5; + +int foo() { return tent + global; } + +int main() { foo(); return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c new file mode 100644 index 0000000..e09c1c9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c @@ -0,0 +1,8 @@ +extern int foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c @@ -0,0 +1,9 @@ +extern int foo2(); +extern int foo3(); +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c new file mode 100644 index 0000000..624e009 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c @@ -0,0 +1,7 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c new file mode 100644 index 0000000..a5058fe --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c @@ -0,0 +1,13 @@ +extern int foo1(); +extern int foo2(); +extern int bar; +int main(){ + int i; + bar = 14; + i = foo1() + foo2() + bar; + if (i == 42) + return 0; + else + return 1; + +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c @@ -0,0 +1,9 @@ +extern int foo2(); +extern int foo3(); +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c new file mode 100644 index 0000000..28d551d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c @@ -0,0 +1,16 @@ + +extern int foo1(void); + +int foo3(void) +{ + return 42; +} + +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c new file mode 100644 index 0000000..3d00382 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c @@ -0,0 +1,10 @@ +extern int foo1(); + +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c new file mode 100644 index 0000000..22427e3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c @@ -0,0 +1,10 @@ +extern int foo1(void); + +int main(void) +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c new file mode 100644 index 0000000..a9c924d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c @@ -0,0 +1,11 @@ +extern int foo1(void); +extern void foo2(void); + +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c new file mode 100644 index 0000000..b44bf6e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c @@ -0,0 +1,14 @@ +extern int foo1(void); +extern void foo2(void); + +void foo4(void) +{ +} +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile b/ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile new file mode 100644 index 0000000..126c10d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a sanity check that an indirect +# library loaded with @loader_path works +# +# ld64 should handle linking against dylibs that have @loader_path based dylib load commands +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name @loader_path/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm *.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c b/ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c new file mode 100644 index 0000000..a307157 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c @@ -0,0 +1,6 @@ + +int bar() +{ + return 1; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c b/ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c new file mode 100644 index 0000000..8c2179d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c @@ -0,0 +1,7 @@ + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/main.c b/ld64/FireOpal/unit-tests/test-cases/loader_path/main.c new file mode 100644 index 0000000..829ca5e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/loader_path/main.c @@ -0,0 +1,8 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile new file mode 100644 index 0000000..8fc14f7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile @@ -0,0 +1,75 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# This test case checks -non_global_symbols_no_strip_list and -non_global_symbols_strip_list +# with and without wildcards +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c -o main + ${FAIL_IF_BAD_MACHO} main + nm -j main > main.nm + # build stripping a.list + ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_strip_list,a.list -o main-a + ${FAIL_IF_BAD_MACHO} main-a + nm -j main-a > main-a.nm + diff main.nm main-a.nm | egrep '<|>' > a.diff + diff a.diff a.expect | ${FAIL_IF_STDIN} + # build but strip at .o file level a.list + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r -arch ${ARCH} main.o foo.o -o all-a.o -non_global_symbols_strip_list a.list + ${CC} ${CCFLAGS} all-a.o -Wl,-non_global_symbols_strip_list,a.list -o main-a + ${FAIL_IF_BAD_MACHO} main-a + nm -j main-a > main-a.nm + diff main.nm main-a.nm | egrep '<|>' > a.diff + diff a.diff a.expect | ${FAIL_IF_STDIN} + # build stripping b.list + ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_strip_list,b.list -o main-b + ${FAIL_IF_BAD_MACHO} main-b + nm -j main-b > main-b.nm + diff main.nm main-b.nm | egrep '<|>' > b.diff + diff b.diff b.expect | ${FAIL_IF_STDIN} + # build but strip at .o file level b.list + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r -arch ${ARCH} main.o foo.o -o all-b.o -non_global_symbols_strip_list b.list + ${CC} ${CCFLAGS} all-b.o -Wl,-non_global_symbols_strip_list,b.list -o main-b + ${FAIL_IF_BAD_MACHO} main-b + nm -j main-b > main-b.nm + diff main.nm main-b.nm | egrep '<|>' > b.diff + diff b.diff b.expect | ${FAIL_IF_STDIN} + # build stripping c.list + ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_no_strip_list,c.list -o main-c + nm -m main-c | grep non-external | grep -v my | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main-c + + +clean: + rm -rf main main.nm main-a main-a.nm a.diff main-b main-b.nm b.diff main-c all-a.o all-b.o foo.o main.o diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect new file mode 100644 index 0000000..9ecbf53 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect @@ -0,0 +1,2 @@ +< _myglobal +< _xmyglobal2 diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list new file mode 100644 index 0000000..16c59a2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list @@ -0,0 +1,2 @@ +_myglobal +_xmyglobal2 diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect new file mode 100644 index 0000000..f1d15e4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect @@ -0,0 +1,3 @@ +< _myfunction +< _myglobal2 +< _xmyglobal2 diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list new file mode 100644 index 0000000..97170ac --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list @@ -0,0 +1,2 @@ +*2 +_myf*on diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list new file mode 100644 index 0000000..be715cd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list @@ -0,0 +1 @@ +*my* diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c new file mode 100644 index 0000000..42e6cb7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c @@ -0,0 +1,11 @@ + + +int __attribute__((visibility("hidden"))) myglobal = 3; +int __attribute__((visibility("hidden"))) myglobal2 = 3; +int __attribute__((visibility("hidden"))) xmyglobal = 3; +int __attribute__((visibility("hidden"))) xmyglobal2 = 3; + +void __attribute__((visibility("hidden"))) myfunction(int x) { } + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c new file mode 100644 index 0000000..013bc55 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c @@ -0,0 +1,11 @@ +#include + +extern int myglobal; +extern void myfunction(int); + +int main() +{ + myfunction(myglobal); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/Makefile b/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/Makefile new file mode 100644 index 0000000..5d4e991 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -mllvm optiions work. Verify --disable-inlining +# results in foo() not being inlined +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o -o main + nm main | grep _foo | ${FAIL_IF_STDIN} + ${LLVMGCC} ${CCFLAGS} main.o -o main2 -Wl,-mllvm -Wl,--disable-inlining + nm main2 | grep _foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main main.o main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/main.c b/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/main.c new file mode 100644 index 0000000..578d24b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/main.c @@ -0,0 +1,15 @@ + +#include + + +void foo(int x) +{ + printf("hello, world %d\n", x); +} + +int main() +{ + foo(10); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/Makefile b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/Makefile new file mode 100644 index 0000000..55ac0f1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that LTO works when a llvm bitcode has a weak symbol _foo +# and mach-o has a strong _foo. +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o foo.o -o main + otool -Iv main | grep _abort | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main foo.o main.o + \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c new file mode 100644 index 0000000..8df913a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c @@ -0,0 +1,6 @@ + + +__attribute__((visibility("hidden"))) void foo() +{ + // do nothing +} diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/main.c b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/main.c new file mode 100644 index 0000000..6a433db --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/main.c @@ -0,0 +1,17 @@ + +#include + +static void die() { abort(); } + + +__attribute__((visibility("hidden"),weak)) void foo() +{ + die(); +} + +int main() +{ + foo(); + +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile b/ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile new file mode 100644 index 0000000..03756ff --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a dynamically referenced symbol is always exported +# + +run: all + +all: + ${CC} main.c -o main-${ARCH} -exported_symbols_list main.exp + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -m main-${ARCH} | grep _magicSymbol | grep "referenced dynamically" | ${PASS_IFF_STDIN} + +clean: + rm main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c b/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c new file mode 100644 index 0000000..1e71f1b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// set magic "dynamically referenced" bit on magicSymbol +int magicSymbol = 1; +asm(".desc _magicSymbol, 0x10"); + + +int main() +{ + return 0; +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp b/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp new file mode 100644 index 0000000..4eb9e89 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp @@ -0,0 +1 @@ +_main diff --git a/ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile b/ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile new file mode 100644 index 0000000..81c3f58 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile @@ -0,0 +1,98 @@ +## +# Copyright (c) 2007 Apple, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that missing arguments don't cause ld to crash +# This tests 64-bit arguments only +# + + +OUTPUT=2>/dev/null +LDCMD=${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} ${OUTPUT} + +run: all + +all: + ${FAIL_IF_SUCCESS} ${LD} -arch 2>/dev/null + ${LDCMD} -filelist + ${LDCMD} -o + ${LDCMD} -read_only_relocs + ${LDCMD} -sect_diff_relocs + ${LDCMD} -weak_reference_mismatches + ${LDCMD} -l + ${LDCMD} -weak-l + ${LDCMD} -weak-library + ${LDCMD} -L + ${LDCMD} -syslibroot + ${LDCMD} -framework + ${LDCMD} -framework name, + ${LDCMD} -weak_framework + ${LDCMD} -weak_framework name + ${LDCMD} -weak_framework name, + ${LDCMD} -F + ${LDCMD} -dylib_file + ${LDCMD} -dylib_file install_name + ${LDCMD} -sectcreate segname sectname + ${LDCMD} -sectorder + ${LDCMD} -sectorder segname sectname + ${LDCMD} -u + ${LDCMD} -e + ${LDCMD} -i + ${LDCMD} -idefinition: + ${LDCMD} -undefined + ${LDCMD} -U + ${LDCMD} -commons + ${LDCMD} -warn_commons + ${LDCMD} -exported_symbols_list + ${LDCMD} -unexported_symbols_list + ${LDCMD} -filelist + ${LDCMD} -filelist listfile, + ${LDCMD} -headerpad + ${LDCMD} -A + ${LDCMD} -dylib_install_name + ${LDCMD} -umbrella + ${LDCMD} -allowable_client + ${LDCMD} -client_name + ${LDCMD} -sub_umbrella + ${LDCMD} -sub_library + ${LDCMD} -init + ${LDCMD} -dylinker_install_name + ${LDCMD} -macosx_version_min + ${LDCMD} -final_output + ${LDCMD} -seg1addr + ${LDCMD} -pagezero_size + ${LDCMD} -dylib_compatibility_version + ${LDCMD} -stack_addr + ${LDCMD} -stack_size + ${LDCMD} -sectcreate + ${LDCMD} -sectcreate segname + ${LDCMD} -sectalign + ${LDCMD} -sectalign segname + ${LDCMD} -sectalign segname sectname + ${LDCMD} -sectorder segname + ${LDCMD} -dylib_current_version + ${PASS_IFF} true + +clean: diff --git a/ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt b/ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt new file mode 100644 index 0000000..8000102 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt @@ -0,0 +1 @@ +Verify that missing arguments don't cause ld to crash diff --git a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile new file mode 100644 index 0000000..136bcf6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} test.s -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_sort test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_sort test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s new file mode 100644 index 0000000..4559893 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .text + .align 2 + + .globl _foo + .globl _foo2 + .globl _foo3 +_foo: +_foo2: +_foo3: + nop + + + + .align 2 +_bar: + nop + + + .align 2 + .globl _xx + .globl __xx +_xx: +__xx: + nop + + + .align 2 +_ok: + nop + diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest new file mode 100644 index 0000000..d0f7df8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to determine if +# common symbols are not allowed with MH_DYLIB output format with the -multi_module option +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${PASS_IFF_ERROR} libtool -dynamic -o libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null + +clean: + rm -rf *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt new file mode 100644 index 0000000..a5a960d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt @@ -0,0 +1 @@ +The point of this test is to determine if common symbols are not allowed with MH_DYLIB output format with the -multi_module option diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c new file mode 100644 index 0000000..f34267a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c @@ -0,0 +1,25 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile b/ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile new file mode 100644 index 0000000..5d52553 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set emit LC_UUID correctly +# + +run: all + +all: + +# Test main executable built with dwarf has uuid + ${CC} ${CCFLAGS} foo.c -o foo -gdwarf-2 + ${FAIL_IF_BAD_MACHO} foo + ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} + rm -f foo + +# Test main executable built with stabs has uuid + ${CC} ${CCFLAGS} foo.c -o foo -gfull -gstabs+ + ${FAIL_IF_BAD_MACHO} foo + ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} + +# Test main executable built with dwarf and -no_uuid does not have uuid + ${CC} ${CCFLAGS} foo.c -o foo -Wl,-no_uuid -gdwarf-2 + ${FAIL_IF_BAD_MACHO} foo + ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN} + +# Test ld -r of stabs file has no uuid + ${CC} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ + ${LD} -arch ${ARCH} foo.o -r -o foo2.o + ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN} + +# Test ld -r of two files one with uuid produces a uuid + ${CC} ${CCFLAGS} foo.c -c -o foo.o -gdwarf-2 + ${LD} -arch ${ARCH} foo.o -r -o foo2.o + ${CC} ${CCFLAGS} bar.c -c -gstabs+ -o bar.o + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + ${OTOOL} -hlv foobar.o | grep LC_UUID | ${PASS_IFF_STDIN} + +clean: + rm -rf foo foo.o foo2.o bar.o foobar.o foo.dSYM diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c b/ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c new file mode 100644 index 0000000..cbefe0f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c @@ -0,0 +1,4 @@ +int bar (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt b/ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt new file mode 100644 index 0000000..269bbbd --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt @@ -0,0 +1 @@ +Test the we set emit LC_UUID correctly diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c b/ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c new file mode 100644 index 0000000..57ed6ba --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c @@ -0,0 +1,4 @@ +int main (void) +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile new file mode 100644 index 0000000..3c14103 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that non-lazy-pointers are properly handled by -r +# + + +all: all-${ARCH} + +all-ppc: hasnl + +all-ppc64: hasnl + +all-i386: hasnl + +all-armv6: hasnl + +all-x86_64: all-true + +all-true: + ${PASS_IFF} true + + +hasnl: + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${CC} ${CCFLAGS} -c other.c -o other.o + ${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo + # make sure there are two indirect symbols: _foo and LOCAL + otool -Iv fooall.o | grep "2 entries" | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN} + # make sure re-parsed correctly + ${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c new file mode 100644 index 0000000..1fa325e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c @@ -0,0 +1,12 @@ + + + +extern int foo; + +int getfoo() { return foo; } + + +extern int other; + +int getother() { return other; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c new file mode 100644 index 0000000..6420437 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c @@ -0,0 +1,2 @@ +int foo = 2; +int other = 3; diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile new file mode 100644 index 0000000..44023e6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify no GSYM for .objc_category_* +# Linker should not make GSYM debug note for .objc_category_* symbols# +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation + nm -ap test | grep GSYM | grep category | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test test.dSYM diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m b/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m new file mode 100644 index 0000000..3f45e25 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +@interface NSObject (stuff) +@end + +@implementation NSObject (stuff) +@end + +@interface NSObject (other) +@end + +@implementation NSObject (other) +- (id) init { return self; } +@end + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile new file mode 100644 index 0000000..454a412 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Test that ObjC class exports can be suppressed +# + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.m -framework Foundation -exported_symbols_list foo.exp -o libfoo.dylib + nm -m libfoo.dylib | grep Foo | grep ') external' | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep Bar | grep non-external | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf *~ libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp new file mode 100644 index 0000000..720c52d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp @@ -0,0 +1 @@ +.objc_class_name_Foo diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m new file mode 100644 index 0000000..d39c8f7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m @@ -0,0 +1,18 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + + + +@interface Bar : NSObject +@end + +@implementation Bar +@end + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile new file mode 100644 index 0000000..0abe7d5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile @@ -0,0 +1,84 @@ +## +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + + +# +# Validate that the linker catches illegal combinations of .o files +# compiled with different GC settings. +# + +test: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + + ${CC} ${CCFLAGS} foo.m -c -o foo-gc.o -fobjc-gc + ${FAIL_IF_BAD_OBJ} foo-gc.o + + ${CC} ${CCFLAGS} foo.m -c -o foo-gc-only.o -fobjc-gc-only + ${FAIL_IF_BAD_OBJ} foo-gc-only.o + + ${CC} ${CCFLAGS} bar.m -c -o bar.o + ${FAIL_IF_BAD_OBJ} bar.o + + ${CC} ${CCFLAGS} bar.m -c -o bar-gc.o -fobjc-gc + ${FAIL_IF_BAD_OBJ} bar-gc.o + + ${CC} ${CCFLAGS} bar.m -c -o bar-gc-only.o -fobjc-gc-only + ${FAIL_IF_BAD_OBJ} bar-gc-only.o + + # check RR + RR -> RR + ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + + # check GC/RR + GC/RR -> GC/RR + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY} + + # check GC + GC -> GC + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} + + # check RR + GC/RR -> RR + ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + + # check GC + GC/RR -> GC + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} + + # check RR + GC -> error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log + + ${PASS_IFF} true + +clean: + rm -rf foo*.o bar*.o libfoobar.dylib fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m new file mode 100644 index 0000000..5c98709 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m @@ -0,0 +1,11 @@ + +@interface Bar { + int f; +} +- (void) doit; +@end + +@implementation Bar +- (void) doit { } +@end + diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt new file mode 100644 index 0000000..953da58 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt @@ -0,0 +1 @@ +Validate that the linker catches illegal combintations of .o files compiled with different GC settings diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m new file mode 100644 index 0000000..e13367e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m @@ -0,0 +1,12 @@ + +@interface Foo { + int f; +} +- (void) doit; +@end + + +@implementation Foo +- (void) doit { } +@end + diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c new file mode 100644 index 0000000..df4aeef --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c @@ -0,0 +1,2 @@ +void _objc_empty_cache() {} +void _objc_empty_vtable() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile new file mode 100644 index 0000000..9edc357 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify an Objective-C object file when run through +# ld -r is unaltered. +# __cls_refs section losing S_LITERAL_POINTERS section type +# +# note: i386 and ppc objc use some anonymous zerofill that moves and needs to be ignore to compare +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.o + ObjectDump -no_content test.o | grep -v zero-fill-at> test.dump + + ${LD} -arch ${ARCH} -r test.o -o test-r.o + ObjectDump -no_content test-r.o | grep -v zero-fill-at > test-r.dump + + diff test.dump test-r.dump | ${PASS_IFF_EMPTY} + +clean: + rm -rf test.o test.dump test-r.o test-r.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m b/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m new file mode 100644 index 0000000..a5c1ea8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +void test() +{ + // two class references + // two selector references + [NSObject superclass]; + [NSString description]; +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile new file mode 100644 index 0000000..a11f1d0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify an Objective-C object file +# is parsed to find the proper class references +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o + ${FAIL_IF_BAD_OBJ} test.${ARCH}.o + + ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_BAD_OBJ} test-r.${ARCH}.o + + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSObject' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSData' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSArray' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSString' | ${PASS_IFF_STDIN} + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt b/ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt new file mode 100644 index 0000000..ce67b71 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify an Objective-C object file is parsed to find the proper class references diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-references/test.m b/ld64/FireOpal/unit-tests/test-cases/objc-references/test.m new file mode 100644 index 0000000..d845227 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-references/test.m @@ -0,0 +1,52 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +@interface Foo : NSObject +- (NSString*) foo; +@end + + +@implementation Foo +- (NSString*) foo +{ + return [NSString stringWithUTF8String:"hello"]; +} +@end + + +@interface Bar : NSData +- (NSArray*) bar; +@end + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile new file mode 100644 index 0000000..982f41d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Test that two ObjC translation units that use the same selector +# link together. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.m other.m -o main -framework Foundation + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m new file mode 100644 index 0000000..a266128 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m @@ -0,0 +1,7 @@ +#include + + +NSString* other() +{ + return [NSString stringWithUTF8String:"hello"]; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m new file mode 100644 index 0000000..77fd926 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m @@ -0,0 +1,10 @@ + +#include + + +int main() +{ + [NSString stringWithUTF8String:"hello"]; + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile b/ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile new file mode 100644 index 0000000..8abf3e1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +run: all + +all: + # verify if operator new is overridden that WEAK_DEFINES is set + ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx + otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} + # verify if operator new is not overridden that WEAK_DEFINES is not set + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx + otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY} + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx b/ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx new file mode 100644 index 0000000..3c99e35 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + + +// +// This test case verifies overriding operator new sets the MH_WEAK_DEFINES bit +// + +#if OP_NEW +void* operator new(size_t s) throw (std::bad_alloc) +{ + return malloc(s);; +} +#endif + +int main() +{ + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile new file mode 100644 index 0000000..23fe568 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that -order_file can be used to order symbols with anonymous name spaces +# + +run: all + +all: + ${CXX} ${CXXFLAGS} main.cxx -DANCHOR=1 -o main -Wl,-order_file -Wl,main.order + ${FAIL_IF_BAD_MACHO} main + nm -n -g -j main | grep "_GLOBAL__N" > main.actual + ${PASS_IFF} diff main.actual main.expected + + +clean: + rm -rf main main.actual diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx new file mode 100644 index 0000000..b0412f9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx @@ -0,0 +1,62 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +#if ANCHOR + int anchor = 4; +#endif + +namespace { + struct myanonstruct { int a; }; +} + +// function defined in anonymous namespace +namespace { + void foo() { } +} + +// function that has an anonymous namespace parameter +void bar(myanonstruct* x) { } + + +// function in anonymous namespace that has an anonymous namespace parameter +namespace { + void baz(myanonstruct* x) { } +} + +// nested namespace +namespace wow { + namespace { + void inner() { } + } +} + + + + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected new file mode 100644 index 0000000..75e104f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected @@ -0,0 +1,4 @@ +__Z3barPN17_GLOBAL__N_anchor12myanonstructE +__ZN3wow17_GLOBAL__N_anchor5innerEv +__ZN17_GLOBAL__N_anchor3bazEPNS_12myanonstructE +__ZN17_GLOBAL__N_anchor3fooEv diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order new file mode 100644 index 0000000..36dd786 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order @@ -0,0 +1,4 @@ +__Z3barPN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C12myanonstructE +__ZN3wow95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C5innerEv +__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3bazEPNS_12myanonstructE +__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3fooEv diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/Makefile b/ld64/FireOpal/unit-tests/test-cases/order_file/Makefile new file mode 100644 index 0000000..21ae0ea --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check -order_file. +# The main1 test verifies that C functions can be re-ordered +# The main2 test verifies that a block of assembly is not moves en mas +# The main1 test verifies that an order file with spaces and comments works +# + +run: all + +all: + as -arch ${ARCH} -L extra.s -o extra.o + ${CC} ${CCFLAGS} main.c extra.o -o main1 -Wl,-order_file -Wl,main1.order + ${FAIL_IF_BAD_MACHO} main1 + nm -n -g -j main1 | grep "_main" > main1.nm + ${PASS_IFF} diff main1.nm main1.expected + + ${CC} ${CCFLAGS} main.c extra.o -o main2 -Wl,-order_file -Wl,main2.order + ${FAIL_IF_BAD_MACHO} main2 + nm -n -j main2 | egrep '^_[a-z]+[0-9]$$' > main2.nm + ${PASS_IFF} diff main2.nm main2.expected + + ${CC} -arch ${ARCH} -c main.c -o main.o + ${CC} ${CCFLAGS} main.o extra.o -o main3 -Wl,-order_file -Wl,main3.order + ${FAIL_IF_BAD_MACHO} main3 + nm -n -g -j main3 | grep "_main" > main3.nm + ${PASS_IFF} diff main3.nm main3.expected + + + + +clean: + rm -rf main1 *.nm main2 *.o warnings.log main3 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/extra.s b/ld64/FireOpal/unit-tests/test-cases/order_file/extra.s new file mode 100644 index 0000000..90166ce --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/extra.s @@ -0,0 +1,24 @@ + + + .text + + .globl _foo1 +_foo1: nop + + .globl _aaa2 +_aaa2: +_bbb2: +_ccc2: + nop + + .globl _bbb3 +_aaa3: +_bbb3: +_ccc3: + nop + + +_aaa4: + nop + + \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main.c b/ld64/FireOpal/unit-tests/test-cases/order_file/main.c new file mode 100644 index 0000000..5643b45 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} + +void main2() {} +void main3() {} +void main4() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected b/ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected new file mode 100644 index 0000000..04d128b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected @@ -0,0 +1,4 @@ +_main4 +_main3 +_main +_main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main1.order b/ld64/FireOpal/unit-tests/test-cases/order_file/main1.order new file mode 100644 index 0000000..06b34d5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main1.order @@ -0,0 +1,4 @@ +_main4 +_main3 + + diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected b/ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected new file mode 100644 index 0000000..8aca65c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected @@ -0,0 +1,11 @@ +_main3 +_foo1 +_aaa2 +_bbb2 +_ccc2 +_aaa3 +_bbb3 +_ccc3 +_aaa4 +_main4 +_main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main2.order b/ld64/FireOpal/unit-tests/test-cases/order_file/main2.order new file mode 100644 index 0000000..87f89e6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main2.order @@ -0,0 +1,6 @@ +_main3 +_aaa3 +_main4 + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected b/ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected new file mode 100644 index 0000000..04d128b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected @@ -0,0 +1,4 @@ +_main4 +_main3 +_main +_main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main3.order b/ld64/FireOpal/unit-tests/test-cases/order_file/main3.order new file mode 100644 index 0000000..d135527 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/order_file/main3.order @@ -0,0 +1,8 @@ + +# spaces before and after main4 +main.o: _main4 +# +main.o: _main3# trailing comment +# + + diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile b/ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile new file mode 100644 index 0000000..79ef53e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# 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: + ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 + otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c b/ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c new file mode 100644 index 0000000..251979e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c @@ -0,0 +1,3 @@ + +int main() { return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile new file mode 100644 index 0000000..07bce59 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is to build a prebound split-seg library +# + +run: all + +all: + ${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 diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table new file mode 100644 index 0000000..b611ca8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table @@ -0,0 +1,4 @@ +# comment +0x91000000 0xA1000000 /foo/bar/libbar.dylib +# + diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c new file mode 100644 index 0000000..46b7269 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int x = 3; +int* xp = &x; + + +int bar() +{ + return *xp; +} + +void* pbar = &bar; + diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile new file mode 100644 index 0000000..be0ba42 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that a non-lazy-pointer +# in foo.o to a private-extern symbol in bar.o will +# properly survive ld -r +# + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + + ${CC} ${CCFLAGS} -c bar.c -o bar.o + ${FAIL_IF_BAD_OBJ} bar.o + + ${LD} -r foo.o bar.o -o foobar.o -arch ${ARCH} + ${FAIL_IF_BAD_OBJ} foobar.o + + ${CC} ${CCFLAGS} hello.c foobar.o -o hello + ${FAIL_IF_BAD_MACHO} hello + + ${LD} -r foo.o bar.o -o foobar2.o -arch ${ARCH} -keep_private_externs + ${FAIL_IF_BAD_OBJ} foobar2.o + + ${CC} ${CCFLAGS} hello.c foobar2.o -o hello2 + ${PASS_IFF_GOOD_MACHO} hello2 + +clean: + rm -rf *.o hello hello2 diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c new file mode 100644 index 0000000..601dc69 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c @@ -0,0 +1,3 @@ + +int __attribute__((visibility("hidden"))) foo = 0; + diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt new file mode 100644 index 0000000..e6d11c0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt @@ -0,0 +1 @@ +The point of this test is to check that a non-lazy-pointer in foo.o to a private-extern symbol in bar.o will properly survive ld -r diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c new file mode 100644 index 0000000..6816d0b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c @@ -0,0 +1,7 @@ + + +extern int foo; + +int getfoo() { return foo; } + + diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c new file mode 100644 index 0000000..20dccc4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int getfoo(); + +int main() +{ + return getfoo(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile new file mode 100644 index 0000000..64ec112 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile @@ -0,0 +1,167 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test all the different ways that re-exports can be specified and implemented +# + + +run: all + +all: + +# -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 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -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} + + +# -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 + 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 + ${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 + ${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 + 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 + ${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 + ${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 + ${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 + otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} + otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} + +# -reexport_library 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_library,libbar.dylib -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-l 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 -Wl,-reexport-lbar -L. -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} + +# -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 + 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. -Wl,-reexport_framework,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} + +# -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" -mmacosx-version-min=10.5 + ${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 + ${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 -mmacosx-version-min=10.4 + ${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 + ${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} + + +# -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 + ${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 + ${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 + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile new file mode 100644 index 0000000..82228ee --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that the MH_NO_REEXPORTED_DYLIBS bit is set in dylibs with no re-exports +# + +run: all + +all: +# build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + +# build library the re-exports base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -sub_library libbar +# test that foo does not have MH_NO_REEXPORTED_DYLIBS bit + ${FAIL_IF_BAD_MACHO} libfoo.dylib + +# build libray that links with base but does not re-export it + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + otool -hv libfoo2.dylib | grep NO_REEXPORTED_DYLIBS | ${PASS_IFF_STDIN} + +clean: + rm -rf *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c new file mode 100644 index 0000000..34e5666 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile new file mode 100644 index 0000000..a1dfd88 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a public re-exported library is automatically added as a dependent +# unless nothing is used from it. +# + + +run: all + +all: + +# -sub_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.4 + 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.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 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.5 + 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 + otool -L main | grep libbar | ${FAIL_IF_STDIN} + + + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libbar.dylib libfoo.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c new file mode 100644 index 0000000..2b85b0e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c @@ -0,0 +1,10 @@ + +extern void bar(); + +int main() +{ +#if CALL_BAR + bar(); +#endif + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile new file mode 100644 index 0000000..2560a86 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that @loader_path and @executable_path can be resolved finding indirect dylibs +# + + +run: all + +all: + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -install_name '@loader_path/libfoo.dylib' -o hide/libfoo.dylib + ${FAIL_IF_BAD_MACHO} hide/libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib -install_name '@executable_path/hide/libbar.dylib' + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide + ${FAIL_IF_BAD_MACHO} hide/libwrap.dylib + ${CC} ${CCFLAGS} main.c -o main hide/libwrap.dylib + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd`/main + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd` + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib libwrap.dylib main libmain.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c new file mode 100644 index 0000000..367c6cb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c @@ -0,0 +1,11 @@ +extern int foo(); +extern int bar(); +extern int wrap(); + +int main() +{ + foo(); + bar(); + wrap(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c new file mode 100644 index 0000000..d3cdd85 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c @@ -0,0 +1,2 @@ +int wrap() { return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile new file mode 100644 index 0000000..02ed1df --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that ld can linke a dylib built with -mdynamic-no-pic +# + + +SHELL = bash # use bash shell so we can redirect just stderr + +NO_PIC = +STATIC = + +ifeq (${ARCH},i386) + NO_PIC = -mdynamic-no-pic + STATIC = -static +else + ifeq (${ARCH},ppc) + NO_PIC = -mdynamic-no-pic + STATIC = -mdynamic-no-pic + endif +endif + + + +all: + # build libfoo.dylib as regular dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + # build libtest.dylib using -mdynamic-no-pic and -read_only_relocs suppress + ${CC} ${CCFLAGS} test.c -c ${NO_PIC} + ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + # build libtest.dylib using -static and -read_only_relocs suppress + ${CC} ${CCFLAGS} test.c -c ${STATIC} + ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + # build main using -static and -read_only_relocs suppress + ${CC} ${CCFLAGS} test.c -c ${STATIC} + ${CC} ${CCFLAGS} test.o libfoo.dylib -o foo -read_only_relocs suppress -Wl,-w + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf test.o libfoo.dylib libtest.dylib foo diff --git a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c new file mode 100644 index 0000000..bef1580 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c @@ -0,0 +1,6 @@ + +int b=0; + +void func() {} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c new file mode 100644 index 0000000..f3484e2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int a=0; +extern int b; +extern void func(); + +int main() +{ + func(); + return a+b; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile new file mode 100644 index 0000000..ba262aa --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to see that a dylib +# run through the rebase tool is the same as if +# the dylib was originally built at that address +# + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o + + ${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o + + ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation + ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib + + ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo-alt.${ARCH}.dylib + + rebase -arch ${ARCH} -low_address 0x12340000 libfoo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib + + ${PASS_IFF} diff libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib + +clean: + rm *.o *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m new file mode 100644 index 0000000..ff4f2ac --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m @@ -0,0 +1,13 @@ +#include + +@interface Bar : NSObject + +-(void) blah; + +@end + +@implementation Bar + +-(void) blah {} + +@end \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt new file mode 100644 index 0000000..013eb45 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt @@ -0,0 +1 @@ +The point of this test is to see that a dylib run through the rebase tool is the same as if the dylib was originally built at that address diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c new file mode 100644 index 0000000..15d4ae6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c @@ -0,0 +1,14 @@ + +int foo() { return 10; } + +void* foop = &foo; + +int glob = 5; + +int* globp = &glob; + + +int big[3000]; + + + \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile new file mode 100644 index 0000000..c231ac4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/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 + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} relocs-asm.s -c -o relocs-asm.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content relocs-asm.${ARCH}.o > relocs-asm.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs relocs-asm.${ARCH}.o -o relocs-asm-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content relocs-asm-r.${ARCH}.o > relocs-asm-r.${ARCH}.o.dump + + ${PASS_IFF} diff relocs-asm.${ARCH}.o.dump relocs-asm-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s new file mode 100644 index 0000000..06e10e8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#if __arm__ + .text + .align 2 + + .globl _test_loads +_test_loads: + @ PIC load of a + ldr r0, L6 +L0: + ldr r0, [pc, r0] + + @ PIC load of c + ldr r0, L6+4 +L1: + ldr r0, [pc, r0] + + @ sorta-absolute load of a + ldr r0, L6+8 + ldr r0, [r0, #0] + + @ sorta-absolute load of c + ldr r0, L6+12 + ldr r0, [r0, #0] + + @ sorta-absolute load of external + ldr r0, L6+16 + ldr r0, [r0, #0] + + @ PIC load of a + addend ?? + bx lr + +L6: + .long _a-(L0+8) + .long _c-(L1+8) + .long _a + .long _c + .long _ax + +_test_calls: + @ call internal + bl _test_branches + + @ call internal + addend + bl _test_branches+0x19000 + + @ call external + bl _external + + @ call external + addend + bl _external+0x19000 + + +_test_branches: + @ call internal + bne _test_calls + + @ call internal + addend + bne _test_calls+16 + + @ call external + bne _external + + @ call external + addend + bne _external+16 +#endif + +#if __ppc__ || __ppc64__ + + .text + .align 2 + + .globl _test_loads +_test_loads: + stmw r30,-8(r1) + stwu r1,-48(r1) +Lpicbase: + + ; PIC load of a + addis r2,r10,ha16(_a-Lpicbase) + lwz r2,lo16(_a-Lpicbase)(r2) + + ; PIC load of c + addis r2,r10,ha16(_c-Lpicbase) + lwz r2,lo16(_c-Lpicbase)(r2) + + ; absolute load of a + lis r2,ha16(_a) + lwz r2,lo16(_a)(r2) + + ; absolute load of c + lis r2,ha16(_c) + lwz r2,lo16(_c)(r2) + + ; absolute load of external + lis r2,ha16(_ax) + lwz r2,lo16(_ax)(r2) + + ; absolute lea of external + lis r2,hi16(_ax) + ori r2,r2,lo16(_ax) + + + ; PIC load of a + addend + addis r2,r10,ha16(_a+0x19000-Lpicbase) + lwz r2,lo16(_a+0x19000-Lpicbase)(r2) + + ; absolute load of a + addend + lis r2,ha16(_a+0x19000) + lwz r2,lo16(_a+0x19000)(r2) + + ; lea of a + addend + lis r2,ha16(_a+0x19000) + addi r2,r2,lo16(_a+0x19000) + + ; alt lea of a + addend + lis r2,hi16(_a+0x19000) + ori r2,r2,lo16(_a+0x19000) + + ; absolute load of external + addend + lis r2,ha16(_ax+0x19000) + lwz r2,lo16(_ax+0x19000)(r2) + + ; absolute lea of external + addend + lis r2,hi16(_ax+0x19000) + ori r2,r2,lo16(_ax+0x19000) + + + ; PIC load of a + addend + addis r2,r10,ha16(_a+0x09000-Lpicbase) + lwz r2,lo16(_a+0x09000-Lpicbase)(r2) + + ; absolute load of a + addend + lis r2,ha16(_a+0x09000) + lwz r2,lo16(_a+0x09000)(r2) + + ; lea of a + addend + lis r2,ha16(_a+0x09000) + addi r2,r2,lo16(_a+0x09000) + + ; alt lea of a + addend + lis r2,hi16(_a+0x09000) + ori r2,r2,lo16(_a+0x09000) + + ; absolute load of external + addend + lis r2,ha16(_ax+0x09000) + lwz r2,lo16(_ax+0x09000)(r2) + + ; absolute lea of external + addend + lis r2,hi16(_ax+0x09000) + ori r2,r2,lo16(_ax+0x09000) + + blr + + +_test_calls: + ; call internal + bl _test_branches + + ; call internal + addend + bl _test_branches+0x19000 + + ; call external + bl _external + + ; call external + addend + bl _external+0x19000 + + +_test_branches: + ; call internal + bne _test_calls + + ; call internal + addend + bne _test_calls+16 + + ; call external + bne _external + + ; call external + addend + bne _external+16 +#endif + + + +#if __i386__ + .text + .align 2 + + .globl _test_loads +_test_loads: + pushl %ebp +Lpicbase: + + # PIC load of a + movl _a-Lpicbase(%ebx), %eax + + # absolute load of a + movl _a, %eax + + # absolute load of external + movl _ax, %eax + + # absolute lea of external + leal _ax, %eax + + + # PIC load of a + addend + movl _a-Lpicbase+0x19000(%ebx), %eax + + # absolute load of a + addend + movl _a+0x19000(%ebx), %eax + + # absolute load of external + addend + movl _ax+0x19000(%ebx), %eax + + # absolute lea of external + addend + leal _ax+0x1900, %eax + + ret + + +_test_calls: + # call internal + call _test_branches + + # call internal + addend + call _test_branches+0x19000 + + # call external + call _external + + # call external + addend + call _external+0x19000 + + +_test_branches: + # call internal + jne _test_calls + + # call internal + addend + jne _test_calls+16 + + # call external + jne _external + + # call external + addend + jne _external+16 + +_pointer_diffs: + nop + call _get_ret_eax +1: movl _foo-1b(%eax),%esi + movl _foo+10-1b(%eax),%esi + movl _test_branches-1b(%eax),%esi + movl _test_branches+3-1b(%eax),%esi + +_word_relocs: + callw _pointer_diffs + +_byte_relocs: + mov $100, %ecx +c_1: + loop c_1 + mov $100, %ecx +c_2: + sub $(1), %ecx + jcxz c_2 + +#endif + + + +#if __x86_64__ + .text + .align 2 + + .globl _test_loads +_test_loads: + + # PIC load of a + movl _a(%rip), %eax + + # PIC load of a + addend + movl _a+0x1234(%rip), %eax + + # PIC lea + leaq _a(%rip), %rax + + # PIC lea through GOT + movq _a@GOTPCREL(%rip), %rax + + # PIC access of GOT + pushq _a@GOTPCREL(%rip) + + # PIC lea external through GOT + movq _ax@GOTPCREL(%rip), %rax + + # PIC external access of GOT + pushq _ax@GOTPCREL(%rip) + + # 1-byte store + movb $0x12, _a(%rip) + movb $0x12, _a+2(%rip) + movb $0x12, L0(%rip) + + # 4-byte store + movl $0x12345678, _a(%rip) + movl $0x12345678, _a+4(%rip) + movl $0x12345678, L0(%rip) + + # test local labels + lea L1(%rip), %rax + movl L0(%rip), %eax + + ret + + +_test_calls: + # call internal + call _test_branches + + # call internal + addend + call _test_branches+0x19000 + + # call external + call _external + + # call external + addend + call _external+0x19000 + + +_test_branches: + # call internal + jne _test_calls + + # call internal + addend + jne _test_calls+16 + + # call external + jne _external + + # call external + addend + jne _external+16 + +_byte_relocs: + mov $100, %ecx +c_1: + loop _byte_relocs + nop + +#endif + + + + # test that pointer-diff relocs are preserved + .text +_test_diffs: + .align 2 +Llocal2: + .long 0 + .long Llocal2-_test_branches + .long . - _test_branches + .long . - _test_branches + 8 + .long _test_branches - . + .long _test_branches - . + 8 + .long _test_branches - . - 8 +#if __ppc64__ + .quad Llocal2-_test_branches +#endif + +_foo: nop + + .align 2 +_distance_from_foo: + .long 0 + .long . - _foo + .long . - 8 - _foo + + +_distance_to_foo: + .long _foo - . + .long _foo - . + 4 + + +_distance_to_here: + .long _foo - _distance_to_here + .long _foo - _distance_to_here - 4 + .long _foo - _distance_to_here - 12 + .long 0 + + +#if __x86_64__ + .data +L0: .quad _test_branches +_prev: + .quad _test_branches+4 +L1: .quad _test_branches - _test_diffs + .quad _test_branches - _test_diffs + 4 + .long _test_branches - _test_diffs +# .long LCL0-. ### assembler bug: should SUB/UNSIGNED with content= LCL0-24, or single pc-rel SIGNED reloc with content = LCL0-.+4 + .quad L1 + .quad L0 + .quad _test_branches - . + .quad _test_branches - L1 + .quad L1 - _prev + +# the following generates: _foo cannot be undefined in a subtraction expression +# but it should be ok (it will be a linker error if _foo and _bar are not in same linkage unit) +# .quad _foo - _bar ### assembler bug + + .section __DATA,__data2 +LCL0: .long 2 + + +#endif + + + .data +_a: + .long 0 + +_b: +#if __ppc__ || __i386__ || __arm__ + .long _test_calls + .long _test_calls+16 + .long _external + .long _external+16 +#elif __ppc64__ || __x86_64__ + .quad _test_calls + .quad _test_calls+16 + .quad _external + .quad _external+16 +#endif + + # test that reloc sizes are the same +Llocal3: + .long 0 + +Llocal4: + .long 0 + + .long Llocal4-Llocal3 + +Lfiller: + .space 0x9000 +_c: + .long 0 + diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile new file mode 100644 index 0000000..7428127 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +ifeq (${ARCH},x86_64) + ADDR_SHIFT = 0x1FF000000 +else + ADDR_SHIFT = 0xF0000000 +endif + + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${FAIL_IF_ERROR} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -seg1addr ${ADDR_SHIFT} -o test2-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test2-r.${ARCH}.o > test2-r.${ARCH}.o.dump + ${PASS_IFF} diff test.${ARCH}.o.dump test2-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c new file mode 100644 index 0000000..b877760 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c @@ -0,0 +1,76 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +static int foo; + +int __attribute__((visibility("hidden"))) foofoo; + +static int uninit_static; +static int init_static = 1; + int __attribute__((visibility("hidden"))) uninit_hidden; + int __attribute__((visibility("hidden"))) init_hidden = 1; + int uninit_global; + int init_global = 1; +extern int extern_global; +extern int __attribute__((visibility("hidden"))) extern_hidden; + +static int uninit_static_array[4]; +static int init_static_array[4] = {1,2,3,4}; + int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; + int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; + int uninit_global_array[4]; + int init_global_array[4] = {1,2,3,4}; +extern int extern_global_array[4]; + +int test1() { return uninit_static; } +int test2() { return init_static; } +int test3() { return uninit_hidden; } +int test4() { return init_hidden; } +int test5() { return uninit_global; } +int test6() { return init_global; } +int test7() { return extern_global; } +int test8() { return extern_hidden; } + +int test_array1() { return uninit_static_array[2]; } +int test_array2() { return init_static_array[2]; } +int test_array3() { return uninit_hidden_array[2]; } +int test_array4() { return init_hidden_array[2]; } +int test_array5() { return uninit_global_array[2]; } +int test_array6() { return init_global_array[2]; } +int test_array7() { return extern_global_array[2]; } + +static int foo2; +int test9() { return foo2; } + + +int* p_init_global = &init_global; +void* p_test1 = (void*)&test1; +unsigned char pad = 2; +unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... + +int func() __attribute__((visibility("hidden"))); +int func() { return foo; } + +int func2() { return func() + 1; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile new file mode 100644 index 0000000..767b210 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile @@ -0,0 +1,58 @@ +## +# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# +# Currently for ppc64 the .o's alternate! in content +# + + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + #grep "plus" test.${ARCH}.o.dump | ${FAIL_IF_STDIN} + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + #grep "plus" test-r.${ARCH}.o.dump | ${FAIL_IF_STDIN} + + ${LD} -arch ${ARCH} -r -keep_private_externs test-r.${ARCH}.o -o test-r-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r-r.${ARCH}.o > test-r-r.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test-r-r.${ARCH}.o -o test-r-r-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r-r-r.${ARCH}.o > test-r-r-r.${ARCH}.o.dump + + ${PASS_IFF} diff -c -w test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt new file mode 100644 index 0000000..2499674 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt @@ -0,0 +1,5 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes + +Currently for ppc64 the .o's alternate! in content diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c new file mode 100644 index 0000000..b877760 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c @@ -0,0 +1,76 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +static int foo; + +int __attribute__((visibility("hidden"))) foofoo; + +static int uninit_static; +static int init_static = 1; + int __attribute__((visibility("hidden"))) uninit_hidden; + int __attribute__((visibility("hidden"))) init_hidden = 1; + int uninit_global; + int init_global = 1; +extern int extern_global; +extern int __attribute__((visibility("hidden"))) extern_hidden; + +static int uninit_static_array[4]; +static int init_static_array[4] = {1,2,3,4}; + int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; + int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; + int uninit_global_array[4]; + int init_global_array[4] = {1,2,3,4}; +extern int extern_global_array[4]; + +int test1() { return uninit_static; } +int test2() { return init_static; } +int test3() { return uninit_hidden; } +int test4() { return init_hidden; } +int test5() { return uninit_global; } +int test6() { return init_global; } +int test7() { return extern_global; } +int test8() { return extern_hidden; } + +int test_array1() { return uninit_static_array[2]; } +int test_array2() { return init_static_array[2]; } +int test_array3() { return uninit_hidden_array[2]; } +int test_array4() { return init_hidden_array[2]; } +int test_array5() { return uninit_global_array[2]; } +int test_array6() { return init_global_array[2]; } +int test_array7() { return extern_global_array[2]; } + +static int foo2; +int test9() { return foo2; } + + +int* p_init_global = &init_global; +void* p_test1 = (void*)&test1; +unsigned char pad = 2; +unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... + +int func() __attribute__((visibility("hidden"))); +int func() { return foo; } + +int func2() { return func() + 1; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile new file mode 100644 index 0000000..a9ca5ef --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c new file mode 100644 index 0000000..2d199d0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + + +// rdar://problem/4732996 +const char* stringFutz(int x) { + return "hello" + 0x1000 + x; +} + +const char* usesAddend = "teststr" + 0x2000; diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile new file mode 100644 index 0000000..23e4a82 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# +ifneq (${ARCH},x86_64) + PIC=-mdynamic-no-pic +endif + +run: all + +all: + ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c new file mode 100644 index 0000000..2d199d0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + + +// rdar://problem/4732996 +const char* stringFutz(int x) { + return "hello" + 0x1000 + x; +} + +const char* usesAddend = "teststr" + 0x2000; diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile new file mode 100644 index 0000000..e0fa4ad --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} -Os -mdynamic-no-pic test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff -C 6 test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c new file mode 100644 index 0000000..31e87c2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c @@ -0,0 +1,47 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile new file mode 100644 index 0000000..0f8846d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m new file mode 100644 index 0000000..1ca2157 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m @@ -0,0 +1,59 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +@interface Foo : NSObject +{ + int ivar; +} +- (id) init; +- (void) foo; +@end + + +@implementation Foo +- (id) init +{ + self = [super init]; + return self; +} + +- (void) foo +{ + [self class]; +} +@end + + + +@interface Base +@end + + +@implementation Base +@end + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile b/ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile new file mode 100644 index 0000000..76b4e95 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker puts non-standard segments in order of discovery +# + +all: + ${CC} ${CCFLAGS} main.c segKKK.s segJJJ.s segLLL.s -o main + nm -j -n main | grep _sym_ > symbol.order + ${FAIL_IF_ERROR} diff symbol.order expected.order + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main symbol.order diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order b/ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order new file mode 100644 index 0000000..e05b042 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order @@ -0,0 +1,3 @@ +_sym_kkk +_sym_jjj +_sym_lll diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/main.c b/ld64/FireOpal/unit-tests/test-cases/segment-order/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/segment-order/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s b/ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s new file mode 100644 index 0000000..d9f5f71 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s @@ -0,0 +1,7 @@ + + .section __JJJ,__jjj +_sym_jjj: .space 128 + + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s b/ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s new file mode 100644 index 0000000..70b1952 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s @@ -0,0 +1,7 @@ + + .section __KKK,__kkk +_sym_kkk: .space 128 + + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s b/ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s new file mode 100644 index 0000000..045eea4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s @@ -0,0 +1,7 @@ + + .section __LLL,__lll +_sym_lll: .space 128 + + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile b/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile new file mode 100644 index 0000000..ee6a0e5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Sanity check that -slow_stubs for i386 leaves no __IMPORT segment +# + +run: all + + + +all: + ${CC} ${CCFLAGS} hello.c -o hello -Wl,-slow_stubs + size -l hello | grep __IMPORT | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib -Wl,-slow_stubs + size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} hello + +clean: + rm hello libhello.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c b/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c new file mode 100644 index 0000000..fe3b0df --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile b/ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile new file mode 100644 index 0000000..060f12e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# automatically strips labels starting with 'l' and 'L' +# + +run: all + +all: + as -arch ${ARCH} -L extra.s -o extra.o + ${CC} ${CCFLAGS} main.c extra.o -o main + nm main | grep "lother" | ${FAIL_IF_STDIN} + nm main | grep "L123" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s b/ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s new file mode 100644 index 0000000..0755508 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s @@ -0,0 +1,9 @@ + + + .data + +_foo: .long 0 +lother: .long 0 +L123: .long 0 +_bar: .long 0 + diff --git a/ld64/FireOpal/unit-tests/test-cases/special-labels/main.c b/ld64/FireOpal/unit-tests/test-cases/special-labels/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/special-labels/main.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile new file mode 100644 index 0000000..6e11c59 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# removes the stabs associated with a copy of a coalesced +# function that was removed. +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: hello.o other.o + ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH} + nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count + echo " 1" > one + ${PASS_IFF} diff stabs-hello-foo-count one + +hello.o : hello.cxx + ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ + ${FAIL_IF_BAD_OBJ} $@ + +other.o : other.cxx + ${CXX} ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ + ${FAIL_IF_BAD_OBJ} $@ + +clean: + rm -rf stabs-hello-* *.o *.stabs stabs-hello-foo-count one diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt new file mode 100644 index 0000000..f22b9a1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt @@ -0,0 +1,3 @@ +The point of this test is a sanity check that ld removes the stabs associated with a copy of a coalesced +function that was removed. Running nm through stabs-filter.pl produces connonical stabs +that can be diffed against a checked in know good set of stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h new file mode 100644 index 0000000..378308f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx new file mode 100644 index 0000000..33bf273 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx new file mode 100644 index 0000000..ee97d7d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "header.h" + +int uninit; +int init = 1; +static int suninit; +static int sinit=0; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile b/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile new file mode 100644 index 0000000..5318933 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test that file paths in a stab reference ends with a / +# if there is no terminating /, gdb does not recognize this as a file path +# The provided files coalesced1a.o coalesced1b.o are ppc64 linked +# rdar://problem/4565088 + +run: all + +all: + $(CXX) -gstabs+ main.c -o outfile + ${FAIL_IF_BAD_MACHO} outfile + nm -ap outfile | ${PASS_IFF} grep '.*\.*test-cases.*/$$' + +clean: + rm outfile* diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c b/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c new file mode 100644 index 0000000..54dc4c5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c @@ -0,0 +1,3 @@ +main() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest new file mode 100644 index 0000000..a9e452f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest @@ -0,0 +1,77 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test the ld commands -stack_addr, -stack_size +# Test using -stack_addr only + + +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xC0000000 + STACK_SIZE = 0x04000000 + STACK_TOP = 0xbc000000 +else +#ifeq (${ARCH},x86_64) + STACK_ADDR = 0x0007fff5fc000000 + STACK_TOP = 0x00007fff57000000 + STACK_SIZE = 0x0000000005000000 +#else + #STACK_ADDR = 0x0007ffff00000000 + #STACK_TOP = 0x0007fffefb000000 + #STACK_SIZE = 0x0000000005000000 +#endif +endif + + +run: all + +all: +# info seems to not work, use warning: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c main.c -o main-${ARCH}.o + + + ${FAIL_IF_ERROR} ${LD} -arch ${ARCH} \ + -stack_addr ${STACK_ADDR} \ + -lcrt1.o -lSystem \ + main-${ARCH}.o -o main \ + 2>lderr.out + +# Can check warning if desired. +#ifeq (,${findstring 64,$(ARCH)}) +# grep "warning no -stack_size specified using the default size" lderr.out | ${FAIL_IF_EMPTY} +#else +# grep "failed: -stack_addr must be used with -stack_size" lderr.out | ${FAIL_IF_EMPTY} +#endif + + +# Check for __UNIXSTACK section in object, check that it has the correct value + ${FAIL_IF_ERROR} ${OTOOL} -l main>ldcmds.out + (echo '1,/^[ ]*segname __UNIXSTACK$$/-d'; echo '/^[ ]*segname /,$$d'; echo w; echo q) | ed ldcmds.out >/dev/null + grep __UNIXSTACK ldcmds.out | ${FAIL_IF_EMPTY} + grep " vmsize[ ]*${STACK_SIZE}" ldcmds.out | ${FAIL_IF_EMPTY} + grep " vmaddr[ ]*${STACK_TOP}" ldcmds.out | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.o *.err *.out main diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt new file mode 100644 index 0000000..da74f89 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt @@ -0,0 +1,11 @@ +Test the ld commands -stack_addr, -stack_size (3939852 and 4729162) + + +-stack_addr value +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. + + +-stack_size value +Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . + + diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile new file mode 100644 index 0000000..670f014 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test the ld option -stack_addr and -stack_size used together + +ifeq ($(ARCH),armv6) + STACK_ADDR = 0x2C000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0x27000000 +else +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xCC000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0xc7000000 +else + STACK_ADDR = 0x110000000 + STACK_TOP = 0x000000010b000000 + STACK_SIZE = 0x0000000005000000 +endif +endif + +run: all + + + +all: + ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR} + # Check for __UNIXSTACK section in object, check that it has the correct value + 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 + +clean: + rm -rf main main.otool diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt new file mode 100644 index 0000000..da74f89 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt @@ -0,0 +1,11 @@ +Test the ld commands -stack_addr, -stack_size (3939852 and 4729162) + + +-stack_addr value +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. + + +-stack_size value +Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . + + diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile new file mode 100644 index 0000000..6134c7e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test the ld option -stack_size adds a custom stack segment + +ifeq ($(ARCH),armv6) + STACK_ADDR = 0x30000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0x2b000000 +else +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xC0000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0xbb000000 +else + STACK_ADDR = 0x0007fff5fc000000 + STACK_TOP = 0x00007fff57000000 + STACK_SIZE = 0x0000000005000000 +endif +endif + + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} + # Check for __UNIXSTACK section in object, check that it has the correct value + 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 + +clean: + rm -rf main main.otool diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt new file mode 100644 index 0000000..5933975 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt @@ -0,0 +1,11 @@ +Test the ld commands -stack_addr, -stack_size + + +-stack_addr value +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. + + +-stack_size value +Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . + + diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c new file mode 100644 index 0000000..4aaef3a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#if __x86_64__ +static char buffer[8000000000]; +#elif __arm__ +static char buffer[100000000]; +#else +static char buffer[2000000000]; +#endif + +int main() +{ + return buffer[0]; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile b/ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile new file mode 100644 index 0000000..2d71eff --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile @@ -0,0 +1,35 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that ld can link a static executable +# + +all: + ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test diff --git a/ld64/FireOpal/unit-tests/test-cases/static-executable/test.c b/ld64/FireOpal/unit-tests/test-cases/static-executable/test.c new file mode 100644 index 0000000..27fe88d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/static-executable/test.c @@ -0,0 +1,11 @@ + +int foo() +{ + return 0; +} + + +int entry() +{ + return foo(); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest new file mode 100644 index 0000000..323200e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a static executable (requires non-public archives) +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} test.c -static -o test-${ARCH} -L/usr/local/lib/system -lc_static -lm_static -Wl,-new_linker + ${FAIL_IF_BAD_MACHO} test-${ARCH} + ${FAIL_IF_ERROR} strip test-${ARCH} + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm -rf test-* diff --git a/ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt b/ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt new file mode 100644 index 0000000..bc535a3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a static executable (requires non-public archives) diff --git a/ld64/FireOpal/unit-tests/test-cases/static-strip/test.c b/ld64/FireOpal/unit-tests/test-cases/static-strip/test.c new file mode 100644 index 0000000..ab472fb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/static-strip/test.c @@ -0,0 +1,28 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile b/ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile new file mode 100644 index 0000000..778770e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile @@ -0,0 +1,70 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test strip: symbols referenced by indirect symbol table entries that can'tÊ +# be stripped in: +# __ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +# __ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +# __ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +# __ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +# __ZN9__gnu_cxx13new_allocatorIiED2Ev +# __ZNSt6vectorIiSaIiEEC1ERKS0_ +# __ZNSaIiEC1ERKS_ +# __ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +# __ZNSaIiED2Ev +# __ZNSt12_Vector_baseIiSaIiEED2Ev +# __ZNSaIiEC1Ev +# __ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +# __ZNSt6vectorIiSaIiEED1Ev +# __ZNSaIiED1Ev +# __ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +# __ZN9__gnu_cxx13new_allocatorIiEC2Ev +# __ZNSaIiEC2ERKS_ +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev + + +run: all + + +all: + $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_BAD_MACHO} main + ${FAIL_IF_ERROR} nm -j main >main-no-strip.nm + $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_BAD_MACHO} main + + # Make sure there are no symbols in the stripped file that aren't + # in the unstripped + nm -j main | comm -23 - main-no-strip.nm | ${FAIL_IF_STDIN} + + # Now make sure that all the __Z symbols exist + strip_cnt=`nm -j main | comm -12 - main-no-strip.nm | grep -c __Z`; \ + nostrip_cnt=`nm -j main|grep -c __Z`; \ + [ x"$$strip_cnt" = x"$$nostrip_cnt" ] + @echo PASS $$UNIT_TEST_NAME + +clean: + rm -rf *.o main-* main diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt @@ -0,0 +1,21 @@ +Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) + +__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +__ZN9__gnu_cxx13new_allocatorIiED2Ev +__ZNSt6vectorIiSaIiEEC1ERKS0_ +__ZNSaIiEC1ERKS_ +__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +__ZNSaIiED2Ev +__ZNSt12_Vector_baseIiSaIiEED2Ev +__ZNSaIiEC1Ev +__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +__ZNSt6vectorIiSaIiEED1Ev +__ZNSaIiED1Ev +__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +__ZN9__gnu_cxx13new_allocatorIiEC2Ev +__ZNSaIiEC2ERKS_ +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx b/ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest new file mode 100644 index 0000000..c1ad4c4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest @@ -0,0 +1,71 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test strip: symbols referenced by indirect symbol table entries that can'tÊ +# be stripped in: +# __ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +# __ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +# __ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +# __ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +# __ZN9__gnu_cxx13new_allocatorIiED2Ev +# __ZNSt6vectorIiSaIiEEC1ERKS0_ +# __ZNSaIiEC1ERKS_ +# __ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +# __ZNSaIiED2Ev +# __ZNSt12_Vector_baseIiSaIiEED2Ev +# __ZNSaIiEC1Ev +# __ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +# __ZNSt6vectorIiSaIiEED1Ev +# __ZNSaIiED1Ev +# __ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +# __ZN9__gnu_cxx13new_allocatorIiEC2Ev +# __ZNSaIiEC2ERKS_ +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev + + +run: all + + +all: + ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_BAD_MACHO} main + ${FAIL_IF_ERROR} nm -j main >main-no-strip.nm + ${FAIL_IF_ERROR} strip main + ${FAIL_IF_BAD_MACHO} main + ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -s -o main + ${PASS_IFF_GOOD_MACHO} main + + # Make sure there are no symbols in the stripped file that aren't + # in the unstripped + nm -j main | comm -23 - main-no-strip.nm | ${FAIL_IF_STDIN} + + # Now make sure that all the __Z symbols exist + strip_cnt=`nm -j main | comm -12 - main-no-strip.nm | grep -c __Z`; \ + nostrip_cnt=`nm -j main|grep -c __Z`; \ + [ x"$$strip_cnt" = x"$$nostrip_cnt" ] + @echo PASS $$UNIT_TEST_NAME +clean: + rm -rf *.o main main-* *.nm *.out diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt @@ -0,0 +1,21 @@ +Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) + +__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +__ZN9__gnu_cxx13new_allocatorIiED2Ev +__ZNSt6vectorIiSaIiEEC1ERKS0_ +__ZNSaIiEC1ERKS_ +__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +__ZNSaIiED2Ev +__ZNSt12_Vector_baseIiSaIiEED2Ev +__ZNSaIiEC1Ev +__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +__ZNSt6vectorIiSaIiEED1Ev +__ZNSaIiED1Ev +__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +__ZN9__gnu_cxx13new_allocatorIiEC2Ev +__ZNSaIiEC2ERKS_ +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx b/ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile b/ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile new file mode 100644 index 0000000..f32267c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# This test case checks merges two .o files. One uses +# a lazy and non-lazy pointer to access the second. +# The result then has local symbols stripped. So the +# end result is a .o file with a lazy and non-lazy pointer to +# anonymous stuff. +# +# + + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -c + ${FAIL_IF_BAD_OBJ} hello.o + + ${CC} ${CCFLAGS} foo.c -c + ${FAIL_IF_BAD_OBJ} foo.o + + ${LD} -r hello.o foo.o -o hellofoo.o + ${FAIL_IF_BAD_OBJ} hellofoo.o + + strip -x hellofoo.o -o hellofoostripped.o + ${CC} ${CCFLAGS} hellofoostripped.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.o main diff --git a/ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c b/ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c new file mode 100644 index 0000000..defa5eb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c @@ -0,0 +1,8 @@ + + +int __attribute__((visibility("hidden"))) data = 3; + +void __attribute__((visibility("hidden"))) func(int x) { } + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c b/ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c new file mode 100644 index 0000000..2deed42 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int data; +extern void func(int); + +int main() +{ + func(data); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile new file mode 100644 index 0000000..c0647b3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ifeq "${ARCH}" "i386" + POINTER_SEGMENT = __IMPORT + POINTER_SECTION = __pointers +else + POINTER_SEGMENT = __DATA + POINTER_SECTION = __nl_symbol_ptr +endif + + +# +# Test that using strip -R to selectively strip symbol names +# of of a .o file still works with ld. +# + +run: all + +all: + ${CC} ${CCFLAGS} a.c -c -o a.o + ${CC} ${CCFLAGS} b.c -c -o b.o + ${CC} ${CCFLAGS} c.c -c -o c.o + ${CC} ${CCFLAGS} func.c -c -o func.o + ${LD} -arch ${ARCH} -r a.o b.o c.o -o most.o + strip -x -R strip.list most.o -o most.stripped.o + ${CC} ${CCFLAGS} most.stripped.o func.o -dynamiclib -o dylib1 + ${LD} -arch ${ARCH} -r most.stripped.o func.o -o all.o + ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 + otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers + otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers + ${PASS_IFF} diff dylib1.pointers dylib2.pointers + +clean: + rm -rf *.o dylib1 dylib2 *.pointers diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c new file mode 100644 index 0000000..141b6d9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c @@ -0,0 +1,7 @@ + +int aData = 0; + +void a() +{ + ++aData; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c new file mode 100644 index 0000000..9608402 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c @@ -0,0 +1,12 @@ + +int bData = 0; + +void b() +{ + ++bData; +} + +void bb() +{ + ++bData; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c new file mode 100644 index 0000000..db8276a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c @@ -0,0 +1,11 @@ +extern void b(); +extern void bb(); + +extern void func(void*); + + +void c() +{ + func(&b); + func(&bb); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c new file mode 100644 index 0000000..5724811 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c @@ -0,0 +1 @@ +void func(void* x) {} diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list new file mode 100644 index 0000000..77ac6e9 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list @@ -0,0 +1,2 @@ +_b +_bb diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile new file mode 100644 index 0000000..e7cf05c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld generates correct stubs when some are weak_import +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + otool -Iv main | grep '_foo' | ${FAIL_IF_EMPTY} + otool -Iv main | grep '_bar' | ${FAIL_IF_EMPTY} + otool -Iv main | grep '_baz' | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm libfoo.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c new file mode 100644 index 0000000..0122e53 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c @@ -0,0 +1,5 @@ + +void foo() {} +void bar() {} +void baz() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c new file mode 100644 index 0000000..1803c6a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c @@ -0,0 +1,40 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void foo() __attribute__((weak_import)); +extern void bar() __attribute__((weak_import)); +extern void baz(); + +int main() +{ + if ( &foo != 0 ) { + foo(); + bar(); + } + baz(); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile b/ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile new file mode 100644 index 0000000..9e6ecc0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld generates correct stubs +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable -o libtest.dylib + # only stub should be to _test + otool -Iv libtest.dylib | grep '1 entries' | ${FAIL_IF_EMPTY} + otool -Iv libtest.dylib | grep '_test' | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + + +clean: + rm libtest.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c b/ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c new file mode 100644 index 0000000..4573622 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c @@ -0,0 +1,40 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +const char kMyStr[] = "hello"; + +int test() +{ + return 10; +} + + +const char* getstr() +{ + test(); + return kMyStr; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile new file mode 100644 index 0000000..aacd78d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -mdynamic-no-pic jump table in the middle of +# a function does not cause relocations. +# +# SPEC2000/eon built with -mdynamic-no-pic won't run +# + +run: all + +all: + # check jump table in a weak function + ${CC} ${CCFLAGS} main.c switch.s -o main + 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 + 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 + 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 + ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-pie -read_only_relocs suppress + otool -rv main | grep "External relocation" | ${FAIL_IF_STDIN} + otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -f main diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c new file mode 100644 index 0000000..f44b624 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s new file mode 100644 index 0000000..12b559f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s @@ -0,0 +1,49 @@ + + .section __TEXT,__textcoal_nt,coalesced,pure_instructions + + + +/* + Simulate a switch statement in a weak function compiled + to a jump table +*/ + .globl _foo + .weak_definition _foo +_foo: + nop + nop +#if __arm__ || __i386__ + .long L1 + .long L2 + .long L3 +#endif + nop +L1: nop +L2: nop +L3: nop + nop + + +/* + Simulate a switch statement in a regular function compiled + to a jump table +*/ + .text + .globl _bar +_bar: nop + nop + nop + nop +#if __arm__ || __i386__ + .long L5 + .long L6 + .long L7 +#endif + nop +L5: nop +L6: nop +L7: nop + nop + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile new file mode 100644 index 0000000..fc85d7b --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile @@ -0,0 +1,93 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test magic $ld$ symbols which tell ld to view exported symbols +# differently than dyld sees them. +# +# In this test case aaa and bbb both moved between libfoo and libar +# between 10.4 and 10.5. +# + + +run: all + +all: + # In this test case aaa and bbb both moved between libfoo and libar + # between 10.4 and 10.5. + ${CC} ${CCFLAGS} -dynamiclib bar.c bbb.c anotb.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c aaa.c bnota.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -o main-10.4 libfoo.dylib libbar.dylib -mmacosx-version-min=10.4 + nm -m main-10.4 | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} + nm -m main-10.4 | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.4a libbar.dylib libfoo.dylib -mmacosx-version-min=10.4 + nm -m main-10.4a | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} + nm -m main-10.4a | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5 libfoo.dylib libbar.dylib -mmacosx-version-min=10.5 + nm -m main-10.5 | grep _aaa | grep libfoo | ${FAIL_IF_EMPTY} + nm -m main-10.5 | grep _bbb | grep libbar | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5a libbar.dylib libfoo.dylib -mmacosx-version-min=10.5 + nm -m main-10.5a | grep _aaa | grep libfoo | ${FAIL_IF_EMPTY} + nm -m main-10.5a | grep _bbb | grep libbar | ${FAIL_IF_EMPTY} + # In this test case aaa and bbb both moved between subframeworks of Foo and Bar + # between 10.4 and 10.5. + mkdir -p Frameworks/Foo.framework/Frameworks/subFoo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.c aaa.c -o Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo \ + -install_name /System/Library/Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo \ + -umbrella Foo + ${CC} ${CCFLAGS} -dynamiclib bnota.c -o Frameworks/Foo.framework/Foo \ + -install_name /System/Library/Frameworks/Frameworks/Foo.framework/Foo \ + Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo + mkdir -p Frameworks/Bar.framework/Frameworks/subBar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c bbb.c -o Frameworks/Bar.framework/Frameworks/subBar.framework/subBar \ + -install_name /System/Library/Frameworks/Bar.framework/Frameworks/subBar.framework/subBar \ + -umbrella Bar + ${CC} ${CCFLAGS} -dynamiclib anotb.c -o Frameworks/Bar.framework/Bar \ + -install_name /System/Library/Frameworks/Frameworks/Bar.framework/Bar \ + Frameworks/Bar.framework/Frameworks/subBar.framework/subBar + ${CC} ${CCFLAGS} main.c -o main-10.4 -framework Foo -framework Bar -mmacosx-version-min=10.4 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.4 | grep _aaa | grep " Bar" | ${FAIL_IF_EMPTY} + nm -m main-10.4 | grep _bbb | grep " Foo" | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.4a -framework Bar -framework Foo -mmacosx-version-min=10.4 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.4a | grep _aaa | grep " Bar" | ${FAIL_IF_EMPTY} + nm -m main-10.4a | grep _bbb | grep " Foo" | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5 -framework Foo -framework Bar -mmacosx-version-min=10.5 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.5 | grep _aaa | grep " Foo" | ${FAIL_IF_EMPTY} + nm -m main-10.5 | grep _bbb | grep " Bar" | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5a -framework Bar -framework Foo -mmacosx-version-min=10.5 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.5a | grep _aaa | grep " Foo" | ${FAIL_IF_EMPTY} + nm -m main-10.5a | grep _bbb | grep " Bar" | ${FAIL_IF_EMPTY} + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c new file mode 100644 index 0000000..71123eb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c @@ -0,0 +1,3 @@ + +void aaa() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c new file mode 100644 index 0000000..60fcf64 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c @@ -0,0 +1,26 @@ + + +#define SYMBOL_IS_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_IS_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.5$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.5$_" #sym ); const char sym##_tmp = 0; + + +// 10.4 10.5 +// aaa libbar libfoo +// bbb libfoo libbar +// + +// bbb is new here in 10.5. It was elsewhere in 10.4 +SYMBOL_NOT_HERE_IN_10_4(bbb) + +// aaa was here in 10.4 and move elsewhere +SYMBOL_IS_HERE_IN_10_4(aaa) + diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bar.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c new file mode 100644 index 0000000..b6e9cc0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c @@ -0,0 +1 @@ +void bbb() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c new file mode 100644 index 0000000..d29b878 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c @@ -0,0 +1,25 @@ +#define SYMBOL_IS_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_IS_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.5$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.5$_" #sym ); const char sym##_tmp = 0; + + +// 10.4 10.5 +// aaa libbar libfoo +// bbb libfoo libbar +// + + +// bbb was here in 10.4 and move elsewhere +SYMBOL_IS_HERE_IN_10_4(bbb) + +// aaa is new here in 10.5. It was elsewhere in 10.4 +SYMBOL_NOT_HERE_IN_10_4(aaa) + diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/foo.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c new file mode 100644 index 0000000..902e908 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c @@ -0,0 +1,17 @@ + +extern void foo(); +extern void bar(); + +extern void aaa(); +extern void bbb(); + + +int main() +{ + foo(); + bar(); + aaa(); + bbb(); + + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile new file mode 100644 index 0000000..e8a4a0e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Test how tentative definitions interact with archives +# main.c has a tenative definition for _var which +# should *not* cause libfoo.a(foo.o) to be loaded. +# +# ld crashes building XsanFS +# -undefined dynamic_lookup causes spurious extra symbols +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${CC} ${CCFLAGS} main.c libfoo.a -o main + ${CC} ${CCFLAGS} main.c libfoo.a -o main -undefined dynamic_lookup + nm -m main | grep "looked up" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main libfoo.a foo.o diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c new file mode 100644 index 0000000..a2254b5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c @@ -0,0 +1,6 @@ + +extern void bar(); + +void foo() { bar(); } + +int var = 9; diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c new file mode 100644 index 0000000..e5046a0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c @@ -0,0 +1,8 @@ + +int var; + +int main() +{ + var = 3; + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile new file mode 100644 index 0000000..0ed1fa4 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Test how tentative definitions interact with dylibs +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-reexport_library,libbar.dylib + # verify -warn_commons works + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-warn_commons 2> warnings.log + grep "using common symbol" warnings.log | ${FAIL_IF_EMPTY} + # verify -commons use_dylibs works + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,use_dylibs + nm -m main | grep _var | grep libfoo | ${FAIL_IF_EMPTY} + # verify -commons ignore_dylibs works + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,ignore_dylibs + nm -m main | grep _var | grep __DATA | ${FAIL_IF_EMPTY} + # verify -commons error works + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,error 2> warnings.log + # verify -commons use_dylibs works with indirect dylibs + ${CC} ${CCFLAGS} main.c -Dvar=bar libfoo.dylib -o main -Wl,-commons,use_dylibs + nm -m main | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main libfoo.dylib libbar.dylib warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c new file mode 100644 index 0000000..c1bb919 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c @@ -0,0 +1,2 @@ +void foo() {} +int var = 9; diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c new file mode 100644 index 0000000..e5046a0 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c @@ -0,0 +1,8 @@ + +int var; + +int main() +{ + var = 3; + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile new file mode 100644 index 0000000..833d676 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify that -r -d -exported_symbol_list uses proper relocations for hidden +# newly defined (no longer tentative) definitions. +# + +ifneq (${ARCH},x86_64) + BETTER_NOT_FIND = _tent +else + # x86_64 uses a different style of relocations, so external relocs are ok to have + BETTER_NOT_FIND = blahblah +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${FAIL_IF_BAD_OBJ} test.o + + ${LD} -arch ${ARCH} -d -r test.o -exported_symbol _tent1 -o test-r.o + otool -rv test-r.o | grep ${BETTER_NOT_FIND} | ${PASS_IFF_EMPTY} + + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c new file mode 100644 index 0000000..c9cf479 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c @@ -0,0 +1,11 @@ + +// tentative definitions +int tent1; +int tent2; +int __attribute__((visibility("hidden"))) tent3; + +// initialized to point to tentative definitions +int* pa = &tent1; +int* pb = &tent2; +int* pc = &tent3; + diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile new file mode 100644 index 0000000..092aa2e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify that -r -d +# will transform a tentative definition into a real one. +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${FAIL_IF_BAD_OBJ} test.${ARCH}.o + + ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r.${ARCH}.o | grep tentative | ${FAIL_IF_EMPTY} + + ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r-d.${ARCH}.o | grep tentative | ${PASS_IFF_EMPTY} + +clean: + rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt new file mode 100644 index 0000000..de80bea --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that -r -d will transform a tentative definition into a real one. diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c new file mode 100644 index 0000000..87360fc --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c @@ -0,0 +1,3 @@ + +// a tentative definition +int a; diff --git a/ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile b/ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile new file mode 100644 index 0000000..65b3131 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify the linker parses call sites correctly. +# The tricky case is thumb, which uses a blx to call to +# the arm stubs. This test verifies that there is no +# +2 error by checking for "plus" and that when the file +# is regenerated through ld -r that the dumped output +# remains unchanged. +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.o > test.o.dump + # verify no +2 errors + grep "plus" test.o.dump | ${FAIL_IF_STDIN} + # verify .o file can be regenerated to an equivalent state + ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.o > test-r.o.dump + # verify final linked image has no +2 errors + ${CC} ${CCFLAGS} test.o -o test + otool -tV -p _main test | grep blx | grep -v _malloc | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} test-r.o -o test-r + otool -tV -p _main test-r | grep blx | grep -v _malloc | ${FAIL_IF_STDIN} + ${PASS_IFF} diff test.o.dump test-r.o.dump + +clean: + rm -rf test.o test-r.o test.o.dump test-r.o.dump test test-r diff --git a/ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c b/ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c new file mode 100644 index 0000000..ce0359f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +int main() +{ + malloc(1); + malloc(2); + malloc(3); + malloc(4); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile new file mode 100644 index 0000000..eebcc37 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -U and -undefined dynamic_lookup work +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup + nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} main.c -o main -Wl,-U,_foo + nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} main.c -o main -flat_namespace -Wl,-U,_foo + nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm main diff --git a/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c b/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c new file mode 100644 index 0000000..5de972f --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile new file mode 100644 index 0000000..789a304 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a late loaded hidden symbol from an archive does not conflict +# with a symbol previously found in a dylib. +# gcc 4.2: DejaGnu failures due to libgcc visibility issues with -m64 -mmacosx-version-min=10.4 (G5) +# + +SHELL = bash # use bash shell so we can redirect just stderr + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -c bar.c -o bar.o + libtool -static bar.o -o libbar.a + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib libbar.a 2>warning.log + cat warning.log | ${PASS_IFF_EMPTY} + + +clean: + rm -f libfoo.dylib bar.o libbar.a main warning.log diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c new file mode 100644 index 0000000..276f502 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c @@ -0,0 +1,11 @@ + + +void __attribute__((weak,visibility("hidden"))) foo() +{ + +} + + +void bar() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c new file mode 100644 index 0000000..3d8616a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c @@ -0,0 +1,5 @@ + + +void __attribute__((weak)) foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c new file mode 100644 index 0000000..8c9c95c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); + + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile new file mode 100644 index 0000000..43bdcd8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that two weak symbols with different visibility causes a warning +# and a weak and strong with different visibilities do not cause a warning. +# Spurious link warnings for inline members of C++ template classes +# + +SHELL = bash # use bash shell so we can redirect just stderr + + +run: all + +all: + ${CC} ${CCFLAGS} -c foo_weak_hidden.c -o foo_weak_hidden.o + ${CC} ${CCFLAGS} -c foo_weak.c -o foo_weak.o + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${CC} ${CCFLAGS} -c foo_hidden.c -o foo_hidden.o + # weak default and weak hidden should warn + ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib 2> warnings.log + grep visibility warnings.log | ${FAIL_IF_EMPTY} + # weak hidden and strong should not warn + ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib 2> warnings.log + grep visibility warnings.log | ${FAIL_IF_STDIN} + # weak default and strong hidden should not warn + ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib 2> warnings.log + grep visibility warnings.log | ${FAIL_IF_STDIN} + # weak default and weak hidden but -w should not warn + ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -w -o libfoo.dylib 2> warnings.log + cat warnings.log | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib foo_weak_hidden.o foo_weak.o foo.o foo_hidden.o warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c new file mode 100644 index 0000000..1624757 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c new file mode 100644 index 0000000..cac53ce --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c @@ -0,0 +1,5 @@ + + +void __attribute__((visibility("hidden"))) foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c new file mode 100644 index 0000000..3d8616a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c @@ -0,0 +1,5 @@ + + +void __attribute__((weak)) foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c new file mode 100644 index 0000000..8d461e6 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c @@ -0,0 +1,5 @@ + + +void __attribute__((weak, visibility("hidden"))) foo() +{ +} diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile new file mode 100644 index 0000000..222a82d --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# libfoo.dylib has weak defintiion of _foo +# libbar.dylib has strong defintiion of _foo +# +# Tests that if you link against libfoo.dylib and libbar.dylib +# that the two-level-namespace ordinal is set to the non-weak definition +# +# ld should keep looking when it finds a weak definition in a dylib# +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib libbar.dylib + nm -m main | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} + nm -m main | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm libfoo.dylib libbar.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c new file mode 100644 index 0000000..ae61731 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c @@ -0,0 +1,6 @@ + +int aaa() +{ + return 1; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c new file mode 100644 index 0000000..d371654 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c @@ -0,0 +1,11 @@ + +int __attribute__((weak)) aaa() +{ + return 0; +} + +int __attribute__((weak)) bbb() +{ + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c new file mode 100644 index 0000000..74726fb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c @@ -0,0 +1,35 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void aaa(); +extern void bbb(); + +int main() +{ + aaa(); + bbb(); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile new file mode 100644 index 0000000..0a00a39 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 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 + otool -lv main | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c new file mode 100644 index 0000000..261a806 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c @@ -0,0 +1,9 @@ + + +#include "bar.h" + +void bar1() {} +void bar2() {} +void bar3() {} +void bar4() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h new file mode 100644 index 0000000..7ea2ef3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h @@ -0,0 +1,9 @@ + + +extern void bar1(); +extern void bar2() __attribute__((weak_import)); +extern void bar3(); +extern void bar4() __attribute__((weak_import)); + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c new file mode 100644 index 0000000..aa25da5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c @@ -0,0 +1,9 @@ + + +#include "foo.h" + +void foo1() {} +void foo2() {} +void foo3() {} +void foo4() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h new file mode 100644 index 0000000..54b3e36 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h @@ -0,0 +1,6 @@ + + +extern void foo1(); +extern void foo2() __attribute__((weak_import)); +extern void foo3(); +extern void foo4() __attribute__((weak_import)); diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c new file mode 100644 index 0000000..cb61aeb --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c @@ -0,0 +1,22 @@ + +#include "foo.h" +#include "bar.h" + +void* p; + +int main (void) +{ + // non-lazy reference to foo2 + p = &foo2; + // lazy reference to foo4 + foo4(); + + // non-lazy reference to bar2 + p = &bar2; + // lazy reference to bar4 and bar1 + bar4(); + bar1(); + + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile new file mode 100644 index 0000000..d1fa1f3 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile @@ -0,0 +1,62 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib + + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null + otool -rv main-${ARCH} | grep _data6 > /dev/null + ${FAIL_IF_BAD_MACHO} main-${ARCH} + + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null + otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null + ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib + +clean: + rm -rf *.dylib main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_import/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest new file mode 100644 index 0000000..5e51b89 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest @@ -0,0 +1,58 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null + ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib + +clean: + rm -rf *.dylib main-* *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt new file mode 100644 index 0000000..5be42d8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt @@ -0,0 +1 @@ +Test the weak_import attribute works diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c new file mode 100644 index 0000000..4580a87 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c @@ -0,0 +1,10 @@ + + +void func2() {} +void func4() {} + + +int data2 = 0; // foo.c also has weak_import initialized +int data4; // foo.c also has weak_import uninitialized +int data6 = 0; // foo.c also has weak_import + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile new file mode 100644 index 0000000..98a2779 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + + ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo1-${ARCH}.o + + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib 2>/dev/null + +clean: + rm -rf *.o *.dylib main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt new file mode 100644 index 0000000..5be42d8 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt @@ -0,0 +1 @@ +Test the weak_import attribute works diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c new file mode 100644 index 0000000..392a5b7 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c @@ -0,0 +1,4 @@ +#include "foo.h" + +int data4; // foo.c also has weak_import uninitialized + diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/Makefile b/ld64/FireOpal/unit-tests/test-cases/why_live/Makefile new file mode 100644 index 0000000..9c4811e --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/why_live/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Text -why_live option with dead code stripping +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c bar.c -o main -dead_strip -Wl,-why_live,_bar 2>call_chains + grep _bar call_chains | ${FAIL_IF_EMPTY} + grep _foo call_chains | ${FAIL_IF_EMPTY} + grep _main call_chains | ${FAIL_IF_EMPTY} + grep _frob call_chains | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main call_chains diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/bar.c b/ld64/FireOpal/unit-tests/test-cases/why_live/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/why_live/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/foo.c b/ld64/FireOpal/unit-tests/test-cases/why_live/foo.c new file mode 100644 index 0000000..9a2edf5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/why_live/foo.c @@ -0,0 +1,12 @@ + +extern void bar(); + +void foo() +{ + bar(); +} + +void frob() +{ + bar(); +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/main.c b/ld64/FireOpal/unit-tests/test-cases/why_live/main.c new file mode 100644 index 0000000..a5a79d5 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/why_live/main.c @@ -0,0 +1,7 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile b/ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile new file mode 100644 index 0000000..d6c0639 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -o test-${ARCH} + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm -rf test-* diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c b/ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c new file mode 100644 index 0000000..cfdc08c --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c @@ -0,0 +1,51 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data +int bigarray1[256]; +int bigarray2[256]; +int bigarray3[256]; +int bigarray4[256]; +int bigarray5[256]; +int bigarray6[256]; +static int staticbigarray1[256]; +static int staticbigarray2[256]; +static int staticbigarray3[256]; +static int staticbigarray4[256]; +static int staticbigarray5[256]; +static int staticbigarray6[256]; + +int main() +{ + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile new file mode 100644 index 0000000..b011e70 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -o test + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c new file mode 100644 index 0000000..219cdc2 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c @@ -0,0 +1,58 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data + +#if __LP64__ + #define BOOST 100UL +#else + #define BOOST 1 +#endif + +int bigarray1[256]; +int bigarray2[2560]; +int bigarray3[25600]; +int bigarray4[256000]; +int bigarray5[2560000]; +int bigarray6[256000000*BOOST]; +static int staticbigarray1[256]; +static int staticbigarray2[2560]; +static int staticbigarray3[25600]; +static int staticbigarray4[256000]; +static int staticbigarray5[2560000]; +static int staticbigarray6[25600000*BOOST]; + +int main() +{ + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile new file mode 100644 index 0000000..6266019 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: test-run-${ARCH} + +# i386 catches the problem in the assembler phase +test-run-i386: + ${PASS_IFF} true + +test-run-ppc test-run-ppc64: test-${ARCH} + +test-run-x86_64 test-ppc64: + ${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: + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null + +clean: + rm -rf test-* *.o *.s *.i diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c new file mode 100644 index 0000000..64ac72a --- /dev/null +++ b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c @@ -0,0 +1,63 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data +int bigarray1[256]; +int bigarray2[2560]; +int bigarray3[25600]; +int bigarray4[256000]; +int bigarray5[2560000]; +int bigarray7[16777216L+1]; +int bigarray8[2*16777216L+1]; +int bigarray9[4*16777216L+1]; +int bigarray10[8*16777216L+1]; +int bigarray11[16*16777216L+1]; +int bigarray99[2147483647U/SHRINK]; +static int staticbigarray1[256]; +static int staticbigarray2[2560]; +static int staticbigarray3[25600]; +static int staticbigarray4[256000]; +static int staticbigarray5[2560000]; +static int staticbigarray6[25600000]; +//static int staticbigarray99[2147483647U/SHRINK]; + +int main() +{ + bigarray5[10] = 4; + bigarray7[10] = 4; + bigarray8[10] = 4; + bigarray9[10] = 4; + bigarray10[10] = 4; + bigarray11[10] = 4; + bigarray99[10] = 4; + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 new file mode 100644 index 0000000..a8b0188 --- /dev/null +++ b/ld64/doc/man/man1/ld.1 @@ -0,0 +1,676 @@ +.Dd December 8, 2006 +.Dt ld 1 +.Os Darwin +.Sh NAME +.Nm ld +.Nd "linker" +.Sh SYNOPSIS +.Nm +files... +.Op options +.Op Fl o Ar outputfile +.Sh DESCRIPTION +The +.Nm ld +command combines several object files and libraries, resolves references, and +produces an ouput file. +.Nm ld +can produce a final linked image (executable, dylib, or bundle), or with the -r +option, produce another object file. If the -o option is not used, the output +file produced is named "a.out". +.Ss Universal +The linker accepts universal (multiple-architecture) input files, but +always creates a "thin" (single-architecture), standard Mach-O output file. +The architecture for the output file is specified using the -arch option. +If this option is not used, +.Nm ld +attempts to determine the output architecture by examining the object +files in command line order. The first "thin" +architecture determines that of the output file. If no input +object file is a "thin" file, the native 32-bit architecture for the host is used. +.Pp +Usually, +.Nm ld +is not used directly. Instead the +.Xr gcc(1) +compiler driver invokes +.Nm ld. +The compiler driver can be passed multiple -arch options and it will create a +universal final linked image by invoking +.Nm ld +multiple times and then running +.Xr lipo(1) +merge the outputs into a universal file. +.Ss Layout +The object files are loaded in the order in which they are specified on the +command line. The segments and the sections in those segments will appear in +the output file in the order they are encountered in the object files being linked. +All zero fill sections will appear after all non-zero fill sections in their segments. +Sections created from files with the -sectcreate option will be laid out at after +sections from .o files. The use of the -order_file option will alter the layout +rules above, and move the symbols specified to start of their section. +.Ss Libraries +A static library (aka static archive) is a collection of .o files with a table of contents +that lists the global symbols in the .o files. +.Nm ld +will only pull .o files out of a static library if needed to resolve some symbol reference. +Unlike traditional linkers, +.Nm ld +will continually search a static library while linking. There is no need to specify a static +library multiple times on the command line. +.Pp +A dynamic library (aka dylib or framework) is a final linked image. Putting a dynamic +library on the command line causes two things: 1) The generated final linked image +will have encoded that it depends on that dynamic library. 2) Exported symbols from the +dynamic library are used to resolve references. +.Pp +Both dynamic and static libraries are searched as they appear on the command line. +.Ss Search paths +.Nm ld +maintains a list of directories to search for a library or framework to use. The default +library search path is /usr/lib then /usr/local/lib. The -L option will add a new library search +path. The default framework search path is /Library/Frameworks then /System/Library/Frameworks. +(Note: previously, /Network/Library/Frameworks was at the end of the default path. If you need +that functionality, you need to explicitly add -F/Network/Library/Frameworks). +The -F option will a new framework search path. The -Z option will remove +the standard search paths. The -syslibroot option will prepend a prefix to all search +paths. +.Ss Two-level namespace +By default all references resolved to a dynamic library record the library to which +they were resolved. At runtime, dyld uses that information to directly resolve +symobls. The alternative is to use the -flat_namespace option. With flat namespace, +the library is not recorded. At runtime, dyld will search each dynamic library in load +order when resolving symbols. This is slower, but more like how other operating systems +resolve symbols. +.Ss Indirect dynamic libraries +If the command line specifies to link against dylib A, and when dylib A was built it linked +against dylib B, then B is considered an indirect dylib. +When linking for two-level namespace, ld does not look at indirect dylibs, except when +re-exported by a direct dylibs. On the other hand when linking for flat namespace, +ld does load all indirect dylibs and uses them to resolve references. +Even though indirect dylibs are specified via a full path, +.Nm ld +first uses the specified search paths to locate each indirect dylib. If one cannot +be found using the search paths, the full path is used. +.Ss Dynamic libraries undefines +When linking for two-level namespace, +.Nm ld +does not verify that undefines in dylibs actually +exist. But when linking for flat namespace, +.Nm ld +does check that all undefines from all loaded dylibs have a matching definition. +This is sometimes used to force selected functions to be loaded from a static library. +.Sh OPTIONS +.Ss Options that control the kind of output +.Bl -tag +.It Fl execute +The default. Produce a mach-o main executable that has file type MH_EXECUTE. +.It Fl dylib +Produce a mach-o shared library that has file type MH_DYLIB. +.It Fl bundle +Produce a mach-o bundle that has file type MH_BUNDLE. +.It Fl r +Merges object files to produce another mach-o object file with file type MH_OBJECT. +.It Fl dylinker +Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when building dyld. +.It Fl dynamic +The default. Implied by -dynamiclib, -bundle, or -execute +.It Fl static +Produces a mach-o file that does not use the dyld. Only used building the kernel. +.It Fl arch Ar arch_name +Specifies which architecture (e.g. ppc, ppc64, i386, x86_64) the output file should be. +.It Fl o Ar path +Specifies the name and location of the output file. If not specified, `a.out' is used. +.El +.Ss Options that control libraries +.Bl -tag +.It Fl l Ns x +This option tells the linker to search for libx.dylib or libx.a in the library search path. +If string x is of the form y.o, then that file is searched for in the same places, but without +prepending `lib' or appending `.a' or `.dylib' to the filename. +.It Fl weak-l Ns Ar x +This is the same as the -lx but forces the library and all references to it to be marked as weak imports. +That is, the library is allowed to be missing at runtime. +.It Fl weak_library Ar path_to_library +This is the same as listing a file name path to a library on the link line except that it forces the +library and all references to it to be marked as weak imports. +.It Fl reexport-l Ns Ar x +This is the same as the -lx but specifies that the all symbols in library x should be available to +clients linking to the library being created. This was previously done with a separate -sub_library option. +.It Fl reexport_library Ar path_to_library +This is the same as listing a file name path to a library on the link line and it specifies that the +all symbols in library path should be available to clients linking to the library being created. +This was previously done with a separate -sub_library option. +.It Fl lazy-l Ns Ar x +This is the same as the -lx but it is only for shared libraries and the linker +will construct glue code so that the shared library is not loaded until +the first function in it is called. +.It Fl lazy_library Ar path_to_library +This is the same as listing a file name path to a shared library on the link line +except that the linker will construct glue code so that the shared library is not +loaded until the first function in it is called. +.It Fl L Ns dir +Add +.Ar dir +to the list of directories in which to search for libraries. +Directories specified with -L are searched in the order they appear on the command line +and before the default search path. +.It Fl Z +Do not search the standard directories when searching for libraries and frameworks. +.It Fl syslibroot Ar rootdir +Prepend +.Ar rootdir +to all search paths when searching for libraries or frameworks. +.It Fl search_paths_first +By default the -lx and -weak-lx options first search for a file of the form `libx.dylib' in each directory +in the library search path, then a file of the form `libx.a' is searched for in the library search paths. +This option changes it so that in each path `libx.dylib' is searched for then `libx.a' before the +next path in the library search path is searched. +.It Fl framework Ar name[,suffix] +This option tells the linker to search for `name.framework/name' the framework search path. +If the optional suffix is specified the framework is first searched for the name with the suffix and then without +(e.g. look for `name.framework/name_suffix' first, if not there try `name.framework/name'). +.It Fl weak_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] but forces the framework and all +references to it to be marked as weak imports. +.It Fl reexport_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] but also specifies that the +all symbols in that framework should be available to clients linking to the library being created. +This was previously done with a separate -sub_umbrella option. +.It Fl lazy_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] except that the linker will +construct glue code so that the framework is not +loaded until the first function in it is called. You cannot directly access +data or Objective-C classes in a frameworked linked this way. +.It Fl F Ns dir +Add +.Ar dir +to the list of directories in which to search for frameworks. +Directories specified with -F are searched in the order they appear on the command line +and before the default search path. +.It Fl all_load +Loads all members of static archive libraries. +.It Fl ObjC +Loads all members of static archive libraries that implement an Objective-C class or category. +.El +.Ss Options that control additional content +.Bl -tag +.It Fl sectcreate Ar segname sectname file +The section +.Ar sectname +in the segment +.Ar segname +is created from the contents of file +.Ar file. +The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname) +from any other input. +.It Fl filelist Ar file[,dirname] +Specifies that the linker should link the files listed in +.Ar file . +This is an alternative to listing the files on the command line. +The file names are listed one per line separated only by newlines. (Spaces and tabs are assumed to be part of the file name.) +If the optional directory name, +.Ar dirname +is specified, it is prepended to each name in the list file. +.It Fl dtrace Ar file +Enables dtrace static probes when producing a final linked image. The file +.Ar file +must be a DTrace script which declares the static probes. +.El +.Ss Options that control optimizations +.Bl -tag +.It Fl dead_strip +Remove functions and data that are unreachable by the entry point or exported symbols. +.It Fl dead_strip_dylibs +Remove dylibs that are unreachable by the entry point or exported symbols. That is, +suppresses the generation of load command commands for dylibs which supplied no +symbols during the link. This option should not be used when linking against a dylib which +is required at runtime for some indirect reason such as the dylib has an important initializer. +.It Fl order_file Ar file +Alters the order in which functions and data are laid out. For each section in the output file, +any symbol in that section that are specified in the order file +.Ar file +is moved to the start of its section and laid out in the same order as in the order file +.Ar file . +Order files are text files with one symbol name per line. Lines starting with a # are comments. +A symbol name may be optionally preceded with its object file leafname and a colon (e.g. foo.o:_foo). +This is useful for static functions/data that occur in multiple files. +A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). +This enables you to have one order file that works for multiple architectures. +Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file. +.It Fl macosx_version_min Ar version +This is set to indicate the oldest Mac OS X version that that the output is to be used on. Specifying +a later version enables the linker to assumes features of that OS in the output file. The format of +.Ar version +is a Mac OS X version number such as 10.4 or 10.5 +.It Fl image_base Ar address +Specifies the perferred load address for a dylib or bundle. The argument +.Ar address +is a hexadecimal number with an optional leading 0x. By choosing non-overlapping address for all +dylibs and bundles that a program loads, launch time can be improved because dyld will not need to +"rebase" the image (that is, adjust pointers within the image to work at the loaded address). +It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs. +It will then choose non-overlapping addresses for the list and rebase them all. +This option is also called -seg1addr for compatibility. +.It Fl no_implicit_dylibs +When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs +that are implicitly linked to make the two-level namespace +encoding more efficient for dyld. For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. +If you link with -framework Cocoa and use a symbol from Foundation, the linker will implicitly add a load +command to load Foundation and encode the symbol as coming from Foundation. If you use this option, +the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then +at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation. +.El +.Ss Options when creating a dynamic library (dylib) +.Bl -tag +.It Fl install_name Ar name +Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library +will record that path as the way dyld should locate this library. If this option is not specified, then +the -o path will be used. This option is also called -dylib_install_name for compatibility. +.It Fl compatibility_version Ar number +Specifies the compatibility version number of the library. When a library is loaded by dyld, the +compatibility version is checked and if the program's version is greater that the library's version, it is an error. +The format of +.Ar number +is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, +and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. +If the compatibility version number is not specified, it has a value of 0 and no checking is done when the library is used. +This option is also called -dylib_compatibility_version for compatibility. +.It Fl current_version Ar number +Specifies the current version number of the library. The current version of the library can be obtained +programmatically by the user of the library so it can determine exactly which version of the library it is using. +The format of +.Ar number +is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, +and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. +If the version number is not specified, it has a value of 0. +This option is also called -dylib_current_version for compatibility. +.El +.Ss Options when creating a main executable +.Bl -tag +.It Fl pie +This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5, the OS +will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled +with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some +security. +.It Fl pagezero_size Ar size +By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence +will cause a bus error if a NULL pointer is dereferenced. The argument +.Ar size +is a hexadecimal number with an optional leading 0x. If +.Ar size +is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size +is 4KB. On 64-bit architectures, the default size if 4GB. The ppc64 architecture has some special cases. Since Mac +OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless +-macosx_version_min is 10.5 or later. Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the +code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero +size is set to 4KB and then a new unredable trailing segment is created after the code, filling up the lower 4GB. +.It Fl stack_size Ar size +Specifies the maximum stack size for the main thread in a program. Without this option a program has a 8MB stack. +The argument +.Ar size +is a hexadecimal number with an optional leading 0x. The +.Ar size +should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. +.It Fl allow_stack_execute +Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. +.El +.Ss Options when creating a bundle +.Bl -tag +.It Fl bundle_loader Ar executable +This specifies the +.Ar executable +that will be loading the bundle output file being linked. +Undefined symbols from the bundle are checked against the specified +.Ar executable +like it was one of the +dynamic libraries the bundle was linked with. +.El +.Ss Options when creating an object file +.Bl -tag +.It Fl keep_private_externs +Don't turn private external (aka visibility=hidden) symbols into static symbols, +but rather leave them as private external in the resulting object file. +.It Fl d +Force definition of common symbols. That is, transform tentative defintions into real definitions. +.El +.Ss Options that control symbol resolution +.Bl -tag +.It Fl exported_symbols_list Ar filename +The specified +.Ar filename +contains a list of global symbol names that will remain as global symbols in the output file. +All other global symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) +and will not be global in the output file. The symbol names listed in filename must be one per line. +Leading and trailing white space are not part of the symbol name. +Lines starting with # are ignored, as are lines with only white space. +Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. +The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches +any single lower case letter from 'a' to 'z'. +.It Fl exported_symbol Ar symbol +The specified +.Ar symbol +is added to the list of global symbols names that will remain as global symbols in the output file. This +option can be used multiple times. For short lists, this can be more convenient than creating a file and using +-exported_symbols_list. +.It Fl unexported_symbols_list Ar file +The specified +.Ar filename +contains a list of global symbol names that will not remain as global symbols in the output file. +The symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) and will not be global +in the output file. The symbol names listed in filename must be one per line. +Leading and trailing white space are not part of the symbol name. +Lines starting with # are ignored, as are lines with only white space. +Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. +The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches +any single lower case letter from 'a' to 'z'. +.It Fl unexported_symbol Ar symbol +The specified +.Ar symbol +is added to the list of global symbols names that will not remain as global symbols in the output file. This +option can be used multiple times. For short lists, this can be more convenient than creating a file and using +-unexported_symbols_list. +.It Fl alias Ar symbol_name Ar alternate_symbol_name +Create an alias named +.Ar alternate_symbol_name +for the symbol +.Ar symbol_name . +By default the alias symbol has global visibility. This option was previous the -idef:indir option. +.It Fl alias_list Ar filename +The specified +.Ar filename +contains a list of aliases. The symbol name and its alias are on one line, separated by whitespace. +Lines starting with # are ignored. +.It Fl flat_namespace +Alters how symbols are resolved at build time and runtime. With -two_levelnamespace (the default), the linker +only searches dylibs on the command line for symbols, and records in which dylib they were found. With -flat_namespace, +the linker searches all dylibs on the command line and all dylibs those original dylibs depend on. The linker +does not record which dylib an external symbol came from, so at runtime dyld again searches all images and uses +the first definition it finds. In addition, any undefines in loaded flat_namespace dylibs must be resolvable +at build time. +.It Fl u Ar symbol_name +Specified that symbol +.Ar symbol_name +must be defined for the link to succeed. This is useful to force selected functions to be loaded +from a static library. +.It Fl U Ar symbol_name +Specified that it is ok for +.Ar symbol_name +to have no definition. With -two_levelnamespace, the resulting symbol will be marked dynamic_lookup which +means dyld will search all loaded images. +.It Fl undefined Ar treatment +Specifies how undefined symbols are to be treated. Options are: error, warning, suppress, or dynamic_lookup. The +default is error. +.It Fl rpath Ar path +Add +.Ar path +to the runpath search path list for image being created. At runtime, dyld uses the runpath when searching +for dylibs whose load path begins with @rpath/. +.It Fl commons Ar treatment +Specifies how commons (aka tentative definitions) are resolved with respect to dylibs. Options are: +ignore_dylibs, use_dylibs, error. The default is ignore_dylibs which means the linker will turn a tentative +definition in an object file into a real definition and not even check dylibs for conflicts. The dylibs +option means the linker should check linked dylibs for definitions and use them to replace tentative definitions +from object files. The error option means the linker should issu an error whenever a tentative definition in an +object file conflicts with an external symbol in a linked dylib. See also -warn_commons. +.El +.Ss Options for introspecting the linker +.Bl -tag +.It Fl why_load +Log why each object file in a static library is loaded. That is, what symbol was needed. Also called -whyload +for compatibility. +.It Fl why_live Ar symbol_name +Logs a chain of references to +.Ar symbol_name . +Only applicable with -dead_strip . +It can help debug why something that you think should be dead strip removed is not removed. +.It Fl print_statistics +Logs information about the amount of memory and time the linker used. +.It Fl t +Logs each file (object, archive, or dylib) the linker loads. Useful for debugging problems with search paths where the wrong library is loaded. +.It Fl whatsloaded +Logs just object files the linker loads. +.It Fl order_file_statistics +Logs information about the processing of a -order_file. +.It Fl map Ar map_file_path +Writes a map file to the specified path which details all symbols and their addresses in the output image. +.El +.Ss Options for controling symbol table optimizations +.Bl -tag +.It Fl S +Do not put debug information (STABS or DWARF) in the output file. +.It Fl x +Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and +getting symbol names in back traces, but are not used at runtime. If -x is used with -r +non-global symbol names are not removed, but instead replaced with a unique, duumy name +that will be automatically removed when linked into a final linked image. This +allows dead code stripping, which uses symbols to break up code and data, to +work properly and provides the security of having source symbol names removed. +.It Fl non_global_symbols_strip_list Ar filename +The specified +.Ar filename +contains a list of non-global symbol names that should be removed from the output file's symbol table. All other +non-global symbol names will remain in the output files symbol table. See -exported_symbols_list for syntax and use +of wildcards. +.It Fl non_global_symbols_no_strip_list Ar filename +The specified +.Ar filename +contains a list of non-global symbol names that should be remain in the output file's symbol table. All other +symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use +of wildcards. +.El +.Ss Rarely used Options +.Bl -tag +.It Fl v +Prints the version of the linker. +.It Fl no_uuid +Do not generate an LC_UUID load command in the output file. +.It Fl root_safe +Sets the MH_ROOT_SAFE bit in the mach header of the output file. +.It Fl setuid_safe +Sets the MH_SETUID_SAFE bit in the mach header of the output file. +.It Fl interposable +Indirects access to all to exported symbols when creating a dynamic library. +.It Fl init Ar symbol_name +The specified symbol_name will be run as the first initializer. Only used when creating a dynamic library. +.It Fl sub_library Ar library_name +The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc. +Only used when creating a dynamic library. +.It Fl sub_umbrella Ar framework_name +The specified framework will be re-exported. Only used when creating a dynamic library. +.It Fl allowable_client Ar name +Restricts what can link against the dynamic library being created. +.It Fl client_name Ar name +Enables a bundle to link against a dylib that was built with -allowable_client. +The name specified must match one of the -allowable_client names specified when the dylib was created. +.It Fl umbrella Ar framework_name +Specifies that the dylib being linked is re-exported through an umbrella framework of the specified name. +.It Fl headerpad Ar size +Specifies the minimum space for future expansion of the load commands. Only useful if intend to run +install_name_tool to alter the load commands later. Size is a hexadecimal number. +.It Fl headerpad_max_install_names +Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. +Only useful if intend to run install_name_tool to alter the load commands later. Size is a hexadecimal number. +.It Fl bind_at_load +Sets a bit in the mach header of the resulting binary which tells dyld to bind all symbols when the binary is loaded, rather than lazily. +.It Fl force_flat_namespace +Sets a bit in the mach header of the resulting binary which tells dyld to not only use flat namespace for the binary, +but force flat namespace binding on all dylibs and bundles loaded in the process. Can only be used when linking main executables. +.It Fl sectalign Ar segname Ar sectname Ar value +The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal +number that must be an integral power of 2. +.It Fl stack_addr Ar address +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to a page boundary. +.It Fl segprot Ar segname Ar max_prot Ar init_prot +Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively. +The values for max and init are any combination of the characters `r' (for read), `w' (for write), `x' (for execute) and `-' (no access). +.It Fl seg_addr_table Ar filename +Specifies a file containing base addresses for dynamic libraries. Each line of the file is a hexadecimal base address +followed by whitespace then the install name of the corresponding dylib. The # character denotes a comment. +.It Fl segs_read_write_addr Ar address +Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address +specified is a hexadecimal number that indicates the base address for the read-write segments. +.It Fl segs_read_only_addr Ar address +Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address +specified is a hexadecimal number that indicates the base address for the read-only segments. +.It Fl segaddr Ar name Ar address +Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number +that is a multiple of 4K page size. +.It Fl dylib_file Ar install_name:file_name +Specifies that a dynamic shared library is in a different location than its standard location. Use this option +when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other +than its default location. install_name specifies the path where the library normally resides. file_name specifies +the path of the library you want to use instead. For example, if you link to a library that depends upon the dynamic +library libsys and you have libsys installed in a nondefault location, you would use this option: +-dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib. +.It Fl prebind +The created output file will be in the prebound format. This was used in Mac OS X 10.3 and earlier to improve launch performance. +.It Fl weak_reference_mismatches Ar treatment +Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another. The valid +treatments are: error, weak, or non-weak. The default is non-weak. +.It Fl read_only_relocs Ar treatment +Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages. The compiler will +normally never generate such code. +.It Fl force_cpusubtype_ALL +The is only applicable with -arch ppc. It tells the linker to ignore the PowerPC cpu requirements (e.g. G3, G4 or G5) encoded +in the object files and mark the resulting binary as runnable on any PowerPC cpu. +.It Fl dylinker_install_name Ar path +Only used when building dyld. +.It Fl no_arch_warnings +Suppresses warning messages about files that have the wrong architecture for the -arch flag +.It Fl arch_errors_fatal +Turns into errors, warnings about files that have the wrong architecture for the -arch flag. +.It Fl e Ar symbol_name +Specifies the entry point of a main executable. By default the entry name is "start" which is found in crt1.o which contains +the glue code need to set up and call main(). +.It Fl w +Suppress all warning messages +.It Fl final_output Ar name +Specifies the install name of a dylib if -install_name is not used. This option is used by gcc driver when it is invoked +with multiple -arch arguments. +.It Fl arch_multiple +Specifes that the linker should augment error and warning messages with the architecture name. This option is used by gcc +driver when it is invoked with multiple -arch arguments. +.It Fl twolevel_namespace_hints +Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the +libraries being linked against have not changed. +.It Fl dot Ar path +Create a file a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. +.It Fl keep_relocs +Add section based relocation records to a final linked image. These relocations are ignored at runtime by dyld. +.It Fl warn_stabs +Print a warning when the linker cannot do a BINCL/EINCL optimzation because the compiler put a bad stab symbol inside +a BINCL/EINCL range. +.It Fl warn_commons +Print a warning whenever the a tentative definition in an object file is found and a external symbol by the same name +is also found in a linked dylib. This often means that the extern keyword is missing from a variable declaration +in a header file. +.It Fl read_only_stubs +[i386 only] Makes the __IMPORT segment of a final linked images read-only. This option makes a program slightly more +secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code. The downside +is the dyld must use mprotect() to temporily make the segment writable while it is binding the stubs. +.It Fl slow_stubs +[i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which +calls through a lazy pointer in the __DATA segment. +.It Fl interposable_list Ar filename +The specified +.Ar filename +contains a list of global symbol names that should always be accessed indirectly. For instance, if libSystem.dylib +is linked such that _malloc is interposable, then calls to malloc() from within libSystem will go through a dyld +stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc +interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed +(not interposed) because they would be direct calls. +.El +.Ss Obsolete Options +.Bl -tag +.It Fl segalign Ar value +All segments must be page aligned. This option is obsolete. +.It Fl seglinkedit +Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. +.It Fl noseglinkedit +This is the default. This option is obsolete. +.It Fl fvmlib +Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. +.It Fl preload +Preload executables (MH_PRELOAD) are no longer supported. This option is obsolete. +.It Fl sectobjectsymbols Ar segname Ar sectname +Adding a local label at a section start is no longer supported. This option is obsolete. +.It Fl nofixprebinding +The MH_NOFIXPREBINDING bit of mach_headers has been ignored since Mac OS X 10.3.9. This option is obsolete. +.It Fl noprebind_all_twolevel_modules +Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. +.It Fl prebind_all_twolevel_modules +Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. +.It Fl prebind_allow_overlap +When using -prebind, the linker allows overlapping by default, so this option is obsolete. +.It Fl noprebind +LD_PREBIND is no longer supported as a way to force on prebinding, so there no longer needs to +be a command line way to override LD_PREBIND. This option is obsolete. +.It Fl sect_diff_relocs Ar treatment +This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into +a main executable, but the false positive rate generated too much noise to make the option useful. +This option is obsolete. +.It Fl run_init_lazily +This option was removed in Mac OS X 10.2. +.It Fl single_module +This is now the default so does not need to be specified. +.It Fl multi_module +Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. +.It Fl no_dead_strip_inits_and_terms +The linker never dead strips initialzation and termination routines. They are considered "roots" of the dead strip graph. +.It Fl A Ar basefile +Obsolete incremental load format. This option is obsolete. +.It Fl b +Used with -A option to strip base file's symbols. This option is obsolete. +..It Fl M +Obsolete option to produce a load map. Use -map option instead. +.It Fl Sn +Don't strip any symbols. This is the default. This option is obsolete. +.It Fl Si +Optimize stabs debug symbols to remove duplicates. This is the default. This option is obsolete. +.It Fl Sp +Write minimal stabs which causes the debugger to open and read the original .o file for full stabs. +This style of debugging is obsolete in Mac OS X 10.5. This option is obsolete. +.It Fl X +Strip local symbols that being the 'L'. This is the default. This option is obsolete. +.It Fl s +Completely strip the output, including removing the symbol table. This file format variant is no longer supported. +This option is obsolete. +.It Fl m +Don't treat multiple definitions as an error. This is no longer supported. This option is obsolete. +.It Fl y Ns symbol +Display each file in which +.Ar symbol +is used. This was previously used to debug where an undefined symbol was used, but the linker now +automatically prints out all usages. The -why_live option can also be used to display what kept +a symbol from being dead striped. This option is obsolete. +.It Fl Y Ar number +Used to control how many occurances of each symbol specifed with -y would be shown. This option is obsolete. +.It Fl nomultidefs +Only used when linking an umbrella framework. Sets the MH_NOMULTIDEFS bit in the mach_header. The MH_NOMULTIDEFS +bit has been obsolete since Mac OS X 10.4. This option is obsolete. +.It Fl multiply_defined_unused Ar treatment +Previously provided a way to warn or error if any of the symbol definitions in the output file matched any +definitions in dynamic library being linked. This option is obsolete. +.It Fl multiply_defined Ar treatment +Previously provided a way to warn or error if any of the symbols used from a dynamic library were also +available in another linked dynamic library. This option is obsolete. +.It Fl private_bundle +Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle +contained a definition that conflicted with a symbol in the main executable. The linker no longer +errors on such conflicts. This option is obsolete. +.It Fl noall_load +This is the default. This option is obsolete. +.It Fl seg_addr_table_filename Ar path +Use +.Ar path +instead of the install name of the library for matching an entry in the seg_addr_table. This option is obsolete. +.It Fl sectorder Ar segname sectname orderfile +Replaced by more general -order_file option. +.It Fl sectorder_detail +Produced extra logging about which entries from a sectorder entries were used. Replaced by -order_file_statistics. +This option is obsolete. +.El +.Sh SEE ALSO +as(1), ar(1), cc(1), nm(1), otool(1) lipo(1), +arch(3), dyld(3), Mach-O(5), strip(1), rebase(1) diff --git a/ld64/doc/man/man1/ld64.1 b/ld64/doc/man/man1/ld64.1 new file mode 100644 index 0000000..615b0e5 --- /dev/null +++ b/ld64/doc/man/man1/ld64.1 @@ -0,0 +1 @@ +.so man1/ld.1 diff --git a/ld64/doc/man/man1/rebase.1 b/ld64/doc/man/man1/rebase.1 new file mode 100644 index 0000000..6743a96 --- /dev/null +++ b/ld64/doc/man/man1/rebase.1 @@ -0,0 +1,39 @@ +.Dd June 6, 2006 +.Dt rebase 1 +.Os Darwin +.Sh NAME +.Nm rebase +.Nd "Changes base address of dylibs and bundles" +.Sh SYNOPSIS +.Nm +.Op Fl low_address Ar addr +.Op Fl high_address Ar addr +.Op Fl arch Ar arch +.Op Fl v +.Ar file(s) +.Sh DESCRIPTION +The base address of an image (dylib or bundle) is the preferred address for it to be loaded. By +default all images are built with a base address of zero. At runtime, if the +preferred memory range is already occupied, dyld will "slide" the image to a new address range. +There is a small cost to the slide, as dyld must do some fix ups. +The rebase tool takes a list of images and adjust their base address to be non-overlapping. If no +low or high address is specified, the a suitable address range is choosen for the architecture. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl low_address Ar addr +Force the base address for the first image to be +.Ar addr +(specified in hex). Each subsequent file gets the next available base address. +.It Fl high_address Ar addr +Force the base address for the last image to be such that when that image is loaded it occupies +memory up to +.Ar addr +(specified in hex). Each preceeding file gets the previous available base address. +.It Fl arch Ar arch +Only rebase the specified architecture. Other architectures in a universal image are left as is. +.It Fl v +Verbose. Print information about rebasing done. +.El +.Sh SEE ALSO +.Xr ld 1 diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5f8cd7f --- /dev/null +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -0,0 +1,788 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXAggregateTarget section */ + F96D5368094A2754008E9EE8 /* unit-tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */; + buildPhases = ( + F96D5367094A2754008E9EE8 /* ShellScript */, + ); + dependencies = ( + F96D536A094A275D008E9EE8 /* PBXTargetDependency */, + F96D536C094A275F008E9EE8 /* PBXTargetDependency */, + F96904890A4333AC00B77D2A /* PBXTargetDependency */, + F9EA73970974999B008B4F1D /* PBXTargetDependency */, + ); + name = "unit-tests"; + productName = "unit-tests"; + }; + F9B1A2670A3A567B00DA8FAB /* all */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */; + buildPhases = ( + ); + dependencies = ( + F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, + F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */, + ); + name = all; + productName = all; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; + F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; + F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; + F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; + F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; + F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; + F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; + F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; + F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; + F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9FCC3F10A54A75600CEB866 /* ld64.1 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + F96904880A4333AC00B77D2A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; + remoteInfo = rebase; + }; + F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9023C3806D5A23E001BBF46; + remoteInfo = ld; + }; + F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F971EED206D5ACF60041D381; + remoteInfo = ObjectDump; + }; + F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9023C3806D5A23E001BBF46; + remoteInfo = ld; + }; + F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; + remoteInfo = rebase; + }; + F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EA72CA097454A6008B4F1D; + remoteInfo = machocheck; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F97F5025070D0B6300B9FCD7 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */, + F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/LTOReader.hpp; sourceTree = ""; }; + C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ExecutableFile.h; sourceTree = ""; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld.cpp; sourceTree = ""; }; + F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ObjectFile.h; sourceTree = ""; }; + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/FileAbstraction.hpp; sourceTree = ""; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/MachOFileAbstraction.hpp; sourceTree = ""; }; + F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/Architectures.hpp; sourceTree = ""; }; + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/MachOReaderDylib.hpp; sourceTree = ""; }; + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/MachOReaderRelocatable.hpp; sourceTree = ""; }; + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/MachOWriterExecutable.hpp; sourceTree = ""; }; + F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; + F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = ""; }; + F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; + F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/OpaqueSection.hpp; sourceTree = ""; }; + F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ArchiveReader.hpp; sourceTree = SOURCE_ROOT; }; + F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = ""; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = ""; }; + F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; + F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/machochecker.cpp; sourceTree = ""; }; + F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/debugline.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = ""; }; + F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; + F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/rebase.cpp; sourceTree = ""; }; + F9FCC3F10A54A75600CEB866 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F9023C3706D5A23E001BBF46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED106D5ACF60041D381 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EA72C9097454A6008B4F1D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EC77EC0A2F85F6002A3E39 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F9023C2C06D5A227001BBF46 = { + isa = PBXGroup; + children = ( + C02A29DE0953B26E001FB8C1 /* ChangeLog */, + F933DC37092A82480083EAC8 /* Architectures.hpp */, + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */, + F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */, + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */, + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, + 3DA587190ACC53BE0015C432 /* LTOReader.hpp */, + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, + F9023C4106D5A254001BBF46 /* ObjectFile.h */, + F98D26850AA779BD00416316 /* OpaqueSection.hpp */, + F9023C3F06D5A254001BBF46 /* ld.cpp */, + F9C0D48A06DD1E1B001C7193 /* Options.cpp */, + F9C0D48B06DD1E1B001C7193 /* Options.h */, + F9EA7583097882F3008B4F1D /* debugline.h */, + F9EA7582097882F3008B4F1D /* debugline.c */, + F9EA72D4097454FF008B4F1D /* machochecker.cpp */, + F971EED706D5AD240041D381 /* ObjectDump.cpp */, + F9EC78050A2F8674002A3E39 /* rebase.cpp */, + F97F5028070D0BB200B9FCD7 /* ld.1 */, + F9FCC3F10A54A75600CEB866 /* ld64.1 */, + F9B1A2580A3A448800DA8FAB /* rebase.1 */, + F9023C3A06D5A23E001BBF46 /* Products */, + ); + sourceTree = ""; + }; + F9023C3A06D5A23E001BBF46 /* Products */ = { + isa = PBXGroup; + children = ( + F9023C3906D5A23E001BBF46 /* ld */, + F971EED306D5ACF60041D381 /* ObjectDump */, + F9EA72CB097454A6008B4F1D /* machocheck */, + F9EC77EE0A2F85F6002A3E39 /* rebase */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F9023C3806D5A23E001BBF46 /* ld */ = { + isa = PBXNativeTarget; + buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; + buildPhases = ( + 0B12F6A50CE39466008ABCAE /* build configure.h */, + F9023C3606D5A23E001BBF46 /* Sources */, + F9023C3706D5A23E001BBF46 /* Frameworks */, + F97F5025070D0B6300B9FCD7 /* CopyFiles */, + F9FCC3EF0A54A4ED00CEB866 /* Run Script */, + ); + buildRules = ( + F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, + F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = ld; + productName = ld64; + productReference = F9023C3906D5A23E001BBF46 /* ld */; + productType = "com.apple.product-type.tool"; + }; + F971EED206D5ACF60041D381 /* ObjectDump */ = { + isa = PBXNativeTarget; + buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */; + buildPhases = ( + F971EED006D5ACF60041D381 /* Sources */, + F971EED106D5ACF60041D381 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ObjectDump; + productName = ObjectDump; + productReference = F971EED306D5ACF60041D381 /* ObjectDump */; + productType = "com.apple.product-type.tool"; + }; + F9EA72CA097454A6008B4F1D /* machocheck */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; + buildPhases = ( + F9EA72C8097454A6008B4F1D /* Sources */, + F9EA72C9097454A6008B4F1D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = machocheck; + productName = machocheck; + productReference = F9EA72CB097454A6008B4F1D /* machocheck */; + productType = "com.apple.product-type.tool"; + }; + F9EC77ED0A2F85F6002A3E39 /* rebase */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */; + buildPhases = ( + F9EC77EB0A2F85F6002A3E39 /* Sources */, + F9EC77EC0A2F85F6002A3E39 /* Frameworks */, + F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rebase; + productName = rebase; + productReference = F9EC77EE0A2F85F6002A3E39 /* rebase */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F9023C3006D5A227001BBF46 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 0; + mainGroup = F9023C2C06D5A227001BBF46; + productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F9B1A2670A3A567B00DA8FAB /* all */, + F9023C3806D5A23E001BBF46 /* ld */, + F9EC77ED0A2F85F6002A3E39 /* rebase */, + F971EED206D5ACF60041D381 /* ObjectDump */, + F9EA72CA097454A6008B4F1D /* machocheck */, + F96D5368094A2754008E9EE8 /* unit-tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0B12F6A50CE39466008ABCAE /* build configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "build configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\nelse\n\techo \"#undef LTO_SUPPORT\t\" > ${DERIVED_FILE_DIR}/configure.h\nfi\n"; + showEnvVarsInLog = 0; + }; + F96D5367094A2754008E9EE8 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + 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"; + showEnvVarsInLog = 0; + }; + F9FCC3EF0A54A4ED00CEB866 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "cd ${DSTROOT}/usr/bin\nln -s ld ld64"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F9023C3606D5A23E001BBF46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, + F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, + F9EA7584097882F3008B4F1D /* debugline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F971EED006D5ACF60041D381 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */, + F9EA75BC09788857008B4F1D /* debugline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EA72C8097454A6008B4F1D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9EC77EB0A2F85F6002A3E39 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F96904890A4333AC00B77D2A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EC77ED0A2F85F6002A3E39 /* rebase */; + targetProxy = F96904880A4333AC00B77D2A /* PBXContainerItemProxy */; + }; + F96D536A094A275D008E9EE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9023C3806D5A23E001BBF46 /* ld */; + targetProxy = F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */; + }; + F96D536C094A275F008E9EE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F971EED206D5ACF60041D381 /* ObjectDump */; + targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */; + }; + F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9023C3806D5A23E001BBF46 /* ld */; + targetProxy = F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */; + }; + F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EC77ED0A2F85F6002A3E39 /* rebase */; + targetProxy = F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */; + }; + F9EA73970974999B008B4F1D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EA72CA097454A6008B4F1D /* machocheck */; + targetProxy = F9EA73960974999B008B4F1D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + F933D91C09291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DEVELOPER_DIR)/usr/local/include", + "$(DEVELOPER_DIR)/usr/include", + ); + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = ""; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + PREBINDING = NO; + PRODUCT_NAME = ld; + SECTORDER_FLAGS = ""; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = Debug; + }; + F933D91D09291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DEVELOPER_DIR)/usr/local/include", + "$(DEVELOPER_DIR)/usr/include", + ); + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + PREBINDING = NO; + PRODUCT_NAME = ld; + SECTORDER_FLAGS = ""; + VALID_ARCHS = "i386 ppc"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = Release; + }; + F933D92009291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/include"; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Debug; + }; + F933D92109291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = s; + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F933D92409291AC90083EAC8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = Debug; + }; + F933D92509291AC90083EAC8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/SDKs/Extra/usr/include"; + }; + name = Release; + }; + F96D536E094A2773008E9EE8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + PRODUCT_NAME = "unit-tests"; + }; + name = Debug; + }; + F96D536F094A2773008E9EE8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = "unit-tests"; + }; + name = Release; + }; + F9B1A26D0A3A568700DA8FAB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = all; + }; + name = Debug; + }; + F9B1A26E0A3A568700DA8FAB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = all; + ZERO_LINK = NO; + }; + name = Release; + }; + F9EA72D0097454D5008B4F1D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = Debug; + }; + F9EA72D1097454D5008B4F1D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = Release; + }; + F9EC77F10A2F8616002A3E39 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = rebase; + }; + name = Debug; + }; + F9EC77F20A2F8616002A3E39 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + INSTALL_PATH = /usr/bin; + PREBINDING = NO; + PRODUCT_NAME = rebase; + VALID_ARCHS = "i386 ppc"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D91C09291AC90083EAC8 /* Debug */, + F933D91D09291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D92009291AC90083EAC8 /* Debug */, + F933D92109291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F933D92409291AC90083EAC8 /* Debug */, + F933D92509291AC90083EAC8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F96D536E094A2773008E9EE8 /* Debug */, + F96D536F094A2773008E9EE8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9B1A26D0A3A568700DA8FAB /* Debug */, + F9B1A26E0A3A568700DA8FAB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9EA72D0097454D5008B4F1D /* Debug */, + F9EA72D1097454D5008B4F1D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9EC77F10A2F8616002A3E39 /* Debug */, + F9EC77F20A2F8616002A3E39 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F9023C3006D5A227001BBF46 /* Project object */; +} diff --git a/ld64/src/Architectures.hpp b/ld64/src/Architectures.hpp new file mode 100644 index 0000000..2546bfe --- /dev/null +++ b/ld64/src/Architectures.hpp @@ -0,0 +1,88 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +struct ppc +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, + kBranch24, kBranch24WeakImport, kBranch14, + kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct ppc64 +{ + typedef Pointer64 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, + kBranch24, kBranch24WeakImport, kBranch14, + kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct x86 +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff16, + kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct x86_64 +{ + typedef Pointer64 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32, + kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, + kBranchPCRel32, kBranchPCRel32WeakImport, + kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, + kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +struct arm +{ + typedef Pointer32 P; + + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kReadOnlyPointer, + kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; +}; + +#endif // __ARCHITECTURES__ + + diff --git a/ld64/src/ArchiveReader.hpp b/ld64/src/ArchiveReader.hpp new file mode 100644 index 0000000..a195090 --- /dev/null +++ b/ld64/src/ArchiveReader.hpp @@ -0,0 +1,454 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_ARCHIVE__ +#define __OBJECT_FILE_ARCHIVE__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ObjectFile.h" +#include "MachOReaderRelocatable.hpp" +#if LTO_SUPPORT + #include "LTOReader.hpp" +#endif + +namespace archive { + +typedef const struct ranlib* ConstRanLibPtr; + +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength); + Reader(const uint8_t fileContent[], uint64_t fileLength, + const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime(){ return fModTime; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabs() { return NULL; } + virtual void optimize(std::vector&, std::vector&, + std::vector&, const std::set&, + uint32_t, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, int okind, + bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs); + +private: + static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength); + static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength); + static cpu_type_t architecture(); + + + class Entry : ar_hdr + { + public: + const char* getName() const; + time_t getModTime() const; + const uint8_t* getContent() const; + uint32_t getContentSize() const; + const Entry* getNext() const; + private: + bool hasLongName() const; + unsigned int getLongNameSpace() const; + + }; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; + + typedef typename A::P P; + typedef typename A::P::E E; + + const struct ranlib* ranlibHashSearch(const char* name); + ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); + void dumpTableOfContents(); + void buildHashTable(); + + const char* fPath; + time_t fModTime; + const ObjectFile::ReaderOptions& fOptions; + uint32_t fOrdinalBase; + const uint8_t* fFileContent; + uint64_t fFileLength; + const struct ranlib* fTableOfContents; + uint32_t fTableOfContentCount; + const char* fStringPool; + std::vector fAllAtoms; + std::vector fInstantiatedReaders; + std::set fInstantiatedEntries; + std::set fPossibleEntries; + NameToEntryMap fHashTable; + + static std::vector fgEmptyList; +}; + +template +std::vector Reader::fgEmptyList; + + +template +bool Reader::Entry::hasLongName() const +{ + return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); +} + +template +unsigned int Reader::Entry::getLongNameSpace() const +{ + char* endptr; + long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); + return result; +} + +template +const char* Reader::Entry::getName() const +{ + if ( this->hasLongName() ) { + int len = this->getLongNameSpace(); + static char longName[256]; + strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); + longName[len] = '\0'; + return longName; + } + else { + static char shortName[20]; + strncpy(shortName, this->ar_name, 16); + shortName[16] = '\0'; + char* space = strchr(shortName, ' '); + if ( space != NULL ) + *space = '\0'; + return shortName; + } +} + +template +time_t Reader::Entry::getModTime() const +{ + char temp[14]; + strncpy(temp, this->ar_date, 12); + temp[12] = '\0'; + char* endptr; + return (time_t)strtol(temp, &endptr, 10); +} + + +template +const uint8_t* Reader::Entry::getContent() const +{ + if ( this->hasLongName() ) + return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); + else + return ((uint8_t*)this) + sizeof(ar_hdr); +} + + +template +uint32_t Reader::Entry::getContentSize() const +{ + char temp[12]; + strncpy(temp, this->ar_size, 10); + temp[10] = '\0'; + char* endptr; + long size = strtol(temp, &endptr, 10); + // long name is included in ar_size + if ( this->hasLongName() ) + size -= this->getLongNameSpace(); + return size; +} + + +template +const class Reader::Entry* Reader::Entry::getNext() const +{ + const uint8_t* p = this->getContent() + getContentSize(); + p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align + return (class Reader::Entry*)p; +} + + +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_I386; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_X86_64; } +template <> cpu_type_t Reader::architecture() { return CPU_TYPE_ARM; } + + +template +bool Reader::validMachOFile(const uint8_t* fileContent, uint64_t fileLength) +{ + return mach_o::relocatable::Reader::validFile(fileContent); +} + +template +bool Reader::validLTOFile(const uint8_t* fileContent, uint64_t fileLength) +{ +#if LTO_SUPPORT + return lto::Reader::validFile(fileContent, fileLength, architecture()); +#else + return false; +#endif +} + + + +template +bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength) +{ + // must have valid archive header + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + return false; + + // peak at first .o file and verify it is correct architecture + const Entry* const start = (Entry*)&fileContent[8]; + const Entry* const end = (Entry*)&fileContent[fileLength]; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + // skip option table-of-content member + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + // archive is valid if first .o file is valid + return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize())); + } + // empty archive + return true; +} + +template +Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL), + fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL) +{ + fPath = strdup(path); + fFileContent = fileContent; + fFileLength = fileLength; + + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + throw "not an archive"; + + // write out path for -whatsloaded option + if ( options.fLogAllFiles ) + printf("%s\n", path); + + if ( !options.fFullyLoadArchives ) { + const Entry* const firstMember = (Entry*)&fFileContent[8]; + if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { + const uint8_t* contents = firstMember->getContent(); + uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); + fTableOfContents = (const struct ranlib*)&contents[4]; + fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); + fStringPool = (const char*)&contents[ranlibArrayLen+8]; + if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) + || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) + throw "malformed archive, perhaps wrong architecture"; + this->buildHashTable(); + } + else + throw "archive has no table of contents"; + } +} + + +template +ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) +{ + const char* memberName = member->getName(); + char memberPath[strlen(fPath) + strlen(memberName)+4]; + strcpy(memberPath, fPath); + strcat(memberPath, "("); + strcat(memberPath, memberName); + strcat(memberPath, ")"); + //fprintf(stderr, "using %s from %s\n", memberName, fPath); + try { + // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive + uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent; + if ( validMachOFile(member->getContent(), member->getContentSize()) ) { + return new typename mach_o::relocatable::Reader::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase); + } +#if LTO_SUPPORT + else if ( validLTOFile(member->getContent(), member->getContentSize()) ) { + return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture()); + } +#endif + throw "not a valid archive member"; + } + catch (const char* msg) { + throwf("in %s, %s", memberPath, msg); + } +} + + +template +std::vector& Reader::getAtoms() +{ + if ( fOptions.fFullyLoadArchives ) { + // build vector of all atoms from all .o files in this archive + const Entry* const start = (Entry*)&fFileContent[8]; + const Entry* const end = (Entry*)&fFileContent[fFileLength]; + for (const Entry* p=start; p < end; p = p->getNext()) { + const char* memberName = p->getName(); + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + if ( fOptions.fWhyLoad ) + printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); + ObjectFile::Reader* r = this->makeObjectReaderForMember(p); + std::vector& atoms = r->getAtoms(); + fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); + fInstantiatedReaders.push_back(r); + } + return fAllAtoms; + } + else if ( fOptions.fLoadAllObjcObjectsFromArchives ) { + // build vector of all atoms from all .o files containing objc classes in this archive + for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) { + if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { + const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)]; + if ( fInstantiatedEntries.count(member) == 0 ) { + if ( fOptions.fWhyLoad ) + printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName()); + // only return these atoms once + fInstantiatedEntries.insert(member); + ObjectFile::Reader* r = makeObjectReaderForMember(member); + std::vector& atoms = r->getAtoms(); + fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); + fInstantiatedReaders.push_back(r); + } + } + } + return fAllAtoms; + } + else { + // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed + return fgEmptyList; + } +} + +template +void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, const std::set& deadAtoms, + uint32_t nextOrdinal, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, int okind, + bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs) +{ + for(std::vector::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) { + (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, nextOrdinal, writer, llvmOptions, + allGlobalsAReDeadStripRoots, okind, verbose, saveTemps, outputFilePath, pie, allowTextRelocs); + } +} + + + +template +ConstRanLibPtr Reader::ranlibHashSearch(const char* name) +{ + class NameToEntryMap::iterator pos = fHashTable.find(name); + if ( pos != fHashTable.end() ) + return pos->second; + else + return NULL; +} + +template +void Reader::buildHashTable() +{ + // walk through list backwards, adding/overwriting entries + // this assures that with duplicates those earliest in the list will be found + for (int i = fTableOfContentCount-1; i >= 0; --i) { + const struct ranlib* entry = &fTableOfContents[i]; + const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; + const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; + //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); + fHashTable[entryName] = entry; + fPossibleEntries.insert(member); + } +} + +template +void Reader::dumpTableOfContents() +{ + for (unsigned int i=0; i < fTableOfContentCount; ++i) { + const struct ranlib* e = &fTableOfContents[i]; + printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); + } +} + +template +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + if ( fOptions.fFullyLoadArchives ) { + return NULL; + } + else { + const struct ranlib* result = NULL; + // do a hash search of table of contents looking for requested symbol + result = ranlibHashSearch(name); + if ( result != NULL ) { + const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; + if ( fInstantiatedEntries.count(member) == 0 ) { + if ( fOptions.fWhyLoad ) + printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); + // only return these atoms once + fInstantiatedEntries.insert(member); + ObjectFile::Reader* r = makeObjectReaderForMember(member); + fInstantiatedReaders.push_back(r); + return new std::vector(r->getAtoms()); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); + return NULL; + } +} + + + + + +}; // namespace archive + + +#endif // __OBJECT_FILE_ARCHIVE__ diff --git a/ld64/src/ExecutableFile.h b/ld64/src/ExecutableFile.h new file mode 100644 index 0000000..b0b760d --- /dev/null +++ b/ld64/src/ExecutableFile.h @@ -0,0 +1,70 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __EXECUTABLEFILE__ +#define __EXECUTABLEFILE__ + +#include +#include + +#include "ObjectFile.h" +#include "Options.h" + + +namespace ExecutableFile { + + struct DyLibUsed + { + ObjectFile::Reader* reader; + DynamicLibraryOptions options; + }; + + class Writer : public ObjectFile::Reader + { + public: + virtual ~Writer() {}; + + virtual const char* getPath() = 0; + virtual std::vector& getAtoms() = 0; + virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; + virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, + bool objcReplacementClasses) = 0; + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; + virtual uint64_t write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool createUUID, bool canScatter, + ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs, + bool overridesDylibWeakDefines) = 0; + + protected: + Writer(std::vector&) {}; + }; + +}; + +#endif // __EXECUTABLEFILE__ diff --git a/ld64/src/FileAbstraction.hpp b/ld64/src/FileAbstraction.hpp new file mode 100644 index 0000000..1f7a629 --- /dev/null +++ b/ld64/src/FileAbstraction.hpp @@ -0,0 +1,145 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/ld64/src/LTOReader.hpp b/ld64/src/LTOReader.hpp new file mode 100644 index 0000000..2736f43 --- /dev/null +++ b/ld64/src/LTOReader.hpp @@ -0,0 +1,684 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LTO_READER_H__ +#define __LTO_READER_H__ + +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ObjectFile.h" +#include "Options.h" + +#include "llvm-c/lto.h" + + +namespace lto { + + +// +// Reference handles Atom references. These references facilitate +// symbol resolution. +// + +class Reference : public ObjectFile::Reference +{ +public: + Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { } + Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { } + + bool isTargetUnbound() const { return fTargetAtom == NULL; } + bool isFromTargetUnbound() const { return true; } + uint8_t getKind() const { return 0; } + uint64_t getFixUpOffset() const { return 0; } + const char * getTargetName() const { return fTargetName; } + ObjectFile::Atom& getTarget() const { return *fTargetAtom; } + uint64_t getTargetOffset() const { return 0; } + bool hasFromTarget() const { return false; } + ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } + const char * getFromTargetName() const { return NULL; } + uint64_t getFromTargetOffset() const { return 0; } + TargetBinding getTargetBinding() const; + TargetBinding getFromTargetBinding() const { return kDontBind; } + void setTarget (ObjectFile::Atom& a, uint64_t offset) + { fTargetAtom = &a; } + void setFromTarget(ObjectFile::Atom &a) { } + const char * getDescription() const; + +private: + const char * fTargetName; + ObjectFile::Atom * fTargetAtom; +}; + + +ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const +{ + if ( fTargetAtom == NULL ) + return kUnboundByName; + else if ( fTargetName == NULL ) + return kBoundDirectly; + else + return kBoundByName; +} + +const char* Reference::getDescription() const +{ + static char temp[256]; + strcpy(temp, "reference to "); + if ( fTargetName != NULL ) + strcat(temp, fTargetName); + else + strcat(temp, fTargetAtom->getDisplayName()); + return temp; +} + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + virtual bool hasFixedAddress() const { return fFixedAddress; } + + static Segment fgBootstrapSegment; + +private: + const char* fName; + const bool fReadable; + const bool fWritable; + const bool fExecutable; + const bool fFixedAddress; +}; + +Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false); + + + + +// +// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, +// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After +// optimization is performed, real Atoms are created for these symobls. However these real Atoms +// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate +// methods to real atom. +// +class Atom : public ObjectFile::Atom +{ +public: + Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom); + + ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; } + bool getTranslationUnitSource (const char **dir, const char **name) const + { return fRealAtom->getTranslationUnitSource(dir, name); } + const char * getName () const { return fName; } + const char * getDisplayName() const { return this->getName(); } + Scope getScope() const { return fScope; } + DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } + SymbolTableInclusion getSymbolTableInclusion() const + { return fRealAtom->getSymbolTableInclusion(); } + bool dontDeadStrip() const { return false; } + bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); } + bool isThumb() const { return false; } + uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); } + std::vector& getReferences() const + { return (fRealAtom ? fRealAtom->getReferences() : (std::vector&)fReferences); } + bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); } + const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); } + // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection. + class ObjectFile::Section * getSection() const { return fSection; } + ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); } + uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); } + ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); } + std::vector* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); } + ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); } + void copyRawContent(uint8_t buffer[]) const + { if (fRealAtom) fRealAtom->copyRawContent(buffer); } + void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; } + + void setRealAtom (ObjectFile::Atom *atom) + { fRealAtom = atom; } + ObjectFile::Atom * getRealAtom() { return fRealAtom; } + void addReference(ObjectFile::Reference *ref) + { fReferences.push_back(ref); } + + void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); } + void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); } + +private: + class Reader& fOwner; + const char* fName; + ObjectFile::Atom::Scope fScope; + ObjectFile::Atom::DefinitionKind fKind; + uint8_t fAlignment; + ObjectFile::Atom* fRealAtom; + std::vector fReferences; +}; + + +Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom) +: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL) +{ + // every Atom references the InternalAtom for its reader + fReferences.push_back(new Reference(internalAtom)); +} + + +// +// ld64 only tracks non-internal symbols from an llvm bitcode file. +// We model this by having an InternalAtom which represent all internal functions and data. +// All non-interal symbols from a bitcode file are represented by a Atom +// and each Atom has a reference to the InternalAtom. The InternalAtom +// also has references to each symbol external to the bitcode file. +// +class InternalAtom : public ObjectFile::Atom +{ +public: + InternalAtom(class Reader& owner) : fOwner(owner) {} + + ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; } + bool getTranslationUnitSource (const char **dir, const char **name) const + { return false; } + const char * getName () const { return "__llvm-internal-atom"; } + const char * getDisplayName() const { return "llvm bitcode"; } + Scope getScope() const { return scopeTranslationUnit; } + DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; } + bool dontDeadStrip() const { return false; } + bool isZeroFill() const { return false; } + bool isThumb() const { return false; } + uint64_t getSize() const { return 0; } + std::vector& getReferences() const { return (std::vector&)fReferences; } + bool mustRemainInSection() const { return false; } + const char * getSectionName() const { return NULL; } + class ObjectFile::Section * getSection() const { return NULL; } + ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; } + uint32_t getOrdinal() const { return 0; } + ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + std::vector* getLineInfo() const { return NULL; } + ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + void copyRawContent(uint8_t buffer[]) const { } + void setScope(Scope s) { } + + void addReference(const char* targetName); + +private: + class Reader& fOwner; + std::vector fReferences; +}; + + +void InternalAtom::addReference(const char* name) +{ + fReferences.push_back(new Reference(name)); +} + + + + +class RemovableAtoms +{ +public: + RemovableAtoms(std::set& iAtoms) : fAtoms(iAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fAtoms.count(atom) != 0 ); + } + +private: + std::set& fAtoms; +}; + + + +// +// LLVM bitcode file reader +// +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); + static bool loaded() { return (::lto_get_version() != NULL); } + Reader(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, + const ObjectFile::ReaderOptions&, cpu_type_t arch); + virtual ~Reader(); + + virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return fModTime; } + virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; } + virtual std::vector* getStabs() { return NULL; } + virtual void optimize(std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, const std::set&, + uint32_t nextInputOrdinal, + ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, + int outputKind, bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs); + +private: + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; + + ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal); + static const char* tripletPrefixForArch(cpu_type_t); + + cpu_type_t fArchitecture; + const char* fPath; + time_t fModTime; + lto_module_t fModule; + std::vector fAtoms; + InternalAtom fInternalAtom; + const ObjectFile::ReaderOptions& fReaderOptions; + static std::set fgReaders; + static bool fgOptimized; +}; + +bool Reader::fgOptimized = false; +std::set Reader::fgReaders; + + +Reader::~Reader() +{ + if ( fModule != NULL ) + ::lto_module_dispose(fModule); +} + +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, cpu_type_t arch) + : fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options) +{ + fgReaders.insert(this); + + fModule = ::lto_module_create_from_memory(fileContent, fileLength); + if ( fModule == NULL ) + throwf("could not parse object file %s: %s", path, lto_get_error_message()); + + fAtoms.push_back(&fInternalAtom); + + uint32_t count = ::lto_module_get_num_symbols(fModule); + for (uint32_t i=0; i < count; ++i) { + const char* name = ::lto_module_get_symbol_name(fModule, i); + lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i); + + ObjectFile::Atom::DefinitionKind kind; + switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { + case LTO_SYMBOL_DEFINITION_REGULAR: + kind = ObjectFile::Atom::kRegularDefinition; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + kind = ObjectFile::Atom::kTentativeDefinition; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + kind = ObjectFile::Atom::kWeakDefinition; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + kind = ObjectFile::Atom::kExternalDefinition; + break; + default: + throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); + } + + // make LLVM atoms for definitions and a reference for undefines + if ( kind != ObjectFile::Atom::kExternalDefinition ) { + ObjectFile::Atom::Scope scope; + switch ( attr & LTO_SYMBOL_SCOPE_MASK) { + case LTO_SYMBOL_SCOPE_INTERNAL: + scope = ObjectFile::Atom::scopeTranslationUnit; + break; + case LTO_SYMBOL_SCOPE_HIDDEN: + scope = ObjectFile::Atom::scopeLinkageUnit; + break; + case LTO_SYMBOL_SCOPE_DEFAULT: + scope = ObjectFile::Atom::scopeGlobal; + break; + default: + throwf("unknown scope for symbol %s in bitcode file %s", name, path); + } + // only make atoms for non-internal symbols + if ( scope == ObjectFile::Atom::scopeTranslationUnit ) + continue; + uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); + // make Atom + fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom)); + } + else { + // add to list of external references + fInternalAtom.addReference(name); + } + } +} + +const char* Reader::tripletPrefixForArch(cpu_type_t arch) +{ + switch (arch) { + case CPU_TYPE_POWERPC: + return "powerpc-"; + case CPU_TYPE_POWERPC64: + return "powerpc64-"; + case CPU_TYPE_I386: + return "i386-"; + case CPU_TYPE_X86_64: + return "x86_64-"; + case CPU_TYPE_ARM: + return "arm-"; + } + return ""; +} + +bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) +{ + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); +} + +void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, const std::set& deadAtoms, + uint32_t nextInputOrdinal, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, + int okind, bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs) +{ + // this method is call on all Readers. We want the first call to trigger optimization + // across all Readers and the subsequent calls to do nothing. + if ( fgOptimized ) + return; + fgOptimized = true; + + Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency + + // print out LTO version string if -v was used + if ( verbose ) + fprintf(stderr, "%s\n", lto_get_version()); + + // create optimizer and add each Reader + lto_code_gen_t generator = ::lto_codegen_create(); + for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { + if ( ::lto_codegen_add_module(generator, (*it)->fModule) ) + throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message()); + } + + // add any -mllvm command line options + for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { + ::lto_codegen_debug_options(generator, *it); + } + + // the linker must preserve all globals in dylibs and flat images + const bool globalsNeedPreserving = allGlobalsAReDeadStripRoots || fReaderOptions.fFlatNamespace; + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringToAtom llvmAtoms; + bool hasNonllvmAtoms = false; + for (std::vector::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + // only look at references come from an atom that is not an llvm atom + if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) { + // remember if we've seen any atoms not from an llvm reader and not from the writer + if ( atom->getFile() != writer ) + hasNonllvmAtoms = true; + std::vector& refs = atom->getReferences(); + for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { + ObjectFile::Reference* ref = *ri; + // add target name to set if target is an llvm atom + if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) { + nonLLVMRefs.insert(ref->getTargetName()); + } + } + } + else { + const char* name = atom->getName(); + if ( name != NULL ) + llvmAtoms[name] = (Atom*)atom; + } + } + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions + // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // atom so that the linker can replace it with the mach-o one later. + CStringToAtom deadllvmAtoms; + for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) { + const char* name = atom->getName(); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)atom; + } + } + + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - globals need preserving and atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( globalsNeedPreserving && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) { + if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { + // HACK, no good way to tell linker we are all done, so just quit + exit(0); + } + warning("could not produce merged bitcode file"); + } + + // if requested, save off merged bitcode file + if ( saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + + // set code-gen model + lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + switch ( outputKind ) { + case Options::kDynamicExecutable: + if ( pie ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // ?? Is this appropriate ? + case Options::kDyld: + if ( allowTextRelocs ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + break; + case Options::kStaticExecutable: + model = LTO_CODEGEN_PIC_MODEL_STATIC; + break; + } + if ( ::lto_codegen_set_pic_model(generator, model) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // run code generator + size_t machOFileLen; + const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + + // if requested, save off temp mach-o file + if ( saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + } + + // parse generated mach-o file into a MachOReader + ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal); + + // sync generated mach-o atoms with existing atoms ld knows about + std::vector machoAtoms = machoReader->getAtoms(); + for (std::vector::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + const char* name = atom->getName(); + if ( name != NULL ) { + CStringToAtom::iterator pos = llvmAtoms.find(name); + if ( pos != llvmAtoms.end() ) { + // turn Atom into a proxy for this mach-o atom + pos->second->setRealAtom(atom); + } + else { + // an atom of this name was not in the allAtoms list the linker gave us + if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { + // this corresponding to an atom that the linker coalesced away. Ignore it + // Make sure there any dependent atoms are also marked dead + std::vector& refs = atom->getReferences(); + for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { + ObjectFile::Reference* ref = *ri; + if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX + ObjectFile::Atom* targ = &ref->getTarget(); + deadllvmAtoms[targ->getName()] = (Atom*)atom; + } + } + } + else + { + // this is something new that lto conjured up, tell ld its new + newAtoms.push_back(atom); + } + } + } + else { + // ld only knew about named atoms, so this one must be new + newAtoms.push_back(atom); + } + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { + ObjectFile::Reference* ref = *rit; + const char* targetName = ref->getTargetName(); + CStringToAtom::iterator pos; + if (targetName != NULL) { + switch ( ref->getTargetBinding() ) { + case ObjectFile::Reference::kUnboundByName: + // accumulate unbounded references so that ld can bound them. + additionalUndefines.push_back(targetName); + break; + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + // If mach-o atom is referencing another mach-o atom then + // reference is not going through Atom proxy. Fix it here to ensure that all + // llvm symbol references always go through Atom proxy. + pos = llvmAtoms.find(targetName); + if ( pos != llvmAtoms.end() ) + ref->setTarget(*pos->second, ref->getTargetOffset()); + break; + case ObjectFile::Reference::kDontBind: + break; + } + } + } + } + + // Remove InternalAtoms from ld + std::set deletedAtoms; + for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { + deletedAtoms.insert(&((*it)->fInternalAtom)); + } + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->getRealAtom() == NULL ) + deletedAtoms.insert(li->second); + } + allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), RemovableAtoms(deletedAtoms)), allAtoms.end()); +} + + +ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal) +{ + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); + break; + } + throw "LLVM LTO, file is not of required architecture"; +} + +}; // namespace lto + + +void printLTOVersion(Options &opts) { + const char* vers = lto_get_version(); + if ( vers != NULL ) + fprintf(stderr, "%s\n", vers); +} + + +#endif + diff --git a/ld64/src/MachOFileAbstraction.hpp b/ld64/src/MachOFileAbstraction.hpp new file mode 100644 index 0000000..83a8ee1 --- /dev/null +++ b/ld64/src/MachOFileAbstraction.hpp @@ -0,0 +1,925 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +// stuff that will eventually go away once newer cctools headers are widespread +#ifndef LC_LAZY_LOAD_DYLIB + #define LC_LAZY_LOAD_DYLIB 0x20 +#endif +#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS + #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#endif +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif +#ifndef N_ARM_THUMB_DEF + #define N_ARM_THUMB_DEF 0x0008 +#endif +enum reloc_type_arm +{ + ARM_RELOC_VANILLA, /* generic relocation as discribed above */ + ARM_RELOC_PAIR, /* the second relocation entry of a pair */ + ARM_RELOC_SECTDIFF, /* a PAIR follows with subtract symbol value */ + ARM_RELOC_LOCAL_SECTDIFF, /* like ARM_RELOC_SECTDIFF, but the symbol + referenced was local. */ + ARM_RELOC_PB_LA_PTR,/* prebound lazy pointer */ + ARM_RELOC_BR24, /* 24 bit branch displacement (to a word address) */ + ARM_THUMB_RELOC_BR22, /* 22 bit branch displacement (to a half-word + address) */ +}; + +#ifndef LC_ENCRYPTION_INFO + #define LC_ENCRYPTION_INFO 0x21 + struct encryption_info_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; /* file offset of encrypted range */ + uint32_t cryptsize; /* file size of encrypted range */ + uint32_t cryptid; /* which enryption system, 0 means not-encrypted yet */ + }; +#endif + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

::CMD + }; + + typedef typename P::E E; +private: + macho_segment_content

segment; +}; + + +// +// mach-o section +// +template struct macho_section_content {}; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; + +template +class macho_section { +public: + const char* sectname() const INLINE { return section.fields.sectname; } + void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } + + const char* segname() const INLINE { return section.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } + + uint64_t addr() const INLINE { return P::getP(section.fields.addr); } + void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } + + uint64_t size() const INLINE { return P::getP(section.fields.size); } + void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } + + uint32_t offset() const INLINE { return E::get32(section.fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } + + uint32_t align() const INLINE { return E::get32(section.fields.align); } + void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } + + uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } + void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } + + uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } + void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } + + uint32_t flags() const INLINE { return E::get32(section.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } + + uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } + void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } + + uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } + void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } + + typedef typename P::E E; +private: + macho_section_content

section; +}; + + +// +// mach-o dylib load command +// +template +class macho_dylib_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } + + uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } + void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } + + uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } + void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } + + uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } + void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylib_command fields; +}; + + +// +// mach-o dylinker load command +// +template +class macho_dylinker_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylinker_command fields; +}; + + +// +// mach-o sub_framework load command +// +template +class macho_sub_framework_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } + void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } + + const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } + void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_framework_command fields; +}; + + +// +// mach-o sub_client load command +// +template +class macho_sub_client_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } + void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } + + const char* client() const INLINE { return (const char*)&fields + client_offset(); } + void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_client_command fields; +}; + + +// +// mach-o sub_umbrella load command +// +template +class macho_sub_umbrella_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } + void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } + + const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } + void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_umbrella_command fields; +}; + + +// +// mach-o sub_library load command +// +template +class macho_sub_library_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } + void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } + + const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } + void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_library_command fields; +}; + + +// +// mach-o uuid load command +// +template +class macho_uuid_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); } + + typedef typename P::E E; +private: + uuid_command fields; +}; + + +// +// mach-o routines load command +// +template struct macho_routines_content {}; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; + +template +class macho_routines_command { +public: + uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } + + uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } + void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } + + uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } + void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } + + uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } + void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } + + uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } + void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } + + uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } + void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } + + uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } + void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } + + uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } + void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } + + uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } + void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } + + typedef typename P::E E; + enum { + CMD = macho_routines_content

::CMD + }; +private: + macho_routines_content

routines; +}; + + +// +// mach-o symbol table load command +// +template +class macho_symtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t symoff() const INLINE { return E::get32(fields.symoff); } + void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } + + uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } + void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } + + uint32_t stroff() const INLINE { return E::get32(fields.stroff); } + void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } + + uint32_t strsize() const INLINE { return E::get32(fields.strsize); } + void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } + + + typedef typename P::E E; +private: + symtab_command fields; +}; + + +// +// mach-o dynamic symbol table load command +// +template +class macho_dysymtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } + + uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } + void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } + + uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } + void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } + + uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } + void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } + + uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } + void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } + + uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } + void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } + + uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } + void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } + + uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } + void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } + + uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } + void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } + + uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } + void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } + + uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } + void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } + + uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } + void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } + + uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } + + uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } + void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } + + uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } + void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } + + typedef typename P::E E; +private: + dysymtab_command fields; +}; + + + + +// +// mach-o module table entry (for compatibility with old ld/dyld) +// +template struct macho_dylib_module_content {}; +template <> struct macho_dylib_module_content > { struct dylib_module fields; }; +template <> struct macho_dylib_module_content > { struct dylib_module fields; }; +template <> struct macho_dylib_module_content > { struct dylib_module_64 fields; }; +template <> struct macho_dylib_module_content > { struct dylib_module_64 fields; }; + +template +class macho_dylib_module { +public: + uint32_t module_name() const INLINE { return E::get32(module.fields.module_name); } + void set_module_name(uint32_t value) INLINE { E::set32(module.fields.module_name, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(module.fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(module.fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(module.fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(module.fields.nextdefsym, value); } + + uint32_t irefsym() const INLINE { return E::get32(module.fields.irefsym); } + void set_irefsym(uint32_t value) INLINE { E::set32(module.fields.irefsym, value); } + + uint32_t nrefsym() const INLINE { return E::get32(module.fields.nrefsym); } + void set_nrefsym(uint32_t value) INLINE { E::set32(module.fields.nrefsym, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(module.fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(module.fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(module.fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(module.fields.nlocalsym, value); } + + uint32_t iextrel() const INLINE { return E::get32(module.fields.iextrel); } + void set_iextrel(uint32_t value) INLINE { E::set32(module.fields.iextrel, value); } + + uint32_t nextrel() const INLINE { return E::get32(module.fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(module.fields.nextrel, value); } + + uint16_t iinit() const INLINE { return E::get32(module.fields.iinit_iterm) & 0xFFFF; } + uint16_t iterm() const INLINE { return E::get32(module.fields.iinit_iterm) > 16; } + void set_iinit_iterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.iinit_iterm, (term<<16) | (init &0xFFFF)); } + + uint16_t ninit() const INLINE { return E::get32(module.fields.ninit_nterm) & 0xFFFF; } + uint16_t nterm() const INLINE { return E::get32(module.fields.ninit_nterm) > 16; } + void set_ninit_nterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.ninit_nterm, (term<<16) | (init &0xFFFF)); } + + uint64_t objc_module_info_addr() const INLINE { return P::getP(module.fields.objc_module_info_addr); } + void set_objc_module_info_addr(uint64_t value) INLINE { P::setP(module.fields.objc_module_info_addr, value); } + + uint32_t objc_module_info_size() const INLINE { return E::get32(module.fields.objc_module_info_size); } + void set_objc_module_info_size(uint32_t value) INLINE { E::set32(module.fields.objc_module_info_size, value); } + + + typedef typename P::E E; +private: + macho_dylib_module_content

module; +}; + + +// +// mach-o dylib_reference entry +// +template +class macho_dylib_reference { +public: + uint32_t isym() const INLINE { return E::getBits(fields, 0, 24); } + void set_isym(uint32_t value) INLINE { E::setBits(fields, value, 0, 24); } + + uint8_t flags() const INLINE { return E::getBits(fields, 24, 8); } + void set_flags(uint8_t value) INLINE { E::setBits(fields, value, 24, 8); } + + typedef typename P::E E; +private: + uint32_t fields; +}; + + + +// +// mach-o two-level hints load command +// +template +class macho_dylib_table_of_contents { +public: + uint32_t symbol_index() const INLINE { return E::get32(fields.symbol_index); } + void set_symbol_index(uint32_t value) INLINE { E::set32(fields.symbol_index, value); } + + uint32_t module_index() const INLINE { return E::get32(fields.module_index); } + void set_module_index(uint32_t value) INLINE { E::set32(fields.module_index, value); } + + typedef typename P::E E; +private: + dylib_table_of_contents fields; +}; + + + +// +// mach-o two-level hints load command +// +template +class macho_twolevel_hints_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t offset() const INLINE { return E::get32(fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } + + uint32_t nhints() const INLINE { return E::get32(fields.nhints); } + void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } + + typedef typename P::E E; +private: + twolevel_hints_command fields; +}; + + +// +// mach-o threads load command +// +template +class macho_thread_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t flavor() const INLINE { return E::get32(fields_flavor); } + void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } + + uint32_t count() const INLINE { return E::get32(fields_count); } + void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } + + uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } + void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } + + typedef typename P::E E; + typedef typename P::uint_t pint_t; +private: + struct thread_command fields; + uint32_t fields_flavor; + uint32_t fields_count; + pint_t thread_registers[1]; +}; + + +// +// mach-o misc data +// +template +class macho_linkedit_data_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } + void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } + + uint32_t datasize() const INLINE { return E::get32(fields.datasize); } + void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } + + + typedef typename P::E E; +private: + struct linkedit_data_command fields; +}; + + +// +// mach-o rpath +// +template +class macho_rpath_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t path_offset() const INLINE { return E::get32(fields.path.offset); } + void set_path_offset(uint32_t value) INLINE { E::set32(fields.path.offset, value); } + + const char* path() const INLINE { return (const char*)&fields + path_offset(); } + void set_path_offset() INLINE { set_path_offset(sizeof(fields)); } + + + typedef typename P::E E; +private: + struct rpath_command fields; +}; + + + +// +// mach-o symbol table entry +// +template struct macho_nlist_content {}; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; + +template +class macho_nlist { +public: + uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } + void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } + + uint8_t n_type() const INLINE { return entry.fields.n_type; } + void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } + + uint8_t n_sect() const INLINE { return entry.fields.n_sect; } + void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } + + uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } + void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } + + uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } + void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } + + typedef typename P::E E; +private: + macho_nlist_content

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) { if ( x > 0x00FFFFFF ) throw "scattered reloc r_address too large"; + uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + + +// +// mach-o encyrption info load command +// +template +class macho_encryption_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); } + void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); } + + uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); } + void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); } + + uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); } + void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); } + + typedef typename P::E E; +private: + encryption_info_command fields; +}; + + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/ld64/src/MachOReaderDylib.hpp b/ld64/src/MachOReaderDylib.hpp new file mode 100644 index 0000000..eb8e844 --- /dev/null +++ b/ld64/src/MachOReaderDylib.hpp @@ -0,0 +1,926 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_DYLIB_MACH_O__ +#define __OBJECT_FILE_DYLIB_MACH_O__ + +#include +#include +#include +#include + + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ObjectFile.h" + +// +// +// To implement architecture xxx, you must write template specializations for the following method: +// Reader::validFile() +// +// + + + + +namespace mach_o { +namespace dylib { + + +// forward reference +template class Reader; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } +private: + const char* fName; +}; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template +class ExportAtom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const { return fName; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual bool dontDeadStrip() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const {} + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + typedef typename A::P P; + + ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal) + : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {} + virtual ~ExportAtom() {} + + ObjectFile::Reader& fOwner; + const char* fName; + uint32_t fOrdinal; + bool fWeakDefinition; + + static std::vector fgEmptyReferenceList; + static Segment fgImportSegment; +}; + +template +Segment ExportAtom::fgImportSegment("__LINKEDIT"); + +template +std::vector ExportAtom::fgEmptyReferenceList; + + + +class ImportReference : public ObjectFile::Reference +{ +public: + ImportReference(const char* name) + : fTarget(NULL), fTargetName(strdup(name)) {} + virtual ~ImportReference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return 0; } + virtual uint64_t getFixUpOffset() const { return 0; } + virtual const char* getTargetName() const { return fTargetName; } + virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } + virtual uint64_t getTargetOffset() const { return 0; } + virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } + virtual const char* getFromTargetName() const { return NULL; } + virtual uint64_t getFromTargetOffset() const { return 0; } + virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; } + virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } + virtual const char* getDescription() const { return "dylib import reference"; } + +private: + const ObjectFile::Atom* fTarget; + const char* fTargetName; +}; + + +// +// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace +// the imports of all flat dylibs are checked +// +template +class ImportAtom : public ObjectFile::Atom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return "flat-imports"; } + virtual const char* getDisplayName() const { return "flat_namespace undefines"; } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return false; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return "._imports"; } + virtual Segment& getSegment() const { return fgImportSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const {} + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + typedef typename A::P P; + + ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector& imports) + : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); } + virtual ~ImportAtom() {} + void makeReferences(std::vector& imports) { + for (std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { + fReferences.push_back(new ImportReference(*it)); + } + } + + + ObjectFile::Reader& fOwner; + uint32_t fOrdinal; + std::vector fReferences; + + static Segment fgImportSegment; +}; + +template +Segment ImportAtom::fgImportSegment("__LINKEDIT"); + + + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + const DynamicLibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, + uint32_t ordinalBase); + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms(); + virtual std::vector* getJustInTimeAtomsFor(const char* name); + virtual std::vector* getStabs() { return NULL; } + virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; } + virtual const char* getInstallPath() { return fDylibInstallPath; } + virtual uint32_t getTimestamp() { return fDylibTimeStamp; } + virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } + virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } + virtual void processIndirectLibraries(DylibHander* handler); + virtual void setExplicitlyLinked() { fExplicitlyLinked = true; } + virtual bool explicitlyLinked() { return fExplicitlyLinked; } + virtual bool implicitlyLinked() { return fImplicitlyLinked; } + virtual bool providedExportAtom() { return fProvidedAtom; } + virtual const char* parentUmbrella() { return fParentUmbrella; } + virtual std::vector* getAllowableClients(); + virtual bool hasWeakExternals() { return fHasWeakExports; } + virtual bool isLazyLoadedDylib() { return fLazyLoaded; } + + virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } + +protected: + + struct ReExportChain { ReExportChain* prev; Reader* reader; }; + + void assertNoReExportCycles(ReExportChain*); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + typedef typename NameToAtomMap::iterator NameToAtomMapIterator; + + struct PathAndFlag { const char* path; bool reExport; }; + + bool isPublicLocation(const char* path); + void addSymbol(const char* name, bool weak, uint32_t ordinal); + + const char* fPath; + const char* fParentUmbrella; + std::vector fAllowableClients; + const char* fDylibInstallPath; + uint32_t fDylibTimeStamp; + uint32_t fDylibtCurrentVersion; + uint32_t fDylibCompatibilityVersion; + uint32_t fReExportedOrdinal; + std::vector fDependentLibraryPaths; + NameToAtomMap fAtoms; + NameSet fIgnoreExports; + bool fNoRexports; + bool fHasWeakExports; + const bool fLinkingFlat; + const bool fLinkingMainExecutable; + bool fExplictReExportFound; + bool fExplicitlyLinked; + bool fImplicitlyLinked; + bool fProvidedAtom; + bool fImplicitlyLinkPublicDylibs; + bool fLazyLoaded; + ObjectFile::Reader::ObjcConstraint fObjcContraint; + std::vector fReExportedChildren; + const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin; + std::vector fFlatImports; + + static bool fgLogHashtable; + static std::vector fgEmptyAtomList; +}; + +template +std::vector Reader::fgEmptyAtomList; +template +bool Reader::fgLogHashtable = false; + + +template +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + const DynamicLibraryOptions& dylibOptions, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), + fDylibCompatibilityVersion(0), fLinkingFlat(options.fFlatNamespace), + fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), + fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), + fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), + fObjcContraint(ObjectFile::Reader::kObjcNone), + fDeploymentVersionMin(options.fVersionMin) +{ + // sanity check + if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) + throw "not a valid mach-o object file"; + + fPath = strdup(path); + + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

) + header->sizeofcmds()); + + // write out path for -whatsloaded option + if ( options.fLogAllFiles ) + printf("%s\n", path); + + if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) ) + warning("using -root_safe but linking against %s which is not root safe", path); + + if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) ) + warning("using -setuid_safe but linking against %s which is not setuid safe", path); + + // a "blank" stub has zero load commands + if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { + // no further processing needed + munmap((caddr_t)fileContent, fileLength); + return; + } + + + // optimize the case where we know there is no reason to look at indirect dylibs + fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); + fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); + bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; + + // pass 1 builds list of all dependent libraries + const macho_load_command

* cmd = cmds; + if ( trackDependentLibraries ) { + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_REEXPORT_DYLIB: + fExplictReExportFound = true; + // fall into next case + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + PathAndFlag entry; + entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); + entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + fDependentLibraryPaths.push_back(entry); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); + } + } + + // pass 2 determines re-export info + const macho_dysymtab_command

* dynamicInfo = NULL; + const macho_nlist

* symbolTable = NULL; + const char* strings = NULL; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); + strings = (char*)header + symtab->stroff(); + } + break; + case LC_DYSYMTAB: + dynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_ID_DYLIB: + { + macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; + fDylibInstallPath = strdup(dylibID->name()); + fDylibTimeStamp = dylibID->timestamp(); + fDylibtCurrentVersion = dylibID->current_version(); + fDylibCompatibilityVersion = dylibID->compatibility_version(); + } + break; + case LC_SUB_UMBRELLA: + if ( trackDependentLibraries ) { + const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + } + break; + case LC_SUB_LIBRARY: + if ( trackDependentLibraries) { + const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + } + break; + case LC_SUB_FRAMEWORK: + fParentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + break; + case macho_segment_command

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

*)cmd)->segname(), "__OBJC") == 0 ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strcmp(sect->sectname(), "__image_info") == 0 ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + fObjcContraint = ObjectFile::Reader::kObjcGC; + else if ( (flags & 2) == 2 ) + fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + else + fObjcContraint = ObjectFile::Reader::kObjcRetainRelease; + } + else if ( sect->size() > 0 ) { + warning("can't parse __OBJC/__image_info section in %s", fPath); + } + } + } + } + } + + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); + } + + // Process the rest of the commands here. + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SUB_CLIENT: + const char *temp = strdup(((macho_sub_client_command

*)cmd)->client()); + fAllowableClients.push_back(temp); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + + // validate minimal load commands + if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", path); + if ( symbolTable == NULL ) + throw "binary missing LC_SYMTAB load command"; + if ( dynamicInfo == NULL ) + throw "binary missing LC_DYSYMTAB load command"; + + // if linking flat and this is a flat dylib, create one atom that references all imported symbols + if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { + std::vector importNames; + importNames.reserve(dynamicInfo->nundefsym()); + const macho_nlist

* start = &symbolTable[dynamicInfo->iundefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nundefsym()]; + for (const macho_nlist

* sym=start; sym < end; ++sym) { + importNames.push_back(&strings[sym->n_strx()]); + } + fFlatImports.push_back(new ImportAtom(*this, ordinalBase++, importNames)); + } + + // build hash table + if ( dynamicInfo->tocoff() == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); + const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; + fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + uint32_t index = ordinalBase; + for (const macho_nlist

* sym=start; sym < end; ++sym, ++index) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, index); + } + fReExportedOrdinal = index; + } + else { + int32_t count = dynamicInfo->ntoc(); + fAtoms.resize(count); // set initial bucket count + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, path); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + dynamicInfo->tocoff()); + for (int32_t i = 0; i < count; ++i) { + const uint32_t index = E::get32(toc[i].symbol_index); + const macho_nlist

* sym = &symbolTable[index]; + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, ordinalBase+i); + } + fReExportedOrdinal = ordinalBase + count; + } + + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + + + +template +void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) +{ + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != NULL ) { + ObjectFile::ReaderOptions::VersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinUnset; + if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { + switch ( symCond[6] - '0' ) { + case 0: + case 1: + symVersionCondition = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + symVersionCondition = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + symVersionCondition = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + symVersionCondition = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + symVersionCondition = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + symVersionCondition = ObjectFile::ReaderOptions::k10_6; + break; + } + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weak, ordinal); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } + } + } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol version: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->getPath()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( fIgnoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = weak; + bucket.ordinal = ordinal; + if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); + fAtoms[strdup(name)] = bucket; + } +} + + +template +std::vector& Reader::getAtoms() +{ + return fFlatImports; +} + + +template +std::vector* Reader::getJustInTimeAtomsFor(const char* name) +{ + std::vector* atoms = NULL; + + NameToAtomMapIterator pos = fAtoms.find(name); + if ( pos != fAtoms.end() ) { + if ( pos->second.atom == NULL ) { + // instantiate atom and update hash table + pos->second.atom = new ExportAtom(*this, name, pos->second.weak, pos->second.ordinal); + fProvidedAtom = true; + if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); + } + // return a vector of one atom + atoms = new std::vector; + atoms->push_back(pos->second.atom); + } + else { + if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); + // if not supposed to ignore this export, see if I have it + if ( fIgnoreExports.count(name) == 0 ) { + // look in children that I re-export + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); + std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); + if ( childAtoms != NULL ) { + // make a new atom that says this reader is the owner + bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); + // return a vector of one atom + ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); + fProvidedAtom = true; + atoms = new std::vector; + atoms->push_back(newAtom); + delete childAtoms; + return atoms; + } + } + } + } + return atoms; +} + + + +template +bool Reader::isPublicLocation(const char* path) +{ + // -no_implicit_dylibs disables this optimization + if ( ! fImplicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&path[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &path[27]; + if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void Reader::processIndirectLibraries(DylibHander* handler) +{ + if ( fLinkingFlat ) { + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + handler->findDylib(it->path, this->getPath()); + } + } + else if ( fNoRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + if ( it->reExport ) { + //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); + if ( isPublicLocation(child->getInstallPath()) ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( this->explicitlyLinked() || this->implicitlyLinked() ) { + //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); + ((Reader*)child)->setImplicitlyLinked(); + } + else + fReExportedChildren.push_back(child); + } + else { + // add all child's symbols to me + fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } + } + else if ( !fExplictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); + const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->getPath(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = NULL; + chain.reader = this; + this->assertNoReExportCycles(&chain); +} + +template +void Reader::assertNoReExportCycles(ReExportChain* prev) +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.reader = this; + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + ObjectFile::Reader* child = *it; + // check child is not already in chain + for (ReExportChain* p = prev; p != NULL; p = p->prev) { + if ( p->reader == child ) { + throwf("cycle in dylib re-exports with %s", child->getPath()); + } + } + ((Reader*)(*it))->assertNoReExportCycles(&chain); + } +} + + +template +std::vector* Reader::getAllowableClients() +{ + std::vector* result = new std::vector; + for (typename std::vector::iterator it = fAllowableClients.begin(); + it != fAllowableClients.end(); + it++) { + result->push_back(*it); + } + return (fAllowableClients.size() != 0 ? result : NULL); +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +}; // namespace dylib +}; // namespace mach_o + + +#endif // __OBJECT_FILE_DYLIB_MACH_O__ diff --git a/ld64/src/MachOReaderRelocatable.hpp b/ld64/src/MachOReaderRelocatable.hpp new file mode 100644 index 0000000..73c58e2 --- /dev/null +++ b/ld64/src/MachOReaderRelocatable.hpp @@ -0,0 +1,4583 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OBJECT_FILE_MACH_O__ +#define __OBJECT_FILE_MACH_O__ + +#include +#include +#include +#include + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ObjectFile.h" +#include "dwarf2.h" +#include "debugline.h" + + +// +// +// To implement architecture xxx, you must write template specializations for the following six methods: +// Reader::validFile() +// Reader::validSectionType() +// Reader::addRelocReference() +// Reference::getDescription() +// +// + + + +extern __attribute__((noreturn)) void throwf(const char* format, ...); +extern void warning(const char* format, ...); + +namespace mach_o { +namespace relocatable { + + + +class ReferenceSorter +{ +public: + bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right) + { + return ( left->getFixUpOffset() < right->getFixUpOffset() ); + } +}; + + +// forward reference +template class Reader; + +struct AtomAndOffset +{ + AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {} + AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} + ObjectFile::Atom* atom; + uint32_t offset; +}; + + +template +class Reference : public ObjectFile::Reference +{ +public: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + + Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget); + Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget); + Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset); + + virtual ~Reference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const; + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const; + virtual uint8_t getKind() const { return (uint8_t)fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } + virtual const char* getTargetName() const { return (fToTargetName != NULL) ? fToTargetName : fToTarget.atom->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } + virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); } + virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } + virtual const char* getFromTargetName() const { return (fFromTargetName != NULL) ? fFromTargetName : fFromTarget.atom->getName(); } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } + virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } + virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } + virtual void setFromTargetName(const char* name) { fFromTargetName = name; } + virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } + virtual const char* getDescription() const; + virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } + + static bool fgForFinalLinkedImage; + +private: + pint_t fFixUpOffsetInSrc; + AtomAndOffset fToTarget; + AtomAndOffset fFromTarget; + const char* fToTargetName; + const char* fFromTargetName; + Kinds fKind; + +}; + +template bool Reference::fgForFinalLinkedImage = true; + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) + : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL), + fKind(kind) +{ + // make reference a by-name unless: + // - the reference type is only used with direct references + // - the target is translation unit scoped + // - the target kind is not regular (is weak or tentative) + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) + && (toTarget.atom != at.atom) ) { + fToTargetName = toTarget.atom->getName(); + //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName()); + fToTarget.atom = NULL; + } + ((class BaseAtom*)at.atom)->addReference(this); + //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset); +} + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget) + : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget), + fToTargetName(NULL), fFromTargetName(NULL), fKind(kind) +{ + // make reference a by-name where needed + if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) + && (toTarget.atom != at.atom) ) { + fToTargetName = toTarget.atom->getName(); + fToTarget.atom = NULL; + } + ((class BaseAtom*)at.atom)->addReference(this); + //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind, + // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset); +} + +template +Reference::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset) + : fFixUpOffsetInSrc(at.offset), + fToTargetName(toName), fFromTargetName(NULL), fKind(kind) +{ + fToTarget.offset = toOffset; + ((class BaseAtom*)at.atom)->addReference(this); +} + +template +ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const +{ + if ( fgForFinalLinkedImage ) { + if ( (fKind == A::kDtraceProbe) || (fKind == A::kDtraceProbeSite) || (fKind == A::kDtraceIsEnabledSite) || (fKind == A::kDtraceTypeReference) ) + return ObjectFile::Reference::kDontBind; + } + if ( fToTarget.atom == NULL ) + return ObjectFile::Reference::kUnboundByName; + if ( fToTargetName == NULL ) + return ObjectFile::Reference::kBoundDirectly; + else + return ObjectFile::Reference::kBoundByName; +} + +template +ObjectFile::Reference::TargetBinding Reference::getFromTargetBinding() const +{ + if ( fFromTarget.atom == NULL ) { + if ( fFromTargetName == NULL ) + return ObjectFile::Reference::kDontBind; + else + return ObjectFile::Reference::kUnboundByName; + } + else { + if ( fFromTargetName == NULL ) + return ObjectFile::Reference::kBoundDirectly; + else + return ObjectFile::Reference::kBoundByName; + } +} + + + +template +class Segment : public ObjectFile::Segment +{ +public: + Segment(const macho_section* sect); + virtual const char* getName() const { return fSection->segname(); } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } +private: + const macho_section* fSection; + bool fWritable; + bool fExecutable; +}; + +template +Segment::Segment(const macho_section* sect) + : fSection(sect), fWritable(true), fExecutable(false) +{ + if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { + fWritable = false; + fExecutable = true; + } + else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { + fWritable = true; + fExecutable = true; + } +} + + +class DataSegment : public ObjectFile::Segment +{ +public: + virtual const char* getName() const { return "__DATA"; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return true; } + virtual bool isContentExecutable() const { return false; } + + static DataSegment fgSingleton; +}; + +DataSegment DataSegment::fgSingleton; + +class LinkEditSegment : public ObjectFile::Segment +{ +public: + virtual const char* getName() const { return "__LINKEDIT"; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return false; } + + static LinkEditSegment fgSingleton; +}; + +LinkEditSegment LinkEditSegment::fgSingleton; + +class BaseAtom : public ObjectFile::Atom +{ +public: + BaseAtom() : fStabsStartIndex(0), fStabsCount(0) {} + + virtual void setSize(uint64_t size) = 0; + virtual void addReference(ObjectFile::Reference* ref) = 0; + virtual void sortReferences() = 0; + virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; + virtual uint64_t getObjectAddress() const = 0; + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual void setOrdinal(uint32_t value) { fOrdinal = value; } + virtual const void* getSectionRecord() const = 0; + virtual bool isAlias() const { return false; } + + uint32_t fStabsStartIndex; + uint32_t fStabsCount; + uint32_t fOrdinal; +}; + +class BaseAtomSorter +{ +public: + bool operator()(const class BaseAtom* left, const class BaseAtom* right) { + if ( left == right ) + return false; + uint64_t leftAddr = left->getObjectAddress(); + uint64_t rightAddr = right->getObjectAddress(); + if ( leftAddr < rightAddr ) { + return true; + } + else if ( leftAddr > rightAddr ) { + return false; + } + else { + // if they have same address, one might be the end of a section and the other the start of the next section + const void* leftSection = left->getSectionRecord(); + const void* rightSection = right->getSectionRecord(); + if ( leftSection != rightSection ) { + return ( leftSection < rightSection ); + } + // if they have same address and section, one might be an alias + bool leftAlias = left->isAlias(); + bool rightAlias = right->isAlias(); + if ( leftAlias && rightAlias ) { + // sort multiple aliases for same address first by scope + ObjectFile::Atom::Scope leftScope = left->getScope(); + ObjectFile::Atom::Scope rightScope = right->getScope(); + if ( leftScope != rightScope ) { + return ( leftScope < rightScope ); + } + // sort multiple aliases for same address then by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + } + else if ( leftAlias ) { + return true; + } + else if ( rightAlias ) { + return false; + } + else { + // they must be tentative defintions + switch ( left->getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + // sort tentative definitions by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + case ObjectFile::Atom::kAbsoluteSymbol: + // sort absolute symbols with same address by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + default: + // hack for rdar://problem/5102873 + if ( !left->isZeroFill() || !right->isZeroFill() ) + warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); + break; + } + } + } + return false; + } +}; + + +// +// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry +// pointing to it. A C function or global variable is represented by one of these atoms. +// +// +template +class SymbolAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) + ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } + virtual bool dontDeadStrip() const; + virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } + virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } + virtual uint64_t getSize() const { return fSize; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual Segment& getSegment() const { return *fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getLineInfo() const { return (std::vector*)&fLineInfo; } + virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size); + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } + virtual uint64_t getObjectAddress() const { return fAddress; } + virtual const void* getSectionRecord() const { return (const void*)fSection; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + SymbolAtom(Reader&, const macho_nlist

*, const macho_section

*); + virtual ~SymbolAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + pint_t fAddress; + pint_t fSize; + const macho_section

* fSection; + Segment* fSegment; + ReferenceVector fReferences; + std::vector fLineInfo; + ObjectFile::Atom::Scope fScope; + SymbolTableInclusion fSymbolTableInclusion; + ObjectFile::Alignment fAlignment; +}; + + +template +SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const macho_section

* section) + : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fAlignment(0) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( (type & N_TYPE) == N_SECT ) { + // real definition + fSegment = new Segment(fSection); + fAddress = fSymbol->n_value(); + pint_t sectionStartAddr = section->addr(); + pint_t sectionEndAddr = section->addr()+section->size(); + if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) { + throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX", + this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(), + (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) ); + } + } + else { + warning("unknown symbol type: %d", type); + } + + //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress); + // support for .o files built with old ld64 + if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) { + const char* name = this->getName(); + const int nameLen = strlen(name); + if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) { + // switch symbol to point at name that does not have trailing $stub + char correctName[nameLen]; + strncpy(correctName, name, nameLen-5); + correctName[nameLen-5] = '\0'; + const macho_nlist

* symbolsStart = fOwner.fSymbols; + const macho_nlist

* symbolsEnd = &symbolsStart[fOwner.fSymbolCount]; + for(const macho_nlist

* s = symbolsStart; s < symbolsEnd; ++s) { + if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) { + fSymbol = s; + break; + } + } + } + } + // support for labeled stubs + switch ( section->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + setSize(section->reserved2()); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + setSize(sizeof(pint_t)); + break; + case S_4BYTE_LITERALS: + setSize(4); + break; + case S_8BYTE_LITERALS: + setSize(8); + break; + case S_16BYTE_LITERALS: + setSize(16); + break; + case S_CSTRING_LITERALS: + setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); + break; + case S_REGULAR: + case S_ZEROFILL: + case S_COALESCED: + // size calculate later after next atom is found + break; + } + + // compute alignment + fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); + + // compute whether this atom needs to be in symbol table + if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; + } + else if ( fOwner.fOptions.fForFinalLinkedImage + && ((section->flags() & SECTION_TYPE) == S_COALESCED) + && ((section->flags() & S_ATTR_NO_TOC) == S_ATTR_NO_TOC) + && ((section->flags() & S_ATTR_STRIP_STATIC_SYMS) == S_ATTR_STRIP_STATIC_SYMS) + && (strcmp(section->sectname(), "__eh_frame") == 0) ) { + // .eh symbols exist so the linker can associate them with functions + // removing them from final linked images is a big space savings rdar://problem/4180168 + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment + fAlignment = ObjectFile::Alignment(0); + } + else if ( fOwner.fOptions.fForFinalLinkedImage + && ((section->flags() & SECTION_TYPE) == S_REGULAR) + && (strncmp(section->sectname(), "__gcc_except_tab", 16) == 0) + && (strncmp(this->getName(), "GCC_except_table", 16) == 0) ) { + // GCC_except_table* symbols don't need to exist in final linked image + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + } + else if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) { + // labels beginning with a lowercase ell are automatically removed in final linked images + // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; + } + else { + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } + + // work around malformed icc generated .o files + // if section starts with a symbol and that symbol address does not match section alignment, then force it to + if ( (section->addr() == fAddress) && (fAlignment.modulus != 0) ) + fAlignment.modulus = 0; +} + +template +bool SymbolAtom::dontDeadStrip() const +{ + // the symbol can have a no-dead-strip bit + if ( (fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 ) + return true; + // or the section can have a no-dead-strip bit + return ( fSection->flags() & S_ATTR_NO_DEAD_STRIP ); +} + + +template +const char* SymbolAtom::getSectionName() const +{ + if ( fOwner.fOptions.fForFinalLinkedImage && (strcmp(fSection->sectname(), "__textcoal_nt") == 0) ) + return "__text"; + + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +template +ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const +{ + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return ref->getTarget(); + } + return *((ObjectFile::Atom*)NULL); +} + + +class Beyond +{ +public: + Beyond(uint64_t offset) : fOffset(offset) {} + bool operator()(ObjectFile::Reference* ref) const { + return ( ref->getFixUpOffset() >= fOffset ); + } +private: + uint64_t fOffset; +}; + + +template +void SymbolAtom::setSize(uint64_t size) +{ + // when resizing, any references beyond the new size are tossed + if ( (fSize != 0) && (fReferences.size() > 0) ) + fReferences.erase(std::remove_if(fReferences.begin(), fReferences.end(), Beyond(size)), fReferences.end()); + // set new size + fSize = size; +} + +template +void SymbolAtom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + +// +// A SymbolAliasAtom represents an alternate name for a SymbolAtom +// +// +template +class SymbolAliasAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fAliasOf.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); } + virtual bool dontDeadStrip() const { return fDontDeadStrip; } + virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); } + virtual bool isThumb() const { return fAliasOf.isThumb(); } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const { return fAliasOf.getSectionName(); } + virtual Segment& getSegment() const { return (Segment&)fAliasOf.getSegment(); } + virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return fAliasOf.getAlignment(); } + virtual void copyRawContent(uint8_t buffer[]) const {} + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { } + virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } + virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } + virtual bool isAlias() const { return true; } + +protected: + typedef typename A::P P; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + SymbolAliasAtom(const char* name, const macho_nlist

*, const BaseAtom& ); + virtual ~SymbolAliasAtom() {} + + const char* fName; + const BaseAtom& fAliasOf; + ObjectFile::Atom::Scope fScope; + bool fDontDeadStrip; + ReferenceVector fReferences; +}; + + +template +SymbolAliasAtom::SymbolAliasAtom(const char* name, const macho_nlist

* symbol, const BaseAtom& aliasOf) + : fName(name), fAliasOf(aliasOf) +{ + //fprintf(stderr, "SymbolAliasAtom(%p) %s\n", this, name); + if ( symbol != NULL ) { + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + fDontDeadStrip = ((symbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); + } + else { + // aliases defined on the command line are initially global scope + fScope = ObjectFile::Atom::scopeGlobal; + fDontDeadStrip = false; + } + // add follow-on reference to real atom + new Reference(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf)); +} + + +// +// A TentativeAtom represents a C "common" or "tentative" defintion of data. +// For instance, "int foo;" is neither a declaration or a definition and +// is represented by a TentativeAtom. +// +template +class TentativeAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } + virtual bool isZeroFill() const { return true; } + virtual bool isThumb() const { return false; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) + ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } + virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } + virtual uint64_t getSize() const { return fSymbol->n_value(); } + virtual std::vector& getReferences() const { return fgNoReferences; } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } + virtual void sortReferences() { } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } + virtual const void* getSectionRecord() const { return NULL; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + friend class Reader; + + TentativeAtom(Reader&, const macho_nlist

*); + virtual ~TentativeAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + ObjectFile::Atom::Scope fScope; + static std::vector fgNoReferences; +}; + +template +std::vector TentativeAtom::fgNoReferences; + +template +TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) + : fOwner(owner), fSymbol(symbol) +{ + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { + // tentative definition + } + else { + warning("unknown symbol type: %d", type); + } + //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName()); +} + + +template +ObjectFile::Alignment TentativeAtom::getAlignment() const +{ + uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc()); + if ( alignment == 0 ) { + // common symbols align to their size + // that is, a 4-byte common aligns to 4-bytes + // if this size is not a power of two, + // then round up to the next power of two + uint64_t size = this->getSize(); + alignment = 63 - (uint8_t)__builtin_clzll(size); + if ( size != (1ULL << alignment) ) + ++alignment; + } + // limit alignment of extremely large commons to 2^15 bytes (8-page) + if ( alignment < 12 ) + return ObjectFile::Alignment(alignment); + else + return ObjectFile::Alignment(12); +} + +template +const char* TentativeAtom::getSectionName() const +{ + if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal ) + return "__common"; + else + return "._tentdef"; +} + + +template +void TentativeAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +// +// An AnonymousAtom represents compiler generated data that has no name. +// For instance, a literal C-string or a 64-bit floating point constant +// is represented by an AnonymousAtom. +// +template +class AnonymousAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fSynthesizedName; } + virtual const char* getDisplayName() const; + virtual ObjectFile::Atom::Scope getScope() const; + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } + virtual bool dontDeadStrip() const { return fDontDeadStrip; } + virtual bool isZeroFill() const; + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return fSize; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const; + virtual Segment& getSegment() const { return *fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const; + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { fSize = size; } + virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } + virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); } + virtual uint64_t getObjectAddress() const { return fAddress; } + virtual const void* getSectionRecord() const { return (const void*)fSection; } + BaseAtom* redirectTo() { return fRedirect; } + bool isWeakImportStub() { return fWeakImportStub; } + void resolveName(); + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + typedef typename std::vector*> ReferenceVector; + typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser + typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser + friend class Reader; + + AnonymousAtom(Reader&, const macho_section

*, pint_t addr, pint_t size); + virtual ~AnonymousAtom() {} + static bool cstringsHaveLabels(); + + Reader& fOwner; + const char* fSynthesizedName; + const char* fDisplayName; + const macho_section

* fSection; + pint_t fAddress; + pint_t fSize; + Segment* fSegment; + ReferenceVector fReferences; + BaseAtom* fRedirect; + bool fDontDeadStrip; + bool fWeakImportStub; + ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; + ObjectFile::Atom::Scope fScope; + ObjectFile::Atom::DefinitionKind fKind; +}; + +template +AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, pint_t addr, pint_t size) + : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), + fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), + fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition) +{ + fSegment = new Segment(fSection); + fRedirect = this; + uint8_t type = fSection->flags() & SECTION_TYPE; + //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath()); + switch ( type ) { + case S_ZEROFILL: + { + asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr); + } + break; + case S_COALESCED: + case S_REGULAR: + if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { + // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only + fSynthesizedName = ".objc_class_name_PENDING"; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + if ( fOwner.fOptions.fForFinalLinkedImage ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + else + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAsAbsolute; + fScope = ObjectFile::Atom::scopeGlobal; + } + else if ( strcmp(fSection->sectname(), "__cstring") == 0 ) { + // handle .o files created by old ld64 -r that are missing cstring section type + const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + } + else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) { + fSynthesizedName = "cfstring-pointer-name-PENDING"; + fScope = ObjectFile::Atom::scopeLinkageUnit; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + fDontDeadStrip = false; + fKind = ObjectFile::Atom::kWeakDefinition; + } + break; + case S_CSTRING_LITERALS: + { + const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } + break; + case S_4BYTE_LITERALS: + { + uint32_t value = E::get32(*(uint32_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + asprintf((char**)&fSynthesizedName, "4-byte-literal=0x%08X", value); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + } + break; + case S_8BYTE_LITERALS: + { + uint64_t value = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + asprintf((char**)&fSynthesizedName, "8-byte-literal=0x%016llX", value); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + } + break; + case S_16BYTE_LITERALS: + { + uint64_t value1 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + uint64_t value2 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 8 - section->addr())); + asprintf((char**)&fSynthesizedName, "16-byte-literal=0x%016llX,%016llX", value1, value2); + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + } + break; + case S_LITERAL_POINTERS: + { + //uint32_t literalNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); + //const char* str = (char*)(owner.fHeader) + section->offset() + literalNameAddr - section->addr(); + //asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", section->segname(), section->sectname(), str); + fSynthesizedName = "literal-pointer-name-PENDING"; + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + fDontDeadStrip = false; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + } + break; + case S_MOD_INIT_FUNC_POINTERS: + asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); + break; + case S_MOD_TERM_FUNC_POINTERS: + asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); + break; + case S_SYMBOL_STUBS: + { + uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2(); + index += fSection->reserved1(); + uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); + const macho_nlist

* sym = &fOwner.fSymbols[symbolIndex]; + uint32_t strOffset = sym->n_strx(); + // want name to not have $stub suffix, this is what automatic stub generation expects + fSynthesizedName = &fOwner.fStrings[strOffset]; + // check for weak import + fWeakImportStub = fOwner.isWeakImportSymbol(sym); + // sometimes the compiler gets confused and generates a stub to a static function + // if so, we should redirect any call to the stub to be calls to the real static function atom + if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) { + BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); + if ( staticAtom != NULL ) + fRedirect = staticAtom; + } + fKind = ObjectFile::Atom::kWeakDefinition; + // might be a spurious stub for a static function, make stub static too + if ( (sym->n_type() & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else + fScope = ObjectFile::Atom::scopeLinkageUnit; + } + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + { + fDontDeadStrip = false; + fScope = ObjectFile::Atom::scopeLinkageUnit; + uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); + index += fSection->reserved1(); + uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { + // Silly codegen with non-lazy pointer to a local symbol + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fOwner.fHeader)+fileOffset))); + // All atoms not created yet, so we need to scan symbol table + const macho_nlist

* closestSym = NULL; + const macho_nlist

* end = &fOwner.fSymbols[fOwner.fSymbolCount]; + for (const macho_nlist

* sym = fOwner.fSymbols; sym < end; ++sym) { + if ( ((sym->n_type() & N_TYPE) == N_SECT) + && ((sym->n_type() & N_STAB) == 0) ) { + if ( sym->n_value() == nonLazyPtrValue ) { + const char* name = &fOwner.fStrings[sym->n_strx()]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + // add direct reference to target later, because its atom may not be constructed yet + fOwner.fLocalNonLazys.push_back(this); + fScope = ObjectFile::Atom::scopeTranslationUnit; + return; + } + else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) { + closestSym = sym; + } + } + } + // add direct reference to target later, because its atom may not be constructed yet + if ( closestSym != NULL ) { + const char* name = &fOwner.fStrings[closestSym->n_strx()]; + char* str; + asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value()); + fSynthesizedName = str; + } + else { + fSynthesizedName = "$interior$non_lazy_ptr"; + } + fScope = ObjectFile::Atom::scopeTranslationUnit; + fOwner.fLocalNonLazys.push_back(this); + return; + } + const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; + const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; + char* str = new char[strlen(name)+16]; + strcpy(str, name); + if ( type == S_LAZY_SYMBOL_POINTERS ) + strcat(str, "$lazy_ptr"); + else + strcat(str, "$non_lazy_ptr"); + fSynthesizedName = str; + + // optimize __IMPORT segment out of i386 dyld or if -slow_stubs is used + if ( (fOwner.fOptions.fForDyld || fOwner.fOptions.fSlowx86Stubs) && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { + macho_section

* dummySection = new macho_section

(*fSection); + dummySection->set_segname("__DATA"); + dummySection->set_sectname("__nl_symbol_ptr"); + fSection = dummySection; + fSegment = new Segment(fSection); + } + + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) + fKind = ObjectFile::Atom::kWeakDefinition; + + if ( (targetSymbol->n_type() & N_EXT) == 0 ) { + // target is translation unit scoped, so add direct reference to target + //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); + new Reference(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value())); + } + else { + if ( fOwner.isWeakImportSymbol(targetSymbol) ) + new Reference(A::kPointerWeakImport, AtomAndOffset(this), name, 0); + else + new Reference(A::kPointer, AtomAndOffset(this), name, 0); + } + } + break; + default: + throwf("section type %d not supported with address=0x%08X", type, addr); + } + //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); +} + +// x86_64 uses L labels on cstrings to allow relocs with addends +template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } +template bool AnonymousAtom::cstringsHaveLabels() { return false; } + + +template +void AnonymousAtom::resolveName() +{ + if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) { + std::vector& references = this->getReferences(); + // references are not yet sorted, so scan the vector + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { + const char* superStr = (*rit)->getTargetName(); + if ( strncmp(superStr, "cstring=", 8) == 0 ) { + const char* superClassName; + asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]); + new Reference(A::kNoFixUp, AtomAndOffset(this), superClassName, 0); + } + break; + } + } + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { + const char* classStr = (*rit)->getTargetName(); + if ( strncmp(classStr, "cstring=", 8) == 0 ) { + asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]); + } + break; + } + } + } + else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) { + std::vector& references = this->getReferences(); + if ( references.size() < 1 ) + throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname()); + ObjectFile::Reference* ref = references[0]; + const char* str = ref->getTargetName(); + if ( strncmp(str, "cstring=", 8) == 0 ) { + asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]); + } + } + else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { + // references are not yet sorted, so scan the vector + std::vector& references = this->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { + const char* superStr = (*rit)->getTargetName(); + if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) { + asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]); + } + else { + // compiled with -fwritable-strings or a non-ASCII string + ObjectFile::Atom& stringDataAtom = (*rit)->getTarget(); + uint8_t buffer[stringDataAtom.getSize()]; + stringDataAtom.copyRawContent(buffer); + fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable + fScope = ObjectFile::Atom::scopeTranslationUnit; + fSynthesizedName = "cfstring-not-coalesable"; + } + break; + } + } + } +} + + +template +const char* AnonymousAtom::getDisplayName() const +{ + if ( fSynthesizedName != NULL ) + return fSynthesizedName; + + if ( fDisplayName != NULL ) + return fDisplayName; + + if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); + } + else { + asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); + } + return fDisplayName; +} + + +template +ObjectFile::Atom::Scope AnonymousAtom::getScope() const +{ + return fScope; +} + + +template +bool AnonymousAtom::isZeroFill() const +{ + return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL ); +} + + +template +const char* AnonymousAtom::getSectionName() const +{ + if ( strlen(fSection->sectname()) > 15 ) { + static char temp[18]; + strncpy(temp, fSection->sectname(), 16); + temp[17] = '\0'; + return temp; + } + return fSection->sectname(); +} + +template +ObjectFile::Alignment AnonymousAtom::getAlignment() const +{ + switch ( fSection->flags() & SECTION_TYPE ) { + case S_4BYTE_LITERALS: + return ObjectFile::Alignment(2); + case S_8BYTE_LITERALS: + return ObjectFile::Alignment(3); + case S_16BYTE_LITERALS: + return ObjectFile::Alignment(4); + case S_NON_LAZY_SYMBOL_POINTERS: + return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t))); + case S_CSTRING_LITERALS: + if ( ! fOwner.fOptions.fForFinalLinkedImage ) + return ObjectFile::Alignment(fSection->align()); + default: + return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); + } +} + + +template +ObjectFile::Atom& AnonymousAtom::getFollowOnAtom() const +{ + for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { + Reference* ref = *it; + if ( ref->getKind() == A::kFollowOn ) + return ref->getTarget(); + } + return *((ObjectFile::Atom*)NULL); +} + +template +void AnonymousAtom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( isZeroFill() ) + bzero(buffer, fSize); + else { + uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; + memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); + } +} + + +// +// An AbsoluteAtom represents an N_ABS symbol which can only be created in +// assembly language and usable by static executables such as the kernel/ +// +template +class AbsoluteAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } + virtual const char* getDisplayName() const { return getName(); } + virtual ObjectFile::Atom::Scope getScope() const { return fScope; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; } + virtual bool dontDeadStrip() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgNoReferences; } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const { return "._absolute"; } + virtual ObjectFile::Segment& getSegment() const { return LinkEditSegment::fgSingleton; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } + virtual void sortReferences() { } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } + virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } + virtual const void* getSectionRecord() const { return NULL; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + friend class Reader; + + AbsoluteAtom(Reader&, const macho_nlist

*); + virtual ~AbsoluteAtom() {} + + Reader& fOwner; + const macho_nlist

* fSymbol; + ObjectFile::Atom::Scope fScope; + static std::vector fgNoReferences; +}; + +template +std::vector AbsoluteAtom::fgNoReferences; + +template +AbsoluteAtom::AbsoluteAtom(Reader& owner, const macho_nlist

* symbol) + : fOwner(owner), fSymbol(symbol) +{ + // store absolute adress in fSectionOffset + fSectionOffset = symbol->n_value(); + // compute scope + uint8_t type = symbol->n_type(); + if ( (type & N_EXT) == 0 ) + fScope = ObjectFile::Atom::scopeTranslationUnit; + else if ( (type & N_PEXT) != 0 ) + fScope = ObjectFile::Atom::scopeLinkageUnit; + else + fScope = ObjectFile::Atom::scopeGlobal; + //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName()); +} + + + +template +class Reader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent); + Reader(const uint8_t* fileContent, const char* path, time_t modTime, + const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); + virtual ~Reader() {} + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return fModTime; } + virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; } + virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return &fStabs; } + virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; } + virtual uint32_t updateCpuConstraint(uint32_t current); + virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); } + virtual bool objcReplacementClasses(){ return fReplacementClasses; } + virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; } + + bool getTranslationUnitSource(const char** dir, const char** name) const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + //typedef typename std::vector*> AtomVector; + //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser + typedef typename A::ReferenceKinds Kinds; + friend class AnonymousAtom; + friend class TentativeAtom; + friend class AbsoluteAtom; + friend class SymbolAtom; + typedef std::map AddrToAtomMap; + + void addReferencesForSection(const macho_section

* sect); + bool addRelocReference(const macho_section

* sect, const macho_relocation_info

* reloc); + bool addRelocReference_powerpc(const macho_section

* sect, const macho_relocation_info

* reloc); + bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); + static bool isWeakImportSymbol(const macho_nlist

* sym); + static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); + static const char* assureFullPath(const char* path); + AtomAndOffset findAtomAndOffset(pint_t addr); + AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr); + Reference* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr); + Reference* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr); + Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr); + Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr); + Reference* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset); + Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); + Reference* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset); + void validSectionType(uint8_t type); + void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName); + void setCpuConstraint(uint32_t cpusubtype); + + BaseAtom* findAtomByName(const char*); + + const char* fPath; + time_t fModTime; + uint32_t fOrdinalBase; + const ObjectFile::ReaderOptions& fOptions; + const macho_header

* fHeader; + const char* fStrings; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const macho_segment_command

* fSegment; + const uint32_t* fIndirectTable; + std::vector fAtoms; + AddrToAtomMap fAddrToAtom; + AddrToAtomMap fAddrToAbsoluteAtom; + std::vector*> fLocalNonLazys; + std::vector*> fAtomsPendingAName; + std::set*> fSectionsWithAtomsPendingAName; + std::vector fDtraceProviderInfo; + ObjectFile::Reader::DebugInfoKind fDebugInfo; + bool fHasUUID; + const macho_section

* fDwarfDebugInfoSect; + const macho_section

* fDwarfDebugAbbrevSect; + const macho_section

* fDwarfDebugLineSect; + const char* fDwarfTranslationUnitDir; + const char* fDwarfTranslationUnitFile; + std::map fDwarfIndexToFile; + std::vector fStabs; + bool fAppleObjc; + bool fHasDTraceProbes; + bool fHaveIndirectSymbols; + bool fReplacementClasses; + bool fHasLongBranchStubs; + ObjectFile::Reader::ObjcConstraint fObjConstraint; + uint32_t fCpuConstraint; +}; + +template +Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) + : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header

*)fileContent), + fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), + fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), + fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), + fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), + fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a valid mach-o object file"; + + Reference::fgForFinalLinkedImage = options.fForFinalLinkedImage; + + // write out path for -t or -whatsloaded option + if ( options.fLogObjectFiles || options.fLogAllFiles ) + printf("%s\n", path); + + // cache intersting pointers + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; + uint32_t undefinedStartIndex = 0; + uint32_t undefinedEndIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)header + symtab->symoff()); + fStrings = (char*)header + symtab->stroff(); + if ( undefinedEndIndex == 0 ) { + undefinedStartIndex = 0; + undefinedEndIndex = symtab->nsyms(); + } + } + break; + case LC_DYSYMTAB: + { + const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); + undefinedStartIndex = dsymtab->iundefsym(); + undefinedEndIndex = undefinedStartIndex + dsymtab->nundefsym(); + } + break; + case LC_UUID: + fHasUUID = true; + break; + + default: + if ( cmd->cmd() == macho_segment_command

::CMD ) { + fSegment = (macho_segment_command

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

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); + } + + // if there are no load commands, then this file has no content, so no atoms + if ( header->ncmds() < 1 ) + return; + + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[fSegment->nsects()]; + + // inital guess for number of atoms + fAtoms.reserve(fSymbolCount); + + // add all atoms that have entries in symbol table + const macho_section

* sections = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + for (int i=fSymbolCount-1; i >= 0 ; --i) { + // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the reaal name + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + uint8_t type = (sym.n_type() & N_TYPE); + if ( type == N_SECT ) { + const macho_section

* section = §ions[sym.n_sect()-1]; + pint_t sectionEndAddr = section->addr() + section->size(); + bool suppress = false; + // ignore atoms in debugger sections + if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { + if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) { + // ignore dtrace probe labels + fHasDTraceProbes = true; + } + else if ( fStrings[sym.n_strx()] == 'L' ) { + // ignore L labels, + } + else { + // ignore labels for atoms in other sections + switch ( section->flags() & SECTION_TYPE ) { + case S_REGULAR: + if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 ) + suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS + case S_ZEROFILL: + case S_COALESCED: + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_16BYTE_LITERALS: + case S_CSTRING_LITERALS: + { + BaseAtom* newAtom; + typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value()); + if ( (pos != fAddrToAtom.end()) && (strcmp(pos->second->getSectionName(), section->sectname())==0) ) { + // another label to an existing address in the same section, make this an alias + newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *pos->second); + } + else { + // make SymbolAtom atom for this address + newAtom = new SymbolAtom(*this, &sym, section); + // don't add symbols at end of section to addr->atom map + if ( sym.n_value() != sectionEndAddr ) + fAddrToAtom[newAtom->getObjectAddress()] = newAtom; + } + if ( ! suppress ) + fAtoms.push_back(newAtom); + } + break; + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + // ignore symboled stubs produces by old ld64 + break; + default: + warning("symbol %s found in unsupported section in %s", + &fStrings[sym.n_strx()], this->getPath()); + } + } + } + } + else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { + fAtoms.push_back(new TentativeAtom(*this, &sym)); + } + else if ( type == N_ABS ) { + const char* symName = &fStrings[sym.n_strx()]; + if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) { + // ignore .objc_class_name_* symbols + fAppleObjc = true; + } + else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) { + // ignore empty *.eh symbols + } + else { + BaseAtom* abAtom = new AbsoluteAtom(*this, &sym); + fAtoms.push_back(abAtom); + fAddrToAbsoluteAtom[sym.n_value()] = abAtom; + } + } + else if ( type == N_INDR ) { + fHaveIndirectSymbols = true; + } + } + } + + // add all fixed size anonymous atoms from special sections + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + pint_t atomSize = 0; + uint8_t type (sect->flags() & SECTION_TYPE); + validSectionType(type); + bool suppress = false; + switch ( type ) { + case S_SYMBOL_STUBS: + suppress = true; + atomSize = sect->reserved2(); + break; + case S_LAZY_SYMBOL_POINTERS: + suppress = true; + atomSize = sizeof(pint_t); + break; + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LITERAL_POINTERS: + case S_MOD_INIT_FUNC_POINTERS: + case S_MOD_TERM_FUNC_POINTERS: + atomSize = sizeof(pint_t); + break; + case S_INTERPOSING: + atomSize = sizeof(pint_t)*2; + break; + case S_4BYTE_LITERALS: + atomSize = 4; + break; + case S_8BYTE_LITERALS: + atomSize = 8; + break; + case S_16BYTE_LITERALS: + atomSize = 16; + break; + case S_REGULAR: + // special case ObjC classes to synthesize .objc_class_name_* symbols + if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) && fAppleObjc ) { + // gcc sometimes over aligns class structure + uint32_t align = 1 << sect->align(); + atomSize = ((12 * sizeof(pint_t)) + align-1) & (-align); + } + // get objc Garbage Collection info + else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) + || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset()); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + fObjConstraint = ObjectFile::Reader::kObjcGC; + else if ( (flags & 2) == 2 ) + fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + else + fObjConstraint = ObjectFile::Reader::kObjcRetainRelease; + if ( (flags & 1) == 1 ) + fReplacementClasses = true; + // don't make atom for this section + atomSize = sect->size(); + suppress = true; + } + else { + warning("can't parse __OBJC/__image_info section in %s", fPath); + } + } + // special case constant NS/CFString literals and make an atom out of each one + else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { + atomSize = 4 * sizeof(pint_t); + } + break; + } + if ( atomSize != 0 ) { + for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { + pint_t atomAddr = sect->addr() + sectOffset; + // add if not already an atom at that address + if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) { + AnonymousAtom* newAtom = new AnonymousAtom(*this, sect, atomAddr, atomSize); + if ( !suppress ) + fAtoms.push_back(newAtom); + fAddrToAtom[atomAddr] = newAtom->redirectTo(); + } + } + } + } + + // add all c-string anonymous atoms + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { + uint32_t stringLen; + pint_t stringAddr; + BaseAtom* mostAlignedEmptyString = NULL; + uint32_t mostAlignedEmptyStringTrailingZeros = 0; + std::vector > emptyStrings; + for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { + stringAddr = sect->addr() + sectOffset; + stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; + // add if not already an atom at that address + if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) { + BaseAtom* newAtom = new AnonymousAtom(*this, sect, stringAddr, stringLen); + if ( stringLen == 1 ) { + // because of padding it may look like there are lots of empty strings, keep track of all + emptyStrings.push_back(std::make_pair(stringAddr, newAtom)); + // record empty string with greatest alignment requirement + uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr); + if ( (mostAlignedEmptyString == NULL) + || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) { + mostAlignedEmptyString = newAtom; + mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros; + } + } + else { + fAtoms.push_back(newAtom); + fAddrToAtom[stringAddr] = newAtom; + } + } + } + // map all uses of empty strings to the most aligned one + if ( mostAlignedEmptyString != NULL ) { + // make most aligned atom a real atom + fAtoms.push_back(mostAlignedEmptyString); + // map all other empty atoms to this one + for (typename std::vector >::iterator it=emptyStrings.begin(); it != emptyStrings.end(); it++) { + fAddrToAtom[it->first] = mostAlignedEmptyString; + } + } + } + } + + // sort all atoms so far by address and section + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + + //fprintf(stderr, "sorted atoms:\n"); + //for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) + // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName()); + + // create atoms to cover any non-debug ranges not handled above + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + pint_t sectionStartAddr = sect->addr(); + pint_t sectionEndAddr = sect->addr() + sect->size(); + const bool setFollowOnAtom = ! this->canScatterAtoms(); + if ( sect->size() != 0 ) { + // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change + if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { + fDebugInfo = kDebugInfoDwarf; + if ( strcmp(sect->sectname(), "__debug_info") == 0 ) + fDwarfDebugInfoSect = sect; + else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) + fDwarfDebugAbbrevSect = sect; + else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) + fDwarfDebugLineSect = sect; + } + else { + if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { + throw "object file contains old DWARF debug info - rebuild with newer compiler"; + } + uint8_t type (sect->flags() & SECTION_TYPE); + switch ( type ) { + case S_REGULAR: + case S_ZEROFILL: + case S_COALESCED: + // if there is not an atom already at the start of this section, add an anonymous one + pint_t previousAtomAddr = 0; + BaseAtom* previousAtom = NULL; + if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { + BaseAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr(), 0); + fAddrToAtom[sect->addr()] = newAtom; + fAtoms.push_back(newAtom); + previousAtomAddr = sectionStartAddr; + previousAtom = newAtom; + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + } + // calculate size of all atoms in this section and add follow-on references + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + pint_t atomAddr = atom->getObjectAddress(); + if ( atom->getSectionRecord() == sect ) { + //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName()); + if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) { + previousAtom->setSize(atomAddr - previousAtomAddr); + if ( setFollowOnAtom && (atom != previousAtom) ) + new Reference(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom)); + } + previousAtomAddr = atomAddr; + previousAtom = atom; + } + } + if ( previousAtom != NULL ) { + // set last atom in section + previousAtom->setSize(sectionEndAddr - previousAtomAddr); + } + break; + } + } + } + } + + // check for object file that defines no objc classes, but uses objc classes + // check for dtrace provider info + for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( (sym.n_type() & N_TYPE) == N_UNDF ) { + const char* undefinedName = &fStrings[sym.n_strx()]; + if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) { + fAppleObjc = true; + } + else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) { + if ( strchr(undefinedName, '$') != NULL ) { + if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) { + // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* + // is extra provider info + fDtraceProviderInfo.push_back(undefinedName); + } + } + } + } + } + } + + // add relocation based references to sections that have atoms with pending names + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( fSectionsWithAtomsPendingAName.count(sect) != 0 ) + addReferencesForSection(sect); + } + + // update any anonymous atoms that need references built in order to name themselves + for (typename std::vector*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) { + (*it)->resolveName(); + } + + // add relocation based references to other sections + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( fSectionsWithAtomsPendingAName.count(sect) == 0 ) + addReferencesForSection(sect); + } + + // add objective-c references + if ( fAppleObjc ) { + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { + for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { + AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset); + ObjectFile::Reference* classRef = ao.atom->getReferences()[0]; + if ( classRef->getFixUpOffset() == 0 ) { + const char* classStr = classRef->getTargetName(); + if ( strncmp(classStr, "cstring=", 8) == 0 ) { + const char* className; + asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]); + new Reference(A::kNoFixUp, ao, className, 0); + } + } + } + } + } + } + + // add direct references to local non-lazy-pointers, can do this now that all atoms are constructed + for (typename std::vector*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) { + AnonymousAtom* localNonLazy = *it; + uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress; + pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset))); + makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); + } + + // add implicit direct reference from each C++ function to its eh info + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_COALESCED) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { + for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // note: this algorithm depens on the map iterator returning entries in address order + if ( (it->first >= sect->addr()) && (it->first < sect->addr()+sect->size()) ) { + pint_t ehAtomAddress = it->first; + BaseAtom* ehAtom = it->second; + const char* ehName = ehAtom->getName(); + if ( (ehName != NULL) && (strcmp(&ehName[strlen(ehName)-3], ".eh") == 0) ) { + makeReferenceToEH(ehName, ehAtomAddress, sect); + // make EH symbol static so linker does not try to coalesce + if ( fOptions.fForFinalLinkedImage ) + ehAtom->setScope(ObjectFile::Atom::scopeTranslationUnit); + // if it has a reference to a LSDA, add a group reference + std::vector& ehrefs = ehAtom->getReferences(); + // all FDE's have at least 2 references (to CIE and to function) + if ( ehrefs.size() > 2 ) { + // a third reference means there is a LSDA + ObjectFile::Atom* lsdaAtom = NULL; + for (std::vector::iterator rit=ehrefs.begin(); rit != ehrefs.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getFixUpOffset() ) { + case 4: + case 8: + // these are CIE and function references + break; + default: + // this is LSDA reference + lsdaAtom = &ref->getTarget(); + } + } + if ( lsdaAtom != NULL ) { + new Reference(A::kGroupSubordinate, AtomAndOffset(ehAtom), AtomAndOffset(lsdaAtom)); + } + } + } + } + } + } + } + + // add command line aliases + for(std::vector::const_iterator it = fOptions.fAliases.begin(); it != fOptions.fAliases.end(); ++it) { + BaseAtom* target = this->findAtomByName(it->realName); + if ( (target != NULL) && target->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn ) + fAtoms.push_back(new SymbolAliasAtom(it->alias, NULL, *target)); + } + + // add dtrace probe locations + if ( fHasDTraceProbes ) { + for (uint32_t i=0; i < fSymbolCount; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( (sym.n_type() & N_TYPE) == N_SECT ) { + const char* symbolName = &fStrings[sym.n_strx()]; + if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) { + //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName); + makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0); + } + } + } + } + } + + // turn indirect symbols int SymbolAliasAtom + if ( fHaveIndirectSymbols ) { + for (uint32_t i=0; i < fSymbolCount; ++i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + if ( (sym.n_type() & N_TYPE) == N_INDR ) { + const char* aliasName = &fStrings[sym.n_strx()]; + const char* targetName = &fStrings[sym.n_value()]; + //fprintf(stderr, "found alias %s for %s\n", aliasName, targetName); + BaseAtom* target = this->findAtomByName(targetName); + // only currently support N_INDR based aliases to something in the same .o file + if ( target != NULL ) { + fAtoms.push_back(new SymbolAliasAtom(aliasName, &sym, *target)); + //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName); + } + } + } + } + } + + //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName()); + //} + + // add translation unit info from dwarf + uint64_t stmtList; + if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // compiler sometimes emits emtpty dwarf sections when there is no debug info, skip those + if ( (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { + if ( !read_comp_unit(&fDwarfTranslationUnitFile, &fDwarfTranslationUnitDir, &stmtList) ) { + // if can't parse dwarf, warn and give up + fDwarfTranslationUnitFile = NULL; + fDwarfTranslationUnitDir = NULL; + warning("can't parse dwarf compilation unit info in %s", this->getPath()); + fDebugInfo = kDebugInfoNone; + } + } + } + + // add line number info to atoms from dwarf + if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // file with just data will have no __debug_line info + if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0) + && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { + // validate stmt_list + if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) { + const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset(); + if ( debug_line != NULL ) { + struct line_reader_data* lines = line_open(&debug_line[stmtList], + fDwarfDebugLineSect->size() - stmtList, E::little_endian); + struct line_info result; + ObjectFile::Atom* curAtom = NULL; + uint32_t curAtomOffset = 0; + uint32_t curAtomAddress = 0; + uint32_t curAtomSize = 0; + while ( line_next (lines, &result, line_stop_pc) ) { + //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", + // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); + // work around weird debug line table compiler generates if no functions in __text section + if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) + continue; + // for performance, see if in next pc is in current atom + if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or pc at end of current atom + else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + else { + // do slow look up of atom by address + AtomAndOffset ao = this->findAtomAndOffset(result.pc); + curAtom = ao.atom; + if ( curAtom == NULL ) + break; // file has line info but no functions + if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { + // a one line function can be returned by line_next() as one entry with pc at end of blob + // look for alt atom starting at end of previous atom + uint32_t previousEnd = curAtomAddress+curAtomSize; + AtomAndOffset alt = this->findAtomAndOffset(previousEnd); + if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { + curAtom = alt.atom; + curAtomOffset = alt.offset; + curAtomAddress = previousEnd - alt.offset; + curAtomSize = curAtom->getSize(); + } + else { + curAtomOffset = ao.offset; + curAtomAddress = result.pc - ao.offset; + curAtomSize = curAtom->getSize(); + } + } + else { + curAtomOffset = ao.offset; + curAtomAddress = result.pc - ao.offset; + curAtomSize = curAtom->getSize(); + } + } + const char* filename; + std::map::iterator pos = fDwarfIndexToFile.find(result.file); + if ( pos == fDwarfIndexToFile.end() ) { + filename = line_file(lines, result.file); + fDwarfIndexToFile[result.file] = filename; + } + else { + filename = pos->second; + } + ObjectFile::LineInfo info; + info.atomOffset = curAtomOffset; + info.fileName = filename; + info.lineNumber = result.line; + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", + // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); + ((BaseAtom*)curAtom)->addLineInfo(info); + if ( result.end_of_sequence ) { + curAtom = NULL; + } + } + line_free(lines); + } + else { + warning("could not parse dwarf line number info in %s", this->getPath()); + } + } + } + } + + // if no dwarf, try processing stabs debugging info + if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { + // scan symbol table for stabs entries + fStabs.reserve(fSymbolCount); // reduce re-allocations + BaseAtom* currentAtom = NULL; + pint_t currentAtomAddress = 0; + enum { start, inBeginEnd, inFun } state = start; + for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { + const macho_nlist

* sym = &fSymbols[symbolIndex]; + bool useStab = true; + uint8_t type = sym->n_type(); + const char* symString = (sym->n_strx() != 0) ? &fStrings[sym->n_strx()] : NULL; + if ( (type & N_STAB) != 0 ) { + fDebugInfo = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs); + Stab stab; + stab.atom = NULL; + stab.type = type; + stab.other = sym->n_sect(); + stab.desc = sym->n_desc(); + stab.value = sym->n_value(); + stab.string = NULL; + switch (state) { + case start: + switch (type) { + case N_BNSYM: + // beginning of function block + state = inBeginEnd; + // fall into case to lookup atom by addresss + case N_LCSYM: + case N_STSYM: + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", + (uint64_t)sym->n_value(), path); + } + break; + case N_SO: + case N_OSO: + case N_OPT: + case N_LSYM: + case N_RSYM: + case N_PSYM: + // not associated with an atom, just copy + stab.string = symString; + break; + case N_GSYM: + { + // n_value field is NOT atom address ;-( + // need to find atom by name match + const char* colon = strchr(symString, ':'); + if ( colon != NULL ) { + // build underscore leading name + int nameLen = colon - symString; + char symName[nameLen+2]; + strlcpy(&symName[1], symString, nameLen+1); + symName[0] = '_'; + symName[nameLen+1] = '\0'; + currentAtom = findAtomByName(symName); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + else { + // might be a debug-note without trailing :G() + currentAtom = findAtomByName(symString); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + if ( stab.atom == NULL ) { + warning("can't find atom for N_GSYM stabs %s in %s", symString, path); + useStab = false; + } + break; + } + case N_FUN: + // old style stabs without BNSYM + state = inFun; + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, path); + } + break; + case N_SOL: + case N_SLINE: + stab.string = symString; + // old stabs + break; + case N_BINCL: + case N_EINCL: + case N_EXCL: + stab.string = symString; + // -gfull built .o file + break; + default: + warning("unknown stabs type 0x%X in %s", type, path); + } + break; + case inBeginEnd: + stab.atom = currentAtom; + switch (type) { + case N_ENSYM: + state = start; + currentAtom = NULL; + break; + case N_LCSYM: + case N_STSYM: + { + BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; + if ( nestedAtom != NULL ) { + stab.atom = nestedAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs 0x%X at %08llX in %s", + type, (uint64_t)sym->n_value(), path); + } + break; + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + default: + stab.string = symString; + break; + } + break; + case inFun: + switch (type) { + case N_FUN: + if ( sym->n_sect() != 0 ) { + // found another start stab, must be really old stabs... + currentAtomAddress = sym->n_value(); + currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, path); + } + } + else { + // found ending stab, switch back to start state + stab.string = symString; + stab.atom = currentAtom; + state = start; + currentAtom = NULL; + } + break; + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + stab.atom = currentAtom; + break; + case N_SO: + stab.string = symString; + state = start; + break; + default: + stab.atom = currentAtom; + stab.string = symString; + break; + } + break; + } + // add to list of stabs for this .o file + if ( useStab ) + fStabs.push_back(stab); + } + } + } + +#if 0 + // special case precompiled header .o file (which has no content) to have one empty atom + if ( fAtoms.size() == 0 ) { + int pathLen = strlen(path); + if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) { + ObjectFile::Atom* phony = new AnonymousAtom(*this, (uint32_t)0); + //phony->fSynthesizedName = ".gch.o"; + fAtoms.push_back(phony); + } + } +#endif + + // sort all atoms by address + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + + // set ordinal and sort references in each atom + uint32_t index = fOrdinalBase; + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + atom->setOrdinal(index++); + atom->sortReferences(); + } + +} + + +template <> +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + switch (cpusubtype) { + case CPU_SUBTYPE_POWERPC_ALL: + case CPU_SUBTYPE_POWERPC_750: + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + case CPU_SUBTYPE_POWERPC_970: + fCpuConstraint = cpusubtype; + break; + default: + warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); + fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL; + break; + } +} + +template <> +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + switch (cpusubtype) { + case CPU_SUBTYPE_ARM_ALL: + case CPU_SUBTYPE_ARM_V4T: + case CPU_SUBTYPE_ARM_V5TEJ: + case CPU_SUBTYPE_ARM_V6: + case CPU_SUBTYPE_ARM_XSCALE: + case CPU_SUBTYPE_ARM_V7: + fCpuConstraint = cpusubtype; + break; + default: + warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); + fCpuConstraint = CPU_SUBTYPE_ARM_ALL; + break; + } +} + +template +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + // no cpu sub types for this architecture +} + +template <> +uint32_t Reader::updateCpuConstraint(uint32_t previous) +{ + switch ( previous ) { + case CPU_SUBTYPE_POWERPC_ALL: + return fCpuConstraint; + break; + case CPU_SUBTYPE_POWERPC_750: + if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 || + fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 || + fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_POWERPC_970: + // G5 can run everything + break; + default: + throw "Unhandled PPC cpu subtype!"; + break; + } + return previous; +} + + + +template <> +uint32_t Reader::updateCpuConstraint(uint32_t previous) +{ + switch (previous) { + case CPU_SUBTYPE_ARM_ALL: + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_V5TEJ: + // v6, v7, and xscale are more constrained than previous file (v5), so use it + if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6) + || (fCpuConstraint == CPU_SUBTYPE_ARM_V7) + || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_V4T: + // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it + if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7) + || (fCpuConstraint == CPU_SUBTYPE_ARM_V6) + || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ) + || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_V6: + // v6 can run everything except xscale and v7 + if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) + throw "can't mix xscale and v6 code"; + if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) + return fCpuConstraint; + break; + case CPU_SUBTYPE_ARM_XSCALE: + // xscale can run everything except v6 and v7 + if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 ) + throw "can't mix xscale and v6 code"; + if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) + throw "can't mix xscale and v7 code"; + break; + case CPU_SUBTYPE_ARM_V7: + // v7 can run everything except xscale + if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) + throw "can't mix xscale and v7 code"; + break; + default: + throw "Unhandled ARM cpu subtype!"; + } + return previous; +} + +template +uint32_t Reader::updateCpuConstraint(uint32_t current) +{ + // no cpu sub types for this architecture + return current; +} + +template +void Reader::addDtraceExtraInfos(uint32_t probeAddr, const char* providerName) +{ + // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with + // a matching provider name, add a by-name kDtraceTypeReference at probe site + const char* dollar = strchr(providerName, '$'); + if ( dollar != NULL ) { + int providerNameLen = dollar-providerName+1; + for ( std::vector::iterator it = fDtraceProviderInfo.begin(); it != fDtraceProviderInfo.end(); ++it) { + const char* typeDollar = strchr(*it, '$'); + if ( typeDollar != NULL ) { + if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) { + makeByNameReference(A::kDtraceTypeReference, probeAddr, *it, 0); + } + } + } + } +} + + +template <> +void Reader::validSectionType(uint8_t type) +{ + switch ( type ) { + case S_SYMBOL_STUBS: + throw "symbol_stub sections not valid in x86_64 object files"; + case S_LAZY_SYMBOL_POINTERS: + throw "lazy pointer sections not valid in x86_64 object files"; + case S_NON_LAZY_SYMBOL_POINTERS: + throw "non lazy pointer sections not valid in x86_64 object files"; + } +} + +template +void Reader::validSectionType(uint8_t type) +{ +} + +template +bool Reader::getTranslationUnitSource(const char** dir, const char** name) const +{ + if ( fDebugInfo == kDebugInfoDwarf ) { + *dir = fDwarfTranslationUnitDir; + *name = fDwarfTranslationUnitFile; + return (fDwarfTranslationUnitFile != NULL); + } + return false; +} + +template +BaseAtom* Reader::findAtomByName(const char* name) +{ + // first search the more important atoms + for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { + const char* atomName = it->second->getName(); + if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { + return it->second; + } + } + // try all atoms, because this might have been a tentative definition + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + const char* atomName = atom->getName(); + if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { + return atom; + } + } + return NULL; +} + +template +Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr)); +} + +template +Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr)); +} + +template +Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr)); +} + +template +Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr) +{ + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr)); +} + +template +Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) +{ + return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); +} + +template +Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +{ + // add a direct reference from function atom to its eh frame atom + const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); + int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function + pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8; + return makeReference(A::kGroupSubordinate, funcAddr, ehAtomAddress); +} + + +template <> +Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) +{ + // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references + // instead check scope of target + BaseAtom* target = findAtomByName(toName); + if ( (target != NULL) && (target->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) + return new Reference(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset)); + else + return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); +} + +template <> +Reference* Reader::makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset) +{ + // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references + // instead check scope of target + const char* symbolName = &fStrings[toSymbol->n_strx()]; + if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) + return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toSymbol->n_value(), toSymbol->n_value()+toOffset)); + else + return new Reference(kind, findAtomAndOffset(atAddr), symbolName, toOffset); +} + + +template <> +Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +{ + // add a direct reference from function atom to its eh frame atom + // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target + uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + ehSect->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { + pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); + return makeReference(x86_64::kGroupSubordinate, funcAddr, ehAtomAddress); + } + } + warning("can't find matching function for eh symbol %s", ehName); + return NULL; +} + + +template +AtomAndOffset Reader::findAtomAndOffset(pint_t addr) +{ + // STL has no built-in for "find largest key that is same or less than" + typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr); + // if no atoms up to this address return none found + if ( it == fAddrToAtom.begin() ) + return AtomAndOffset(NULL); + // otherwise upper_bound gets us next key, so we back up one + --it; + AtomAndOffset result; + result.atom = it->second; + result.offset = addr - it->first; + //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n", + // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize()); + return result; +} + +// "scattered" relocations enable you to offset into an atom past the end of it +// baseAddr is the address of the target atom, +// realAddr is the points into it +template +AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) +{ + typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr); + if ( it != fAddrToAtom.end() ) { + AtomAndOffset result; + result.atom = it->second; + result.offset = realAddr - it->first; + //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); + return result; + } + // getting here means we have a scattered relocation to an address without a label + // we should never get here... + // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section + return findAtomAndOffset(realAddr); +} + + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (const uint8_t ** offset, const uint8_t * end) +{ + while (*offset != end && **offset >= 0x80) + (*offset)++; + if (*offset != end) + (*offset)++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (const uint8_t ** offset, const uint8_t * end) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (*offset == end) + return (uint64_t) -1; + + b = **offset & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*(*offset)++ >= 0x80); + return result; +} + + +/* Skip over a DWARF attribute of form FORM. */ +template +bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, + uint8_t addr_size, bool dwarf64) +{ + int64_t sz=0; + + switch (form) + { + case DW_FORM_addr: + sz = addr_size; + break; + + case DW_FORM_block2: + if (end - *offset < 2) + return false; + sz = 2 + A::P::E::get16(*(uint16_t*)offset); + break; + + case DW_FORM_block4: + if (end - *offset < 4) + return false; + sz = 2 + A::P::E::get32(*(uint32_t*)offset); + break; + + case DW_FORM_data2: + case DW_FORM_ref2: + sz = 2; + break; + + case DW_FORM_data4: + case DW_FORM_ref4: + sz = 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + sz = 8; + break; + + case DW_FORM_string: + while (*offset != end && **offset) + ++*offset; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + sz = 1; + break; + + case DW_FORM_block: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_block1: + if (*offset == end) + return false; + sz = 1 + **offset; + break; + + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + skip_leb128 (offset, end); + return true; + + case DW_FORM_strp: + case DW_FORM_ref_addr: + sz = dwarf64 ? 8 : 4; + break; + + default: + return false; + } + if (end - *offset < sz) + return false; + *offset += sz; + return true; +} + +// Look at the compilation unit DIE and determine +// its NAME, compilation directory (in COMP_DIR) and its +// line number information offset (in STMT_LIST). NAME and COMP_DIR +// may be NULL (especially COMP_DIR) if they are not in the .o file; +// STMT_LIST will be (uint64_t) -1. +// +// At present this assumes that there's only one compilation unit DIE. +// +template +bool Reader::read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list) +{ + const uint8_t * debug_info; + const uint8_t * debug_abbrev; + const uint8_t * di; + const uint8_t * da; + const uint8_t * end; + const uint8_t * enda; + uint64_t sz; + uint16_t vers; + uint64_t abbrev_base; + uint64_t abbrev; + uint8_t address_size; + bool dwarf64; + + *name = NULL; + *comp_dir = NULL; + *stmt_list = (uint64_t) -1; + + if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) ) + return false; + + debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset(); + debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset(); + di = debug_info; + + if (fDwarfDebugInfoSect->size() < 12) + /* Too small to be a real debug_info section. */ + return false; + sz = A::P::E::get32(*(uint32_t*)di); + di += 4; + dwarf64 = sz == 0xffffffff; + if (dwarf64) + sz = A::P::E::get64(*(uint64_t*)di), di += 8; + else if (sz > 0xffffff00) + /* Unknown dwarf format. */ + return false; + + /* Verify claimed size. */ + if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + return false; + + vers = A::P::E::get16(*(uint16_t*)di); + if (vers < 2 || vers > 3) + /* DWARF version wrong for this code. + Chances are we could continue anyway, but we don't know for sure. */ + return false; + di += 2; + + /* Find the debug_abbrev section. */ + abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); + di += dwarf64 ? 8 : 4; + + if (abbrev_base > fDwarfDebugAbbrevSect->size()) + return false; + da = debug_abbrev + abbrev_base; + enda = debug_abbrev + fDwarfDebugAbbrevSect->size(); + + address_size = *di++; + + /* Find the abbrev number we're looking for. */ + end = di + sz; + abbrev = read_uleb128 (&di, end); + if (abbrev == (uint64_t) -1) + return false; + + /* Skip through the debug_abbrev section looking for that abbrev. */ + for (;;) + { + uint64_t this_abbrev = read_uleb128 (&da, enda); + uint64_t attr; + + if (this_abbrev == abbrev) + /* This is almost always taken. */ + break; + skip_leb128 (&da, enda); /* Skip the tag. */ + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + do { + attr = read_uleb128 (&da, enda); + skip_leb128 (&da, enda); + } while (attr != 0 && attr != (uint64_t) -1); + if (attr != 0) + return false; + } + + /* Check that the abbrev is one for a DW_TAG_compile_unit. */ + if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) + return false; + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + /* Now, go through the DIE looking for DW_AT_name, + DW_AT_comp_dir, and DW_AT_stmt_list. */ + for (;;) + { + uint64_t attr = read_uleb128 (&da, enda); + uint64_t form = read_uleb128 (&da, enda); + + if (attr == (uint64_t) -1) + return false; + else if (attr == 0) + return true; + + if (form == DW_FORM_indirect) + form = read_uleb128 (&di, end); + + if (attr == DW_AT_name && form == DW_FORM_string) + *name = (const char *) di; + else if (attr == DW_AT_comp_dir && form == DW_FORM_string) + *comp_dir = (const char *) di; + /* Really we should support DW_FORM_strp here, too, but + there's usually no reason for the producer to use that form + for the DW_AT_name and DW_AT_comp_dir attributes. */ + else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) + *stmt_list = A::P::E::get32(*(uint32_t*)di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) + *stmt_list = A::P::E::get64(*(uint64_t*)di); + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } +} + +template +const char* Reader::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + + +// +// +// To implement architecture xxx, you must write template specializations for the following six methods: +// Reader::validFile() +// Reader::addRelocReference() +// Reference::getDescription() +// +// + + +template <> +bool Reader::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; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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_I386 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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_X86_64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Reader::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; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template +bool Reader::isWeakImportSymbol(const macho_nlist

* sym) +{ + return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) ); +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + return addRelocReference_powerpc(sect, reloc); +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + return addRelocReference_powerpc(sect, reloc); +} + + +// +// ppc and ppc64 both use the same relocations, so process them in one common routine +// +template +bool Reader::addRelocReference_powerpc(const macho_section* sect, + const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + uint32_t offsetInTarget; + int16_t lowBits; + bool result = false; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + const macho_relocation_info

* nextReloc = &reloc[1]; + const char* targetName = NULL; + bool weakImport = false; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != PPC_RELOC_PAIR ) + instruction = BigEndian::get32(*fixUpPtr); + srcAddr = sect->addr() + reloc->r_address(); + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + weakImport = this->isWeakImportSymbol(targetSymbol); + } + switch ( reloc->r_type() ) { + case PPC_RELOC_BR24: + { + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + } + else { + printf("bad instruction for BR24 reloc"); + } + if ( reloc->r_extern() ) { + offsetInTarget = srcAddr + displacement; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( weakImport ) + makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(A::kBranch24WeakImport, srcAddr, dstAddr); + else + makeReference(A::kBranch24, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_BR14: + { + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + if ( reloc->r_extern() ) { + offsetInTarget = srcAddr + displacement; + makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + makeReference(A::kBranch14, srcAddr, dstAddr); + } + } + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_LO16 missing following pair"); + break; + } + result = true; + lowBits = (instruction & 0xFFFF); + if ( reloc->r_extern() ) { + offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsLow16, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsLow16, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_LO14: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_LO14 missing following pair"); + break; + } + result = true; + lowBits = (instruction & 0xFFFC); + if ( reloc->r_extern() ) { + offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsLow14, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsLow14, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_HI16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_HI16 missing following pair"); + break; + } + result = true; + if ( reloc->r_extern() ) { + offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsHigh16, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsHigh16, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_HA16: + { + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_HA16 missing following pair"); + break; + } + result = true; + lowBits = (nextReloc->r_address() & 0x0000FFFF); + if ( reloc->r_extern() ) { + offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0); + else + makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); + } + else { + makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); + } + } + } + break; + case PPC_RELOC_VANILLA: + { + pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + if ( weakImport ) + makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue); + else + makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); + } + else { + makeReference(A::kPointer, srcAddr, pointerValue); + } + } + break; + case PPC_RELOC_JBSR: + // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { + warning("PPC_RELOC_JBSR missing following pair"); + break; + } + fHasLongBranchStubs = true; + result = true; + makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + } + else { + fprintf(stderr, "bad instruction for BR24 reloc"); + } + if ( reloc->r_extern() ) { + fprintf(stderr, "PPC_RELOC_JBSR should not be using an external relocation"); + } + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + uint32_t betterDstAddr; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch (sreloc->r_type()) { + case PPC_RELOC_VANILLA: + { + betterDstAddr = P::getP(*(pint_t*)fixUpPtr); + //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_BR14: + { + instruction = BigEndian::get32(*fixUpPtr); + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + betterDstAddr = srcAddr+displacement; + //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement); + makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_BR24: + { + instruction = BigEndian::get32(*fixUpPtr); + if ( (instruction & 0x4C000000) == 0x48000000 ) { + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + betterDstAddr = srcAddr+displacement; + makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr); + } + } + break; + case PPC_RELOC_LO16_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO16_SECTDIFF missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFF); + displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_LO14_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO14_SECTDIFF missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFC); + displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_HA16_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_HA16_SECTDIFF missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0x0000FFFF); + displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; + makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); + } + break; + case PPC_RELOC_LO14: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO14 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFC); + betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_LO16: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_LO16 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (instruction & 0xFFFF); + betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_HA16: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_HA16 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0xFFFF); + betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_HI16: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_HI16 missing following PAIR"); + break; + } + instruction = BigEndian::get32(*fixUpPtr); + lowBits = (nextRelocAddress & 0xFFFF); + betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr); + } + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) { + warning("PPC_RELOC_SECTDIFF missing following pair"); + break; + } + Kinds kind = A::kPointerDiff32;; + uint32_t contentAddr = 0; + switch ( sreloc->r_length() ) { + case 0: + throw "bad diff relocations r_length (0) for ppc architecture"; + case 1: + kind = A::kPointerDiff16; + contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + kind = A::kPointerDiff32; + contentAddr = BigEndian::get32(*fixUpPtr); + break; + case 3: + kind = A::kPointerDiff64; + contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr)); + break; + } + AtomAndOffset srcao = findAtomAndOffset(srcAddr); + AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); + AtomAndOffset toao = findAtomAndOffset(dstAddr); + // check for addend encoded in the section content + //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", + // dstAddr, nextRelocValue, contentAddr); + if ( (dstAddr - nextRelocValue) != contentAddr ) { + if ( toao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else if ( fromao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else + fromao.offset += (dstAddr - contentAddr) - nextRelocValue; + } + //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", + // srcao.atom->getDisplayName(), srcao.offset, + // fromao.atom->getDisplayName(), fromao.offset, + // toao.atom->getDisplayName(), toao.offset); + new Reference(kind, srcao, fromao, toao); + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + bool result = false; + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + switch ( reloc->r_type() ) { + case GENERIC_RELOC_VANILLA: + { + x86::ReferenceKinds kind = x86::kPointer; + uint32_t pointerValue = E::get32(*fixUpPtr); + if ( reloc->r_pcrel() ) { + switch( reloc->r_length() ) { + case 0: + kind = x86::kPCRel8; + pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t); + break; + case 1: + kind = x86::kPCRel16; + pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t); + break; + case 2: + kind = x86::kPCRel32; + pointerValue += srcAddr + sizeof(uint32_t); + break; + case 3: + throw "bad pc-rel vanilla relocation length"; + } + } + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { + kind = x86::kAbsolute32; + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + } + else { + kind = x86::kPointer; + if ( reloc->r_length() != 2 ) + throw "bad vanilla relocation length"; + } + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + if ( this->isWeakImportSymbol(targetSymbol) ) { + if ( reloc->r_pcrel() ) + kind = x86::kPCRel32WeakImport; + else + kind = x86::kPointerWeakImport; + } + const char* targetName = &fStrings[targetSymbol->n_strx()]; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else + makeByNameReference(kind, srcAddr, targetName, pointerValue); + } + else { + // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol + ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom; + const char* targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue); + else if ( reloc->r_symbolnum() != R_ABS ) + makeReference(kind, srcAddr, pointerValue); + else { + // find absolute symbol that corresponds to pointerValue + AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(kind, srcAddr, pos->second->getName(), 0); + else + throwf("R_ABS reloc but no absolute symbol at target address"); + } + } + } + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + pint_t betterDstAddr; + // file format allows pair to be scattered or not + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + } + } + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + betterDstAddr = LittleEndian::get32(*fixUpPtr); + //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( sreloc->r_pcrel() ) { + betterDstAddr += srcAddr + 4; + makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); + } + else { + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); + else + makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); + } + break; + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + { + if ( !nextRelocIsPair ) { + warning("GENERIC_RELOC_SECTDIFF missing following pair"); + break; + } + x86::ReferenceKinds kind = x86::kPointerDiff; + uint32_t contentAddr = 0; + switch ( sreloc->r_length() ) { + case 0: + case 3: + throw "bad length for GENERIC_RELOC_SECTDIFF"; + case 1: + kind = x86::kPointerDiff16; + contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + kind = x86::kPointerDiff; + contentAddr = LittleEndian::get32(*fixUpPtr); + break; + } + AtomAndOffset srcao = findAtomAndOffset(srcAddr); + AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); + AtomAndOffset toao = findAtomAndOffset(dstAddr); + // check for addend encoded in the section content + //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", + // dstAddr, nextRelocValue, contentAddr); + if ( (dstAddr - nextRelocValue) != contentAddr ) { + if ( toao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else if ( fromao.atom == srcao.atom ) + toao.offset += (contentAddr + nextRelocValue) - dstAddr; + else + fromao.offset += (dstAddr - contentAddr) - nextRelocValue; + } + //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", + // srcao.atom->getDisplayName(), srcao.offset, + // fromao.atom->getDisplayName(), fromao.offset, + // toao.atom->getDisplayName(), toao.offset); + new Reference(kind, srcao, fromao, toao); + } + break; + case GENERIC_RELOC_PAIR: + // do nothing, already used via a look ahead + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + +template <> +bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) +{ + uint64_t srcAddr; + uint64_t dstAddr = 0; + uint64_t addend; + uint32_t* fixUpPtr; + x86_64::ReferenceKinds kind = x86_64::kNoFixUp; + bool result = false; + const macho_nlist

* targetSymbol = NULL; + const char* targetName = NULL; + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + //fprintf(stderr, "addReloc type=%d\n", reloc->r_type()); + if ( reloc->r_extern() ) { + targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + } + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; + if ( reloc->r_length() != 3 ) + throw "length < 3 and X86_64_RELOC_UNSIGNED not supported"; + dstAddr = E::get64(*((uint64_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kPointer, srcAddr, dstAddr); + // verify that dstAddr is in the section being targeted + int sectNum = reloc->r_symbolnum(); + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const targetSection = §ionsStart[sectNum-1]; + if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { + throwf("local relocation for address 0x%08llX in section %s does not target section %s", + srcAddr, sect->sectname(), targetSection->sectname()); + } + } + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( reloc->r_extern() ) { + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + kind = x86_64::kPCRel32; + // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created + if ( addend == (uint64_t)(-1) ) { + addend = 0; + kind = x86_64::kPCRel32_1; + } + else if ( addend == (uint64_t)(-2) ) { + addend = 0; + kind = x86_64::kPCRel32_2; + } + else if ( addend == (uint64_t)(-4) ) { + addend = 0; + kind = x86_64::kPCRel32_4; + } + break; + // end support for old .o files before X86_64_RELOC_SIGNED_1 was created + case X86_64_RELOC_SIGNED_1: + kind = x86_64::kPCRel32_1; + addend += 1; + break; + case X86_64_RELOC_SIGNED_2: + kind = x86_64::kPCRel32_2; + addend += 2; + break; + case X86_64_RELOC_SIGNED_4: + kind = x86_64::kPCRel32_4; + addend += 4; + break; + } + makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend); + } + else { + uint64_t ripRelativeOffset = addend; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + dstAddr = srcAddr + 4 + ripRelativeOffset; + kind = x86_64::kPCRel32; + break; + case X86_64_RELOC_SIGNED_1: + dstAddr = srcAddr + 5 + ripRelativeOffset; + kind = x86_64::kPCRel32_1; + break; + case X86_64_RELOC_SIGNED_2: + dstAddr = srcAddr + 6 + ripRelativeOffset; + kind = x86_64::kPCRel32_2; + break; + case X86_64_RELOC_SIGNED_4: + dstAddr = srcAddr + 8 + ripRelativeOffset; + kind = x86_64::kPCRel32_4; + break; + } + makeReference(kind, srcAddr, dstAddr); + // verify that dstAddr is in the section being targeted + int sectNum = reloc->r_symbolnum(); + const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + const macho_section

* const targetSection = §ionsStart[sectNum-1]; + if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { + throwf("local relocation for address 0x%08llX in section %s does not target section %s", + srcAddr, sect->sectname(), targetSection->sectname()); + } + } + break; + case X86_64_RELOC_BRANCH: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_BRANCH not supported"; + if ( reloc->r_length() == 2 ) { + dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( reloc->r_extern() ) { + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr); + else + makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr); + } + } + else if ( reloc->r_length() == 0 ) { + dstAddr = *((int8_t*)fixUpPtr); + if ( reloc->r_extern() ) { + makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr); + } + else { + makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr); + } + } + else { + throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());; + } + break; + case X86_64_RELOC_GOT: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend); + else + makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend); + break; + case X86_64_RELOC_GOT_LOAD: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; + addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); + if ( isWeakImportSymbol(targetSymbol) ) + makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend); + else + makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend); + break; + case X86_64_RELOC_SUBTRACTOR: + { + if ( reloc->r_pcrel() ) + throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; + const macho_relocation_info* nextReloc = &reloc[1]; + if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; + Reference* ref; + bool negativeAddend; + if ( reloc->r_length() == 2 ) { + kind = x86_64::kPointerDiff32; + dstAddr = E::get32(*fixUpPtr); // addend is in content + negativeAddend = ((dstAddr & 0x80000000) != 0); + } + else { + kind = x86_64::kPointerDiff; + dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content + negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); + } + ObjectFile::Atom* inAtom = this->findAtomAndOffset(srcAddr).atom; + // create reference with "to" target + if ( nextReloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; + const char* targetName = &fStrings[targetSymbol->n_strx()]; + ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0); + // if "to" is in this atom, change by-name to a direct reference + if ( strcmp(targetName, inAtom->getName()) == 0 ) + ref->setTarget(*inAtom, 0); + } + else { + ref = makeReference(kind, srcAddr, dstAddr); + } + // add in "from" target + if ( reloc->r_extern() ) { + const macho_nlist

* targetFromSymbol = &fSymbols[reloc->r_symbolnum()]; + const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()]; + if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) { + // from target is translation unit scoped, so use a direct reference + ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom)); + } + else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) { + // if "from" is in this atom, change by-name to a direct reference + ref->setFromTarget(*inAtom); + } + else { + // some non-static other atom + ref->setFromTargetName(fromTargetName); + } + } + // addend goes in from side iff negative + if ( negativeAddend ) + ref->setFromTargetOffset(-dstAddr); + else + ref->setToTargetOffset(dstAddr); + break; + } + default: + warning("unknown relocation type %d", reloc->r_type()); + } + return result; +} + + +/// Reader::addRelocReference - +/// turns arm relocation entries into references. Returns true if the next +/// relocation should be skipped, false otherwise. +template <> +bool Reader::addRelocReference(const macho_section* sect, + const macho_relocation_info* reloc) +{ + uint32_t * fixUpPtr; + int32_t displacement; + uint32_t instruction = 0; + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t pointerValue; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + // non-scattered relocation + const char* targetName = NULL; + bool weakImport = false; + + srcAddr = sect->addr() + reloc->r_address(); + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != ARM_RELOC_PAIR ) + instruction = LittleEndian::get32(*fixUpPtr); + + if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + targetName = &fStrings[targetSymbol->n_strx()]; + weakImport = this->isWeakImportSymbol(targetSymbol); + } + + switch ( reloc->r_type() ) { + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + + if ( reloc->r_extern() ) { + uint32_t offsetInTarget = srcAddr + displacement; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( weakImport ) + makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget); + } + else { + dstAddr = srcAddr + displacement; + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + // check for dtrace probes and weak_import stubs + const char* targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr); + else if ( reloc->r_symbolnum() != R_ABS ) + makeReference(arm::kBranch24, srcAddr, dstAddr); + else { + // find absolute symbol that corresponds to pointerValue + AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0); + else + throwf("R_ABS reloc but no absolute symbol at target address"); + } + } + break; + + case ARM_THUMB_RELOC_BR22: + // First instruction has upper 11 bits of the displacement. + displacement = (instruction & 0x7FF) << 12; + if ( (displacement & 0x400000) != 0 ) + displacement |= 0xFF800000; + // Second instruction has lower eleven bits of the displacement. + displacement += ((instruction >> 16) & 0x7FF) << 1; + // The pc added will be +4 from the pc + displacement += 4; + // If the instruction was blx, force the low 2 bits to be clear + dstAddr = srcAddr + displacement; + if ((instruction & 0xF8000000) == 0xE8000000) + dstAddr &= 0xFFFFFFFC; + + if ( reloc->r_extern() ) { + uint32_t offsetInTarget = dstAddr; + if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( weakImport ) + makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget); + else + makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget); + } + else { + ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; + // check for dtrace probes and weak_import stubs + const char* targetName = atom->getName(); + if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { + makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[16]); + } + else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { + makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); + addDtraceExtraInfos(srcAddr, &targetName[20]); + } + else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)atom)->isWeakImportStub() ) + makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr); + else if ( reloc->r_symbolnum() != R_ABS ) + makeReference(arm::kThumbBranch22, srcAddr, dstAddr); + else { + // find absolute symbol that corresponds to pointerValue + AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); + if ( pos != fAddrToAbsoluteAtom.end() ) + makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0); + else + throwf("R_ABS reloc but no absolute symbol at target address"); + } + } + break; + + case ARM_RELOC_VANILLA: + if ( reloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + + pointerValue = instruction; + if ( reloc->r_extern() ) { + if ( weakImport ) + makeByNameReference(arm::kPointerWeakImport, srcAddr, targetName, pointerValue); + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeByNameReference(arm::kReadOnlyPointer, srcAddr, targetName, pointerValue); + else + makeByNameReference(arm::kPointer, srcAddr, targetName, pointerValue); + } + else { + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReference(arm::kReadOnlyPointer, srcAddr, pointerValue); + else + makeReference(arm::kPointer, srcAddr, pointerValue); + } + break; + + default: + warning("unexpected relocation type %u", reloc->r_type()); + break; + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + uint32_t betterDstAddr; + fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); + instruction = LittleEndian::get32(*fixUpPtr); + + // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF} + // relocation types, and it is an error to see one otherwise. + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + + switch (sreloc->r_type()) { + case ARM_RELOC_VANILLA: + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + + betterDstAddr = LittleEndian::get32(*fixUpPtr); + //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + makeReferenceWithToBase(arm::kReadOnlyPointer, srcAddr, betterDstAddr, dstAddr); + else + makeReferenceWithToBase(arm::kPointer, srcAddr, betterDstAddr, dstAddr); + break; + + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + betterDstAddr = srcAddr+displacement; + makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr); + break; + + case ARM_THUMB_RELOC_BR22: + // First instruction has upper 11 bits of the displacement. + displacement = (instruction & 0x7FF) << 12; + if ( (displacement & 0x400000) != 0 ) + displacement |= 0xFF800000; + // Second instruction has lower eleven bits of the displacement. + displacement += ((instruction >> 16) & 0x7FF) << 1; + // The pc added will be +4 from the pc + displacement += 4; + betterDstAddr = srcAddr+displacement; + // If the instruction was blx, force the low 2 bits to be clear + if ((instruction & 0xF8000000) == 0xE8000000) + betterDstAddr &= 0xFFFFFFFC; + makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr); + break; + + case ARM_RELOC_SECTDIFF: + case ARM_RELOC_LOCAL_SECTDIFF: + if ( !nextRelocIsPair ) { + warning("ARM_RELOC_SECTDIFF missing following pair"); + break; + } + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_SECTDIFF"; + { + AtomAndOffset srcao = findAtomAndOffset(srcAddr); + AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); + AtomAndOffset toao = findAtomAndOffset(dstAddr); + // check for addend encoded in the section content + pointerValue = LittleEndian::get32(*fixUpPtr); + if ( (dstAddr - nextRelocValue) != pointerValue ) { + if ( toao.atom == srcao.atom ) + toao.offset += (pointerValue + nextRelocValue) - dstAddr; + else if ( fromao.atom == srcao.atom ) + toao.offset += (pointerValue + nextRelocValue) - dstAddr; + else + fromao.offset += (dstAddr - pointerValue) - nextRelocValue; + } + new Reference(arm::kPointerDiff, srcao, fromao, toao); + } + break; + + default: + warning("unexpected srelocation type %u", sreloc->r_type()); + break; + } + } + return result; +} + +template +void Reader::addReferencesForSection(const macho_section

* sect) +{ + // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change + if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) { + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + case S_LAZY_SYMBOL_POINTERS: + // we ignore compiler generated stubs, so ignore those relocs too + break; + default: + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); + for (uint32_t r = 0; r < relocCount; ++r) { + try { + if ( addRelocReference(sect, &relocs[r]) ) + ++r; // skip next + } + catch (const char* msg) { + throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); + } + } + } + } +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case x86::kNoFixUp: + sprintf(temp, "reference to "); + break; + case x86::kFollowOn: + sprintf(temp, "followed by "); + break; + case x86::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case x86::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case x86::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case x86::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86::kPointerDiff16: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86::kPCRel32WeakImport: + sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc); + break; + case x86::kPCRel32: + sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86::kPCRel16: + sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc); + break; + case x86::kPCRel8: + sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc); + break; + case x86::kAbsolute32: + sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); + break; + case x86::kDtraceProbe: + sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); + break; + case x86::kDtraceProbeSite: + sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); + break; + case x86::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case x86::kDtraceTypeReference: + sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case ppc::kNoFixUp: + sprintf(temp, "reference to "); + break; + case ppc::kFollowOn: + sprintf(temp, "followed by "); + break; + case ppc::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case ppc::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case ppc::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case ppc::kPointerDiff16: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc::kPointerDiff32: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc::kPointerDiff64: + throw "unsupported refrence kind"; + break; + case ppc::kBranch24WeakImport: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case ppc::kBranch24: + case ppc::kBranch14: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case ppc::kPICBaseLow16: + sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); + break; + case ppc::kPICBaseLow14: + sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); + break; + case ppc::kPICBaseHigh16: + sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); + break; + case ppc::kAbsLow16: + sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsLow14: + sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsHigh16: + sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kAbsHigh16AddLow: + sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc::kDtraceProbe: + sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); + break; + case ppc::kDtraceProbeSite: + sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); + break; + case ppc::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case ppc::kDtraceTypeReference: + sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case ppc64::kNoFixUp: + sprintf(temp, "reference to "); + break; + case ppc64::kFollowOn: + sprintf(temp, "followed by "); + break; + case ppc64::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case ppc64::kPointerWeakImport: + sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); + break; + case ppc64::kPointer: + sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); + break; + case ppc64::kPointerDiff64: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kPointerDiff32: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kPointerDiff16: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case ppc64::kBranch24WeakImport: + sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case ppc64::kBranch24: + case ppc64::kBranch14: + sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case ppc64::kPICBaseLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kPICBaseLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kPICBaseHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + break; + case ppc64::kAbsLow16: + sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsLow14: + sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsHigh16: + sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kAbsHigh16AddLow: + sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceProbe: + sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceProbeSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case ppc64::kDtraceTypeReference: + sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); + + return temp; +} + + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case x86_64::kNoFixUp: + sprintf(temp, "reference to "); + break; + case x86_64::kFollowOn: + sprintf(temp, "followed by "); + break; + case x86_64::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case x86_64::kPointerWeakImport: + sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); + break; + case x86_64::kPointer: + sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); + break; + case x86_64::kPointerDiff32: + case x86_64::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; + sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", + fFixUpOffsetInSrc, size, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + break; + case x86_64::kPCRel32: + sprintf(temp, "offset 0x%04llX, rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_1: + sprintf(temp, "offset 0x%04llX, rel32-1 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_2: + sprintf(temp, "offset 0x%04llX, rel32-2 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32_4: + sprintf(temp, "offset 0x%04llX, rel32-4 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel32: + sprintf(temp, "offset 0x%04llX, branch rel32 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel32WeakImport: + sprintf(temp, "offset 0x%04llX, branch rel32 reference to weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOT: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTWeakImport: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTLoad: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); + break; + case x86_64::kPCRel32GOTLoadWeakImport: + sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); + break; + case x86_64::kBranchPCRel8: + sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceProbe: + sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceProbeSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case x86_64::kDtraceTypeReference: + sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); + + return temp; +} + +template <> +const char* Reference::getDescription() const +{ + static char temp[2048]; + switch( fKind ) { + case arm::kNoFixUp: + sprintf(temp, "reference to "); + break; + case arm::kFollowOn: + sprintf(temp, "followed by "); + break; + case arm::kGroupSubordinate: + sprintf(temp, "group subordinate "); + break; + case arm::kPointer: + sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); + break; + case arm::kPointerWeakImport: + sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); + break; + case arm::kPointerDiff: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + return temp; + } + case arm::kReadOnlyPointer: + sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc); + break; + case arm::kBranch24: + case arm::kThumbBranch22: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); + break; + case arm::kBranch24WeakImport: + case arm::kThumbBranch22WeakImport: + sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); + break; + case arm::kDtraceProbe: + sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); + break; + case arm::kDtraceProbeSite: + sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); + break; + case arm::kDtraceIsEnabledSite: + sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); + break; + case arm::kDtraceTypeReference: + sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); + break; + } + // always quote by-name references + if ( fToTargetName != NULL ) { + strcat(temp, "\""); + strcat(temp, fToTargetName); + strcat(temp, "\""); + } + else if ( fToTarget.atom != NULL ) { + strcat(temp, fToTarget.atom->getDisplayName()); + } + else { + strcat(temp, "NULL target"); + } + if ( fToTarget.offset != 0 ) + sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); + + return temp; +} + +}; // namespace relocatable +}; // namespace mach_o + +#endif // __OBJECT_FILE_MACH_O__ diff --git a/ld64/src/MachOWriterExecutable.hpp b/ld64/src/MachOWriterExecutable.hpp new file mode 100644 index 0000000..8667ae4 --- /dev/null +++ b/ld64/src/MachOWriterExecutable.hpp @@ -0,0 +1,8579 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __EXECUTABLE_MACH_O__ +#define __EXECUTABLE_MACH_O__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ObjectFile.h" +#include "ExecutableFile.h" +#include "Options.h" + +#include "MachOFileAbstraction.hpp" + + +// +// +// To implement architecture xxx, you must write template specializations for the following methods: +// MachHeaderAtom::setHeaderInfo() +// ThreadsLoadCommandsAtom::getSize() +// ThreadsLoadCommandsAtom::copyRawContent() +// Writer::addObjectRelocs() +// Writer::fixUpReferenceRelocatable() +// Writer::fixUpReferenceFinal() +// Writer::stubableReference() +// Writer::weakImportReferenceKind() +// Writer::GOTReferenceKind() +// + + +namespace mach_o { +namespace executable { + +// forward references +template class WriterAtom; +template class PageZeroAtom; +template class CustomStackAtom; +template class MachHeaderAtom; +template class SegmentLoadCommandsAtom; +template class EncryptionLoadCommandsAtom; +template class SymbolTableLoadCommandsAtom; +template class ThreadsLoadCommandsAtom; +template class DylibIDLoadCommandsAtom; +template class RoutinesLoadCommandsAtom; +template class DyldLoadCommandsAtom; +template class UUIDLoadCommandAtom; +template class LinkEditAtom; +template class SectionRelocationsLinkEditAtom; +template class LocalRelocationsLinkEditAtom; +template class ExternalRelocationsLinkEditAtom; +template class SymbolTableLinkEditAtom; +template class SegmentSplitInfoLoadCommandsAtom; +template class SegmentSplitInfoContentAtom; +template class IndirectTableLinkEditAtom; +template class ModuleInfoLinkEditAtom; +template class StringsLinkEditAtom; +template class LoadCommandsPaddingAtom; +template class StubAtom; +template class StubHelperAtom; +template class LazyPointerAtom; +template class NonLazyPointerAtom; +template class DylibLoadCommandsAtom; + + +// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes +class SectionInfo : public ObjectFile::Section { +public: + SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), + fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false), + fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false), + fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false), + fHasTextLocalRelocs(false), fHasTextExternalRelocs(false) + { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } + void setIndex(unsigned int index) { fIndex=index; } + std::vector fAtoms; + char fSegmentName[20]; + char fSectionName[20]; + uint64_t fFileOffset; + uint64_t fSize; + uint32_t fRelocCount; + uint32_t fRelocOffset; + uint32_t fIndirectSymbolOffset; + uint8_t fAlignment; + bool fAllLazyPointers; + bool fAllLazyDylibPointers; + bool fAllNonLazyPointers; + bool fAllStubs; + bool fAllSelfModifyingStubs; + bool fAllZeroFill; + bool fVirtualSection; + bool fHasTextLocalRelocs; + bool fHasTextExternalRelocs; +}; + +// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes +class SegmentInfo +{ +public: + SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), + fBaseAddress(0), fSize(0), fFixedAddress(false), + fIndependentAddress(false) { fName[0] = '\0'; } + std::vector fSections; + char fName[20]; + uint32_t fInitProtection; + uint32_t fMaxProtection; + uint64_t fFileOffset; + uint64_t fFileSize; + uint64_t fBaseAddress; + uint64_t fSize; + bool fFixedAddress; + bool fIndependentAddress; +}; + +template +class Writer : public ExecutableFile::Writer +{ +public: + Writer(const char* path, Options& options, std::vector& dynamicLibraries); + virtual ~Writer(); + + virtual const char* getPath() { return fFilePath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms() { return fWriterSynthesizedAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return NULL; } + + virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, + bool objcReplacementClasses); + virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); + virtual uint64_t write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool createUUID, bool canScatter, + ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs, bool overridesDylibWeakDefines); + +private: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; + + void assignFileOffsets(); + void synthesizeStubs(); + void insertDummyStubs(); + void partitionIntoSections(); + bool addBranchIslands(); + bool addPPCBranchIslands(); + bool isBranch24Reference(uint8_t kind); + void adjustLoadCommandsAndPadding(); + void createDynamicLinkerCommand(); + void createDylibCommands(); + void buildLinkEdit(); + const char* getArchString(); + void writeMap(); + uint64_t writeAtoms(); + void writeNoOps(int fd, uint32_t from, uint32_t to); + void copyNoOps(uint8_t* from, uint8_t* to); + bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to); + void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref); + void collectExportedAndImportedAndLocalAtoms(); + void setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count); + void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); + void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); + void buildSymbolTable(); + const char* symbolTableName(const ObjectFile::Atom* atom); + void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); + void copyNlistRange(const std::vector >& entries, uint32_t startIndex); + uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); + uint8_t ordinalForLibrary(ObjectFile::Reader* file); + bool shouldExport(const ObjectFile::Atom& atom) const; + void buildFixups(); + void adjustLinkEditSections(); + void buildObjectFileFixups(); + void buildExecutableFixups(); + bool preboundLazyPointerType(uint8_t* type); + uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; + void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; + void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; + void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, + uint8_t buffer[], bool finalLinkedImage) const; + uint32_t symbolIndex(ObjectFile::Atom& atom); + bool makesExternalRelocatableReference(ObjectFile::Atom& target) const; + uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref); + uint8_t getRelocPointerSize(); + uint64_t maxAddress(); + bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref); + bool GOTReferenceKind(uint8_t kind); + bool optimizableGOTReferenceKind(uint8_t kind); + bool weakImportReferenceKind(uint8_t kind); + unsigned int collectStabs(); + uint64_t valueForStab(const ObjectFile::Reader::Stab& stab); + uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab); + uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab); + void addStabs(uint32_t startIndex); + RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const; + bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&); + bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); + bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); + bool mightNeedPadSegment(); + void scanForAbsoluteReferences(); + bool needsModuleTable(); + void optimizeDylibReferences(); + bool indirectSymbolIsLocal(const ObjectFile::Reference* ref) const; + + struct DirectLibrary { + class ObjectFile::Reader* fLibrary; + bool fWeak; + bool fReExport; + }; + + friend class WriterAtom; + friend class PageZeroAtom; + friend class CustomStackAtom; + friend class MachHeaderAtom; + friend class SegmentLoadCommandsAtom; + friend class EncryptionLoadCommandsAtom; + friend class SymbolTableLoadCommandsAtom; + friend class ThreadsLoadCommandsAtom; + friend class DylibIDLoadCommandsAtom; + friend class RoutinesLoadCommandsAtom; + friend class DyldLoadCommandsAtom; + friend class UUIDLoadCommandAtom; + friend class LinkEditAtom; + friend class SectionRelocationsLinkEditAtom; + friend class LocalRelocationsLinkEditAtom; + friend class ExternalRelocationsLinkEditAtom; + friend class SymbolTableLinkEditAtom; + friend class SegmentSplitInfoLoadCommandsAtom; + friend class SegmentSplitInfoContentAtom; +// friend class IndirectTableLinkEditAtom; + friend class ModuleInfoLinkEditAtom; + friend class StringsLinkEditAtom; + friend class LoadCommandsPaddingAtom; + friend class StubAtom; + friend class StubHelperAtom; + friend class LazyPointerAtom; + friend class NonLazyPointerAtom; + friend class DylibLoadCommandsAtom; + + const char* fFilePath; + Options& fOptions; + std::vector* fAllAtoms; + std::vector* fStabs; + class SectionInfo* fLoadCommandsSection; + class SegmentInfo* fLoadCommandsSegment; + class EncryptionLoadCommandsAtom* fEncryptionLoadCommand; + class SegmentLoadCommandsAtom* fSegmentCommands; + class SymbolTableLoadCommandsAtom* fSymbolTableCommands; + class LoadCommandsPaddingAtom* fHeaderPadding; + class UUIDLoadCommandAtom* fUUIDAtom; + std::vector fWriterSynthesizedAtoms; + std::vector fSegmentInfos; + class SegmentInfo* fPadSegmentInfo; + class ObjectFile::Atom* fEntryPoint; + class ObjectFile::Atom* fDyldHelper; + class ObjectFile::Atom* fDyldLazyDylibHelper; + std::map*> fLibraryToLoadCommand; + std::map fLibraryToOrdinal; + std::map fLibraryAliases; + std::vector fExportedAtoms; + std::vector fImportedAtoms; + std::vector fLocalSymbolAtoms; + std::vector > fLocalExtraLabels; + std::vector > fGlobalExtraLabels; + class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; + class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; + class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; + class SymbolTableLinkEditAtom* fSymbolTableAtom; + class SegmentSplitInfoContentAtom* fSplitCodeToDataContentAtom; + class IndirectTableLinkEditAtom* fIndirectTableAtom; + class ModuleInfoLinkEditAtom* fModuleInfoAtom; + class StringsLinkEditAtom* fStringsAtom; + class PageZeroAtom* fPageZeroAtom; + macho_nlist

* fSymbolTable; + std::vector > fSectionRelocs; + std::vector > fInternalRelocs; + std::vector > fExternalRelocs; + std::map fStubsMap; + std::map fGOTMap; + std::vector*> fAllSynthesizedStubs; + std::vector fAllSynthesizedStubHelpers; + std::vector*> fAllSynthesizedLazyPointers; + std::vector*> fAllSynthesizedLazyDylibPointers; + std::vector*> fAllSynthesizedNonLazyPointers; + uint32_t fSymbolTableCount; + uint32_t fSymbolTableStabsCount; + uint32_t fSymbolTableStabsStartIndex; + uint32_t fSymbolTableLocalCount; + uint32_t fSymbolTableLocalStartIndex; + uint32_t fSymbolTableExportCount; + uint32_t fSymbolTableExportStartIndex; + uint32_t fSymbolTableImportCount; + uint32_t fSymbolTableImportStartIndex; + uint32_t fLargestAtomSize; + bool fEmitVirtualSections; + bool fHasWeakExports; + bool fReferencesWeakImports; + bool fCanScatter; + bool fWritableSegmentPastFirst4GB; + bool fNoReExportedDylibs; + bool fBiggerThanTwoGigs; + bool fSlideable; + std::map fWeakImportMap; + std::set fDylibReadersWithNonWeakImports; + std::set fDylibReadersWithWeakImports; + SegmentInfo* fFirstWritableSegment; + ObjectFile::Reader::CpuConstraint fCpuConstraint; + uint32_t fAnonNameIndex; +}; + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) + : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + virtual bool hasFixedAddress() const { return fFixedAddress; } + + static Segment fgTextSegment; + static Segment fgPageZeroSegment; + static Segment fgLinkEditSegment; + static Segment fgStackSegment; + static Segment fgImportSegment; + static Segment fgROImportSegment; + static Segment fgDataSegment; + static Segment fgObjCSegment; + + +private: + const char* fName; + const bool fReadable; + const bool fWritable; + const bool fExecutable; + const bool fFixedAddress; +}; + +Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true); +Segment Segment::fgTextSegment("__TEXT", true, false, true, false); +Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false); +Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true); +Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); +Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false); +Segment Segment::fgDataSegment("__DATA", true, true, false, false); +Segment Segment::fgObjCSegment("__OBJC", true, true, false, false); + + +template +class WriterAtom : public ObjectFile::Atom +{ +public: + enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; + WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { } + + virtual ObjectFile::Reader* getFile() const { return &fWriter; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return NULL; } + virtual const char* getDisplayName() const { return this->getName(); } + virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return true; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + virtual bool mustRemainInSection() const { return true; } + virtual ObjectFile::Segment& getSegment() const { return fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return 0; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); } + virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } + virtual void setScope(Scope) { } + + +protected: + virtual ~WriterAtom() {} + typedef typename A::P P; + typedef typename A::P::E E; + + static std::vector fgEmptyReferenceList; + + Writer& fWriter; + Segment& fSegment; +}; + +template std::vector WriterAtom::fgEmptyReferenceList; + + +template +class PageZeroAtom : public WriterAtom +{ +public: + PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment), + fSize(fWriter.fOptions.zeroPageSize()) {} + virtual const char* getDisplayName() const { return "page zero content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fSize; } + virtual const char* getSectionName() const { return "._zeropage"; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } + void setSize(uint64_t size) { fSize = size; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint64_t fSize; +}; + + +template +class DsoHandleAtom : public WriterAtom +{ +public: + DsoHandleAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + virtual const char* getName() const { return "___dso_handle"; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual uint64_t getSize() const { return 0; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual void copyRawContent(uint8_t buffer[]) const {} +}; + + +template +class MachHeaderAtom : public WriterAtom +{ +public: + MachHeaderAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + virtual const char* getName() const; + virtual const char* getDisplayName() const; + virtual ObjectFile::Atom::Scope getScope() const; + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; + virtual uint64_t getSize() const { return sizeof(macho_header); } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } + virtual const char* getSectionName() const { return "._mach_header"; } + virtual uint32_t getOrdinal() const { return 1; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + void setHeaderInfo(macho_header& header) const; +}; + +template +class CustomStackAtom : public WriterAtom +{ +public: + CustomStackAtom(Writer& writer); + virtual const char* getDisplayName() const { return "custom stack content"; } + virtual bool isZeroFill() const { return true; } + virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } + virtual const char* getSectionName() const { return "._stack"; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + static bool stackGrowsDown(); +}; + +template +class LoadCommandAtom : public WriterAtom +{ +protected: + LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment), fOrdinal(fgCurrentOrdinal++) {} + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } + virtual const char* getSectionName() const { return "._load_commands"; } + virtual uint32_t getOrdinal() const { return fOrdinal; } + static uint64_t alignedSize(uint64_t size); +protected: + uint32_t fOrdinal; + static uint32_t fgCurrentOrdinal; +}; + +template uint32_t LoadCommandAtom::fgCurrentOrdinal = 0; + +template +class SegmentLoadCommandsAtom : public LoadCommandAtom +{ +public: + SegmentLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0) + { writer.fSegmentCommands = this; } + virtual const char* getDisplayName() const { return "segment load commands"; } + virtual uint64_t getSize() const { return fSize; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void computeSize(); + void setup(); + unsigned int commandCount() { return fCommandCount; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + unsigned int fCommandCount; + uint32_t fSize; +}; + + +template +class SymbolTableLoadCommandsAtom : public LoadCommandAtom +{ +public: + SymbolTableLoadCommandsAtom(Writer&); + virtual const char* getDisplayName() const { return "symbol table load commands"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; + unsigned int commandCount(); + void needDynamicTable(); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + bool fNeedsDynamicSymbolTable; + macho_symtab_command fSymbolTable; + macho_dysymtab_command fDynamicSymbolTable; +}; + +template +class ThreadsLoadCommandsAtom : public LoadCommandAtom +{ +public: + ThreadsLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "thread load commands"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint8_t* fBuffer; + uint32_t fBufferSize; +}; + +template +class DyldLoadCommandsAtom : public LoadCommandAtom +{ +public: + DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "dyld load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom +{ +public: + SegmentSplitInfoLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "segment split info load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class AllowableClientLoadCommandsAtom : public LoadCommandAtom +{ +public: + AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : + LoadCommandAtom(writer, Segment::fgTextSegment), clientString(client) {} + virtual const char* getDisplayName() const { return "allowable_client load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* clientString; +}; + +template +class DylibLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) + : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info), + fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; } + virtual const char* getDisplayName() const { return "dylib load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void optimizeAway() { fOptimizedAway = true; } + bool linkedWeak() { return fInfo.options.fWeakImport; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + ExecutableFile::DyLibUsed fInfo; + bool fOptimizedAway; +}; + +template +class DylibIDLoadCommandsAtom : public LoadCommandAtom +{ +public: + DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "dylib ID load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class RoutinesLoadCommandsAtom : public LoadCommandAtom +{ +public: + RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + virtual const char* getDisplayName() const { return "routines load command"; } + virtual uint64_t getSize() const { return sizeof(macho_routines_command); } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "sub-umbrella load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + typedef typename A::P P; + const char* fName; +}; + +template +class SubLibraryLoadCommandsAtom : public LoadCommandAtom +{ +public: + SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) + : LoadCommandAtom(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} + virtual const char* getDisplayName() const { return "sub-library load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fNameStart; + int fNameLength; +}; + +template +class UmbrellaLoadCommandsAtom : public LoadCommandAtom +{ +public: + UmbrellaLoadCommandsAtom(Writer& writer, const char* name) + : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + virtual const char* getDisplayName() const { return "umbrella load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class UUIDLoadCommandAtom : public LoadCommandAtom +{ +public: + UUIDLoadCommandAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) {} + virtual const char* getDisplayName() const { return "uuid load command"; } + virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void generate(); + void setContent(const uint8_t uuid[16]); + const uint8_t* getUUID() { return fUUID; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uuid_t fUUID; + bool fEmit; +}; + + +template +class RPathLoadCommandsAtom : public LoadCommandAtom +{ +public: + RPathLoadCommandsAtom(Writer& writer, const char* path) + : LoadCommandAtom(writer, Segment::fgTextSegment), fPath(path) {} + virtual const char* getDisplayName() const { return "rpath load command"; } + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fPath; +}; + +template +class EncryptionLoadCommandsAtom : public LoadCommandAtom +{ +public: + EncryptionLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment), fStartOffset(0), + fEndOffset(0) {} + virtual const char* getDisplayName() const { return "encryption info load command"; } + virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command); } + virtual void copyRawContent(uint8_t buffer[]) const; + void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; } + void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint32_t fStartOffset; + uint32_t fEndOffset; +}; + +template +class LoadCommandsPaddingAtom : public WriterAtom +{ +public: + LoadCommandsPaddingAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment), fSize(0) {} + virtual const char* getDisplayName() const { return "header padding"; } + virtual uint64_t getSize() const { return fSize; } + virtual const char* getSectionName() const { return "._load_cmds_pad"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void setSize(uint64_t newSize); +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint64_t fSize; +}; + +template +class LinkEditAtom : public WriterAtom +{ +public: + LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment), fOrdinal(fgCurrentOrdinal++) {} + uint64_t getFileOffset() const; + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } + virtual uint32_t getOrdinal() const { return fOrdinal; } +private: + uint32_t fOrdinal; + static uint32_t fgCurrentOrdinal; +private: + typedef typename A::P P; +}; + +template uint32_t LinkEditAtom::fgCurrentOrdinal = 0; + +template +class SectionRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "section relocations"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._section_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class LocalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "local relocations"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._local_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class SymbolTableLinkEditAtom : public LinkEditAtom +{ +public: + SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "symbol table"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._symbol_table"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class ExternalRelocationsLinkEditAtom : public LinkEditAtom +{ +public: + ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "external relocations"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._extern_relocs"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +struct IndirectEntry { + uint32_t indirectIndex; + uint32_t symbolIndex; +}; + + +template +class SegmentSplitInfoContentAtom : public LinkEditAtom +{ +public: + SegmentSplitInfoContentAtom(Writer& writer) : LinkEditAtom(writer), fCantEncode(false) { } + virtual const char* getDisplayName() const { return "split segment info"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._split_info"; } + virtual void copyRawContent(uint8_t buffer[]) const; + bool canEncode() { return !fCantEncode; } + void setCantEncode() { fCantEncode = true; } + void add32bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind1Locations.push_back(AtomAndOffset(atom, offset)); } + void add64bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind2Locations.push_back(AtomAndOffset(atom, offset)); } + void addPPCHi16Location(const ObjectFile::Atom* atom, uint32_t offset) { fKind3Locations.push_back(AtomAndOffset(atom, offset)); } + void add32bitImportLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind4Locations.push_back(AtomAndOffset(atom, offset)); } + void encode(); + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + struct AtomAndOffset { + AtomAndOffset(const ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} + const ObjectFile::Atom* atom; + uint32_t offset; + }; + void uleb128EncodeAddresses(const std::vector& locations); + + std::vector fKind1Locations; + std::vector fKind2Locations; + std::vector fKind3Locations; + std::vector fKind4Locations; + std::vector fEncodedData; + bool fCantEncode; +}; + +template +class IndirectTableLinkEditAtom : public LinkEditAtom +{ +public: + IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "indirect symbol table"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._indirect_syms"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + std::vector fTable; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + +template +class ModuleInfoLinkEditAtom : public LinkEditAtom +{ +public: + ModuleInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer), fModuleNameOffset(0) { } + virtual const char* getDisplayName() const { return "module table"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._module_info"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); } + uint32_t getTableOfContentsFileOffset() const; + uint32_t getModuleTableFileOffset() const; + uint32_t getReferencesFileOffset() const; + uint32_t getReferencesCount() const; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + uint32_t fModuleNameOffset; +}; + + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +template +class StringsLinkEditAtom : public LinkEditAtom +{ +public: + StringsLinkEditAtom(Writer& writer); + virtual const char* getDisplayName() const { return "string pool"; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "._string_pool"; } + virtual void copyRawContent(uint8_t buffer[]) const; + + int32_t add(const char* name); + int32_t addUnique(const char* name); + int32_t emptyString() { return 1; } + const char* stringForIndex(int32_t) const; + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + enum { kBufferSize = 0x01000000 }; + typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; + + std::vector fFullBuffers; + char* fCurrentBuffer; + uint32_t fCurrentBufferUsed; + StringToOffset fUniqueStrings; +}; + + + +template +class UndefinedSymbolProxyAtom : public WriterAtom +{ +public: + UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, Segment::fgLinkEditSegment), fName(name) {} + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual uint64_t getSize() const { return 0; } + virtual const char* getSectionName() const { return "._imports"; } +private: + using WriterAtom::fWriter; + typedef typename A::P P; + const char* fName; +}; + +template +class BranchIslandAtom : public WriterAtom +{ +public: + BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__text"; } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + uint32_t fTargetOffset; +}; + +template +class StubAtom : public WriterAtom +{ +public: + StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual ObjectFile::Alignment getAlignment() const; + virtual const char* getSectionName() const { return "__symbol_stub1"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + static const char* stubName(const char* importName); + bool pic() const { return fWriter.fSlideable; } + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; + bool fForLazyDylib; +}; + +template +class StubHelperAtom : public WriterAtom +{ +public: + StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer, bool forLazyDylib); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__stub_helper"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + static const char* stubName(const char* importName); + using WriterAtom::fWriter; + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + +template +class LazyPointerAtom : public WriterAtom +{ +public: + LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, + StubAtom& stub, bool forLazyDylib); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } + virtual const char* getSectionName() const { return fForLazyDylib ? "__ld_symbol_ptr" : "__la_symbol_ptr"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fExternalTarget; } +private: + using WriterAtom::fWriter; + static const char* lazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + ObjectFile::Atom& fExternalTarget; + std::vector fReferences; + bool fForLazyDylib; +}; + + +template +class NonLazyPointerAtom : public WriterAtom +{ +public: + NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target); + virtual const char* getName() const { return fName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } + virtual const char* getSectionName() const { return "__nl_symbol_ptr"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + ObjectFile::Atom* getTarget() { return &fTarget; } +private: + using WriterAtom::fWriter; + static const char* nonlazyPointerName(const char* importName); + const char* fName; + ObjectFile::Atom& fTarget; + std::vector fReferences; +}; + + +template +class ObjCInfoAtom : public WriterAtom +{ +public: + ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcContraint, + bool objcReplacementClasses); + virtual const char* getName() const { return "objc$info"; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const { return 8; } + virtual const char* getSectionName() const; + virtual void copyRawContent(uint8_t buffer[]) const; +private: + Segment& getInfoSegment() const; + uint32_t fContent[2]; +}; + + +template +class WriterReference : public ObjectFile::Reference +{ +public: + typedef typename A::ReferenceKinds Kinds; + + WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, + uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) + : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), + fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} + + virtual ~WriterReference() {} + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return (uint8_t)fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } + virtual const char* getTargetName() const { return fTarget->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *fTarget; } + virtual uint64_t getTargetOffset() const { return fTargetOffset; } + virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } + virtual const char* getFromTargetName() const { return fFromTarget->getName(); } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; } + virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } + virtual void setFromTargetName(const char* name) { } + virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; } + virtual const char* getDescription() const { return "writer reference"; } + virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } + +private: + Kinds fKind; + uint32_t fFixUpOffsetInSrc; + ObjectFile::Atom* fTarget; + uint32_t fTargetOffset; + ObjectFile::Atom* fFromTarget; + uint32_t fFromTargetOffset; +}; + + + +template <> +StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, + ObjectFile::Atom& lazyPointer, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubHelpers.push_back(this); + + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldLazyDylibHelper)); + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); + } +} + +template <> +uint64_t StubHelperAtom::getSize() const +{ + return 12; +} + +template <> +void StubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0xE9; // jmp dyld_stub_binding_helper + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; +} + + +template +const char* StubHelperAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stubHelper", name); + return buf; +} + + +// specialize lazy pointer for x86_64 to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + StubHelperAtom* helper = new StubHelperAtom(writer, target, *this, forLazyDylib); + fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); +} + +// specialize lazy pointer for x86 to initially pointer to second half of stub +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + // helper part of stub is 14 or 6 bytes into stub + fReferences.push_back(new WriterReference(0, x86::kPointer, &stub, writer.fSlideable ? 14 : 6)); +} + +template +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + + + +template +const char* LazyPointerAtom::lazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$lazy_pointer", name); + return buf; +} + +template +void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + +template +NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedNonLazyPointers.push_back(this); + + fReferences.push_back(new WriterReference(0, A::kPointer, &target)); +} + +template +const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$non_lazy_pointer", name); + return buf; +} + +template +void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, getSize()); +} + + + +template <> +bool StubAtom::pic() const +{ + // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB. + // Usually that only happens if page zero is very large + return ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ); +} + + +template <> +bool StubAtom::pic() const +{ + return fWriter.fSlideable; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 2; +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), + fTarget(target), fForLazyDylib(forLazyDylib) +{ + writer.fAllSynthesizedStubs.push_back(this); + LazyPointerAtom* lp; + if ( fWriter.fOptions.prebind() ) { + // for prebound ppc, lazy pointer starts out pointing to target symbol's address + // if target is a weak definition within this linkage unit or zero if in some dylib + lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + } + else { + // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); + } + } + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, this, 8)); + fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, this, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc::kAbsLow16, lp)); + } +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), + fTarget(target), fForLazyDylib(forLazyDylib) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); + } + if ( pic() ) { + // picbase is 8 bytes into atom + fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, this, 8)); + fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, this, 8)); + } + else { + fReferences.push_back(new WriterReference(0, ppc64::kAbsHigh16AddLow, lp)); + fReferences.push_back(new WriterReference(4, ppc64::kAbsLow14, lp)); + } +} + +// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, (writer.fOptions.slowx86Stubs() || forLazyDylib) ? Segment::fgTextSegment : + ( writer.fOptions.readOnlyx86Stubs() ? Segment::fgROImportSegment : Segment::fgImportSegment)), + fTarget(target), fForLazyDylib(forLazyDylib) +{ + if ( writer.fOptions.slowx86Stubs() || forLazyDylib ) { + fName = stubName(target.getName()); + writer.fAllSynthesizedStubs.push_back(this); + LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + ObjectFile::Atom* helper; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + helper = writer.fDyldLazyDylibHelper; + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + helper = writer.fDyldHelper; + } + if ( pic() ) { + // picbase is 5 bytes into atom + fReferences.push_back(new WriterReference(8, x86::kPointerDiff, lp, 0, this, 5)); + fReferences.push_back(new WriterReference(16, x86::kPCRel32, helper)); + } + else { + fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); + fReferences.push_back(new WriterReference(7, x86::kAbsolute32, lp)); + fReferences.push_back(new WriterReference(12, x86::kPCRel32, helper)); + } + } + else { + if ( &target == NULL ) + fName = "cache-line-crossing-stub"; + else { + fName = stubName(target.getName()); + writer.fAllSynthesizedStubs.push_back(this); + } + } +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, lp)); +} + +template <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp; + if ( fWriter.fOptions.prebind() && !forLazyDylib ) { + // for prebound arm, lazy pointer starts out pointing to target symbol's address + // if target is a weak definition within this linkage unit or zero if in some dylib + lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + } + else { + // for non-prebound arm, lazy pointer starts out pointing to dyld_stub_binding_helper glue code + ObjectFile::Atom* helper; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + helper = writer.fDyldLazyDylibHelper; + } + else { + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + helper = writer.fDyldHelper; + } + lp = new LazyPointerAtom(writer, *helper, *this, forLazyDylib); + } + if ( pic() ) + fReferences.push_back(new WriterReference(12, arm::kPointerDiff, lp, 0, this, 12)); + else + fReferences.push_back(new WriterReference(8, arm::kPointer, lp)); +} + +template +const char* StubAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stub", name); + return buf; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 32 : 16 ); +} + + +template <> +uint64_t StubAtom::getSize() const +{ + return ( pic() ? 16 : 12 ); +} + +template <> +uint64_t StubAtom::getSize() const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { + if ( pic() ) + return 20; + else + return 16; + } + return 5; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return 6; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) + return 2; + else + return 0; // special case x86 fast stubs to be byte aligned +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + else { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) + OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + else { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) + OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { + if ( pic() ) { + buffer[0] = 0xE8; // call picbase + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x58; // pop eax + buffer[6] = 0x8D; // lea foo$lazy_pointer-picbase(eax),eax + buffer[7] = 0x80; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0xFF; // jmp *(eax) + buffer[13] = 0x20; + buffer[14] = 0x50; // push eax + buffer[15] = 0xE9; // jump dyld_stub_binding_helper + buffer[16] = 0x00; + buffer[17] = 0x00; + buffer[18] = 0x00; + buffer[19] = 0x00; + } + else { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x68; // pushl $foo$lazy_pointer + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0xE9; // jump dyld_stub_binding_helper + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x00; + } + } + else { + if ( fWriter.fOptions.prebind() ) { + uint32_t address = this->getAddress(); + int32_t rel32 = 0 - (address+5); + buffer[0] = 0xE9; + buffer[1] = rel32 & 0xFF; + buffer[2] = (rel32 >> 8) & 0xFF; + buffer[3] = (rel32 >> 16) & 0xFF; + buffer[4] = (rel32 >> 24) & 0xFF; + } + else { + buffer[0] = 0xF4; + buffer[1] = 0xF4; + buffer[2] = 0xF4; + buffer[3] = 0xF4; + buffer[4] = 0xF4; + } + } +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; +} + +template <> +void StubAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( pic() ) { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + } + else { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + } +} + +// x86_64 stubs are 7 bytes and need no alignment +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + return 0; +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + return ( pic() ? "__picsymbolstub4" : "__symbol_stub4"); +} + +template <> +const char* StubAtom::getSectionName() const +{ + if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { + if ( pic() ) + return "__picsymbol_stub"; + else + return "__symbol_stub"; + } + return "__jump_table"; +} + + + + +struct AtomByNameSorter +{ + bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) + { + return (strcmp(left->getName(), right->getName()) < 0); + } +}; + +template +struct ExternalRelocSorter +{ + bool operator()(const macho_relocation_info

& left, const macho_relocation_info

& right) + { + // sort first by symbol number + if ( left.r_symbolnum() != right.r_symbolnum() ) + return (left.r_symbolnum() < right.r_symbolnum()); + // then sort all uses of the same symbol by address + return (left.r_address() < right.r_address()); + } +}; + + +template +Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) + : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), + fAllAtoms(NULL), fStabs(NULL), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), + fSymbolTableCommands(NULL), fHeaderPadding(NULL), + fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), fDyldHelper(NULL), fDyldLazyDylibHelper(NULL), + fSectionRelocationsAtom(NULL), fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), + fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL), + fStringsAtom(NULL), fPageZeroAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), fSymbolTableStabsCount(0), + fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), + fLargestAtomSize(1), + fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), + fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), + fBiggerThanTwoGigs(false), fSlideable(false), + fFirstWritableSegment(NULL), fAnonNameIndex(1000) +{ + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + if ( fOptions.zeroPageSize() != 0 ) + fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicExecutable ) + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + if ( fOptions.hasCustomStack() ) + fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + // fall through + case Options::kObjectFile: + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); + if ( fOptions.initFunctionName() != NULL ) + fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + if ( fOptions.sharedRegionEligible() ) + fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + if ( fOptions.sharedRegionEligible() ) { + fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom(*this)); + } + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + if ( this->needsModuleTable() ) + fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kDyld: + fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + } + + // add extra commmands + bool hasReExports = false; + uint32_t ordinal = 1; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + if ( fOptions.makeEncryptable() ) { + fEncryptionLoadCommand = new EncryptionLoadCommandsAtom(*this); + fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand); + } + // fall through + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + { + // add dylib load command atoms for all dynamic libraries + const unsigned int libCount = dynamicLibraries.size(); + for (unsigned int i=0; i < libCount; ++i) { + ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; + //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() ); + + if ( dylibInfo.options.fReExport ) { + hasReExports = true; + } + else { + const char* parentUmbrella = dylibInfo.reader->parentUmbrella(); + if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) { + const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/'); + if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) ) + hasReExports = true; + } + } + + if ( dylibInfo.options.fBundleLoader ) { + fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL; + } + else { + // see if a DylibLoadCommandsAtom has already been created for this install path + bool newDylib = true; + const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); + for (unsigned int seenLib=0; seenLib < i; ++seenLib) { + ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; + if ( !seenDylibInfo.options.fBundleLoader ) { + const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); + if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { + fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; + fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader]; + fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader; + newDylib = false; + break; + } + } + } + + if ( newDylib ) { + // assign new ordinal and check for other paired load commands + fLibraryToOrdinal[dylibInfo.reader] = ordinal++; + DylibLoadCommandsAtom* dyliblc = new DylibLoadCommandsAtom(*this, dylibInfo); + fLibraryToLoadCommand[dylibInfo.reader] = dyliblc; + fWriterSynthesizedAtoms.push_back(dyliblc); + if ( dylibInfo.options.fReExport + && (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) + && (fOptions.outputKind() == Options::kDynamicLibrary) ) { + // see if child has sub-framework that is this + bool isSubFramework = false; + const char* childInUmbrella = dylibInfo.reader->parentUmbrella(); + if ( childInUmbrella != NULL ) { + const char* myLeaf = strrchr(fOptions.installPath(), '/'); + if ( myLeaf != NULL ) { + if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) + isSubFramework = true; + } + } + // LC_SUB_FRAMEWORK is in child, so do nothing in parent + if ( ! isSubFramework ) { + // this dylib also needs a sub_x load command + bool isFrameworkReExport = false; + const char* lastSlash = strrchr(dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + char frameworkName[strlen(lastSlash)+20]; + sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); + isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); + } + if ( isFrameworkReExport ) { + // needs a LC_SUB_UMBRELLA command + fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1])); + } + else { + // needs a LC_SUB_LIBRARY command + const char* nameStart = &lastSlash[1]; + if ( lastSlash == NULL ) + nameStart = dylibInstallPath; + int len = strlen(nameStart); + const char* dot = strchr(nameStart, '.'); + if ( dot != NULL ) + len = dot - nameStart; + fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len)); + } + } + } + } + } + } + // add umbrella command if needed + if ( fOptions.umbrellaName() != NULL ) { + fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); + } + // add allowable client commands if used + std::vector& allowableClients = fOptions.allowableClients(); + for (std::vector::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it) + fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom(*this, *it)); + } + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + break; + } + fNoReExportedDylibs = !hasReExports; + + // add any rpath load commands + for(std::vector::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) { + fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom(*this, *it)); + } + + // set up fSlideable + switch ( fOptions.outputKind() ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + fSlideable = false; + break; + case Options::kDynamicExecutable: + fSlideable = fOptions.positionIndependentExecutable(); + break; + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fSlideable = true; + break; + } + + //fprintf(stderr, "ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); + //} +} + +template +Writer::~Writer() +{ + if ( fFilePath != NULL ) + free((void*)fFilePath); + if ( fSymbolTable != NULL ) + delete [] fSymbolTable; +} + + +// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments +template <>bool Writer::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); } +template bool Writer::mightNeedPadSegment() { return false; } + + +template +ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) +{ + if ( fOptions.outputKind() == Options::kObjectFile ) { + // when doing -r -exported_symbols_list, don't creat proxy for a symbol + // that is supposed to be exported. We want an error instead + // ld does not report error when -r is used and exported symbols are not defined. + if ( fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) + return NULL; + else + return new UndefinedSymbolProxyAtom(*this, name); + } + else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) ) + return new UndefinedSymbolProxyAtom(*this, name); + else + return NULL; +} + +template +uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) +{ + // flat namespace images use zero for all ordinals + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) + return 0; + + // is an UndefinedSymbolProxyAtom + if ( lib == this ) + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) + return DYNAMIC_LOOKUP_ORDINAL; + + std::map::iterator pos = fLibraryToOrdinal.find(lib); + if ( pos != fLibraryToOrdinal.end() ) + return pos->second; + + throw "can't find ordinal for imported symbol"; +} + +template +ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) +{ + return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses)); +} + + +template +uint64_t Writer::write(std::vector& atoms, + std::vector& stabs, + class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs, bool overridesDylibWeakDefines) +{ + fAllAtoms = &atoms; + fStabs = &stabs; + fEntryPoint = entryPointAtom; + fDyldHelper = dyldHelperAtom; + fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; + fCanScatter = canScatter; + fCpuConstraint = cpuConstraint; + fBiggerThanTwoGigs = biggerThanTwoGigs; + fHasWeakExports = overridesDylibWeakDefines; // dyld needs to search this image as if it had weak exports + + try { + // Set for create UUID + if (createUUID) + fUUIDAtom->generate(); + + // remove uneeded dylib load commands + optimizeDylibReferences(); + + // check for mdynamic-no-pic codegen + scanForAbsoluteReferences(); + + // create inter-library stubs + synthesizeStubs(); + + // create SegmentInfo and SectionInfo objects and assign all atoms to a section + partitionIntoSections(); + + // segment load command can now be sized and padding can be set + adjustLoadCommandsAndPadding(); + + // assign each section a file offset + assignFileOffsets(); + + // if need to add branch islands, reassign file offsets + if ( addBranchIslands() ) + assignFileOffsets(); + + // build symbol table and relocations + buildLinkEdit(); + + // write map file if requested + writeMap(); + + // write everything + return writeAtoms(); + } catch (...) { + // clean up if any errors + (void)unlink(fFilePath); + throw; + } +} + +template +void Writer::buildLinkEdit() +{ + this->collectExportedAndImportedAndLocalAtoms(); + this->buildSymbolTable(); + this->buildFixups(); + this->adjustLinkEditSections(); +} + + + +template +uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) +{ + return atom->getAddress(); +// SectionInfo* info = (SectionInfo*)atom->getSection(); +// return info->getBaseAddress() + atom->getSectionOffset(); +} + + +template <> +const char* Writer::symbolTableName(const ObjectFile::Atom* atom) +{ + static unsigned int counter = 0; + const char* name = atom->getName(); + if ( strncmp(name, "cstring=", 8) == 0 ) + asprintf((char**)&name, "LC%u", counter++); + return name; +} + +template +const char* Writer::symbolTableName(const ObjectFile::Atom* atom) +{ + return atom->getName(); +} + +template +void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_strx + entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom))); + + // set n_type + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { + entry->set_n_type(N_EXT | N_ABS); + } + else { + entry->set_n_type(N_EXT | N_SECT); + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { + if ( fOptions.keepPrivateExterns() ) + entry->set_n_type(N_EXT | N_SECT | N_PEXT); + } + } + + // set n_sect (section number of implementation ) + uint8_t sectionIndex = atom->getSection()->getIndex(); + entry->set_n_sect(sectionIndex); + + // the __mh_execute_header is magic and must be an absolute symbol + if ( (sectionIndex==0) + && (fOptions.outputKind() == Options::kDynamicExecutable) + && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) + entry->set_n_type(N_EXT | N_ABS); + + // set n_desc + uint16_t desc = 0; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + desc |= N_WEAK_DEF; + fHasWeakExports = true; + } + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) + entry->set_n_value(atom->getSectionOffset()); + else + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + +template +void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_strx + entry->set_n_strx(this->fStringsAtom->add(atom->getName())); + + // set n_type + if ( (fOptions.outputKind() == Options::kObjectFile) + && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) + && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) + entry->set_n_type(N_UNDF | N_EXT | N_PEXT); + else if ( fOptions.prebind() ) + entry->set_n_type(N_PBUD | N_EXT); + else + entry->set_n_type(N_UNDF | N_EXT); + + // set n_sect + entry->set_n_sect(0); + + uint16_t desc = 0; + if ( fOptions.outputKind() != Options::kObjectFile ) { + // set n_desc ( high byte is library ordinal, low byte is reference type ) + std::map::iterator pos = fStubsMap.find(atom); + if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) + desc = REFERENCE_FLAG_UNDEFINED_LAZY; + else + desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY; + try { + uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); + //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); + SET_LIBRARY_ORDINAL(desc, ordinal); + } + catch (const char* msg) { + throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) { + uint8_t align = atom->getAlignment().powerOf2; + // always record custom alignment of common symbols to match what compiler does + SET_COMM_ALIGN(desc, align); + } + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + desc |= N_REF_TO_WEAK; + fReferencesWeakImports = true; + } + // set weak_import attribute + if ( fWeakImportMap[atom] ) + desc |= N_WEAK_REF; + entry->set_n_desc(desc); + + // set n_value, zero for import proxy and size for tentative definition + entry->set_n_value(atom->getSize()); +} + + +template +void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) +{ + // set n_strx + const char* symbolName = this->symbolTableName(atom); + char anonName[32]; + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { + sprintf(anonName, "l%u", fAnonNameIndex++); + symbolName = anonName; + } + entry->set_n_strx(this->fStringsAtom->add(symbolName)); + + // set n_type + uint8_t type = N_SECT; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) + type = N_ABS; + if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) + type |= N_PEXT; + entry->set_n_type(type); + + // set n_sect (section number of implementation ) + uint8_t sectIndex = atom->getSection()->getIndex(); + if ( sectIndex == 0 ) { + // see synthesized lable for mach_header needs special section number... + if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) + sectIndex = 1; + } + entry->set_n_sect(sectIndex); + + // set n_desc + uint16_t desc = 0; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + desc |= N_WEAK_DEF; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + entry->set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) + entry->set_n_value(atom->getSectionOffset()); + else + entry->set_n_value(this->getAtomLoadAddress(atom)); +} + + +template +void Writer::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) +{ + macho_nlist

entry; + + // set n_strx + entry.set_n_strx(fStringsAtom->add(name)); + + // set n_type + entry.set_n_type(N_SECT); + + // set n_sect (section number of implementation ) + entry.set_n_sect(atom.getSection()->getIndex()); + + // set n_desc + entry.set_n_desc(0); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); + + // add + fLocalExtraLabels.push_back(entry); +} + + + +template +void Writer::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) +{ + macho_nlist

entry; + + // set n_strx + entry.set_n_strx(fStringsAtom->add(name)); + + // set n_type + entry.set_n_type(N_SECT|N_EXT); + + // set n_sect (section number of implementation ) + entry.set_n_sect(atom.getSection()->getIndex()); + + // set n_desc + entry.set_n_desc(0); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); + + // add + fGlobalExtraLabels.push_back(entry); +} + +template +void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for (uint32_t i=0; i < count; ++i, ++entry) { + ObjectFile::Atom* atom = atoms[i]; + if ( &atoms == &fExportedAtoms ) { + this->setExportNlist(atom, entry); + } + else if ( &atoms == &fImportedAtoms ) { + this->setImportNlist(atom, entry); + } + else { + this->setLocalNlist(atom, entry); + } + } +} + +template +void Writer::copyNlistRange(const std::vector >& entries, uint32_t startIndex) +{ + for ( typename std::vector >::const_iterator it = entries.begin(); it != entries.end(); ++it) + fSymbolTable[startIndex++] = *it; +} + + +template +struct NListNameSorter +{ + NListNameSorter(StringsLinkEditAtom* pool) : fStringPool(pool) {} + + bool operator()(const macho_nlist& left, const macho_nlist& right) + { + return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0); + } +private: + StringsLinkEditAtom* fStringPool; +}; + + +template +void Writer::buildSymbolTable() +{ + fSymbolTableStabsStartIndex = 0; + fSymbolTableStabsCount = fStabs->size(); + fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; + fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size(); + fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; + fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size(); + fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; + fSymbolTableImportCount = fImportedAtoms.size(); + + // allocate symbol table + fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; + fSymbolTable = new macho_nlist

[fSymbolTableCount]; + + // fill in symbol table and string pool (do stabs last so strings are at end of pool) + setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size()); + if ( fLocalExtraLabels.size() != 0 ) + copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size()); + setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size()); + if ( fGlobalExtraLabels.size() != 0 ) { + copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size()); + // re-sort combined range + std::sort( &fSymbolTable[fSymbolTableExportStartIndex], + &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount], + NListNameSorter(fStringsAtom) ); + } + setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); + addStabs(fSymbolTableStabsStartIndex); + + // set up module table + if ( fModuleInfoAtom != NULL ) + fModuleInfoAtom->setName(); +} + + + +template +bool Writer::shouldExport(const ObjectFile::Atom& atom) const +{ + switch ( atom.getSymbolTableInclusion() ) { + case ObjectFile::Atom::kSymbolTableNotIn: + return false; + case ObjectFile::Atom::kSymbolTableInAndNeverStrip: + return true; + case ObjectFile::Atom::kSymbolTableInAsAbsolute: + case ObjectFile::Atom::kSymbolTableIn: + switch ( atom.getScope() ) { + case ObjectFile::Atom::scopeGlobal: + return true; + case ObjectFile::Atom::scopeLinkageUnit: + return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); + default: + return false; + } + break; + } + return false; +} + +template +void Writer::collectExportedAndImportedAndLocalAtoms() +{ + const int atomCount = fAllAtoms->size(); + // guess at sizes of each bucket to minimize re-allocations + fImportedAtoms.reserve(100); + fExportedAtoms.reserve(atomCount/2); + fLocalSymbolAtoms.reserve(atomCount); + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + // only named atoms go in symbol table + if ( atom->getName() != NULL ) { + // put atom into correct bucket: imports, exports, locals + //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + fImportedAtoms.push_back(atom); + break; + case ObjectFile::Atom::kTentativeDefinition: + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { + fImportedAtoms.push_back(atom); + break; + } + // else fall into + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + if ( this->shouldExport(*atom) ) + fExportedAtoms.push_back(atom); + else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) + && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) + fLocalSymbolAtoms.push_back(atom); + break; + } + } + // when geneating a .o file, dtrace static probes become local labels + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == A::kDtraceProbe ) { + // dtrace probe points to be add back into generated .o file + this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + } + } + } + // when linking kernel, old style dtrace static probes become global labels + else if ( fOptions.readerOptions().fForStatic ) { + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == A::kDtraceProbe ) { + // dtrace probe points to be add back into generated .o file + this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + } + } + } + } + + // sort exported atoms by name + std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter()); + // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable) + std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter()); +} + + +template +uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) +{ + switch ( stab.type ) { + case N_FUN: + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of function N_FUN has size + return stab.atom->getSize(); + } + else { + // start of function N_FUN has address + return getAtomLoadAddress(stab.atom); + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + if ( stab.atom == NULL ) + // some weird assembly files have slines not associated with a function + return stab.value; + else + // all these stab types need their value changed from an offset in the atom to an address + return getAtomLoadAddress(stab.atom) + stab.value; + case N_STSYM: + case N_LCSYM: + case N_BNSYM: + // all these need address of atom + return getAtomLoadAddress(stab.atom);; + case N_ENSYM: + return stab.atom->getSize(); + case N_SO: + if ( stab.atom == NULL ) { + return 0; + } + else { + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of translation unit N_SO has address of end of last atom + return getAtomLoadAddress(stab.atom) + stab.atom->getSize(); + } + else { + // start of translation unit N_SO has address of end of first atom + return getAtomLoadAddress(stab.atom); + } + } + break; + default: + return stab.value; + } +} + +template +uint32_t Writer::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) +{ + switch (stab.type) { + case N_SO: + if ( (stab.string == NULL) || stab.string[0] == '\0' ) { + return this->fStringsAtom->emptyString(); + break; + } + // fall into uniquing case + case N_SOL: + case N_BINCL: + case N_EXCL: + return this->fStringsAtom->addUnique(stab.string); + break; + default: + if ( stab.string == NULL ) + return 0; + else if ( stab.string[0] == '\0' ) + return this->fStringsAtom->emptyString(); + else + return this->fStringsAtom->add(stab.string); + } + return 0; +} + +template +uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) +{ + // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN + if ( stab.type == N_FUN ) + return stab.other; + else if ( stab.atom != NULL ) + return stab.atom->getSection()->getIndex(); + else + return stab.other; +} + +template +void Writer::addStabs(uint32_t startIndex) +{ + macho_nlist

* entry = &fSymbolTable[startIndex]; + for(std::vector::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) { + const ObjectFile::Reader::Stab& stab = *it; + entry->set_n_type(stab.type); + entry->set_n_sect(sectionIndexForStab(stab)); + entry->set_n_desc(stab.desc); + entry->set_n_value(valueForStab(stab)); + entry->set_n_strx(stringOffsetForStab(stab)); + } +} + + + +template +uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) +{ + // search imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableImportStartIndex; + ++i; + } + + // search locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableLocalStartIndex; + ++i; + } + + // search exports + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableExportStartIndex; + ++i; + } + + throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); +} + + +template <> +bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const +{ + switch ( target.getSymbolTableInclusion() ) { + case ObjectFile::Atom::kSymbolTableNotIn: + return false; + case ObjectFile::Atom::kSymbolTableInAsAbsolute: + case ObjectFile::Atom::kSymbolTableIn: + case ObjectFile::Atom::kSymbolTableInAndNeverStrip: + return true; + }; + return false; +} + +template +bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const +{ + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + case ObjectFile::Atom::kTentativeDefinition: + if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal ) + return false; + else + return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit); + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + return shouldExport(target); + } + return false; +} + +template +void Writer::buildFixups() +{ + if ( fOptions.outputKind() == Options::kObjectFile ) { + this->buildObjectFileFixups(); + } + else { + if ( fOptions.keepRelocations() ) + this->buildObjectFileFixups(); + this->buildExecutableFixups(); + } +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool external = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + case x86_64::kGroupSubordinate: + return 0; + + case x86_64::kPointer: + case x86_64::kPointerWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPointerDiff32: + case x86_64::kPointerDiff: + { + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex(); + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolIndex); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + fSectionRelocs.push_back(reloc1); + fSectionRelocs.push_back(reloc2); + return 2; + } + + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kDtraceProbeSite: + case x86_64::kDtraceIsEnabledSite: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32_1: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_1); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32_2: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_2); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32_4: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_4); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kBranchPCRel8: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(0); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); + fSectionRelocs.push_back(reloc1); + return 1; + + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // generates no relocs + return 0; + } + return 0; +} + + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); + + if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) + warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", + target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); + + + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + case x86::kGroupSubordinate: + return 0; + + case x86::kPointer: + case x86::kPointerWeakImport: + case x86::kAbsolute32: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case x86::kPointerDiff16: + case x86::kPointerDiff: + { + //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n", + // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(), + // ref->getFromTarget().getAddress(), ref->getFromTargetOffset()); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); + sreloc2->set_r_type(GENERIC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case x86::kPCRel32WeakImport: + case x86::kPCRel32: + case x86::kPCRel16: + case x86::kPCRel8: + case x86::kDtraceProbeSite: + case x86::kDtraceIsEnabledSite: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case x86::kDtraceTypeReference: + case x86::kDtraceProbe: + // generates no relocs + return 0; + + } + return 0; +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind(); + + if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) + warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", + target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); + + + switch ( kind ) { + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + return 0; + + case arm::kPointer: + case arm::kReadOnlyPointer: + case arm::kPointerWeakImport: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(ARM_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case arm::kPointerDiff: + { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(ARM_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(ARM_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case arm::kBranch24WeakImport: + case arm::kBranch24: + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(ARM_RELOC_BR24); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case arm::kThumbBranch22WeakImport: + case arm::kThumbBranch22: + if ( !isExtern && (ref->getTargetOffset() != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(ARM_THUMB_RELOC_BR22); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case arm::kDtraceTypeReference: + case arm::kDtraceProbe: + // generates no relocs + return 0; + + } + return 0; +} + +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } +template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 2; +} + +template <> +uint8_t Writer::getRelocPointerSize() +{ + return 3; +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +template <> +uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + return addObjectRelocs_powerpc(atom, ref); +} + +// +// addObjectRelocs and addObjectRelocs are almost exactly the same, so +// they use a common addObjectRelocs_powerpc() method. +// +template +uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) +{ + ObjectFile::Atom& target = ref->getTarget(); + bool isExtern = this->makesExternalRelocatableReference(target); + uint32_t symbolIndex = 0; + if ( isExtern ) + symbolIndex = this->symbolIndex(target); + uint32_t sectionNum = target.getSection()->getIndex(); + uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind(); + + switch ( kind ) { + case A::kNoFixUp: + case A::kFollowOn: + case A::kGroupSubordinate: + return 0; + + case A::kPointer: + case A::kPointerWeakImport: + if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) { + // use scattered reloc is target offset is outside target + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(getRelocPointerSize()); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + else { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(getRelocPointerSize()); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case A::kPointerDiff16: + case A::kPointerDiff32: + case A::kPointerDiff64: + { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); + if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(sreloc1->r_length()); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kBranch24WeakImport: + case A::kBranch24: + case A::kDtraceProbeSite: + case A::kDtraceIsEnabledSite: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR24); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case A::kBranch14: + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_type(PPC_RELOC_BR14); + reloc1.set_r_extern(isExtern); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + fSectionRelocs.push_back(reloc1); + return 1; + + case A::kPICBaseLow16: + case A::kPICBaseLow14: + { + pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kPICBaseHigh16: + { + pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kAbsLow14: + case A::kAbsLow16: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() >> 16); + else + reloc2.set_r_address(toAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kAbsHigh16: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HI16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kAbsHigh16AddLow: + { + pint_t toAddr = target.getAddress() + ref->getTargetOffset(); + uint32_t overflow = 0; + if ( (toAddr & 0x00008000) != 0 ) + overflow = 0x10000; + if ( (ref->getTargetOffset() == 0) || isExtern ) { + reloc1.set_r_address(address); + if ( isExtern ) + reloc1.set_r_symbolnum(symbolIndex); + else + reloc1.set_r_symbolnum(sectionNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(PPC_RELOC_HA16); + } + else { + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(target.getAddress()); + } + if ( isExtern ) + reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); + else + reloc2.set_r_address(toAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); + return 2; + } + + case A::kDtraceTypeReference: + case A::kDtraceProbe: + // generates no relocs + return 0; + } + return 0; +} + + + +// +// There are cases when an entry in the indirect symbol table is the magic value +// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens +// the content of the corresponding part of the __nl_symbol_pointer section +// must also change. +// +template +bool Writer::indirectSymbolIsLocal(const ObjectFile::Reference* ref) const +{ + // use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend + return ( !this->shouldExport(ref->getTarget()) || (ref->getTargetOffset() != 0) ); +} + + +template +void Writer::buildObjectFileFixups() +{ + uint32_t relocIndex = 0; + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName); + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers + || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + curSection->fRelocOffset = relocIndex; + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + //fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName()); + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers + || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) { + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / atom->getSize(); + uint32_t undefinedSymbolIndex; + if ( curSection->fAllStubs ) { + ObjectFile::Atom& stubTarget =ref->getTarget(); + ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget(); + undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); + //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); + } + else if ( curSection->fAllNonLazyPointers) { + // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend + if ( this->indirectSymbolIsLocal(ref) ) + undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + else + undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); + } + else { + // should never get here, fAllLazyPointers not used in generated .o files + undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + } + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + if ( curSection->fAllLazyPointers ) { + ObjectFile::Atom& target = ref->getTarget(); + ObjectFile::Atom& fromTarget = ref->getFromTarget(); + if ( &fromTarget == NULL ) { + warning("lazy pointer %s missing initial binding", atom->getDisplayName()); + } + else { + bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition)) + && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ); + macho_relocation_info

reloc1; + reloc1.set_r_address(atom->getSectionOffset()); + reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(); + reloc1.set_r_extern(isExtern); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + fSectionRelocs.push_back(reloc1); + ++relocIndex; + } + } + else if ( curSection->fAllStubs ) { + relocIndex += this->addObjectRelocs(atom, ref); + } + } + else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) { + relocIndex += this->addObjectRelocs(atom, ref); + } + } + } + curSection->fRelocCount = relocIndex - curSection->fRelocOffset; + } + } + } + + // reverse the relocs + std::reverse(fSectionRelocs.begin(), fSectionRelocs.end()); + + // now reverse section reloc offsets + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; + } + } + +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + switch ( ref.getKind() ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( fSlideable ) + return true; + } + return false; +} + + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + switch ( ref.getKind() ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( fSlideable ) + return true; + } + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // illegal in dylibs/bundles, until we support TEXT relocs + return fSlideable; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // illegal until we support TEXT relocs + return true; + case ObjectFile::Atom::kAbsoluteSymbol: + // absolute symbbols only allowed in static executables + return ( fOptions.outputKind() != Options::kStaticExecutable); + } + } + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + return false; +} + +template <> +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) +{ + if ( ref.getKind() == arm::kReadOnlyPointer ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // illegal in dylibs/bundles, until we support TEXT relocs + return fSlideable; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // illegal until we support TEXT relocs + return true; + case ObjectFile::Atom::kAbsoluteSymbol: + // absolute symbbols only allowed in static executables + return ( fOptions.outputKind() != Options::kStaticExecutable); + } + } + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( fSlideable ) { + macho_relocation_info

reloc; + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(sectInfo->getIndex()); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(false); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(reloc); + atomSection->fHasTextLocalRelocs = true; + return true; + } + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + } + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + switch ( ref.getKind() ) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( fSlideable ) { + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); + reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc1.set_r_symbolnum(sectInfo->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + reloc2.set_r_address(targetAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.push_back(reloc1); + fInternalRelocs.push_back(reloc2); + atomSection->fHasTextLocalRelocs = true; + return true; + } + break; + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + if ( fSlideable ) { + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); + reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc1.set_r_symbolnum(sectInfo->getIndex()); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); + reloc2.set_r_address(targetAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + fInternalRelocs.push_back(reloc1); + fInternalRelocs.push_back(reloc2); + atomSection->fHasTextLocalRelocs = true; + return true; + } + } + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( ref.getKind() == arm::kReadOnlyPointer ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( fSlideable ) { + macho_relocation_info

reloc; + SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(sectInfo->getIndex()); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(false); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(reloc); + atomSection->fHasTextLocalRelocs = true; + return true; + } + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + } + return false; +} + + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) +{ + // text relocs not supported (usually never needed because of RIP addressing) + return false; +} + +template <> +bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) +{ + // text relocs not supported + return false; +} + +template <> +bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( ref.getKind() == x86::kAbsolute32 ) { + macho_relocation_info

reloc; + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // a reference to the absolute address of something in another linkage unit can be + // encoded as an external text reloc in a dylib or bundle + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(true); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + fExternalRelocs.push_back(reloc); + atomSection->fHasTextExternalRelocs = true; + return true; + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + } + return false; +} + +template +bool Writer::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) +{ + return false; +} + + + + +template +typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const +{ + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + // in main executables, the only way regular symbols are indirected is if -interposable is used + if ( fOptions.outputKind() == Options::kDynamicExecutable ) { + if ( this->shouldExport(target) && fOptions.interposable(target.getName()) ) + return kRelocExternal; + else if ( fSlideable ) + return kRelocInternal; + else + return kRelocNone; + } + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + else if ( this->shouldExport(target) && + ((fOptions.nameSpace() == Options::kFlatNameSpace) + || (fOptions.nameSpace() == Options::kForceFlatNameSpace) + || fOptions.interposable(target.getName())) + && (target.getName() != NULL) + && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // + return kRelocExternal; + else if ( fSlideable ) + return kRelocInternal; + else + return kRelocNone; + case ObjectFile::Atom::kWeakDefinition: + // all calls to global weak definitions get indirected + if ( this->shouldExport(target) ) + return kRelocExternal; + else if ( fSlideable ) + return kRelocInternal; + else + return kRelocNone; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + return kRelocExternal; + case ObjectFile::Atom::kAbsoluteSymbol: + return kRelocNone; + } + return kRelocNone; +} + +template +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for 32-bit architectures, the r_address field in relocs + // for final linked images is the offset from the first segment + uint64_t result = address - fSegmentInfos[0]->fBaseAddress; + // or the offset from the first writable segment if built split-seg + if ( fOptions.splitSeg() ) + result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0x7FFFFFFF ) { + throwf("image too large: address can't fit in 31-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for x86_64, the r_address field in relocs for final linked images + // is the offset from the start address of the first writable segment + uint64_t result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + // for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address. + // the 10.5 dyld, iterprets the r_address as: + // 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise + // 2) an offset from the base address of the first writable segment + // For dyld, r_address is always the offset from the base address + uint64_t result; + bool badFor10_4 = false; + if ( fWritableSegmentPastFirst4GB ) { + if ( fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5 ) + badFor10_4 = true; + result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + result = address - fSegmentInfos[0]->fBaseAddress; + if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) && (result > 0x7FFFFFFF) ) + badFor10_4 = true; + } + if ( badFor10_4 ) { + throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + return result; +} + + +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } +template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; } + +template +void Writer::buildExecutableFixups() +{ + fIndirectTableAtom->fTable.reserve(50); // minimize reallocations + std::vector& segmentInfos = fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* curSegment = segmentInfos[i]; + std::vector& sectionInfos = curSegment->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + //fprintf(stderr, "starting section %s\n", curSection->fSectionName); + std::vector& sectionAtoms = curSection->fAtoms; + if ( ! curSection->fAllZeroFill ) { + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers + || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + const int atomCount = sectionAtoms.size(); + for (int k=0; k < atomCount; ++k) { + ObjectFile::Atom* atom = sectionAtoms[k]; + std::vector& refs = atom->getReferences(); + const int refCount = refs.size(); + //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); + for (int l=0; l < refCount; ++l) { + ObjectFile::Reference* ref = refs[l]; + if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol + if ( atom->getSize() != sizeof(pint_t) ) { + warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath()); + } + ObjectFile::Atom* pointerTarget = &(ref->getTarget()); + if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + pointerTarget = ((LazyPointerAtom*)atom)->getTarget(); + } + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(pint_t); + uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) + undefinedSymbolIndex = this->symbolIndex(*pointerTarget); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X), pointerTarget=%s\n", + // indirectTableIndex, undefinedSymbolIndex, pointerTarget->getDisplayName()); + fIndirectTableAtom->fTable.push_back(entry); + if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + uint8_t preboundLazyType; + if ( fOptions.prebind() && (fDyldHelper != NULL) + && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) { + // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid + macho_scattered_relocation_info

pblaReloc; + pblaReloc.set_r_scattered(true); + pblaReloc.set_r_pcrel(false); + pblaReloc.set_r_length(); + pblaReloc.set_r_type(preboundLazyType); + pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); + pblaReloc.set_r_value(fDyldHelper->getAddress()); + fInternalRelocs.push_back(*((macho_relocation_info

*)&pblaReloc)); + } + else if ( fSlideable ) { + // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides + macho_relocation_info

dyldHelperReloc; + uint32_t sectionNum = 1; + if ( fDyldHelper != NULL ) + sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex(); + //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); + dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); + dyldHelperReloc.set_r_symbolnum(sectionNum); + dyldHelperReloc.set_r_pcrel(false); + dyldHelperReloc.set_r_length(); + dyldHelperReloc.set_r_extern(false); + dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(dyldHelperReloc); + } + } + } + else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) { + if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { + throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { + case kRelocNone: + // no reloc needed + break; + case kRelocInternal: + { + macho_relocation_info

internalReloc; + SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); + uint32_t sectionNum = sectInfo->getIndex(); + // special case _mh_dylib_header and friends which are not in any real section + if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) + sectionNum = 1; + internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); + internalReloc.set_r_symbolnum(sectionNum); + internalReloc.set_r_pcrel(false); + internalReloc.set_r_length(); + internalReloc.set_r_extern(false); + internalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fInternalRelocs.push_back(internalReloc); + } + break; + case kRelocExternal: + { + macho_relocation_info

externalReloc; + externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); + externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); + externalReloc.set_r_pcrel(false); + externalReloc.set_r_length(); + externalReloc.set_r_extern(true); + externalReloc.set_r_type(GENERIC_RELOC_VANILLA); + fExternalRelocs.push_back(externalReloc); + } + break; + } + } + else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { + if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { + if ( fOptions.warnAboutTextRelocs() ) + warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); + if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { + // relocs added to fInternalRelocs + } + else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) { + // relocs added to fExternalRelocs + } + else { + throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. " + "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath()); + } + } + } + if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { + ObjectFile::Atom* stubTarget = ((StubAtom*)atom)->getTarget(); + uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS; + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / atom->getSize(); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; + //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize()); + fIndirectTableAtom->fTable.push_back(entry); + } + } + } + } + } + if ( fSplitCodeToDataContentAtom != NULL ) + fSplitCodeToDataContentAtom->encode(); +} + + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (ppc::ReferenceKinds)ref->getKind() ) { + case ppc::kPICBaseHigh16: + fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); + break; + case ppc::kPointerDiff32: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc::kPointerDiff64: + fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc::kNoFixUp: + case ppc::kGroupSubordinate: + case ppc::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + // ignore + break; + default: + warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (ppc64::ReferenceKinds)ref->getKind() ) { + case ppc64::kPICBaseHigh16: + fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); + break; + case ppc64::kPointerDiff32: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc64::kPointerDiff64: + fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case ppc64::kNoFixUp: + case ppc64::kGroupSubordinate: + case ppc64::kPointer: + case ppc64::kPointerWeakImport: + case ppc64::kPICBaseLow16: + case ppc64::kPICBaseLow14: + // ignore + break; + default: + warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (x86::ReferenceKinds)ref->getKind() ) { + case x86::kPointerDiff: + if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 ) + fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); + else + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case x86::kNoFixUp: + case x86::kGroupSubordinate: + case x86::kPointer: + case x86::kPointerWeakImport: + // ignore + break; + case x86::kPCRel32: + case x86::kPCRel32WeakImport: + if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment) + || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) { + fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); + break; + } + // fall into warning case + default: + warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (x86_64::ReferenceKinds)ref->getKind() ) { + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPointerDiff32: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case x86_64::kPointerDiff: + fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case x86_64::kNoFixUp: + case x86_64::kGroupSubordinate: + case x86_64::kPointer: + // ignore + break; + default: + warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template <> +void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) +{ + switch ( (arm::ReferenceKinds)ref->getKind() ) { + case arm::kPointerDiff: + fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); + break; + case arm::kNoFixUp: + case arm::kGroupSubordinate: + case arm::kPointer: + case arm::kPointerWeakImport: + case arm::kReadOnlyPointer: + // ignore + break; + default: + warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName()); + fSplitCodeToDataContentAtom->setCantEncode(); + } +} + +template +bool Writer::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to) +{ + switch ( to.getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // segments with same permissions slide together + return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable()) + || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) ); + } + throw "ld64 internal error"; +} + + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fd, &ppcNop, 4, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint32_t ppcNop; + OSWriteBigInt32(&ppcNop, 0, 0x60000000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fd, &ppcNop, 4, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint8_t x86Nop = 0x90; + for (uint32_t p=from; p < to; ++p) + ::pwrite(fd, &x86Nop, 1, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + uint8_t x86Nop = 0x90; + for (uint32_t p=from; p < to; ++p) + ::pwrite(fd, &x86Nop, 1, p); +} + +template <> +void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) +{ + // FIXME: need thumb nop? + uint32_t armNop; + OSWriteLittleInt32(&armNop, 0, 0xe1a00000); + for (uint32_t p=from; p < to; p += 4) + ::pwrite(fd, &armNop, 4, p); +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; ++p) + *p = 0x90; +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + for (uint8_t* p=from; p < to; ++p) + *p = 0x90; +} + +template <> +void Writer::copyNoOps(uint8_t* from, uint8_t* to) +{ + // fixme: need thumb nop? + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); +} + +static const char* stringName(const char* str) +{ + if ( strncmp(str, "cstring=", 8) == 0) { + static char buffer[1024]; + char* t = buffer; + *t++ = '\"'; + for(const char*s = &str[8]; *s != '\0'; ++s) { + switch(*s) { + case '\n': + *t++ = '\\'; + *t++ = 'n'; + break; + case '\t': + *t++ = '\\'; + *t++ = 't'; + break; + default: + *t++ = *s; + break; + } + if ( t > &buffer[1020] ) { + *t++= '\"'; + *t++= '.'; + *t++= '.'; + *t++= '.'; + *t++= '\0'; + return buffer; + } + } + *t++= '\"'; + *t++= '\0'; + return buffer; + } + else { + return str; + } +} + + +template <> const char* Writer::getArchString() { return "ppc"; } +template <> const char* Writer::getArchString() { return "ppc64"; } +template <> const char* Writer::getArchString() { return "i386"; } +template <> const char* Writer::getArchString() { return "x86_64"; } +template <> const char* Writer::getArchString() { return "arm"; } + +template +void Writer::writeMap() +{ + if ( fOptions.generatedMapPath() != NULL ) { + FILE* mapFile = fopen(fOptions.generatedMapPath(), "w"); + if ( mapFile != NULL ) { + // write output path + fprintf(mapFile, "# Path: %s\n", fFilePath); + // write output architecure + fprintf(mapFile, "# Arch: %s\n", getArchString()); + // write UUID + if ( fUUIDAtom != NULL ) { + const uint8_t* uuid = fUUIDAtom->getUUID(); + fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n", + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + } + // write table of object files + std::map readerToOrdinal; + std::map ordinalToReader; + std::map readerToFileOrdinal; + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + if ( ! (*secit)->fVirtualSection ) { + std::vector& sectionAtoms = (*secit)->fAtoms; + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Reader* reader = (*ait)->getFile(); + uint32_t readerOrdinal = (*ait)->getOrdinal(); + std::map::iterator pos = readerToOrdinal.find(reader); + if ( pos == readerToOrdinal.end() ) { + readerToOrdinal[reader] = readerOrdinal; + ordinalToReader[readerOrdinal] = reader; + } + } + } + } + } + fprintf(mapFile, "# Object files:\n"); + fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); + uint32_t fileIndex = 0; + readerToFileOrdinal[this] = fileIndex++; + for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first != 0 ) { + fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->getPath()); + readerToFileOrdinal[it->second] = fileIndex++; + } + } + // write table of sections + fprintf(mapFile, "# Sections:\n"); + fprintf(mapFile, "# Address\tSize \tSegment\tSection\n"); + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + if ( ! (*secit)->fVirtualSection ) { + SectionInfo* sect = *secit; + fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->getBaseAddress(), sect->fSize, + (*segit)->fName, sect->fSectionName); + } + } + } + // write table of symbols + fprintf(mapFile, "# Symbols:\n"); + fprintf(mapFile, "# Address\tSize \tFile Name\n"); + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + if ( ! (*secit)->fVirtualSection ) { + std::vector& sectionAtoms = (*secit)->fAtoms; + bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0); + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Atom* atom = *ait; + fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(), + readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName()); + } + } + } + } + fclose(mapFile); + } + else { + warning("could not write map file: %s\n", fOptions.generatedMapPath()); + } + } +} + +static const char* sCleanupFile = NULL; +static void cleanup(int sig) +{ + ::signal(sig, SIG_DFL); + if ( sCleanupFile != NULL ) { + ::unlink(sCleanupFile); + } + if ( sig == SIGINT ) + ::exit(1); +} + + +template +uint64_t Writer::writeAtoms() +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) ) + throwf("can't write output file: %s", fFilePath); + + int permissions = 0777; + if ( fOptions.outputKind() == Options::kObjectFile ) + permissions = 0666; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where fFilePath file is not writable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + (void)unlink(fFilePath); + + // try to allocate buffer for entire output file content + int fd = -1; + SectionInfo* lastSection = fSegmentInfos.back()->fSections.back(); + uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096); + uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1); + uint8_t* atomBuffer = NULL; + bool streaming = false; + if ( wholeBuffer == NULL ) { + fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); + atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; + streaming = true; + // install signal handlers to delete output file if program is killed + sCleanupFile = fFilePath; + ::signal(SIGINT, cleanup); + ::signal(SIGBUS, cleanup); + ::signal(SIGSEGV, cleanup); + } + uint32_t size = 0; + uint32_t end = 0; + try { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + bool isTextSeg = (strcmp(curSegment->fName, "__TEXT") == 0); + std::vector& sectionInfos = curSegment->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + SectionInfo* curSection = *secit; + std::vector& sectionAtoms = curSection->fAtoms; + //printf("writing with max atom size 0x%X\n", fLargestAtomSize); + //fprintf(stderr, "writing %lu atoms for section %s\n", sectionAtoms.size(), curSection->fSectionName); + if ( ! curSection->fAllZeroFill ) { + end = curSection->fFileOffset; + bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0); + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Atom* atom = *ait; + if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) + && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) + && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) { + uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset(); + if ( fileOffset != end ) { + if ( needsNops ) { + // fill gaps with no-ops + if ( streaming ) + writeNoOps(fd, end, fileOffset); + else + copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]); + } + else if ( streaming ) { + // zero fill gaps + if ( (fileOffset-end) == 4 ) { + uint32_t zero = 0; + ::pwrite(fd, &zero, 4, end); + } + else { + uint8_t zero = 0x00; + for (uint32_t p=end; p < fileOffset; ++p) + ::pwrite(fd, &zero, 1, p); + } + } + } + uint64_t atomSize = atom->getSize(); + if ( streaming ) { + if ( atomSize > fLargestAtomSize ) + throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX", + atom->getDisplayName(), atomSize, fLargestAtomSize); + } + else { + if ( fileOffset > fileBufferSize ) + throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX", + atom->getDisplayName(), fileOffset, fileBufferSize); + } + uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset]; + end = fileOffset+atomSize; + // copy raw bytes + atom->copyRawContent(buffer); + // apply any fix-ups + try { + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( fOptions.outputKind() == Options::kObjectFile ) { + // doing ld -r + // skip fix-ups for undefined targets + if ( &(ref->getTarget()) != NULL ) + this->fixUpReferenceRelocatable(ref, atom, buffer); + } + else { + // producing final linked image + this->fixUpReferenceFinal(ref, atom, buffer); + } + } + } + catch (const char* msg) { + throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); + } + //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n", + // fileOffset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); + if ( streaming ) { + // write out + ::pwrite(fd, buffer, atomSize, fileOffset); + } + else { + if ( (fileOffset + atomSize) > size ) + size = fileOffset + atomSize; + } + } + } + } + } + } + + // update content based UUID + if ( fOptions.getUUIDMode() == Options::kUUIDContent ) { + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + if ( streaming ) { + // if output file file did not fit in memory, re-read file to generate md5 hash + uint32_t kMD5BufferSize = 16*1024; + uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize); + if ( md5Buffer != NULL ) { + CC_MD5_CTX md5State; + CC_MD5_Init(&md5State); + ::lseek(fd, 0, SEEK_SET); + ssize_t len; + while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 ) + CC_MD5_Update(&md5State, md5Buffer, len); + CC_MD5_Final(digest, &md5State); + ::free(md5Buffer); + } + else { + // if malloc fails, fall back to random uuid + ::uuid_generate_random(digest); + } + fUUIDAtom->setContent(digest); + uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); + fUUIDAtom->copyRawContent(atomBuffer); + ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset); + } + else { + // if output file fit in memory, just genrate an md5 hash in memory + #if 1 + // temp hack for building on Tiger + CC_MD5_CTX md5State; + CC_MD5_Init(&md5State); + CC_MD5_Update(&md5State, wholeBuffer, size); + CC_MD5_Final(digest, &md5State); + #else + CC_MD5(wholeBuffer, size, digest); + #endif + fUUIDAtom->setContent(digest); + uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); + fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]); + } + } + } + catch (...) { + if ( sCleanupFile != NULL ) + ::unlink(sCleanupFile); + throw; + } + + // finish up + if ( streaming ) { + delete [] atomBuffer; + close(fd); + // restore default signal handlers + sCleanupFile = NULL; + ::signal(SIGINT, SIG_DFL); + ::signal(SIGBUS, SIG_DFL); + ::signal(SIGSEGV, SIG_DFL); + } + else { + // write whole output file in one chunk + fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); + ::pwrite(fd, wholeBuffer, size, 0); + close(fd); + delete [] wholeBuffer; + } + + return end; +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + int64_t displacement; + int64_t baseAddr; + uint32_t instruction; + uint32_t newInstruction; + uint64_t targetAddr = 0; + uint32_t firstDisp; + uint32_t nextDisp; + uint32_t opcode; + bool relocateableExternal = false; + bool is_bl; + bool is_blx; + bool targetIsThumb; + + if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { + targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); + } + + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + switch ( (arm::ReferenceKinds)(ref->getKind()) ) { + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + // do nothing + break; + case arm::kPointerWeakImport: + case arm::kPointer: + // If this is the lazy pointers section, then set all lazy pointers to + // point to the dyld stub binding helper. + if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers + || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound lazy pointer to another dylib ==> pointer contains zero + LittleEndian::set32(*fixUp, 0); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + // prebound lazy pointer to withing this dylib ==> pointer contains address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + break; + } + } + else if ( relocateableExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + else { + // pointer contains target address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + } + break; + case arm::kPointerDiff: + LittleEndian::set32(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case arm::kReadOnlyPointer: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); + break; + } + break; + case arm::kBranch24WeakImport: + case arm::kBranch24: + displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + // The pc added will be +8 from the pc + displacement -= 8; + // fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); + // max positive displacement is 0x007FFFFF << 2 + // max negative displacement is 0xFF800000 << 2 + if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { + throwf("b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + instruction = LittleEndian::get32(*fixUp); + // Make sure we are calling arm with bl, thumb with blx + is_bl = ((instruction & 0xFF000000) == 0xEB000000); + is_blx = ((instruction & 0xFE000000) == 0xFA000000); + if ( is_bl && ref->getTarget().isThumb() ) { + uint32_t opcode = 0xFA000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; + newInstruction = opcode | h_bit | disp; + } + else if ( is_blx && !ref->getTarget().isThumb() ) { + uint32_t opcode = 0xEB000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + newInstruction = opcode | disp; + } + else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { + throwf("don't know how to convert instruction %x referencing %s to thumb", + instruction, ref->getTarget().getDisplayName()); + } + else { + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); + } + LittleEndian::set32(*fixUp, newInstruction); + break; + case arm::kThumbBranch22WeakImport: + case arm::kThumbBranch22: + instruction = LittleEndian::get32(*fixUp); + is_bl = ((instruction & 0xF8000000) == 0xF8000000); + is_blx = ((instruction & 0xF8000000) == 0xE8000000); + targetIsThumb = ref->getTarget().isThumb(); + + // The pc added will be +4 from the pc + baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; + // If the target is not thumb, we will be generating a blx instruction + // Since blx cannot have the low bit set, set bit[1] of the target to + // bit[1] of the base address, so that the difference is a multiple of + // 4 bytes. + if ( !targetIsThumb ) { + targetAddr &= -3ULL; + targetAddr |= (baseAddr & 2LL); + } + displacement = targetAddr - baseAddr; + + // max positive displacement is 0x003FFFFE + // max negative displacement is 0xFFC00000 + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the first + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the next + // 11 bits of the displacement, as well as differentiating bl and blx. + { + firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; + nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; + if ( is_bl && !targetIsThumb ) { + opcode = 0xE800F000; + } + else if ( is_blx && targetIsThumb ) { + opcode = 0xF800F000; + } + else if ( !is_bl && !is_blx && !targetIsThumb ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, ref->getTarget().getDisplayName()); + } + else { + opcode = instruction & 0xF800F800; + } + newInstruction = opcode | (nextDisp << 16) | firstDisp; + LittleEndian::set32(*fixUp, newInstruction); + } + break; + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + if ( inAtom->isThumb() ) { + // change 32-bit blx call site to two thumb NOPs + LittleEndian::set32(*fixUp, 0x46C046C0); + } + else { + // change call site to a NOP + LittleEndian::set32(*fixUp, 0xE1A00000); + } + break; + case arm::kDtraceTypeReference: + case arm::kDtraceProbe: + // nothing to fix up + break; + default: + throw "boom shaka laka"; + } +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + int64_t displacement; + uint32_t instruction; + uint32_t newInstruction; + uint64_t targetAddr = 0; + int64_t baseAddr; + uint32_t firstDisp; + uint32_t nextDisp; + uint32_t opcode; + bool relocateableExternal = false; + bool is_bl; + bool is_blx; + bool targetIsThumb; + + if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { + targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); + } + + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + switch ( (arm::ReferenceKinds)(ref->getKind()) ) { + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + // do nothing + break; + case arm::kPointer: + case arm::kReadOnlyPointer: + case arm::kPointerWeakImport: + { + if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content + if ( this->indirectSymbolIsLocal(ref) ) + LittleEndian::set32(*fixUp, targetAddr); + else + LittleEndian::set32(*fixUp, 0); + } + else if ( relocateableExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + LittleEndian::set32(*fixUp, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + } + else { + // internal relocation + if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { + // pointer contains target address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + } + else { + // pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + } + break; + case arm::kPointerDiff: + LittleEndian::set32(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + case arm::kBranch24WeakImport: + case arm::kBranch24: + displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + // The pc added will be +8 from the pc + displacement -= 8; + // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + // max positive displacement is 0x007FFFFF << 2 + // max negative displacement is 0xFF800000 << 2 + if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { + throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + } + instruction = LittleEndian::get32(*fixUp); + // Make sure we are calling arm with bl, thumb with blx + is_bl = ((instruction & 0xFF000000) == 0xEB000000); + is_blx = ((instruction & 0xFE000000) == 0xFA000000); + if ( is_bl && ref->getTarget().isThumb() ) { + uint32_t opcode = 0xFA000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; + newInstruction = opcode | h_bit | disp; + } + else if ( is_blx && !ref->getTarget().isThumb() ) { + uint32_t opcode = 0xEB000000; + uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; + newInstruction = opcode | disp; + } + else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { + throwf("don't know how to convert instruction %x referencing %s to thumb", + instruction, ref->getTarget().getDisplayName()); + } + else { + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); + } + LittleEndian::set32(*fixUp, newInstruction); + break; + case arm::kThumbBranch22WeakImport: + case arm::kThumbBranch22: + instruction = LittleEndian::get32(*fixUp); + is_bl = ((instruction & 0xF8000000) == 0xF8000000); + is_blx = ((instruction & 0xF8000000) == 0xE8000000); + targetIsThumb = ref->getTarget().isThumb(); + + // The pc added will be +4 from the pc + baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; + // If the target is not thumb, we will be generating a blx instruction + // Since blx cannot have the low bit set, set bit[1] of the target to + // bit[1] of the base address, so that the difference is a multiple of + // 4 bytes. + if (!targetIsThumb) { + targetAddr &= -3ULL; + targetAddr |= (baseAddr & 2LL); + } + displacement = targetAddr - baseAddr; + + //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + // max positive displacement is 0x003FFFFE + // max negative displacement is 0xFFC00000 + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the first + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the next + // 11 bits of the displacement, as well as differentiating bl and blx. + firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; + nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; + if ( is_bl && !targetIsThumb ) { + opcode = 0xE800F000; + } + else if ( is_blx && targetIsThumb ) { + opcode = 0xF800F000; + } + else if ( !is_bl && !is_blx && !targetIsThumb ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, ref->getTarget().getDisplayName()); + } + else { + opcode = instruction & 0xF800F800; + } + newInstruction = opcode | (nextDisp << 16) | firstDisp; + LittleEndian::set32(*fixUp, newInstruction); + break; + case arm::kDtraceProbe: + case arm::kDtraceTypeReference: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + uint8_t* dtraceProbeSite; + const int64_t kTwoGigLimit = 0x7FFFFFFF; + const int64_t kSixtyFourKiloLimit = 0x7FFF; + const int64_t kOneTwentyEightLimit = 0x7F; + int64_t displacement; + x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + case x86::kGroupSubordinate: + // do nothing + break; + case x86::kPointerWeakImport: + case x86::kPointer: + { + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + else { + // pointer contains target address + //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86::kPointerDiff: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + LittleEndian::set32(*fixUp, (uint32_t)displacement); + break; + case x86::kPointerDiff16: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) + throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); + LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); + break; + case x86::kDtraceProbeSite: + // change call site to a NOP + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x90; // 1-byte nop + dtraceProbeSite[0] = 0x0F; // 4-byte nop + dtraceProbeSite[1] = 0x1F; + dtraceProbeSite[2] = 0x40; + dtraceProbeSite[3] = 0x00; + break; + case x86::kDtraceIsEnabledSite: + // change call site to a clear eax + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x33; // xorl eax,eax + dtraceProbeSite[0] = 0xC0; + dtraceProbeSite[1] = 0x90; // 1-byte nop + dtraceProbeSite[2] = 0x90; // 1-byte nop + dtraceProbeSite[3] = 0x90; // 1-byte nop + break; + case x86::kPCRel32WeakImport: + case x86::kPCRel32: + case x86::kPCRel16: + case x86::kPCRel8: + displacement = 0; + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throw "codegen problem, can't use rel32 to external symbol"; + case ObjectFile::Atom::kTentativeDefinition: + displacement = 0; + break; + case ObjectFile::Atom::kAbsoluteSymbol: + displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + } + if ( kind == x86::kPCRel8 ) { + if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel8 out of range in %s", inAtom->getDisplayName()); + } + *(int8_t*)fixUp = (int8_t)displacement; + } + else if ( kind == x86::kPCRel16 ) { + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel16 out of range in %s", inAtom->getDisplayName()); + } + LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); + } + else { + if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel32 out of range in %s", inAtom->getDisplayName()); + } + LittleEndian::set32(*fixUp, (int32_t)displacement); + } + break; + case x86::kAbsolute32: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + // pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); + break; + } + break; + case x86::kDtraceTypeReference: + case x86::kDtraceProbe: + // nothing to fix up + break; + } +} + + + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t kTwoGigLimit = 0x7FFFFFFF; + const int64_t kSixtyFourKiloLimit = 0x7FFF; + const int64_t kOneTwentyEightLimit = 0x7F; + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + bool isExtern = this->makesExternalRelocatableReference(ref->getTarget()); + int64_t displacement; + x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); + switch ( kind ) { + case x86::kNoFixUp: + case x86::kFollowOn: + case x86::kGroupSubordinate: + // do nothing + break; + case x86::kPointer: + case x86::kPointerWeakImport: + case x86::kAbsolute32: + { + if ( isExtern ) { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + else if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero + if ( this->indirectSymbolIsLocal(ref) ) + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + else + LittleEndian::set32(*fixUp, 0); + } + else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { + // internal relocation => pointer contains target address + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + else { + // internal relocation to tentative ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } + } + break; + case x86::kPointerDiff: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + LittleEndian::set32(*fixUp, (uint32_t)displacement); + break; + case x86::kPointerDiff16: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) + throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); + LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); + break; + case x86::kPCRel8: + case x86::kPCRel16: + case x86::kPCRel32: + case x86::kPCRel32WeakImport: + case x86::kDtraceProbeSite: + case x86::kDtraceIsEnabledSite: + { + if ( isExtern ) + displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + else + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + if ( kind == x86::kPCRel8 ) { + displacement += 3; + if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName()); + } + int8_t byte = (int8_t)displacement; + *((int8_t*)fixUp) = byte; + } + else if ( kind == x86::kPCRel16 ) { + displacement += 2; + if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("rel16 out of range in %s", inAtom->getDisplayName()); + } + int16_t word = (int16_t)displacement; + LittleEndian::set16(*((uint16_t*)fixUp), word); + } + else { + if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { + //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement, + // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + throwf("rel32 out of range in %s", inAtom->getDisplayName()); + } + LittleEndian::set32(*fixUp, (int32_t)displacement); + } + } + break; + case x86::kDtraceProbe: + case x86::kDtraceTypeReference: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; + uint8_t* dtraceProbeSite; + int64_t displacement = 0; + switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + case x86_64::kGroupSubordinate: + // do nothing + break; + case x86_64::kPointerWeakImport: + case x86_64::kPointer: + { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { + // external relocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation + // pointer contains target address + //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86_64::kPointerDiff32: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) + throw "32-bit pointer difference out of range"; + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); + break; + case x86_64::kPointerDiff: + LittleEndian::set64(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + // if GOT entry was optimized away, change movq instruction to a leaq + if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) { + //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName()); + uint8_t* opcodes = (uint8_t*)fixUp; + if ( opcodes[-2] != 0x8B ) + throw "GOT load reloc does not point to a movq instruction"; + opcodes[-2] = 0x8D; + } + // fall into general rel32 case + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel8: + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kTentativeDefinition: + displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + displacement = (ref->getTarget().getSectionOffset() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throw "codegen problem, can't use rel32 to external symbol"; + break; + } + switch ( ref->getKind() ) { + case x86_64::kPCRel32_1: + displacement -= 1; + break; + case x86_64::kPCRel32_2: + displacement -= 2; + break; + case x86_64::kPCRel32_4: + displacement -= 4; + break; + case x86_64::kBranchPCRel8: + displacement += 3; + break; + } + if ( ref->getKind() == x86_64::kBranchPCRel8 ) { + if ( (displacement > 127) || (displacement < (-128)) ) { + fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n", + inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); + throw "rel8 out of range"; + } + *((int8_t*)fixUp) = (int8_t)displacement; + } + else { + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + fprintf(stderr, "call out of range from %s (%llX) in %s to %s (%llX) in %s\n", + inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + } + break; + case x86_64::kDtraceProbeSite: + // change call site to a NOP + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x90; // 1-byte nop + dtraceProbeSite[0] = 0x0F; // 4-byte nop + dtraceProbeSite[1] = 0x1F; + dtraceProbeSite[2] = 0x40; + dtraceProbeSite[3] = 0x00; + break; + case x86_64::kDtraceIsEnabledSite: + // change call site to a clear eax + dtraceProbeSite = (uint8_t*)fixUp; + dtraceProbeSite[-1] = 0x48; // xorq eax,eax + dtraceProbeSite[0] = 0x33; + dtraceProbeSite[1] = 0xC0; + dtraceProbeSite[2] = 0x90; // 1-byte nop + dtraceProbeSite[3] = 0x90; // 1-byte nop + break; + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + bool external = this->makesExternalRelocatableReference(ref->getTarget()); + uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; + int64_t displacement = 0; + int32_t temp32; + switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { + case x86_64::kNoFixUp: + case x86_64::kFollowOn: + case x86_64::kGroupSubordinate: + // do nothing + break; + case x86_64::kPointer: + case x86_64::kPointerWeakImport: + { + if ( external ) { + // external relocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + else { + // internal relocation ==> pointer contains target address + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; + case x86_64::kPointerDiff32: + // addend in content + LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset() - ref->getFromTargetOffset() ); + break; + case x86_64::kPointerDiff: + // addend in content + LittleEndian::set64(*fixUp, ref->getTargetOffset() - ref->getFromTargetOffset() ); + break; + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kDtraceProbeSite: + case x86_64::kDtraceIsEnabledSite: + case x86_64::kPCRel32: + case x86_64::kPCRel32_1: + case x86_64::kPCRel32_2: + case x86_64::kPCRel32_4: + // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had + temp32 = ref->getTargetOffset(); + if ( external ) { + // extern relocation contains addend + displacement = temp32; + } + else { + // internal relocations contain delta to target address + displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + } + switch ( ref->getKind() ) { + case x86_64::kPCRel32_1: + displacement -= 1; + break; + case x86_64::kPCRel32_2: + displacement -= 2; + break; + case x86_64::kPCRel32_4: + displacement -= 4; + break; + } + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel32 out of range"; + } + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + break; + case x86_64::kBranchPCRel8: + // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had + temp32 = ref->getTargetOffset(); + if ( external ) { + // extern relocation contains addend + displacement = temp32; + } + else { + // internal relocations contain delta to target address + displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 1); + } + if ( (displacement > 127) || (displacement < (-128)) ) { + //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throw "rel8 out of range"; + } + *((int8_t*)fixUp) = (int8_t)displacement; + break; + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoadWeakImport: + // contains addend (usually zero) + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); + break; + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // nothing to fix up + break; + } +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, true); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +template <> +void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const +{ + fixUpReference_powerpc(ref, inAtom, buffer, false); +} + +// +// ppc and ppc64 are mostly the same, so they share a template specialzation +// +template +void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const +{ + uint32_t instruction; + uint32_t newInstruction; + int64_t displacement; + uint64_t targetAddr = 0; + uint64_t picBaseAddr; + uint16_t instructionLowHalf; + uint16_t instructionHighHalf; + uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; + pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()]; + bool relocateableExternal = false; + const int64_t picbase_twoGigLimit = 0x80000000; + + if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { + targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + if ( finalLinkedImage ) + relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); + else + relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); + } + + switch ( (typename A::ReferenceKinds)(ref->getKind()) ) { + case A::kNoFixUp: + case A::kFollowOn: + case A::kGroupSubordinate: + // do nothing + break; + case A::kPointerWeakImport: + case A::kPointer: + { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers + || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound lazy pointer to another dylib ==> pointer contains zero + P::setP(*fixUpPointer, 0); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + // prebound lazy pointer to withing this dylib ==> pointer contains address + P::setP(*fixUpPointer, targetAddr); + break; + } + } + else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero + if ( this->indirectSymbolIsLocal(ref) ) + P::setP(*fixUpPointer, targetAddr); + else + P::setP(*fixUpPointer, 0); + } + else if ( relocateableExternal ) { + if ( fOptions.prebind() ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // prebound external relocation ==> pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // prebound external relocation to internal atom ==> pointer contains target address + addend + P::setP(*fixUpPointer, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // external relocation ==> pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + } + } + else { + // internal relocation + if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { + // pointer contains target address + //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr); + P::setP(*fixUpPointer, targetAddr); + } + else { + // pointer contains addend + P::setP(*fixUpPointer, ref->getTargetOffset()); + } + } + } + break; + case A::kPointerDiff64: + P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kPointerDiff32: + P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kPointerDiff16: + P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case A::kDtraceProbeSite: + if ( finalLinkedImage ) { + // change call site to a NOP + BigEndian::set32(*fixUp, 0x60000000); + } + else { + // set bl instuction to branch to address zero in .o file + int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kDtraceIsEnabledSite: + if ( finalLinkedImage ) { + // change call site to a li r3,0 + BigEndian::set32(*fixUp, 0x38600000); + } + else { + // set bl instuction to branch to address zero in .o file + int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kBranch24WeakImport: + case A::kBranch24: + { + //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); + int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + else { + const int64_t bl_eightMegLimit = 0x00FFFFFF; + if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s", + displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath()); + } + } + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); + //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kBranch14: + { + int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( relocateableExternal ) { + // doing "ld -r" to an external symbol + // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target + displacement -= ref->getTarget().getAddress(); + } + const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; + if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { + //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); + throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + + //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset()); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC); + //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); + BigEndian::set32(*fixUp, newInstruction); + } + break; + case A::kPICBaseLow16: + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + instructionLowHalf = (displacement & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kPICBaseLow14: + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + if ( (displacement & 0x3) != 0 ) + throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement); + instructionLowHalf = (displacement & 0xFFFC); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kPICBaseHigh16: + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + displacement = targetAddr - picBaseAddr; + if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) + throw "32-bit pic-base out of range"; + instructionLowHalf = displacement >> 16; + if ( (displacement & 0x00008000) != 0 ) + ++instructionLowHalf; + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsLow16: + if ( relocateableExternal && !finalLinkedImage ) + targetAddr -= ref->getTarget().getAddress(); + instructionLowHalf = (targetAddr & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsLow14: + if ( relocateableExternal && !finalLinkedImage ) + targetAddr -= ref->getTarget().getAddress(); + if ( (targetAddr & 0x3) != 0 ) + throw "bad address for absolute lo14 instruction fix-up"; + instructionLowHalf = (targetAddr & 0xFFFF); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsHigh16: + if ( relocateableExternal ) { + if ( finalLinkedImage ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // use target address + break; + case ObjectFile::Atom::kAbsoluteSymbol: + targetAddr = ref->getTarget().getSectionOffset(); + break; + } + } + else { + targetAddr -= ref->getTarget().getAddress(); + } + } + instructionHighHalf = (targetAddr >> 16); + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf; + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kAbsHigh16AddLow: + if ( relocateableExternal ) { + if ( finalLinkedImage ) { + switch (ref->getTarget().getDefinitionKind()) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); + break; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + // use target address + break; + case ObjectFile::Atom::kAbsoluteSymbol: + targetAddr = ref->getTarget().getSectionOffset(); + break; + } + } + else { + targetAddr -= ref->getTarget().getAddress(); + } + } + if ( targetAddr & 0x00008000 ) + targetAddr += 0x00010000; + instruction = BigEndian::get32(*fixUp); + newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16); + BigEndian::set32(*fixUp, newInstruction); + break; + case A::kDtraceTypeReference: + case A::kDtraceProbe: + // nothing to fix up + break; + } +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (ppc::ReferenceKinds)kind ) { + case ppc::kNoFixUp: + case ppc::kFollowOn: + case ppc::kGroupSubordinate: + case ppc::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPointerDiff16: + case ppc::kPointerDiff32: + case ppc::kPointerDiff64: + case ppc::kDtraceProbe: + case ppc::kDtraceProbeSite: + case ppc::kDtraceIsEnabledSite: + case ppc::kDtraceTypeReference: + // these are never used to call external functions + return false; + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + case ppc::kBranch14: + // these are used to call external functions + return true; + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + case ppc::kPICBaseHigh16: + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + // these are only used to call external functions + // in -mlong-branch stubs + switch ( ref->getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // if the .o file this atom came from has long-branch stubs, + // then assume these instructions in a stub. + // Otherwise, these are a direct reference to something (maybe a runtime text reloc) + return ( inAtom->getFile()->hasLongBranchStubs() ); + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + break; + } + return false; +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (arm::ReferenceKinds)kind ) { + case arm::kBranch24: + case arm::kBranch24WeakImport: + case arm::kThumbBranch22: + case arm::kThumbBranch22WeakImport: + return true; + case arm::kNoFixUp: + case arm::kFollowOn: + case arm::kGroupSubordinate: + case arm::kPointer: + case arm::kReadOnlyPointer: + case arm::kPointerWeakImport: + case arm::kPointerDiff: + case arm::kDtraceProbe: + case arm::kDtraceProbeSite: + case arm::kDtraceIsEnabledSite: + case arm::kDtraceTypeReference: + return false; + } + return false; +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (ppc64::ReferenceKinds)kind ) { + case ppc::kNoFixUp: + case ppc::kFollowOn: + case ppc::kGroupSubordinate: + case ppc::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPointerDiff16: + case ppc::kPointerDiff32: + case ppc::kPointerDiff64: + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + case ppc::kPICBaseHigh16: + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + case ppc::kDtraceProbe: + case ppc::kDtraceProbeSite: + case ppc::kDtraceIsEnabledSite: + case ppc::kDtraceTypeReference: + // these are never used to call external functions + return false; + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + case ppc::kBranch14: + // these are used to call external functions + return true; + } + return false; +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); +} + +template <> +bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport); +} + + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPointerWeakImport: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template <> +bool Writer::weakImportReferenceKind(uint8_t kind) +{ + return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport || + kind == arm::kPointerWeakImport); +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template <> +bool Writer::GOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + switch ( kind ) { + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + return true; + } + return false; +} + +template <> +bool Writer::optimizableGOTReferenceKind(uint8_t kind) +{ + return false; +} + +// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity +template bool Writer::needsModuleTable() {return fOptions.needsModuleTable(); } +template <> bool Writer::needsModuleTable() { return false; } +template <> bool Writer::needsModuleTable() { return false; } + + +template +void Writer::optimizeDylibReferences() +{ + //fprintf(stderr, "original ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); + //} + // find unused dylibs that can be removed + std::map ordinalToReader; + std::map readerAliases; + for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + ObjectFile::Reader* reader = it->first; + std::map::iterator aliasPos = fLibraryAliases.find(reader); + if ( aliasPos != fLibraryAliases.end() ) { + // already noticed that this reader has same install name as another reader + readerAliases[reader] = aliasPos->second; + } + else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || fOptions.deadStripDylibs()) ) { + // this reader can be optimized away + it->second = 0xFFFFFFFF; + typename std::map* >::iterator pos = fLibraryToLoadCommand.find(reader); + if ( pos != fLibraryToLoadCommand.end() ) + pos->second->optimizeAway(); + } + else { + // mark this reader as using it ordinal + std::map::iterator pos = ordinalToReader.find(it->second); + if ( pos == ordinalToReader.end() ) + ordinalToReader[it->second] = reader; + else + readerAliases[reader] = pos->second; + } + } + // renumber ordinals (depends on iterator walking in ordinal order) + // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals + uint32_t newOrdinal = 0; + for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first <= fLibraryToOrdinal.size() ) { + if ( ! it->second->isLazyLoadedDylib() ) + fLibraryToOrdinal[it->second] = ++newOrdinal; + } + } + for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first <= fLibraryToOrdinal.size() ) { + if ( it->second->isLazyLoadedDylib() ) { + fLibraryToOrdinal[it->second] = ++newOrdinal; + } + } + } + + // linker does not error when dylib ordinal exceeds 250 + if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) ) + throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal); + + // add aliases (e.g. -lm points to libSystem.dylib) + for (std::map::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) { + fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second]; + } + + //fprintf(stderr, "new ordinals table:\n"); + //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { + // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); + //} +} + + +template <> +void Writer::scanForAbsoluteReferences() +{ + // arm codegen never has absolute references. FIXME: Is this correct? +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // x86_64 codegen never has absolute references +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used + if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch (ref->getKind()) { + case x86::kAbsolute32: + throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); + return; + } + } + } + } +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used + if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch (ref->getKind()) { + case ppc::kAbsLow16: + case ppc::kAbsLow14: + case ppc::kAbsHigh16: + case ppc::kAbsHigh16AddLow: + throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); + return; + } + } + } + } +} + + +// for ppc64 look for any -mdynamic-no-pic codegen +template <> +void Writer::scanForAbsoluteReferences() +{ + // only do this for main executable + if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch (ref->getKind()) { + case ppc64::kAbsLow16: + case ppc64::kAbsLow14: + case ppc64::kAbsHigh16: + case ppc64::kAbsHigh16AddLow: + //fprintf(stderr, "found -mdynamic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + // shrink page-zero and add pad segment to compensate + fPadSegmentInfo = new SegmentInfo(); + strcpy(fPadSegmentInfo->fName, "__4GBFILL"); + fPageZeroAtom->setSize(0x1000); + return; + } + } + } + } +} + + +template +void Writer::insertDummyStubs() +{ + // only needed for x86 +} + +template <> +void Writer::insertDummyStubs() +{ + // any 5-byte stubs that cross a 32-byte cache line may update incorrectly + std::vector*> betterStubs; + for (std::vector*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) { + switch (betterStubs.size() % 64 ) { + case 12:// stub would occupy 0x3C->0x41 + case 25:// stub would occupy 0x7D->0x82 + case 38:// stub would occupy 0xBE->0xC3 + case 51:// stub would occupy 0xFF->0x04 + betterStubs.push_back(new StubAtom(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub + break; + } + betterStubs.push_back(*it); + } + // replace + fAllSynthesizedStubs.clear(); + fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end()); +} + +template +void Writer::synthesizeStubs() +{ + switch ( fOptions.outputKind() ) { + case Options::kObjectFile: + // these output kinds never have stubs + return; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDynamicExecutable: + // try to synthesize stubs for these + break; + } + + // walk every atom and reference + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getTargetBinding()) { + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + break; + case ObjectFile::Reference::kBoundByName: + case ObjectFile::Reference::kBoundDirectly: + ObjectFile::Atom& target = ref->getTarget(); + // build map of which symbols need weak importing + if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + bool weakImport = this->weakImportReferenceKind(ref->getKind()); + // Obj-C Symbols in Leopard Can't Be Weak Linked + // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols + // in dylibs that are weakly linked. + if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) { + typename std::map* >::iterator pos; + pos = fLibraryToLoadCommand.find(target.getFile()); + if ( pos != fLibraryToLoadCommand.end() ) { + if ( pos->second->linkedWeak() ) + weakImport = true; + } + } + std::map::iterator pos = fWeakImportMap.find(&target); + if ( pos == fWeakImportMap.end() ) { + // target not in fWeakImportMap, so add + fWeakImportMap[&target] = weakImport; + } + else { + // target in fWeakImportMap, check for weakness mismatch + if ( pos->second != weakImport ) { + // found mismatch + switch ( fOptions.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", target.getName()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + // update if we use a weak_import or a strong import from this dylib + if ( fWeakImportMap[&target] ) + fDylibReadersWithWeakImports.insert(target.getFile()); + else + fDylibReadersWithNonWeakImports.insert(target.getFile()); + } + // create stubs as needed + if ( this->stubableReference(atom, ref) + && (ref->getTargetOffset() == 0) + && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { + ObjectFile::Atom* stub = NULL; + std::map::iterator pos = fStubsMap.find(&target); + if ( pos == fStubsMap.end() ) { + bool forLazyDylib = false; + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + case ObjectFile::Atom::kTentativeDefinition: + break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + if ( target.getFile()->isLazyLoadedDylib() ) + forLazyDylib = true; + break; + } + stub = new StubAtom(*this, target, forLazyDylib); + fStubsMap[&target] = stub; + } + else { + stub = pos->second; + } + // alter reference to use stub instead + ref->setTarget(*stub, 0); + } + else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) { + throwf("illegal reference to %s in lazy loaded dylib from %s in %s", + target.getDisplayName(), atom->getDisplayName(), + atom->getFile()->getPath()); + } + // create GOT slots (non-lazy pointers) as needed + else if ( this->GOTReferenceKind(ref->getKind()) ) { + // + bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); + bool useGOT; + if ( fBiggerThanTwoGigs ) { + // in big images use GOT for all zero fill atoms + // this is just a heuristic and may need to be re-examined + useGOT = mustUseGOT || ref->getTarget().isZeroFill(); + } + else { + // < 2GB image so remove all GOT entries that we can + useGOT = mustUseGOT; + } + // if this GOT usage cannot be optimized away then make a GOT enry + if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) + useGOT = true; + if ( useGOT ) { + ObjectFile::Atom* nlp = NULL; + std::map::iterator pos = fGOTMap.find(&target); + if ( pos == fGOTMap.end() ) { + nlp = new NonLazyPointerAtom(*this, target); + fGOTMap[&target] = nlp; + } + else { + nlp = pos->second; + } + // alter reference to use non lazy pointer instead + ref->setTarget(*nlp, ref->getTargetOffset()); + } + } + } + } + } + + // sort stubs + std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); + + // add dummy fast stubs (x86 only) + if ( !fOptions.slowx86Stubs() ) + this->insertDummyStubs(); + + // sort lazy pointers + std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); + std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter()); + + + // add stubs to fAllAtoms + if ( fAllSynthesizedStubs.size() != 0 ) { + std::vector textStubs; + std::vector importStubs; + for (typename std::vector*>::iterator sit=fAllSynthesizedStubs.begin(); sit != fAllSynthesizedStubs.end(); ++sit) { + ObjectFile::Atom* stubAtom = *sit; + if ( strcmp(stubAtom->getSegment().getName(), "__TEXT") == 0 ) + textStubs.push_back(stubAtom); + else + importStubs.push_back(stubAtom); + } + // any helper stubs go right after regular stubs + if ( fAllSynthesizedStubHelpers.size() != 0 ) + textStubs.insert(textStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); + // insert text stubs right after __text section + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) { + // found end of __text section, insert stubs here + fAllAtoms->insert(it, textStubs.begin(), textStubs.end()); + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( importStubs.size() != 0 ) { + // insert __IMPORTS stubs right before __LINKEDIT + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + // for i386 where stubs are not in __TEXT segment + if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0)) + || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) { + // insert stubs at end of __IMPORT segment, or before __LINKEDIT + fAllAtoms->insert(it, importStubs.begin(), importStubs.end()); + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + } + } + + + // add lazy dylib pointers to fAllAtoms + if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert lazy pointers, __dyld section not found"; + } + } + + // add lazy pointers to fAllAtoms + if ( fAllSynthesizedLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert lazy pointers, __dyld section not found"; + } + } + + // add non-lazy pointers to fAllAtoms + if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) + && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0) + || ((strcmp(prevAtom->getSectionName(), "__data") == 0) && + ((fOptions.outputKind() == Options::kDyld) || (fOptions.outputKind() == Options::kStaticExecutable))) ) ) { + // found end of __dyld section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert non-lazy pointers, __dyld section not found"; + } + } + + // build LC_SEGMENT_SPLIT_INFO content now that all atoms exist + if ( fSplitCodeToDataContentAtom != NULL ) { + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getTargetBinding()) { + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + break; + case ObjectFile::Reference::kBoundByName: + case ObjectFile::Reference::kBoundDirectly: + if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) { + this->addCrossSegmentRef(atom, ref); + } + break; + } + } + } + } + +} + + +template +void Writer::partitionIntoSections() +{ + const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); + + // for every atom, set its sectionInfo object and section offset + // build up fSegmentInfos along the way + ObjectFile::Section* curSection = NULL; + SectionInfo* currentSectionInfo = NULL; + SegmentInfo* currentSegmentInfo = NULL; + SectionInfo* cstringSectionInfo = NULL; + unsigned int sectionIndex = 1; + fSegmentInfos.reserve(8); + for (unsigned int i=0; i < fAllAtoms->size(); ++i) { + ObjectFile::Atom* atom = (*fAllAtoms)[i]; + if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) { + if ( oneSegmentCommand ) { + if ( currentSegmentInfo == NULL ) { + currentSegmentInfo = new SegmentInfo(); + currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) ) + cstringSectionInfo = currentSectionInfo; + } + else { + if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { + currentSegmentInfo = new SegmentInfo(); + strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); + uint32_t initprot = 0; + if ( atom->getSegment().isContentReadable() ) + initprot |= VM_PROT_READ; + if ( atom->getSegment().isContentWritable() ) + initprot |= VM_PROT_WRITE; + if ( atom->getSegment().isContentExecutable() ) + initprot |= VM_PROT_EXECUTE; + if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) ) + initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker + currentSegmentInfo->fInitProtection = initprot; + if ( initprot == 0 ) + currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 + else if ( fOptions.architecture() == CPU_TYPE_ARM ) + currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init + else + currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + std::vector& customSegProtections = fOptions.customSegmentProtections(); + for(std::vector::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) { + if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) { + currentSegmentInfo->fInitProtection = it->init; + currentSegmentInfo->fMaxProtection = it->max; + } + } + currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); + currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); + if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) ) + currentSegmentInfo->fIndependentAddress = true; + this->fSegmentInfos.push_back(currentSegmentInfo); + } + currentSectionInfo = new SectionInfo(); + currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large + strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); + strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); + currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; + // check for -sectalign override + std::vector& alignmentOverrides = fOptions.sectionAlignments(); + for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { + if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) + currentSectionInfo->fAlignment = it->alignment; + } + currentSectionInfo->fAllZeroFill = atom->isZeroFill(); + currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); + if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) + currentSectionInfo->setIndex(sectionIndex++); + currentSegmentInfo->fSections.push_back(currentSectionInfo); + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { + fLoadCommandsSection = currentSectionInfo; + fLoadCommandsSegment = currentSegmentInfo; + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) ) + currentSectionInfo->fAllLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__ld_symbol_ptr") == 0) ) + currentSectionInfo->fAllLazyDylibPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (fOptions.outputKind() == Options::kDyld) && (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) + currentSectionInfo->fAllNonLazyPointers = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub4") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub4") == 0) ) + currentSectionInfo->fAllStubs = true; + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { + currentSectionInfo->fAllSelfModifyingStubs = true; + currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary + } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) ) + currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned + curSection = atom->getSection(); + if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers + || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) { + fSymbolTableCommands->needDynamicTable(); + } + } + // any non-zero fill atoms make whole section marked not-zero-fill + if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) + currentSectionInfo->fAllZeroFill = false; + // change section object to be Writer's SectionInfo object + atom->setSection(currentSectionInfo); + // section alignment is that of a contained atom with the greatest alignment + uint8_t atomAlign = atom->getAlignment().powerOf2; + if ( currentSectionInfo->fAlignment < atomAlign ) + currentSectionInfo->fAlignment = atomAlign; + // calculate section offset for this atom + uint64_t offset = currentSectionInfo->fSize; + uint64_t alignment = 1 << atomAlign; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->getAlignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + atom->setSectionOffset(offset); + uint64_t curAtomSize = atom->getSize(); + currentSectionInfo->fSize = offset + curAtomSize; + // add atom to section vector + currentSectionInfo->fAtoms.push_back(atom); + // update largest size + if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) + fLargestAtomSize = curAtomSize; + } + if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) { + // when merging cstring sections in .o files, all strings need to use the max alignment + uint64_t offset = 0; + uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment; + for (std::vector::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) { + offset = (offset + (cstringAlignment-1)) & (-cstringAlignment); + ObjectFile::Atom* atom = *it; + atom->setSectionOffset(offset); + offset += atom->getSize(); + } + cstringSectionInfo->fSize = offset; + } +} + + +struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; +class TargetAndOffsetComparor +{ +public: + bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const + { + if ( left.atom != right.atom ) + return ( left.atom < right.atom ); + return ( left.offset < right.offset ); + } +}; + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + return this->addPPCBranchIslands(); +} + +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G address space, so no need for branch islands + return false; +} + +template <> +bool Writer::addBranchIslands() +{ + // x86 branches can reach entire 4G size of largest image + return false; +} + +template <> +bool Writer::addBranchIslands() +{ + // arm branch islands not (yet) supported + // you can instead compile with -mlong-call + return false; +} + +template <> +bool Writer::isBranch24Reference(uint8_t kind) +{ + switch (kind) { + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + return true; + } + return false; +} + +template <> +bool Writer::isBranch24Reference(uint8_t kind) +{ + switch (kind) { + case ppc64::kBranch24: + case ppc64::kBranch24WeakImport: + return true; + } + return false; +} + +// +// PowerPC can do PC relative branches as far as +/-16MB. +// If a branch target is >16MB then we insert one or more +// "branch islands" between the branch and its target that +// allows island hoping to the target. +// +// Branch Island Algorithm +// +// If the __TEXT segment < 16MB, then no branch islands needed +// Otherwise, every 15MB into the __TEXT segment is region is +// added which can contain branch islands. Every out of range +// bl instruction is checked. If it crosses a region, an island +// is added to that region with the same target and the bl is +// adjusted to target the island instead. +// +// In theory, if too many islands are added to one region, it +// could grow the __TEXT enough that other previously in-range +// bl branches could be pushed out of range. We reduce the +// probability this could happen by placing the ranges every +// 15MB which means the region would have to be 1MB (256K islands) +// before any branches could be pushed out of range. +// +template +bool Writer::addPPCBranchIslands() +{ + bool log = false; + bool result = false; + // Can only possibly need branch islands if __TEXT segment > 16M + if ( fLoadCommandsSegment->fSize > 16000000 ) { + if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); + const uint32_t kBetweenRegions = 15*1024*1024; // place regions of islands every 15MB in __text section + SectionInfo* textSection = NULL; + for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { + if ( strcmp((*it)->fSectionName, "__text") == 0 ) { + textSection = *it; + if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize); + break; + } + } + const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions; + typedef std::map AtomToIsland; + AtomToIsland regionsMap[kIslandRegionsCount]; + std::vector regionsIslands[kIslandRegionsCount]; + unsigned int islandCount = 0; + if ( log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + + // create islands for branch references that are out of range + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( this->isBranch24Reference(ref->getKind()) ) { + ObjectFile::Atom& target = ref->getTarget(); + int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); + int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); + int64_t displacement = dstAddr - srcAddr; + TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; + const int64_t kFifteenMegLimit = kBetweenRegions; + if ( displacement > kFifteenMegLimit ) { + // create forward branch chain + ObjectFile::Atom* nextTarget = ⌖ + uint64_t nextTargetOffset = ref->getTargetOffset(); + for (int i=kIslandRegionsCount-1; i >=0 ; --i) { + AtomToIsland* region = ®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress(); + if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset); + island->setSection(textSection); + (*region)[finalTargetAndOffset] = island; + if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); + regionsIslands[i].push_back(island); + ++islandCount; + nextTarget = island; + nextTargetOffset = 0; + } + else { + nextTarget = pos->second; + nextTargetOffset = 0; + } + } + } + if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName()); + ref->setTarget(*nextTarget, nextTargetOffset); + } + else if ( displacement < (-kFifteenMegLimit) ) { + // create back branching chain + ObjectFile::Atom* prevTarget = ⌖ + uint64_t prevTargetOffset = ref->getTargetOffset(); + for (int i=0; i < kIslandRegionsCount ; ++i) { + AtomToIsland* region = ®ionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset); + island->setSection(textSection); + (*region)[finalTargetAndOffset] = island; + if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); + regionsIslands[i].push_back(island); + ++islandCount; + prevTarget = island; + prevTargetOffset = 0; + } + else { + prevTarget = pos->second; + prevTargetOffset = 0; + } + } + } + if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); + ref->setTarget(*prevTarget, prevTargetOffset); + } + } + } + } + + // insert islands into __text section and adjust section offsets + if ( islandCount > 0 ) { + if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); + std::vector newAtomList; + newAtomList.reserve(textSection->fAtoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress(); + uint64_t textSectionAlignment = (1 << textSection->fAlignment); + int regionIndex = 0; + uint64_t atomSlide = 0; + uint64_t sectionOffset = 0; + for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getAddress() > islandRegionAddr ) { + uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide; + sectionOffset = islandStartOffset; + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + ++regionIndex; + islandRegionAddr += kBetweenRegions; + uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment; + atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment); + } + newAtomList.push_back(atom); + if ( atomSlide != 0 ) + atom->setSectionOffset(atom->getSectionOffset()+atomSlide); + } + sectionOffset = textSection->fSize+atomSlide; + // put any remaining islands at end of __text section + if ( regionIndex < kIslandRegionsCount ) { + std::vector* regionIslands = ®ionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + ObjectFile::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + islandAtom->setSectionOffset(sectionOffset); + sectionOffset += islandAtom->getSize(); + } + } + + textSection->fAtoms = newAtomList; + textSection->fSize = sectionOffset; + result = true; + } + + } + return result; +} + + +template +void Writer::adjustLoadCommandsAndPadding() +{ + fSegmentCommands->computeSize(); + + // recompute load command section offsets + uint64_t offset = 0; + std::vector& loadCommandAtoms = fLoadCommandsSection->fAtoms; + const unsigned int atomCount = loadCommandAtoms.size(); + for (unsigned int i=0; i < atomCount; ++i) { + ObjectFile::Atom* atom = loadCommandAtoms[i]; + uint64_t alignment = 1 << atom->getAlignment().powerOf2; + offset = ( (offset+alignment-1) & (-alignment) ); + atom->setSectionOffset(offset); + uint32_t atomSize = atom->getSize(); + if ( atomSize > fLargestAtomSize ) + fLargestAtomSize = atomSize; + offset += atomSize; + fLoadCommandsSection->fSize = offset; + } + + std::vector& sectionInfos = fLoadCommandsSegment->fSections; + const int sectionCount = sectionInfos.size(); + uint32_t totalSizeOfHeaderAndLoadCommands = 0; + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + totalSizeOfHeaderAndLoadCommands += curSection->fSize; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + break; + } + uint64_t paddingSize = 0; + if ( fOptions.outputKind() == Options::kDyld ) { + // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address + paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); + } + else if ( fOptions.outputKind() == Options::kObjectFile ) { + // mach-o .o files need no padding between load commands and first section + paddingSize = 0; + } + else if ( fOptions.makeEncryptable() ) { + // want load commands to end on a page boundary, so __text starts on page boundary + paddingSize = 4096 - ((totalSizeOfHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); + fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfHeaderAndLoadCommands+paddingSize); + } + else { + // work backwards from end of segment and lay out sections so that extra room goes to padding atom + uint64_t addr = 0; + for(int j=sectionCount-1; j >=0; --j) { + SectionInfo* curSection = sectionInfos[j]; + addr -= curSection->fSize; + addr = addr & (0 - (1 << curSection->fAlignment)); + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { + addr -= totalSizeOfHeaderAndLoadCommands; + paddingSize = addr % 4096; + break; + } + } + + // if command line requires more padding than this + uint32_t minPad = fOptions.minimumHeaderPad(); + if ( fOptions.maxMminimumHeaderPad() ) { + // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes + uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN; + if ( fOptions.outputKind() == Options::kDynamicLibrary ) + altMin += MAXPATHLEN; + if ( altMin > minPad ) + minPad = altMin; + } + if ( paddingSize < minPad ) { + int extraPages = (minPad - paddingSize + 4095)/4096; + paddingSize += extraPages * 4096; + } + } + + // adjust atom size and update section size + fHeaderPadding->setSize(paddingSize); + for(int j=0; j < sectionCount; ++j) { + SectionInfo* curSection = sectionInfos[j]; + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) + curSection->fSize = paddingSize; + } +} + +// assign file offsets and logical address to all segments +template +void Writer::assignFileOffsets() +{ + bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile); + bool haveFixedSegments = false; + uint64_t fileOffset = 0; + uint64_t nextContiguousAddress = fOptions.baseAddress(); + uint64_t nextReadOnlyAddress = fOptions.baseAddress(); + uint64_t nextWritableAddress = fOptions.baseWritableAddress(); + + // process segments with fixed addresses (-segaddr) + for (std::vector::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( strcmp(curSegment->fName, it->name) == 0 ) { + curSegment->fBaseAddress = it->address; + curSegment->fFixedAddress = true; + break; + } + } + } + + // Run through the segments and each segment's sections to assign addresses + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + + if ( fOptions.splitSeg() ) { + if ( curSegment->fInitProtection & VM_PROT_WRITE ) + nextContiguousAddress = nextWritableAddress; + else + nextContiguousAddress = nextReadOnlyAddress; + } + + fileOffset = (fileOffset+4095) & (-4096); + curSegment->fFileOffset = fileOffset; + + // Set the segment base address + if ( curSegment->fFixedAddress ) + haveFixedSegments = true; + else + curSegment->fBaseAddress = nextContiguousAddress; + + // We've set the segment address, now run through each section. + uint64_t address = curSegment->fBaseAddress; + SectionInfo* firstZeroFillSection = NULL; + SectionInfo* prevSection = NULL; + + std::vector& sectionInfos = curSegment->fSections; + + for (std::vector::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) { + SectionInfo* curSection = *it; + + // adjust section address based on alignment + uint64_t alignment = 1 << curSection->fAlignment; + address = ( (address+alignment-1) & (-alignment) ); + + // adjust file offset to match address + if ( prevSection != NULL ) { + if ( finalLinkedImage || !prevSection->fVirtualSection ) + fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; + else + fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); + } + + // update section info + curSection->fFileOffset = fileOffset; + curSection->setBaseAddress(address); + //fprintf(stderr, "%s %s %llX\n", curSegment->fName, curSection->fSectionName, address); + + // keep track of trailing zero fill sections + if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) + firstZeroFillSection = curSection; + if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage ) + throwf("zero-fill section %s not at end of segment", curSection->fSectionName); + + // update running pointers + if ( finalLinkedImage || !curSection->fVirtualSection ) + address += curSection->fSize; + fileOffset += curSection->fSize; + + // sanity check size of 32-bit binaries + if ( address > maxAddress() ) + throwf("section %s exceeds 4GB limit", curSection->fSectionName); + + // update segment info + curSegment->fFileSize = fileOffset - curSegment->fFileOffset; + curSegment->fSize = curSegment->fFileSize; + prevSection = curSection; + } + + if ( fOptions.outputKind() == Options::kObjectFile ) { + // don't page align .o files + } + else { + // optimize trailing zero-fill sections to not occupy disk space + if ( firstZeroFillSection != NULL ) { + curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset; + fileOffset = firstZeroFillSection->fFileOffset; + } + // page align segment size + curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); + curSegment->fSize = (curSegment->fSize+4095) & (-4096); + if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { + nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); + if ( curSegment->fInitProtection & VM_PROT_WRITE ) + nextWritableAddress = nextContiguousAddress; + else + nextReadOnlyAddress = nextContiguousAddress; + } + } + } + + // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) + if ( haveFixedSegments ) { + int segCount = fSegmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* segment1 = fSegmentInfos[i]; + + for(int j=0; j < segCount; ++j) { + if ( i != j ) { + SegmentInfo* segment2 = fSegmentInfos[j]; + + if ( segment1->fBaseAddress < segment2->fBaseAddress ) { + if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { + if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) { + throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", + segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); + } + } + } + } + } + + // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) { + if ( fFirstWritableSegment == NULL ) + fFirstWritableSegment = curSegment; + if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL ) + fWritableSegmentPastFirst4GB = true; + } + } + + // record size of encrypted part of __TEXT segment + if ( fOptions.makeEncryptable() ) { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( strcmp(curSegment->fName, "__TEXT") == 0 ) { + fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize); + break; + } + } + } + +} + +template +void Writer::adjustLinkEditSections() +{ + // link edit content is always in last segment + SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; + unsigned int firstLinkEditSectionIndex = 0; + while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) + ++firstLinkEditSectionIndex; + + const unsigned int linkEditSectionCount = lastSeg->fSections.size(); + uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; + uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); + if ( fPadSegmentInfo != NULL ) { + // insert __4GBFILL segment into segments vector before LINKEDIT + for(std::vector::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) { + if ( *it == lastSeg ) { + fSegmentInfos.insert(it, fPadSegmentInfo); + break; + } + } + // adjust __4GBFILL segment to span from end of last segment to zeroPageSize + fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address; + fPadSegmentInfo->fBaseAddress = address; + // adjust LINKEDIT to start at zeroPageSize + address = fOptions.zeroPageSize(); + lastSeg->fBaseAddress = fOptions.zeroPageSize(); + } + for (unsigned int i=firstLinkEditSectionIndex; i < linkEditSectionCount; ++i) { + std::vector& atoms = lastSeg->fSections[i]->fAtoms; + // adjust section address based on alignment + uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment; + uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address; + address += pad; + fileOffset += pad; // adjust file offset to match address + lastSeg->fSections[i]->setBaseAddress(address); + if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 ) + lastSeg->fSections[i]->setBaseAddress(0); + lastSeg->fSections[i]->fFileOffset = fileOffset; + uint64_t sectionOffset = 0; + for (unsigned int j=0; j < atoms.size(); ++j) { + ObjectFile::Atom* atom = atoms[j]; + uint64_t alignment = 1 << atom->getAlignment().powerOf2; + sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); + atom->setSectionOffset(sectionOffset); + uint64_t size = atom->getSize(); + sectionOffset += size; + if ( size > fLargestAtomSize ) + fLargestAtomSize = size; + } + //fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset); + lastSeg->fSections[i]->fSize = sectionOffset; + fileOffset += sectionOffset; + address += sectionOffset; + } + if ( fOptions.outputKind() == Options::kObjectFile ) { + //lastSeg->fBaseAddress = 0; + //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> + //lastSeg->fFileOffset = 0; + //lastSeg->fFileSize = + } + else { + lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; + lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); + } +} + + +template +ObjectFile::Atom::Scope MachHeaderAtom::getScope() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return ObjectFile::Atom::scopeGlobal; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kObjectFile: + return ObjectFile::Atom::scopeLinkageUnit; + } + throw "unknown header type"; +} + +template +ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusion() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + return ObjectFile::Atom::kSymbolTableInAndNeverStrip; + case Options::kStaticExecutable: + return ObjectFile::Atom::kSymbolTableInAsAbsolute; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return ObjectFile::Atom::kSymbolTableIn; + case Options::kObjectFile: + return ObjectFile::Atom::kSymbolTableNotIn; + } + throw "unknown header type"; +} + +template +const char* MachHeaderAtom::getName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return "__mh_execute_header"; + case Options::kDynamicLibrary: + return "__mh_dylib_header"; + case Options::kDynamicBundle: + return "__mh_bundle_header"; + case Options::kObjectFile: + return NULL; + case Options::kDyld: + return "__mh_dylinker_header"; + } + throw "unknown header type"; +} + +template +const char* MachHeaderAtom::getDisplayName() const +{ + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + return this->getName(); + case Options::kObjectFile: + return "mach header"; + } + throw "unknown header type"; +} + +template +void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const +{ + // get file type + uint32_t fileType = 0; + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fileType = MH_EXECUTE; + break; + case Options::kDynamicLibrary: + fileType = MH_DYLIB; + break; + case Options::kDynamicBundle: + fileType = MH_BUNDLE; + break; + case Options::kObjectFile: + fileType = MH_OBJECT; + break; + case Options::kDyld: + fileType = MH_DYLINKER; + break; + } + + // get flags + uint32_t flags = 0; + if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { + if ( fWriter.fCanScatter ) + flags = MH_SUBSECTIONS_VIA_SYMBOLS; + } + else { + if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { + flags |= MH_NOUNDEFS; + } + else { + flags = MH_DYLDLINK; + if ( fWriter.fOptions.bindAtLoad() ) + flags |= MH_BINDATLOAD; + switch ( fWriter.fOptions.nameSpace() ) { + case Options::kTwoLevelNameSpace: + flags |= MH_TWOLEVEL | MH_NOUNDEFS; + break; + case Options::kFlatNameSpace: + break; + case Options::kForceFlatNameSpace: + flags |= MH_FORCE_FLAT; + break; + } + if ( fWriter.fHasWeakExports ) + flags |= MH_WEAK_DEFINES; + if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) + flags |= MH_BINDS_TO_WEAK; + if ( fWriter.fOptions.prebind() ) + flags |= MH_PREBOUND; + if ( fWriter.fOptions.splitSeg() ) + flags |= MH_SPLIT_SEGS; + if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs ) + flags |= MH_NO_REEXPORTED_DYLIBS; + if ( fWriter.fOptions.positionIndependentExecutable() ) + flags |= MH_PIE; + } + if ( fWriter.fOptions.hasExecutableStack() ) + flags |= MH_ALLOW_STACK_EXECUTION; + if ( fWriter.fOptions.readerOptions().fRootSafe ) + flags |= MH_ROOT_SAFE; + if ( fWriter.fOptions.readerOptions().fSetuidSafe ) + flags |= MH_SETUID_SAFE; + } + + // get commands info + uint32_t commandsSize = 0; + uint32_t commandsCount = 0; + + std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; + for (std::vector::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + commandsSize += atom->getSize(); + // segment and symbol table atoms can contain more than one load command + if ( atom == fWriter.fSegmentCommands ) + commandsCount += fWriter.fSegmentCommands->commandCount(); + else if ( atom == fWriter.fSymbolTableCommands ) + commandsCount += fWriter.fSymbolTableCommands->commandCount(); + else if ( atom->getSize() != 0 ) + ++commandsCount; + } + + // fill out mach_header + macho_header* mh = (macho_header*)buffer; + setHeaderInfo(*mh); + mh->set_filetype(fileType); + mh->set_ncmds(commandsCount); + mh->set_sizeofcmds(commandsSize); + mh->set_flags(flags); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_POWERPC); + header.set_cpusubtype(fWriter.fCpuConstraint); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC_64); + header.set_cputype(CPU_TYPE_POWERPC64); + if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000); + else + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); + header.set_reserved(0); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_I386); + header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC_64); + header.set_cputype(CPU_TYPE_X86_64); + if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000); + else + header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); + header.set_reserved(0); +} + +template <> +void MachHeaderAtom::setHeaderInfo(macho_header& header) const +{ + header.set_magic(MH_MAGIC); + header.set_cputype(CPU_TYPE_ARM); + header.set_cpusubtype(fWriter.fCpuConstraint); +} + +template +CustomStackAtom::CustomStackAtom(Writer& writer) + : WriterAtom(writer, Segment::fgStackSegment) +{ + if ( stackGrowsDown() ) + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); + else + Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); +} + + +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } +template <> bool CustomStackAtom::stackGrowsDown() { return true; } + +template +void SegmentLoadCommandsAtom::computeSize() +{ + uint64_t size = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + size += sizeof(macho_segment_command

); + std::vector& sectionInfos = segmentInfos[i]->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) + size += sizeof(macho_section

); + } + } + fSize = size; + fCommandCount = segCount; + if ( fWriter.fPadSegmentInfo != NULL ) { + ++fCommandCount; + fSize += sizeof(macho_segment_command

); + } +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + +template <> +uint64_t LoadCommandAtom::alignedSize(uint64_t size) +{ + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o +} + +template +void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); + bzero(buffer, size); + uint8_t* p = buffer; + typename std::vector& segmentInfos = fWriter.fSegmentInfos; + const int segCount = segmentInfos.size(); + for(int i=0; i < segCount; ++i) { + SegmentInfo* segInfo = segmentInfos[i]; + const int sectionCount = segInfo->fSections.size(); + macho_segment_command

* cmd = (macho_segment_command

*)p; + cmd->set_cmd(macho_segment_command

::CMD); + cmd->set_segname(segInfo->fName); + cmd->set_vmaddr(segInfo->fBaseAddress); + cmd->set_vmsize(segInfo->fSize); + cmd->set_fileoff(segInfo->fFileOffset); + cmd->set_filesize(segInfo->fFileSize); + cmd->set_maxprot(segInfo->fMaxProtection); + cmd->set_initprot(segInfo->fInitProtection); + // add sections array + macho_section

* const sections = (macho_section

*)&p[sizeof(macho_segment_command

)]; + unsigned int sectionsEmitted = 0; + for (int j=0; j < sectionCount; ++j) { + SectionInfo* sectInfo = segInfo->fSections[j]; + if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { + macho_section

* sect = §ions[sectionsEmitted++]; + if ( oneSegment ) { + // .o file segment does not cover load commands, so recalc at first real section + if ( sectionsEmitted == 1 ) { + cmd->set_vmaddr(sectInfo->getBaseAddress()); + cmd->set_fileoff(sectInfo->fFileOffset); + } + cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); + cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); + } + sect->set_sectname(sectInfo->fSectionName); + sect->set_segname(sectInfo->fSegmentName); + sect->set_addr(sectInfo->getBaseAddress()); + sect->set_size(sectInfo->fSize); + sect->set_offset(sectInfo->fFileOffset); + sect->set_align(sectInfo->fAlignment); + if ( sectInfo->fRelocCount != 0 ) { + sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info

) + fWriter.fSectionRelocationsAtom->getFileOffset()); + sect->set_nreloc(sectInfo->fRelocCount); + } + if ( sectInfo->fAllZeroFill ) { + sect->set_flags(S_ZEROFILL); + sect->set_offset(0); + } + else if ( sectInfo->fAllLazyPointers ) { + sect->set_flags(S_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllLazyDylibPointers ) { + sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllNonLazyPointers ) { + sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + } + else if ( sectInfo->fAllStubs ) { + sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + } + else if ( sectInfo->fAllSelfModifyingStubs ) { + sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); + sect->set_reserved1(sectInfo->fIndirectSymbolOffset); + sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_INIT_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_MOD_TERM_FUNC_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); + } + else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_COALESCED); + } + else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_INTERPOSING); + } + else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_CSTRING_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_4BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_8BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_16BYTE_LITERALS); + } + else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } + else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } + else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_DTRACE_DOF); + } + else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_DTRACE_DOF); + } + else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { + sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + if ( sectInfo->fHasTextLocalRelocs ) + sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); + if ( sectInfo->fHasTextExternalRelocs ) + sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC); + } + } + } + p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; + cmd->set_cmdsize(sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)); + cmd->set_nsects(sectionsEmitted); + } +} + + +template +SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer, Segment::fgTextSegment) +{ + bzero(&fSymbolTable, sizeof(macho_symtab_command

)); + bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command

)); + switch ( fWriter.fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + fNeedsDynamicSymbolTable = true; + break; + case Options::kObjectFile: + case Options::kStaticExecutable: + fNeedsDynamicSymbolTable = false; + break; + } + writer.fSymbolTableCommands = this; +} + + + +template +void SymbolTableLoadCommandsAtom::needDynamicTable() +{ + fNeedsDynamicSymbolTable = true; +} + + +template +uint64_t SymbolTableLoadCommandsAtom::getSize() const +{ + if ( fNeedsDynamicSymbolTable ) + return this->alignedSize(sizeof(macho_symtab_command

) + sizeof(macho_dysymtab_command

)); + else + return this->alignedSize(sizeof(macho_symtab_command

)); +} + +template +void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + // build LC_DYSYMTAB command + macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)buffer; + bzero(symbolTableCmd, sizeof(macho_symtab_command

)); + symbolTableCmd->set_cmd(LC_SYMTAB); + symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); + symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); + symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset()); + symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset()); + symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); + + // build LC_DYSYMTAB command + if ( fNeedsDynamicSymbolTable ) { + macho_dysymtab_command

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)&buffer[sizeof(macho_symtab_command

)]; + bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command

)); + dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); + dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); + dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); + dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); + dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount); + dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex); + dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount); + if ( fWriter.fModuleInfoAtom != NULL ) { + dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); + dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); + dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); + dynamicSymbolTableCmd->set_nmodtab(1); + dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); + dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); + } + dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size()); + if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { + dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); + dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); + } + } +} + + +template +unsigned int SymbolTableLoadCommandsAtom::commandCount() +{ + return fNeedsDynamicSymbolTable ? 2 : 1; +} + +template +uint64_t DyldLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylinker_command

) + strlen("/usr/lib/dyld") + 1); +} + +template +void DyldLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_dylinker_command

* cmd = (macho_dylinker_command

*)buffer; + if ( fWriter.fOptions.outputKind() == Options::kDyld ) + cmd->set_cmd(LC_ID_DYLINKER); + else + cmd->set_cmd(LC_LOAD_DYLINKER); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + strcpy((char*)&buffer[sizeof(macho_dylinker_command

)], "/usr/lib/dyld"); +} + +template +uint64_t AllowableClientLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_client_command

) + strlen(this->clientString) + 1); +} + +template +void AllowableClientLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + + bzero(buffer, size); + macho_sub_client_command

* cmd = (macho_sub_client_command

*)buffer; + cmd->set_cmd(LC_SUB_CLIENT); + cmd->set_cmdsize(size); + cmd->set_client_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_client_command

)], this->clientString); + +} + +template +uint64_t DylibLoadCommandsAtom::getSize() const +{ + if ( fOptimizedAway ) { + return 0; + } + else { + const char* path = fInfo.reader->getInstallPath(); + return this->alignedSize(sizeof(macho_dylib_command

) + strlen(path) + 1); + } +} + +template +void DylibLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + if ( fOptimizedAway ) + return; + uint64_t size = this->getSize(); + bzero(buffer, size); + const char* path = fInfo.reader->getInstallPath(); + macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; + // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) + && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); + if ( fInfo.options.fLazyLoad ) + cmd->set_cmd(LC_LAZY_LOAD_DYLIB); + else if ( fInfo.options.fWeakImport || autoWeakLoadDylib ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); + else if ( fInfo.options.fReExport && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + cmd->set_cmd(LC_REEXPORT_DYLIB); + else + cmd->set_cmd(LC_LOAD_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses + cmd->set_current_version(fInfo.reader->getCurrentVersion()); + cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); + cmd->set_name_offset(); + strcpy((char*)&buffer[sizeof(macho_dylib_command

)], path); +} + + + +template +uint64_t DylibIDLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_dylib_command

) + strlen(fWriter.fOptions.installPath()) + 1); +} + +template +void DylibIDLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; + cmd->set_cmd(LC_ID_DYLIB); + cmd->set_cmdsize(this->getSize()); + cmd->set_name_offset(); + cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses + cmd->set_current_version(fWriter.fOptions.currentVersion()); + cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); + strcpy((char*)&buffer[sizeof(macho_dylib_command

)], fWriter.fOptions.installPath()); +} + + +template +void RoutinesLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + if (fWriter.fEntryPoint->isThumb()) + initAddr |= 1ULL; + bzero(buffer, sizeof(macho_routines_command

)); + macho_routines_command

* cmd = (macho_routines_command

*)buffer; + cmd->set_cmd(macho_routines_command

::CMD); + cmd->set_cmdsize(this->getSize()); + cmd->set_init_address(initAddr); +} + + +template +uint64_t SubUmbrellaLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(fName) + 1); +} + +template +void SubUmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_umbrella_command

* cmd = (macho_sub_umbrella_command

*)buffer; + cmd->set_cmd(LC_SUB_UMBRELLA); + cmd->set_cmdsize(this->getSize()); + cmd->set_sub_umbrella_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command

)], fName); +} + +template +void UUIDLoadCommandAtom::generate() +{ + switch ( fWriter.fOptions.getUUIDMode() ) { + case Options::kUUIDNone: + fEmit = false; + break; + case Options::kUUIDRandom: + ::uuid_generate_random(fUUID); + fEmit = true; + break; + case Options::kUUIDContent: + bzero(fUUID, 16); + fEmit = true; + break; + } +} + +template +void UUIDLoadCommandAtom::setContent(const uint8_t uuid[16]) +{ + memcpy(fUUID, uuid, 16); +} + +template +void UUIDLoadCommandAtom::copyRawContent(uint8_t buffer[]) const +{ + if (fEmit) { + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_uuid_command

* cmd = (macho_uuid_command

*)buffer; + cmd->set_cmd(LC_UUID); + cmd->set_cmdsize(this->getSize()); + cmd->set_uuid((uint8_t*)fUUID); + } +} + + +template +uint64_t SubLibraryLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_library_command

) + fNameLength + 1); +} + +template +void SubLibraryLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_library_command

* cmd = (macho_sub_library_command

*)buffer; + cmd->set_cmd(LC_SUB_LIBRARY); + cmd->set_cmdsize(this->getSize()); + cmd->set_sub_library_offset(); + strncpy((char*)&buffer[sizeof(macho_sub_library_command

)], fNameStart, fNameLength); + buffer[sizeof(macho_sub_library_command

)+fNameLength] = '\0'; +} + +template +uint64_t UmbrellaLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_sub_framework_command

) + strlen(fName) + 1); +} + +template +void UmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_sub_framework_command

* cmd = (macho_sub_framework_command

*)buffer; + cmd->set_cmd(LC_SUB_FRAMEWORK); + cmd->set_cmdsize(this->getSize()); + cmd->set_umbrella_offset(); + strcpy((char*)&buffer[sizeof(macho_sub_framework_command

)], fName); +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 +} + +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); +} + +// We should be picking it up from a header +template <> +uint64_t ThreadsLoadCommandsAtom::getSize() const +{ + return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_thread_register(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 +} + + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_thread_register(0, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); // i386_THREAD_STATE + cmd->set_count(16); // i386_THREAD_STATE_COUNT; + cmd->set_thread_register(10, start); + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(x86_THREAD_STATE64); + cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_thread_register(16, start); // rip + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp +} + +template <> +void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + bzero(buffer, size); + macho_thread_command* cmd = (macho_thread_command*)buffer; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(size); + cmd->set_flavor(1); + cmd->set_count(17); + cmd->set_thread_register(15, start); // pc + if ( fWriter.fOptions.hasCustomStack() ) + cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp? +} + +template +uint64_t RPathLoadCommandsAtom::getSize() const +{ + return this->alignedSize(sizeof(macho_rpath_command

) + strlen(fPath) + 1); +} + +template +void RPathLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_rpath_command

* cmd = (macho_rpath_command

*)buffer; + cmd->set_cmd(LC_RPATH); + cmd->set_cmdsize(this->getSize()); + cmd->set_path_offset(); + strcpy((char*)&buffer[sizeof(macho_rpath_command

)], fPath); +} + + + +template +void EncryptionLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)buffer; + cmd->set_cmd(LC_ENCRYPTION_INFO); + cmd->set_cmdsize(this->getSize()); + cmd->set_cryptoff(fStartOffset); + cmd->set_cryptsize(fEndOffset-fStartOffset); + cmd->set_cryptid(0); +} + + + +template +void LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const +{ + bzero(buffer, fSize); +} + +template +void LoadCommandsPaddingAtom::setSize(uint64_t newSize) +{ + fSize = newSize; + // this resizing by-passes the way fLargestAtomSize is set, so re-check here + if ( fWriter.fLargestAtomSize < newSize ) + fWriter.fLargestAtomSize = newSize; +} + +template +uint64_t LinkEditAtom::getFileOffset() const +{ + return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); +} + + +template +uint64_t SectionRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void SectionRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize()); +} + + +template +uint64_t LocalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void LocalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize()); +} + + + +template +uint64_t SymbolTableLinkEditAtom::getSize() const +{ + return fWriter.fSymbolTableCount * sizeof(macho_nlist

); +} + +template +void SymbolTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fWriter.fSymbolTable, this->getSize()); +} + +template +uint64_t ExternalRelocationsLinkEditAtom::getSize() const +{ + return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info

); +} + +template +void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter

()); + memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); +} + + + +template +uint64_t IndirectTableLinkEditAtom::getSize() const +{ + return fTable.size() * sizeof(uint32_t); +} + +template +void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + const uint32_t indirectTableSize = fTable.size(); + uint32_t* indirectTable = (uint32_t*)buffer; + for(std::vector::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { + if ( it->indirectIndex < indirectTableSize ) { + A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); + } + else { + throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); + } + } +} + + + +template +uint64_t ModuleInfoLinkEditAtom::getSize() const +{ + return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

) + + sizeof(macho_dylib_module

) + + this->getReferencesCount()*sizeof(uint32_t); +} + +template +uint32_t ModuleInfoLinkEditAtom::getTableOfContentsFileOffset() const +{ + return this->getFileOffset(); +} + +template +uint32_t ModuleInfoLinkEditAtom::getModuleTableFileOffset() const +{ + return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

); +} + +template +uint32_t ModuleInfoLinkEditAtom::getReferencesFileOffset() const +{ + return this->getModuleTableFileOffset() + sizeof(macho_dylib_module

); +} + +template +uint32_t ModuleInfoLinkEditAtom::getReferencesCount() const +{ + return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount; +} + +template +void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + // create toc. The symbols are already sorted, they are all in the smae module + macho_dylib_table_of_contents

* p = (macho_dylib_table_of_contents

*)buffer; + for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) { + p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i); + p->set_module_index(0); + } + // create module table (one entry) + uint16_t numInits = 0; + uint16_t numTerms = 0; + std::vector& segmentInfos = fWriter.fSegmentInfos; + for (std::vector::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) { + if ( strcmp((*segit)->fName, "__DATA") == 0 ) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { + if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 ) + numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t); + else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 ) + numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t); + } + } + } + macho_dylib_module

* module = (macho_dylib_module

*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

)]; + module->set_module_name(fModuleNameOffset); + module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); + module->set_nextdefsym(fWriter.fSymbolTableExportCount); + module->set_irefsym(0); + module->set_nrefsym(this->getReferencesCount()); + module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); + module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount); + module->set_iextrel(0); + module->set_nextrel(fWriter.fExternalRelocs.size()); + module->set_iinit_iterm(0,0); + module->set_ninit_nterm(numInits,numTerms); + module->set_objc_module_info_addr(0); // Not used by ld_classic, and not used by objc runtime for many years + module->set_objc_module_info_size(0); // Not used by ld_classic, and not used by objc runtime for many years + // create reference table + macho_dylib_reference

* ref = (macho_dylib_reference

*)((uint8_t*)module + sizeof(macho_dylib_module

)); + for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) { + ref->set_isym(fWriter.fSymbolTableExportStartIndex+i); + ref->set_flags(REFERENCE_FLAG_DEFINED); + } + for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) { + ref->set_isym(fWriter.fSymbolTableImportStartIndex+i); + std::map::iterator pos = fWriter.fStubsMap.find(fWriter.fImportedAtoms[i]); + if ( pos != fWriter.fStubsMap.end() ) + ref->set_flags(REFERENCE_FLAG_UNDEFINED_LAZY); + else + ref->set_flags(REFERENCE_FLAG_UNDEFINED_NON_LAZY); + } +} + + + +template +StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) + : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) +{ + fCurrentBuffer = new char[kBufferSize]; + // burn first byte of string pool (so zero is never a valid string offset) + fCurrentBuffer[fCurrentBufferUsed++] = ' '; + // make offset 1 always point to an empty string + fCurrentBuffer[fCurrentBufferUsed++] = '\0'; +} + +template +uint64_t StringsLinkEditAtom::getSize() const +{ + // align size + return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t)); +} + +template +void StringsLinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t offset = 0; + for (unsigned int i=0; i < fFullBuffers.size(); ++i) { + memcpy(&buffer[offset], fFullBuffers[i], kBufferSize); + offset += kBufferSize; + } + memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed); + // zero fill end to align + offset += fCurrentBufferUsed; + while ( (offset % sizeof(typename A::P::uint_t)) != 0 ) + buffer[offset++] = 0; +} + +template +int32_t StringsLinkEditAtom::add(const char* name) +{ + int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; + int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1; + if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) { + fCurrentBufferUsed += lenNeeded; + } + else { + int copied = kBufferSize-fCurrentBufferUsed-1; + // change trailing '\0' that strlcpy added to real char + fCurrentBuffer[kBufferSize-1] = name[copied]; + // alloc next buffer + fFullBuffers.push_back(fCurrentBuffer); + fCurrentBuffer = new char[kBufferSize]; + fCurrentBufferUsed = 0; + // append rest of string + this->add(&name[copied+1]); + } + return offset; +} + + +template +int32_t StringsLinkEditAtom::addUnique(const char* name) +{ + StringToOffset::iterator pos = fUniqueStrings.find(name); + if ( pos != fUniqueStrings.end() ) { + return pos->second; + } + else { + int32_t offset = this->add(name); + fUniqueStrings[name] = offset; + return offset; + } +} + + +template +const char* StringsLinkEditAtom::stringForIndex(int32_t index) const +{ + int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size(); + int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed; + // check for out of bounds + if ( index > maxIndex ) + return ""; + // check for index in fCurrentBuffer + if ( index > currentBufferStartIndex ) + return &fCurrentBuffer[index-currentBufferStartIndex]; + // otherwise index is in a full buffer + uint32_t fullBufferIndex = index/kBufferSize; + return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; +} + + + +template +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) + : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset) +{ + char* buf = new char[strlen(name)+32]; + if ( targetOffset == 0 ) { + if ( islandRegion == 0 ) + sprintf(buf, "%s$island", name); + else + sprintf(buf, "%s$island_%d", name, islandRegion); + } + else { + sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); + } + fName = buf; +} + + +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); +} + +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + +template <> +uint64_t BranchIslandAtom::getSize() const +{ + return 4; +} + + + +template +uint64_t SegmentSplitInfoLoadCommandsAtom::getSize() const +{ + if ( fWriter.fSplitCodeToDataContentAtom->canEncode() ) + return this->alignedSize(sizeof(macho_linkedit_data_command

)); + else + return 0; // a zero size causes the load command to be suppressed +} + +template +void SegmentSplitInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t size = this->getSize(); + bzero(buffer, size); + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)buffer; + cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); + cmd->set_cmdsize(size); + cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset()); + cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize()); +} + + +template +uint64_t SegmentSplitInfoContentAtom::getSize() const +{ + return fEncodedData.size(); +} + +template +void SegmentSplitInfoContentAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fEncodedData[0], fEncodedData.size()); +} + + +template +void SegmentSplitInfoContentAtom::uleb128EncodeAddresses(const std::vector::AtomAndOffset>& locations) +{ + pint_t addr = fWriter.fOptions.baseAddress(); + for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { + pint_t nextAddr = it->atom->getAddress() + it->offset; + //fprintf(stderr, "\t0x%0llX\n", (uint64_t)nextAddr); + uint64_t delta = nextAddr - addr; + if ( delta == 0 ) + throw "double split seg info for same address"; + // uleb128 encode + uint8_t byte; + do { + byte = delta & 0x7F; + delta &= ~0x7F; + if ( delta != 0 ) + byte |= 0x80; + fEncodedData.push_back(byte); + delta = delta >> 7; + } + while( byte >= 0x80 ); + addr = nextAddr; + } +} + +template +void SegmentSplitInfoContentAtom::encode() +{ + if ( ! fCantEncode ) { + fEncodedData.reserve(8192); + + if ( fKind1Locations.size() != 0 ) { + fEncodedData.push_back(1); + //fprintf(stderr, "type 1:\n"); + this->uleb128EncodeAddresses(fKind1Locations); + fEncodedData.push_back(0); + } + + if ( fKind2Locations.size() != 0 ) { + fEncodedData.push_back(2); + //fprintf(stderr, "type 2:\n"); + this->uleb128EncodeAddresses(fKind2Locations); + fEncodedData.push_back(0); + } + + if ( fKind3Locations.size() != 0 ) { + fEncodedData.push_back(3); + //fprintf(stderr, "type 3:\n"); + this->uleb128EncodeAddresses(fKind3Locations); + fEncodedData.push_back(0); + } + + if ( fKind4Locations.size() != 0 ) { + fEncodedData.push_back(4); + //fprintf(stderr, "type 4:\n"); + this->uleb128EncodeAddresses(fKind4Locations); + fEncodedData.push_back(0); + } + + // always add zero byte to mark end + fEncodedData.push_back(0); + + // add zeros to end to align size + while ( (fEncodedData.size() % sizeof(pint_t)) != 0 ) + fEncodedData.push_back(0); + } +} + + +template +ObjCInfoAtom::ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, bool objcReplacementClasses) + : WriterAtom(writer, getInfoSegment()) +{ + fContent[0] = 0; + uint32_t value = 0; + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + if ( objcReplacementClasses ) + value = 1; + switch ( objcConstraint ) { + case ObjectFile::Reader::kObjcNone: + case ObjectFile::Reader::kObjcRetainRelease: + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + value |= 2; + break; + case ObjectFile::Reader::kObjcGC: + value |= 6; + break; + } + A::P::E::set32(fContent[1], value); +} + +template +void ObjCInfoAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &fContent[0], 8); +} + + +// objc info section is in a different segment and section for 32 vs 64 bit runtimes +template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } + +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } + + +}; // namespace executable +}; // namespace mach_o + + +#endif // __EXECUTABLE_MACH_O__ diff --git a/ld64/src/ObjectDump.cpp b/ld64/src/ObjectDump.cpp new file mode 100644 index 0000000..a06c7a2 --- /dev/null +++ b/ld64/src/ObjectDump.cpp @@ -0,0 +1,497 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +#include "MachOReaderRelocatable.hpp" + +#define LTO_SUPPORT 0 + +#if LTO_SUPPORT + #include "LTOReader.hpp" +#endif + +static bool sDumpContent= true; +static bool sDumpStabs = false; +static bool sSort = true; +static bool sNMmode = false; +static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; +static const char* sMatchName; +static int sPrintRestrict; +static int sPrintAlign; +static int sPrintName; + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + +void warning(const char* format, ...) +{ + va_list list; + fprintf(stderr, "warning: "); + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} + +static void dumpStabs(std::vector* stabs) +{ + // debug info + printf("stabs: (%lu)\n", stabs->size()); + for (std::vector::iterator it = stabs->begin(); it != stabs->end(); ++it ) { + ObjectFile::Reader::Stab& stab = *it; + const char* code = "?????"; + switch (stab.type) { + case N_GSYM: + code = " GSYM"; + break; + case N_FNAME: + code = "FNAME"; + break; + case N_FUN: + code = " FUN"; + break; + case N_STSYM: + code = "STSYM"; + break; + case N_LCSYM: + code = "LCSYM"; + break; + case N_BNSYM: + code = "BNSYM"; + break; + case N_OPT: + code = " OPT"; + break; + case N_RSYM: + code = " RSYM"; + break; + case N_SLINE: + code = "SLINE"; + break; + case N_ENSYM: + code = "ENSYM"; + break; + case N_SSYM: + code = " SSYM"; + break; + case N_SO: + code = " SO"; + break; + case N_OSO: + code = " OSO"; + break; + case N_LSYM: + code = " LSYM"; + break; + case N_BINCL: + code = "BINCL"; + break; + case N_SOL: + code = " SOL"; + break; + case N_PARAMS: + code = "PARMS"; + break; + case N_VERSION: + code = " VERS"; + break; + case N_OLEVEL: + code = "OLEVL"; + break; + case N_PSYM: + code = " PSYM"; + break; + case N_EINCL: + code = "EINCL"; + break; + case N_ENTRY: + code = "ENTRY"; + break; + case N_LBRAC: + code = "LBRAC"; + break; + case N_EXCL: + code = " EXCL"; + break; + case N_RBRAC: + code = "RBRAC"; + break; + case N_BCOMM: + code = "BCOMM"; + break; + case N_ECOMM: + code = "ECOMM"; + break; + case N_LENG: + code = "LENG"; + break; + } + printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string); + } +} + + +static void dumpAtomLikeNM(ObjectFile::Atom* atom) +{ + uint32_t size = atom->getSize(); + + const char* visibility; + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + visibility = "internal"; + break; + case ObjectFile::Atom::scopeLinkageUnit: + visibility = "hidden "; + break; + case ObjectFile::Atom::scopeGlobal: + visibility = "global "; + break; + default: + visibility = " "; + break; + } + + const char* kind; + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + kind = "regular "; + break; + case ObjectFile::Atom::kTentativeDefinition: + kind = "tentative"; + break; + case ObjectFile::Atom::kWeakDefinition: + kind = "weak "; + break; + case ObjectFile::Atom::kAbsoluteSymbol: + kind = "absolute "; + break; + default: + kind = " "; + break; + } + + printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName()); +} + + +static void dumpAtom(ObjectFile::Atom* atom) +{ + if(sMatchName && strcmp(sMatchName, atom->getDisplayName())) + return; + + //printf("atom: %p\n", atom); + + // name + if(!sPrintRestrict || sPrintName) + printf("name: %s\n", atom->getDisplayName()); + + // scope + if(!sPrintRestrict) + switch ( atom->getScope() ) { + case ObjectFile::Atom::scopeTranslationUnit: + printf("scope: translation unit\n"); + break; + case ObjectFile::Atom::scopeLinkageUnit: + printf("scope: linkage unit\n"); + break; + case ObjectFile::Atom::scopeGlobal: + printf("scope: global\n"); + break; + default: + printf("scope: unknown\n"); + } + + // kind + if(!sPrintRestrict) + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kRegularDefinition: + printf("kind: regular\n"); + break; + case ObjectFile::Atom::kWeakDefinition: + printf("kind: weak\n"); + break; + case ObjectFile::Atom::kTentativeDefinition: + printf("kind: tentative\n"); + break; + case ObjectFile::Atom::kExternalDefinition: + printf("kind: import\n"); + break; + case ObjectFile::Atom::kExternalWeakDefinition: + printf("kind: weak import\n"); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + printf("kind: absolute symbol\n"); + break; + default: + printf("kind: unknown\n"); + } + + // segment and section + if(!sPrintRestrict && (atom->getSectionName() != NULL) ) + printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + + // attributes + if(!sPrintRestrict) { + printf("attrs: "); + if ( atom->dontDeadStrip() ) + printf("dont-dead-strip "); + if ( atom->isZeroFill() ) + printf("zero-fill "); + if ( atom->isThumb() ) + printf("thumb "); + printf("\n"); + } + + // size + if(!sPrintRestrict) + printf("size: 0x%012llX\n", atom->getSize()); + + // alignment + if(!sPrintRestrict || sPrintAlign) + printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); + + // content + if (!sPrintRestrict && sDumpContent ) { + uint64_t size = atom->getSize(); + if ( size < 4096 ) { + uint8_t content[size]; + atom->copyRawContent(content); + printf("content: "); + if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + printf("\""); + for (unsigned int i=0; i < size; ++i) { + if(content[i]<'!' || content[i]>=127) + printf("\\%o", content[i]); + else + printf("%c", content[i]); + } + printf("\""); + } + else { + for (unsigned int i=0; i < size; ++i) + printf("%02X ", content[i]); + } + } + printf("\n"); + } + + // references + if(!sPrintRestrict) { + std::vector& references = atom->getReferences(); + const int refCount = references.size(); + printf("references: (%u)\n", refCount); + for (int i=0; i < refCount; ++i) { + ObjectFile::Reference* ref = references[i]; + printf(" %s\n", ref->getDescription()); + } + } + + // line info + if(!sPrintRestrict) { + std::vector* lineInfo = atom->getLineInfo(); + if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) { + printf("line info: (%lu)\n", lineInfo->size()); + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); + } + } + } + + if(!sPrintRestrict) + printf("\n"); +} + +struct AtomSorter +{ + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) + { + if ( left == right ) + return false; + return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); + } +}; + + +static void dumpFile(ObjectFile::Reader* reader) +{ + // stabs debug info + if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) { + std::vector* stabs = reader->getStabs(); + if ( stabs != NULL ) + dumpStabs(stabs); + } + + // get all atoms + std::vector atoms = reader->getAtoms(); + + // make copy of vector and sort (so output is canonical) + std::vector sortedAtoms(atoms); + if ( sSort ) + std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter()); + + for(std::vector::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) { + if ( sNMmode ) + dumpAtomLikeNM(*it); + else + dumpAtom(*it); + } +} + + +static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) +{ + struct stat stat_buf; + + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("cannot open file: %s", path); + ::fstat(fd, &stat_buf); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { + p = p + OSSwapBigToHostInt32(archs[i].offset); + mh = (struct mach_header*)p; + } + } + } + if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); + else if ( mach_o::relocatable::Reader::validFile(p) ) + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); +#if LTO_SUPPORT + if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) { + return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0); + } +#endif + + throwf("not a mach-o object file: %s", path); +} + +static +void +usage() +{ + fprintf(stderr, "ObjectDump options:\n" + "\t-no_content\tdon't dump contents\n" + "\t-stabs\t\tdump stabs\n" + "\t-arch aaa\tonly dump info about arch aaa\n" + "\t-only sym\tonly dump info about sym\n" + "\t-align\t\tonly print alignment info\n" + "\t-name\t\tonly print symbol names\n" + ); +} + +int main(int argc, const char* argv[]) +{ + if(argc<2) { + usage(); + return 0; + } + + ObjectFile::ReaderOptions options; + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-no_content") == 0 ) { + sDumpContent = false; + } + else if ( strcmp(arg, "-nm") == 0 ) { + sNMmode = true; + } + else if ( strcmp(arg, "-stabs") == 0 ) { + sDumpStabs = true; + } + else if ( strcmp(arg, "-no_sort") == 0 ) { + sSort = false; + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = ++i +#include +#include +#include + + + +// +// These classes represent the abstract Atoms and References that are the basis of the linker. +// An Atom and a Reference correspond to a Node and Edge in graph theory. +// +// A Reader is a class which parses an object file and presents it as Atoms and References. +// All linking operations are done on Atoms and References. This makes the linker file +// format independent. +// +// A Writer takes a vector of Atoms with all References resolved and produces an executable file. +// +// + + + +namespace ObjectFile { + + +struct LineInfo +{ + uint32_t atomOffset; + const char* fileName; + uint32_t lineNumber; +}; + + +class ReaderOptions +{ +public: + ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), + fLinkingMainExecutable(false), fSlowx86Stubs(false), + fForFinalLinkedImage(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), + fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), + fImplicitlyLinkPublicDylibs(true), fLogObjectFiles(false), fLogAllFiles(false), + fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), + fTraceOutputFile(NULL), fVersionMin(kMinUnset) {} + enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; + enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; + + struct AliasPair { + const char* realName; + const char* alias; + }; + + bool fFullyLoadArchives; + bool fLoadAllObjcObjectsFromArchives; + bool fFlatNamespace; + bool fLinkingMainExecutable; + bool fSlowx86Stubs; + bool fForFinalLinkedImage; + bool fForStatic; + bool fForDyld; + bool fMakeTentativeDefinitionsReal; + bool fWhyLoad; + bool fRootSafe; + bool fSetuidSafe; + DebugInfoStripping fDebugInfoStripping; + bool fImplicitlyLinkPublicDylibs; + bool fLogObjectFiles; + bool fLogAllFiles; + bool fTraceDylibs; + bool fTraceIndirectDylibs; + bool fTraceArchives; + const char* fTraceOutputFile; + VersionMin fVersionMin; + std::vector fAliases; +}; + + +class Reader +{ +public: + enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + struct Stab + { + class Atom* atom; + uint8_t type; + uint8_t other; + uint16_t desc; + uint32_t value; + const char* string; + }; + enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC }; + enum CpuConstraint { kCpuAny = 0 }; + + class DylibHander + { + public: + virtual ~DylibHander() {} + virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0; + }; + + + static Reader* createReader(const char* path, const ReaderOptions& options); + + virtual const char* getPath() = 0; + virtual time_t getModificationTime() = 0; + virtual DebugInfoKind getDebugInfoKind() = 0; + virtual std::vector& getAtoms() = 0; + virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; + virtual std::vector* getStabs() = 0; + virtual ObjcConstraint getObjCConstraint() { return kObjcNone; } + virtual uint32_t updateCpuConstraint(uint32_t current) { return current; } + virtual bool objcReplacementClasses() { return false; } + + // For relocatable object files only + virtual bool canScatterAtoms() { return true; } + virtual void optimize(std::vector&, std::vector&, + std::vector&, const std::set&, + uint32_t, ObjectFile::Reader* writer, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, int okind, + bool verbose, bool saveTemps, const char* outputFilePath, + bool pie, bool allowTextRelocs) { } + virtual bool hasLongBranchStubs() { return false; } + + // For Dynamic Libraries only + virtual const char* getInstallPath() { return NULL; } + virtual uint32_t getTimestamp() { return 0; } + virtual uint32_t getCurrentVersion() { return 0; } + virtual uint32_t getCompatibilityVersion() { return 0; } + virtual void processIndirectLibraries(DylibHander* handler) { } + virtual void setExplicitlyLinked() { } + virtual bool explicitlyLinked() { return false; } + virtual bool implicitlyLinked() { return false; } + virtual bool providedExportAtom() { return false; } + virtual const char* parentUmbrella() { return NULL; } + virtual std::vector* getAllowableClients() { return NULL; } + virtual bool hasWeakExternals() { return false; } + virtual bool isLazyLoadedDylib() { return false; } + +protected: + Reader() {} + virtual ~Reader() {} +}; + +class Segment +{ +public: + virtual const char* getName() const = 0; + virtual bool isContentReadable() const = 0; + virtual bool isContentWritable() const = 0; + virtual bool isContentExecutable() const = 0; + + uint64_t getBaseAddress() const { return fBaseAddress; } + void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + virtual bool hasFixedAddress() const { return false; } + +protected: + Segment() : fBaseAddress(0) {} + virtual ~Segment() {} + uint64_t fBaseAddress; +}; + +class Reference; + +class Section +{ +public: + unsigned int getIndex() { return fIndex; } + uint64_t getBaseAddress() { return fBaseAddress; } + void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } + void* fOther; + +protected: + Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} + uint64_t fBaseAddress; + unsigned int fIndex; +}; + + +struct Alignment +{ + Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} + uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } + uint16_t powerOf2; + uint16_t modulus; +}; + +// +// An atom is the fundamental unit of linking. A C function or global variable is an atom. +// An atom has content and some attributes. The content of a function atom is the instructions +// that implement the function. The content of a global variable atom is its initial bits. +// +// Name: +// The name of an atom is the label name generated by the compiler. A C compiler names foo() +// as _foo. A C++ compiler names foo() as __Z3foov. +// The name refers to the first byte of the content. An atom cannot have multiple entry points. +// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. +// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. +// +// Scope: +// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond +// to the C visibility of static, hidden, default. +// +// DefinitionKind: +// An atom is one of five defintion kinds: +// regular Most atoms. +// weak C++ compiler makes some functions weak if there might be multiple copies +// that the linker needs to coalesce. +// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. +// It could be a prototype or it could be a definition. +// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists +// so that all References can be resolved. +// external-weak Same as external, but the definition in the dylib is weak. +// +// SymbolTableInclusion: +// An atom may or may not be in the symbol table in an object file. +// in Most atoms for functions or global data +// not-in Anonymous atoms such literal c-strings, or other compiler generated data +// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) +// +// Ordinal: +// When a reader is created it is given a base ordinal number. All atoms created by the reader +// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal +// values are used by the linker to sort the atom graph when producing the output file. +// +class Atom +{ +public: + enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; + enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; + enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; + + virtual Reader* getFile() const = 0; + virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; + virtual const char* getName() const = 0; + virtual const char* getDisplayName() const = 0; + virtual Scope getScope() const = 0; + virtual DefinitionKind getDefinitionKind() const = 0; + virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; + virtual bool dontDeadStrip() const = 0; + virtual bool isZeroFill() const = 0; + virtual bool isThumb() const = 0; + virtual uint64_t getSize() const = 0; + virtual std::vector& getReferences() const = 0; + virtual bool mustRemainInSection() const = 0; + virtual const char* getSectionName() const = 0; + virtual Segment& getSegment() const = 0; + virtual Atom& getFollowOnAtom() const = 0; + virtual uint32_t getOrdinal() const = 0; + virtual std::vector* getLineInfo() const = 0; + virtual Alignment getAlignment() const = 0; + virtual void copyRawContent(uint8_t buffer[]) const = 0; + virtual void setScope(Scope) = 0; + + + uint64_t getSectionOffset() const { return fSectionOffset; } + uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } + class Section* getSection() const { return fSection; } + + virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } + virtual void setSection(class Section* sect) { fSection = sect; } + +protected: + Atom() : fSectionOffset(0), fSection(NULL) {} + virtual ~Atom() {} + + uint64_t fSectionOffset; + class Section* fSection; +}; + + +// +// A Reference is a directed edge to another Atom. When an instruction in +// the content of an Atom refers to another Atom, that is represented by a +// Reference. +// +// There are two kinds of references: direct and by-name. With a direct Reference, +// the target is bound by the Reader that created it. For instance a reference to a +// static would produce a direct reference. A by-name reference requires the linker +// to find the target Atom with the required name in order to be bound. +// +// For a link to succeed all References must be bound. +// +// A Reference has an optional "from" target. This is used when the content to fix-up +// is the difference of two Atom address. For instance, if a pointer sized data Atom +// is to contain A - B, then the Atom would have on Reference with a target of "A" and +// a from-target of "B". +// +// A Reference also has a fix-up-offset. This is the offset into the content of the +// Atom holding the reference where the fix-up (relocation) will be applied. +// +// +// +class Reference +{ +public: + enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind }; + + virtual TargetBinding getTargetBinding() const = 0; + virtual TargetBinding getFromTargetBinding() const = 0; + virtual uint8_t getKind() const = 0; + virtual uint64_t getFixUpOffset() const = 0; + virtual const char* getTargetName() const = 0; + virtual Atom& getTarget() const = 0; + virtual uint64_t getTargetOffset() const = 0; + virtual Atom& getFromTarget() const = 0; + virtual const char* getFromTargetName() const = 0; + virtual uint64_t getFromTargetOffset() const = 0; + + virtual void setTarget(Atom&, uint64_t offset) = 0; + virtual void setFromTarget(Atom&) = 0; + virtual const char* getDescription() const = 0; + +protected: + Reference() {} + virtual ~Reference() {} +}; + + +}; // namespace ObjectFile + + +#endif // __OBJECTFILE__ diff --git a/ld64/src/OpaqueSection.hpp b/ld64/src/OpaqueSection.hpp new file mode 100644 index 0000000..cddd45c --- /dev/null +++ b/ld64/src/OpaqueSection.hpp @@ -0,0 +1,199 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OPAQUE_SECTION__ +#define __OPAQUE_SECTION__ + + +#include + +#include "ObjectFile.h" + +namespace opaque_section { + + +class Segment : public ObjectFile::Segment +{ +public: + Segment(const char* name) { fName = name; } + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return true; } + virtual bool isContentWritable() const { return false; } + virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); } +private: + const char* fName; +}; + + +class Reader : public ObjectFile::Reader +{ +public: + Reader(const char* segmentName, const char* sectionName, const char* path, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL); + virtual ~Reader(); + + void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, + uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0); + + virtual const char* getPath() { return fPath; } + virtual time_t getModificationTime() { return 0; } + virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } + virtual std::vector& getAtoms() { return fAtoms; } + virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } + virtual std::vector* getStabs() { return NULL; } + +private: + const char* fPath; + std::vector fAtoms; +}; + +class Reference : public ObjectFile::Reference +{ +public: + Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset, + const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0) + : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind), + fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {} + virtual ~Reference() {} + + + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } + virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } + virtual uint8_t getKind() const { return fKind; } + virtual uint64_t getFixUpOffset() const { return fFixUpOffset; } + virtual const char* getTargetName() const { return fTarget->getName(); } + virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } + virtual uint64_t getTargetOffset() const { return fTargetOffset; } + virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); } + virtual const char* getFromTargetName() const { return fFromTarget->getName(); } + virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } + virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; } + virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } + virtual const char* getDescription() const { return "opaque section reference"; } + +private: + uint64_t fFixUpOffset; + const ObjectFile::Atom* fTarget; + uint64_t fTargetOffset; + uint8_t fKind; + const ObjectFile::Atom* fFromTarget; + uint64_t fFromTargetOffset; +}; + + +class Atom : public ObjectFile::Atom { +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } + virtual const char* getName() const { return fName; } + virtual const char* getDisplayName() const; + virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return true; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual uint64_t getSize() const { return fFileLength; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual bool mustRemainInSection() const { return false; } + virtual const char* getSectionName() const { return fSectionName; } + virtual Segment& getSegment() const { return fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } + virtual uint32_t getOrdinal() const { return fOrdinal; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); } + virtual void copyRawContent(uint8_t buffer[]) const; + + virtual void setScope(Scope) { } + +protected: + friend class Reader; + + Atom(Reader& owner, Segment& segment, const char* sectionName, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName); + virtual ~Atom() {} + + Reader& fOwner; + Segment& fSegment; + const char* fName; + const char* fSectionName; + const uint8_t* fFileContent; + uint32_t fOrdinal; + uint64_t fFileLength; + std::vector fReferences; +}; + + + +Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName) + : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength) +{ + if ( symbolName != NULL ) + fName = strdup(symbolName); + else + asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName); +} + + +Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], + uint64_t fileLength, uint32_t ordinal, const char* symbolName) + : fPath(path) +{ + fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName)); +} + +Reader::~Reader() +{ +} + +void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, + uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget) +{ + fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget)); +} + + +const char* Atom::getDisplayName() const +{ + static char name[64]; + sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName); + return name; +} + + +void Atom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fFileContent, fFileLength); +} + + + +}; + + + +#endif // __OPAQUE_SECTION__ + + + diff --git a/ld64/src/Options.cpp b/ld64/src/Options.cpp new file mode 100644 index 0000000..eb244ed --- /dev/null +++ b/ld64/src/Options.cpp @@ -0,0 +1,3150 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +#include "configure.h" +#include "Options.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +extern void printLTOVersion(Options &opts); + + +static bool sEmitWarnings = true; +static const char* sWarningsSideFilePath = NULL; +static FILE* sWarningsSideFile = NULL; + +void warning(const char* format, ...) +{ + if ( sEmitWarnings ) { + va_list list; + if ( sWarningsSideFilePath != NULL ) { + if ( sWarningsSideFile == NULL ) + sWarningsSideFile = fopen(sWarningsSideFilePath, "a"); + } + va_start(list, format); + fprintf(stderr, "ld warning: "); + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); + if ( sWarningsSideFile != NULL ) { + fprintf(sWarningsSideFile, "ld warning: "); + vfprintf(sWarningsSideFile, format, list); + fprintf(sWarningsSideFile, "\n"); + fflush(sWarningsSideFile); + } + va_end(list); + } +} + +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + +Options::Options(int argc, const char* argv[]) + : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable), + fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), + fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), + fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0), + fBaseWritableAddress(0), fSplitSegs(false), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), + fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), + fClientName(NULL), + fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), + fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), + fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(32), + fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), + fVerbose(false), fKeepRelocations(false), fWarnStabs(false), + fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), + fSharedRegionEligible(false), fPrintOrderFileStatistics(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), + fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), + fUsingLazyDylibLinking(false), fEncryptable(true), fSaveTempFiles(false) +{ + this->checkForClassic(argc, argv); + this->parsePreCommandLineEnvironmentSettings(); + this->parse(argc, argv); + this->parsePostCommandLineEnvironmentSettings(); + this->reconfigureDefaults(); + this->checkIllegalOptionCombinations(); +} + +Options::~Options() +{ +} + +const ObjectFile::ReaderOptions& Options::readerOptions() +{ + return fReaderOptions; +} + + +const char* Options::getOutputFilePath() +{ + return fOutputFile; +} + +std::vector& Options::getInputFiles() +{ + return fInputFiles; +} + +Options::OutputKind Options::outputKind() +{ + return fOutputKind; +} + +bool Options::bindAtLoad() +{ + return fBindAtLoad; +} + +bool Options::prebind() +{ + return fPrebind; +} + +bool Options::fullyLoadArchives() +{ + return fReaderOptions.fFullyLoadArchives; +} + +Options::NameSpace Options::nameSpace() +{ + return fNameSpace; +} + +const char* Options::installPath() +{ + if ( fDylibInstallName != NULL ) + return fDylibInstallName; + else if ( fFinalName != NULL ) + return fFinalName; + else + return fOutputFile; +} + +uint32_t Options::currentVersion() +{ + return fDylibCurrentVersion; +} + +uint32_t Options::compatibilityVersion() +{ + return fDylibCompatVersion; +} + +const char* Options::entryName() +{ + return fEntryName; +} + +uint64_t Options::baseAddress() +{ + return fBaseAddress; +} + +bool Options::keepPrivateExterns() +{ + return fKeepPrivateExterns; +} + +bool Options::interposable(const char* name) +{ + switch ( fInterposeMode ) { + case kInterposeNone: + return false; + case kInterposeAllExternal: + return true; + case kInterposeSome: + return fInterposeList.contains(name); + } + throw "internal error"; +} + +bool Options::needsModuleTable() +{ + return fNeedsModuleTable; +} + +bool Options::ignoreOtherArchInputFiles() +{ + return fIgnoreOtherArchFiles; +} + +bool Options::forceCpuSubtypeAll() +{ + return fForceSubtypeAll; +} + +bool Options::traceDylibs() +{ + return fReaderOptions.fTraceDylibs; +} + +bool Options::traceArchives() +{ + return fReaderOptions.fTraceArchives; +} + +Options::UndefinedTreatment Options::undefinedTreatment() +{ + return fUndefinedTreatment; +} + +ObjectFile::ReaderOptions::VersionMin Options::macosxVersionMin() +{ + return fReaderOptions.fVersionMin; +} + +Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() +{ + return fWeakReferenceMismatchTreatment; +} + +const char* Options::umbrellaName() +{ + return fUmbrellaName; +} + +std::vector& Options::allowableClients() +{ + return fAllowableClients; +} + +const char* Options::clientName() +{ + return fClientName; +} + +uint64_t Options::zeroPageSize() +{ + return fZeroPageSize; +} + +bool Options::hasCustomStack() +{ + return (fStackSize != 0); +} + +uint64_t Options::customStackSize() +{ + return fStackSize; +} + +uint64_t Options::customStackAddr() +{ + return fStackAddr; +} + +bool Options::hasExecutableStack() +{ + return fExecutableStack; +} + +std::vector& Options::initialUndefines() +{ + return fInitialUndefines; +} + +bool Options::printWhyLive(const char* symbolName) +{ + return ( fWhyLive.find(symbolName) != fWhyLive.end() ); +} + + +const char* Options::initFunctionName() +{ + return fInitFunctionName; +} + +const char* Options::dotOutputFile() +{ + return fDotOutputFile; +} + +bool Options::hasExportRestrictList() +{ + return (fExportMode != kExportDefault); +} + +bool Options::hasExportMaskList() +{ + return (fExportMode == kExportSome); +} + + +bool Options::hasWildCardExportRestrictList() +{ + // has -exported_symbols_list which contains some wildcards + return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); +} + + +bool Options::allGlobalsAreDeadStripRoots() +{ + // -exported_symbols_list means globals are not exported by default + if ( fExportMode == kExportSome ) + return false; + // + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // by default unused globals in a main executable are stripped + return false; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + return true; + } + return false; +} + +uint32_t Options::minimumHeaderPad() +{ + return fMinimumHeaderPad; +} + +std::vector& Options::extraSections() +{ + return fExtraSections; +} + +std::vector& Options::sectionAlignments() +{ + return fSectionAlignments; +} + +Options::CommonsMode Options::commonsMode() +{ + return fCommonsMode; +} + +bool Options::warnCommons() +{ + return fWarnCommons; +} + +bool Options::keepRelocations() +{ + return fKeepRelocations; +} + +bool Options::warnStabs() +{ + return fWarnStabs; +} + +const char* Options::executablePath() +{ + return fExecutablePath; +} + +Options::DeadStripMode Options::deadStrip() +{ + return fDeadStrip; +} + +bool Options::shouldExport(const char* symbolName) +{ + switch (fExportMode) { + case kExportSome: + return fExportSymbols.contains(symbolName); + case kDontExportSome: + return ! fDontExportSymbols.contains(symbolName); + case kExportDefault: + return true; + } + throw "internal error"; +} + +bool Options::keepLocalSymbol(const char* symbolName) +{ + switch (fLocalSymbolHandling) { + case kLocalSymbolsAll: + return true; + case kLocalSymbolsNone: + return false; + case kLocalSymbolsSelectiveInclude: + return fLocalSymbolsIncluded.contains(symbolName); + case kLocalSymbolsSelectiveExclude: + return ! fLocalSymbolsExcluded.contains(symbolName); + } + throw "internal error"; +} + +void Options::parseArch(const char* architecture) +{ + if ( architecture == NULL ) + throw "-arch must be followed by an architecture string"; + if ( strcmp(architecture, "ppc") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; + } + else if ( strcmp(architecture, "ppc64") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC64; + fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; + } + else if ( strcmp(architecture, "i386") == 0 ) { + fArchitecture = CPU_TYPE_I386; + fSubArchitecture = CPU_SUBTYPE_I386_ALL; + } + else if ( strcmp(architecture, "x86_64") == 0 ) { + fArchitecture = CPU_TYPE_X86_64; + fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; + } + else if ( strcmp(architecture, "arm") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_ALL; + } + // compatibility support for cpu-sub-types + else if ( strcmp(architecture, "ppc750") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_750; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "ppc7400") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "ppc7450") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "ppc970") == 0 ) { + fArchitecture = CPU_TYPE_POWERPC; + fSubArchitecture = CPU_SUBTYPE_POWERPC_970; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv6") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V6; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv5") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv4t") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V4T; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "xscale") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; + fHasPreferredSubType = true; + } + else if ( strcmp(architecture, "armv7") == 0 ) { + fArchitecture = CPU_TYPE_ARM; + fSubArchitecture = CPU_SUBTYPE_ARM_V7; + fHasPreferredSubType = true; + } + else + throwf("unknown/unsupported architecture name for: -arch %s", architecture); +} + +bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) +{ + struct stat statBuffer; + char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; + sprintf(possiblePath, format, dir, rootName); + bool found = (stat(possiblePath, &statBuffer) == 0); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); + if ( found ) { + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return true; + } + return false; +} + + +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) +{ + FileInfo result; + const int rootNameLen = strlen(rootName); + // if rootName ends in .o there is no .a vs .dylib choice + if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/%s", dir, rootName, result) ) + return result; + } + } + else { + bool lookForDylibs = ( fOutputKind != Options::kDyld); + switch ( fLibrarySearchMode ) { + case kSearchAllDirsForDylibsThenAllDirsForArchives: + // first look in all directories for just for dylibs + if ( lookForDylibs ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + return result; + } + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.so", dir, rootName, result) ) + return result; + } + } + // next look in all directories for just for archives + if ( !dylibsOnly ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + } + break; + + case kSearchDylibAndArchiveInEachDir: + // look in each directory for just for a dylib then for an archive + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + return result; + if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) + return result; + if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) ) + return result; + } + break; + } + } + throwf("library not found for -l%s", rootName); +} + +Options::FileInfo Options::findFramework(const char* frameworkName) +{ + if ( frameworkName == NULL ) + throw "-framework missing next argument"; + char temp[strlen(frameworkName)+1]; + strcpy(temp, frameworkName); + const char* name = temp; + const char* suffix = NULL; + char* comma = strchr(temp, ','); + if ( comma != NULL ) { + *comma = '\0'; + suffix = &comma[1]; + } + return findFramework(name, suffix); +} + +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) +{ + struct stat statBuffer; + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) { + // ??? Shouldn't we be using String here and just initializing it? + // ??? Use str.c_str () to pull out the string for the stat call. + const char* dir = *it; + char possiblePath[PATH_MAX]; + strcpy(possiblePath, dir); + strcat(possiblePath, "/"); + strcat(possiblePath, rootName); + strcat(possiblePath, ".framework/"); + strcat(possiblePath, rootName); + if ( suffix != NULL ) { + char realPath[PATH_MAX]; + // no symlink in framework to suffix variants, so follow main symlink + if ( realpath(possiblePath, realPath) != NULL ) { + strcpy(possiblePath, realPath); + strcat(possiblePath, suffix); + } + } + bool found = (stat(possiblePath, &statBuffer) == 0); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound framework: '%s'\n", + (found ? " " : " not "), possiblePath); + if ( found ) { + FileInfo result; + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + // try without suffix + if ( suffix != NULL ) + return findFramework(rootName, NULL); + else + throwf("framework not found %s", rootName); +} + +Options::FileInfo Options::findFile(const char* path) +{ + FileInfo result; + struct stat statBuffer; + + // if absolute path and not a .o file, the use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { + const int pathLen = strlen(path); + for (std::vector::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { + // ??? Shouldn't we be using String here? + const char* sdkPathDir = *it; + const int sdkPathDirLen = strlen(sdkPathDir); + char possiblePath[sdkPathDirLen+pathLen+4]; + strcpy(possiblePath, sdkPathDir); + if ( possiblePath[sdkPathDirLen-1] == '/' ) + possiblePath[sdkPathDirLen-1] = '\0'; + strcat(possiblePath, path); + if ( stat(possiblePath, &statBuffer) == 0 ) { + result.path = strdup(possiblePath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + } + // try raw path + if ( stat(path, &statBuffer) == 0 ) { + result.path = strdup(path); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + + // try @executable_path substitution + if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { + char newPath[strlen(fExecutablePath) + strlen(path)]; + strcpy(newPath, fExecutablePath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newPath, &path[17]); + if ( stat(newPath, &statBuffer) == 0 ) { + result.path = strdup(newPath); + result.fileLen = statBuffer.st_size; + result.modTime = statBuffer.st_mtime; + return result; + } + } + + // not found + throwf("file not found: %s", path); +} + +Options::FileInfo Options::findFileUsingPaths(const char* path) +{ + FileInfo result; + + const char* lastSlash = strrchr(path, '/'); + const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1]; + + // Is this in a framework? + // /path/Foo.framework/Foo ==> true (Foo) + // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) + // /path/Foo.framework/Resources/Bar ==> false + bool isFramework = false; + if ( lastSlash != NULL ) { + char frameworkDir[strlen(leafName) + 20]; + strcpy(frameworkDir, "/"); + strcat(frameworkDir, leafName); + strcat(frameworkDir, ".framework/"); + if ( strstr(path, frameworkDir) != NULL ) + isFramework = true; + } + + // These are abbreviated versions of the routines findFramework and findLibrary above + // because we already know the final name of the file that we're looking for and so + // don't need to try variations, just paths. We do need to add the additional bits + // onto the framework path though. + if ( isFramework ) { + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) { + const char* dir = *it; + char possiblePath[PATH_MAX]; + strcpy(possiblePath, dir); + strcat(possiblePath, "/"); + strcat(possiblePath, leafName); + strcat(possiblePath, ".framework"); + + //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName); + if ( checkForFile("%s/%s", possiblePath, leafName, result) ) + return result; + } + } + else { + // if this is a .dylib inside a framework, do not search -L paths + // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + int leafLen = strlen(leafName); + bool embeddedDylib = ( (leafLen > 6) + && (strcmp(&leafName[leafLen-6], ".dylib") == 0) + && (strstr(path, ".framework/") != NULL) ); + if ( !embeddedDylib ) { + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) { + const char* dir = *it; + //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); + if ( checkForFile("%s/%s", dir, leafName, result) ) + return result; + } + } + } + + // If we didn't find it fall back to findFile. + return findFile(path); +} + + +void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath) +{ + FILE* file = fopen(segAddrPath, "r"); + if ( file == NULL ) { + warning("-seg_addr_table file cannot be read: %s", segAddrPath); + return; + } + + char path[PATH_MAX]; + uint64_t firstColumAddress = 0; + uint64_t secondColumAddress = 0; + bool hasSecondColumn = false; + while ( fgets(path, PATH_MAX, file) != NULL ) { + path[PATH_MAX-1] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + // ignore lines not starting with 0x number + if ( (path[0] == '0') && (path[1] == 'x') ) { + char* p; + firstColumAddress = strtoull(path, &p, 16); + while ( isspace(*p) ) + ++p; + // see if second column is a number + if ( (p[0] == '0') && (p[1] == 'x') ) { + secondColumAddress = strtoull(p, &p, 16); + hasSecondColumn = true; + while ( isspace(*p) ) + ++p; + } + while ( isspace(*p) ) + ++p; + if ( p[0] == '/' ) { + // remove any trailing whitespace + for(char* end = eol-1; (end > p) && isspace(*end); --end) + *end = '\0'; + // see if this line is for the dylib being linked + if ( strcmp(p, installPath) == 0 ) { + fBaseAddress = firstColumAddress; + if ( hasSecondColumn ) { + fBaseWritableAddress = secondColumAddress; + fSplitSegs = true; + } + break; // out of while loop + } + } + } + } + + fclose(file); +} + +void Options::loadFileList(const char* fileOfPaths) +{ + FILE* file; + const char* comma = strrchr(fileOfPaths, ','); + const char* prefix = NULL; + if ( comma != NULL ) { + prefix = comma+1; + int realFileOfPathsLen = comma-fileOfPaths; + char realFileOfPaths[realFileOfPathsLen+1]; + strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); + realFileOfPaths[realFileOfPathsLen] = '\0'; + file = fopen(realFileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", realFileOfPaths); + } + else { + file = fopen(fileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", fileOfPaths); + } + + char path[PATH_MAX]; + while ( fgets(path, PATH_MAX, file) != NULL ) { + path[PATH_MAX-1] = '\0'; + char* eol = strchr(path, '\n'); + if ( eol != NULL ) + *eol = '\0'; + if ( prefix != NULL ) { + char builtPath[strlen(prefix)+strlen(path)+2]; + strcpy(builtPath, prefix); + strcat(builtPath, "/"); + strcat(builtPath, path); + fInputFiles.push_back(findFile(builtPath)); + } + else { + fInputFiles.push_back(findFile(path)); + } + } + fclose(file); +} + +bool Options::SetWithWildcards::hasWildCards(const char* symbol) +{ + // an exported symbol name containing *, ?, or [ requires wildcard matching + return ( strpbrk(symbol, "*?[") != NULL ); +} + +void Options::SetWithWildcards::insert(const char* symbol) +{ + if ( hasWildCards(symbol) ) + fWildCard.push_back(symbol); + else + fRegular.insert(symbol); +} + +bool Options::SetWithWildcards::contains(const char* symbol) +{ + // first look at hash table on non-wildcard symbols + if ( fRegular.find(symbol) != fRegular.end() ) + return true; + // next walk list of wild card symbols looking for a match + for(std::vector::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { + if ( wildCardMatch(*it, symbol) ) + return true; + } + return false; +} + + +bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) +{ + ++p; // find end + const char* b = p; + while ( *p != '\0' ) { + if ( *p == ']') { + const char* e = p; + // found beginining [ and ending ] + unsigned char last = '\0'; + for ( const char* s = b; s < e; ++s ) { + if ( *s == '-' ) { + unsigned char next = *(++s); + if ( (last <= c) && (c <= next) ) + return true; + ++s; + } + else { + if ( *s == c ) + return true; + last = *s; + } + } + return false; + } + ++p; + } + return false; +} + +bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) +{ + const char* s = symbol; + for (const char* p = pattern; *p != '\0'; ++p) { + switch ( *p ) { + case '*': + if ( p[1] == '\0' ) + return true; + for (const char* t = s; *t != '\0'; ++t) { + if ( wildCardMatch(&p[1], t) ) + return true; + } + return false; + case '?': + if ( *s == '\0' ) + return false; + ++s; + break; + case '[': + if ( ! inCharRange(p, *s) ) + return false; + ++s; + break; + default: + if ( *s != *p ) + return false; + ++s; + } + } + return (*s == '\0'); +} + + +void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) +{ + // read in whole file + int fd = ::open(fileOfExports, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open %s file: %s", option, fileOfExports); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process %s file: %s", option, fileOfExports); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read %s file: %s", option, fileOfExports); + + ::close(fd); + + // parse into symbols and add to hash_set + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (*s == '\r') ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(symbolStart); + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( (*s == '\n') || (*s == '\r') ) + state = lineStart; + break; + } + } + if ( state == inSymbol ) { + warning("missing line-end at end of file \"%s\"", fileOfExports); + int len = end-symbolStart+1; + char* temp = new char[len]; + strlcpy(temp, symbolStart, len); + + // remove any trailing spaces + char* last = &temp[len-2]; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + set.insert(temp); + } + + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table +} + +void Options::parseAliasFile(const char* fileOfAliases) +{ + // read in whole file + int fd = ::open(fileOfAliases, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open alias file: %s", fileOfAliases); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size+1); + if ( p == NULL ) + throwf("can't process alias file: %s", fileOfAliases); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read alias file: %s", fileOfAliases); + p[stat_buf.st_size] = '\n'; + ::close(fd); + + // parse into symbols and add to fAliases + ObjectFile::ReaderOptions::AliasPair pair; + char * const end = &p[stat_buf.st_size+1]; + enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; + int lineNumber = 1; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inRealName; + pair.realName = s; + } + break; + case inRealName: + if ( *s == '\n' ) { + warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( isspace(*s) ) { + *s = '\0'; + state = inBetween; + } + break; + case inBetween: + if ( *s == '\n' ) { + warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( ! isspace(*s) ) { + state = inAliasName; + pair.alias = s; + } + break; + case inAliasName: + if ( *s =='#' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + fReaderOptions.fAliases.push_back(pair); + state = inComment; + } + else if ( *s == '\n' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + fReaderOptions.fAliases.push_back(pair); + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + + // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases +} + + + +void Options::setUndefinedTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; + + if ( strcmp(treatment, "warning") == 0 ) + fUndefinedTreatment = kUndefinedWarning; + else if ( strcmp(treatment, "error") == 0 ) + fUndefinedTreatment = kUndefinedError; + else if ( strcmp(treatment, "suppress") == 0 ) + fUndefinedTreatment = kUndefinedSuppress; + else if ( strcmp(treatment, "dynamic_lookup") == 0 ) + fUndefinedTreatment = kUndefinedDynamicLookup; + else + throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; +} + +Options::Treatment Options::parseTreatment(const char* treatment) +{ + if ( treatment == NULL ) + return kNULL; + + if ( strcmp(treatment, "warning") == 0 ) + return kWarning; + else if ( strcmp(treatment, "error") == 0 ) + return kError; + else if ( strcmp(treatment, "suppress") == 0 ) + return kSuppress; + else + return kInvalid; +} + +void Options::setMacOSXVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-macosx_version_min argument missing"; + + if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { + int num = version[3] - '0'; + switch ( num ) { + case 0: + case 1: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; + break; + default: + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; + break; + } + } + else { + warning("unknown option to -macosx_version_min, not 10.x"); + } +} + +void Options::setIPhoneVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-iphoneos_version_min argument missing"; + + if ( ((strncmp(version, "1.", 2) == 0) || (strncmp(version, "2.", 2) == 0)) && isdigit(version[2]) ) { + int num = version[2] - '0'; + switch ( num ) { + case 2: + // TODO: store deployment version + break; + default: + break; + } + } + else { + warning("unknown option to -iphoneos_version_min, not 1.x or 2.x"); + } +} + +void Options::setWeakReferenceMismatchTreatment(const char* treatment) +{ + if ( treatment == NULL ) + throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; + + if ( strcmp(treatment, "error") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; + else if ( strcmp(treatment, "weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; + else if ( strcmp(treatment, "non-weak") == 0 ) + fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; + else + throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; +} + +Options::CommonsMode Options::parseCommonsTreatment(const char* mode) +{ + if ( mode == NULL ) + throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; + + if ( strcmp(mode, "ignore_dylibs") == 0 ) + return kCommonsIgnoreDylibs; + else if ( strcmp(mode, "use_dylibs") == 0 ) + return kCommonsOverriddenByDylibs; + else if ( strcmp(mode, "error") == 0 ) + return kCommonsConflictsDylibsError; + else + throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; +} + +void Options::addDylibOverride(const char* paths) +{ + if ( paths == NULL ) + throw "-dylib_file must followed by two colon separated paths"; + const char* colon = strchr(paths, ':'); + if ( colon == NULL ) + throw "-dylib_file must followed by two colon separated paths"; + int len = colon-paths; + char* target = new char[len+2]; + strncpy(target, paths, len); + target[len] = '\0'; + DylibOverride entry; + entry.installName = target; + entry.useInstead = &colon[1]; + fDylibOverrides.push_back(entry); +} + +uint64_t Options::parseAddress(const char* addr) +{ + char* endptr; + uint64_t result = strtoull(addr, &endptr, 16); + return result; +} + +uint32_t Options::parseProtection(const char* prot) +{ + uint32_t result = 0; + for(const char* p = prot; *p != '\0'; ++p) { + switch(tolower(*p)) { + case 'r': + result |= VM_PROT_READ; + break; + case 'w': + result |= VM_PROT_WRITE; + break; + case 'x': + result |= VM_PROT_EXECUTE; + break; + case '-': + break; + default: + throwf("unknown -segprot lettter in %s", prot); + } + } + return result; +} + + + +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +// +uint32_t Options::parseVersionNumber(const char* versionString) +{ + unsigned long x = 0; + unsigned long y = 0; + unsigned long z = 0; + char* end; + x = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed version number: %s", versionString); + + return (x << 16) | ( y << 8 ) | z; +} + +static const char* cstringSymbolName(const char* orderFileString) +{ + char* result; + asprintf(&result, "cstring=%s", orderFileString); + // convert escaped characters + char* d = result; + for(const char* s=result; *s != '\0'; ++s, ++d) { + if ( *s == '\\' ) { + ++s; + switch ( *s ) { + case 'n': + *d = '\n'; + break; + case 't': + *d = '\t'; + break; + case 'v': + *d = '\v'; + break; + case 'b': + *d = '\b'; + break; + case 'r': + *d = '\r'; + break; + case 'f': + *d = '\f'; + break; + case 'a': + *d = '\a'; + break; + case '\\': + *d = '\\'; + break; + case '?': + *d = '\?'; + break; + case '\'': + *d = '\r'; + break; + case '\"': + *d = '\"'; + break; + case 'x': + // hexadecimal value of char + { + ++s; + char value = 0; + while ( isxdigit(*s) ) { + value *= 16; + if ( isdigit(*s) ) + value += (*s-'0'); + else + value += ((toupper(*s)-'A') + 10); + ++s; + } + *d = value; + } + break; + default: + if ( isdigit(*s) ) { + // octal value of char + char value = 0; + while ( isdigit(*s) ) { + value = (value << 3) + (*s-'0'); + ++s; + } + *d = value; + } + } + } + else { + *d = *s; + } + } + *d = '\0'; + return result; +} + +void Options::parseOrderFile(const char* path, bool cstring) +{ + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open order file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size+1); + if ( p == NULL ) + throwf("can't process order file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read order file: %s", path); + ::close(fd); + p[stat_buf.st_size] = '\n'; + + // parse into vector of pairs + char * const end = &p[stat_buf.st_size+1]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) || cstring ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (!cstring && (*s == '#')) ) { + bool wasComment = (*s == '#'); + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { + if ( fArchitecture == CPU_TYPE_POWERPC ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; + } + // if there is an architecture prefix, only use this symbol it if matches current arch + else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_POWERPC64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { + if ( fArchitecture == CPU_TYPE_I386 ) + symbolStart = &symbolStart[5]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) { + if ( fArchitecture == CPU_TYPE_X86_64 ) + symbolStart = &symbolStart[7]; + else + symbolStart = NULL; + } + else if ( strncmp(symbolStart, "arm:", 4) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; + } + if ( symbolStart != NULL ) { + char* objFileName = NULL; + char* colon = strstr(symbolStart, ".o:"); + if ( colon != NULL ) { + colon[2] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[3]; + } + // trim leading spaces + while ( isspace(*symbolStart) ) + ++symbolStart; + Options::OrderedSymbol pair; + if ( cstring ) + pair.symbolName = cstringSymbolName(symbolStart); + else + pair.symbolName = symbolStart; + pair.objectFileName = objFileName; + fOrderedSymbols.push_back(pair); + } + symbolStart = NULL; + if ( wasComment ) + state = inComment; + else + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols +} + +void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) +{ + if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) { + parseOrderFile(path, true); + } + else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) { + warning("sorting of __literal[4,8,16] sections not supported"); + } + else { + // ignore section information and append all symbol names to global order file + parseOrderFile(path, false); + } +} + +void Options::addSection(const char* segment, const char* section, const char* path) +{ + if ( strlen(segment) > 16 ) + throw "-seccreate segment name max 16 chars"; + if ( strlen(section) > 16 ) { + char* tmp = strdup(section); + tmp[16] = '\0'; + warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp); + section = tmp; + } + + // read in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open -sectcreate file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process -sectcreate file: %s", path); + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read -sectcreate file: %s", path); + ::close(fd); + + // record section to create + ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; + fExtraSections.push_back(info); +} + +void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) +{ + if ( strlen(segment) > 16 ) + throw "-sectalign segment name max 16 chars"; + if ( strlen(section) > 16 ) + throw "-sectalign section name max 16 chars"; + + // argument to -sectalign is a hexadecimal number + char* endptr; + unsigned long value = strtoul(alignmentStr, &endptr, 16); + if ( *endptr != '\0') + throw "argument for -sectalign is not a hexadecimal number"; + if ( value > 0x8000 ) + throw "argument for -sectalign must be less than or equal to 0x8000"; + if ( value == 0 ) { + warning("zero is not a valid -sectalign"); + value = 1; + } + + // alignment is power of 2 (e.g. page alignment = 12) + uint8_t alignment = (uint8_t)__builtin_ctz(value); + if ( (unsigned long)(1 << alignment) != value ) { + warning("alignment for -sectalign %s %s is not a power of two, using 0x%X", + segment, section, 1 << alignment); + } + + SectionAlignment info = { segment, section, alignment }; + fSectionAlignments.push_back(info); +} + +void Options::addLibrary(const FileInfo& info) +{ + // if this library has already been added, don't add again (archives are automatically repeatedly searched) + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + if ( strcmp(info.path, fit->path) == 0 ) { + // if dylib is specified again but weak, record that it should be weak + if ( info.options.fWeakImport ) + fit->options.fWeakImport = true; + return; + } + } + // add to list + fInputFiles.push_back(info); +} + +void Options::warnObsolete(const char* arg) +{ + warning("option %s is obsolete and being ignored", arg); +} + + + + +// +// Process all command line arguments. +// +// The only error checking done here is that each option is valid and if it has arguments +// that they too are valid. +// +// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified, +// whichever was last on the command line is used. +// +// Error check for invalid combinations of options is done in checkIllegalOptionCombinations() +// +void Options::parse(int argc, const char* argv[]) +{ + // pass one builds search list from -L and -F options + this->buildSearchPaths(argc, argv); + + // reduce re-allocations + fInputFiles.reserve(32); + + // pass two parse all other options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + + if ( arg[0] == '-' ) { + + // Since we don't care about the files passed, just the option names, we do this here. + if (fPrintOptions) + fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); + + if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + // previously handled by buildSearchPaths() + } + // The one gnu style option we have to keep compatibility + // with gcc. Might as well have the single hyphen one as well. + else if ( (strcmp(arg, "--help") == 0) + || (strcmp(arg, "-help") == 0)) { + fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n"); + exit (0); + } + else if ( strcmp(arg, "-arch") == 0 ) { + parseArch(argv[++i]); + } + else if ( strcmp(arg, "-dynamic") == 0 ) { + // default + } + else if ( strcmp(arg, "-static") == 0 ) { + if ( fOutputKind != kObjectFile ) + fOutputKind = kStaticExecutable; + fReaderOptions.fForStatic = true; + } + else if ( strcmp(arg, "-dylib") == 0 ) { + fOutputKind = kDynamicLibrary; + } + else if ( strcmp(arg, "-bundle") == 0 ) { + fOutputKind = kDynamicBundle; + } + else if ( strcmp(arg, "-dylinker") == 0 ) { + fOutputKind = kDyld; + } + else if ( strcmp(arg, "-execute") == 0 ) { + if ( fOutputKind != kStaticExecutable ) + fOutputKind = kDynamicExecutable; + } + else if ( strcmp(arg, "-r") == 0 ) { + fOutputKind = kObjectFile; + } + else if ( strcmp(arg, "-o") == 0 ) { + fOutputFile = argv[++i]; + } + else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { + addLibrary(findLibrary(&arg[2])); + } + // This causes a dylib to be weakly bound at + // link time. This corresponds to weak_import. + else if ( strncmp(arg, "-weak-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7]); + info.options.fWeakImport = true; + addLibrary(info); + } + else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7], true); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } + // Avoid lazy binding. + // ??? Deprecate. + else if ( strcmp(arg, "-bind_at_load") == 0 ) { + fBindAtLoad = true; + } + else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { + fNameSpace = kTwoLevelNameSpace; + } + else if ( strcmp(arg, "-flat_namespace") == 0 ) { + fNameSpace = kFlatNameSpace; + } + // Also sets a bit to ensure dyld causes everything + // in the namespace to be flat. + // ??? Deprecate + else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { + fNameSpace = kForceFlatNameSpace; + } + // Similar to --whole-archive. + else if ( strcmp(arg, "-all_load") == 0 ) { + fReaderOptions.fFullyLoadArchives = true; + } + else if ( strcmp(arg, "-noall_load") == 0) { + warnObsolete(arg); + } + // Similar to -all_load + else if ( strcmp(arg, "-ObjC") == 0 ) { + fReaderOptions.fLoadAllObjcObjectsFromArchives = true; + } + // Library versioning. + else if ( (strcmp(arg, "-dylib_compatibility_version") == 0) + || (strcmp(arg, "-compatibility_version") == 0)) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-dylib_compatibility_version missing "; + fDylibCompatVersion = parseVersionNumber(vers); + } + else if ( (strcmp(arg, "-dylib_current_version") == 0) + || (strcmp(arg, "-current_version") == 0)) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-dylib_current_version missing "; + fDylibCurrentVersion = parseVersionNumber(vers); + } + else if ( strcmp(arg, "-sectorder") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectorder missing

"; + parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( strcmp(arg, "-order_file") == 0 ) { + parseOrderFile(argv[++i], false); + } + else if ( strcmp(arg, "-order_file_statistics") == 0 ) { + fPrintOrderFileStatistics = true; + } + // ??? Deprecate segcreate. + // -sectcreate puts whole files into a section in the output. + else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectcreate missing
"; + addSection(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + // Since we have a full path in binary/library names we need to be able to override it. + else if ( (strcmp(arg, "-dylib_install_name") == 0) + || (strcmp(arg, "-dylinker_install_name") == 0) + || (strcmp(arg, "-install_name") == 0)) { + fDylibInstallName = argv[++i]; + if ( fDylibInstallName == NULL ) + throw "-install_name missing "; + } + // Sets the base address of the output. + else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) { + const char* address = argv[++i]; + if ( address == NULL ) + throwf("%s missing
", arg); + fBaseAddress = parseAddress(address); + uint64_t temp = (fBaseAddress+4095) & (-4096); // page align + if ( fBaseAddress != temp ) { + warning("-seg1addr not page aligned, rounding up"); + fBaseAddress = temp; + } + } + else if ( strcmp(arg, "-e") == 0 ) { + fEntryName = argv[++i]; + } + // Same as -@ from the FSF linker. + else if ( strcmp(arg, "-filelist") == 0 ) { + const char* path = argv[++i]; + if ( (path == NULL) || (path[0] == '-') ) + throw "-filelist missing "; + loadFileList(path); + } + else if ( strcmp(arg, "-keep_private_externs") == 0 ) { + fKeepPrivateExterns = true; + } + else if ( strcmp(arg, "-final_output") == 0 ) { + fFinalName = argv[++i]; + } + // Ensure that all calls to exported symbols go through lazy pointers. Multi-module + // just ensures that this happens for cross object file boundaries. + else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { + switch ( fInterposeMode ) { + case kInterposeNone: + case kInterposeAllExternal: + fInterposeMode = kInterposeAllExternal; + break; + case kInterposeSome: + // do nothing, -interposable_list overrides -interposable" + break; + } + } + else if ( strcmp(arg, "-interposable_list") == 0 ) { + fInterposeMode = kInterposeSome; + loadExportFile(argv[++i], "-interposable_list", fInterposeList); + } + // Default for -interposable/-multi_module/-single_module. + else if ( strcmp(arg, "-single_module") == 0 ) { + fInterposeMode = kInterposeNone; + } + else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbols_list and -unexported_symbols_list"; + fExportMode = kExportSome; + loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); + } + else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -unexported_symbols_list and -exported_symbols_list"; + fExportMode = kDontExportSome; + loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); + } + else if ( strcmp(arg, "-exported_symbol") == 0 ) { + if ( fExportMode == kDontExportSome ) + throw "can't use -exported_symbol and -unexported_symbols"; + fExportMode = kExportSome; + fExportSymbols.insert(argv[++i]); + } + else if ( strcmp(arg, "-unexported_symbol") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -unexported_symbol and -exported_symbol"; + fExportMode = kDontExportSome; + fDontExportSymbols.insert(argv[++i]); + } + else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { + if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude ) + throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; + fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; + loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); + } + else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { + if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude ) + throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; + fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; + loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); + } + // ??? Deprecate + else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { + fIgnoreOtherArchFiles = true; + } + else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { + fForceSubtypeAll = true; + } + // Similar to -weak-l but uses the absolute path name to the library. + else if ( strcmp(arg, "-weak_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fWeakImport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-lazy_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } + else if ( strcmp(arg, "-framework") == 0 ) { + addLibrary(findFramework(argv[++i])); + } + else if ( strcmp(arg, "-weak_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fWeakImport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-lazy_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } + else if ( strcmp(arg, "-search_paths_first") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-undefined") == 0 ) { + setUndefinedTreatment(argv[++i]); + } + // Debugging output flag. + else if ( strcmp(arg, "-arch_multiple") == 0 ) { + fMessagesPrefixedWithArchitecture = true; + } + // Specify what to do with relocations in read only + // sections like .text. Could be errors, warnings, + // or suppressed. Currently we do nothing with the + // flag. + else if ( strcmp(arg, "-read_only_relocs") == 0 ) { + switch ( parseTreatment(argv[++i]) ) { + case kNULL: + case kInvalid: + throw "-read_only_relocs missing [ warning | error | suppress ]"; + case kWarning: + fWarnTextRelocs = true; + fAllowTextRelocs = true; + break; + case kSuppress: + fWarnTextRelocs = false; + fAllowTextRelocs = true; + break; + case kError: + fWarnTextRelocs = false; + fAllowTextRelocs = false; + break; + } + } + else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { + warnObsolete(arg); + ++i; + } + // Warn, error or make strong a mismatch between weak + // and non-weak references. + else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { + setWeakReferenceMismatchTreatment(argv[++i]); + } + // For a deployment target of 10.3 and earlier ld64 will + // prebind an executable with 0s in all addresses that + // are prebound. This can then be fixed up by update_prebinding + // later. Prebinding is less useful on 10.4 and greater. + else if ( strcmp(arg, "-prebind") == 0 ) { + fPrebind = true; + } + else if ( strcmp(arg, "-noprebind") == 0 ) { + warnObsolete(arg); + fPrebind = false; + } + else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-nofixprebinding") == 0 ) { + warnObsolete(arg); + } + // This should probably be deprecated when we respect -L and -F + // when searching for libraries. + else if ( strcmp(arg, "-dylib_file") == 0 ) { + addDylibOverride(argv[++i]); + } + // What to expand @executable_path to if found in dependent dylibs + else if ( strcmp(arg, "-executable_path") == 0 ) { + fExecutablePath = argv[++i]; + if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') ) + throw "-executable_path missing "; + // if a directory was passed, add / to end + // ld64 can't find @executable _path relative dylibs from our umbrella frameworks + struct stat statBuffer; + if ( stat(fExecutablePath, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) { + char* pathWithSlash = new char[strlen(fExecutablePath)+2]; + strcpy(pathWithSlash, fExecutablePath); + strcat(pathWithSlash, "/"); + fExecutablePath = pathWithSlash; + } + } + } + // Aligns all segments to the power of 2 boundary specified. + else if ( strcmp(arg, "-segalign") == 0 ) { + warnObsolete(arg); + ++i; + } + // Puts a specified segment at a particular address that must + // be a multiple of the segment alignment. + else if ( strcmp(arg, "-segaddr") == 0 ) { + SegmentStart seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) ) + throw "-segaddr missing segName Adddress"; + seg.address = parseAddress(argv[++i]); + uint64_t temp = seg.address & (-4096); // page align + if ( (seg.address != temp) ) + warning("-segaddr %s not page aligned, rounding down", seg.name); + fCustomSegmentAddresses.push_back(seg); + } + // ??? Deprecate when we deprecate split-seg. + else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { + fBaseAddress = parseAddress(argv[++i]); + } + // ??? Deprecate when we deprecate split-seg. + else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { + fBaseWritableAddress = parseAddress(argv[++i]); + fSplitSegs = true; + } + // ??? Deprecate when we get rid of basing at build time. + else if ( strcmp(arg, "-seg_addr_table") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-seg_addr_table missing argument"; + fSegAddrTablePath = name; + } + else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-segprot") == 0 ) { + SegmentProtect seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) ) + throw "-segprot missing segName max-prot init-prot"; + seg.max = parseProtection(argv[++i]); + seg.init = parseProtection(argv[++i]); + fCustomSegmentProtections.push_back(seg); + } + else if ( strcmp(arg, "-pagezero_size") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-pagezero_size missing "; + fZeroPageSize = parseAddress(size); + uint64_t temp = fZeroPageSize & (-4096); // page align + if ( (fZeroPageSize != temp) ) + warning("-pagezero_size not page aligned, rounding down"); + fZeroPageSize = temp; + } + else if ( strcmp(arg, "-stack_addr") == 0 ) { + const char* address = argv[++i]; + if ( address == NULL ) + throw "-stack_addr missing
"; + fStackAddr = parseAddress(address); + } + else if ( strcmp(arg, "-stack_size") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-stack_size missing
"; + fStackSize = parseAddress(size); + uint64_t temp = fStackSize & (-4096); // page align + if ( (fStackSize != temp) ) + warning("-stack_size not page aligned, rounding down"); + } + else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { + fExecutableStack = true; + } + else if ( strcmp(arg, "-sectalign") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) + throw "-sectalign missing
"; + addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); + i += 3; + } + else if ( strcmp(arg, "-sectorder_detail") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { + warnObsolete(arg); + i += 2; + } + else if ( strcmp(arg, "-bundle_loader") == 0 ) { + fBundleLoader = argv[++i]; + if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) + throw "-bundle_loader missing "; + FileInfo info = findFile(fBundleLoader); + info.options.fBundleLoader = true; + fInputFiles.push_back(info); + } + else if ( strcmp(arg, "-private_bundle") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { + // FIX FIX + } + // Use this flag to set default behavior for deployement targets. + else if ( strcmp(arg, "-macosx_version_min") == 0 ) { + setMacOSXVersionMin(argv[++i]); + } + else if ( (strcmp(arg, "-aspen_version_min") == 0) || (strcmp(arg, "-iphone_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { + setIPhoneVersionMin(argv[++i]); + } + else if ( strcmp(arg, "-multiply_defined") == 0 ) { + //warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-nomultidefs") == 0 ) { + warnObsolete(arg); + } + // Display each file in which the argument symbol appears and whether + // the file defines or references it. This option takes an argument + // as -y note that there is no space. + else if ( strncmp(arg, "-y", 2) == 0 ) { + warnObsolete("-y"); + } + // Same output as -y, but output number of undefined symbols only. + else if ( strcmp(arg, "-Y") == 0 ) { + //warnObsolete(arg); + ++i; + } + // This option affects all objects linked into the final result. + else if ( strcmp(arg, "-m") == 0 ) { + warnObsolete(arg); + } + else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { + fReaderOptions.fWhyLoad = true; + } + else if ( strcmp(arg, "-why_live") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-why_live missing symbol name argument"; + fWhyLive.insert(name); + } + else if ( strcmp(arg, "-u") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-u missing argument"; + fInitialUndefines.push_back(name); + } + else if ( strcmp(arg, "-U") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-U missing argument"; + fAllowedUndefined.insert(name); + } + else if ( strcmp(arg, "-s") == 0 ) { + warnObsolete(arg); + fLocalSymbolHandling = kLocalSymbolsNone; + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + } + else if ( strcmp(arg, "-x") == 0 ) { + fLocalSymbolHandling = kLocalSymbolsNone; + } + else if ( strcmp(arg, "-S") == 0 ) { + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + } + else if ( strcmp(arg, "-X") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-Si") == 0 ) { + warnObsolete(arg); + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + } + else if ( strcmp(arg, "-b") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-Sn") == 0 ) { + warnObsolete(arg); + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + } + else if ( strcmp(arg, "-Sp") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-dead_strip") == 0 ) { + fDeadStrip = kDeadStripOnPlusUnusedInits; + } + else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { + fDeadStrip = kDeadStripOn; + } + else if ( strcmp(arg, "-w") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-M") == 0 ) { + // FIX FIX + } + else if ( strcmp(arg, "-headerpad") == 0 ) { + const char* size = argv[++i]; + if ( size == NULL ) + throw "-headerpad missing argument"; + fMinimumHeaderPad = parseAddress(size); + } + else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { + fMaxMinimumHeaderPad = true; + } + else if ( strcmp(arg, "-t") == 0 ) { + fReaderOptions.fLogAllFiles = true; + } + else if ( strcmp(arg, "-whatsloaded") == 0 ) { + fReaderOptions.fLogObjectFiles = true; + } + else if ( strcmp(arg, "-A") == 0 ) { + warnObsolete(arg); + ++i; + } + else if ( strcmp(arg, "-umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-umbrella missing argument"; + fUmbrellaName = name; + } + else if ( strcmp(arg, "-allowable_client") == 0 ) { + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-allowable_client missing argument"; + + fAllowableClients.push_back(name); + } + else if ( strcmp(arg, "-client_name") == 0 ) { + const char* name = argv[++i]; + + if ( name == NULL ) + throw "-client_name missing argument"; + + fClientName = name; + } + else if ( strcmp(arg, "-sub_umbrella") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_umbrella missing argument"; + fSubUmbellas.push_back(name); + } + else if ( strcmp(arg, "-sub_library") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-sub_library missing argument"; + fSubLibraries.push_back(name); + } + else if ( strcmp(arg, "-init") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-init missing argument"; + fInitFunctionName = name; + } + else if ( strcmp(arg, "-dot") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dot missing argument"; + fDotOutputFile = name; + } + else if ( strcmp(arg, "-warn_commons") == 0 ) { + fWarnCommons = true; + } + else if ( strcmp(arg, "-commons") == 0 ) { + fCommonsMode = parseCommonsTreatment(argv[++i]); + } + else if ( strcmp(arg, "-keep_relocs") == 0 ) { + fKeepRelocations = true; + } + else if ( strcmp(arg, "-warn_stabs") == 0 ) { + fWarnStabs = true; + } + else if ( strcmp(arg, "-pause") == 0 ) { + fPause = true; + } + else if ( strcmp(arg, "-print_statistics") == 0 ) { + fStatistics = true; + } + else if ( strcmp(arg, "-d") == 0 ) { + fReaderOptions.fMakeTentativeDefinitionsReal = true; + } + else if ( strcmp(arg, "-v") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-Z") == 0 ) { + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-syslibroot") == 0 ) { + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-no_uuid") == 0 ) { + fUUIDMode = kUUIDNone; + } + else if ( strcmp(arg, "-random_uuid") == 0 ) { + fUUIDMode = kUUIDRandom; + } + else if ( strcmp(arg, "-dtrace") == 0 ) { + const char* name = argv[++i]; + if ( name == NULL ) + throw "-dtrace missing argument"; + fDtraceScriptName = name; + } + else if ( strcmp(arg, "-root_safe") == 0 ) { + fReaderOptions.fRootSafe = true; + } + else if ( strcmp(arg, "-setuid_safe") == 0 ) { + fReaderOptions.fSetuidSafe = true; + } + else if ( strcmp(arg, "-alias") == 0 ) { + ObjectFile::ReaderOptions::AliasPair pair; + pair.realName = argv[++i]; + if ( pair.realName == NULL ) + throw "missing argument to -alias"; + pair.alias = argv[++i]; + if ( pair.alias == NULL ) + throw "missing argument to -alias"; + fReaderOptions.fAliases.push_back(pair); + } + else if ( strcmp(arg, "-alias_list") == 0 ) { + parseAliasFile(argv[++i]); + } + // put this last so that it does not interfer with other options starting with 'i' + else if ( strncmp(arg, "-i", 2) == 0 ) { + const char* colon = strchr(arg, ':'); + if ( colon == NULL ) + throwf("unknown option: %s", arg); + ObjectFile::ReaderOptions::AliasPair pair; + char* temp = new char[colon-arg]; + strlcpy(temp, &arg[2], colon-arg-1); + pair.realName = &colon[1]; + pair.alias = temp; + fReaderOptions.fAliases.push_back(pair); + } + else if ( strcmp(arg, "-save-temps") == 0 ) { + fSaveTempFiles = true; + } + else if ( strcmp(arg, "-rpath") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "missing argument to -rpath"; + fRPaths.push_back(path); + } + else if ( strcmp(arg, "-read_only_stubs") == 0 ) { + fReadOnlyx86Stubs = true; + } + else if ( strcmp(arg, "-slow_stubs") == 0 ) { + fReaderOptions.fSlowx86Stubs = true; + } + else if ( strcmp(arg, "-map") == 0 ) { + fMapPath = argv[++i]; + if ( fMapPath == NULL ) + throw "missing argument to -map"; + } + else if ( strcmp(arg, "-pie") == 0 ) { + fPositionIndependentExecutable = true; + } + else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { + FileInfo info = findLibrary(&arg[11], true); + info.options.fReExport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-reexport_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fReExport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-reexport_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fReExport = true; + addLibrary(info); + } + else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { + fDeadStripDylibs = true; + } + else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { + fReaderOptions.fImplicitlyLinkPublicDylibs = false; + } + else if ( strcmp(arg, "-new_linker") == 0 ) { + // ignore + } + else if ( strcmp(arg, "-no_encryption") == 0 ) { + fEncryptable = false; + } + else if ( strcmp(arg, "-mllvm") == 0 ) { + const char* opts = argv[++i]; + if ( opts == NULL ) + throw "missing argument to -mllvm"; + fLLVMOptions.push_back(opts); + } + else { + throwf("unknown option: %s", arg); + } + } + else { + FileInfo info = findFile(arg); + if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 ) + addLibrary(info); + else + fInputFiles.push_back(info); + } + } + + // if a -lazy option was used, implicitly link in lazydylib1.o + if ( fUsingLazyDylibLinking ) { + addLibrary(findLibrary("lazydylib1.o")); + } +} + + + +// +// -syslibroot is used for SDK support. +// The rule is that all search paths (both explicit and default) are +// checked to see if they exist in the SDK. If so, that path is +// replaced with the sdk prefixed path. If not, that search path +// is used as is. If multiple -syslibroot options are specified +// their directory structures are logically overlayed and files +// from sdks specified earlier on the command line used before later ones. + +void Options::buildSearchPaths(int argc, const char* argv[]) +{ + bool addStandardLibraryDirectories = true; + std::vector libraryPaths; + std::vector frameworkPaths; + libraryPaths.reserve(10); + frameworkPaths.reserve(10); + // scan through argv looking for -L, -F, -Z, and -syslibroot options + for(int i=0; i < argc; ++i) { + if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) + libraryPaths.push_back(&argv[i][2]); + else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) + frameworkPaths.push_back(&argv[i][2]); + else if ( strcmp(argv[i], "-Z") == 0 ) + addStandardLibraryDirectories = false; + else if ( strcmp(argv[i], "-v") == 0 ) { + fVerbose = true; + extern const char ldVersionString[]; + fprintf(stderr, "%s", ldVersionString); + // if only -v specified, exit cleanly + if ( argc == 2 ) { +#if LTO_SUPPORT + printLTOVersion(*this); +#endif + exit(0); + } + } + else if ( strcmp(argv[i], "-syslibroot") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-syslibroot missing argument"; + fSDKPaths.push_back(path); + } + else if ( strcmp(argv[i], "-search_paths_first") == 0 ) { + // ??? Deprecate when we get -Bstatic/-Bdynamic. + fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; + } + else if ( strcmp(argv[i], "-w") == 0 ) { + sEmitWarnings = false; + } + } + if ( addStandardLibraryDirectories ) { + libraryPaths.push_back("/usr/lib"); + libraryPaths.push_back("/usr/local/lib"); + + frameworkPaths.push_back("/Library/Frameworks/"); + frameworkPaths.push_back("/System/Library/Frameworks/"); + // remove /Network from default search path + //frameworkPaths.push_back("/Network/Library/Frameworks/"); + } + + // Support for configure based hacks + // if last -syslibroot is /, then ignore all syslibroots + if ( fSDKPaths.size() > 0 ) { + if ( strcmp(fSDKPaths.back(), "/") == 0 ) { + fSDKPaths.clear(); + } + } + + // now merge sdk and library paths to make real search paths + fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1)); + for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { + const char* libDir = *it; + bool sdkOverride = false; + if ( libDir[0] == '/' ) { + char betterLibDir[PATH_MAX]; + if ( strstr(libDir, "/..") != NULL ) { + if ( realpath(libDir, betterLibDir) != NULL ) + libDir = strdup(betterLibDir); + } + const int libDirLen = strlen(libDir); + for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + // ??? Should be using string here. + const char* sdkDir = *sdkit; + const int sdkDirLen = strlen(sdkDir); + char newPath[libDirLen + sdkDirLen+4]; + strcpy(newPath, sdkDir); + if ( newPath[sdkDirLen-1] == '/' ) + newPath[sdkDirLen-1] = '\0'; + strcat(newPath, libDir); + struct stat statBuffer; + if ( stat(newPath, &statBuffer) == 0 ) { + fLibrarySearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } + } + } + if ( !sdkOverride ) + fLibrarySearchPaths.push_back(libDir); + } + + // now merge sdk and framework paths to make real search paths + fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); + for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { + const char* frameworkDir = *it; + bool sdkOverride = false; + if ( frameworkDir[0] == '/' ) { + char betterFrameworkDir[PATH_MAX]; + if ( strstr(frameworkDir, "/..") != NULL ) { + if ( realpath(frameworkDir, betterFrameworkDir) != NULL ) + frameworkDir = strdup(betterFrameworkDir); + } + const int frameworkDirLen = strlen(frameworkDir); + for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { + // ??? Should be using string here + const char* sdkDir = *sdkit; + const int sdkDirLen = strlen(sdkDir); + char newPath[frameworkDirLen + sdkDirLen+4]; + strcpy(newPath, sdkDir); + if ( newPath[sdkDirLen-1] == '/' ) + newPath[sdkDirLen-1] = '\0'; + strcat(newPath, frameworkDir); + struct stat statBuffer; + if ( stat(newPath, &statBuffer) == 0 ) { + fFrameworkSearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } + } + } + if ( !sdkOverride ) + fFrameworkSearchPaths.push_back(frameworkDir); + } + + if ( fVerbose ) { + fprintf(stderr,"Library search paths:\n"); + for (std::vector::iterator it = fLibrarySearchPaths.begin(); + it != fLibrarySearchPaths.end(); + it++) + fprintf(stderr,"\t%s\n", *it); + fprintf(stderr,"Framework search paths:\n"); + for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + it != fFrameworkSearchPaths.end(); + it++) + fprintf(stderr,"\t%s\n", *it); + } +} + +// this is run before the command line is parsed +void Options::parsePreCommandLineEnvironmentSettings() +{ + if ((getenv("LD_TRACE_ARCHIVES") != NULL) + || (getenv("RC_TRACE_ARCHIVES") != NULL)) + fReaderOptions.fTraceArchives = true; + + if ((getenv("LD_TRACE_DYLIBS") != NULL) + || (getenv("RC_TRACE_DYLIBS") != NULL)) { + fReaderOptions.fTraceDylibs = true; + fReaderOptions.fTraceIndirectDylibs = true; + } + + if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { + fTraceDylibSearching = true; + } + + if (getenv("LD_PRINT_OPTIONS") != NULL) + fPrintOptions = true; + + if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) + fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); + + if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) + fPrintOrderFileStatistics = true; + + if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL) + fSplitSegs = true; + + if (getenv("LD_NO_ENCRYPT") != NULL) + fEncryptable = false; + + sWarningsSideFilePath = getenv("LD_WARN_FILE"); +} + + +// this is run after the command line is parsed +void Options::parsePostCommandLineEnvironmentSettings() +{ + // when building a dynamic main executable, default any use of @executable_path to output path + if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) { + fExecutablePath = fOutputFile; + } + + // allow build system to set default seg_addr_table + if ( fSegAddrTablePath == NULL ) + fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE"); + + // allow build system to turn on prebinding + if ( !fPrebind ) { + fPrebind = ( getenv("LD_PREBIND") != NULL ); + } + + // allow build system to force on dead-code-stripping + if ( fDeadStrip == kDeadStripOff ) { + if ( getenv("LD_DEAD_STRIP") != NULL ) { + switch (fOutputKind) { + case Options::kDynamicLibrary: + case Options::kDynamicExecutable: + case Options::kDynamicBundle: + fDeadStrip = kDeadStripOn; + break; + case Options::kObjectFile: + case Options::kDyld: + case Options::kStaticExecutable: + break; + } + } + } + + // allow build system to force on -warn_commons + if ( getenv("LD_WARN_COMMONS") != NULL ) + fWarnCommons = true; +} + +void Options::reconfigureDefaults() +{ + // sync reader options + switch ( fOutputKind ) { + case Options::kObjectFile: + fReaderOptions.fForFinalLinkedImage = false; + break; + case Options::kDyld: + fReaderOptions.fForDyld = true; + fReaderOptions.fForFinalLinkedImage = true; + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + fReaderOptions.fForFinalLinkedImage = true; + break; + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fReaderOptions.fLinkingMainExecutable = true; + fReaderOptions.fForFinalLinkedImage = true; + break; + } + + // set default min OS version + if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) { + // if -macosx_version_min not used, try environment variable + const char* envVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + if ( envVers != NULL ) + setMacOSXVersionMin(envVers); + // if -macosx_version_min and environment variable not used assume current OS version + if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; // FIX FIX, this really should be a check of the OS version the linker is running on + } + + // adjust min based on architecture + switch ( fArchitecture ) { + case CPU_TYPE_I386: + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for i386"); + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + } + break; + case CPU_TYPE_POWERPC64: + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for ppc64"); + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + } + break; + case CPU_TYPE_X86_64: + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for x86_64"); + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + } + break; + } + + // disable implicit dylibs when targetting 10.3 + // add option to disable implicit load commands for indirectly used public dylibs + if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_3 ) + fReaderOptions.fImplicitlyLinkPublicDylibs = false; + + + // determine if info for shared region should be added + if ( fOutputKind == Options::kDynamicLibrary ) { + if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + if ( fArchitecture != CPU_TYPE_ARM ) + fSharedRegionEligible = true; + } + + // allow build system to force linker to ignore seg_addr_table + if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL ) + fSegAddrTablePath = NULL; + + // check for base address specified externally + if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) { + parseSegAddrTable(fSegAddrTablePath, this->installPath()); + // HACK to support seg_addr_table entries that are physical paths instead of install paths + if ( fBaseAddress == 0 ) { + if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib"); + + else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib"); + + else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib"); + } + } + + // split segs only allowed for dylibs + if ( fSplitSegs ) { + // split seg only supported for ppc, i386, and arm. + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + if ( fOutputKind != Options::kDynamicLibrary ) + fSplitSegs = false; + // make sure read and write segments are proper distance apart + if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) ) + fBaseWritableAddress = fBaseAddress + 0x10000000; + break; + case CPU_TYPE_ARM: + if ( fOutputKind != Options::kDynamicLibrary ) { + fSplitSegs = false; + } + else { + // make sure read and write segments are proper distance apart + if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) ) + fBaseWritableAddress = fBaseAddress + 0x08000000; + } + break; + default: + fSplitSegs = false; + fBaseAddress = 0; + fBaseWritableAddress = 0; + } + } + + // disable prebinding depending on arch and min OS version + if ( fPrebind ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::k10_4 ) { + // in 10.4 only split seg dylibs are prebound + if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) + fPrebind = false; + } + else if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { + // in 10.5 nothing is prebound + fPrebind = false; + } + else { + // in 10.3 and earlier only dylibs and main executables could be prebound + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + // only main executables and dylibs can be prebound + break; + case Options::kStaticExecutable: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + // disable prebinding for everything else + fPrebind = false; + break; + } + } + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + fPrebind = false; + break; + case CPU_TYPE_ARM: + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + // only main executables and dylibs can be prebound + break; + case Options::kStaticExecutable: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + // disable prebinding for everything else + fPrebind = false; + break; + } + break; + } + } + + // only prebound images can be split-seg + if ( fSplitSegs && !fPrebind ) + fSplitSegs = false; + + // figure out if module table is needed for compatibility with old ld/dyld + if ( fOutputKind == Options::kDynamicLibrary ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table + case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table + if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_5 ) + fNeedsModuleTable = true; + break; + case CPU_TYPE_ARM: + fNeedsModuleTable = true; // redo_prebinding requires a module table + break; + } + } + + // -r -x implies -S + if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + + // only ARM main executables can be encrypted + if ( fOutputKind != Options::kDynamicExecutable ) + fEncryptable = false; + if ( fArchitecture != CPU_TYPE_ARM ) + fEncryptable = false; +} + +void Options::checkIllegalOptionCombinations() +{ + // check -undefined setting + switch ( fUndefinedTreatment ) { + case kUndefinedError: + case kUndefinedDynamicLookup: + // always legal + break; + case kUndefinedWarning: + case kUndefinedSuppress: + // requires flat namespace + if ( fNameSpace == kTwoLevelNameSpace ) + throw "can't use -undefined warning or suppress with -twolevel_namespace"; + break; + } + + // unify -sub_umbrella with dylibs + for (std::vector::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) { + const char* subUmbrella = *it; + bool found = false; + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + Options::FileInfo& info = *fit; + const char* lastSlash = strrchr(info.path, '/'); + if ( lastSlash == NULL ) + lastSlash = info.path - 1; + if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { + info.options.fReExport = true; + found = true; + break; + } + } + if ( ! found ) + warning("-sub_umbrella %s does not match a supplied dylib", subUmbrella); + } + + // unify -sub_library with dylibs + for (std::vector::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) { + const char* subLibrary = *it; + bool found = false; + for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { + Options::FileInfo& info = *fit; + const char* lastSlash = strrchr(info.path, '/'); + if ( lastSlash == NULL ) + lastSlash = info.path - 1; + const char* dot = strchr(&lastSlash[1], '.'); + if ( dot == NULL ) + dot = &lastSlash[strlen(lastSlash)]; + if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { + info.options.fReExport = true; + found = true; + break; + } + } + if ( ! found ) + warning("-sub_library %s does not match a supplied dylib", subLibrary); + } + + // sync reader options + if ( fNameSpace != kTwoLevelNameSpace ) + fReaderOptions.fFlatNamespace = true; + + // check -stack_addr + if ( fStackAddr != 0 ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + case CPU_TYPE_ARM: + if ( fStackAddr > 0xFFFFFFFF ) + throw "-stack_addr must be < 4G for 32-bit processes"; + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + break; + } + if ( (fStackAddr & -4096) != fStackAddr ) + throw "-stack_addr must be multiples of 4K"; + if ( fStackSize == 0 ) + throw "-stack_addr must be used with -stack_size"; + } + + // check -stack_size + if ( fStackSize != 0 ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackAddr == 0 ) { + fStackAddr = 0xC0000000; + } + if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) + warning("custom stack placement overlaps and will disable shared region"); + break; + case CPU_TYPE_ARM: + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackAddr == 0 ) + fStackAddr = 0x30000000; + if ( fStackAddr > 0x40000000) + throw "-stack_addr must be < 1G for arm"; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + if ( fStackAddr == 0 ) { + fStackAddr = 0x00007FFF5C000000LL; + } + break; + } + if ( (fStackSize & -4096) != fStackSize ) + throw "-stack_size must be multiples of 4K"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // custom stack size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-stack_size option can only be used when linking a main executable"; + } + } + + // check that -allow_stack_execute is only used with main executables + if ( fExecutableStack ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // -allow_stack_execute size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + throw "-allow_stack_execute option can only be used when linking a main executable"; + } + } + + // check -client_name is only used when making a bundle or main executable + if ( fClientName != NULL ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicBundle: + break; + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kObjectFile: + case Options::kDyld: + throw "-client_name can only be used with -bundle"; + } + } + + // check -init is only used when building a dylib + if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) + throw "-init can only be used with -dynamiclib"; + + // check -bundle_loader only used with -bundle + if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) ) + throw "-bundle_loader can only be used with -bundle"; + + // check -dtrace not used with -r + if ( (fDtraceScriptName != NULL) && (fOutputKind == Options::kObjectFile) ) + throw "-dtrace can only be used when creating final linked images"; + + // check -d can only be used with -r + if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) + throw "-d can only be used with -r"; + + // check that -root_safe is not used with -r + if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) ) + throw "-root_safe cannot be used with -r"; + + // check that -setuid_safe is not used with -r + if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) ) + throw "-setuid_safe cannot be used with -r"; + + // make sure all required exported symbols exist + std::vector impliedExports; + for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) { + const char* name = *it; + // never export .eh symbols + const int len = strlen(name); + if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) + warning("ignoring %s in export list", name); + else + fInitialUndefines.push_back(name); + if ( strncmp(name, ".objc_class_name_", 17) == 0 ) { + // rdar://problem/4718189 map ObjC class names to new runtime names + switch (fArchitecture) { + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: + char* temp; + asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + break; + } + } + } + for (std::vector::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) { + const char* name = *it; + fExportSymbols.insert(name); + fInitialUndefines.push_back(name); + } + + // make sure that -init symbol exist + if ( fInitFunctionName != NULL ) + fInitialUndefines.push_back(fInitFunctionName); + + // check custom segments + if ( fCustomSegmentAddresses.size() != 0 ) { + // verify no segment is in zero page + if ( fZeroPageSize != ULLONG_MAX ) { + for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( (it->address >= 0) && (it->address < fZeroPageSize) ) + throwf("-segaddr %s 0x%X conflicts with -pagezero_size", it->name, it->address); + } + } + // verify no duplicates + for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + for (std::vector::iterator it2 = fCustomSegmentAddresses.begin(); it2 != fCustomSegmentAddresses.end(); ++it2) { + if ( (it->address == it2->address) && (it != it2) ) + throwf("duplicate -segaddr addresses for %s and %s", it->name, it2->name); + } + // a custom segment address of zero will disable the use of a zero page + if ( it->address == 0 ) + fZeroPageSize = 0; + } + } + + if ( fZeroPageSize == ULLONG_MAX ) { + // zero page size not specified on command line, set default + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: + case CPU_TYPE_ARM: + // first 4KB for 32-bit architectures + fZeroPageSize = 0x1000; + break; + case CPU_TYPE_POWERPC64: + // first 4GB for ppc64 on 10.5 + if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + fZeroPageSize = 0x100000000ULL; + else + fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page + break; + case CPU_TYPE_X86_64: + // first 4GB for x86_64 on all OS's + fZeroPageSize = 0x100000000ULL; + break; + default: + // if -arch not used, default to 4K zero-page + fZeroPageSize = 0x1000; + } + } + else { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + // -pagezero_size size only legal when building main executable + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + if ( fZeroPageSize != 0 ) + throw "-pagezero_size option can only be used when linking a main executable"; + } + } + + // -dead_strip and -r are incompatible + if ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) ) + throw "-r and -dead_strip cannot be used together"; + + // can't use -rpath unless targeting 10.5 or later + if ( fRPaths.size() > 0 ) { + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) + throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + throw "-rpath can only be used when creating a dynamic final linked image"; + } + } + + // check -pie is only used when building a dynamic main executable for 10.5 + if ( fPositionIndependentExecutable ) { + if ( fOutputKind != Options::kDynamicExecutable ) + throw "-pie can only be used when linking a main executable"; + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) + throw "-pie can only be used when targeting Mac OS X 10.5 or later"; + } +} + + + +void Options::checkForClassic(int argc, const char* argv[]) +{ + // scan options + bool archFound = false; + bool staticFound = false; + bool dtraceFound = false; + bool rFound = false; + bool creatingMachKernel = false; + bool newLinker = false; + + for(int i=0; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-arch") == 0 ) { + parseArch(argv[++i]); + archFound = true; + } + else if ( strcmp(arg, "-static") == 0 ) { + staticFound = true; + } + else if ( strcmp(arg, "-dtrace") == 0 ) { + dtraceFound = true; + } + else if ( strcmp(arg, "-r") == 0 ) { + rFound = true; + } + else if ( strcmp(arg, "-new_linker") == 0 ) { + newLinker = true; + } + else if ( strcmp(arg, "-classic_linker") == 0 ) { + // ld_classic does not understand this option, so remove it + for(int j=i; j < argc; ++j) + argv[j] = argv[j+1]; + this->gotoClassicLinker(argc-1, argv); + } + else if ( strcmp(arg, "-o") == 0 ) { + const char* outfile = argv[++i]; + if ( (outfile != NULL) && (strstr(outfile, "/mach_kernel") != NULL) ) + creatingMachKernel = true; + } + } + } + + // -dtrace only supported by new linker + if( dtraceFound ) + return; + + if( archFound ) { + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + case CPU_TYPE_ARM: +// if ( staticFound && (rFound || !creatingMachKernel) ) { + if ( staticFound && !newLinker ) { + // this environment variable will disable use of ld_classic for -static links + if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { + // ld_classic does not support -aspen_version_min, so change + for(int j=0; j < argc; ++j) { + if ( (strcmp(argv[j], "-aspen_version_min") == 0) + || (strcmp(argv[j], "-iphone_version_min") == 0) + || (strcmp(argv[j], "-iphoneos_version_min") == 0) ) { + argv[j] = "-macosx_version_min"; + if ( j < argc-1 ) + argv[j+1] = "10.5"; + break; + } + } + this->gotoClassicLinker(argc, argv); + } + } + break; + } + } + else { + // work around for VSPTool + if ( staticFound ) + this->gotoClassicLinker(argc, argv); + } + +} + +void Options::gotoClassicLinker(int argc, const char* argv[]) +{ + argv[0] = "ld_classic"; + execvp(argv[0], (char**)argv); + fprintf(stderr, "can't exec ld_classic\n"); + exit(1); +} diff --git a/ld64/src/Options.h b/ld64/src/Options.h new file mode 100644 index 0000000..4bbcffe --- /dev/null +++ b/ld64/src/Options.h @@ -0,0 +1,368 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OPTIONS__ +#define __OPTIONS__ + + +#include +#include + +#include +#include + +#include "ObjectFile.h" + +extern void throwf (const char* format, ...) __attribute__ ((noreturn)); +extern void warning(const char* format, ...); + +class DynamicLibraryOptions +{ +public: + DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false) {} + + bool fWeakImport; + bool fReExport; + bool fBundleLoader; + bool fLazyLoad; +}; + +// +// The public interface to the Options class is the abstract representation of what work the linker +// should do. +// +// This abstraction layer will make it easier to support a future where the linker is a shared library +// invoked directly from Xcode. The target settings in Xcode would be used to directly construct an Options +// object (without building a command line which is then parsed). +// +// +class Options +{ +public: + Options(int argc, const char* argv[]); + ~Options(); + + enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld }; + enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace }; + // Standard treatment for many options. + enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid }; + enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup }; + enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, + kWeakReferenceMismatchNonWeak }; + enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; + enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; + enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; + enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; + + struct FileInfo { + const char* path; + uint64_t fileLen; + time_t modTime; + DynamicLibraryOptions options; + }; + + struct ExtraSection { + const char* segmentName; + const char* sectionName; + const char* path; + const uint8_t* data; + uint64_t dataLen; + }; + + struct SectionAlignment { + const char* segmentName; + const char* sectionName; + uint8_t alignment; + }; + + struct OrderedSymbol { + const char* symbolName; + const char* objectFileName; + }; + + struct SegmentStart { + const char* name; + uint64_t address; + }; + + struct SegmentProtect { + const char* name; + uint32_t max; + uint32_t init; + }; + + struct DylibOverride { + const char* installName; + const char* useInstead; + }; + + + const ObjectFile::ReaderOptions& readerOptions(); + const char* getOutputFilePath(); + std::vector& getInputFiles(); + + cpu_type_t architecture() { return fArchitecture; } + bool preferSubArchitecture() { return fHasPreferredSubType; } + cpu_subtype_t subArchitecture() { return fSubArchitecture; } + OutputKind outputKind(); + bool prebind(); + bool bindAtLoad(); + bool fullyLoadArchives(); + NameSpace nameSpace(); + const char* installPath(); // only for kDynamicLibrary + uint32_t currentVersion(); // only for kDynamicLibrary + uint32_t compatibilityVersion(); // only for kDynamicLibrary + const char* entryName(); // only for kDynamicExecutable or kStaticExecutable + const char* executablePath(); + uint64_t baseAddress(); + bool keepPrivateExterns(); // only for kObjectFile + bool needsModuleTable(); // only for kDynamicLibrary + bool interposable(const char* name); + bool hasExportRestrictList(); + bool hasExportMaskList(); + bool hasWildCardExportRestrictList(); + bool allGlobalsAreDeadStripRoots(); + bool shouldExport(const char*); + bool ignoreOtherArchInputFiles(); + bool forceCpuSubtypeAll(); + bool traceDylibs(); + bool traceArchives(); + DeadStripMode deadStrip(); + UndefinedTreatment undefinedTreatment(); + ObjectFile::ReaderOptions::VersionMin macosxVersionMin(); + bool messagesPrefixedWithArchitecture(); + Treatment picTreatment(); + WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); + const char* umbrellaName(); + std::vector& allowableClients(); + const char* clientName(); + const char* initFunctionName(); // only for kDynamicLibrary + const char* dotOutputFile(); + uint64_t zeroPageSize(); + bool hasCustomStack(); + uint64_t customStackSize(); + uint64_t customStackAddr(); + bool hasExecutableStack(); + std::vector& initialUndefines(); + bool printWhyLive(const char* name); + uint32_t minimumHeaderPad(); + bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; } + std::vector& extraSections(); + std::vector& sectionAlignments(); + CommonsMode commonsMode(); + bool warnCommons(); + bool keepRelocations(); + FileInfo findFile(const char* path); + UUIDMode getUUIDMode() { return fUUIDMode; } + bool warnStabs(); + bool pauseAtEnd() { return fPause; } + bool printStatistics() { return fStatistics; } + bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } + void gotoClassicLinker(int argc, const char* argv[]); + bool sharedRegionEligible() { return fSharedRegionEligible; } + bool printOrderFileStatistics() { return fPrintOrderFileStatistics; } + const char* dTraceScriptName() { return fDtraceScriptName; } + bool dTrace() { return (fDtraceScriptName != NULL); } + std::vector& orderedSymbols() { return fOrderedSymbols; } + bool splitSeg() { return fSplitSegs; } + uint64_t baseWritableAddress() { return fBaseWritableAddress; } + std::vector& customSegmentAddresses() { return fCustomSegmentAddresses; } + std::vector& customSegmentProtections() { return fCustomSegmentProtections; } + bool saveTempFiles() { return fSaveTempFiles; } + const std::vector& rpaths() { return fRPaths; } + bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; } + bool slowx86Stubs() { return fReaderOptions.fSlowx86Stubs; } + std::vector& dylibOverrides() { return fDylibOverrides; } + const char* generatedMapPath() { return fMapPath; } + bool positionIndependentExecutable() { return fPositionIndependentExecutable; } + Options::FileInfo findFileUsingPaths(const char* path); + bool deadStripDylibs() { return fDeadStripDylibs; } + bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } + bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); } + LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; } + bool keepLocalSymbol(const char* symbolName); + bool allowTextRelocs() { return fAllowTextRelocs; } + bool warnAboutTextRelocs() { return fWarnTextRelocs; } + bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; } + bool verbose() { return fVerbose; } + bool makeEncryptable() { return fEncryptable; } + std::vector& llvmOptions() { return fLLVMOptions; } + +private: + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; + enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; + enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome }; + + class SetWithWildcards { + public: + void insert(const char*); + bool contains(const char*); + bool hasWildCards() { return !fWildCard.empty(); } + NameSet::iterator regularBegin() { return fRegular.begin(); } + NameSet::iterator regularEnd() { return fRegular.end(); } + private: + static bool hasWildCards(const char*); + bool wildCardMatch(const char* pattern, const char* candidate); + bool inCharRange(const char*& range, unsigned char c); + + NameSet fRegular; + std::vector fWildCard; + }; + + + void parse(int argc, const char* argv[]); + void checkIllegalOptionCombinations(); + void buildSearchPaths(int argc, const char* argv[]); + void parseArch(const char* architecture); + FileInfo findLibrary(const char* rootName, bool dylibsOnly=false); + FileInfo findFramework(const char* frameworkName); + FileInfo findFramework(const char* rootName, const char* suffix); + bool checkForFile(const char* format, const char* dir, const char* rootName, + FileInfo& result); + uint32_t parseVersionNumber(const char*); + void parseSectionOrderFile(const char* segment, const char* section, const char* path); + void parseOrderFile(const char* path, bool cstring); + void addSection(const char* segment, const char* section, const char* path); + void addSubLibrary(const char* name); + void loadFileList(const char* fileOfPaths); + uint64_t parseAddress(const char* addr); + void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set); + void parseAliasFile(const char* fileOfAliases); + void parsePreCommandLineEnvironmentSettings(); + void parsePostCommandLineEnvironmentSettings(); + void setUndefinedTreatment(const char* treatment); + void setMacOSXVersionMin(const char* version); + void setIPhoneVersionMin(const char* version); + void setWeakReferenceMismatchTreatment(const char* treatment); + void addDylibOverride(const char* paths); + void addSectionAlignment(const char* segment, const char* section, const char* alignment); + CommonsMode parseCommonsTreatment(const char* mode); + Treatment parseTreatment(const char* treatment); + void reconfigureDefaults(); + void checkForClassic(int argc, const char* argv[]); + void parseSegAddrTable(const char* segAddrPath, const char* installPath); + void addLibrary(const FileInfo& info); + void warnObsolete(const char* arg); + uint32_t parseProtection(const char* prot); + + + ObjectFile::ReaderOptions fReaderOptions; + const char* fOutputFile; + std::vector fInputFiles; + cpu_type_t fArchitecture; + cpu_subtype_t fSubArchitecture; + OutputKind fOutputKind; + bool fHasPreferredSubType; + bool fPrebind; + bool fBindAtLoad; + bool fKeepPrivateExterns; + bool fNeedsModuleTable; + bool fIgnoreOtherArchFiles; + bool fForceSubtypeAll; + InterposeMode fInterposeMode; + DeadStripMode fDeadStrip; + NameSpace fNameSpace; + uint32_t fDylibCompatVersion; + uint32_t fDylibCurrentVersion; + const char* fDylibInstallName; + const char* fFinalName; + const char* fEntryName; + uint64_t fBaseAddress; + uint64_t fBaseWritableAddress; + bool fSplitSegs; + SetWithWildcards fExportSymbols; + SetWithWildcards fDontExportSymbols; + SetWithWildcards fInterposeList; + ExportMode fExportMode; + LibrarySearchMode fLibrarySearchMode; + UndefinedTreatment fUndefinedTreatment; + bool fMessagesPrefixedWithArchitecture; + WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment; + std::vector fSubUmbellas; + std::vector fSubLibraries; + std::vector fAllowableClients; + std::vector fRPaths; + const char* fClientName; + const char* fUmbrellaName; + const char* fInitFunctionName; + const char* fDotOutputFile; + const char* fExecutablePath; + const char* fBundleLoader; + const char* fDtraceScriptName; + const char* fSegAddrTablePath; + const char* fMapPath; + uint64_t fZeroPageSize; + uint64_t fStackSize; + uint64_t fStackAddr; + bool fExecutableStack; + uint32_t fMinimumHeaderPad; + CommonsMode fCommonsMode; + UUIDMode fUUIDMode; + SetWithWildcards fLocalSymbolsIncluded; + SetWithWildcards fLocalSymbolsExcluded; + LocalSymbolHandling fLocalSymbolHandling; + bool fWarnCommons; + bool fVerbose; + bool fKeepRelocations; + bool fWarnStabs; + bool fTraceDylibSearching; + bool fPause; + bool fStatistics; + bool fPrintOptions; + bool fSharedRegionEligible; + bool fPrintOrderFileStatistics; + bool fReadOnlyx86Stubs; + bool fPositionIndependentExecutable; + bool fMaxMinimumHeaderPad; + bool fDeadStripDylibs; + bool fAllowTextRelocs; + bool fWarnTextRelocs; + bool fUsingLazyDylibLinking; + bool fEncryptable; + std::vector fInitialUndefines; + NameSet fAllowedUndefined; + NameSet fWhyLive; + std::vector fExtraSections; + std::vector fSectionAlignments; + std::vector fOrderedSymbols; + std::vector fCustomSegmentAddresses; + std::vector fCustomSegmentProtections; + std::vector fDylibOverrides; + std::vector fLLVMOptions; + std::vector fLibrarySearchPaths; + std::vector fFrameworkSearchPaths; + std::vector fSDKPaths; + bool fSaveTempFiles; +}; + + + +#endif // __OPTIONS__ diff --git a/ld64/src/SectCreate.h b/ld64/src/SectCreate.h new file mode 100644 index 0000000..d5c7f4e --- /dev/null +++ b/ld64/src/SectCreate.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __SECTCREATE__ +#define __SECTCREATE__ + + +#include "ObjectFile.h" + +namespace SectCreate { + + extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); + +}; + + +#endif + + + + diff --git a/ld64/src/debugline.c b/ld64/src/debugline.c new file mode 100644 index 0000000..ff0e1d9 --- /dev/null +++ b/ld64/src/debugline.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef KLD +#include +#include +#include +#include +#include "dwarf2.h" +#include "debugline.h" + +struct line_reader_data +{ + bool little_endian; + + /* From the line number information header. */ + uint8_t minimum_instruction_length; + int8_t line_base; + uint8_t line_range; + uint8_t opcode_base; + const uint8_t * standard_opcode_lengths; + size_t numdir; + const uint8_t * * dirnames; + size_t numfile_orig; + size_t numfile; /* As updated during execution of the table. */ + const uint8_t * * filenames; + + /* Current position in the line table. */ + const uint8_t * cpos; + /* End of this part of the line table. */ + const uint8_t * end; + /* Start of the line table. */ + const uint8_t * init; + + struct line_info cur; +}; + +/* Read in a word of fixed size, which may be unaligned, in the + appropriate endianness. */ +#define read_16(p) (lnd->little_endian \ + ? ((p)[1] << 8 | (p)[0]) \ + : ((p)[0] << 8 | (p)[1])) +#define read_32(p) (lnd->little_endian \ + ? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \ + : ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])) +#define read_64(p) (lnd->little_endian \ + ? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \ + | (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \ + | (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \ + | (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \ + : ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \ + | (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \ + | (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \ + | (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7])) + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (struct line_reader_data * leb) +{ + while (leb->cpos != leb->end && *leb->cpos >= 0x80) + leb->cpos++; + if (leb->cpos != leb->end) + leb->cpos++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (struct line_reader_data * leb) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (leb->cpos == leb->end) + return (uint64_t) -1; + + b = *leb->cpos & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*leb->cpos++ >= 0x80); + return result; +} + + +/* Read a SLEB128 into a 64-bit word. Return 0 on overflow or error + (which is not very helpful). On overflow, skip past the rest of + the SLEB128. For negative numbers, this actually overflows when + under -2^62, but since this is used for line numbers that ought to + be OK... */ +static int64_t +read_sleb128 (struct line_reader_data * leb) +{ + const uint8_t * start_pos = leb->cpos; + uint64_t v = read_uleb128 (leb); + uint64_t signbit; + + if (v >= 1ull << 63) + return 0; + if (leb->cpos - start_pos > 9) + return v; + + signbit = 1ull << ((leb->cpos - start_pos) * 7 - 1); + + return v | -(v & signbit); +} + +/* Free a line_reader_data structure. */ +void +line_free (struct line_reader_data * lnd) +{ + if (! lnd) + return; + if (lnd->dirnames) + free (lnd->dirnames); + if (lnd->filenames) + free (lnd->filenames); + free (lnd); +} + +/* Return the pathname of the file in S, or NULL on error. + The result will have been allocated with malloc. */ + +char * +line_file (struct line_reader_data *lnd, uint64_t n) +{ + const uint8_t * prev_pos = lnd->cpos; + size_t filelen, dirlen; + uint64_t dir; + char * result; + + /* I'm not sure if this is actually an error. */ + if (n == 0 + || n > lnd->numfile) + return NULL; + + filelen = strlen ((const char *)lnd->filenames[n - 1]); + lnd->cpos = lnd->filenames[n - 1] + filelen + 1; + dir = read_uleb128 (lnd); + lnd->cpos = prev_pos; + if (dir == 0 + || lnd->filenames[n - 1][0] == '/') + return strdup ((const char *)lnd->filenames[n - 1]); + else if (dir > lnd->numdir) + return NULL; + + dirlen = strlen ((const char *) lnd->dirnames[dir - 1]); + result = malloc (dirlen + filelen + 2); + memcpy (result, lnd->dirnames[dir - 1], dirlen); + result[dirlen] = '/'; + memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen); + result[dirlen + 1 + filelen] = '\0'; + return result; +} + +/* Initialize a state S. Return FALSE on error. */ + +static void +init_state (struct line_info *s) +{ + s->file = 1; + s->line = 1; + s->col = 0; + s->pc = 0; + s->end_of_sequence = false; +} + +/* Read a debug_line section. */ + +struct line_reader_data * +line_open (const uint8_t * debug_line, size_t debug_line_size, + int little_endian) +{ + struct line_reader_data * lnd = NULL; + bool dwarf_size_64; + + uint64_t lnd_length, header_length; + const uint8_t * table_start; + + if (debug_line_size < 12) + return NULL; + + lnd = malloc (sizeof (struct line_reader_data)); + if (! lnd) + goto error; + + lnd->little_endian = little_endian; + lnd->cpos = debug_line; + + lnd_length = read_32 (lnd->cpos); + lnd->cpos += 4; + if (lnd_length == 0xffffffff) + { + lnd_length = read_64 (lnd->cpos); + lnd->cpos += 8; + dwarf_size_64 = true; + } + else if (lnd_length > 0xfffffff0) + /* Not a format we understand. */ + goto error; + else + dwarf_size_64 = false; + + if (debug_line_size < lnd_length + (dwarf_size_64 ? 12 : 4) + || lnd_length < (dwarf_size_64 ? 15 : 11)) + /* Too small. */ + goto error; + + if (read_16 (lnd->cpos) != 2) + /* Unknown line number format. */ + goto error; + lnd->cpos += 2; + + header_length = dwarf_size_64 ? (uint64_t)read_64(lnd->cpos) : (uint64_t)read_32(lnd->cpos); + lnd->cpos += dwarf_size_64 ? 8 : 4; + if (lnd_length < header_length + (lnd->cpos - debug_line) + || header_length < 7) + goto error; + + lnd->minimum_instruction_length = lnd->cpos[0]; + /* Ignore default_is_stmt. */ + lnd->line_base = lnd->cpos[2]; + lnd->line_range = lnd->cpos[3]; + lnd->opcode_base = lnd->cpos[4]; + + if (lnd->opcode_base == 0) + /* Every valid line number program must use at least opcode 0 + for DW_LNE_end_sequence. */ + goto error; + + lnd->standard_opcode_lengths = lnd->cpos + 5; + if (header_length < (uint64_t)(5 + (lnd->opcode_base - 1))) + /* Header not long enough. */ + goto error; + lnd->cpos += 5 + lnd->opcode_base - 1; + lnd->end = debug_line + header_length + (dwarf_size_64 ? 22 : 10); + + /* Make table of offsets to directory names. */ + table_start = lnd->cpos; + lnd->numdir = 0; + while (lnd->cpos != lnd->end && *lnd->cpos) + { + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); + if (! lnd->cpos) + goto error; + lnd->cpos++; + lnd->numdir++; + } + if (lnd->cpos == lnd->end) + goto error; + lnd->dirnames = malloc (lnd->numdir * sizeof (const uint8_t *)); + if (! lnd->dirnames) + goto error; + lnd->numdir = 0; + lnd->cpos = table_start; + while (*lnd->cpos) + { + lnd->dirnames[lnd->numdir++] = lnd->cpos; + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; + } + lnd->cpos++; + + /* Make table of offsets to file entries. */ + table_start = lnd->cpos; + lnd->numfile = 0; + while (lnd->cpos != lnd->end && *lnd->cpos) + { + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); + if (! lnd->cpos) + goto error; + lnd->cpos++; + skip_leb128 (lnd); + skip_leb128 (lnd); + skip_leb128 (lnd); + lnd->numfile++; + } + if (lnd->cpos == lnd->end) + goto error; + lnd->filenames = malloc (lnd->numfile * sizeof (const uint8_t *)); + if (! lnd->filenames) + goto error; + lnd->numfile = 0; + lnd->cpos = table_start; + while (*lnd->cpos) + { + lnd->filenames[lnd->numfile++] = lnd->cpos; + lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; + skip_leb128 (lnd); + skip_leb128 (lnd); + skip_leb128 (lnd); + } + lnd->cpos++; + + lnd->numfile_orig = lnd->numfile; + lnd->cpos = lnd->init = lnd->end; + lnd->end = debug_line + lnd_length + (dwarf_size_64 ? 12 : 4); + + init_state (&lnd->cur); + + return lnd; + + error: + line_free (lnd); + return NULL; +} + +/* Reset back to the beginning. */ +void +line_reset (struct line_reader_data * lnd) +{ + lnd->cpos = lnd->init; + lnd->numfile = lnd->numfile_orig; + init_state (&lnd->cur); +} + +/* Is there no more line data available? */ +int +line_at_eof (struct line_reader_data * lnd) +{ + return lnd->cpos == lnd->end; +} + +static bool +next_state (struct line_reader_data *lnd) +{ + if (lnd->cur.end_of_sequence) + init_state (&lnd->cur); + + for (;;) + { + uint8_t op; + uint64_t tmp; + + if (lnd->cpos == lnd->end) + return false; + op = *lnd->cpos++; + if (op >= lnd->opcode_base) + { + op -= lnd->opcode_base; + + lnd->cur.line += op % lnd->line_range + lnd->line_base; + lnd->cur.pc += (op / lnd->line_range + * lnd->minimum_instruction_length); + return true; + } + else switch (op) + { + case DW_LNS_extended_op: + { + uint64_t sz = read_uleb128 (lnd); + const uint8_t * op = lnd->cpos; + + if ((uint64_t)(lnd->end - op) < sz || sz == 0) + return false; + lnd->cpos += sz; + switch (*op++) + { + case DW_LNE_end_sequence: + lnd->cur.end_of_sequence = true; + return true; + + case DW_LNE_set_address: + if (sz == 9) + lnd->cur.pc = read_64 (op); + else if (sz == 5) + lnd->cur.pc = read_32 (op); + else + return false; + break; + + case DW_LNE_define_file: + { + const uint8_t * * filenames; + filenames = realloc + (lnd->filenames, + (lnd->numfile + 1) * sizeof (const uint8_t *)); + if (! filenames) + return false; + /* Check for zero-termination. */ + if (! memchr (op, 0, lnd->cpos - op)) + return false; + filenames[lnd->numfile++] = op; + lnd->filenames = filenames; + + /* There's other data here, like file sizes and modification + times, but we don't need to read it so skip it. */ + } + break; + + default: + /* Don't understand it, so skip it. */ + break; + } + break; + } + + case DW_LNS_copy: + //fprintf(stderr, "DW_LNS_copy\n"); + return true; + case DW_LNS_advance_pc: + //fprintf(stderr, "DW_LNS_advance_pc\n"); + tmp = read_uleb128 (lnd); + if (tmp == (uint64_t) -1) + return false; + lnd->cur.pc += tmp * lnd->minimum_instruction_length; + break; + case DW_LNS_advance_line: + //fprintf(stderr, "DW_LNS_advance_line\n"); + lnd->cur.line += read_sleb128 (lnd); + break; + case DW_LNS_set_file: + //fprintf(stderr, "DW_LNS_set_file\n"); + lnd->cur.file = read_uleb128 (lnd); + break; + case DW_LNS_set_column: + //fprintf(stderr, "DW_LNS_set_column\n"); + lnd->cur.col = read_uleb128 (lnd); + break; + case DW_LNS_const_add_pc: + //fprintf(stderr, "DW_LNS_const_add_pc\n"); + lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range + * lnd->minimum_instruction_length); + break; + case DW_LNS_fixed_advance_pc: + //fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); + if (lnd->end - lnd->cpos < 2) + return false; + lnd->cur.pc += read_16 (lnd->cpos); + lnd->cpos += 2; + break; + default: + { + /* Don't know what it is, so skip it. */ + int i; + for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++) + skip_leb128 (lnd); + break; + } + } + } +} + + +/* Set RESULT to the next 'interesting' line state, as indicated + by STOP, or return FALSE on error. The final (end-of-sequence) + line state is always considered interesting. */ +int +line_next (struct line_reader_data * lnd, + struct line_info * result, + enum line_stop_constants stop) +{ + for (;;) + { + struct line_info prev = lnd->cur; + + if (! next_state (lnd)) + return false; + + if (lnd->cur.end_of_sequence) + break; + if (stop == line_stop_always) + break; + if ((stop & line_stop_pc) && lnd->cur.pc != prev.pc) + break; + if ((stop & line_stop_pos_mask) && lnd->cur.file != prev.file) + break; + if ((stop & line_stop_pos_mask) >= line_stop_line + && lnd->cur.line != prev.line) + break; + if ((stop & line_stop_pos_mask) >= line_stop_col + && lnd->cur.col != prev.col) + break; + } + *result = lnd->cur; + return true; +} + +/* Find the region (START->pc through END->pc) in the debug_line + information which contains PC. This routine starts searching at + the current position (which is returned as END), and will go all + the way around the debug_line information. It will return false if + an error occurs or if there is no matching region; these may be + distinguished by looking at START->end_of_sequence, which will be + false on error and true if there was no matching region. + You could write this routine using line_next, but this version + will be slightly more efficient, and of course more convenient. */ + +int +line_find_addr (struct line_reader_data * lnd, + struct line_info * start, + struct line_info * end, + uint64_t pc) +{ + const uint8_t * startpos; + struct line_info prev; + + if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) + line_reset (lnd); + + startpos = lnd->cpos; + + do { + prev = lnd->cur; + if (! next_state (lnd)) + { + start->end_of_sequence = false; + return false; + } + if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) + line_reset (lnd); + if (lnd->cpos == startpos) + { + start->end_of_sequence = true; + return false; + } + } while (lnd->cur.pc <= pc || prev.pc > pc || prev.end_of_sequence); + *start = prev; + *end = lnd->cur; + return true; +} +#endif /* ! KLD */ + diff --git a/ld64/src/debugline.h b/ld64/src/debugline.h new file mode 100644 index 0000000..51d585e --- /dev/null +++ b/ld64/src/debugline.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Information about a line. + DIRECTORY is to be ignored if FILENAME is absolute. + PC will be relative to the file the debug_line section is in. */ +struct line_info +{ + uint64_t file; + int64_t line; + uint64_t col; + uint64_t pc; + int end_of_sequence; +}; + +/* Opaque status structure for the line readers. */ +struct line_reader_data; + +/* Create a line_reader_data, given address and size of the debug_line section. + SIZE may be (size_t)-1 if unknown, although this suppresses checking + for an incorrectly large size in the debug_line section. + LITTLE_ENDIAN is set if the debug_line section is for a little-endian + machine. + Returns NULL on error. */ +struct line_reader_data * line_open (const uint8_t * debug_line, + size_t debug_line_size, + int little_endian); + +/* The STOP parameter to line_next is one of line_stop_{file,line,col}, + perhaps ORed with line_stop_pc; or line_stop_atend, or line_stop_always. */ +enum line_stop_constants { + line_stop_atend = 0, /* Stop only at the end of a sequence. */ + line_stop_file = 1, /* Stop if DIRECTORY or FILENAME change. */ + line_stop_line = 2, /* Stop if LINE, DIRECTORY, or FILENAME change. */ + line_stop_col = 3, /* Stop if COL, LINE, DIRECTORY, or FILENAME change. */ + line_stop_pos_mask = 3, + line_stop_pc = 4, /* Stop if PC changes. */ + line_stop_always = 8 /* Stop always. */ +}; + +/* Either return FALSE on an error, in which case the line_reader_data + may be invalid and should be passed immediately to line_free; or + fill RESULT with the first 'interesting' line, as determined by STOP. + The last line data in a sequence is always considered 'interesting'. */ +int line_next (struct line_reader_data * lnd, + struct line_info * result, + enum line_stop_constants stop); + +/* Find the region (START->pc through END->pc) in the debug_line + information which contains PC. This routine starts searching at + the current position (which is returned as END), and will go all + the way around the debug_line information. It will return false if + an error occurs or if there is no matching region; these may be + distinguished by looking at START->end_of_sequence, which will be + false on error and true if there was no matching region. + You could write this routine using line_next, but this version + will be slightly more efficient, and of course more convenient. */ + +int line_find_addr (struct line_reader_data * lnd, + struct line_info * start, + struct line_info * end, + uint64_t pc); + +/* Return TRUE if there is more line data to be fetched. + If line_next has not been called or it has been called but did not + set END_OF_SEQUENCE, you can assume there is more line data, + but it's safe to call this routine anyway. */ +int line_at_eof (struct line_reader_data * lnd); + +/* Return the pathname of the file in S, or NULL on error. + The result will have been allocated with malloc. */ +char * line_file (struct line_reader_data *lnd, uint64_t file); + +/* Reset the line_reader_data: go back to the beginning. */ +void line_reset (struct line_reader_data * lnd); + +/* Free a line_reader_data structure. */ +void line_free (struct line_reader_data * lnd); + +#ifdef __cplusplus +} +#endif + diff --git a/ld64/src/dwarf2.h b/ld64/src/dwarf2.h new file mode 100644 index 0000000..530b465 --- /dev/null +++ b/ld64/src/dwarf2.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. */ + +/* This is not a complete list. */ +enum { + DW_TAG_compile_unit = 17, + DW_TAG_partial_unit = 60 +}; + +/* This is not a complete list. */ +enum { + DW_AT_sibling = 1, + DW_AT_name = 3, + DW_AT_stmt_list = 16, + DW_AT_comp_dir = 27 +}; + +enum { + DW_FORM_addr = 1, + DW_FORM_block2 = 3, + DW_FORM_block4, + DW_FORM_data2, + DW_FORM_data4, + DW_FORM_data8, + DW_FORM_string, + DW_FORM_block, + DW_FORM_block1, + DW_FORM_data1, + DW_FORM_flag, + DW_FORM_sdata, + DW_FORM_strp, + DW_FORM_udata, + DW_FORM_ref_addr, + DW_FORM_ref1, + DW_FORM_ref2, + DW_FORM_ref4, + DW_FORM_ref8, + DW_FORM_ref_udata, + DW_FORM_indirect /* 22 */ +}; + +enum { + DW_LNS_extended_op = 0, + DW_LNS_copy, + DW_LNS_advance_pc, + DW_LNS_advance_line, + DW_LNS_set_file, + DW_LNS_set_column, + DW_LNS_negate_stmt, + DW_LNS_set_basic_block, + DW_LNS_const_add_pc, + DW_LNS_fixed_advance_pc, + DW_LNS_set_prologue_end, + DW_LNS_set_epilogue_begin, + DW_LNS_set_isa +}; + +enum { + DW_LNE_end_sequence = 1, + DW_LNE_set_address, + DW_LNE_define_file +}; diff --git a/ld64/src/ld.cpp b/ld64/src/ld.cpp new file mode 100644 index 0000000..acd18c8 --- /dev/null +++ b/ld64/src/ld.cpp @@ -0,0 +1,3778 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// start temp HACK for cross builds +extern "C" double log2 ( double ); +#define __MATH__ +// end temp HACK for cross builds + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configure.h" +#include "Options.h" + +#include "ObjectFile.h" + +#include "MachOReaderRelocatable.hpp" +#include "ArchiveReader.hpp" +#include "MachOReaderDylib.hpp" +#include "MachOWriterExecutable.hpp" + + +#if LTO_SUPPORT +#include "LTOReader.hpp" +#endif + + +#include "OpaqueSection.hpp" + + +class CStringComparor +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } +}; + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +class Section : public ObjectFile::Section +{ +public: + static Section* find(const char* sectionName, const char* segmentName, bool zeroFill); + static void assignIndexes(); + const char* getName() { return fSectionName; } +private: + Section(const char* sectionName, const char* segmentName, bool zeroFill); + + struct Sorter { + static int segmentOrdinal(const char* segName); + bool operator()(Section* left, Section* right); + }; + + typedef __gnu_cxx::hash_map, CStringEquals> NameToOrdinal; + typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; + //typedef std::map NameToSection; + + const char* fSectionName; + const char* fSegmentName; + bool fZeroFill; + + static NameToSection fgMapping; + static std::vector fgSections; + static NameToOrdinal fgSegmentDiscoverOrder; +}; + +Section::NameToSection Section::fgMapping; +std::vector Section::fgSections; +Section::NameToOrdinal Section::fgSegmentDiscoverOrder; + +Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) + : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill) +{ + this->fIndex = fgSections.size(); + //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); +} + +Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) +{ + NameToSection::iterator pos = fgMapping.find(sectionName); + if ( pos != fgMapping.end() ) { + if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) + return pos->second; + // otherwise same section name is used in different segments, look slow way + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) + return *it; + } + } + + // does not exist, so make a new one + Section* sect = new Section(sectionName, segmentName, zeroFill); + fgMapping[sectionName] = sect; + fgSections.push_back(sect); + + if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { + // special case __textcoal_nt to be right after __text + find("__textcoal_nt", "__TEXT", false); + } + + // remember segment discovery order + if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) + fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); + + return sect; +} + +int Section::Sorter::segmentOrdinal(const char* segName) +{ + if ( strcmp(segName, "__PAGEZERO") == 0 ) + return 1; + if ( strcmp(segName, "__TEXT") == 0 ) + return 2; + if ( strcmp(segName, "__DATA") == 0 ) + return 3; + if ( strcmp(segName, "__OBJC") == 0 ) + return 4; + if ( strcmp(segName, "__OBJC2") == 0 ) + return 5; + if ( strcmp(segName, "__LINKEDIT") == 0 ) + return INT_MAX; // linkedit segment should always sort last + else + return fgSegmentDiscoverOrder[segName]+6; +} + + +bool Section::Sorter::operator()(Section* left, Section* right) +{ + // Segment is primary sort key + int leftSegOrdinal = segmentOrdinal(left->fSegmentName); + int rightSegOrdinal = segmentOrdinal(right->fSegmentName); + if ( leftSegOrdinal < rightSegOrdinal ) + return true; + if ( leftSegOrdinal > rightSegOrdinal ) + return false; + + // zerofill section sort to the end + if ( !left->fZeroFill && right->fZeroFill ) + return true; + if ( left->fZeroFill && !right->fZeroFill ) + return false; + + // section discovery order is last sort key + return left->fIndex < right->fIndex; +} + +void Section::assignIndexes() +{ + //printf("unsorted sections:\n"); + //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); + //} + + // sort it + std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); + + // assign correct section ordering to each Section object + unsigned int newOrder = 1; + for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) + (*it)->fIndex = newOrder++; + + //printf("sorted sections:\n"); + //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { + // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); + //} +} + +class Linker : public ObjectFile::Reader::DylibHander { +public: + Linker(int argc, const char* argv[]); + + const char* getArchPrefix(); + const char* architectureName(); + bool showArchitectureInErrors(); + bool isInferredArchitecture(); + void createReaders(); + void createWriter(); + void addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& ); + void setOutputFile(ExecutableFile::Writer* writer); + void link(); + void optimize(); + + // implemenation from ObjectFile::Reader::DylibHander + virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath); + +private: + struct WhyLiveBackChain + { + WhyLiveBackChain* previous; + const char* name; + }; + + ObjectFile::Reader* createReader(const Options::FileInfo&); + void addAtom(ObjectFile::Atom& atom); + void addAtoms(std::vector& atoms); + void buildAtomList(); + void processDylibs(); + void markDead(ObjectFile::Atom* atom); + void updateConstraints(ObjectFile::Reader* reader); + void loadAndResolve(); + void processDTrace(); + void checkObjC(); + void loadUndefines(); + void checkUndefines(); + void addWeakAtomOverrides(); + void resolveReferences(); + void deadStripResolve(); + void addLiveRoot(const char* name); + ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); + void logArchive(ObjectFile::Reader* reader); + void sortSections(); + void sortAtoms(); + void tweakLayout(); + void writeDotOutput(); + static bool minimizeStab(ObjectFile::Reader::Stab& stab); + static const char* truncateStabString(const char* str); + void collectDebugInfo(); + void writeOutput(); + ObjectFile::Atom* entryPoint(bool orInit); + ObjectFile::Atom* dyldHelper(); + ObjectFile::Atom* dyldLazyLibraryHelper(); + const char* assureFullPath(const char* path); + void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); + void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); + void synthesizeDebugNotes(std::vector& allAtomsByReader); + void printStatistics(); + void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); + char* commatize(uint64_t in, char* out); + void getVMInfo(vm_statistics_data_t& info); + cpu_type_t inferArchitecture(); + void addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName); + void checkDylibClientRestrictions(ObjectFile::Reader* reader); + void logDylib(ObjectFile::Reader* reader, bool indirect); + + void resolve(ObjectFile::Reference* reference); + void resolveFrom(ObjectFile::Reference* reference); + std::vector* addJustInTimeAtoms(const char* name, bool dylibsOnly=false); + void addJustInTimeAtomsAndMarkLive(const char* name); + + ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); + + void logTraceInfo(const char* format, ...); + + + class SymbolTable + { + public: + typedef __gnu_cxx::hash_map, CStringEquals> Mapper; + + SymbolTable(Linker&); + void require(const char* name); + bool add(ObjectFile::Atom& atom); + ObjectFile::Atom* find(const char* name); + unsigned int getRequireCount() { return fRequireCount; } + void getNeededNames(bool andWeakDefintions, std::vector& undefines); + bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } + bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } + void setHasExternalWeakDefinitions() { fHasExternalWeakDefinitions = true; } + Mapper::iterator begin() { return fTable.begin(); } + Mapper::iterator end() { return fTable.end(); } + + private: + Linker& fOwner; + Mapper fTable; + unsigned int fRequireCount; + bool fHasExternalTentativeDefinitions; + bool fHasExternalWeakDefinitions; + }; + + class AtomSorter + { + public: + AtomSorter(std::map* map) : fOverriddenOrdinalMap(map) {} + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); + private: + std::map* fOverriddenOrdinalMap; + }; + + typedef std::map SectionOrder; + + struct DTraceProbeInfo { + DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} + const ObjectFile::Atom* atom; + uint32_t offset; + const char* probeName; + }; + typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToReader; + + struct IndirectLibrary { + const char* path; + uint64_t fileLen; + ObjectFile::Reader* reader; + std::set parents; + ObjectFile::Reader* reExportedViaDirectLibrary; + }; + + ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib); + + Options fOptions; + SymbolTable fGlobalSymbolTable; + uint32_t fNextInputOrdinal; + std::vector fInputFiles; + ExecutableFile::Writer* fOutputFile; + InstallNameToReader fDylibMap; + std::map fDylibOptionsMap; + std::set fDylibsProcessed; + ObjectFile::Reader* fBundleLoaderReader; + std::vector fReadersThatHaveSuppliedAtoms; + std::vector fAllAtoms; + std::set fArchiveReaders; + std::set fArchiveReadersLogged; + std::set fDeadAtoms; + std::set fLiveAtoms; + std::set fLiveRootAtoms; + std::vector fStabs; + std::vector fAtomsWithUnresolvedReferences; + std::vector fDtraceProbes; + std::vector fDtraceProbeSites; + std::vector fDtraceIsEnabledSites; + std::map fDtraceAtomToTypes; + bool fCreateUUID; + bool fCanScatter; + SectionOrder fSectionOrder; + cpu_type_t fArchitecture; + const char* fArchitectureName; + bool fArchitectureInferred; + bool fDirectLibrariesComplete; + bool fBiggerThanTwoGigOutput; + uint64_t fOutputFileSize; + uint64_t fTotalZeroFillSize; + uint64_t fTotalSize; + uint64_t fStartTime; + uint64_t fStartCreateReadersTime; + uint64_t fStartCreateWriterTime; + uint64_t fStartBuildAtomsTime; + uint64_t fStartLoadAndResolveTime; + uint64_t fStartSortTime; + uint64_t fStartDebugTime; + uint64_t fStartWriteTime; + uint64_t fEndTime; + uint64_t fTotalObjectSize; + uint64_t fTotalArchiveSize; + uint32_t fTotalObjectLoaded; + uint32_t fTotalArchivesLoaded; + uint32_t fTotalDylibsLoaded; + vm_statistics_data_t fStartVMInfo; + ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint; + ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint; + bool fObjcReplacmentClasses; + bool fAllDirectDylibsLoaded; +}; + + +Linker::Linker(int argc, const char* argv[]) + : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), + fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), + fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), + fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), + fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), + fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny), + fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false) +{ + fStartTime = mach_absolute_time(); + if ( fOptions.printStatistics() ) + getVMInfo(fStartVMInfo); + + fArchitecture = fOptions.architecture(); + if ( fArchitecture == 0 ) { + // -arch not specified, scan .o files to figure out what it should be + fArchitecture = inferArchitecture(); + fArchitectureInferred = true; + } + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + fArchitectureName = "ppc"; + break; + case CPU_TYPE_POWERPC64: + fArchitectureName = "ppc64"; + break; + case CPU_TYPE_I386: + fArchitectureName = "i386"; + break; + case CPU_TYPE_X86_64: + fArchitectureName = "x86_64"; + break; + case CPU_TYPE_ARM: + fArchitectureName = "arm"; + break; + default: + fArchitectureName = "unknown architecture"; + break; + } +} + +const char* Linker::architectureName() +{ + return fArchitectureName; +} + +bool Linker::showArchitectureInErrors() +{ + return fOptions.printArchPrefix(); +} + +bool Linker::isInferredArchitecture() +{ + return fArchitectureInferred; +} + +cpu_type_t Linker::inferArchitecture() +{ + // scan all input files, looking for a thin .o file. + // the first one found is presumably the architecture to link + uint8_t buffer[sizeof(mach_header_64)]; + std::vector& files = fOptions.getInputFiles(); + for (std::vector::iterator it = files.begin(); it != files.end(); ++it) { + int fd = ::open(it->path, O_RDONLY, 0); + if ( fd != -1 ) { + ssize_t amount = read(fd, buffer, sizeof(buffer)); + ::close(fd); + if ( amount >= (ssize_t)sizeof(buffer) ) { + if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch ppc based on %s", it->path); + return CPU_TYPE_POWERPC; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch ppc64 based on %s", it->path); + return CPU_TYPE_POWERPC64; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch i386 based on %s", it->path); + return CPU_TYPE_I386; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch x86_64 based on %s", it->path); + return CPU_TYPE_X86_64; + } + else if ( mach_o::relocatable::Reader::validFile(buffer) ) { + //warning("-arch not used, infering -arch arm based on %s", it->path); + return CPU_TYPE_ARM; + } + } + } + } + + // no thin .o files found, so default to same architecture this was built as + warning("-arch not specified"); +#if __ppc__ + return CPU_TYPE_POWERPC; +#elif __i386__ + return CPU_TYPE_I386; +#elif __ppc64__ + return CPU_TYPE_POWERPC64; +#elif __x86_64__ + return CPU_TYPE_X86_64; +#elif __arm__ + return CPU_TYPE_ARM; +#else + #error unknown default architecture +#endif +} + + +void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) +{ + fInputFiles.push_back(reader); + fDylibOptionsMap[reader] = info.options; +} + +void Linker::setOutputFile(ExecutableFile::Writer* writer) +{ + fOutputFile = writer; +} + +class InSet +{ +public: + InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fDeadAtoms.count(atom) != 0 ); + } + +private: + std::set& fDeadAtoms; +}; + +void Linker::loadAndResolve() +{ + fStartLoadAndResolveTime = mach_absolute_time(); + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // without dead-code-stripping: + // find atoms to resolve all undefines + this->loadUndefines(); + // verify nothing is missing + this->checkUndefines(); + // once all undefines fulfill, then bind all references + this->resolveReferences(); + // remove atoms weak atoms that have been overridden + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + } + else { + // with dead code stripping: + // start binding references from roots, + this->deadStripResolve(); + // verify nothing is missing + this->checkUndefines(); + } +} + +void Linker::optimize() +{ + // give each reader a chance to do any optimizations + std::vector newAtoms; + std::vector additionalUndefines; + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, fNextInputOrdinal, fOutputFile, + fOptions.llvmOptions(), + fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(), + fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(), + fOptions.allowTextRelocs()); + } + + // add all newly created atoms to fAllAtoms and update symbol table + this->addAtoms(newAtoms); + + // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could + // not have their section set until now. + for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { + ObjectFile::Atom *atom = *itr; + if ( atom->getSection() == NULL ) + atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); + } + + // resolve new undefines + for(std::vector::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { + const char *targetName = *riter; + //fprintf(stderr, "LTO additional undefine: %s\n", targetName); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL) { + // mark that this symbol is needed + fGlobalSymbolTable.require(targetName); + // try to find it in some library + this->addJustInTimeAtoms(targetName); + } + } + + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + fLiveAtoms.clear(); + this->deadStripResolve(); + } + else { + this->checkUndefines(); + this->resolveReferences(); + } +} + +void Linker::link() +{ + this->buildAtomList(); + this->loadAndResolve(); + this->optimize(); + this->checkObjC(); + this->processDTrace(); + this->tweakLayout(); + this->sortSections(); + this->sortAtoms(); + this->writeDotOutput(); + this->collectDebugInfo(); + this->writeOutput(); + this->printStatistics(); + + if ( fOptions.pauseAtEnd() ) + sleep(10); +} + +void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; + //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); + } + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; + uint32_t milliSeconds = milliSecondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + +char* Linker::commatize(uint64_t in, char* out) +{ + char* result = out; + char rawNum[30]; + sprintf(rawNum, "%llu", in); + const int rawNumLen = strlen(rawNum); + for(int i=0; i < rawNumLen-1; ++i) { + *out++ = rawNum[i]; + if ( ((rawNumLen-i) % 3) == 1 ) + *out++ = ','; + } + *out++ = rawNum[rawNumLen-1]; + *out = '\0'; + return result; +} + +void Linker::getVMInfo(vm_statistics_data_t& info) +{ + mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); + kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count); + if (error != KERN_SUCCESS) { + bzero(&info, sizeof(vm_statistics_data_t)); + } +} + +void Linker::printStatistics() +{ + fEndTime = mach_absolute_time(); + if ( fOptions.printStatistics() ) { + vm_statistics_data_t endVMInfo; + getVMInfo(endVMInfo); + + uint64_t totalTime = fEndTime - fStartTime; + printTime("ld total time", totalTime, totalTime); + printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); + printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); + printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); + printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime); + printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime); + printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); + printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); + printTime(" write output", fEndTime - fStartWriteTime, totalTime); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, + endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); + char temp[40]; + fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp)); + fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp)); + fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded); + fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp)); + } +} + +inline void Linker::addAtom(ObjectFile::Atom& atom) +{ + // add to list of all atoms + fAllAtoms.push_back(&atom); + + if ( fOptions.deadStrip() == Options::kDeadStripOff ) { + // not dead-stripping code, so add atom's references's names to symbol table as to-be-resolved-later + std::vector& references = atom.getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) + fGlobalSymbolTable.require(reference->getTargetName()); + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) + fGlobalSymbolTable.require(reference->getFromTargetName()); + if ( reference->getTargetBinding() == ObjectFile::Reference::kDontBind ) + addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); + } + // update total size info (except for __ZEROPAGE atom) + if ( atom.getSegment().isContentReadable() ) { + fTotalSize += atom.getSize(); + if ( atom.isZeroFill() ) + fTotalZeroFillSize += atom.getSize(); + } + } + else { + if ( atom.dontDeadStrip() ) + fLiveRootAtoms.insert(&atom); + } + + // if in global namespace, add atom itself to symbol table + ObjectFile::Atom::Scope scope = atom.getScope(); + const char* name = atom.getName(); + if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { + // update scope based on export list + if ( fOptions.hasExportRestrictList() ) { + if ( scope == ObjectFile::Atom::scopeGlobal ) { + // check for globals that are downgraded to hidden + bool doExport = fOptions.shouldExport(name); + if ( !doExport ) { + atom.setScope(ObjectFile::Atom::scopeLinkageUnit); + } + } + else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { + // check for hiddens that were requested to be exported + if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { + warning("cannot export hidden symbol %s from %s", name, atom.getFile()->getPath()); + } + } + } + // add to symbol table + if ( fOptions.outputKind() == Options::kObjectFile ) { + // in ld -r mode don't add .eh symbols to symbol table + // instead kGroupSubordinate references will keep them paired + // with their functions. + const char* sectionName = atom.getSectionName(); + if ( (sectionName != NULL) && (strcmp(sectionName, "__eh_frame") != 0) ) + fGlobalSymbolTable.add(atom); + } + else { + fGlobalSymbolTable.add(atom); + } + } + + // record section orders so output file can have same order + if (atom.getSectionName()) + atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); +} + + +void Linker::markDead(ObjectFile::Atom* atom) +{ + fDeadAtoms.insert(atom); + // + // The kGroupSubordinate reference kind is used to model group comdat. + // The "signature" atom in the group has a kGroupSubordinate reference to + // all other members of the group. So, if the signature atom is + // coalesced away, all other atoms in the group should also be removed. + // + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX + ObjectFile::Atom* targetAtom = &(ref->getTarget()); + if ( targetAtom == NULL ) { + warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName()); + } + else { + if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) { + // ok for .eh symbols to be not static in -r mode + if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) ) + warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName()); + } + this->markDead(targetAtom); + } + } + } +} + +void Linker::updateConstraints(ObjectFile::Reader* reader) +{ + // check objc objects were compiled compatibly + ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); + if ( reader->getInstallPath() == NULL ) { + // adding a .o file + switch ( objcAddition ) { + case ObjectFile::Reader::kObjcNone: + break; + case ObjectFile::Reader::kObjcRetainRelease: + if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); + fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease; + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone ) + fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; + break; + case ObjectFile::Reader::kObjcGC: + if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); + fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC; + break; + } + } + if ( reader->objcReplacementClasses() ) + fObjcReplacmentClasses = true; + + // check cpu sub-types for stricter sub-type + fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint); +} + +inline void Linker::addAtoms(std::vector& atoms) +{ + bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; + bool first = true; + for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { + // usually we only need to get the first atom's reader, but + // with -all_load all atoms from all .o files come come back together + // so we need to scan all atoms + if ( first || scanAll ) { + // update fReadersThatHaveSuppliedAtoms + ObjectFile::Reader* reader = (*it)->getFile(); + if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) + == fReadersThatHaveSuppliedAtoms.end() ) { + fReadersThatHaveSuppliedAtoms.push_back(reader); + updateConstraints(reader); + } + } + this->addAtom(**it); + first = false; + } +} + +void Linker::logArchive(ObjectFile::Reader* reader) +{ + if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) { + fArchiveReadersLogged.insert(reader); + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + } +} + + +void Linker::buildAtomList() +{ + fStartBuildAtomsTime = mach_absolute_time(); + // add initial undefines from -u option + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { + fGlobalSymbolTable.require(*it); + } + + // writer can contribute atoms + this->addAtoms(fOutputFile->getAtoms()); + + // each reader contributes atoms + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + std::vector& atoms = reader->getAtoms(); + this->addAtoms(atoms); + if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) ) + logArchive(reader); + } + + // extra command line section always at end + std::vector& extraSections = fOptions.extraSections(); + for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { + this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); + fNextInputOrdinal += it->dataLen; + } +} + +static const char* pathLeafName(const char* path) +{ + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + return path; + else + return &shortPath[1]; +} + +void Linker::loadUndefines() +{ + // keep looping until no more undefines were added in last loop + unsigned int undefineCount = 0xFFFFFFFF; + while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { + undefineCount = fGlobalSymbolTable.getRequireCount(); + std::vector undefineNames; + fGlobalSymbolTable.getNeededNames(false, undefineNames); + for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* name = *it; + ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); + if ( (possibleAtom == NULL) + || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) + && (fOptions.outputKind() != Options::kObjectFile) + && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) { + std::vector* atoms = this->addJustInTimeAtoms(name); + if ( atoms != NULL ) + delete atoms; + } + } + } +} + +// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names +class ExportedObjcClass +{ +public: + ExportedObjcClass(Options& opt) : fOptions(opt) {} + + bool operator()(const char* name) const { + if ( fOptions.shouldExport(name) ) { + if ( strncmp(name, ".objc_class_name_", 17) == 0 ) + return true; + if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 ) + return true; + if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 ) + return true; + } + //fprintf(stderr, "%s is not exported\n", name); + return false; + } +private: + Options& fOptions; +}; + + +void Linker::checkUndefines() +{ + // error out on any remaining undefines + bool doPrint = true; + bool doError = true; + switch ( fOptions.undefinedTreatment() ) { + case Options::kUndefinedError: + break; + case Options::kUndefinedDynamicLookup: + doError = false; + break; + case Options::kUndefinedWarning: + doError = false; + break; + case Options::kUndefinedSuppress: + doError = false; + doPrint = false; + break; + } + std::vector unresolvableUndefines; + fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); + + // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names + // ignore unresolved references to Objc class names that are listed in -exported_symbols_list + if ( fOptions.hasExportRestrictList() ) + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end()); + + const int unresolvableCount = unresolvableUndefines.size(); + int unresolvableExportsCount = 0; + if ( unresolvableCount != 0 ) { + if ( doPrint ) { + if ( fOptions.printArchPrefix() ) + fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); + else + fprintf(stderr, "Undefined symbols:\n"); + for (int i=0; i < unresolvableCount; ++i) { + const char* name = unresolvableUndefines[i]; + fprintf(stderr, " \"%s\", referenced from:\n", name); + // scan all atoms for references + bool foundAtomReference = false; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + if ( strcmp(reference->getTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); + foundAtomReference = true; + } + } + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + if ( strcmp(reference->getFromTargetName(), name) == 0 ) { + fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); + foundAtomReference = true; + } + } + } + } + // scan command line options + if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbols_list command line option\n"); + ++unresolvableExportsCount; + } + } + } + if ( doError ) + throw "symbol(s) not found"; + } + + // for each tentative definition in symbol table look for dylib that exports same symbol name + if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { + for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { + ObjectFile::Atom* atom = it->second; + if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition) + && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { + // look for dylibs that export same name as used by global tentative definition + addJustInTimeAtoms(atom->getName(), true); + } + } + } + + // if we have no weak symbols, see if we override some weak symbol in some dylib + if ( !fGlobalSymbolTable.hasExternalWeakDefinitions() ) { + bool done = false; + for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); !done && (it != fGlobalSymbolTable.end()); ++it) { + ObjectFile::Atom* atom = it->second; + if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) + && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { + const char* name = atom->getName(); + //fprintf(stderr, "looking for dylibs with a weak %s\n", name); + // look for dylibs with weak exports of the same name + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + ObjectFile::Reader* reader = it->second; + if ( reader->hasWeakExternals() ) { + std::vector* atoms = reader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + // if this is a weak definition in a dylib + if ( (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fGlobalSymbolTable.setHasExternalWeakDefinitions(); + done = true; + break; + } + } + } + } + } + } + } + +} + + + +std::vector* Linker::addJustInTimeAtoms(const char* name, bool dylibsOnly) +{ + // when creating final linked image, writer gets first chance + if ( fOptions.outputKind() != Options::kObjectFile ) { + std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); + return atoms; // found a definition, no need to search anymore + } + } + + // give readers a chance + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); + bool isDylibReader = (reader->getInstallPath() != NULL); + if ( !dylibsOnly || isDylibReader ) { + std::vector* atoms = reader->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) { + logArchive(reader); + } + // if this is a weak definition in a dylib + if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + // keep looking for a non-weak definition + } + else { + // found a definition, no need to search anymore + return atoms; + } + } + } + } + } + + // for two level namesapce, give all implicitly link dylibs a chance + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->implicitlyLinked() ) { + //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() ); + std::vector* atoms = it->second->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + // if this is a weak definition in a dylib + if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + // keep looking for a non-weak definition + } + else { + // found a definition, no need to search anymore + return atoms; + } + } + } + } + } + + // for flat namespace, give indirect dylibs + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) { + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( ! it->second->explicitlyLinked() ) { + std::vector* atoms = it->second->getJustInTimeAtomsFor(name); + if ( atoms != NULL ) { + this->addAtoms(*atoms); + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + return atoms; // found a definition, no need to search anymore + } + } + } + } + + // writer creates a proxy in two cases: + // 1) ld -r is being used to create a .o file + // 2) -undefined dynamic_lookup is being used + // 3) -U _foo is being used + if ( (fOptions.outputKind() == Options::kObjectFile) + || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && !dylibsOnly) + || (fOptions.someAllowedUndefines() && !dylibsOnly) ) { + ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); + if ( atom != NULL ) { + this->addAtom(*atom); + return NULL; + } + } + //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); + return NULL; +} + +void Linker::resolve(ObjectFile::Reference* reference) +{ + // look in global symbol table + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + fprintf(stderr, "Undefined symbol: %s\n", targetName); + } + reference->setTarget(*target, reference->getTargetOffset()); +} + +void Linker::resolveFrom(ObjectFile::Reference* reference) +{ + // handle references that have two (from and to) targets + const char* fromTargetName = reference->getFromTargetName(); + ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); + if ( fromTarget == NULL ) { + fprintf(stderr, "Undefined symbol: %s\n", fromTargetName); + } + reference->setFromTarget(*fromTarget); +} + + +void Linker::resolveReferences() +{ + // note: the atom list may grow during this loop as libraries supply needed atoms + for (unsigned int j=0; j < fAllAtoms.size(); ++j) { + ObjectFile::Atom* atom = fAllAtoms[j]; + std::vector& references = atom->getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) + this->resolve(reference); + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) + this->resolveFrom(reference); + } + } +} + + +// used to remove stabs associated with atoms that won't be in output file +class NotInSet +{ +public: + NotInSet(std::set& theSet) : fSet(theSet) {} + + bool operator()(const ObjectFile::Reader::Stab& stab) const { + if ( stab.atom == NULL ) + return false; // leave stabs that are not associated with any atome + else + return ( fSet.count(stab.atom) == 0 ); + } + +private: + std::set& fSet; +}; + + +class NotLive +{ +public: + NotLive(std::set& set) : fLiveAtoms(set) {} + + bool operator()(ObjectFile::Atom*& atom) const { + //if ( fLiveAtoms.count(atom) == 0 ) + // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); + return ( fLiveAtoms.count(atom) == 0 ); + } +private: + std::set& fLiveAtoms; +}; + + +void Linker::addJustInTimeAtomsAndMarkLive(const char* name) +{ + std::vector* atoms = this->addJustInTimeAtoms(name); + if ( atoms != NULL ) { + if ( fOptions.allGlobalsAreDeadStripRoots() ) { + for (std::vector::iterator it=atoms->begin(); it != atoms->end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = atom->getDisplayName(); + this->markLive(*atom, &rootChain); + } + } + } + delete atoms; + } +} + +void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) +{ + if ( fLiveAtoms.count(&atom) == 0 ) { + // if -whylive cares about this symbol, then dump chain + if ( (previous->name != NULL) && fOptions.printWhyLive(previous->name) ) { + int depth = 0; + for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { + for(int i=depth; i > 0; --i) + fprintf(stderr, " "); + fprintf(stderr, "%s\n", p->name); + } + } + // set up next chain + WhyLiveBackChain thisChain; + thisChain.previous = previous; + // this atom is live + fLiveAtoms.insert(&atom); + // update total size info (except for __ZEROPAGE atom) + if ( atom.getSegment().isContentReadable() ) { + fTotalSize += atom.getSize(); + if ( atom.isZeroFill() ) + fTotalZeroFillSize += atom.getSize(); + } + // and all atoms it references + std::vector& references = atom.getReferences(); + for (std::vector::iterator it=references.begin(); it != references.end(); it++) { + ObjectFile::Reference* reference = *it; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + // look in global symbol table + const char* targetName = reference->getTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtomsAndMarkLive(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + } + else { + // mark as undefined, for later error processing + fAtomsWithUnresolvedReferences.push_back(&atom); + fGlobalSymbolTable.require(targetName); + } + } + switch ( reference->getTargetBinding() ) { + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + thisChain.name = reference->getTargetName(); + markLive(reference->getTarget(), &thisChain); + break; + case ObjectFile::Reference::kDontBind: + addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); + break; + case ObjectFile::Reference::kUnboundByName: + // do nothing + break; + } + // do the same as above, for "from target" + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + // look in global symbol table + const char* targetName = reference->getFromTargetName(); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL ) { + // load archives or dylibs + this->addJustInTimeAtomsAndMarkLive(targetName); + } + // look again + target = fGlobalSymbolTable.find(targetName); + if ( target != NULL ) { + reference->setFromTarget(*target); + } + else { + // mark as undefined, for later error processing + fGlobalSymbolTable.require(targetName); + } + } + switch ( reference->getFromTargetBinding() ) { + case ObjectFile::Reference::kBoundDirectly: + case ObjectFile::Reference::kBoundByName: + thisChain.name = reference->getFromTargetName(); + markLive(reference->getFromTarget(), &thisChain); + break; + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + // do nothing + break; + } + } + } +} + + +void Linker::addLiveRoot(const char* name) +{ + ObjectFile::Atom* target = fGlobalSymbolTable.find(name); + if ( target == NULL ) { + this->addJustInTimeAtomsAndMarkLive(name); + target = fGlobalSymbolTable.find(name); + } + if ( target != NULL ) + fLiveRootAtoms.insert(target); +} + + +void Linker::deadStripResolve() +{ + // add main() to live roots + ObjectFile::Atom* entryPoint = this->entryPoint(false); + if ( entryPoint != NULL ) + fLiveRootAtoms.insert(entryPoint); + + // add dyld_stub_binding_helper() to live roots + ObjectFile::Atom* dyldHelper = this->dyldHelper(); + if ( dyldHelper != NULL ) + fLiveRootAtoms.insert(dyldHelper); + + // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots + if ( fOptions.usingLazyDylibLinking() ) { + ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper(); + if ( dyldLazyDylibHelper != NULL ) + fLiveRootAtoms.insert(dyldLazyDylibHelper); + } + + // add -exported_symbols_list, -init, and -u entries to live roots + std::vector& initialUndefines = fOptions.initialUndefines(); + for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) + addLiveRoot(*it); + + // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots + // + if ( fOptions.hasWildCardExportRestrictList() ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) + && (fDeadAtoms.count(atom) == 0) + && fOptions.shouldExport(atom->getName()) ) + fLiveRootAtoms.insert(atom); + } + } + + // in some cases, every global scope atom in initial .o files is a root + if ( fOptions.allGlobalsAreDeadStripRoots() ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) + fLiveRootAtoms.insert(atom); + } + } + + // mark all roots as live, and all atoms they reference + for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.name = (*it)->getDisplayName(); + markLive(**it, &rootChain); + } + + // it is possible that there are unresolved references that can be resolved now + // this can happen if the first reference to a common symbol in an archive. + // common symbols are not in the archive TOC, but the .o could have been pulled in later. + // ld64 while linking cc1 [ when dead_strip is ON] + for (std::vector::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { + std::vector& references = (*it)->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); + if ( target != NULL ) { + reference->setTarget(*target, reference->getTargetOffset()); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + warning("internal error %s is not a tentative definition", target->getDisplayName()); + } + } + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { + ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); + if ( target != NULL ) { + reference->setFromTarget(*target); + fLiveAtoms.insert(target); + // by just adding this atom to fLiveAtoms set, we are assuming it has no + // references, which is true for commons. + if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) + warning("internal error %s is not a tentative definition", target->getDisplayName()); + } + } + } + } + + // now remove all non-live atoms from fAllAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); +} + +void Linker::checkObjC() +{ + // check dylibs + switch ( fCurrentObjCConstraint ) { + case ObjectFile::Reader::kObjcNone: + // can link against any dylib + break; + case ObjectFile::Reader::kObjcRetainRelease: + // cannot link against GC-only dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->explicitlyLinked() ) { + if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC ) + throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath()); + } + } + break; + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + // can link against GC or RR dylibs + break; + case ObjectFile::Reader::kObjcGC: + // cannot link against RR-only dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->explicitlyLinked() ) { + if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease ) + throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath()); + } + } + break; + } + + // synthesize __OBJC __image_info atom if needed + if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) { + this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses)); + } +} + +void Linker::addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName) +{ + if ( probeName != NULL ) { + if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) + fDtraceProbeSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) + fDtraceIsEnabledSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) + fDtraceAtomToTypes[&atom].insert(probeName); + else if ( fOptions.dTrace() && (strncmp(probeName, "__dtrace_probe$", 15) == 0) ) + fDtraceProbes.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); + } +} + +static uint8_t pointerKind(cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + return ppc::kPointer; + case CPU_TYPE_POWERPC64: + return ppc64::kPointer; + case CPU_TYPE_I386: + return x86::kPointer; + case CPU_TYPE_X86_64: + return x86_64::kPointer; + case CPU_TYPE_ARM: + return arm::kPointer; + } + throw "uknown architecture"; +} + +static uint8_t pcRelKind(cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + return ppc::kPointerDiff32; + case CPU_TYPE_POWERPC64: + return ppc64::kPointerDiff32; + case CPU_TYPE_I386: + return x86::kPointerDiff; + case CPU_TYPE_X86_64: + return x86_64::kPointerDiff32; + case CPU_TYPE_ARM: + return arm::kPointerDiff; + } + throw "uknown architecture"; +} + +typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); +typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); + + +void Linker::processDTrace() +{ + // handle dtrace 2.0 static probes + if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) { + // partition probes by provider name + // The symbol names looks like: + // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] + // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] + ProviderToProbes providerToProbes; + std::vector emptyList; + for(std::vector::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { + const char* providerStart = &it->probeName[16]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + } + for(std::vector::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { + const char* providerStart = &it->probeName[20]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + } + + // create a DOF section for each provider + int dofIndex=1; + CStringSet sectionNamesUsed; + for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { + const char* providerName = pit->first; + const std::vector& probes = pit->second; + + // open library and find dtrace_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); + createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); + // build list of typedefs/stability infos for this provider + CStringSet types; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + std::map::iterator pos = fDtraceAtomToTypes.find(it->atom); + if ( pos != fDtraceAtomToTypes.end() ) { + for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { + const char* providerStart = strchr(*sit, '$')+1; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char aProviderName[providerEnd-providerStart+1]; + strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); + if ( strcmp(aProviderName, providerName) == 0 ) + types.insert(*sit); + } + } + } + } + int typeCount = types.size(); + const char* typeNames[typeCount]; + //fprintf(stderr, "types for %s:\n", providerName); + uint32_t index = 0; + for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { + typeNames[index] = *it; + //fprintf(stderr, "\t%s\n", *it); + ++index; + } + + // build list of probe/isenabled sites + const uint32_t probeCount = probes.size(); + const char* probeNames[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + index = 0; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + probeNames[index] = it->probeName; + funtionNames[index] = it->atom->getName(); + offsetsInDOF[index] = 0; + ++index; + } + //fprintf(stderr, "calling libtrace to create DOF\n"); + //for(uint32_t i=0; i < probeCount; ++i) + // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); + // call dtrace library to create DOF section + size_t dofSectionSize; + uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + char sectionName[18]; + strcpy(sectionName, "__dof_"); + strlcpy(§ionName[6], providerName, 10); + // create unique section name so each DOF is in its own section + if ( sectionNamesUsed.count(sectionName) != 0 ) { + sectionName[15] = '0'; + sectionName[16] = '\0'; + while ( sectionNamesUsed.count(sectionName) != 0 ) + ++sectionName[15]; + } + sectionNamesUsed.insert(sectionName); + char symbolName[strlen(providerName)+64]; + sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); + opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, + "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); + fNextInputOrdinal += dofSectionSize; + // add references + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); + } + this->addAtoms(reader->getAtoms()); + } + else { + throw "error creating dtrace DOF section"; + } + } + } + // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files + else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) { + const uint32_t probeCount = fDtraceProbes.size(); + const char* labels[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + + // open libray and find dtrace_ld64_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror()); + oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror()); + + // build argument list + uint32_t index = 0; + for(std::vector::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) { + labels[index] = it->probeName; + funtionNames[index] = it->atom->getName(); + offsetsInDOF[index] = 0; + ++index; + } + size_t dofSectionSize; + // call dtrace library to create DOF section + uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal); + fNextInputOrdinal += dofSectionSize; + // add references + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX", i, offset, dofSectionSize); + reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset); + } + this->addAtoms(reader->getAtoms()); + } + else { + throw "error created dtrace DOF section"; + } + } +} + + +static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName) +{ + if ( objectFileLeafName == NULL ) + return true; + const char* atomFullPath = atom->getFile()->getPath(); + const char* lastSlash = strrchr(atomFullPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) + return true; + } + else { + if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) + return true; + } + return false; +} + + +static bool usesAnonymousNamespace(const char* symbol) +{ + return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) ); +} + + +// +// convert: +// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv +// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv +// +static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[]) +{ + const char* globPtr = strstr(inSymbol, "_GLOBAL__N_"); + while ( isdigit(*(--globPtr)) ) + ; // loop + char* endptr; + unsigned long length = strtoul(globPtr+1, &endptr, 10); + const char* globEndPtr = endptr + length; + int startLen = globPtr-inSymbol+1; + memcpy(outSymbol, inSymbol, startLen); + outSymbol[startLen] = '-'; + strcpy(&outSymbol[startLen+1], globEndPtr); +} + + +ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) +{ + ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); + if ( atom != NULL ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) + return atom; + } + else { + // slow case. The requested symbol is not in symbol table, so might be static function + static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; + static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace; + static bool built = false; + // build a hash_map the first time + if ( !built ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + const char* name = atom->getName(); + if ( name != NULL) { + if ( usesAnonymousNamespace(name) ) { + // symbol that uses anonymous namespace + char canonicalName[strlen(name)+2]; + canonicalizeAnonymousName(name, canonicalName); + const char* hashName = strdup(canonicalName); + SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName); + if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() ) + hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom; + else + hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL + } + else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // static function or data + SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); + if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) + hashTableOfTranslationUnitScopedSymbols[name] = atom; + else + hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL + } + } + } + //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size()); + built = true; + } + + // look for name in hashTableOfTranslationUnitScopedSymbols + SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName); + if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); + return pos->second; + } + if ( pos->second == NULL ) + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + const char* name = atom->getName(); + if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( fOptions.printOrderFileStatistics() ) + warning("%s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + + // look for name in hashTableOfSymbolsWithAnonymousNamespace + if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) { + // symbol that uses anonymous namespace + char canonicalName[strlen(orderedSymbol.symbolName)+2]; + canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName); + SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName); + if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName); + return pos->second; + } + if ( pos->second == NULL ) + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atom = *it; + const char* name = atom->getName(); + if ( (name != NULL) && usesAnonymousNamespace(name) ) { + char canonicalAtomName[strlen(name)+2]; + canonicalizeAnonymousName(name, canonicalAtomName); + if ( strcmp(canonicalAtomName, canonicalName) == 0 ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( fOptions.printOrderFileStatistics() ) + warning("%s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + } + } + return NULL; +} + + +void Linker::sortSections() +{ + Section::assignIndexes(); +} + + +// +// Linker::sortAtoms() +// +// The purpose of this method is to take the graph of all Atoms and produce an ordered +// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must +// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified +// in an order_file are seqenced as in the order_file and before Atoms not specified, +// 4) Atoms in the same section from the same .o file should be contiguous and sequenced +// in the same order they were in the .o file, 5) Atoms in the same Section but which came +// from different .o files should be sequenced in the same order that the .o files +// were passed to the linker (i.e. command line order). +// +// The way this is implemented is that the linker passes a "base ordinal" to each Reader +// as it is constructed. The reader should construct it Atoms so that calling getOrdinal() +// on its atoms returns a contiguous range of values starting at the base ordinal. Then +// sorting is just sorting by section, then by ordinal. +// +// If an order_file is specified, it gets more complicated. First, an override-ordinal map +// is created. It causes the sort routine to ignore the value returned by getOrdinal() and +// use the override value instead. Next some Atoms must be layed out consecutively +// (e.g. hand written assembly that does not end with return, but rather falls into +// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of +// kFollowOn refernces produces "clusters" of atoms that must stay together. +// If an order_file tries to move one atom, it may need to move a whole cluster. The +// algorithm to do this models clusters using two maps. The "starts" maps maps any +// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a +// cluster to the next Atom in the cluster. With this in place, while processing an +// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is +// given ordinal overrides. +// +void Linker::sortAtoms() +{ + fStartSortTime = mach_absolute_time(); + // if -order_file is used, build map of atom ordinal overrides + std::map* ordinalOverrideMap = NULL; + std::map theOrdinalOverrideMap; + const bool log = false; + if ( fOptions.orderedSymbols().size() != 0 ) { + // first make a pass to find all follow-on references and build start/next maps + // which are a way to represent clusters of atoms that must layout together + std::map followOnStarts; + std::map followOnNexts; + for (std::vector::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) { + ObjectFile::Atom* atom = *ait; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == 1 ) { // FIX FIX + ObjectFile::Atom* targetAtom = &ref->getTarget(); + if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName()); + std::map::iterator startFrom = followOnStarts.find(atom); + std::map::iterator startTo = followOnStarts.find(targetAtom); + if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) { + // this is first time we've seen either atom, make simple cluster of the two + if ( log ) fprintf(stderr, " new cluster\n"); + followOnStarts[atom] = atom; + followOnStarts[targetAtom] = atom; + followOnNexts[atom] = targetAtom; + followOnNexts[targetAtom] = NULL; + } + else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) { + // atom is at end of an existing cluster, so append target to end of cluster + if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName()); + followOnNexts[atom] = targetAtom; + followOnNexts[targetAtom] = NULL; + followOnStarts[targetAtom] = followOnStarts[atom]; + } + else { + // gerneral case of inserting into an existing cluster + if ( followOnNexts[atom] != NULL ) { + // an atom with two follow-ons is illegal + warning("can't order %s because both %s and %s must follow it", + atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName()); + } + else { + // there already exists an atom that says target must be its follow-on + const ObjectFile::Atom* originalStart = startTo->second; + const ObjectFile::Atom* originalPrevious = originalStart; + while ( followOnNexts[originalPrevious] != targetAtom ) + originalPrevious = followOnNexts[originalPrevious]; + bool otherIsAlias = (originalPrevious->getSize() == 0); + bool thisIsAlias = (atom->getSize() == 0); + if ( !otherIsAlias && !thisIsAlias ) { + warning("can't order %s because both %s and %s must preceed it", + targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName()); + } + else if ( otherIsAlias ) { + if ( originalPrevious == originalStart ) { + // other is alias at start of cluster, make this the new start of cluster + if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName()); + followOnNexts[atom] = originalPrevious; + for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) + followOnStarts[nextAtom] = atom; + } + else { + // other is alias in middle of cluster, insert new atom before it + if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); + followOnStarts[atom] = originalStart; + followOnNexts[atom] = originalPrevious; + for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) { + if ( followOnNexts[a] == originalPrevious ) { + followOnNexts[a] = atom; + break; + } + } + } + } + else { + // this is alias, so it can go inbetween originalPrevious and targetAtom + if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); + followOnStarts[atom] = originalStart; + followOnNexts[atom] = followOnNexts[originalPrevious]; + followOnNexts[originalPrevious] = atom; + } + } + } + } + } + } + + if ( log ) { + for(std::map::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it) + fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName()); + + for(std::map::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it) + fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null"); + } + + // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals + ordinalOverrideMap = &theOrdinalOverrideMap; + uint32_t index = 0; + uint32_t matchCount = 0; + std::vector& orderedSymbols = fOptions.orderedSymbols(); + for(std::vector::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) { + ObjectFile::Atom* atom = this->findAtom(*it); + if ( atom != NULL ) { + std::map::iterator start = followOnStarts.find(atom); + if ( start != followOnStarts.end() ) { + // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together + for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) { + std::map::iterator pos = theOrdinalOverrideMap.find(nextAtom); + if ( pos == theOrdinalOverrideMap.end() ) { + theOrdinalOverrideMap[nextAtom] = index++; + if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath()); + } + else { + if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", + atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] ); + } + } + } + else { + theOrdinalOverrideMap[atom] = index; + if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + ++matchCount; + //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName); + } + ++index; + } + if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) { + warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() ); + } + } + + // sort atoms + std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap)); + + //fprintf(stderr, "Sorted atoms:\n"); + //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName()); + //} +} + + +// make sure given addresses are within reach of branches, etc +void Linker::tweakLayout() +{ + // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB + if ( fTotalSize > 0x7F000000 ) { + fBiggerThanTwoGigOutput = true; + + if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 ) + throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); + + // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment + Section* hugeZeroFills = Section::find("__huge", "__DATA", true); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) ) + atom->setSection(hugeZeroFills); + } + } +} + + +void Linker::writeDotOutput() +{ + const char* dotOutFilePath = fOptions.dotOutputFile(); + if ( dotOutFilePath != NULL ) { + FILE* out = fopen(dotOutFilePath, "w"); + if ( out != NULL ) { + // print header + fprintf(out, "digraph dg\n{\n"); + fprintf(out, "\tconcentrate = true;\n"); + fprintf(out, "\trankdir = LR;\n"); + + // print each atom as a node + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() != fOutputFile ) { + const char* name = atom->getDisplayName(); + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); + } + else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + char cstring[atom->getSize()+2]; + atom->copyRawContent((uint8_t*)cstring); + fprintf(out, "\taddr%p [ label = \"string: '", atom); + for (const char* s=cstring; *s != '\0'; ++s) { + if ( *s == '\n' ) + fprintf(out, "\\\\n"); + else + fputc(*s, out); + } + fprintf(out, "'\" ];\n"); + } + else { + fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name); + } + } + } + fprintf(out, "\n"); + + // print each reference as an edge + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* fromAtom = *it; + if ( fromAtom->getFile() != fOutputFile ) { + std::vector& references = fromAtom->getReferences(); + std::set seenTargets; + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + ObjectFile::Atom* toAtom = &(reference->getTarget()); + if ( seenTargets.count(toAtom) == 0 ) { + seenTargets.insert(toAtom); + fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); + } + } + } + } + fprintf(out, "\n"); + + // push all imports to bottom of graph + fprintf(out, "{ rank = same; "); + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->getFile() != fOutputFile ) + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fprintf(out, "addr%p; ", atom); + } + } + fprintf(out, "};\n "); + + // print footer + fprintf(out, "}\n"); + fclose(out); + } + else { + warning("could not write dot output file: %s", dotOutFilePath); + } + } +} + +ObjectFile::Atom* Linker::entryPoint(bool orInit) +{ + // if main executable, find entry point atom + ObjectFile::Atom* entryPoint = NULL; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + if ( entryPoint == NULL ) { + throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); + } + break; + case Options::kDynamicLibrary: + if ( orInit && (fOptions.initFunctionName() != NULL) ) { + entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); + if ( entryPoint == NULL ) { + throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); + } + } + break; + case Options::kObjectFile: + case Options::kDynamicBundle: + entryPoint = NULL; + break; + } + return entryPoint; +} + +ObjectFile::Atom* Linker::dyldHelper() +{ + return fGlobalSymbolTable.find("dyld_stub_binding_helper"); +} + +ObjectFile::Atom* Linker::dyldLazyLibraryHelper() +{ + return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper"); +} + +const char* Linker::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + + +// +// The stab strings are of the form: +// ':' +// but the contain a colon. +// For C++ may contain a double colon (e.g. std::string:f(0,1) ) +// For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) +// +const char* Linker::truncateStabString(const char* str) +{ + enum { start, inObjc } state = start; + for (const char* s = str; *s != 0; ++s) { + char c = *s; + switch (state) { + case start: + if ( c == '[' ) { + state = inObjc; + } + else { + if ( c == ':' ) { + if ( s[1] == ':' ) { + ++s; + } + else { + // found colon + // Duplicate strndup behavior here. + int trunStrLen = s-str+2; + char* temp = new char[trunStrLen+1]; + memcpy(temp, str, trunStrLen); + temp[trunStrLen] = '\0'; + return temp; + } + } + } + break; + case inObjc: + if ( c == ']' ) { + state = start; + } + break; + } + } + // malformed + return str; +} + + +bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) +{ + switch(stab.type){ + case N_GSYM: + case N_STSYM: + case N_LCSYM: + case N_FUN: + // these all need truncated strings + stab.string = truncateStabString(stab.string); + return true; + case N_SO: + case N_OSO: + case N_OPT: + case N_SOL: + // these are included in the minimal stabs, but they keep their full string + return true; + default: + return false; + } +} + + +struct HeaderRange { + std::vector::iterator begin; + std::vector::iterator end; + int parentRangeIndex; + uint32_t sum; + bool sumPrecomputed; + bool useEXCL; + bool cannotEXCL; // because of SLINE, etc stabs +}; + + +typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> PathToSums; + +// hash table that maps header path to a vector of known checksums for that path +static PathToSums sKnownBINCLs; + + +void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) +{ + const bool log = false; + bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); + std::vector* readerStabs = reader->getStabs(); + if ( readerStabs == NULL ) + return; + + if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); + std::vector ranges; + int curRangeIndex = -1; + int count = 0; + ObjectFile::Atom* atomWithLowestOrdinal = NULL; + ObjectFile::Atom* atomWithHighestOrdinal = NULL; + uint32_t highestOrdinal = 0; + uint32_t lowestOrdinal = UINT_MAX; + std::vector > soRanges; + // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums + // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + ++count; + switch ( it->type ) { + case N_BINCL: + { + HeaderRange range; + range.begin = it; + range.end = readerStabs->end(); + range.parentRangeIndex = curRangeIndex; + range.sum = it->value; + range.sumPrecomputed = (range.sum != 0); + range.useEXCL = false; + range.cannotEXCL = false; + curRangeIndex = ranges.size(); + if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string); + ranges.push_back(range); + } + break; + case N_EINCL: + if ( curRangeIndex == -1 ) { + warning("EINCL missing BINCL in %s", reader->getPath()); + } + else { + ranges[curRangeIndex].end = it+1; + if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); + curRangeIndex = ranges[curRangeIndex].parentRangeIndex; + } + break; + case N_FUN: + { + std::map::iterator pos = atomOrdinals.find(it->atom); + if ( pos != atomOrdinals.end() ) { + uint32_t ordinal = pos->second; + if ( ordinal > highestOrdinal ) { + highestOrdinal = ordinal; + atomWithHighestOrdinal = it->atom; + } + if ( ordinal < lowestOrdinal ) { + lowestOrdinal = ordinal; + atomWithLowestOrdinal = it->atom; + } + } + } + // fall through + case N_BNSYM: + case N_ENSYM: + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + case N_STSYM: + case N_LCSYM: + if ( curRangeIndex != -1 ) { + ranges[curRangeIndex].cannotEXCL = true; + if ( fOptions.warnStabs() ) + warning("cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + } + break; + case N_SO: + if ( (it->string != NULL) && (strlen(it->string) > 0) ) { + // start SO, reset hi/low FUN tracking + atomWithLowestOrdinal = NULL; + atomWithHighestOrdinal = NULL; + highestOrdinal = 0; + lowestOrdinal = UINT_MAX; + } + else { + // end SO, record hi/low atoms for this SO range + soRanges.push_back(std::make_pair(atomWithLowestOrdinal, atomWithHighestOrdinal)); + } + // fall through + default: + if ( curRangeIndex != -1 ) { + if ( ! ranges[curRangeIndex].sumPrecomputed ) { + uint32_t sum = 0; + const char* s = it->string; + char c; + while ( (c = *s++) != 0 ) { + sum += c; + // don't checkusm first number (file index) after open paren in string + if ( c == '(' ) { + while(isdigit(*s)) + ++s; + } + } + ranges[curRangeIndex].sum += sum; + } + } + + } + } + if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); + if ( curRangeIndex != -1 ) + warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath()); + + // if no BINCLs + if ( ranges.size() == 0 ) { + unsigned int soIndex = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + // copy minimal or all stabs + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( soIndex < soRanges.size() ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + } + fStabs.push_back(stab); + } + } + return; + } + + //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); + //for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { + // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); + //} + + // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL + for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { + if ( ! it->cannotEXCL ) { + const char* header = it->begin->string; + uint32_t sum = it->sum; + PathToSums::iterator pos = sKnownBINCLs.find(header); + if ( pos != sKnownBINCLs.end() ) { + std::vector& sums = pos->second; + for(std::vector::iterator sit=sums.begin(); sit != sums.end(); ++sit) { + if (*sit == sum) { + //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath()); + it->useEXCL = true; + break; + } + } + if ( ! it->useEXCL ) { + // have seen this path, but not this checksum + //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header); + sums.push_back(sum); + } + } + else { + // have not seen this path, so add to known BINCLs + std::vector empty; + sKnownBINCLs[header] = empty; + sKnownBINCLs[header].push_back(sum); + //fprintf(stderr, "registering checksum %08X for %s\n", sum, header); + } + } + } + + // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs + curRangeIndex = -1; + const int maxRangeIndex = ranges.size(); + int soIndex = 0; + for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { + switch ( it->type ) { + case N_BINCL: + for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) { + if ( ranges[i].begin == it ) { + curRangeIndex = i; + HeaderRange& range = ranges[curRangeIndex]; + ObjectFile::Reader::Stab stab = *it; + stab.value = range.sum; // BINCL and EXCL have n_value set to checksum + if ( range.useEXCL ) + stab.type = N_EXCL; // transform BINCL into EXCL + if ( !minimal ) + fStabs.push_back(stab); + break; + } + } + break; + case N_EINCL: + if ( curRangeIndex != -1 ) { + if ( !ranges[curRangeIndex].useEXCL && !minimal ) + fStabs.push_back(*it); + curRangeIndex = ranges[curRangeIndex].parentRangeIndex; + } + break; + default: + if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { + ObjectFile::Reader::Stab stab = *it; + if ( !minimal || minimizeStab(stab) ) { + if ( stab.type == N_SO ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { + // starting SO is associated with first atom + stab.atom = soRanges[soIndex].first; + } + else { + // ending SO is associated with last atom + stab.atom = soRanges[soIndex].second; + ++soIndex; + } + } + fStabs.push_back(stab); + } + } + } + } + +} + + +// used to prune out atoms that don't need debug notes generated +class NoDebugNoteAtom +{ +public: + NoDebugNoteAtom(const std::map& readersWithDwarfOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} + + bool operator()(const ObjectFile::Atom* atom) const { + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + return true; + if ( atom->getName() == NULL ) + return true; + if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) + return true; + return false; + } + +private: + const std::map& fReadersWithDwarfOrdinals; +}; + +// used to sort atoms with debug notes +class ReadersWithDwarfSorter +{ +public: + ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, + const std::map& atomOrdinals) + : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} + + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const + { + // first sort by reader + unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; + unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; + if ( leftReaderIndex != rightReaderIndex ) + return (leftReaderIndex < rightReaderIndex); + + // then sort by atom ordinal + unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; + unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; + return leftAtomIndex < rightAtomIndex; + } + +private: + const std::map& fReadersWithDwarfOrdinals; + const std::map& fAtomOrdinals; +}; + + + + + +void Linker::synthesizeDebugNotes(std::vector& allAtomsByReader) +{ + // synthesize "debug notes" and add them to master stabs vector + const char* dirPath = NULL; + const char* filename = NULL; + bool wroteStartSO = false; + bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL); + __gnu_cxx::hash_set, CStringEquals> seenFiles; + for (std::vector::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { + ObjectFile::Atom* atom = *it; + const char* newDirPath; + const char* newFilename; + //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( filename != NULL ) { + // translation unit change, emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ObjectFile::Reader::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + fStabs.push_back(dirPathStab); + ObjectFile::Reader::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + fStabs.push_back(fileStab); + // Synthesize OSO for start of file + ObjectFile::Reader::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + objStab.other = 0; + objStab.desc = 1; + objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); + objStab.string = assureFullPath(atom->getFile()->getPath()); + fStabs.push_back(objStab); + wroteStartSO = true; + // add the source file path to seenFiles so it does not show up in SOLs + seenFiles.insert(newFilename); + } + filename = newFilename; + dirPath = newDirPath; + if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { + // Synthesize BNSYM and start FUN stabs + ObjectFile::Reader::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + fStabs.push_back(beginSym); + ObjectFile::Reader::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = atom->getName(); + fStabs.push_back(startFun); + // Synthesize any SOL stabs needed + std::vector* lineInfo = atom->getLineInfo(); + if ( lineInfo != NULL ) { + const char* curFile = NULL; + for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + if ( it->fileName != curFile ) { + if ( seenFiles.count(it->fileName) == 0 ) { + seenFiles.insert(it->fileName); + ObjectFile::Reader::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = it->fileName; + fStabs.push_back(sol); + } + curFile = it->fileName; + } + } + } + // Synthesize end FUN and ENSYM stabs + ObjectFile::Reader::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + fStabs.push_back(endFun); + ObjectFile::Reader::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + fStabs.push_back(endSym); + } + else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) { + // no stabs for atoms that would not be in the symbol table + } + else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { + // no stabs for absolute symbols + } + else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) { + // no stabs for .eh atoms + } + else { + ObjectFile::Reader::Stab globalsStab; + const char* name = atom->getName(); + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + else { + // Synthesize GSYM stab for other globals + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + fStabs.push_back(globalsStab); + } + } + } + } + + if ( wroteStartSO ) { + // emit ending SO + ObjectFile::Reader::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + fStabs.push_back(endFileStab); + } +} + + + + +void Linker::collectDebugInfo() +{ + std::map atomOrdinals; + fStartDebugTime = mach_absolute_time(); + if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { + + // determine mixture of stabs and dwarf + bool someStabs = false; + bool someDwarf = false; + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoNone: + break; + case ObjectFile::Reader::kDebugInfoStabs: + someStabs = true; + break; + case ObjectFile::Reader::kDebugInfoDwarf: + someDwarf = true; + fCreateUUID = true; + break; + case ObjectFile::Reader::kDebugInfoStabsUUID: + someStabs = true; + fCreateUUID = true; + break; + default: + throw "Unhandled type of debug information"; + } + } + } + + if ( someDwarf || someStabs ) { + // try to minimize re-allocations + fStabs.reserve(1024); + + // make mapping from atoms to ordinal + uint32_t ordinal = 1; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { + atomOrdinals[*it] = ordinal++; + } + } + + // process all dwarf .o files as a batch + if ( someDwarf ) { + // make mapping from readers with dwarf to ordinal + std::map readersWithDwarfOrdinals; + uint32_t readerOrdinal = 1; + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { + readersWithDwarfOrdinals[reader] = readerOrdinal++; + } + } + + // make a vector of atoms + std::vector allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); + // remove those not from a reader that has dwarf + allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), + NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); + // sort by reader then atom ordinal + std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); + // add debug notes for each atom + this->synthesizeDebugNotes(allAtomsByReader); + } + + // process all stabs .o files one by one + if ( someStabs ) { + // get stabs from each reader, in command line order + for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); + it != fReadersThatHaveSuppliedAtoms.end(); + it++) { + ObjectFile::Reader* reader = *it; + if ( reader != NULL ) { + switch ( reader->getDebugInfoKind() ) { + case ObjectFile::Reader::kDebugInfoDwarf: + case ObjectFile::Reader::kDebugInfoNone: + // do nothing + break; + case ObjectFile::Reader::kDebugInfoStabs: + case ObjectFile::Reader::kDebugInfoStabsUUID: + collectStabs(reader, atomOrdinals); + break; + default: + throw "Unhandled type of debug information"; + } + } + } + // remove stabs associated with atoms that won't be in output + std::set allAtomsSet; + allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); + fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); + } + } +} + +void Linker::writeOutput() +{ + if ( fOptions.forceCpuSubtypeAll() ) + fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; + + fStartWriteTime = mach_absolute_time(); + // tell writer about each segment's atoms + fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), + this->dyldHelper(), this->dyldLazyLibraryHelper(), + fCreateUUID, fCanScatter, + fCurrentCpuConstraint, fBiggerThanTwoGigOutput, + fGlobalSymbolTable.hasExternalWeakDefinitions()); +} + +ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) +{ + // map in whole file + uint64_t len = info.fileLen; + int fd = ::open(info.path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + if ( info.fileLen < 20 ) + throw "file too small"; + + uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file, errno=%d", errno); + + // if fat file, skip to architecture we want + // Note: fat header is always big-endian + const fat_header* fh = (fat_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + uint32_t sliceToUse; + bool sliceFound = false; + if ( fOptions.preferSubArchitecture() ) { + // first try to find a slice that match cpu-type and cpu-sub-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture) + && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( !sliceFound ) { + // look for any slice that matches just cpu-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( sliceFound ) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); + len = OSSwapBigToHostInt32(archs[sliceToUse].size); + // if requested architecture is page aligned within fat file, then remap just that portion of file + if ( (fileOffset & 0x00000FFF) == 0 ) { + // unmap whole file + munmap((caddr_t)p, info.fileLen); + // re-map just part we need + p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); + if ( p == (uint8_t*)(-1) ) + throwf("can't re-map file, errno=%d", errno); + } + else { + p = &p[fileOffset]; + } + } + } + ::close(fd); + + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Reader::validFile(p) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); + else if ( archive::Reader::validFile(p, len) ) + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + break; + break; + } + +#if LTO_SUPPORT + if ( lto::Reader::validFile(p, len, fArchitecture) ) { + return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); + } + else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) { + throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded"; + } +#endif + // error handling + if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + throwf("missing required architecture %s in file", fArchitectureName); + } + else { + throw "file is not of required architecture"; + } +} + +void Linker::logDylib(ObjectFile::Reader* reader, bool indirect) +{ + if ( fOptions.readerOptions().fTraceDylibs ) { + const char* fullPath = reader->getPath(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + if ( indirect ) + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + else + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } +} + + + +ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath) +{ + //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); + InstallNameToReader::iterator pos = fDylibMap.find(installPath); + if ( pos != fDylibMap.end() ) { + return pos->second; + } + else { + // allow -dylib_path option to override indirect library to use + for (std::vector::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) { + if ( strcmp(dit->installName,installPath) == 0 ) {\ + try { + Options::FileInfo info = fOptions.findFile(dit->useInstead); + ObjectFile::Reader* reader = this->createReader(info); + fDylibMap[strdup(installPath)] = reader; + this->logDylib(reader, true); + return reader; + } + catch (const char* msg) { + warning("ignoring -dylib_file option, %s", msg); + } + } + } + char newPath[MAXPATHLEN]; + // handle @loader_path + if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { + strcpy(newPath, fromPath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &installPath[13]); + else + strcpy(newPath, &installPath[13]); + installPath = newPath; + } + // note: @executable_path case is handled inside findFileUsingPaths() + // search for dylib using -F and -L paths + Options::FileInfo info = fOptions.findFileUsingPaths(installPath); + try { + ObjectFile::Reader* reader = this->createReader(info); + fDylibMap[strdup(installPath)] = reader; + this->logDylib(reader, true); + return reader; + } + catch (const char* msg) { + throwf("in %s, %s", info.path, msg); + } + } +} + + +void Linker::processDylibs() +{ + fAllDirectDylibsLoaded = true; + + // mark all dylibs initially specified as required and check if they can be used + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + it->second->setExplicitlyLinked(); + this->checkDylibClientRestrictions(it->second); + } + + // keep processing dylibs until no more dylibs are added + unsigned long lastMapSize = 0; + while ( lastMapSize != fDylibMap.size() ) { + lastMapSize = fDylibMap.size(); + // can't iterator fDylibMap while modifying it, so use temp buffer + std::vector currentUnprocessedReaders; + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( fDylibsProcessed.count(it->second) == 0 ) + currentUnprocessedReaders.push_back(it->second); + } + for (std::vector::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) { + fDylibsProcessed.insert(*it); + (*it)->processIndirectLibraries(this); + } + } + + // go back over original dylibs and mark sub frameworks as re-exported + if ( fOptions.outputKind() == Options::kDynamicLibrary ) { + const char* myLeaf = strrchr(fOptions.installPath(), '/'); + if ( myLeaf != NULL ) { + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + const char* childParent = reader->parentUmbrella(); + if ( childParent != NULL ) { + if ( strcmp(childParent, &myLeaf[1]) == 0 ) { + // set re-export bit of info + std::map::iterator pos = fDylibOptionsMap.find(reader); + if ( pos != fDylibOptionsMap.end() ) { + pos->second.fReExport = true; + } + } + } + } + } + } + +} + + + +void Linker::createReaders() +{ + fStartCreateReadersTime = mach_absolute_time(); + std::vector& files = fOptions.getInputFiles(); + const int count = files.size(); + if ( count == 0 ) + throw "no object files specified"; + // add all direct object, archives, and dylibs + for (int i=0; i < count; ++i) { + Options::FileInfo& entry = files[i]; + // ignore /usr/lib/dyld on command line in crt.o build + if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { + try { + this->addInputFile(this->createReader(entry), entry); + } + catch (const char* msg) { + if ( strstr(msg, "architecture") != NULL ) { + if ( fOptions.ignoreOtherArchInputFiles() ) { + // ignore, because this is about an architecture not in use + } + else { + warning("in %s, %s", entry.path, msg); + } + } + else { + throwf("in %s, %s", entry.path, msg); + } + } + } + } + + this->processDylibs(); +} + + + +ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + fNextInputOrdinal += mappedLen; + // remember which readers are archives because they are logged differently + fArchiveReaders.insert(reader); + + // update stats + fTotalArchiveSize += mappedLen; + ++fTotalArchivesLoaded; + return reader; +} + +ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + fNextInputOrdinal += mappedLen; + // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't + if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() ) + fCanScatter = false; + + // update stats + fTotalObjectSize += mappedLen; + ++fTotalObjectLoaded; + return reader; +} + + +void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) +{ + // Check for any restrictions on who can link with this dylib + const char* readerParentName = reader->parentUmbrella() ; + std::vector* clients = reader->getAllowableClients(); + if ( (readerParentName != NULL) || (clients != NULL) ) { + // only dylibs that are in an umbrella or have a client list need verification + const char* installName = fOptions.installPath(); + const char* installNameLastSlash = strrchr(installName, '/'); + bool isParent = false; + bool isSibling = false; + bool isAllowableClient = false; + // There are three cases: + if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) { + // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella + isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 ); + + // hack to support umbrella variants that encode the variant name in the install name + // e.g. CoreServices_profile + if ( !isParent ) { + const char* underscore = strchr(&installNameLastSlash[1], '_'); + if ( underscore != NULL ) { + isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 ); + } + } + + // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent + isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); + } + + if ( !isParent && !isSibling && (clients != NULL) ) { + // case 3) the dylib has a list of allowable clients, and we are creating one of them + const char* clientName = fOptions.clientName(); + int clientNameLen = 0; + if ( clientName != NULL ) { + // use client name as specified on command line + clientNameLen = strlen(clientName); + } + else { + // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) + clientName = installName; + clientNameLen = strlen(clientName); + // starts after last slash + if ( installNameLastSlash != NULL ) + clientName = &installNameLastSlash[1]; + if ( strncmp(clientName, "lib", 3) == 0 ) + clientName = &clientName[3]; + // up to first dot + const char* firstDot = strchr(clientName, '.'); + if ( firstDot != NULL ) + clientNameLen = firstDot - clientName; + // up to first underscore + const char* firstUnderscore = strchr(clientName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) + clientNameLen = firstUnderscore - clientName; + } + + // Use clientName to check if this dylib is able to link against the allowable clients. + for (std::vector::iterator it = clients->begin(); it != clients->end(); it++) { + if ( strncmp(*it, clientName, clientNameLen) == 0 ) + isAllowableClient = true; + } + } + + if ( !isParent && !isSibling && !isAllowableClient ) { + if ( readerParentName != NULL ) { + throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", + reader->getPath(), readerParentName); + } + else { + throwf("cannot link directly with %s", reader->getPath()); + } + } + } + + +} + +ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + fNextInputOrdinal += mappedLen; + if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { + // this is a "blank" stub + // silently ignore it + return reader; + } + // add to map of loaded dylibs + const char* installPath = reader->getInstallPath(); + if ( installPath != NULL ) { + InstallNameToReader::iterator pos = fDylibMap.find(installPath); + if ( pos == fDylibMap.end() ) { + fDylibMap[strdup(installPath)] = reader; + } + else { + InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); + if ( pos2 == fDylibMap.end() ) + fDylibMap[strdup(reader->getPath())] = reader; + else + warning("duplicate dylib %s", reader->getPath()); + } + } + else if ( info.options.fBundleLoader ) + fBundleLoaderReader = reader; + + // log direct readers + if ( !fAllDirectDylibsLoaded ) + this->logDylib(reader, false); + + // update stats + ++fTotalDylibsLoaded; + + return reader; +} + + +void Linker::logTraceInfo (const char* format, ...) +{ + static int trace_file = -1; + char trace_buffer[MAXPATHLEN * 2]; + char *buffer_ptr; + int length; + ssize_t amount_written; + const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile; + + if(trace_file == -1) { + if(trace_file_path != NULL) { + trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); + if(trace_file == -1) + throwf("Could not open or create trace file: %s", trace_file_path); + } + else { + trace_file = fileno(stderr); + } + } + + va_list ap; + va_start(ap, format); + length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); + va_end(ap); + buffer_ptr = trace_buffer; + + while(length > 0) { + amount_written = write(trace_file, buffer_ptr, length); + if(amount_written == -1) + /* Failure to write shouldn't fail the build. */ + return; + buffer_ptr += amount_written; + length -= amount_written; + } +} + + + +void Linker::createWriter() +{ + fStartCreateWriterTime = mach_absolute_time(); + + // make a vector out of all required dylibs in fDylibMap + std::vector dynamicLibraries; + // need to preserve command line order + for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { + ObjectFile::Reader* reader = *it; + for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) { + if ( reader == mit->second ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = reader; + dylibInfo.options = fDylibOptionsMap[reader]; + dynamicLibraries.push_back(dylibInfo); + break; + } + } + } + // then add any other dylibs + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + if ( it->second->implicitlyLinked() ) { + // if not already in dynamicLibraries + bool alreadyInDynamicLibraries = false; + for (std::vector::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) { + if ( dit->reader == it->second ) { + alreadyInDynamicLibraries = true; + break; + } + } + if ( ! alreadyInDynamicLibraries ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = it->second; + std::map::iterator pos = fDylibOptionsMap.find(it->second); + if ( pos != fDylibOptionsMap.end() ) { + dylibInfo.options = pos->second; + } + else { + dylibInfo.options.fWeakImport = false; // FIX ME + dylibInfo.options.fReExport = false; + dylibInfo.options.fBundleLoader = false; + } + dynamicLibraries.push_back(dylibInfo); + } + } + } + if ( fBundleLoaderReader != NULL ) { + ExecutableFile::DyLibUsed dylibInfo; + dylibInfo.reader = fBundleLoaderReader; + dylibInfo.options.fWeakImport = false; + dylibInfo.options.fReExport = false; + dylibInfo.options.fBundleLoader = true; + dynamicLibraries.push_back(dylibInfo); + } + + const char* path = fOptions.getOutputFilePath(); + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_POWERPC64: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_I386: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_X86_64: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + case CPU_TYPE_ARM: + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); + break; + default: + throw "unknown architecture"; + } +} + + +Linker::SymbolTable::SymbolTable(Linker& owner) + : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false) +{ +} + +void Linker::SymbolTable::require(const char* name) +{ + //fprintf(stderr, "require(%s)\n", name); + Mapper::iterator pos = fTable.find(name); + if ( pos == fTable.end() ) { + fTable[name] = NULL; + ++fRequireCount; + } +} + +// convenience labels for 2-dimensional switch statement +enum AllDefinitionCombinations { + kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, + kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, + kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, + kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, + kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, + kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition, + kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition, + kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition, + kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition, + kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition, + kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol +}; + +bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) +{ + bool useNew = true; + bool checkVisibilityMismatch = false; + const char* name = newAtom.getName(); + if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { + switch ( newAtom.getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + fHasExternalTentativeDefinitions = true; + break; + case ObjectFile::Atom::kWeakDefinition: + fHasExternalWeakDefinitions = true; + break; + default: + break; + } + } + //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); + Mapper::iterator pos = fTable.find(name); + ObjectFile::Atom* existingAtom = NULL; + if ( pos != fTable.end() ) + existingAtom = pos->second; + if ( existingAtom != NULL ) { + // already have atom with same name in symbol table + switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { + case kRegAndReg: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kRegAndWeak: + // ignore new weak atom, because we already have a non-weak one + useNew = false; + break; + case kRegAndTent: + // ignore new tentative atom, because we already have a regular one + useNew = false; + checkVisibilityMismatch = true; + if ( newAtom.getSize() > existingAtom->getSize() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "is smaller than the real definition of size %llu from %s", + newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), + existingAtom->getSize(), existingAtom->getFile()->getPath()); + } + break; + case kRegAndExtern: + // ignore external atom, because we already have a one + useNew = false; + break; + case kRegAndExternWeak: + // ignore external atom, because we already have a one + useNew = false; + break; + case kRegAndAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case kWeakAndReg: + // replace existing weak atom with regular one + break; + case kWeakAndWeak: + // have another weak atom, use whichever has largest alignment requirement + // because codegen of some client may require alignment + useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); + checkVisibilityMismatch = true; + break; + case kWeakAndTent: + // replace existing weak atom with tentative one ??? + break; + case kWeakAndExtern: + // keep weak atom, at runtime external one may override + useNew = false; + break; + case kWeakAndExternWeak: + // keep weak atom, at runtime external one may override + useNew = false; + break; + case kWeakAndAbsolute: + // replace existing weak atom with absolute one + break; + case kTentAndReg: + // replace existing tentative atom with regular one + checkVisibilityMismatch = true; + if ( newAtom.getSize() < existingAtom->getSize() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s", + newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), + newAtom.getSize(), newAtom.getFile()->getPath()); + } + break; + case kTentAndWeak: + // replace existing tentative atom with weak one ??? + break; + case kTentAndTent: + // use largest + checkVisibilityMismatch = true; + if ( newAtom.getSize() < existingAtom->getSize() ) { + useNew = false; + } + else { + if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) + warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName()); + } + break; + case kTentAndExtern: + case kTentAndExternWeak: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("replacing common symbol %s from %s with true definition from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + } + break; + case kTentAndAbsolute: + // replace tentative with absolute (can't size check because absolutes have no size) + break; + case kExternAndReg: + // replace external atom with regular one + break; + case kExternAndWeak: + // replace external atom with weak one + break; + case kExternAndTent: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("replacing defintion of %s from dylib %s with common symbol from %s", + newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + } + break; + case kExternAndExtern: + throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kExternAndExternWeak: + // keep strong dylib atom, ignore weak one + useNew = false; + break; + case kExternAndAbsolute: + // replace external atom with absolute one + break; + case kExternWeakAndReg: + // replace existing weak external with regular + break; + case kExternWeakAndWeak: + // replace existing weak external with weak (let dyld decide at runtime which to use) + break; + case kExternWeakAndTent: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( fOwner.fOptions.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( fOwner.fOptions.warnCommons() ) + warning("replacing defintion of %s from dylib %s with common symbol from %s", + newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + } + break; + case kExternWeakAndExtern: + // replace existing weak external with external + break; + case kExternWeakAndExternWeak: + // keep existing external weak + useNew = false; + break; + case kExternWeakAndAbsolute: + // replace existing weak external with absolute + break; + case kAbsoluteAndReg: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + case kAbsoluteAndWeak: + // ignore new weak atom, because we already have a non-weak one + useNew = false; + break; + case kAbsoluteAndTent: + // ignore new tentative atom, because we already have a regular one + useNew = false; + break; + case kAbsoluteAndExtern: + // ignore external atom, because we already have a one + useNew = false; + break; + case kAbsoluteAndExternWeak: + // ignore external atom, because we already have a one + useNew = false; + break; + case kAbsoluteAndAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; + } + } + if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) { + warning("%s has different visibility (%s) in %s and (%s) in %s", + newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath()); + } + if ( useNew ) { + fTable[name] = &newAtom; + if ( existingAtom != NULL ) + fOwner.markDead(existingAtom); + } + else { + fOwner.markDead(&newAtom); + } + return useNew; +} + + + +ObjectFile::Atom* Linker::SymbolTable::find(const char* name) +{ + Mapper::iterator pos = fTable.find(name); + if ( pos != fTable.end() ) { + return pos->second; + } + return NULL; +} + + +void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector& undefines) +{ + for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { + if ( (it->second == NULL) || (andWeakDefintions && (it->second->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)) ) { + undefines.push_back(it->first); + } + } +} + + + +bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) +{ + if ( left == right ) + return false; + + // first sort by section order (which is already sorted by segment) + unsigned int leftSectionIndex = left->getSection()->getIndex(); + unsigned int rightSectionIndex = right->getSection()->getIndex(); + if ( leftSectionIndex != rightSectionIndex) + return (leftSectionIndex < rightSectionIndex); + + // if a -order_file is specified, then sorting is altered to sort those symbols first + if ( fOverriddenOrdinalMap != NULL ) { + std::map::iterator leftPos = fOverriddenOrdinalMap->find(left); + std::map::iterator rightPos = fOverriddenOrdinalMap->find(right); + std::map::iterator end = fOverriddenOrdinalMap->end(); + if ( leftPos != end ) { + if ( rightPos != end ) { + // both left and right are overridden, so compare overridden ordinals + return leftPos->second < rightPos->second; + } + else { + // left is overridden and right is not, so left < right + return true; + } + } + else { + if ( rightPos != end ) { + // right is overridden and left is not, so right < left + return false; + } + else { + // neither are overridden, do default sort + // fall into default sorting below + } + } + } + + // the __common section can have real or tentative definitions + // we want the real ones to sort before tentative ones + bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); + bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); + if ( leftIsTent != rightIsTent ) + return rightIsTent; + + // lastly sort by atom ordinal. this is already sorted by .o order + return left->getOrdinal() < right->getOrdinal(); +} + + +int main(int argc, const char* argv[]) +{ + const char* archName = NULL; + bool showArch = false; + bool archInferred = false; + try { + // create linker object given command line arguments + Linker ld(argc, argv); + + // save error message prefix + archName = ld.architectureName(); + archInferred = ld.isInferredArchitecture(); + showArch = ld.showArchitectureInErrors(); + + // open all input files + ld.createReaders(); + + // open output file + ld.createWriter(); + + // do linking + ld.link(); + } + catch (const char* msg) { + if ( archInferred ) + fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); + else if ( showArch ) + fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); + else + fprintf(stderr, "ld: %s\n", msg); + return 1; + } + + return 0; +} diff --git a/ld64/src/machochecker.cpp b/ld64/src/machochecker.cpp new file mode 100644 index 0000000..311809b --- /dev/null +++ b/ld64/src/machochecker.cpp @@ -0,0 +1,965 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +template +class MachOChecker +{ +public: + static bool validFile(const uint8_t* fileContent); + static MachOChecker* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) + { return new MachOChecker(fileContent, fileLength, path); } + virtual ~MachOChecker() {} + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + + MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); + void checkMachHeader(); + void checkLoadCommands(); + void checkSection(const macho_segment_command

* segCmd, const macho_section

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

* reloc); + void checkLocalReloation(const macho_relocation_info

* reloc); + pint_t relocBase(); + bool addressInWritableSegment(pint_t address); + + const char* fPath; + const macho_header

* fHeader; + uint32_t fLength; + const char* fStrings; + const char* fStringsEnd; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const macho_dysymtab_command

* fDynamicSymbolTable; + const uint32_t* fIndirectTable; + uint32_t fIndirectTableCount; + const macho_relocation_info

* fLocalRelocations; + uint32_t fLocalRelocationsCount; + const macho_relocation_info

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

* fFirstSegment; + const macho_segment_command

* fFirstWritableSegment; +}; + + + +template <> +bool MachOChecker::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 MachOChecker::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 MachOChecker::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_I386 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool MachOChecker::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_X86_64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool MachOChecker::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: + return true; + } + return false; +} + +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } + +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) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a mach-o file that can be checked"; + + fPath = strdup(path); + fHeader = (const macho_header

*)fileContent; + + // sanity check header + checkMachHeader(); + + // check load commands + checkLoadCommands(); + + checkIndirectSymbolTable(); + + checkRelocations(); + + checkSymbolTable(); +} + + +template +void MachOChecker::checkMachHeader() +{ + if ( (fHeader->sizeofcmds() + sizeof(macho_header

)) > fLength ) + throw "sizeofcmds in mach_header is larger than file"; + + uint32_t flags = fHeader->flags(); + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000; + if ( flags & invalidBits ) + throw "invalid bits in mach_header flags"; + if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) + throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs"; +} + +template +void MachOChecker::checkLoadCommands() +{ + // check that all load commands fit within the load command space file + const macho_encryption_info_command

* encryption_info = 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(); + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & this->loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > endOfLoadCommands ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + switch ( cmd->cmd() ) { + case macho_segment_command

::CMD: + case LC_SYMTAB: + case LC_UNIXTHREAD: + case LC_DYSYMTAB: + case LC_LOAD_DYLIB: + case LC_ID_DYLIB: + case LC_LOAD_DYLINKER: + case LC_ID_DYLINKER: + case macho_routines_command

::CMD: + case LC_SUB_FRAMEWORK: + case LC_SUB_CLIENT: + case LC_TWOLEVEL_HINTS: + case LC_PREBIND_CKSUM: + case LC_LOAD_WEAK_DYLIB: + case LC_LAZY_LOAD_DYLIB: + case LC_UUID: + case LC_REEXPORT_DYLIB: + case LC_SEGMENT_SPLIT_INFO: + case LC_CODE_SIGNATURE: + break; + case LC_ENCRYPTION_INFO: + encryption_info = (macho_encryption_info_command

*)cmd; + break; + case LC_SUB_UMBRELLA: + case LC_SUB_LIBRARY: + if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS ) + throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; + break; + default: + throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd()); + } + cmd = (const macho_load_command

*)endOfCmd; + } + + // check segments + cmd = cmds; + std::vector > segmentAddressRanges; + std::vector > segmentFileOffsetRanges; + const macho_segment_command

* linkEditSegment = 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; + if ( segCmd->cmdsize() != (sizeof(macho_segment_command

) + segCmd->nsects() * sizeof(macho_section_content

)) ) + throw "invalid segment load command size"; + + // see if this overlaps another segment address range + uint64_t startAddr = segCmd->vmaddr(); + uint64_t endAddr = startAddr + segCmd->vmsize(); + for (typename std::vector >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) { + if ( it->first < startAddr ) { + if ( it->second > startAddr ) + throw "overlapping segment vm addresses"; + } + else if ( it->first > startAddr ) { + if ( it->first < endAddr ) + throw "overlapping segment vm addresses"; + } + else { + throw "overlapping segment vm addresses"; + } + segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); + } + // see if this overlaps another segment file offset range + uint64_t startOffset = segCmd->fileoff(); + uint64_t endOffset = startOffset + segCmd->filesize(); + for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) { + if ( it->first < startOffset ) { + if ( it->second > startOffset ) + throw "overlapping segment file data"; + } + else if ( it->first > startOffset ) { + if ( it->first < endOffset ) + throw "overlapping segment file data"; + } + else { + throw "overlapping segment file data"; + } + segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); + // check is within file bounds + if ( (startOffset > fLength) || (endOffset > fLength) ) + throw "segment file data is past end of file"; + } + // verify it fits in file + if ( startOffset > fLength ) + throw "segment fileoff does not fit in file"; + if ( endOffset > fLength ) + throw "segment fileoff+filesize does not fit in file"; + + // keep LINKEDIT segment + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + linkEditSegment = segCmd; + + // cache interesting segments + if ( fFirstSegment == NULL ) + fFirstSegment = segCmd; + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { + if ( fFirstWritableSegment == NULL ) + fFirstWritableSegment = segCmd; + if ( segCmd->vmaddr() > 0x100000000ULL ) + fWriteableSegmentWithAddrOver4G = true; + } + + // check section ranges + const macho_section

* 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 + if ( sect->addr() < startAddr ) + throwf("section %s vm address not within segment", sect->sectname()); + if ( (sect->addr()+sect->size()) > endAddr ) + throwf("section %s vm address not within segment", sect->sectname()); + if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) { + if ( sect->offset() < startOffset ) + throwf("section %s file offset not within segment", sect->sectname()); + if ( (sect->offset()+sect->size()) > endOffset ) + throwf("section %s file offset not within segment", sect->sectname()); + } + checkSection(segCmd, sect); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // verify there was a LINKEDIT segment + if ( linkEditSegment == NULL ) + throw "no __LINKEDIT segment"; + + // checks for executables + bool isStaticExecutable = false; + if ( fHeader->filetype() == MH_EXECUTE ) { + isStaticExecutable = true; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_LOAD_DYLINKER: + // the existence of a dyld load command makes a executable dynamic + isStaticExecutable = false; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( isStaticExecutable ) { + if ( fHeader->flags() != MH_NOUNDEFS ) + throw "invalid bits in mach_header flags for static executable"; + } + } + + // verify encryption info + if ( encryption_info != NULL ) { + if ( fHeader->filetype() != MH_EXECUTE ) + throw "LC_ENCRYPTION_INFO load command is only legal in main executables"; + if ( encryption_info->cryptoff() < (sizeof(macho_header

) + fHeader->sizeofcmds()) ) + throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands"; + if ( (encryption_info->cryptoff() % 4096) != 0 ) + throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned"; + if ( (encryption_info->cryptsize() % 4096) != 0 ) + throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized"; + for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); + it != segmentFileOffsetRanges.end(); ++it) { + if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) { + if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second ) + throw "LC_ENCRYPTION_INFO load command is not contained within one segment"; + } + } + } + + // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO + cmd = cmds; + bool foundDynamicSymTab = false; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); + if ( symtab->symoff() < linkEditSegment->fileoff() ) + throw "symbol table not in __LINKEDIT"; + if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist

*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "symbol table end not in __LINKEDIT"; + if ( (symtab->symoff() % sizeof(pint_t)) != 0 ) + throw "symbol table start not pointer aligned"; + fStrings = (char*)fHeader + symtab->stroff(); + fStringsEnd = fStrings + symtab->strsize(); + if ( symtab->stroff() < linkEditSegment->fileoff() ) + throw "string pool not in __LINKEDIT"; + if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "string pool extends beyond __LINKEDIT"; + if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed + throw "string pool start not pointer aligned"; + if ( (symtab->strsize() % sizeof(pint_t)) != 0 ) + throw "string pool size not a multiple of pointer size"; + } + break; + case LC_DYSYMTAB: + { + if ( isStaticExecutable ) + throw "LC_DYSYMTAB should not be used in static executable"; + foundDynamicSymTab = true; + fDynamicSymbolTable = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); + fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); + if ( fIndirectTableCount != 0 ) { + if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() ) + throw "indirect symbol table not in __LINKEDIT"; + if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "indirect symbol table not in __LINKEDIT"; + if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 ) + throw "indirect symbol table not pointer aligned"; + } + fLocalRelocationsCount = fDynamicSymbolTable->nlocrel(); + if ( fLocalRelocationsCount != 0 ) { + fLocalRelocations = (const macho_relocation_info

*)((char*)fHeader + fDynamicSymbolTable->locreloff()); + if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() ) + throw "local relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "local relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 ) + throw "local relocations table not pointer aligned"; + } + fExternalRelocationsCount = fDynamicSymbolTable->nextrel(); + if ( fExternalRelocationsCount != 0 ) { + fExternalRelocations = (const macho_relocation_info

*)((char*)fHeader + fDynamicSymbolTable->extreloff()); + if ( fDynamicSymbolTable->extreloff() < linkEditSegment->fileoff() ) + throw "external relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "external relocations not in __LINKEDIT"; + if ( (fDynamicSymbolTable->extreloff() % sizeof(pint_t)) != 0 ) + throw "external relocations table not pointer aligned"; + } + } + break; + case LC_SEGMENT_SPLIT_INFO: + { + if ( isStaticExecutable ) + throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; + const macho_linkedit_data_command

* info = (struct macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "split seg info not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "split seg info not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "split seg info table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "split seg info size not a multiple of pointer size"; + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( !isStaticExecutable && !foundDynamicSymTab ) + throw "missing dynamic symbol table"; + if ( fStrings == NULL ) + throw "missing symbol table"; + +} + +template +void MachOChecker::checkSection(const macho_segment_command

* segCmd, const macho_section

* sect) +{ + uint8_t sectionType = (sect->flags() & SECTION_TYPE); + if ( sectionType == S_ZEROFILL ) { + if ( sect->offset() != 0 ) + throwf("section offset should be zero for zero-fill section %s", sect->sectname()); + } + + // more section tests here +} + +template +void MachOChecker::checkIndirectSymbolTable() +{ + 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 start = 0; + uint32_t elementSize = 0; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + elementSize = sect->reserved2(); + start = sect->reserved1(); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + elementSize = sizeof(pint_t); + start = sect->reserved1(); + break; + } + if ( elementSize != 0 ) { + uint32_t count = sect->size() / elementSize; + if ( (count*elementSize) != sect->size() ) + throwf("%s section size is not an even multiple of element size", sect->sectname()); + if ( (start+count) > fIndirectTableCount ) + throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount ); + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void MachOChecker::checkSymbolTable() +{ + // verify no duplicate external symbol names + if ( fDynamicSymbolTable != NULL ) { + StringSet externalNames; + const macho_nlist

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

* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()]; + for(const macho_nlist

* p = exportedStart; p < exportedEnd; ++p) { + const char* symName = &fStrings[p->n_strx()]; + if ( externalNames.find(symName) != externalNames.end() ) + throwf("duplicate external symbol: %s", symName); + externalNames.insert(symName); + } + } +} + + +template <> +ppc::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +ppc64::P::uint_t MachOChecker::relocBase() +{ + if ( fWriteableSegmentWithAddrOver4G ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86_64::P::uint_t MachOChecker::relocBase() +{ + // check for split-seg + return fFirstWritableSegment->vmaddr(); +} + +template <> +arm::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + + +template +bool MachOChecker::addressInWritableSegment(pint_t address) +{ + 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; + if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) { + // if segment is writable, we are fine + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) + return true; + // could be a text reloc, make sure section bit is set + const macho_section

* 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 ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) { + // found section for this address, if has relocs we are fine + return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 ); + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return false; +} + + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 2 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 2 ) + throw "bad external relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad external relocation length"; + if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "exernal relocation address not in writable segment"; + // FIX: check r_symbol +} + +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 2 ) + throw "bad external relocation length"; + if ( reloc->r_type() != ARM_RELOC_VANILLA ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad external relocation pc_rel"; + if ( reloc->r_extern() == 0 ) + throw "local relocation found with external relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "external relocation address not in writable segment"; + // FIX: check r_symbol +} + + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_address() & R_SCATTERED ) { + // scattered + const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; + // FIX + + } + else { + // ignore pair relocs + if ( reloc->r_type() == PPC_RELOC_PAIR ) + return; + // FIX + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); + } +} + + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + // FIX +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_address() & R_SCATTERED ) { + // scattered + const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_length() != 2 ) + throw "bad local scattered relocation length"; + if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR ) + throw "bad local scattered relocation type"; + } + else { + if ( reloc->r_length() != 2 ) + throw "bad local relocation length"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; + } +} + +template +void MachOChecker::checkRelocations() +{ + // external relocations should be sorted to minimize dyld symbol lookups + // therefore every reloc with the same r_symbolnum value should be contiguous + std::set previouslySeenSymbolIndexes; + uint32_t lastSymbolIndex = 0xFFFFFFFF; + const macho_relocation_info

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

* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { + this->checkExternalReloation(reloc); + if ( reloc->r_symbolnum() != lastSymbolIndex ) { + if ( previouslySeenSymbolIndexes.count(reloc->r_symbolnum()) != 0 ) + throw "external relocations not sorted"; + previouslySeenSymbolIndexes.insert(lastSymbolIndex); + lastSymbolIndex = reloc->r_symbolnum(); + } + } + + const macho_relocation_info

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

* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { + this->checkLocalReloation(reloc); + } +} + + +static void check(const char* path) +{ + struct stat stat_buf; + + try { + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throw "cannot open file"; + ::fstat(fd, &stat_buf); + uint32_t length = stat_buf.st_size; + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == ((uint8_t*)(-1)) ) + throw "cannot map file"; + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + size_t offset = OSSwapBigToHostInt32(archs[i].offset); + size_t size = OSSwapBigToHostInt32(archs[i].size); + unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); + + switch(cputype) { + case CPU_TYPE_POWERPC: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, ppc slice does not contain ppc mach-o"; + break; + case CPU_TYPE_I386: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, i386 slice does not contain i386 mach-o"; + break; + case CPU_TYPE_POWERPC64: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; + break; + case CPU_TYPE_X86_64: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; + break; + case CPU_TYPE_ARM: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path); + else + throw "in universal file, arm slice does not contain arm mach-o"; + break; + default: + throwf("in universal file, unknown architecture slice 0x%x\n", cputype); + } + } + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } + else { + throw "not a known file type"; + } + } + catch (const char* msg) { + throwf("%s in %s", msg, path); + } +} + + +int main(int argc, const char* argv[]) +{ + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-no_content") == 0 ) { + + } + else { + throwf("unknown option: %s\n", arg); + } + } + else { + check(arg); + } + } + } + catch (const char* msg) { + fprintf(stderr, "machocheck failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/ld64/src/rebase.cpp b/ld64/src/rebase.cpp new file mode 100644 index 0000000..ad9b905 --- /dev/null +++ b/ld64/src/rebase.cpp @@ -0,0 +1,945 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + +static bool verbose = false; + +__attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +class AbstractRebaser +{ +public: + virtual cpu_type_t getArchitecture() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual void setBaseAddress(uint64_t) = 0; +}; + + +template +class Rebaser : public AbstractRebaser +{ +public: + Rebaser(const void* machHeader); + virtual ~Rebaser() {} + + virtual cpu_type_t getArchitecture() const; + virtual uint64_t getBaseAddress() const; + virtual uint64_t getVMSize() const; + virtual void setBaseAddress(uint64_t); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; }; + + void setRelocBase(); + void buildSectionTable(); + void adjustLoadCommands(); + void adjustSymbolTable(); + void adjustDATA(); + void doLocalRelocation(const macho_relocation_info

* reloc); + pint_t* mappedAddressForVMAddress(uint32_t vmaddress); + + const macho_header

* fHeader; + pint_t fOrignalVMRelocBaseAddress; + pint_t fSlide; + pint_t fRelocBase; + std::vector fVMMApping; +}; + + + +class MultiArchRebaser +{ +public: + MultiArchRebaser(const char* path, bool writable=false); + ~MultiArchRebaser(); + + const std::vector& getArchs() const { return fRebasers; } + void commit(); + +private: + std::vector fRebasers; + void* fMappingAddress; + uint64_t fFileSize; +}; + + + +MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) + : fMappingAddress(0), fFileSize(0) +{ + // map in whole file + int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small %s", path); + const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file %s, errno=%d", path, errno); + ::close(fd); + + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); + try { + switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { + case CPU_TYPE_POWERPC: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_POWERPC64: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_I386: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_X86_64: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + case CPU_TYPE_ARM: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; + default: + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + } + else { + try { + if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { + fRebasers.push_back(new Rebaser(mh)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { + fRebasers.push_back(new Rebaser(mh)); + } + else { + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + + fMappingAddress = p; + fFileSize = stat_buf.st_size; +} + + +MultiArchRebaser::~MultiArchRebaser() +{ + ::munmap(fMappingAddress, fFileSize); +} + +void MultiArchRebaser::commit() +{ + ::msync(fMappingAddress, fFileSize, MS_ASYNC); +} + + + +template +Rebaser::Rebaser(const void* machHeader) + : fHeader((const macho_header

*)machHeader) +{ + switch ( fHeader->filetype() ) { + case MH_DYLIB: + if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 ) + throw "split-seg dylibs cannot be rebased"; + break; + case MH_BUNDLE: + break; + default: + throw "file is not a dylib or bundle"; + } + +} + +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM; } + +template +uint64_t Rebaser::getBaseAddress() const +{ + uint64_t lowestSegmentAddress = LLONG_MAX; + 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; + if ( segCmd->vmaddr() < lowestSegmentAddress ) { + lowestSegmentAddress = segCmd->vmaddr(); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return lowestSegmentAddress; +} + +template +uint64_t Rebaser::getVMSize() const +{ + const macho_segment_command

* highestSegmentCmd = NULL; + 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; + if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) { + highestSegmentCmd = segCmd; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096)); +} + + +template +void Rebaser::setBaseAddress(uint64_t addr) +{ + // calculate slide + fSlide = addr - this->getBaseAddress(); + + // compute base address for relocations + this->setRelocBase(); + + // build cache of section index to section + this->buildSectionTable(); + + // update load commands + this->adjustLoadCommands(); + + // update symbol table + this->adjustSymbolTable(); + + // update writable segments that have internal pointers + this->adjustDATA(); +} + +template +void Rebaser::adjustLoadCommands() +{ + const macho_segment_command

* highestSegmentCmd = NULL; + 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) { + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear timestamp so that any prebound clients are invalidated + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(1); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear expected timestamps so that this image will load with invalid prebinding + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(2); + } + break; + case macho_routines_command

::CMD: + // update -init command + { + struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; + routines->set_init_address(routines->init_address() + fSlide); + } + break; + case macho_segment_command

::CMD: + // update segment commands + { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + seg->set_vmaddr(seg->vmaddr() + fSlide); + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + fSlide); + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Rebaser::buildSectionTable() +{ + // build vector of sections + 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

* seg = (macho_segment_command

*)cmd; + vmmap mapping; + mapping.vmaddr = seg->vmaddr(); + mapping.vmsize = seg->vmsize(); + mapping.fileoff = seg->fileoff(); + fVMMApping.push_back(mapping); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Rebaser::adjustSymbolTable() +{ + const macho_dysymtab_command

* dysymtab = NULL; + macho_nlist

* symbolTable = NULL; + + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (macho_nlist

*)(((uint8_t*)fHeader) + symtab->symoff()); + } + break; + case LC_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all exports and slide their n_value + macho_nlist

* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()]; + for (macho_nlist

* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) == N_SECT ) + entry->set_n_value(entry->n_value() + fSlide); + } + + // walk all local symbols and slide their n_value + macho_nlist

* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; + for (macho_nlist

* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { + if ( entry->n_sect() != NO_SECT ) + entry->set_n_value(entry->n_value() + fSlide); + } + + // FIXME ¥¥¥ adjust dylib_module if it exists +} + +template +void Rebaser::adjustDATA() +{ + const macho_dysymtab_command

* dysymtab = NULL; + + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all local relocations and slide every pointer + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + dysymtab->locreloff()); + const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + this->doLocalRelocation(reloc); + } + + // walk non-lazy-pointers and slide the ones that are LOCAL + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); + for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + uint32_t pointerCount = sect->size() / sizeof(pint_t); + pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); + for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { + if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { + P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + +} + + +template +typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(uint32_t vmaddress) +{ + for(typename std::vector::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) { + //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize); + if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) { + return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader); + } + } + throwf("reloc address 0x%08X not found", vmaddress); +} + + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info* reloc) +{ + if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + else { + throw "invalid relocation type"; + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == ARM_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == ARM_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); + P::setP(*addr, P::getP(*addr) + fSlide); + } + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } +} + + +template +void Rebaser::setRelocBase() +{ + // reloc addresses are from the start of the mapped file (base address) + fRelocBase = (pint_t)fHeader; + fOrignalVMRelocBaseAddress = this->getBaseAddress(); + //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress); +} + +template <> +void Rebaser::setRelocBase() +{ + // reloc addresses either: + // 1) from the base address if no writable segment is > 4GB from base address + // 2) from start of first writable segment + const macho_load_command

* 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; + if ( segCmd->initprot() & VM_PROT_WRITE ) { + if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) { + // found writable segment with address > 4GB past base address + fRelocBase = segCmd->fileoff() + (pint_t)fHeader; + fOrignalVMRelocBaseAddress = segCmd->vmaddr(); + return; + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // just use base address + fRelocBase = (pint_t)fHeader; + fOrignalVMRelocBaseAddress = this->getBaseAddress(); +} + +template <> +void Rebaser::setRelocBase() +{ + // reloc addresses are always based from the start of the first writable segment + 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; + if ( segCmd->initprot() & VM_PROT_WRITE ) { + fRelocBase = segCmd->fileoff() + (pint_t)fHeader; + fOrignalVMRelocBaseAddress = segCmd->vmaddr(); + return; + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + throw "no writable segment"; +} + + +static void copyFile(const char* srcFile, const char* dstFile) +{ + // open files + int src = open(srcFile, O_RDONLY); + if ( src == -1 ) + throwf("can't open file %s, errno=%d", srcFile, errno); + struct stat stat_buf; + if ( fstat(src, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", srcFile, errno); + + // create new file with all same permissions to hold copy of dylib + ::unlink(dstFile); + int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); + if ( dst == -1 ) + throwf("can't create temp file %s, errnor=%d", dstFile, errno); + + // mark source as "don't cache" + (void)fcntl(src, F_NOCACHE, 1); + // we want to cache the dst because we are about to map it in and modify it + + // copy permission bits + if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 ) + throwf("can't chmod temp file %s, errno=%d", dstFile, errno); + if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1) + throwf("can't chown temp file %s, errno=%d", dstFile, errno); + + // copy contents + ssize_t len; + const uint32_t kBufferSize = 128*1024; + static uint8_t* buffer = NULL; + if ( buffer == NULL ) { + vm_address_t addr = 0; + if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS ) + buffer = (uint8_t*)addr; + else + throw "can't allcoate copy buffer"; + } + while ( (len = read(src, buffer, kBufferSize)) > 0 ) { + if ( write(dst, buffer, len) == -1 ) + throwf("write failure copying feil %s, errno=%d", dstFile, errno); + } + + // close files + int result1 = close(dst); + int result2 = close(src); + if ( (result1 != 0) || (result2 != 0) ) + throw "can't close file"; +} + + +// scan dylibs and collect size info +// calculate new base address for each dylib +// rebase each file +// copy to temp and mmap +// update content +// unmap/flush +// rename + +struct archInfo { + cpu_type_t arch; + uint64_t vmSize; + uint64_t orgBase; + uint64_t newBase; +}; + +struct fileInfo +{ + fileInfo(const char* p) : path(p) {} + + const char* path; + std::vector archs; +}; + +// +// add archInfos to fileInfo for every slice of a fat file +// for ppc, there may be duplicate architectures (with different sub-types) +// +static void setSizes(fileInfo& info, const std::set& onlyArchs) +{ + const MultiArchRebaser mar(info.path); + const std::vector& rebasers = mar.getArchs(); + for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { + AbstractRebaser* rebaser = *rit; + if ( rebaser->getArchitecture() == *ait ) { + archInfo ai; + ai.arch = *ait; + ai.vmSize = rebaser->getVMSize(); + ai.orgBase = rebaser->getBaseAddress(); + ai.newBase = 0; + //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize); + info.archs.push_back(ai); + } + } + } +} + +static const char* nameForArch(cpu_type_t arch) +{ + switch( arch ) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_POWERPC64: + return "ppca64"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + return "arm"; + } + return "unknown"; +} + +static void rebase(const fileInfo& info) +{ + // generate temp file name + char realFilePath[PATH_MAX]; + if ( realpath(info.path, realFilePath) == NULL ) { + throwf("realpath() failed on %s, errno=%d", info.path, errno); + } + const char* tempPath; + asprintf((char**)&tempPath, "%s_rebase", realFilePath); + + // copy whole file to temp file + copyFile(info.path, tempPath); + + try { + // rebase temp file + MultiArchRebaser mar(tempPath, true); + const std::vector& rebasers = mar.getArchs(); + for(std::vector::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) { + for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { + if ( (*rit)->getArchitecture() == fait->arch ) { + (*rit)->setBaseAddress(fait->newBase); + if ( verbose ) + printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path); + } + } + } + + // flush temp file out to disk + mar.commit(); + + // rename + int result = rename(tempPath, info.path); + if ( result != 0 ) { + throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno); + } + + // make sure every really gets out to disk + ::sync(); + } + catch (const char* msg) { + // delete temp file + ::unlink(tempPath); + + // throw exception with file name added + const char* newMsg; + asprintf((char**)&newMsg, "%s for file %s", msg, info.path); + throw newMsg; + } +} + +static uint64_t totalVMSize(cpu_type_t arch, std::vector& files) +{ + uint64_t totalSize = 0; + for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { + fileInfo& fi = *fit; + for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { + if ( fait->arch == arch ) + totalSize += fait->vmSize; + } + } + return totalSize; +} + +static uint64_t startAddress(cpu_type_t arch, std::vector& files, uint64_t lowAddress, uint64_t highAddress) +{ + if ( lowAddress != 0 ) + return lowAddress; + else if ( highAddress != 0 ) { + uint64_t totalSize = totalVMSize(arch, files); + if ( highAddress < totalSize ) + throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize); + return highAddress - totalSize; + } + else { + if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) { + // place dylibs below dyld + uint64_t topAddr = 0x8FE00000; + uint64_t totalSize = totalVMSize(arch, files); + if ( totalSize > topAddr ) + throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize); + return topAddr - totalSize; + } + else if ( arch == CPU_TYPE_POWERPC64 ) { + return 0x200000000ULL; + } + else if ( arch == CPU_TYPE_X86_64 ) { + return 0x200000000ULL; + } + else if ( arch == CPU_TYPE_ARM ) { + // place dylibs below dyld + uint64_t topAddr = 0x2FE00000; + uint64_t totalSize = totalVMSize(arch, files); + if ( totalSize > topAddr ) + throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize); + return topAddr - totalSize; + } + else + throw "unknown architecture"; + } +} + +static void usage() +{ + fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch ] files...\n"); +} + + +int main(int argc, const char* argv[]) +{ + std::vector files; + std::set onlyArchs; + uint64_t lowAddress = 0; + uint64_t highAddress = 0; + + try { + // parse command line options + char* endptr; + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-v") == 0 ) { + verbose = true; + } + else if ( strcmp(arg, "-low_address") == 0 ) { + lowAddress = strtoull(argv[++i], &endptr, 16); + } + else if ( strcmp(arg, "-high_address") == 0 ) { + highAddress = strtoull(argv[++i], &endptr, 16); + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = argv[++i]; + if ( strcmp(arch, "ppc") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC); + else if ( strcmp(arch, "ppc64") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC64); + else if ( strcmp(arch, "i386") == 0 ) + onlyArchs.insert(CPU_TYPE_I386); + else if ( strcmp(arch, "x86_64") == 0 ) + onlyArchs.insert(CPU_TYPE_X86_64); + else if ( strcmp(arch, "arm") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM); + else if ( strcmp(arch, "armv6") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM); + else + throwf("unknown architecture %s", arch); + } + else { + usage(); + throwf("unknown option: %s\n", arg); + } + } + else { + files.push_back(fileInfo(arg)); + } + } + + if ( files.size() == 0 ) + throw "no files specified"; + + // use all architectures if no restrictions specified + if ( onlyArchs.size() == 0 ) { + onlyArchs.insert(CPU_TYPE_POWERPC); + onlyArchs.insert(CPU_TYPE_POWERPC64); + onlyArchs.insert(CPU_TYPE_I386); + onlyArchs.insert(CPU_TYPE_X86_64); + onlyArchs.insert(CPU_TYPE_ARM); + } + + // scan files and collect sizes + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + setSizes(*it, onlyArchs); + } + + // assign new base address for each arch + for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + cpu_type_t arch = *ait; + uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress); + for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { + fileInfo& fi = *fit; + for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { + if ( fait->arch == arch ) { + fait->newBase = baseAddress; + baseAddress += fait->vmSize; + baseAddress = (baseAddress + 4095) & (-4096); // page align + } + } + } + } + + // rebase each file if it contains something rebaseable + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + fileInfo& fi = *it; + if ( fi.archs.size() > 0 ) + rebase(fi); + } + + } + catch (const char* msg) { + fprintf(stderr, "rebase failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/ld64/unit-tests/README b/ld64/unit-tests/README new file mode 100644 index 0000000..a0fd0a2 --- /dev/null +++ b/ld64/unit-tests/README @@ -0,0 +1,28 @@ + +The easy way to run all tests is within Xcode. Just select "unit-tests" as the target and click Build. + +When run from within Xcode, the just built linker will be used. If you cd into a test case and run it, the +installed linker (e.g. /usr/bin/ld) will be used. + +Each test case is a directory with a Makefile. The Makefile default target should do whatever work is necessary +to perform the test. If successful is should print "PASS xxx" where xxx is the name of the test case. Otherwise +it should print "FAIL xxx reason". If nothing is printed (for instance a tool crashed), the harness will +automatically print that it failed. The harness will always pass ARCH to the Makefile to specify which +architecture to test. The Makefile should also have a "clean" target which removes and generated files. + + +There are some utility functions for use in Makefiles for generating the PASS/FAIL strings: + + ${PASS_IFF} can be put in front of the last command in the make rule and it will print PASS + if the command returned 0 or FAIL otherwise. Example: + ${PASS_IFF} ${CC} foo.c -o foo + Will print PASS if and only if the compilation succeeded + + ${PASS_IFF_EMPTY} can have data piped into it. It prints PASS if there is no data, otherwise FAIL. + Example: + otool -hv foo.o | grep SUBSECTIONS_VIA_SYMBOLS | ${PASS_IFF_EMPTY} + Will print PASS if and only if the output of otool does not contain SUBSECTIONS_VIA_SYMBOLS + + + + diff --git a/ld64/unit-tests/bin/exit-non-zero-pass.pl b/ld64/unit-tests/bin/exit-non-zero-pass.pl new file mode 100755 index 0000000..fcc65eb --- /dev/null +++ b/ld64/unit-tests/bin/exit-non-zero-pass.pl @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_UNLESS} "test name" command +# + +use strict; + +my $string = shift @ARGV; +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $string\n"); +} +else +{ + printf("PASS $string\n"); +} + +exit 0; diff --git a/ld64/unit-tests/bin/fail-if-exit-non-zero.pl b/ld64/unit-tests/bin/fail-if-exit-non-zero.pl new file mode 100755 index 0000000..7fb54b2 --- /dev/null +++ b/ld64/unit-tests/bin/fail-if-exit-non-zero.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl -w + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(system(@ARGV) != 0) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +exit 0; diff --git a/ld64/unit-tests/bin/fail-if-exit-zero.pl b/ld64/unit-tests/bin/fail-if-exit-zero.pl new file mode 100755 index 0000000..7859888 --- /dev/null +++ b/ld64/unit-tests/bin/fail-if-exit-zero.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +exit 0; diff --git a/ld64/unit-tests/bin/fail-if-no-stdin.pl b/ld64/unit-tests/bin/fail-if-no-stdin.pl new file mode 100755 index 0000000..a44f5d3 --- /dev/null +++ b/ld64/unit-tests/bin/fail-if-no-stdin.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${FAIL_IF_EMPTY} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +exit 0; diff --git a/ld64/unit-tests/bin/fail-if-stdin.pl b/ld64/unit-tests/bin/fail-if-stdin.pl new file mode 100755 index 0000000..c0bf290 --- /dev/null +++ b/ld64/unit-tests/bin/fail-if-stdin.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${FAIL_IF_STDIN} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + exit 0; +} + +printf("FAIL $test_name\n"); +exit 1; diff --git a/ld64/unit-tests/bin/fail-iff-exit-zero.pl b/ld64/unit-tests/bin/fail-iff-exit-zero.pl new file mode 100755 index 0000000..6d49a25 --- /dev/null +++ b/ld64/unit-tests/bin/fail-iff-exit-zero.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${FALL_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/ld64/unit-tests/bin/make-recursive-newtest.pl b/ld64/unit-tests/bin/make-recursive-newtest.pl new file mode 100755 index 0000000..031a45d --- /dev/null +++ b/ld64/unit-tests/bin/make-recursive-newtest.pl @@ -0,0 +1,127 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; +$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, + 'makefile.newtest' => undef, + 'Makefile.newtest' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + push @$cmd, "-f"; + push @$cmd, $makefile; + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/ld64/unit-tests/bin/make-recursive.pl b/ld64/unit-tests/bin/make-recursive.pl new file mode 100755 index 0000000..f860985 --- /dev/null +++ b/ld64/unit-tests/bin/make-recursive.pl @@ -0,0 +1,123 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; +$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/ld64/unit-tests/bin/mkld b/ld64/unit-tests/bin/mkld new file mode 100755 index 0000000..33aacc1 --- /dev/null +++ b/ld64/unit-tests/bin/mkld @@ -0,0 +1,73 @@ +#!/bin/sh + +hide() +{ + $PROCTOR set_hidden $1 1 >/dev/null +} + +if [ -z "$1" ] + then echo "Usage: mkld HOST [ DBPATH ]" >&2 + exit 1 +fi + +if [ -z "$PROCTOR" ] + then PROCTOR=proctor +fi + +DBNAME="$2" +[ -z "$DBNAME" ] && DBNAME=ld +PROCTOR="$PROCTOR $1 $DBNAME" + +$PROCTOR tools gcc g++ objc obj-c++ libstdc++ ld ld ld_classic cctools +$PROCTOR sysattrs \ + ld64="ld64" \ + ld="ld (ld_classic)" \ + gcc="GCC" \ + cctools="cctools" \ + os="OS Build" \ + processor=Processor \ + platform=Platform \ + hostname="Hostname" \ + gcc_opts="gcc options" \ + g++_opts="g++ options" \ + objc_opts="objc options" \ + obj-c++_opts="obj-c++ options" \ + libstdc++_opts="libstdc++ options" \ + LANG="LANG environment variable" \ + LC_CTYPE="LC_CTYPE environment variable" \ + LC_MESSAGES="LC_MESSAGES environment variable" \ + LC_ALL="LC_ALL environment variable" \ + TMPDIR="TMPDIR environment variable" \ + GCC_EXEC_PREFIX="GCC_EXEC_PREFIX environment variable" \ + COMPILER_PATH="COMPILER_PATH environment variable" \ + LIBRARY_PATH="LIBRARY_PATH environment variable" \ + LANG="LANG environment variable" \ + CPATH="CPATH environment variable" \ + C_INCLUDE_PATH="C_INCLUDE_PATH environment variable" \ + CPLUS_INCLUDE_PATH="CPLUS_INCLUDE_PATH environment variable" \ + OBJC_INCLUDE_PATH="OBJC_INCLUDE_PATH environment variable" \ + DEPENDENCIES_OUTPUT="DEPENDENCIES_OUTPUT environment variable" \ + SUNPRO_DEPENDENCIES="SUNPRO_DEPENDENCIES environment variable" \ + +for TOOL in gcc g++ objc obj-c++ libstdc++ + do hide ${TOOL}_opts +done + +hide LANG +hide LC_CTYPE +hide LC_MESSAGES +hide LC_ALL +hide TMPDIR +hide GCC_EXEC_PREFIX +hide COMPILER_PATH +hide LIBRARY_PATH +hide LANG +hide CPATH +hide C_INCLUDE_PATH +hide CPLUS_INCLUDE_PATH +hide OBJC_INCLUDE_PATH +hide DEPENDENCIES_OUTPUT +hide SUNPRO_DEPENDENCIES + +$PROCTOR results PASS=1 XFAIL=1 KFAIL=1 FAIL=0 XPASS=0 KPASS=0 UNRESOLVED=0 TIMEDOUT=0 UNSUPPORTED=0 UNTESTED=0 +$PROCTOR severities logline NOTE WARNING ERROR diff --git a/ld64/unit-tests/bin/pass-iff-exit-non-zero.pl b/ld64/unit-tests/bin/pass-iff-exit-non-zero.pl new file mode 100755 index 0000000..beb76a9 --- /dev/null +++ b/ld64/unit-tests/bin/pass-iff-exit-non-zero.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +my $ret = system(@ARGV); +my $exit_value = $ret >> 8; +my $signal_num = $ret & 127; +my $dumped_core = $ret & 128; +my $crashed = $signal_num + $dumped_core; + +if(0 == $exit_value || 0 != $crashed) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/ld64/unit-tests/bin/pass-iff-exit-zero.pl b/ld64/unit-tests/bin/pass-iff-exit-zero.pl new file mode 100755 index 0000000..07854b5 --- /dev/null +++ b/ld64/unit-tests/bin/pass-iff-exit-zero.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 != system(@ARGV)) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/ld64/unit-tests/bin/pass-iff-no-stdin.pl b/ld64/unit-tests/bin/pass-iff-no-stdin.pl new file mode 100755 index 0000000..19b98b4 --- /dev/null +++ b/ld64/unit-tests/bin/pass-iff-no-stdin.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${PASS_IFF_EMPTY} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("PASS $test_name\n"); + exit 0; +} + +printf("FAIL $test_name\n"); +exit 1; diff --git a/ld64/unit-tests/bin/pass-iff-stdin.pl b/ld64/unit-tests/bin/pass-iff-stdin.pl new file mode 100755 index 0000000..d5ee99f --- /dev/null +++ b/ld64/unit-tests/bin/pass-iff-stdin.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# command | ${PASS_IFF_STDIN} +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if( eof STDIN ) +{ + printf("FAIL $test_name\n"); + exit 1 +} + +printf("PASS $test_name\n"); +exit 0; + diff --git a/ld64/unit-tests/bin/result-filter.pl b/ld64/unit-tests/bin/result-filter.pl new file mode 100755 index 0000000..8168e79 --- /dev/null +++ b/ld64/unit-tests/bin/result-filter.pl @@ -0,0 +1,131 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Find; +use Cwd; + +$Data::Dumper::Terse = 1; + +my $root = undef; +my $entry = ''; +my $pass_count = 0; +my $total_count = 0; + +# first match "root: " + +# a line starting with "cwd:" marks the beginning of a new test case +# call process_entry() on each test case +while(<>) +{ + if(m/^root:\s+(.*?)$/) + { + $root = $1; + } + elsif(m/^cwd:\s+(.*?)$/) + { + if(length($entry)) + { + &process_entry($root, $entry); + $entry = ''; + } + $entry .= $_; + } + else + { + $entry .= $_; + } +} +# don't forget last test case (no cwd: to mark end) +if(length($entry)) +{ + &process_entry($root, $entry); +} + +# show totals +my $percentage = $pass_count * 100 / $total_count; +printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; + + +sub process_entry +{ + my ($root, $lines) = @_; + + # build an associative array of keys to value(s) + my $lines_seq = [split /\n/, $lines]; + #print Dumper($lines_seq); + my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; + my $line; + foreach $line (@$lines_seq) + { + if($line =~ m/^(\w+):\s+(.*)$/) + { + my $key = $1; + my $val = $2; + if(!exists($$tbl{$key})) + { $$tbl{$key} = ''; } + + if($key eq 'stdout' || $key eq 'stderr') # if type is @array + { + push @{$$tbl{$key}}, $val; + } + else + { + $$tbl{$key} .= $val; + } + } + else + { + print "ERROR: $line"; + } + } + #print Dumper($tbl); + #return; + + my $test_name = $$tbl{cwd}; + if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) + { + $test_name = $1; + } + + #if make failed (exit was non-zero), mark this as a failure + if(0 ne $$tbl{exit}) + { + printf "%-40s FAIL Makefile failure\n", $test_name; + $total_count++; + return; + } + + #if there was any output to stderr, mark this as a failure + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + return; + } + + # scan all stdout looking for lines that start with PASS or FAIL + my $seen_result = 0; + foreach $line (@{$$tbl{stdout}}) + { + if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) + { + $total_count++; + if($line =~ m/^PASS.+/) + { + $pass_count++; + } + else + { + # only print failure lines + printf "%-40s %s\n", $test_name, $line; + } + $seen_result = 1; + } + } + if(!$seen_result) + { + printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; + $total_count++; + } +} diff --git a/ld64/unit-tests/bin/rm-stale-test-logs b/ld64/unit-tests/bin/rm-stale-test-logs new file mode 100755 index 0000000..936dec1 --- /dev/null +++ b/ld64/unit-tests/bin/rm-stale-test-logs @@ -0,0 +1,36 @@ +#!/bin/sh + +usage() { + echo Usage: $0 number-of-tests-logs-to-keep + echo where number-of-tests-logs-to-keep must be a non-zero integer + exit +} + +# Usage: if no arguments +[ -z "$1" ] && usage + +# Check if requesting 0 tests to remain! +[ "$1" -ne 0 ] + +# don't test directly--use the result value +# because the command can fail for badly formed integers +[ $? -ne 0 ] && usage + +# get the dir names of all tests in date order +ls -1dtr /tmp/proctor*>/tmp/all$$ 2>/dev/null + +# select the last few to keep +tail -$1 /tmp/all$$>/tmp/keep$$ + +# get a list of the others +DELLIST=`diff /tmp/all$$ /tmp/keep$$|grep '^<'|sed -e 's/^< //'` + +# any work to do? +if [ "$DELLIST" ] +then + echo rm -rf $DELLIST + rm -rf $DELLIST +fi + +# rm the temps +rm /tmp/all$$ /tmp/keep$$ diff --git a/ld64/unit-tests/clean-tests b/ld64/unit-tests/clean-tests new file mode 100755 index 0000000..4c38b4e --- /dev/null +++ b/ld64/unit-tests/clean-tests @@ -0,0 +1,63 @@ +find_makefile() +{ + local j + + MF="" + + if [ ! -d $1 ] + then + return 1 + fi + + for j in Makefile makefile Makefile.newtest makefile.newtest + do + [ -f $1/$j ] && MF=$j + done + + [ "$MF" ] && return 0 + return 1 +} + +find_path_to_test_dir() +{ + # FIND THE PATH TO THE TEST DIR + # SO THAT WE CAN ADD THE BIN DIR INTO + # THE SEARCH PATH + + # remember the top level execution dir + chmod +x "$0" # just in case + + #add path to $0 into search + savedir=$PWD + DIRNAME=`dirname $0` + [ -d "$DIRNAME" ] && cd "$DIRNAME" + PATH=$PATH:$PWD + cd "$savedir" + + chmod +x "$0" # just in case + EXECNAME=`which $0` + DIRNAME=`dirname "$EXECNAME"` + if [ -d "$DIRNAME" ] + then + TEST_HOME_DIR="$DIRNAME" + else + TEST_HOME_DIR="$savedir" # Give up and assume current dir + fi + + PATH="$PATH":"$TEST_HOME_DIR"/bin:"$savedir" +} + +find_path_to_test_dir + +cd "$TEST_HOME_DIR/test-cases" + +for i in * +do + [ -d "$i" ] && + ( + if find_makefile $i + then + make -C $i -f $MF -s -k clean >/dev/null 2>/dev/null + fi + ) +done diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile new file mode 100644 index 0000000..70e05f8 --- /dev/null +++ b/ld64/unit-tests/include/common.makefile @@ -0,0 +1,76 @@ +# stuff to include in every test Makefile + +SHELL = /bin/sh + +# set default to be host +ARCH ?= $(shell arch) + +# set default to be all +VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6" + +MYDIR=$(shell cd ../../bin;pwd) + +# if run within Xcode, add the just built tools to the command path +ifdef BUILT_PRODUCTS_DIR + PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH} +else + ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" + RELEASEDIR=$(shell cd ../../../build/Release;pwd) + PATH := ${RELEASEDIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${RELEASEDIR}:${MYDIR}:${COMPILER_PATH} + else + PATH := ${MYDIR}:${PATH}: + COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: + endif +endif +export PATH +export COMPILER_PATH + +LD = ld +OBJECTDUMP = ObjectDump +MACHOCHECK = machocheck +OTOOL = otool + +CC = gcc-4.0 -arch ${ARCH} +CCFLAGS = -Wall -std=c99 +ASMFLAGS = + +CXX = g++-4.0 -arch ${ARCH} +CXXFLAGS = -Wall + +ifeq ($(ARCH),armv6) + LDFLAGS := -syslibroot /Developer/SDKs/Purple + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +ifeq ($(ARCH),thumb) + LDFLAGS := -syslibroot /Developer/SDKs/Purple + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv6 + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +RM = rm +RMFLAGS = -rf + +# utilites for Makefiles +PASS_IFF = pass-iff-exit-zero.pl +PASS_IFF_SUCCESS = ${PASS_IFF} +PASS_IFF_EMPTY = pass-iff-no-stdin.pl +PASS_IFF_STDIN = pass-iff-stdin.pl +FAIL_IFF = fail-iff-exit-zero.pl +FAIL_IFF_SUCCESS = ${FAIL_IFF} +PASS_IFF_ERROR = pass-iff-exit-non-zero.pl +FAIL_IF_ERROR = fail-if-exit-non-zero.pl +FAIL_IF_SUCCESS = fail-if-exit-zero.pl +FAIL_IF_EMPTY = fail-if-no-stdin.pl +FAIL_IF_STDIN = fail-if-stdin.pl +PASS_IFF_GOOD_MACHO = ${PASS_IFF} ${MACHOCHECK} +FAIL_IF_BAD_MACHO = ${FAIL_IF_ERROR} ${MACHOCHECK} +FAIL_IF_BAD_OBJ = ${FAIL_IF_ERROR} ${OBJECTDUMP} >/dev/null diff --git a/ld64/unit-tests/include/test.h b/ld64/unit-tests/include/test.h new file mode 100644 index 0000000..faca714 --- /dev/null +++ b/ld64/unit-tests/include/test.h @@ -0,0 +1,35 @@ +#include +#include + +#define DEFINE_TEST_FUNC(name) \ + static \ + inline \ + void \ + name(const char *format, ...) \ + { \ + va_list args; \ + va_start(args, format); \ + common(stdout, #name, format, args); \ + va_end(args); \ + return; \ + } + +static +inline +void +common(FILE *file, const char *prefix, const char *format, va_list args) +{ + fprintf(file, "%s \"", prefix); + vfprintf(file, format, args); + fprintf(file, "\"\n"); // should check for trailing newline + return; +} + +DEFINE_TEST_FUNC(PASS); +DEFINE_TEST_FUNC(XPASS); +DEFINE_TEST_FUNC(FAIL); +DEFINE_TEST_FUNC(XFAIL); + +DEFINE_TEST_FUNC(UNTESTED); +DEFINE_TEST_FUNC(UNSUPPORTED); +DEFINE_TEST_FUNC(UNRESOLVED); diff --git a/ld64/unit-tests/proctor-run b/ld64/unit-tests/proctor-run new file mode 100755 index 0000000..e7bdea4 --- /dev/null +++ b/ld64/unit-tests/proctor-run @@ -0,0 +1,204 @@ +#!/bin/sh + +all_archs="ppc ppc64 i386 x86_64" + +sysattr() +{ + echo " " +} + +doresults() +{ + local ver + + echo "" + + echo " " + sysattr cctools "`as&1 |sed 's/.*cctools-//;s/,.*//'`" + sysattr hostname "`hostname`" + sysattr os "`uname -r`" + sysattr platform "`uname -m`" + sysattr ld64 "`ld64 -v 2>&1|sed 's/.*PROJECT://;s/ .*//'`" + sysattr ld "`ld_classic -v 2>&1|sed 's/.*cctools-//;s/ .*//'`" + sysattr gcc "`gcc --version|head -1`" + sysattr processor "`uname -p`" + sysattr LANG "$LANG" + sysattr LC_CTYPE "$LC_CTYPE" + sysattr LC_MESSAGES "$LC_MESSAGES" + sysattr LC_ALL "$LC_ALL" + sysattr TMPDIR "$TMPDIR" + sysattr GCC_EXEC_PREFIX "$GCC_EXEC_PREFIX" + sysattr COMPILER_PATH "$COMPILER_PATH" + sysattr LIBRARY_PATH "$LIBRARY_PATH" + sysattr LANG "$LANG" + sysattr CPATH "$CPATH" + sysattr C_INCLUDE_PATH "$C_INCLUDE_PATH" + sysattr CPLUS_INCLUDE_PATH "$CPLUS_INCLUDE_PATH" + sysattr OBJC_INCLUDE_PATH "$OBJC_INCLUDE_PATH" + sysattr DEPENDENCIES_OUTPUT "$DEPENDENCIES_OUTPUT" + sysattr SUNPRO_DEPENDENCIES "$SUNPRO_DEPENDENCIES" + echo " " + + echo "" + echo "" + echo " " + for i in $* + do + echo " " + cat $i + echo " " + done + + echo " " + echo "" + echo "" + echo "" + + #rm $* +} + +find_path_to_test_dir() +{ + # FIND THE PATH TO THE TEST DIR + # SO THAT WE CAN ADD THE BIN DIR INTO + # THE SEARCH PATH + + # remember the top level execution dir + chmod +x "$0" # just in case + + #add path to $0 into search + local savedir + savedir=$PWD + DIRNAME=`dirname $0` + [ -d "$DIRNAME" ] && cd "$DIRNAME" + PATH=$PATH:$PWD + cd "$savedir" + + chmod +x "$0" # just in case + EXECNAME=`which $0` + DIRNAME=`dirname "$EXECNAME"` + if [ -d "$DIRNAME" ] + then + TEST_HOME_DIR=`cd "$DIRNAME";pwd` + fi + + if [ -z "$TEST_HOME_DIR" ] + then + TEST_HOME_DIR="$savedir" # Give up and assume current dir + fi + + cd "$TEST_HOME_DIR" + cd ../build/Release + + PATH="$PWD":"$TEST_HOME_DIR/bin":"$PATH" + cd "$savedir" +} + +start_time=`date +%s` + +find_path_to_test_dir + +# Execute from the location of the script; or if not found the current loc +[ -d $TEST_HOME_DIR/test-cases ] && cd $TEST_HOME_DIR/test-cases || cd test-cases + +rm-stale-test-logs 3 >/dev/null & + +make -C ../src # make sure the binaries are available + +DATEFORMAT=`date +%F-%H%M | sed -e 's/ //'` +tmpdir=/tmp/proctor$DATEFORMAT + +if ! mkdir $tmpdir >/dev/null 2>/dev/null +then + rm -rf $tmpdir + mkdir $tmpdir +fi + + +linestart=0 +if [ x$1 = x-comment ] +then + shift + comment="$1" + shift +fi + +find_makefile() +{ + local j + + MF="" + + if [ ! -d $1 ] + then + return 1 + fi + + for j in Makefile makefile + do + [ -f $1/$j ] && MF=$j + done + + if [ "$NEWTEST" ] + then + for j in Makefile.newtest makefile.newtest + do + [ -f $1/$j ] && MF=$j + done + fi + + [ "$MF" ] && return 0 + return 1 +} + +one_test() +{ + echo cwd: $1 + echo cmd: $1 ARCH="$arch" + make -f "$MF" -C $1 ARCH="$arch" 2>$tmpdir/stderr >$tmpdir/stdout + result=$? + sed 's/^/stdout: /'<$tmpdir/stdout + sed 's/^/stderr: /'<$tmpdir/stderr + echo exit: $? +} + +if [ "$1" ] +then + i="$1" + for arch in $all_archs + do + rm -f $tmpdir/$arch + if find_makefile $i + then + one_test $i + fi + #fi | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch + linestart=`expr $linestart + 10000` + done | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch +else + for arch in $all_archs + do + rm -f $tmpdir/$arch + for i in * + do + if find_makefile $i + then + one_test $i + fi + done | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch + linestart=`expr $linestart + 10000` + done +fi + +(cd $tmpdir; doresults $all_archs)>$tmpdir/o.xml +../bin/xmlparser $tmpdir/o.xml >/dev/null +if [ $? = 0 ] +then + if ! proctor localhost ld import $tmpdir/o.xml + then + proctor database load failed! + fi +else + echo Test results not loaded: internal xml error! + exit 1 +fi diff --git a/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests new file mode 100755 index 0000000..e21c2b3 --- /dev/null +++ b/ld64/unit-tests/run-all-unit-tests @@ -0,0 +1,35 @@ +#!/bin/sh + +unset RC_TRACE_DYLIBS +unset RC_TRACE_ARCHIVES +unset LD_TRACE_DYLIBS +unset LD_TRACE_ARCHIVES + +export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +[ "$PROCTORRUN" ] && exec ../proctor-run + +all_archs="x86_64 armv6 thumb ppc ppc64 i386 " +valid_archs="x86_64 armv6 ppc ppc64 i386 " + +# clean first +../bin/make-recursive.pl clean > /dev/null + +mkdir /tmp/$$ +for arch in $all_archs +do + echo "" + echo " * * * Running all unit tests for architecture $arch * * *" + + # build architecture + [ "$NEWTEST" ] && NT=-newtest + + ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$valid_archs" | ../bin/result-filter.pl + + # clean up so svn is happy + ../bin/make-recursive.pl ARCH=$arch clean > /dev/null + + echo "" +done diff --git a/ld64/unit-tests/run-all-unit-tests-debug b/ld64/unit-tests/run-all-unit-tests-debug new file mode 100755 index 0000000..a239748 --- /dev/null +++ b/ld64/unit-tests/run-all-unit-tests-debug @@ -0,0 +1,26 @@ +#!/bin/sh + +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +[ "$PROCTORRUN" ] && exec ../proctor-run + +all_archs="ppc ppc64 i386 x86_64" + +mkdir /tmp/$$ +for arch in $all_archs +do + echo "" + echo " * * * Running all unit tests for architecture $arch * * *" + + # build architecture + [ "$NEWTEST" ] && NT=-newtest + + mkdir /tmp/$$ + ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$all_archs" | tee /tmp/$$/raw | ../bin/result-filter.pl | tee /tmp/$$/sum + + # clean up so svn is happy + ../bin/make-recursive.pl ARCH=$arch clean > /dev/null + + echo "" +done diff --git a/ld64/unit-tests/src/Makefile b/ld64/unit-tests/src/Makefile new file mode 100644 index 0000000..d3bdeab --- /dev/null +++ b/ld64/unit-tests/src/Makefile @@ -0,0 +1,9 @@ + +all: ../bin/results-to-xml ../bin/xmlparser + +../bin/results-to-xml: results-to-xml.cpp + g++ -g -O -Wall $< -o ../bin/results-to-xml + +../bin/xmlparser: + cd xmlparser; xcodebuild -alltargets + cp -p xmlparser/build/Release/xmlparser ../bin/. diff --git a/ld64/unit-tests/src/results-to-xml.cpp b/ld64/unit-tests/src/results-to-xml.cpp new file mode 100644 index 0000000..a305347 --- /dev/null +++ b/ld64/unit-tests/src/results-to-xml.cpp @@ -0,0 +1,260 @@ +#include +#include +#include + +using namespace std; + +#define NELEMENTS(a) (sizeof (a)/sizeof *(a)) + +#define NO_RESULT (-1) +#define PASS 1 +#define FAIL 0 + +#define DBG bhole + +void +bhole(...) +{ +} + +class line_category +{ +public: + const char *key; + char line[99999]; + void (*test)(line_category &); + int test_result; +} lc; + +void +deft(line_category &l) +{ + l.test_result = PASS; +} + +void +pass_fail(line_category &l) +{ + if(FAIL!=l.test_result) + l.test_result = strnstr(l.line, "FAIL", 4)? FAIL: PASS; +} + +void +stderr_output(line_category &l) +{ + if(FAIL==l.test_result) + return; + + if(l.line[0]) + l.test_result = FAIL; +} + +void +exit_test(line_category &l) +{ + if(!atoi(l.line)) { + DBG("exit_test(%s)==%d\n", l.line, atoi(l.line)); + l.test_result = PASS; + } +} + +#define STDOUT "stdout: " +#define STDERR "stderr: " +#define CWD "cwd: " +#define CMD "cmd: " +#define SEXIT "exit: " +line_category line_categories[] = { + { CWD, "" , deft, NO_RESULT}, /* must be first */ + { CMD, "", deft, NO_RESULT}, + { STDOUT, "", pass_fail, NO_RESULT}, + { STDERR, "", stderr_output, NO_RESULT }, + { SEXIT, "", exit_test, NO_RESULT }, +}; + +static line_category no_line_category = { "none", "no test", deft, NO_RESULT }; + +line_category & +retrieve(line_category &l, const char *s) +{ + unsigned j; + line_category *lp = &l; + //int final_result = PASS; + + for(j=0; jkey, s)) { + char *p; + + for(p=(char *)lp->line; *p; ++p) { + switch(*p) { + case '\0': + break; + case '"': + case '<': + *p = ' '; + // fall thru + default: + continue; + } + } +DBG("FOUND line_categories[j].line==%s\n", lp->line); + return line_categories[j]; + } + } + + return no_line_category; +} + +void +xml_string_print(FILE *strm, const char *s) +{ + fputc('"', strm); + for( ; ; ++s) { + switch(*s) { + case '\0': + break; + case '&': + fputs("&", strm); + continue; + default: + fputc(*s, strm); + continue; + } + break; + } + fputc('"', strm); +} + +// +// FAIL if stderr non-zero +// FAIL if stdout=="FAIL" +// UNRESOLVED if make exit non-zero +// PASS otherwise +// + +static int cnt; +void +dump_test(void) +{ + unsigned j; + int final_result = PASS; + + for(j=0; j\n"); + + printf(" \n"); + s = retrieve(line_categories[0], STDERR).line; + if(*s) { + printf(" \n"); + } +#if 1 + s = retrieve(line_categories[0], STDOUT).line; + if(*s) { + printf(" \n"); + } +#endif + printf(" \n"); + printf("\n\n"); + + for(j=0; j1) + cnt = atoi(argv[1]); + + for(;;) { + char line[99999]; + int i; + + line[0] = '\0'; + fgets(line, sizeof line, stdin); + if(feof(stdin)) { + dump_test(); + break; + } + + for(i=0; ; ++i) { + size_t len = strlen(line_categories[i].key); + + //DBG("strnstr(%s, %s, %u)\n", line, line_categories[i].key, len); + if(strnstr(line, line_categories[i].key, len)) { + if(firsttime) + firsttime = 0; + else if(0==i) + dump_test(); + + char *lp = &line[len]; + //DBG("%s%s", line_categories[i].key, lp); + strncpy(line_categories[i].line, lp, sizeof line_categories[i].line); + line_categories[i].test(line_categories[i]); + break; + } + + if(i==NELEMENTS(line_categories)-1) { + DBG("BADLINE:%s", line); + break; + } + } + } + return 0; +} diff --git a/ld64/unit-tests/src/xmlparser/xmlparser.1 b/ld64/unit-tests/src/xmlparser/xmlparser.1 new file mode 100644 index 0000000..6eab06a --- /dev/null +++ b/ld64/unit-tests/src/xmlparser/xmlparser.1 @@ -0,0 +1,79 @@ +.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. +.\"See Also: +.\"man mdoc.samples for a complete listing of options +.\"man mdoc for the short list of editing options +.\"/usr/share/misc/mdoc.template +.Dd 9/18/06 \" DATE +.Dt xmlparser 1 \" Program name and manual section number +.Os Darwin +.Sh NAME \" Section Header - required - don't modify +.Nm xmlparser, +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm Other_name_for_same_program(), +.Nm Yet another name for the same program. +.\" Use .Nm macro to designate other names for the documented program. +.Nd This line parsed for whatis database. +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm +.Op Fl abcd \" [-abcd] +.Op Fl a Ar path \" [-a path] +.Op Ar file \" [file] +.Op Ar \" [file ...] +.Ar arg0 \" Underlined argument - use .Ar anywhere to underline +arg2 ... \" Arguments +.Sh DESCRIPTION \" Section Header - required - don't modify +Use the .Nm macro to refer to your program throughout the man page like such: +.Nm +Underlining is accomplished with the .Ar macro like this: +.Ar underlined text . +.Pp \" Inserts a space +A list of items with descriptions: +.Bl -tag -width -indent \" Begins a tagged list +.It item a \" Each item preceded by .It macro +Description of item a +.It item b +Description of item b +.El \" Ends the list +.Pp +A list of flags and their descriptions: +.Bl -tag -width -indent \" Differs from above in tag removed +.It Fl a \"-a flag as a list item +Description of -a flag +.It Fl b +Description of -b flag +.El \" Ends the list +.Pp +.\" .Sh ENVIRONMENT \" May not be needed +.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 +.\" .It Ev ENV_VAR_1 +.\" Description of ENV_VAR_1 +.\" .It Ev ENV_VAR_2 +.\" Description of ENV_VAR_2 +.\" .El +.Sh FILES \" File used or created by the topic of the man page +.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact +.It Pa /usr/share/file_name +FILE_1 description +.It Pa /Users/joeuser/Library/really_long_file_name +FILE_2 description +.El \" Ends the list +.\" .Sh DIAGNOSTICS \" May not be needed +.\" .Bl -diag +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .El +.Sh SEE ALSO +.\" List links in ascending order by section, alphabetically within a section. +.\" Please do not reference files that do not exist without filing a bug report +.Xr a 1 , +.Xr b 1 , +.Xr c 1 , +.Xr a 2 , +.Xr b 2 , +.Xr a 3 , +.Xr b 3 +.\" .Sh BUGS \" Document known, unremedied bugs +.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/ld64/unit-tests/src/xmlparser/xmlparser.m b/ld64/unit-tests/src/xmlparser/xmlparser.m new file mode 100644 index 0000000..a9c82fc --- /dev/null +++ b/ld64/unit-tests/src/xmlparser/xmlparser.m @@ -0,0 +1,25 @@ +#import + +int main(int argc, char *argv[]) { + [[NSAutoreleasePool alloc] init]; + + if(argc != 2) { + NSLog(@"Usage: %s path-to-XML\n", argv[0]); + return 1; + } + NSString *path = [NSString stringWithUTF8String:argv[1]]; + + NSError *err = nil; + NSXMLDocument *doc = [[NSXMLDocument alloc] + initWithContentsOfURL:[NSURL + fileURLWithPath:path] + options:0 + error:&err]; + if(err) { + NSLog(@"ERROR: %@", err); + return 1; + } else { + NSLog(@"Parsed!"); + return 0; + } +} diff --git a/ld64/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj b/ld64/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9492293 --- /dev/null +++ b/ld64/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj @@ -0,0 +1,218 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 8DD76F9A0486AA7600D96B5E /* xmlparser.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* xmlparser.m */; settings = {ATTRIBUTES = (); }; }; + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; + 8DD76F9F0486AA7600D96B5E /* xmlparser.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* xmlparser.1 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 8DD76F9F0486AA7600D96B5E /* xmlparser.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 08FB7796FE84155DC02AAC07 /* xmlparser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = xmlparser.m; sourceTree = ""; }; + 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32A70AAB03705E1F00C91783 /* xmlparser_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmlparser_Prefix.pch; sourceTree = ""; }; + 8DD76FA10486AA7600D96B5E /* xmlparser */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xmlparser; sourceTree = BUILT_PRODUCTS_DIR; }; + C6859EA3029092ED04C91782 /* xmlparser.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = xmlparser.1; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* xmlparser */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + C6859EA2029092E104C91782 /* Documentation */, + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = xmlparser; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 32A70AAB03705E1F00C91783 /* xmlparser_Prefix.pch */, + 08FB7796FE84155DC02AAC07 /* xmlparser.m */, + ); + name = Source; + sourceTree = ""; + }; + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 08FB779EFE84155DC02AAC07 /* Foundation.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FA10486AA7600D96B5E /* xmlparser */, + ); + name = Products; + sourceTree = ""; + }; + C6859EA2029092E104C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + C6859EA3029092ED04C91782 /* xmlparser.1 */, + ); + name = Documentation; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F960486AA7600D96B5E /* xmlparser */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "xmlparser" */; + buildPhases = ( + 8DD76F990486AA7600D96B5E /* Sources */, + 8DD76F9B0486AA7600D96B5E /* Frameworks */, + 8DD76F9E0486AA7600D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = xmlparser; + productInstallPath = "$(HOME)/bin"; + productName = xmlparser; + productReference = 8DD76FA10486AA7600D96B5E /* xmlparser */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "xmlparser" */; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* xmlparser */; + projectDirPath = ""; + targets = ( + 8DD76F960486AA7600D96B5E /* xmlparser */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F990486AA7600D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9A0486AA7600D96B5E /* xmlparser.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB927508733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = xmlparser_Prefix.pch; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = xmlparser; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB927608733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = xmlparser_Prefix.pch; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = xmlparser; + }; + name = Release; + }; + 1DEB927908733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB927A08733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "xmlparser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927508733DD40010E9CD /* Debug */, + 1DEB927608733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "xmlparser" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927908733DD40010E9CD /* Debug */, + 1DEB927A08733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/ld64/unit-tests/src/xmlparser/xmlparser_Prefix.pch b/ld64/unit-tests/src/xmlparser/xmlparser_Prefix.pch new file mode 100644 index 0000000..530e057 --- /dev/null +++ b/ld64/unit-tests/src/xmlparser/xmlparser_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'xmlparser' target in the 'xmlparser' project. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/ld64/unit-tests/test-cases/16-byte-alignment/Makefile b/ld64/unit-tests/test-cases/16-byte-alignment/Makefile new file mode 100644 index 0000000..a3a256f --- /dev/null +++ b/ld64/unit-tests/test-cases/16-byte-alignment/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -O2 optimization fails when trying to make a long 16-byte aligned. +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o + + # verify that the alignment is correct in the .o + ObjectDump -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null + + # now verify the executable + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH} + ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai\>' >/dev/null" + ${PASS_IFF_GOOD_MACHO} tl_test2-${ARCH} + +clean: + rm -rf tl_test2-* diff --git a/ld64/unit-tests/test-cases/16-byte-alignment/comment.txt b/ld64/unit-tests/test-cases/16-byte-alignment/comment.txt new file mode 100644 index 0000000..eb9eaf5 --- /dev/null +++ b/ld64/unit-tests/test-cases/16-byte-alignment/comment.txt @@ -0,0 +1 @@ +Test 16 byte alignment with -O2 optimization. Radar #4662185 diff --git a/ld64/unit-tests/test-cases/16-byte-alignment/tl_test2.c b/ld64/unit-tests/test-cases/16-byte-alignment/tl_test2.c new file mode 100644 index 0000000..ff27fec --- /dev/null +++ b/ld64/unit-tests/test-cases/16-byte-alignment/tl_test2.c @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +int i = 1; +int ai __attribute__ ((aligned (16))) = 1; + +int main() { + + long addr = (long)&ai; + i = ai; + + if (addr & 0xf) { + printf("failed: ai = %p\n", (void *)addr); + return 1; + } + + printf("passed: ai = %p\n", (void *)addr); + return 0; + +} diff --git a/ld64/unit-tests/test-cases/absolute-symbol/Makefile b/ld64/unit-tests/test-cases/absolute-symbol/Makefile new file mode 100644 index 0000000..806c64e --- /dev/null +++ b/ld64/unit-tests/test-cases/absolute-symbol/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker processing of absolute symbols +# + +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 + nm -m all.o | grep _myAbs | grep absolute | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker + ${PASS_IFF_GOOD_MACHO} main2 + +clean: + rm -rf main.o abs.o all.o main main2 diff --git a/ld64/unit-tests/test-cases/absolute-symbol/abs.s b/ld64/unit-tests/test-cases/absolute-symbol/abs.s new file mode 100644 index 0000000..216867c --- /dev/null +++ b/ld64/unit-tests/test-cases/absolute-symbol/abs.s @@ -0,0 +1,3 @@ + + .globl _myAbs + .set _myAbs, 0xfe000000 diff --git a/ld64/unit-tests/test-cases/absolute-symbol/main.c b/ld64/unit-tests/test-cases/absolute-symbol/main.c new file mode 100644 index 0000000..eec3f61 --- /dev/null +++ b/ld64/unit-tests/test-cases/absolute-symbol/main.c @@ -0,0 +1,5 @@ + +extern int* myAbs; + +int main() { return *myAbs; } + diff --git a/ld64/unit-tests/test-cases/alias-command-line/Makefile b/ld64/unit-tests/test-cases/alias-command-line/Makefile new file mode 100644 index 0000000..9e87329 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-command-line/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that added aliases to a .o +# file via the command line is the same as doing so in the sources. +# The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} aliases.s -DALIASES=1 -c -o aliases.source.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.source.${ARCH}.o > aliases.source.${ARCH}.o.dump + + ${CC} ${ASMFLAGS} aliases.s -c -o aliases.tmp.${ARCH}.o + ${FAIL_IF_BAD_OBJ} aliases.tmp.${ARCH}.o + + ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -o aliases.cmdline.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.cmdline.${ARCH}.o > aliases.cmdline.${ARCH}.o.dump + ${FAIL_IF_ERROR} diff aliases.source.${ARCH}.o.dump aliases.cmdline.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.file.${ARCH}.o > aliases.file.${ARCH}.o.dump + ${PASS_IFF} diff aliases.source.${ARCH}.o.dump aliases.file.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/alias-command-line/aliases.s b/ld64/unit-tests/test-cases/alias-command-line/aliases.s new file mode 100644 index 0000000..ffab4a9 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-command-line/aliases.s @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .text + +_temp: nop + nop + + .globl _foo +_foo: nop + nop + +#if ALIASES + .globl _fooalt + .globl _fooalt2 +/* this should make an alias "_fooalt" for "_foo" */ +_fooalt = _foo +_fooalt2 = _foo +#endif + +_bar: nop + nop + + + .subsections_via_symbols \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/alias-command-line/aliases.txt b/ld64/unit-tests/test-cases/alias-command-line/aliases.txt new file mode 100644 index 0000000..291f2f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-command-line/aliases.txt @@ -0,0 +1,6 @@ +_foo _fooalt +# comment +_foo _fooalt2 + + + diff --git a/ld64/unit-tests/test-cases/alias-objects/Makefile b/ld64/unit-tests/test-cases/alias-objects/Makefile new file mode 100644 index 0000000..f4bfdc8 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-objects/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify a .o file with aliases +# can round trip through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} aliases.s -c -o aliases.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.${ARCH}.o -o aliases-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.${ARCH}.o > aliases.${ARCH}.o.dump + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.${ARCH}.o > aliases-r.${ARCH}.o.dump + ${PASS_IFF} diff aliases.${ARCH}.o.dump aliases-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/alias-objects/aliases.s b/ld64/unit-tests/test-cases/alias-objects/aliases.s new file mode 100644 index 0000000..b21f8c7 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-objects/aliases.s @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .text + +_temp: nop + nop + + .globl _foo +_foo: nop + nop + + .globl _fooalt + .globl _fooalt2 +/* this should make an alias "_fooalt" for "_foo" */ +_fooalt = _foo +_fooalt2 = _foo + +_bar: nop + nop + + + .subsections_via_symbols \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/align-modulus/Makefile b/ld64/unit-tests/test-cases/align-modulus/Makefile new file mode 100644 index 0000000..6b6a50f --- /dev/null +++ b/ld64/unit-tests/test-cases/align-modulus/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that the modules of +# symbol _b is maintained. The address for _b must be +# 3 mod 16. Therefore the last hexdigit of the address +# must be 3. + +run: all + +all: + ${CC} ${ASMFLAGS} -dynamiclib -single_module -dead_strip foo.c align.s -exported_symbols_list foo.exp -o foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + nm foo.${ARCH}.dylib | grep "3 d _b" | ${PASS_IFF_STDIN} + +clean: + rm -rf *.dylib diff --git a/ld64/unit-tests/test-cases/align-modulus/align.s b/ld64/unit-tests/test-cases/align-modulus/align.s new file mode 100644 index 0000000..a288960 --- /dev/null +++ b/ld64/unit-tests/test-cases/align-modulus/align.s @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .data + .align 0 +_a: .byte 3 + .byte 3 + .byte 3 + .globl _b +_b: .byte 4 ;# address is 3 + .align 4 +L1: .quad 0 +_c: .long 8 + + .subsections_via_symbols + diff --git a/ld64/unit-tests/test-cases/align-modulus/comment.txt b/ld64/unit-tests/test-cases/align-modulus/comment.txt new file mode 100644 index 0000000..240c171 --- /dev/null +++ b/ld64/unit-tests/test-cases/align-modulus/comment.txt @@ -0,0 +1,2 @@ +The point of this test is to verify that the modules of symbol _b is maintained. The address for _b must be +3 mod 16. Therefore the last hexdigit of the address must be 3. diff --git a/ld64/unit-tests/test-cases/align-modulus/foo.c b/ld64/unit-tests/test-cases/align-modulus/foo.c new file mode 100644 index 0000000..0b88dff --- /dev/null +++ b/ld64/unit-tests/test-cases/align-modulus/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern char b; +int my = 2; + +char foo() +{ + return my+b; +} + + diff --git a/ld64/unit-tests/test-cases/align-modulus/foo.exp b/ld64/unit-tests/test-cases/align-modulus/foo.exp new file mode 100644 index 0000000..70eefe9 --- /dev/null +++ b/ld64/unit-tests/test-cases/align-modulus/foo.exp @@ -0,0 +1 @@ +_foo diff --git a/ld64/unit-tests/test-cases/allow-stack-execute/Makefile b/ld64/unit-tests/test-cases/allow-stack-execute/Makefile new file mode 100644 index 0000000..6e0be3f --- /dev/null +++ b/ld64/unit-tests/test-cases/allow-stack-execute/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set the stack execution bit properly. +# + +run: all + +all: + +# Test with the flag + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -Wl,-allow_stack_execute + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${FAIL_IF_EMPTY} + rm -f foo-${ARCH} + +# Test without the flag + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} + ${FAIL_IF_BAD_MACHO} foo-${ARCH} + ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${PASS_IFF_EMPTY} + +clean: + rm -rf foo-* diff --git a/ld64/unit-tests/test-cases/allow-stack-execute/comment.txt b/ld64/unit-tests/test-cases/allow-stack-execute/comment.txt new file mode 100644 index 0000000..525ab2b --- /dev/null +++ b/ld64/unit-tests/test-cases/allow-stack-execute/comment.txt @@ -0,0 +1 @@ +Test the we set the stack execution bit properly. diff --git a/ld64/unit-tests/test-cases/allow-stack-execute/foo.c b/ld64/unit-tests/test-cases/allow-stack-execute/foo.c new file mode 100644 index 0000000..57ed6ba --- /dev/null +++ b/ld64/unit-tests/test-cases/allow-stack-execute/foo.c @@ -0,0 +1,4 @@ +int main (void) +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/allowable-client/Makefile b/ld64/unit-tests/test-cases/allowable-client/Makefile new file mode 100644 index 0000000..ef293a6 --- /dev/null +++ b/ld64/unit-tests/test-cases/allowable-client/Makefile @@ -0,0 +1,110 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that the -allowable_client and -client options +# work when linking against subframeworks. +# + +run: all + +all: +# build with two allowable_clients + ${CC} ${CCFLAGS} -dynamiclib -umbrella foo -allowable_client bar -allowable_client baz foo.c -o foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + +# test that -o works + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libbar.${ARCH}.dylib + +# test that a framework style output works + mkdir -p bar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.framework/bar foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} bar.framework/bar + +# test that second -o works + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib + +# test that -o and -install_name works with install_name as an allowable + ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar.${ARCH}.dylib foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib + +# test that -install_name works with variant name + ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar_profile foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib + +# test that -o and -install_name fails with install_name different than allowable + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.${ARCH}.dylib -install_name /tmp/fail.${ARCH}.dylib foo.${ARCH}.dylib >& fail.log + +# test that a bundle and no client_name fails + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -bundle bar.c -o temp.${ARCH}.bundle foo.${ARCH}.dylib >& fail.log + +# test that a bundle and an allowable client_name passes + ${CC} ${CCFLAGS} -bundle bar.c -client_name bar -o bar.${ARCH}.bundle foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} bar.${ARCH}.bundle + +# test umbrella can link against subs + mkdir -p foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo + ${FAIL_IF_BAD_MACHO} foo.framework/foo + +# test umbrella variant can link against subs + mkdir -p foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo_debug -install_name /path/foo.framework/foo_debug + ${FAIL_IF_BAD_MACHO} foo.framework/foo_debug + +# test sibling in umbrella can link against subs + ${CC} ${CCFLAGS} -dynamiclib main.c -umbrella foo foo.${ARCH}.dylib -o ./main.dylib + ${FAIL_IF_BAD_MACHO} main.dylib + +# test anyone can link against umbrella + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -framework foo -F. + ${FAIL_IF_BAD_MACHO} main.${ARCH} + +# test that an executable and no client_name fails + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} foo.${ARCH}.dylib >& fail.log + +# test that an executable and an allowable client_name passes + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name bar foo.${ARCH}.dylib + ${PASS_IFF_GOOD_MACHO} main.${ARCH} + +# test that a regular dylib can be made unlinkable by using -allowable_client + ${CC} ${CCFLAGS} -dynamiclib foo.c -allowable_client '!' -o unlinkable_foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} unlinkable_foo.${ARCH}.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} unlinkable_foo.${ARCH}.dylib >& fail.log + +# test that a regular dylib can be made linkable by only specially named clients + ${CC} ${CCFLAGS} -dynamiclib foo.c -allowable_client special -o restrictive_foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} restrictive_foo.${ARCH}.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} restrictive_foo.${ARCH}.dylib >& fail.log + ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name special restrictive_foo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} main.${ARCH} + +# print final pass + ${PASS_IFF_GOOD_MACHO} foo.${ARCH}.dylib + +clean: + rm -rf *.dylib *.bundle main.???* fail.log *.framework diff --git a/ld64/unit-tests/test-cases/allowable-client/bar.c b/ld64/unit-tests/test-cases/allowable-client/bar.c new file mode 100644 index 0000000..dbaeef8 --- /dev/null +++ b/ld64/unit-tests/test-cases/allowable-client/bar.c @@ -0,0 +1,6 @@ +extern int foo (); + +int bar (void) +{ + return foo(); +} diff --git a/ld64/unit-tests/test-cases/allowable-client/baz.c b/ld64/unit-tests/test-cases/allowable-client/baz.c new file mode 100644 index 0000000..dbaeef8 --- /dev/null +++ b/ld64/unit-tests/test-cases/allowable-client/baz.c @@ -0,0 +1,6 @@ +extern int foo (); + +int bar (void) +{ + return foo(); +} diff --git a/ld64/unit-tests/test-cases/allowable-client/comment.txt b/ld64/unit-tests/test-cases/allowable-client/comment.txt new file mode 100644 index 0000000..0202b5f --- /dev/null +++ b/ld64/unit-tests/test-cases/allowable-client/comment.txt @@ -0,0 +1 @@ +Test that the -allowable_client and -client options work when linking against subframeworks. diff --git a/ld64/unit-tests/test-cases/allowable-client/foo.c b/ld64/unit-tests/test-cases/allowable-client/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/allowable-client/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/allowable-client/main.c b/ld64/unit-tests/test-cases/allowable-client/main.c new file mode 100644 index 0000000..7b76173 --- /dev/null +++ b/ld64/unit-tests/test-cases/allowable-client/main.c @@ -0,0 +1,6 @@ +extern int foo (); + +int main (void) +{ + return foo(); +} diff --git a/ld64/unit-tests/test-cases/archive-ObjC/Makefile b/ld64/unit-tests/test-cases/archive-ObjC/Makefile new file mode 100644 index 0000000..892043d --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that -ObjC loads all (and only) +# .o files that contain Objective-C code. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + ${CC} ${CCFLAGS} baz.m -c -o baz-${ARCH}.o + ${FAIL_IF_BAD_OBJ} baz-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobarbaz-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobarbaz-${ARCH} -L. -o main-${ARCH} -ObjC -framework Foundation -framework CoreFoundation + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm main-${ARCH} | grep "_bar" | ${FAIL_IF_STDIN} + nm main-${ARCH} | grep "_Foo" | ${FAIL_IF_EMPTY} + nm main-${ARCH} | grep "_Baz" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/unit-tests/test-cases/archive-ObjC/bar.c b/ld64/unit-tests/test-cases/archive-ObjC/bar.c new file mode 100644 index 0000000..d9b468b --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC/bar.c @@ -0,0 +1,2 @@ + +int bar() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-ObjC/baz.m b/ld64/unit-tests/test-cases/archive-ObjC/baz.m new file mode 100644 index 0000000..90ae0a1 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC/baz.m @@ -0,0 +1,8 @@ +#include + +@interface Baz : NSObject +@end + +@implementation Baz +@end + diff --git a/ld64/unit-tests/test-cases/archive-ObjC/foo.m b/ld64/unit-tests/test-cases/archive-ObjC/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC/foo.m @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/ld64/unit-tests/test-cases/archive-ObjC/main.c b/ld64/unit-tests/test-cases/archive-ObjC/main.c new file mode 100644 index 0000000..dc2fbef --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC/main.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} diff --git a/ld64/unit-tests/test-cases/archive-basic/Makefile b/ld64/unit-tests/test-cases/archive-basic/Makefile new file mode 100644 index 0000000..39c0ef9 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-basic/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that .o files +# can be found in archives. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm main-${ARCH} | grep "_bar" | ${PASS_IFF_EMPTY} + ${CC} ${CCFLAGS} main.c -all_load -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/unit-tests/test-cases/archive-basic/bar.c b/ld64/unit-tests/test-cases/archive-basic/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-basic/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-basic/comment.txt b/ld64/unit-tests/test-cases/archive-basic/comment.txt new file mode 100644 index 0000000..0d34170 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-basic/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that .o files can be found in archives. diff --git a/ld64/unit-tests/test-cases/archive-basic/foo.c b/ld64/unit-tests/test-cases/archive-basic/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-basic/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/ld64/unit-tests/test-cases/archive-basic/main.c b/ld64/unit-tests/test-cases/archive-basic/main.c new file mode 100644 index 0000000..57c4c68 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-basic/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + return foo(); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/archive-duplicate/Makefile b/ld64/unit-tests/test-cases/archive-duplicate/Makefile new file mode 100644 index 0000000..61f4bc7 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-duplicate/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ia an archive +# is listed multiple times, the extras are ignored. +# This is done in some makefiles because the traditional linker +# semantics is to only search an archive once. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -lfoobar-${ARCH} -L. -o main-${ARCH} -all_load + ${FAIL_IF_BAD_MACHO} main-${ARCH} + ${CC} ${CCFLAGS} main.c ./libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} -all_load + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/unit-tests/test-cases/archive-duplicate/bar.c b/ld64/unit-tests/test-cases/archive-duplicate/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-duplicate/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-duplicate/foo.c b/ld64/unit-tests/test-cases/archive-duplicate/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-duplicate/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/ld64/unit-tests/test-cases/archive-duplicate/main.c b/ld64/unit-tests/test-cases/archive-duplicate/main.c new file mode 100644 index 0000000..57c4c68 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-duplicate/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + return foo(); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/archive-weak/Makefile b/ld64/unit-tests/test-cases/archive-weak/Makefile new file mode 100644 index 0000000..2d53734 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-weak/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test if the linker already has a weak definition +# it does not try to find another copy in an archive +# +# There are two case to test: +# 1) both the main .o files and the archive have the same weak symbol (_foo) +# 2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) +# In both cases the linker should ignore the archive. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + ${CC} ${CCFLAGS} baz.c -c -o baz-${ARCH}.o + ${FAIL_IF_BAD_OBJ} baz-${ARCH}.o + libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobar-${ARCH}.a + ${CC} ${CCFLAGS} main.c foo.c -lfoobar-${ARCH} -L. -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -m main-${ARCH} | grep _baz | grep weak | ${PASS_IFF_STDIN} + +clean: + rm -rf main-* *.o *.a diff --git a/ld64/unit-tests/test-cases/archive-weak/bar.c b/ld64/unit-tests/test-cases/archive-weak/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-weak/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-weak/baz.c b/ld64/unit-tests/test-cases/archive-weak/baz.c new file mode 100644 index 0000000..387ccc1 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-weak/baz.c @@ -0,0 +1,11 @@ + + + +// intentionally not-weak +int baz() +{ + return 1; +} + + + diff --git a/ld64/unit-tests/test-cases/archive-weak/comment.txt b/ld64/unit-tests/test-cases/archive-weak/comment.txt new file mode 100644 index 0000000..902d22c --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-weak/comment.txt @@ -0,0 +1,7 @@ +The point of this test if the linker already has a weak definition +it does not try to find another copy in an archive + +There are two case to test: +1) both the main .o files and the archive have the same weak symbol (_foo) +2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) +In both cases the linker should ignore the archive. diff --git a/ld64/unit-tests/test-cases/archive-weak/foo.c b/ld64/unit-tests/test-cases/archive-weak/foo.c new file mode 100644 index 0000000..d7003fa --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-weak/foo.c @@ -0,0 +1,13 @@ + + +void collisionChecker() { } + + +int __attribute__((weak)) foo() +{ + collisionChecker(); + return 1; +} + + + diff --git a/ld64/unit-tests/test-cases/archive-weak/main.c b/ld64/unit-tests/test-cases/archive-weak/main.c new file mode 100644 index 0000000..d1ce4a9 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-weak/main.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); +extern int bar(); + +// intentionally weak +int __attribute__((weak)) baz() +{ + return 1; +} + +int main() +{ + fprintf(stdout, "hello\n"); + return foo() + bar() + baz(); +} + + + diff --git a/ld64/unit-tests/test-cases/auto-arch/Makefile b/ld64/unit-tests/test-cases/auto-arch/Makefile new file mode 100644 index 0000000..3b1cc7c --- /dev/null +++ b/ld64/unit-tests/test-cases/auto-arch/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Check that ld can figure out architecture from .o files without -arch option +# + +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} + +clean: + rm -rf hello.o hello diff --git a/ld64/unit-tests/test-cases/auto-arch/hello.c b/ld64/unit-tests/test-cases/auto-arch/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/ld64/unit-tests/test-cases/auto-arch/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/ld64/unit-tests/test-cases/blank-stubs/Makefile b/ld64/unit-tests/test-cases/blank-stubs/Makefile new file mode 100644 index 0000000..346e2b7 --- /dev/null +++ b/ld64/unit-tests/test-cases/blank-stubs/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(VALID_ARCHS)) + +# +# Test that blank stubs are handled properly +# + +run: all + +all: +# build example fully fat dylib + + gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + # handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo + if [ x${ARCH} != xppc ]; \ + then \ + SUB_ARCH=${ARCH}; \ + else \ + SUB_ARCH=`lipo -info libfoo.dylib | sed 's/.*://;s/ppc64 //;s/.* \(ppc[^ ]*\).*/\1/'`; \ + echo SUB_ARCH $$SUB_ARCH; \ + if [ x$$SUB_ARCH = xALL ]; \ + then \ + SUB_ARCH=ppc; \ + fi \ + fi; \ + lipo libfoo.dylib -remove $$SUB_ARCH -output libfoo.dylib + + lipo -create libfoo.dylib -arch_blank ${ARCH} -output libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${OTOOL} -L main | grep libfoo | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/blank-stubs/comment.txt b/ld64/unit-tests/test-cases/blank-stubs/comment.txt new file mode 100644 index 0000000..77a8764 --- /dev/null +++ b/ld64/unit-tests/test-cases/blank-stubs/comment.txt @@ -0,0 +1 @@ +Test that blank stubs are handled properly diff --git a/ld64/unit-tests/test-cases/blank-stubs/foo.c b/ld64/unit-tests/test-cases/blank-stubs/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/blank-stubs/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/blank-stubs/main.c b/ld64/unit-tests/test-cases/blank-stubs/main.c new file mode 100644 index 0000000..6c9f6a4 --- /dev/null +++ b/ld64/unit-tests/test-cases/blank-stubs/main.c @@ -0,0 +1,5 @@ + +int main (void) +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/branch-islands/Makefile new file mode 100644 index 0000000..c85f382 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-islands/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ifeq ($(ARCH),armv6) + ARCH_FLAGS = -mlong-branch +else + ARCH_FLAGS = +endif + + + +# +# Simple test for branch islands +# + +run: all + + + +all: + ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} + ${PASS_IFF_GOOD_MACHO} hello + +clean: + rm hello diff --git a/ld64/unit-tests/test-cases/branch-islands/extra.c b/ld64/unit-tests/test-cases/branch-islands/extra.c new file mode 100644 index 0000000..a1991fe --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-islands/extra.c @@ -0,0 +1,8 @@ +#include + + +void foo() +{ + fprintf(stdout, "foo\n"); +} + diff --git a/ld64/unit-tests/test-cases/branch-islands/hello.c b/ld64/unit-tests/test-cases/branch-islands/hello.c new file mode 100644 index 0000000..89caa9d --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-islands/hello.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); + +int main() +{ + fprintf(stdout, "hello\n"); + foo(); +} + diff --git a/ld64/unit-tests/test-cases/branch-islands/space.s b/ld64/unit-tests/test-cases/branch-islands/space.s new file mode 100644 index 0000000..dd28637 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-islands/space.s @@ -0,0 +1,39 @@ + +#if __ppc__ + + .text + +_prejunk: + mr r3,r5 + mr r3,r4 + blr + + +_space1: + .space 15*1024*1024 + 2 + + .align 5 +_junk: + mr r3,r5 + mr r3,r4 + blr + + +_space2: + .space 2*1024*1024 + +#endif + + +#if __arm__ + + +_space1: + .space 32*1024*1024 + 2 + + +#endif + + + .subsections_via_symbols + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/bundle_loader/Makefile b/ld64/unit-tests/test-cases/bundle_loader/Makefile new file mode 100644 index 0000000..96d83b8 --- /dev/null +++ b/ld64/unit-tests/test-cases/bundle_loader/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Ccheck that ld -bundle_loader works with bundles and executable, +# and _bar is found in the loader and not in libbar.dylib +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + # test with loader as main executable + ${CC} ${CCFLAGS} main.c bar.c -o main + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader main libbar.dylib -o bundle.bundle + ${FAIL_IF_BAD_MACHO} bundle.bundle + nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} + # test with loader as another bundle + ${CC} ${CCFLAGS} -bundle main.c bar.c -o mainbundle + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader mainbundle libbar.dylib -o bundle.bundle + ${FAIL_IF_BAD_MACHO} bundle.bundle + nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} + # verify error message when linking with raw bundle + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c bar.c -o main bundle.bundle 2> error.log + grep "link with bundle" error.log | ${PASS_IFF_STDIN} + +clean: + rm *.dylib main bundle.bundle mainbundle error.log diff --git a/ld64/unit-tests/test-cases/bundle_loader/bar.c b/ld64/unit-tests/test-cases/bundle_loader/bar.c new file mode 100644 index 0000000..b737953 --- /dev/null +++ b/ld64/unit-tests/test-cases/bundle_loader/bar.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// funcation called by a loaded bundle +int bar() +{ + return 1; +} + diff --git a/ld64/unit-tests/test-cases/bundle_loader/bundle.c b/ld64/unit-tests/test-cases/bundle_loader/bundle.c new file mode 100644 index 0000000..ba78c28 --- /dev/null +++ b/ld64/unit-tests/test-cases/bundle_loader/bundle.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/ld64/unit-tests/test-cases/bundle_loader/main.c b/ld64/unit-tests/test-cases/bundle_loader/main.c new file mode 100644 index 0000000..c9e53f9 --- /dev/null +++ b/ld64/unit-tests/test-cases/bundle_loader/main.c @@ -0,0 +1,30 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} + diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile b/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile new file mode 100644 index 0000000..c1ca816 --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that cfstring literals are coalesced and dead stripped +# There is 3 CFSTR in foo.c and 1 CFSTR in bar.c +# After coalescing and dead stripping there should be only one CFSTR in the output +# + +ifeq (,${findstring 64,$(ARCH)}) + CFSTRING_SIZE = 16 + CFSTRING_SIZE_WRITABLE = 32 +else + CFSTRING_SIZE = 32 + CFSTRING_SIZE_WRITABLE = 64 +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip + size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} + # now try with -fwritable-strings + ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings + size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE_WRITABLE}" | ${PASS_IFF_STDIN} + +clean: + rm -rf foo foo_writable diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c b/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c new file mode 100644 index 0000000..9a84abd --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c @@ -0,0 +1,7 @@ +#include + + +void bar() +{ + CFStringGetLength(CFSTR("live")); +} diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c b/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c new file mode 100644 index 0000000..fe1b30e --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c @@ -0,0 +1,19 @@ +#include + +extern void bar(); + +void foo() +{ + CFStringGetLength(CFSTR("hello")); + CFStringGetLength(CFSTR("world")); +} + + +int main() +{ + CFStringGetLength(CFSTR("live")); + bar(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile new file mode 100644 index 0000000..3375aae --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that utf16 cfstring literals are not coalesced. +# There is 3 CFSTR in foo.m and 1 CFSTR in bar.m +# After coalescing and dead stripping there should be only one CFSTR in the output +# + +ifeq (,${findstring 64,$(ARCH)}) + CFSTRING_SIZE = 48 +else + CFSTRING_SIZE = 96 +endif + USTRING_SIZE = 37 + + + +run: all + +all: + ${CC} ${CCFLAGS} foo.m bar.m -o foo -framework CoreFoundation -dead_strip + size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} + size -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf foo diff --git a/ld64/unit-tests/test-cases/cfstring-utf16/bar.m b/ld64/unit-tests/test-cases/cfstring-utf16/bar.m new file mode 100644 index 0000000..bdf5168 --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-utf16/bar.m @@ -0,0 +1,7 @@ +#include + + +void bar() +{ + CFStringGetLength(CFSTR("über")); +} diff --git a/ld64/unit-tests/test-cases/cfstring-utf16/foo.m b/ld64/unit-tests/test-cases/cfstring-utf16/foo.m new file mode 100644 index 0000000..6e4c268 --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-utf16/foo.m @@ -0,0 +1,20 @@ +#include + +extern void bar(); + +void foo() +{ + CFStringGetLength(CFSTR("hello")); + CFStringGetLength(CFSTR("überhund")); +} + + +int main() +{ + CFStringGetLength(CFSTR("über")); + CFStringGetLength(CFSTR("überhund")); + bar(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/commons-alignment/Makefile b/ld64/unit-tests/test-cases/commons-alignment/Makefile new file mode 100644 index 0000000..d073c0e --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-alignment/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker preserves commons with custom alignment +# + +all: + ${CC} ${CCFLAGS} foo.s -c -o foo.o + nm -m foo.o | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY} + ${LD} foo.o -r -o foo2.o + nm -m foo2.o | grep '(alignment 2^6)' | ${PASS_IFF_STDIN} + +clean: + rm -rf foo.o foo2.o diff --git a/ld64/unit-tests/test-cases/commons-alignment/foo.s b/ld64/unit-tests/test-cases/commons-alignment/foo.s new file mode 100644 index 0000000..03c28fc --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-alignment/foo.s @@ -0,0 +1,2 @@ + + .comm _mycomm64aligned,15,6 diff --git a/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile new file mode 100644 index 0000000..5823a61 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify if a header file is missing an extern that there won't be +# duplicates definitions when using -dead_strip. +# + +run: all + +all: + ${CC} ${CCFLAGS} a.c -c -o a.o + ${CC} ${CCFLAGS} b.c -c -o b.o + ${CC} ${CCFLAGS} c.c -c -o c.o + ${CC} -arch ${ARCH} -dynamiclib a.o b.o c.o -o libabc.dylib -dead_strip + ${PASS_IFF_GOOD_MACHO} libabc.dylib + +clean: + rm -rf a.o b.o c.o libabc.dylib one abc.bar.count diff --git a/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/a.c b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/a.c new file mode 100644 index 0000000..fea7234 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/a.c @@ -0,0 +1,4 @@ +#include "c.h" + +float aa() { return bar; } + diff --git a/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/b.c b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/b.c new file mode 100644 index 0000000..12e46e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/b.c @@ -0,0 +1,4 @@ +#include "c.h" + +float bb() { return bar; } + diff --git a/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.c b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.c new file mode 100644 index 0000000..165d5e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.c @@ -0,0 +1,3 @@ + +const float bar = 1.0; + diff --git a/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.h b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.h new file mode 100644 index 0000000..86a61ae --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-coalesced-dead_strip/c.h @@ -0,0 +1,4 @@ + +// missing extern +const float bar; + diff --git a/ld64/unit-tests/test-cases/commons-mixed/Makefile b/ld64/unit-tests/test-cases/commons-mixed/Makefile new file mode 100644 index 0000000..a59a858 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-mixed/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o built with +# commons and one built without commons can be merged. +# +# problem merging .o files built with and without -fno-common +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o + ${CC} ${CCFLAGS} bar.c -c -fno-common -o bar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs foo.${ARCH}.o bar.${ARCH}.o -o foobar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foobar.${ARCH}.o + nm -m foobar.${ARCH}.o | grep bar | grep __common | ${PASS_IFF_STDIN} + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/commons-mixed/bar.c b/ld64/unit-tests/test-cases/commons-mixed/bar.c new file mode 100644 index 0000000..771802c --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-mixed/bar.c @@ -0,0 +1,2 @@ + +int bar; diff --git a/ld64/unit-tests/test-cases/commons-mixed/foo.c b/ld64/unit-tests/test-cases/commons-mixed/foo.c new file mode 100644 index 0000000..42e0a8e --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-mixed/foo.c @@ -0,0 +1,2 @@ + +int foo; diff --git a/ld64/unit-tests/test-cases/commons-order/Makefile b/ld64/unit-tests/test-cases/commons-order/Makefile new file mode 100644 index 0000000..68d809c --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-order/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker puts commons in the correct order. +# -fno-commons come first, followed by all other commons +# in .o order and alphabetically within each .o +# + +all: + ${CC} ${CCFLAGS} baz.c -fno-common -c -o baz.o + ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main -mmacosx-version-min=10.5 + nm -j -n main | grep _common > symbol.order + ${FAIL_IF_ERROR} diff symbol.order expected.order + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main baz.o symbol.order diff --git a/ld64/unit-tests/test-cases/commons-order/bar.c b/ld64/unit-tests/test-cases/commons-order/bar.c new file mode 100644 index 0000000..2d258fc --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-order/bar.c @@ -0,0 +1,3 @@ +int ddd_common; +int iii_common[4]; +int bbb_common[4]; diff --git a/ld64/unit-tests/test-cases/commons-order/baz.c b/ld64/unit-tests/test-cases/commons-order/baz.c new file mode 100644 index 0000000..6497d29 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-order/baz.c @@ -0,0 +1,3 @@ +int fff_common; +int iii_common[4]; +int ttt_common; diff --git a/ld64/unit-tests/test-cases/commons-order/expected.order b/ld64/unit-tests/test-cases/commons-order/expected.order new file mode 100644 index 0000000..4738fc2 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-order/expected.order @@ -0,0 +1,8 @@ +_fff_common +_iii_common +_ttt_common +_aaa_common +_eee_common +_ggg_common +_bbb_common +_ddd_common diff --git a/ld64/unit-tests/test-cases/commons-order/foo.c b/ld64/unit-tests/test-cases/commons-order/foo.c new file mode 100644 index 0000000..ca80a59 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-order/foo.c @@ -0,0 +1,3 @@ +int aaa_common; +int ggg_common[4]; +int eee_common; diff --git a/ld64/unit-tests/test-cases/commons-order/main.c b/ld64/unit-tests/test-cases/commons-order/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/ld64/unit-tests/test-cases/commons-order/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile b/ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile new file mode 100644 index 0000000..7586a51 --- /dev/null +++ b/ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile @@ -0,0 +1,96 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate cpu subtype preference +# + +test: test-${ARCH} + +test-ppc64: + ${PASS_IFF} true + +test-i386: + ${PASS_IFF} true + +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 + + # check -arch armv4t pulls out V4 slice + ${LD} -r -arch armv4t foo.o -o fooa.o + otool -hv fooa.o | grep V4T | ${FAIL_IF_EMPTY} + nm fooa.o | grep _aaa | ${FAIL_IF_EMPTY} + + # check -arch armv5 pulls out V5 slice + ${LD} -r -arch armv5 foo.o -o foob.o + otool -hv foob.o | grep V5 | ${FAIL_IF_EMPTY} + nm foob.o | grep _bbb | ${FAIL_IF_EMPTY} + + # check -arch armv6 pulls out V6 slice + ${LD} -r -arch armv6 foo.o -o fooc.o + 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} + + ${PASS_IFF} true + + +test-ppc: + gcc foo.c -arch ppc750 -c -DAAA=1 -o foog3.o + gcc foo.c -arch ppc7400 -c -DBBB=1 -o foog4.o + gcc foo.c -arch ppc970 -c -DCCC=1 -o foog5.o + lipo foog3.o foog4.o foog5.o -create -output foo.o + + # check -arch ppc750 pulls out g3 slice + ${LD} -r -arch ppc750 foo.o -o fooa.o + otool -hv fooa.o | grep ppc750 | ${FAIL_IF_EMPTY} + nm fooa.o | grep _aaa | ${FAIL_IF_EMPTY} + + # check -arch ppc7400 pulls out g4 slice + ${LD} -r -arch ppc7400 foo.o -o foob.o + otool -hv foob.o | grep ppc7400 | ${FAIL_IF_EMPTY} + nm foob.o | grep _bbb | ${FAIL_IF_EMPTY} + + # check -arch ppc970 pulls out g5 slice + ${LD} -r -arch ppc970 foo.o -o fooc.o + otool -hv fooc.o | grep ppc970 | ${FAIL_IF_EMPTY} + nm fooc.o | grep _ccc | ${FAIL_IF_EMPTY} + + ${PASS_IFF} true + + +clean: + rm -f *.o diff --git a/ld64/unit-tests/test-cases/cpu-sub-types-preference/foo.c b/ld64/unit-tests/test-cases/cpu-sub-types-preference/foo.c new file mode 100644 index 0000000..983ed81 --- /dev/null +++ b/ld64/unit-tests/test-cases/cpu-sub-types-preference/foo.c @@ -0,0 +1,25 @@ + + +#if AAA + void aaa() {} +#endif + +#if BBB + void bbb() {} +#endif + +#if CCC + void ccc() {} +#endif + +#if DDD + void ddd() {} +#endif + +#if EEE + void eee() {} +#endif + +#if FFFF + void fff() {} +#endif diff --git a/ld64/unit-tests/test-cases/cpu-sub-types/Makefile b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile new file mode 100644 index 0000000..695ffba --- /dev/null +++ b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile @@ -0,0 +1,157 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate cpu subtypes processing +# + +test: test-${ARCH} + +test-ppc64: + ${PASS_IFF} true + +test-i386: + ${PASS_IFF} true + +test-x86_64: + ${PASS_IFF} true + +test-armv6: + gcc 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 + ${FAIL_IF_BAD_OBJ} foo-v5.o + gcc 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 + ${FAIL_IF_BAD_OBJ} foo-xscale.o + gcc 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 + ${FAIL_IF_BAD_OBJ} main-v5.o + gcc 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 + ${FAIL_IF_BAD_OBJ} main-xscale.o + + # check V4+V4 -> V4 + ${LD} -r main-v4.o foo-v4.o -o all.o + ${FAIL_IF_BAD_OBJ} all.o + 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} + + # 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} + + # 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} + + # check V5+V5 -> V5 + ${LD} -r main-v5.o foo-v5.o -o all.o + ${FAIL_IF_BAD_OBJ} all.o + 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} + + # 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} + + # 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 + ${FAIL_IF_BAD_OBJ} all.o + otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} + + ${PASS_IFF} true + + +test-ppc: + gcc foo.c -arch ppc -mmacosx-version-min=10.4 -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + gcc foo.c -arch ppc750 -c -o foo-G3.o + ${FAIL_IF_BAD_OBJ} foo-G3.o + gcc foo.c -arch ppc7400 -c -o foo-G4.o + ${FAIL_IF_BAD_OBJ} foo-G4.o + gcc foo.c -arch ppc970 -c -o foo-G5.o + ${FAIL_IF_BAD_OBJ} foo-G5.o + gcc main.c -arch ppc -mmacosx-version-min=10.4 -c -o main.o + ${FAIL_IF_BAD_OBJ} main.o + gcc main.c -arch ppc970 -c -o main-G5.o + ${FAIL_IF_BAD_OBJ} main-G5.o + + # check ALL+ALL -> ALL + ${LD} -r main.o foo.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ALL | ${FAIL_IF_EMPTY} + + # check G3+ALL -> G3 + ${LD} -r main.o foo-G3.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc750 | ${FAIL_IF_EMPTY} + + # check G4+ALL -> G4 + ${LD} -r main.o foo-G4.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc7400 | ${FAIL_IF_EMPTY} + + # check G5+ALL -> G5 + ${LD} -r main.o foo-G5.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} + + # check G5+G4 -> G5 + ${LD} -r main-G5.o foo-G4.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} + + # check G4+G5 -> G5 + ${LD} -r foo-G4.o main-G5.o -o main-r.o + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} + + # check -force_cpusubtype_ALL + ${LD} -r main.o foo-G5.o -o main-r.o -force_cpusubtype_ALL + ${FAIL_IF_BAD_OBJ} main-r.o + otool -hv main-r.o | grep ALL | ${PASS_IFF_STDIN} + +clean: + rm -f *.o diff --git a/ld64/unit-tests/test-cases/cpu-sub-types/comment.txt b/ld64/unit-tests/test-cases/cpu-sub-types/comment.txt new file mode 100644 index 0000000..5e47ece --- /dev/null +++ b/ld64/unit-tests/test-cases/cpu-sub-types/comment.txt @@ -0,0 +1,2 @@ +The point of this test is validate cpu subtypes processsing for PowerPC + diff --git a/ld64/unit-tests/test-cases/cpu-sub-types/foo.c b/ld64/unit-tests/test-cases/cpu-sub-types/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/ld64/unit-tests/test-cases/cpu-sub-types/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/ld64/unit-tests/test-cases/cpu-sub-types/main.c b/ld64/unit-tests/test-cases/cpu-sub-types/main.c new file mode 100644 index 0000000..b48076d --- /dev/null +++ b/ld64/unit-tests/test-cases/cpu-sub-types/main.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-global/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive-global/Makefile new file mode 100644 index 0000000..ef985b7 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-global/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a global symbol in an archive will stil be exported (and not dead stripped). +# + +run: all + +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 -dynamiclib -Os libfoo.a -dead_strip -o libmain.dylib + nm libmain.dylib | grep _bar | ${FAIL_IF_EMPTY} + nm libmain.dylib | grep _baz | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libmain.dylib + + +clean: + rm -rf *.dylib *.a *.o diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-global/foo.c b/ld64/unit-tests/test-cases/dead_strip-archive-global/foo.c new file mode 100644 index 0000000..41478e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-global/foo.c @@ -0,0 +1,12 @@ + +static int foo_count = 0; +static int bar_count = 0; +static int baz_count = 0; + + +void foo() { ++foo_count; } + +void bar() { ++bar_count; } + +void __attribute__((visibility("hidden"))) + baz() { ++baz_count; } diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-global/main.c b/ld64/unit-tests/test-cases/dead_strip-archive-global/main.c new file mode 100644 index 0000000..da2b2fd --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-global/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive/Makefile new file mode 100644 index 0000000..1542d78 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a common symbol can be used from an archive with -dead_strip. The tricky +# part is that common symbols are not in the table of contents for archives. +# If the linker seens a need for my_common, that won't trigger pulling in the .o +# file from the archive. But the later use of foo will. +# + +run: all + +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 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.a *.o diff --git a/ld64/unit-tests/test-cases/dead_strip-archive/comment.txt b/ld64/unit-tests/test-cases/dead_strip-archive/comment.txt new file mode 100644 index 0000000..8dde1c5 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive/comment.txt @@ -0,0 +1 @@ +The point of this test that -dead_strip removes unreference code/data from archives diff --git a/ld64/unit-tests/test-cases/dead_strip-archive/foo.c b/ld64/unit-tests/test-cases/dead_strip-archive/foo.c new file mode 100644 index 0000000..be1b438 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive/foo.c @@ -0,0 +1,7 @@ + + +void foo() {} + + +int my_common; + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive/main.c b/ld64/unit-tests/test-cases/dead_strip-archive/main.c new file mode 100644 index 0000000..f6a7d5a --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive/main.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern void foo(); +extern int my_common; + +int main() +{ + // the reference to the common symbol has to be first + my_common += 1; + // refrence to foo is next + foo(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/dead_strip-init-archive/Makefile b/ld64/unit-tests/test-cases/dead_strip-init-archive/Makefile new file mode 100644 index 0000000..4b4f912 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-init-archive/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a -init function can be pulled from an archive when using -dead_strip. +# + +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 -dynamiclib -Os libfoo.a -dead_strip -o libbar.dylib -init _foo + ${PASS_IFF_GOOD_MACHO} libbar.dylib + +clean: + rm -rf libbar.dylib libfoo.a foo.o + diff --git a/ld64/unit-tests/test-cases/dead_strip-init-archive/bar.c b/ld64/unit-tests/test-cases/dead_strip-init-archive/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-init-archive/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/ld64/unit-tests/test-cases/dead_strip-init-archive/foo.c b/ld64/unit-tests/test-cases/dead_strip-init-archive/foo.c new file mode 100644 index 0000000..95ec91c --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-init-archive/foo.c @@ -0,0 +1,6 @@ + + +void foo() {} + + + diff --git a/ld64/unit-tests/test-cases/dead_strip/Makefile b/ld64/unit-tests/test-cases/dead_strip/Makefile new file mode 100644 index 0000000..32ac1c5 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check -dead_strip +# +# 1) in a main executable, dead globals are removed +# 2) in a dylib/bundle with -exported_symbols_list, dead globals are removed +# 3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} + ${FAIL_IF_BAD_MACHO} dylib-${ARCH} + nm -j dylib-${ARCH} | grep dead | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -o dylib2-${ARCH} + ${FAIL_IF_BAD_MACHO} dylib2-${ARCH} + nm -j dylib2-${ARCH} | grep dead_door_knob | ${FAIL_IF_EMPTY} + nm -j dylib2-${ARCH} | grep deadwood | ${PASS_IFF_EMPTY} + +clean: + rm -rf main-* dylib-* dylib2-* diff --git a/ld64/unit-tests/test-cases/dead_strip/comment.txt b/ld64/unit-tests/test-cases/dead_strip/comment.txt new file mode 100644 index 0000000..90a289c --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip/comment.txt @@ -0,0 +1,5 @@ +The point of this test is a sanity check -dead_strip + +1) in a main executable, dead globals are removed +2) in a dylib/bundle with -exported_symbols_list, dead globals are removed +3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed diff --git a/ld64/unit-tests/test-cases/dead_strip/deadwood.c b/ld64/unit-tests/test-cases/dead_strip/deadwood.c new file mode 100644 index 0000000..176ec78 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip/deadwood.c @@ -0,0 +1,11 @@ + + +// deadwood() is local to its linkage unit and is unsed, +// so reference to undef() is ok + +extern void undef(); + +void dead_wood() __attribute__((visibility("hidden"))); +void dead_wood() { undef(); } + + diff --git a/ld64/unit-tests/test-cases/dead_strip/main.c b/ld64/unit-tests/test-cases/dead_strip/main.c new file mode 100644 index 0000000..029aed9 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} + + + +void dead_door_knob() { } diff --git a/ld64/unit-tests/test-cases/dead_strip/main.exp b/ld64/unit-tests/test-cases/dead_strip/main.exp new file mode 100644 index 0000000..4eb9e89 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip/main.exp @@ -0,0 +1 @@ +_main diff --git a/ld64/unit-tests/test-cases/dead_strip_dylibs/Makefile b/ld64/unit-tests/test-cases/dead_strip_dylibs/Makefile new file mode 100644 index 0000000..309655e --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_dylibs/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test -dead_strip_dylibs +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib libbaz.dylib -o main + ${FAIL_IF_BAD_MACHO} main + otool -L main | grep libfoo.dylib | ${FAIL_IF_EMPTY} + otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY} + otool -L main | grep libbaz.dylib | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libbar.dylib libbaz.dylib -o main -Wl,-dead_strip_dylibs + ${FAIL_IF_BAD_MACHO} main + otool -L main | grep libfoo.dylib | ${FAIL_IF_STDIN} + otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY} + otool -L main | grep libbaz.dylib | ${FAIL_IF_STDIN} + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libbar.dylib libfoo.dylib libbaz.dylib main diff --git a/ld64/unit-tests/test-cases/dead_strip_dylibs/bar.c b/ld64/unit-tests/test-cases/dead_strip_dylibs/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_dylibs/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/dead_strip_dylibs/baz.c b/ld64/unit-tests/test-cases/dead_strip_dylibs/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_dylibs/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/dead_strip_dylibs/foo.c b/ld64/unit-tests/test-cases/dead_strip_dylibs/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_dylibs/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/dead_strip_dylibs/main.c b/ld64/unit-tests/test-cases/dead_strip_dylibs/main.c new file mode 100644 index 0000000..2b85b0e --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_dylibs/main.c @@ -0,0 +1,10 @@ + +extern void bar(); + +int main() +{ +#if CALL_BAR + bar(); +#endif + return 0; +} diff --git a/ld64/unit-tests/test-cases/dead_strip_section_attribute/Makefile b/ld64/unit-tests/test-cases/dead_strip_section_attribute/Makefile new file mode 100644 index 0000000..e6471f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_section_attribute/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that -dead_strip does not remove +# atoms in sections with the S_ATTR_NO_DEAD_STRIP bit set +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -dead_strip -o main-${ARCH} + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -j main-${ARCH} | grep _bar | ${FAIL_IF_STDIN} + nm -j main-${ARCH} | grep _foo | ${PASS_IFF_STDIN} + +clean: + rm -rf main-* diff --git a/ld64/unit-tests/test-cases/dead_strip_section_attribute/comment.txt b/ld64/unit-tests/test-cases/dead_strip_section_attribute/comment.txt new file mode 100644 index 0000000..6ca56e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_section_attribute/comment.txt @@ -0,0 +1,2 @@ +The point of this test is to check that -dead_strip does not remove +atoms in sections with the S_ATTR_NO_DEAD_STRIP bit set diff --git a/ld64/unit-tests/test-cases/dead_strip_section_attribute/main.c b/ld64/unit-tests/test-cases/dead_strip_section_attribute/main.c new file mode 100644 index 0000000..5fc1fad --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip_section_attribute/main.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} + + +// foo should not be dead stripped +void __attribute__ ((section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo() +{ + +} + +// bar should be dead stripped +void __attribute__ ((section ("__DATA,__text2"))) bar() +{ + +} + diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile new file mode 100644 index 0000000..533169f --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that C++ coalescing throws away static probes associated +# with weak functions that are coalesced away. +# Build two programs that both should have the same number of probes +# and hence the same DOF section size +# + +all: simp coal + otool -lv coal | grep -A3 __dof_Number | grep size > coal-dof-size + otool -lv simp | grep -A3 __dof_Number | grep size > simp-dof-size + ${PASS_IFF_SUCCESS} diff coal-dof-size simp-dof-size + +Number.h: Number.d + dtrace -h -s Number.d + +a.o : a.cxx Number.h header.h + ${CXX} ${CXXFLAGS} -c -DFUNC=a a.cxx -o a.o + +b.o : a.cxx Number.h header.h + ${CXX} ${CXXFLAGS} -c -DFUNC=b a.cxx -o b.o + +c.o : a.cxx Number.h header.h + ${CXX} ${CXXFLAGS} -c -DFUNC=c a.cxx -o c.o + +coal : a.o b.o c.o + ${CXX} -dynamiclib a.o b.o c.o -o coal + +simp : x.cxx Number.h + ${CXX} -dynamiclib x.cxx -o simp + + + +clean: + rm -rf coal simp a.o b.o c.o Number.h coal-dof-size simp-dof-size diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d new file mode 100644 index 0000000..9383fe4 --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d @@ -0,0 +1,3 @@ +provider Number { + probe hit(int value); +}; diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx new file mode 100644 index 0000000..8e7e3c6 --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx @@ -0,0 +1,8 @@ + +#include + +#include "header.h" + + +void FUNC() { foo(); } + diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h new file mode 100644 index 0000000..162419b --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h @@ -0,0 +1,11 @@ + +#include "Number.h" + +#define LOTS_O_PROBES { NUMBER_HIT(1); NUMBER_HIT(2); NUMBER_HIT(3); NUMBER_HIT(4); } + + +inline void foo() { + LOTS_O_PROBES +} + + diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx new file mode 100644 index 0000000..b756394 --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx @@ -0,0 +1,6 @@ + +#include "header.h" + +void f() { LOTS_O_PROBES } + + diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile b/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile new file mode 100644 index 0000000..b59760f --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -0,0 +1,60 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a progam with dtrace static probes +# + +all: run + +run: main main-r main-dead_strip libmain.dylib + ${FAIL_IF_BAD_MACHO} main + ${PASS_IFF_GOOD_MACHO} main-r + +main: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -o main + +main-dead_strip: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -o main-dead_strip -dead_strip + +main-r: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -c -o main.o + #${FAIL_IF_BAD_OBJ} main.o + ${LD} -r main.o -o main-r.o + #${FAIL_IF_BAD_OBJ} main-r.o + ${CC} ${CCFLAGS} main-r.o -o main-r + +libmain.dylib: main.c foo.h bar.h + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib + +foo.h: foo.d + dtrace -h -s foo.d + +bar.h: bar.d + dtrace -h -s bar.d + +clean: + rm -rf main libmain.dylib main-r main-dead_strip foo.h bar.h *.o diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/bar.d b/ld64/unit-tests/test-cases/dtrace-static-probes/bar.d new file mode 100644 index 0000000..fab36ea --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/bar.d @@ -0,0 +1,7 @@ +typedef int weirdType; + +provider Bar { + probe count1(weirdType); +}; + +#pragma D attributes Evolving/Evolving/Common provider Bar args diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/comment.txt b/ld64/unit-tests/test-cases/dtrace-static-probes/comment.txt new file mode 100644 index 0000000..b02ac8b --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a progam with dtrace static probes diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/foo.d b/ld64/unit-tests/test-cases/dtrace-static-probes/foo.d new file mode 100644 index 0000000..03f1a19 --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/foo.d @@ -0,0 +1,8 @@ +typedef int weirdType2; + +provider Foo { + probe count1(weirdType2); +}; + + +#pragma D attributes Evolving/Evolving/Common provider Foo args diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/main.c b/ld64/unit-tests/test-cases/dtrace-static-probes/main.c new file mode 100644 index 0000000..8a417e9 --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/main.c @@ -0,0 +1,29 @@ + +#include + +typedef int weirdType; +typedef int weirdType2; + +#include "foo.h" +#include "bar.h" + + +int deadwood() +{ + BAR_COUNT1(2); + return 0; +} + + +int main() { + int a = 1; + + while(a) { + FOO_COUNT1(1); + printf("test\n"); + BAR_COUNT1(2); + sleep(1); + } + + return 0; +} diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile b/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile new file mode 100644 index 0000000..8d38d99 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that using -all_load to pull all .o files out of an archive +# proeduces good "debug notes". +# + +run: + ${CC} ${CCFLAGS} -gdwarf-2 foo.c -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + ${CC} ${CCFLAGS} -gdwarf-2 bar.c -c -o bar.o + ${FAIL_IF_BAD_OBJ} bar.o + ${CC} ${CCFLAGS} -gdwarf-2 baz.c -c -o baz.o + ${FAIL_IF_BAD_OBJ} baz.o + libtool -static bar.o baz.o foo.o -o liball.a + ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs + ${FAIL_IF_BAD_MACHO} liball.dylib + nm -fap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs + ${PASS_IFF} diff liball.dylib.stabs expected-stabs + +clean: + rm -rf *.o liball.a liball.dylib liball.dylib.stabs diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/bar.c b/ld64/unit-tests/test-cases/dwarf-archive-all_load/bar.c new file mode 100644 index 0000000..06752f3 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/bar.c @@ -0,0 +1,2 @@ +void bar() {} + diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/baz.c b/ld64/unit-tests/test-cases/dwarf-archive-all_load/baz.c new file mode 100644 index 0000000..256a0e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/baz.c @@ -0,0 +1 @@ +void baz() {} diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/comment.txt b/ld64/unit-tests/test-cases/dwarf-archive-all_load/comment.txt new file mode 100644 index 0000000..6992acd --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/comment.txt @@ -0,0 +1,2 @@ +Test that using -all_load to pull all .o files out of an archive +proeduces good "debug notes". diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs b/ld64/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs new file mode 100644 index 0000000..0071455 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs @@ -0,0 +1,24 @@ +0000 SO CWD/ +0000 SO bar.c +0001 OSO CWD/liball.a(bar.o) +0000 BNSYM +0000 FUN _bar +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO baz.c +0001 OSO CWD/liball.a(baz.o) +0000 BNSYM +0000 FUN _baz +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO foo.c +0001 OSO CWD/liball.a(foo.o) +0000 BNSYM +0000 FUN _foo +0000 FUN +0000 ENSYM +0000 SO diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/foo.c b/ld64/unit-tests/test-cases/dwarf-archive-all_load/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl b/ld64/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile new file mode 100644 index 0000000..1eb6310 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# produces good "debug notes" stabs from dwarf .o files after +# some of the .o files are merged with ld -r. +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: foobar.o main.o + ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-test-${ARCH} + nm -ap dwarf-test-${ARCH} | ./stabs-filter.pl > dwarf-test-${ARCH}.stabs + ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs + +foobar.o : foo.o bar.o + ${LD} -r -arch ${ARCH} foo.o bar.o -o foobar.o + ${FAIL_IF_BAD_OBJ} foobar.o + +foo.o : foo.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +bar.o : bar.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +main.o : main.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +clean: + rm -rf dwarf-test-* *.o *.stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx new file mode 100644 index 0000000..c3f6a18 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx @@ -0,0 +1,4 @@ +int bar() +{ + return 10; +} diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt new file mode 100644 index 0000000..0f1d0b1 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt @@ -0,0 +1,5 @@ +The point of this test is a sanity check that ld +produces good "debug notes" stabs from dwarf .o files after +some of the .o files are merged with ld -r. +Running nm through stabs-filter.pl produces connonical stabs +that can be diffed against a checked in know good set of stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs new file mode 100644 index 0000000..f8abc12 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs @@ -0,0 +1,24 @@ +0000 SO CWD/ +0000 SO main.cxx +0001 OSO CWD/main.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO foo.cxx +0001 OSO CWD/foo.o +0000 BNSYM +0000 FUN __Z3foov +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO bar.cxx +0001 OSO CWD/bar.o +0000 BNSYM +0000 FUN __Z3barv +0000 FUN +0000 ENSYM +0000 SO diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx new file mode 100644 index 0000000..1f46ef4 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx @@ -0,0 +1,4 @@ +int foo() +{ + return 10; +} diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile new file mode 100644 index 0000000..42f1cb7 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# produces good "debug notes" stabs from dwarf .o files +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: hello.o other.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + nm -ap dwarf-hello-${ARCH} | ./stabs-filter.pl > dwarf-hello-${ARCH}.stabs + ${PASS_IFF} diff dwarf-hello-${ARCH}.stabs expected-stabs + +hello.o : hello.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +other.o : other.cxx + ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ + +clean: + rm -rf dwarf-hello-* *.o *.stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/comment.txt b/ld64/unit-tests/test-cases/dwarf-debug-notes/comment.txt new file mode 100644 index 0000000..5caf129 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/comment.txt @@ -0,0 +1,4 @@ +The point of this test is a sanity check that ld +produces good "debug notes" stabs from dwarf .o files +Running nm through stabs-filter.pl produces connonical stabs +that can be diffed against a checked in know good set of stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs new file mode 100644 index 0000000..4ab634d --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -0,0 +1,33 @@ +0000 SO CWD/ +0000 SO hello.cxx +0001 OSO CWD/hello.o +0000 BNSYM +0000 FUN _main +0000 FUN +0000 ENSYM +0000 BNSYM +0000 FUN __Z3fooi +0000 SOL header.h +0000 FUN +0000 ENSYM +0000 SO +0000 SO CWD/ +0000 SO other.cxx +0001 OSO CWD/other.o +0000 BNSYM +0000 FUN __Z3bari +0000 FUN +0000 ENSYM +0000 BNSYM +0000 FUN .my_non_standard_function_name +0000 FUN +0000 ENSYM +0000 GSYM _init +0000 STSYM .my_non_standard_name +0000 STSYM .my_non_standard_name_static +0000 STSYM __ZZ3bariE8bar_init +0000 GSYM _uninit +0000 STSYM _sinit +0000 STSYM _suninit +0000 STSYM __ZZ3bariE10bar_uninit +0000 SO diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/header.h b/ld64/unit-tests/test-cases/dwarf-debug-notes/header.h new file mode 100644 index 0000000..aa960dd --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/header.h @@ -0,0 +1,8 @@ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/hello.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes/hello.cxx new file mode 100644 index 0000000..0d508e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx new file mode 100644 index 0000000..b1b0e77 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx @@ -0,0 +1,27 @@ + +#include "header.h" + +int uninit; +int init = 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; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + scustominit = x; + custom = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + +extern void disappear() _asm("lbegone"); +void disappear() {} + +extern void foo() _asm(".my_non_standard_function_name"); +void foo() { disappear(); } + diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl b/ld64/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +use strict; +use Cwd; + +my $dir = getcwd; +#my $xxx = $ARGV[1]; + +while(<>) +{ + # get stabs lines that match "NNNNNNN - xxx" + if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) + { + # replace any occurances of cwd path with $CWD + my $line = $3; + if($line =~ m/(.*?)$dir(.*?)$/) + { + $line = $1 . "CWD" . $2; + } + + printf "$line\n"; + } +} + + diff --git a/ld64/unit-tests/test-cases/dwarf-ignore/Makefile b/ld64/unit-tests/test-cases/dwarf-ignore/Makefile new file mode 100644 index 0000000..94b026c --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-ignore/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# strips out the dwarf segment by default +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} + +clean: + rm -rf dwarf-hello-* diff --git a/ld64/unit-tests/test-cases/dwarf-ignore/comment.txt b/ld64/unit-tests/test-cases/dwarf-ignore/comment.txt new file mode 100644 index 0000000..06822b2 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-ignore/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld strips out the dwarf segment by default diff --git a/ld64/unit-tests/test-cases/dwarf-ignore/hello.c b/ld64/unit-tests/test-cases/dwarf-ignore/hello.c new file mode 100644 index 0000000..e2f0fe9 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-ignore/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dwarf-strip/Makefile b/ld64/unit-tests/test-cases/dwarf-strip/Makefile new file mode 100644 index 0000000..947afa7 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-strip/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld -S +# produces no debug notes (stabs) +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o dwarf-hello-${ARCH} + #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} + nm -ap dwarf-hello-${ARCH} | grep -e " - " | ${PASS_IFF_EMPTY} + +clean: + rm -rf dwarf-hello-* diff --git a/ld64/unit-tests/test-cases/dwarf-strip/comment.txt b/ld64/unit-tests/test-cases/dwarf-strip/comment.txt new file mode 100644 index 0000000..5a23827 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-strip/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld -S produces no debug notes (stabs) diff --git a/ld64/unit-tests/test-cases/dwarf-strip/hello.c b/ld64/unit-tests/test-cases/dwarf-strip/hello.c new file mode 100644 index 0000000..ddca819 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-strip/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dylib-aliases/Makefile b/ld64/unit-tests/test-cases/dylib-aliases/Makefile new file mode 100644 index 0000000..1cf9fa0 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-aliases/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that libfoo.dylib can be aliases with a symlink or +# as another dylib and still link +# + +run: all + +all: libfoo.dylib libbar.dylib libbaz.dylib + ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. + ${PASS_IFF_GOOD_MACHO} main + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name libfoo.dylib + +libbaz.dylib : libfoo.dylib + ln -s libfoo.dylib libbaz.dylib + +clean: + rm -rf *.dylib main diff --git a/ld64/unit-tests/test-cases/dylib-aliases/bar.c b/ld64/unit-tests/test-cases/dylib-aliases/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-aliases/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/unit-tests/test-cases/dylib-aliases/foo.c b/ld64/unit-tests/test-cases/dylib-aliases/foo.c new file mode 100644 index 0000000..2fb55ee --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-aliases/foo.c @@ -0,0 +1 @@ +void foo() { } diff --git a/ld64/unit-tests/test-cases/dylib-aliases/main.c b/ld64/unit-tests/test-cases/dylib-aliases/main.c new file mode 100644 index 0000000..03c4b39 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-aliases/main.c @@ -0,0 +1,8 @@ +extern void foo(); +extern void bar(); + +int main() { + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile new file mode 100644 index 0000000..617fbfc --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Test that searches for indirect libraries does not cause a cycle +# OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found +# + +run: all + +all: libfoo.dylib + ${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true + grep "cycle" errmsg | ${PASS_IFF_STDIN} + +libfoo.dylib : foo.c libbar.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 + +libbar.dylib : bar.c other/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 + +other/libfoo.dylib : foo.c + mkdir -p other + ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 + + + +clean: + rm -rf other libfoo.dylib libbar.dylib main errmsg diff --git a/ld64/unit-tests/test-cases/dylib-re-export-cycle/bar.c b/ld64/unit-tests/test-cases/dylib-re-export-cycle/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-re-export-cycle/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/unit-tests/test-cases/dylib-re-export-cycle/foo.c b/ld64/unit-tests/test-cases/dylib-re-export-cycle/foo.c new file mode 100644 index 0000000..2fb55ee --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-re-export-cycle/foo.c @@ -0,0 +1 @@ +void foo() { } diff --git a/ld64/unit-tests/test-cases/dylib-re-export-cycle/main.c b/ld64/unit-tests/test-cases/dylib-re-export-cycle/main.c new file mode 100644 index 0000000..8273541 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-re-export-cycle/main.c @@ -0,0 +1,6 @@ +extern void unfindable(); + +int main() { + unfindable(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/dylib_file-missing/Makefile b/ld64/unit-tests/test-cases/dylib_file-missing/Makefile new file mode 100644 index 0000000..f6371af --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file-missing/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) +SHELL = bash # use bash shell so we can redirect just stderr + + +# Verify that if -dylib_file option points to a missing, file the link does not fail + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} foo.c "${PWD}/libbar.dylib" -dynamiclib -o libfoo.dylib -sub_library libbar + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -dylib_file "${PWD}/libbar.dylib:libbar2.dylib" 2>warnings.log + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.dylib main warnings.log diff --git a/ld64/unit-tests/test-cases/dylib_file-missing/bar.c b/ld64/unit-tests/test-cases/dylib_file-missing/bar.c new file mode 100644 index 0000000..2668f9a --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file-missing/bar.c @@ -0,0 +1,13 @@ + + + +void bar() +{ +} + +#if BAR_EXTRA +void bar_extra() +{ +} +#endif + diff --git a/ld64/unit-tests/test-cases/dylib_file-missing/foo.c b/ld64/unit-tests/test-cases/dylib_file-missing/foo.c new file mode 100644 index 0000000..aa31241 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file-missing/foo.c @@ -0,0 +1,7 @@ + +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/ld64/unit-tests/test-cases/dylib_file-missing/main.c b/ld64/unit-tests/test-cases/dylib_file-missing/main.c new file mode 100644 index 0000000..3f109f2 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file-missing/main.c @@ -0,0 +1,15 @@ + +extern void foo(); +extern void bar(); +extern void bar_extra(); + +int main() +{ + foo(); + bar(); +#if BAR_EXTRA + bar_extra(); +#endif + return 0; +} + diff --git a/ld64/unit-tests/test-cases/dylib_file/Makefile b/ld64/unit-tests/test-cases/dylib_file/Makefile new file mode 100644 index 0000000..181eee5 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) +SHELL = bash # use bash shell so we can redirect just stderr + + +# Verify that -dylib_file option allows you to replace an indirect dylib + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} bar.c -DBAR_EXTRA -dynamiclib -o libbar2.dylib -install_name "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} foo.c "${PWD}/libbar.dylib" -dynamiclib -o libfoo.dylib -sub_library libbar + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + # verify that if main needs bar_extra, it fails + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DBAR_EXTRA libfoo.dylib -o main 2> fail.log + # verify that if main needs bar_extra, it works with -dylib_file option + ${CC} ${CCFLAGS} main.c -DBAR_EXTRA libfoo.dylib -o main -dylib_file "${PWD}/libbar.dylib:libbar2.dylib" + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.dylib main fail.log diff --git a/ld64/unit-tests/test-cases/dylib_file/bar.c b/ld64/unit-tests/test-cases/dylib_file/bar.c new file mode 100644 index 0000000..2668f9a --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file/bar.c @@ -0,0 +1,13 @@ + + + +void bar() +{ +} + +#if BAR_EXTRA +void bar_extra() +{ +} +#endif + diff --git a/ld64/unit-tests/test-cases/dylib_file/comment.txt b/ld64/unit-tests/test-cases/dylib_file/comment.txt new file mode 100644 index 0000000..e3bfbb7 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file/comment.txt @@ -0,0 +1 @@ +Verify that -dylib_file option allows you to replace an indirect dylib diff --git a/ld64/unit-tests/test-cases/dylib_file/foo.c b/ld64/unit-tests/test-cases/dylib_file/foo.c new file mode 100644 index 0000000..aa31241 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file/foo.c @@ -0,0 +1,7 @@ + +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/ld64/unit-tests/test-cases/dylib_file/main.c b/ld64/unit-tests/test-cases/dylib_file/main.c new file mode 100644 index 0000000..3f109f2 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_file/main.c @@ -0,0 +1,15 @@ + +extern void foo(); +extern void bar(); +extern void bar_extra(); + +int main() +{ + foo(); + bar(); +#if BAR_EXTRA + bar_extra(); +#endif + return 0; +} + diff --git a/ld64/unit-tests/test-cases/dylib_init/Makefile b/ld64/unit-tests/test-cases/dylib_init/Makefile new file mode 100644 index 0000000..3ccbc85 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_init/Makefile @@ -0,0 +1,36 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Verify -init option creates a LC_ROUTINES load command + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -init __init + otool -lv libfoo.dylib | grep LC_ROUTINES | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf libfoo.dylib diff --git a/ld64/unit-tests/test-cases/dylib_init/foo.c b/ld64/unit-tests/test-cases/dylib_init/foo.c new file mode 100644 index 0000000..9ba156e --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib_init/foo.c @@ -0,0 +1,2 @@ +void _init() { +} diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile b/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile new file mode 100644 index 0000000..3fb8a25 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# +# comdat warnings in ld -r +# +# also use -falign-functions to force an out of order coalesing +# +run: all + +all: + ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o + ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32 + ${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o + ${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log + grep warning warnings.log | ${PASS_IFF_EMPTY} + + +clean: + rm foo.o bar.o baz.o foobarbaz.o warnings.log diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/bar.cxx b/ld64/unit-tests/test-cases/eh-coalescing-r/bar.cxx new file mode 100644 index 0000000..b48923c --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/bar.cxx @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "func.h" + +void bar() +{ + func(); +} + + diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/foo.cxx b/ld64/unit-tests/test-cases/eh-coalescing-r/foo.cxx new file mode 100644 index 0000000..0a8d99a --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/foo.cxx @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "func.h" + + +void test() +{ + func(); +} diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/func.h b/ld64/unit-tests/test-cases/eh-coalescing-r/func.h new file mode 100644 index 0000000..4505225 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/func.h @@ -0,0 +1,35 @@ +/* -*- 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@ + */ + +extern int global; +extern void foo(int); + + +// this weak func() will have unwind info and a LSDA +inline int func() +{ + global = 1; + return global; +} + diff --git a/ld64/unit-tests/test-cases/eh-coalescing/Makefile b/ld64/unit-tests/test-cases/eh-coalescing/Makefile new file mode 100644 index 0000000..0c7fb32 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# There are two copies of func(). The one from +# foo.o is weak and has an FDE (.eh) and LSDA. +# The one from bar.o is not weak and does not have +# exception info. Verify that since linker uses func() +# from bar.o that it does not use exception info +# from foo.o +# +# wrong EH information might be used +# + +run: all + +all: + ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o + ${CXX} ${CCXXFLAGS} foo2.cxx -c -o foo2.o + ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -fno-exceptions + ${CXX} ${CCXXFLAGS} -dynamiclib foo.o foo2.o bar.o -o libfoobar.dylib -Wl,-map,libfoobar.map + # verify .eh symbol is missing or is from bar.o (file 3) + grep '\[ 2\] __Z4funcv.eh' libfoobar.map | ${FAIL_IF_STDIN} + # verify no LSDA + size -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY} + +clean: + rm foo.o foo2.o bar.o libfoobar.map libfoobar.dylib diff --git a/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx b/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx new file mode 100644 index 0000000..83d1845 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// this non-seak func() will have no unwind info and no LSDA +int func() { return 0; } + +void foo(int x) {} + + diff --git a/ld64/unit-tests/test-cases/eh-coalescing/foo.cxx b/ld64/unit-tests/test-cases/eh-coalescing/foo.cxx new file mode 100644 index 0000000..ce9939f --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing/foo.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "func.h" + +int global; + +void test() +{ + func(); +} diff --git a/ld64/unit-tests/test-cases/eh-coalescing/foo2.cxx b/ld64/unit-tests/test-cases/eh-coalescing/foo2.cxx new file mode 100644 index 0000000..4440aed --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing/foo2.cxx @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "func.h" + +void test2() +{ + func(); +} diff --git a/ld64/unit-tests/test-cases/eh-coalescing/func.h b/ld64/unit-tests/test-cases/eh-coalescing/func.h new file mode 100644 index 0000000..5bb7c28 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing/func.h @@ -0,0 +1,43 @@ +/* -*- 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@ + */ + +extern int global; +extern void foo(int); + + +// this weak func() will have unwind info and a LSDA +inline int func() +{ + global = 1; + try { + foo(1); + global = 2; + } + catch (int x) { + foo(2); + global = 3; + } + return global; +} + diff --git a/ld64/unit-tests/test-cases/eh-strip-test/Makefile b/ld64/unit-tests/test-cases/eh-strip-test/Makefile new file mode 100644 index 0000000..0b8fcff --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-strip-test/Makefile @@ -0,0 +1,34 @@ +## +# 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 + +run: all + + +all: + ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_ERROR} nm -j main | grep '\.eh$$'| ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main +clean: + rm -rf *.o main main-* *.nm diff --git a/ld64/unit-tests/test-cases/eh-strip-test/comment.txt b/ld64/unit-tests/test-cases/eh-strip-test/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-strip-test/comment.txt @@ -0,0 +1,21 @@ +Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) + +__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +__ZN9__gnu_cxx13new_allocatorIiED2Ev +__ZNSt6vectorIiSaIiEEC1ERKS0_ +__ZNSaIiEC1ERKS_ +__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +__ZNSaIiED2Ev +__ZNSt12_Vector_baseIiSaIiEED2Ev +__ZNSaIiEC1Ev +__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +__ZNSt6vectorIiSaIiEED1Ev +__ZNSaIiED1Ev +__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +__ZN9__gnu_cxx13new_allocatorIiEC2Ev +__ZNSaIiEC2ERKS_ +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/unit-tests/test-cases/eh-strip-test/main.cxx b/ld64/unit-tests/test-cases/eh-strip-test/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-strip-test/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/ld64/unit-tests/test-cases/eh_frame/Makefile b/ld64/unit-tests/test-cases/eh_frame/Makefile new file mode 100644 index 0000000..6216d8e --- /dev/null +++ b/ld64/unit-tests/test-cases/eh_frame/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# strips .eh symbols out of final linked images, +# even when an intermediate ld -r was used. +# + +run: all + +all: + ${CXX} ${CCXXFLAGS} foo.cxx -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + nm libfoo.dylib | grep '.eh' | ${FAIL_IF_STDIN} + ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + ${LD} -r foo.o -o foo2.o + ${FAIL_IF_BAD_OBJ} foo2.o + ${CXX} ${CCXXFLAGS} foo2.o bar.cxx -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + nm libfoobar.dylib | grep '.eh' | ${PASS_IFF_EMPTY} + +clean: + rm *.dylib *.o diff --git a/ld64/unit-tests/test-cases/eh_frame/bar.cxx b/ld64/unit-tests/test-cases/eh_frame/bar.cxx new file mode 100644 index 0000000..9623da1 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh_frame/bar.cxx @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +static void bar1() +{ + fprintf(stderr, "hello\n"); +} + +void bar2() +{ + bar1(); + fprintf(stderr, "world\n"); +} + + diff --git a/ld64/unit-tests/test-cases/eh_frame/foo.cxx b/ld64/unit-tests/test-cases/eh_frame/foo.cxx new file mode 100644 index 0000000..d1e2dd1 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh_frame/foo.cxx @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +static void foo1() +{ + fprintf(stderr, "hello\n"); +} + +void foo2() +{ + foo1(); + fprintf(stderr, "world\n"); +} + + diff --git a/ld64/unit-tests/test-cases/empty-object/Makefile b/ld64/unit-tests/test-cases/empty-object/Makefile new file mode 100644 index 0000000..4142b8c --- /dev/null +++ b/ld64/unit-tests/test-cases/empty-object/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can handle an empty (no load commands) .o file +# + +run: all + +all: + touch empty.s + as -arch ${ARCH} -n empty.s -o empty.o + ${CC} ${CCFLAGS} main.c empty.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main empty.s empty.o diff --git a/ld64/unit-tests/test-cases/empty-object/main.c b/ld64/unit-tests/test-cases/empty-object/main.c new file mode 100644 index 0000000..76e8197 --- /dev/null +++ b/ld64/unit-tests/test-cases/empty-object/main.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/ld64/unit-tests/test-cases/end-label/Makefile b/ld64/unit-tests/test-cases/end-label/Makefile new file mode 100644 index 0000000..69f51a7 --- /dev/null +++ b/ld64/unit-tests/test-cases/end-label/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld maintains two symbols with the same address and in different sections +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.s -c -o foo.o + ${CC} ${CCFLAGS} bar.s -c -o bar.o + ${LD} -r foo.o bar.o -o foobar.o + nm -m foobar.o | grep _next | grep __other | ${FAIL_IF_EMPTY} + ${LD} -r foobar.o -o foobar2.o + nm -m foobar2.o | grep _next | grep __other | ${PASS_IFF_STDIN} + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/end-label/bar.s b/ld64/unit-tests/test-cases/end-label/bar.s new file mode 100644 index 0000000..db389c4 --- /dev/null +++ b/ld64/unit-tests/test-cases/end-label/bar.s @@ -0,0 +1,7 @@ + + .section __DATA,__other,regular + + .globl _next +_next: + nop + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/end-label/foo.s b/ld64/unit-tests/test-cases/end-label/foo.s new file mode 100644 index 0000000..888af5f --- /dev/null +++ b/ld64/unit-tests/test-cases/end-label/foo.s @@ -0,0 +1,11 @@ + + .data + + .globl _start + .globl _end +_start: + .long 0 + .long 0 +_end: + + diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile b/ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile new file mode 100644 index 0000000..cf300a3 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests the use of wildcards in exported symbol lists and dead stripping +# + +run: all + +all: + ${CC} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_AB*' -dead_strip + nm -j -f libfoo.dylib | grep _good | ${FAIL_IF_EMPTY} + nm -j -f libfoo.dylib | grep _bad | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c b/ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c new file mode 100644 index 0000000..6bb8d95 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void good() {} +void bad() {} + + +void ABC() {} +void ABD() { good(); } +void DEF() {} +void DEG() { bad(); } + diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile b/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile new file mode 100644 index 0000000..10d1eb1 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile @@ -0,0 +1,78 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests the use of wildcards in exported symbol lists +# + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*bar' + nm -j -g -f libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f?o' + nm -j -g -f libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*' + nm -j -g -f libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f*o*' + nm -j -g -f libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,_foo -Wl,-exported_symbol -Wl,'_*bar' + nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list list5 + nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-unexported_symbol -Wl,'_*2*' + nm -j -g -f libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[abcdef]o' + nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-f]o' + nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-z]o' + nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-fnop]o' + nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect1 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect1 new file mode 100644 index 0000000..75aadb3 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect1 @@ -0,0 +1,2 @@ +_foo2bar +_foobar diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect2 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect2 new file mode 100644 index 0000000..09883f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect2 @@ -0,0 +1,3 @@ +_fao +_ffo +_foo diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect3 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect3 new file mode 100644 index 0000000..478bf69 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect3 @@ -0,0 +1,4 @@ +_foo +_foo2 +_foo2bar +_foobar diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect4 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect4 new file mode 100644 index 0000000..b78c206 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect4 @@ -0,0 +1,6 @@ +_fao +_ffo +_foo +_foo2 +_foo2bar +_foobar diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect5 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect5 new file mode 100644 index 0000000..cc935a4 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect5 @@ -0,0 +1,3 @@ +_foo +_foo2bar +_foobar diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect6 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect6 new file mode 100644 index 0000000..bdf1d31 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect6 @@ -0,0 +1,4 @@ +_fao +_ffo +_foo +_foobar diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect7 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect7 new file mode 100644 index 0000000..c691c40 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect7 @@ -0,0 +1,2 @@ +_fao +_ffo diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect8 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect8 new file mode 100644 index 0000000..09883f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/expect8 @@ -0,0 +1,3 @@ +_fao +_ffo +_foo diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/foo.c b/ld64/unit-tests/test-cases/exported-symbols-wildcards/foo.c new file mode 100644 index 0000000..bc5be8e --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/foo.c @@ -0,0 +1,55 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int foo() +{ + return 1; +} + +int foo2() +{ + return 1; +} + +int foobar() +{ + return 1; +} + +int foo2bar() +{ + return 1; +} + +int fao() +{ + return 1; +} + +int ffo() +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/list5 b/ld64/unit-tests/test-cases/exported-symbols-wildcards/list5 new file mode 100644 index 0000000..a6776d2 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/list5 @@ -0,0 +1,2 @@ +_foo +_*bar diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile b/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile new file mode 100644 index 0000000..25a50b4 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -exported_symbols_list can be used with a file with Mac (0x0D) line endings +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib + nm -jg libtest.dylib | grep _ > test.nm + diff test.nm expected.nm + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib test.nm diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-eol/expected.nm b/ld64/unit-tests/test-cases/exported_symbols_list-eol/expected.nm new file mode 100644 index 0000000..b21cbbf --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-eol/expected.nm @@ -0,0 +1,2 @@ +_common_global2 +_func_global2 diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-eol/test.c b/ld64/unit-tests/test-cases/exported_symbols_list-eol/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-eol/test.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-eol/test.exp b/ld64/unit-tests/test-cases/exported_symbols_list-eol/test.exp new file mode 100644 index 0000000..6ab1532 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-eol/test.exp @@ -0,0 +1 @@ +_func_global2 _common_global2 \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-hidden/Makefile b/ld64/unit-tests/test-cases/exported_symbols_list-hidden/Makefile new file mode 100644 index 0000000..c27287d --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-hidden/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -exported_symbols_list of a hidden symbol causes a warning +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib 2>warning.log + grep _func_hidden1 warning.log | ${FAIL_IF_EMPTY} + grep _common_hidden1 warning.log | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib warning.log diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.c b/ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.exp b/ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.exp new file mode 100644 index 0000000..f45858b --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-hidden/test.exp @@ -0,0 +1,4 @@ +_func_global1 +_func_hidden1 +_common_global1 +_common_hidden1 diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-r/Makefile b/ld64/unit-tests/test-cases/exported_symbols_list-r/Makefile new file mode 100644 index 0000000..96ef763 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-r/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -exported_symbols_list can be used with -r +# to reduce visibility of symbols and any missing symbols +# causes an error +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${FAIL_IF_BAD_OBJ} test.o + ${LD} -arch ${ARCH} -r -keep_private_externs test.o -exported_symbols_list test.exp -o test-r.o + ${FAIL_IF_BAD_OBJ} test-r.o + # verify common not in export-list got demoted to private extern + nm -m test-r.o | grep "private external _common_global1" | ${FAIL_IF_EMPTY} + # verify only _common_global1 and _func_global1 changed + nm -m test.o | egrep -v '_common_global1|_func_global1|__eh_frame|__data' > test.nm + nm -m test-r.o | egrep -v '_common_global1|_func_global1|__eh_frame|__data' > test-r.nm + diff test.nm test-r.nm + # verify without -keep_private_externs that commons stay private extern + ${LD} -arch ${ARCH} -r test.o -exported_symbols_list test.exp -o test-rr.o + nm -m test-rr.o | grep _common_hidden | grep ') external' | ${FAIL_IF_STDIN} + nm -m test-rr.o | grep _common_global1 | grep ') external' | ${FAIL_IF_STDIN} + # should error out if told to export unavailable symbol + ${FAIL_IFF_SUCCESS} ${LD} -arch ${ARCH} -r test.o -exported_symbols_list test-bad.exp -o test2.o 2>/dev/null + +clean: + rm -rf test.o test-r.o test-rr.o test.nm test-r.nm test2.o diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp b/ld64/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp new file mode 100644 index 0000000..73154ba --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp @@ -0,0 +1,3 @@ +_bar +_baz +_foobar diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-r/test.c b/ld64/unit-tests/test-cases/exported_symbols_list-r/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-r/test.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-r/test.exp b/ld64/unit-tests/test-cases/exported_symbols_list-r/test.exp new file mode 100644 index 0000000..93e7e17 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported_symbols_list-r/test.exp @@ -0,0 +1,2 @@ +_func_global2 +_common_global2 diff --git a/ld64/unit-tests/test-cases/external-reloc-sorting/Makefile b/ld64/unit-tests/test-cases/external-reloc-sorting/Makefile new file mode 100644 index 0000000..d9b92d5 --- /dev/null +++ b/ld64/unit-tests/test-cases/external-reloc-sorting/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to see that external relocations +# are sorted so that dyld only has to look up symbols once. +# The machochecker tool verifies that the relocs are sorted. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main libfoo.dylib diff --git a/ld64/unit-tests/test-cases/external-reloc-sorting/foo.c b/ld64/unit-tests/test-cases/external-reloc-sorting/foo.c new file mode 100644 index 0000000..bc28725 --- /dev/null +++ b/ld64/unit-tests/test-cases/external-reloc-sorting/foo.c @@ -0,0 +1,5 @@ + +int foo = 1; +int bar = 2; +int baz = 3; + diff --git a/ld64/unit-tests/test-cases/external-reloc-sorting/main.c b/ld64/unit-tests/test-cases/external-reloc-sorting/main.c new file mode 100644 index 0000000..6c68c4c --- /dev/null +++ b/ld64/unit-tests/test-cases/external-reloc-sorting/main.c @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// in libfoo.dylib +extern int foo; +extern int bar; +extern int baz; + +// this initialilzed data will result in external relocations +// alternating the values, will create relocs that need sorting +int* array[] = { &foo, &bar, &baz, &foo, &bar, &baz, &foo, &bar, &baz }; + + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/filelist/Makefile b/ld64/unit-tests/test-cases/filelist/Makefile new file mode 100644 index 0000000..0b1a743 --- /dev/null +++ b/ld64/unit-tests/test-cases/filelist/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +PWD = $(shell pwd) + +# +# The point of this test is to check the two forms of the +# -filelist option +# + +run: all + +all: + ${CC} ${CCFLAGS} -c hello.c -o hello-${ARCH}.o + ${FAIL_IF_BAD_OBJ} hello-${ARCH}.o + echo "${PWD}/hello-${ARCH}.o" > "${PWD}/filelist1" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}" + ${FAIL_IF_BAD_MACHO} hello-${ARCH} + echo "hello-${ARCH}.o" > "${PWD}/filelist2" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-* *.o filelist1 filelist2 diff --git a/ld64/unit-tests/test-cases/filelist/comment.txt b/ld64/unit-tests/test-cases/filelist/comment.txt new file mode 100644 index 0000000..f193b11 --- /dev/null +++ b/ld64/unit-tests/test-cases/filelist/comment.txt @@ -0,0 +1 @@ +The point of this test is to check the two forms of the -filelist option diff --git a/ld64/unit-tests/test-cases/filelist/hello.c b/ld64/unit-tests/test-cases/filelist/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/ld64/unit-tests/test-cases/filelist/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/ld64/unit-tests/test-cases/flat-dylib/Makefile b/ld64/unit-tests/test-cases/flat-dylib/Makefile new file mode 100644 index 0000000..b1015b2 --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-dylib/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a small dylib -flat_namespace and +# indirect internal references. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib -flat_namespace + otool -Iv libmain.dylib | grep _foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libmain.dylib + +clean: + rm *.dylib diff --git a/ld64/unit-tests/test-cases/flat-dylib/main.c b/ld64/unit-tests/test-cases/flat-dylib/main.c new file mode 100644 index 0000000..6014829 --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-dylib/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void foo() {} + + +int main() +{ + foo(); + fprintf(stdout, "hello\n"); +} diff --git a/ld64/unit-tests/test-cases/flat-indirect-undefines/Makefile b/ld64/unit-tests/test-cases/flat-indirect-undefines/Makefile new file mode 100644 index 0000000..a3715e0 --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-indirect-undefines/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that when linking a main executable for flat-namespace +# that undefines in loaded flat-namespace dylibs are resolved. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -flat_namespace -dynamiclib -o libfoo.dylib -undefined suppress + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static bar.o -o libbar.a + # test that linking main executable -twolevel_namespace does not pull in bar() + ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.a -o main + nm -mn main | grep _bar | ${FAIL_IF_STDIN} + # test that linking dylib -flat_namespace does not pull in bar() + ${CC} ${CCFLAGS} main.c -flat_namespace libfoo.dylib libbar.a -dynamiclib -o main.dylib + nm -mn main.dylib | grep _bar | ${FAIL_IF_STDIN} + # test that linking main executable -flat_namespace pulls in bar() + ${CC} ${CCFLAGS} main.c -flat_namespace libfoo.dylib libbar.a -o main_flat + nm -mn main_flat | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main_flat + +clean: + rm libfoo.dylib libbar.a bar.o main main_flat main.dylib diff --git a/ld64/unit-tests/test-cases/flat-indirect-undefines/bar.c b/ld64/unit-tests/test-cases/flat-indirect-undefines/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-indirect-undefines/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/ld64/unit-tests/test-cases/flat-indirect-undefines/foo.c b/ld64/unit-tests/test-cases/flat-indirect-undefines/foo.c new file mode 100644 index 0000000..39df2ea --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-indirect-undefines/foo.c @@ -0,0 +1,8 @@ + +extern void bar(); + +void foo() +{ + bar(); +} + diff --git a/ld64/unit-tests/test-cases/flat-indirect-undefines/main.c b/ld64/unit-tests/test-cases/flat-indirect-undefines/main.c new file mode 100644 index 0000000..246fed4 --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-indirect-undefines/main.c @@ -0,0 +1,10 @@ +#include + +extern void foo(); + + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/flat-main/Makefile b/ld64/unit-tests/test-cases/flat-main/Makefile new file mode 100644 index 0000000..d31b7ab --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-main/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program -flat_namespace and +# does not indirect internal references. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main-${ARCH} -flat_namespace + otool -Iv main-${ARCH} | grep _foo | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + +clean: + rm main-* diff --git a/ld64/unit-tests/test-cases/flat-main/main.c b/ld64/unit-tests/test-cases/flat-main/main.c new file mode 100644 index 0000000..6014829 --- /dev/null +++ b/ld64/unit-tests/test-cases/flat-main/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void foo() {} + + +int main() +{ + foo(); + fprintf(stdout, "hello\n"); +} diff --git a/ld64/unit-tests/test-cases/got-elimination/Makefile b/ld64/unit-tests/test-cases/got-elimination/Makefile new file mode 100644 index 0000000..3f70e74 --- /dev/null +++ b/ld64/unit-tests/test-cases/got-elimination/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld can remove non-lazy pointers for x86_64 +# + +all: all-${ARCH} + +all-armv6: all-true + +all-ppc: all-true + +all-ppc64: all-true + +all-i386: all-true + +all-true: + ${PASS_IFF} true + +all-x86_64: + ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib + otool -Iv libfoobar.dylib | grep 0x | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib -flat_namespace + otool -Iv libfoobar.dylib | grep 0x | ${PASS_IFF_STDIN} + +clean: + rm -rf libfoobar.dylib diff --git a/ld64/unit-tests/test-cases/got-elimination/bar.c b/ld64/unit-tests/test-cases/got-elimination/bar.c new file mode 100644 index 0000000..781c6fd --- /dev/null +++ b/ld64/unit-tests/test-cases/got-elimination/bar.c @@ -0,0 +1,28 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bar1 = 1; +int bar2 = 2; +int bar3 = 3; + diff --git a/ld64/unit-tests/test-cases/got-elimination/foo.c b/ld64/unit-tests/test-cases/got-elimination/foo.c new file mode 100644 index 0000000..45675a3 --- /dev/null +++ b/ld64/unit-tests/test-cases/got-elimination/foo.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int bar1; +extern int bar2; // just under 2GB array +extern int bar3; + +int getbar1() +{ + return bar1; +} + +int getbar2() +{ + return bar2; +} + +int getbar3() +{ + return bar3; +} diff --git a/ld64/unit-tests/test-cases/header-pad/Makefile b/ld64/unit-tests/test-cases/header-pad/Makefile new file mode 100644 index 0000000..d8cdd51 --- /dev/null +++ b/ld64/unit-tests/test-cases/header-pad/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} -Wl,-headerpad -Wl,0x3000 + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-* diff --git a/ld64/unit-tests/test-cases/header-pad/comment.txt b/ld64/unit-tests/test-cases/header-pad/comment.txt new file mode 100644 index 0000000..79114eb --- /dev/null +++ b/ld64/unit-tests/test-cases/header-pad/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a hello-world program with no errors (or crashes) diff --git a/ld64/unit-tests/test-cases/header-pad/hello.c b/ld64/unit-tests/test-cases/header-pad/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/ld64/unit-tests/test-cases/header-pad/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} diff --git a/ld64/unit-tests/test-cases/hello-world/Makefile b/ld64/unit-tests/test-cases/hello-world/Makefile new file mode 100644 index 0000000..a1ade02 --- /dev/null +++ b/ld64/unit-tests/test-cases/hello-world/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} + ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + +clean: + rm hello-* diff --git a/ld64/unit-tests/test-cases/hello-world/comment.txt b/ld64/unit-tests/test-cases/hello-world/comment.txt new file mode 100644 index 0000000..79114eb --- /dev/null +++ b/ld64/unit-tests/test-cases/hello-world/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a hello-world program with no errors (or crashes) diff --git a/ld64/unit-tests/test-cases/hello-world/hello.c b/ld64/unit-tests/test-cases/hello-world/hello.c new file mode 100644 index 0000000..e2f0fe9 --- /dev/null +++ b/ld64/unit-tests/test-cases/hello-world/hello.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/implicit-common2/Makefile.newtest b/ld64/unit-tests/test-cases/implicit-common2/Makefile.newtest new file mode 100644 index 0000000..af93da1 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common2/Makefile.newtest @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null + #ranlib libtest-${ARCH}.a + #${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + #${PASS_IFF_GOOD_MACHO} a-${ARCH} + + ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o a-${ARCH}.o 2>/dev/null + ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a + ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a diff --git a/ld64/unit-tests/test-cases/implicit-common2/a.c b/ld64/unit-tests/test-cases/implicit-common2/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common2/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/unit-tests/test-cases/implicit-common2/comment.txt b/ld64/unit-tests/test-cases/implicit-common2/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common2/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/implicit-common2/test.c b/ld64/unit-tests/test-cases/implicit-common2/test.c new file mode 100644 index 0000000..94578c6 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common2/test.c @@ -0,0 +1,26 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; +extern int main(); diff --git a/ld64/unit-tests/test-cases/implicit-common3/Makefile b/ld64/unit-tests/test-cases/implicit-common3/Makefile new file mode 100644 index 0000000..6785960 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common3/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_BAD_OBJ} test-${ARCH}.o + ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_BAD_OBJ} a-${ARCH}.o + ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null + ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a + ${CC} ${CCFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a a-* diff --git a/ld64/unit-tests/test-cases/implicit-common3/a.c b/ld64/unit-tests/test-cases/implicit-common3/a.c new file mode 100644 index 0000000..110842f --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common3/a.c @@ -0,0 +1,8 @@ +extern int common_var; +int *fn(); + +int +main(int argc, char **argv) +{ + return 0!=&common_var; +} diff --git a/ld64/unit-tests/test-cases/implicit-common3/comment.txt b/ld64/unit-tests/test-cases/implicit-common3/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common3/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/implicit-common3/test.c b/ld64/unit-tests/test-cases/implicit-common3/test.c new file mode 100644 index 0000000..e0fd7e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common3/test.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +struct abc { + int a; + int b; + int c; +} struct_var; + +int common_var; +extern const int defined_var; + +int *fn() +{ + return &common_var; +} diff --git a/ld64/unit-tests/test-cases/implicit-common4/Makefile.newtest b/ld64/unit-tests/test-cases/implicit-common4/Makefile.newtest new file mode 100644 index 0000000..d70c634 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common4/Makefile.newtest @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o + #${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + #${PASS_IFF_GOOD_MACHO} a-${ARCH} + + ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o a-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a diff --git a/ld64/unit-tests/test-cases/implicit-common4/a.c b/ld64/unit-tests/test-cases/implicit-common4/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common4/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/unit-tests/test-cases/implicit-common4/comment.txt b/ld64/unit-tests/test-cases/implicit-common4/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common4/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/implicit-common4/test.c b/ld64/unit-tests/test-cases/implicit-common4/test.c new file mode 100644 index 0000000..94578c6 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common4/test.c @@ -0,0 +1,26 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; +extern int main(); diff --git a/ld64/unit-tests/test-cases/implicit-common5/Makefile.newtest b/ld64/unit-tests/test-cases/implicit-common5/Makefile.newtest new file mode 100644 index 0000000..d5b6d73 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common5/Makefile.newtest @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *a diff --git a/ld64/unit-tests/test-cases/implicit-common5/a.c b/ld64/unit-tests/test-cases/implicit-common5/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common5/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/unit-tests/test-cases/implicit-common5/comment.txt b/ld64/unit-tests/test-cases/implicit-common5/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common5/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/implicit-common5/test.c b/ld64/unit-tests/test-cases/implicit-common5/test.c new file mode 100644 index 0000000..f34267a --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit-common5/test.c @@ -0,0 +1,25 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; diff --git a/ld64/unit-tests/test-cases/implicit_dylib/Makefile b/ld64/unit-tests/test-cases/implicit_dylib/Makefile new file mode 100644 index 0000000..c982885 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit_dylib/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + + +# Verify -no_implicit_dylibs option +# add option to disable implicit load commands for indirectly used public dylibs +# + + +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 + # 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} + # verify that -no_implicit_dylibs causes main to get bar from libfoo + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-no_implicit_dylibs -L. + nm -m main | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/unit-tests/test-cases/implicit_dylib/bar.c b/ld64/unit-tests/test-cases/implicit_dylib/bar.c new file mode 100644 index 0000000..e2164a2 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit_dylib/bar.c @@ -0,0 +1,7 @@ + + + +void bar() +{ +} + diff --git a/ld64/unit-tests/test-cases/implicit_dylib/foo.c b/ld64/unit-tests/test-cases/implicit_dylib/foo.c new file mode 100644 index 0000000..1624757 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit_dylib/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/ld64/unit-tests/test-cases/implicit_dylib/main.c b/ld64/unit-tests/test-cases/implicit_dylib/main.c new file mode 100644 index 0000000..1f50353 --- /dev/null +++ b/ld64/unit-tests/test-cases/implicit_dylib/main.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + bar(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/indirect-dylib/Makefile b/ld64/unit-tests/test-cases/indirect-dylib/Makefile new file mode 100644 index 0000000..e3056a5 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a sanity check that an indirect +# library is not accidentally searched for symbols. +# +# wrong error message when symbol is found in unused indirect library# +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main libfoo.dylib 2> fail.log + grep ordinal fail.log | ${PASS_IFF_EMPTY} + +clean: + rm *.dylib main fail.log diff --git a/ld64/unit-tests/test-cases/indirect-dylib/bar.c b/ld64/unit-tests/test-cases/indirect-dylib/bar.c new file mode 100644 index 0000000..f39ee21 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-dylib/bar.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// function called by a loaded bundle +int bar() +{ + return 1; +} + diff --git a/ld64/unit-tests/test-cases/indirect-dylib/comment.txt b/ld64/unit-tests/test-cases/indirect-dylib/comment.txt new file mode 100644 index 0000000..311aa79 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-dylib/comment.txt @@ -0,0 +1,4 @@ +The point of this test is a sanity check that an indirect +library is not accidentally searched for symbols. + + wrong error message when symbol is found in unused indirect library# diff --git a/ld64/unit-tests/test-cases/indirect-dylib/foo.c b/ld64/unit-tests/test-cases/indirect-dylib/foo.c new file mode 100644 index 0000000..ba78c28 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-dylib/foo.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/ld64/unit-tests/test-cases/indirect-dylib/main.c b/ld64/unit-tests/test-cases/indirect-dylib/main.c new file mode 100644 index 0000000..13f57d7 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-dylib/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void bar(); + +int main() +{ + bar(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/indirect-path-search/Makefile b/ld64/unit-tests/test-cases/indirect-path-search/Makefile new file mode 100644 index 0000000..e79eb0f --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-path-search/Makefile @@ -0,0 +1,106 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -F and -L work when finding indirect libraries +# + + +run: all + +all: + +# build foo that re-exports bar + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar + ${FAIL_IF_BAD_MACHO} libfoo.dylib + +# build an alternate libbar that also has baz + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/libbar.dylib -install_name libbar.dylib + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + +# build an executable that depends on a symbol in the alternate bar to validate that -L is used for indirect dylibs + ${CC} ${CCFLAGS} main.c -o main -lfoo -Lhide -L. + ${FAIL_IF_BAD_MACHO} main + + + +# build Foo.framework that re-exports Bar.framework + mkdir -p Bar.framework Foo.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" + ${FAIL_IF_BAD_MACHO} Bar.framework/Bar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar + ${FAIL_IF_BAD_MACHO} Foo.framework/Foo + +# build an alternate Bar.framework that also has baz + mkdir -p hide/Bar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" + ${FAIL_IF_BAD_MACHO} hide/Bar.framework/Bar + +# build an executable that depends on a symbol in the alternate Bar.framework to validate that -F is used for indirect dylibs + ${CC} ${CCFLAGS} main.c -o main -Fhide -F. -framework Foo + ${FAIL_IF_BAD_MACHO} main + + + +# build foo that links against bar + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + +# build an alternate libbar that also has baz + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/libbar.dylib -install_name libbar.dylib + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + +# build a flat executable that depends on a symbol in the alternate bar to validate that -L is used for indirect dylibs + ${CC} ${CCFLAGS} -flat_namespace main.c -o main -lfoo -Lhide -L. + ${FAIL_IF_BAD_MACHO} main + + + +# build Foo.framework that re-exports libbar.dylib embedded in framework + mkdir -p Foo.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o Foo.framework/libbar.dylib -install_name "`pwd`/Foo.framework/libbar.dylib" + ${FAIL_IF_BAD_MACHO} Foo.framework/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. Foo.framework/libbar.dylib -sub_library libbar + ${FAIL_IF_BAD_MACHO} Foo.framework/Foo + +# build an alternate libbar.dylib that does not have baz + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + +# build an executable that depends on a symbol not in the alternate libbar.dylib to validate dylibs embedded in frameworks are not searched for + ${CC} ${CCFLAGS} main.c -o main -Lhide -F. -framework Foo + ${PASS_IFF_GOOD_MACHO} main + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework main diff --git a/ld64/unit-tests/test-cases/indirect-path-search/bar.c b/ld64/unit-tests/test-cases/indirect-path-search/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-path-search/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/indirect-path-search/baz.c b/ld64/unit-tests/test-cases/indirect-path-search/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-path-search/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/indirect-path-search/foo.c b/ld64/unit-tests/test-cases/indirect-path-search/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-path-search/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/indirect-path-search/main.c b/ld64/unit-tests/test-cases/indirect-path-search/main.c new file mode 100644 index 0000000..f02701a --- /dev/null +++ b/ld64/unit-tests/test-cases/indirect-path-search/main.c @@ -0,0 +1,8 @@ +extern int foo (); +extern int bar (); +extern int baz (); + +int main (void) +{ + return foo() + bar() + baz(); +} diff --git a/ld64/unit-tests/test-cases/interposable_list/Makefile b/ld64/unit-tests/test-cases/interposable_list/Makefile new file mode 100644 index 0000000..2fe99a8 --- /dev/null +++ b/ld64/unit-tests/test-cases/interposable_list/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -interposable and -interposable_list work +# + +run: all + +all: + # by default, no test* functions should go through stubs + ${CC} ${CCFLAGS} test.c -dynamiclib -o libtest.dylib + # -interposable should make all four test* functions go through stubs + ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable -o libtest.dylib + otool -Iv libtest.dylib | grep '4 entries' | ${FAIL_IF_EMPTY} + otool -Iv libtest.dylib | grep '_test' | ${FAIL_IF_EMPTY} + # -interposable_list should make just two test* functions go through stubs + ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable_list test.exp -o libtest.dylib + otool -Iv libtest.dylib | grep '2 entries' | ${FAIL_IF_EMPTY} + otool -Iv libtest.dylib | grep '_test3' | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + + +clean: + rm libtest.dylib diff --git a/ld64/unit-tests/test-cases/interposable_list/test.c b/ld64/unit-tests/test-cases/interposable_list/test.c new file mode 100644 index 0000000..937420e --- /dev/null +++ b/ld64/unit-tests/test-cases/interposable_list/test.c @@ -0,0 +1,57 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +const char kMyStr[] = "hello"; + +int test1() +{ + return 10; +} + +int test2() +{ + return 10; +} + +int test3() +{ + return 10; +} + +int test4() +{ + return 10; +} + +const char* getstr() +{ + test1(); + test2(); + test3(); + test4(); + return kMyStr; +} + + diff --git a/ld64/unit-tests/test-cases/interposable_list/test.exp b/ld64/unit-tests/test-cases/interposable_list/test.exp new file mode 100644 index 0000000..8f104f1 --- /dev/null +++ b/ld64/unit-tests/test-cases/interposable_list/test.exp @@ -0,0 +1,2 @@ +_test1 +_test2 diff --git a/ld64/unit-tests/test-cases/large-data/Makefile b/ld64/unit-tests/test-cases/large-data/Makefile new file mode 100644 index 0000000..408a2da --- /dev/null +++ b/ld64/unit-tests/test-cases/large-data/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld can link > 4GB zero fill section +# + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq (,${findstring 64,$(ARCH)}) + 32BIT_SHOULD_FAIL = ${FAIL_IF_SUCCESS} +else + 32BIT_SHOULD_FAIL = +endif + + +run: all + +all: + ${CC} ${CCFLAGS} test1.c -c -o test1.o + ${CC} ${CCFLAGS} test2.c -c -o test2.o + ${CC} ${CCFLAGS} test3.c -c -o test3.o + ${CC} ${CCFLAGS} test4.c -c -o test4.o + ${32BIT_SHOULD_FAIL} ${CC} ${CCFLAGS} test1.o test2.o test3.o test4.o -dynamiclib -o libtest.dylib 2> fail.log + ${PASS_IFF} true + +clean: + rm -rf test*.o libtest.dylib fail.log diff --git a/ld64/unit-tests/test-cases/large-data/test1.c b/ld64/unit-tests/test-cases/large-data/test1.c new file mode 100644 index 0000000..2d9ec94 --- /dev/null +++ b/ld64/unit-tests/test-cases/large-data/test1.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int mediumarray1[1000]; +int bigarray1[500000000]; // just under 2GB array +int small1; + +int getbig1() +{ + return bigarray1[0]; +} + +int getmedium1() +{ + return mediumarray1[0]; +} + +int getsmall1() +{ + return small1; +} diff --git a/ld64/unit-tests/test-cases/large-data/test2.c b/ld64/unit-tests/test-cases/large-data/test2.c new file mode 100644 index 0000000..d97bed1 --- /dev/null +++ b/ld64/unit-tests/test-cases/large-data/test2.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bigarray2[500000000]; // just under 2GB array +int small2; + +int getbig2() +{ + return bigarray2[0]; +} + + +int getsmall2() +{ + return small2; +} diff --git a/ld64/unit-tests/test-cases/large-data/test3.c b/ld64/unit-tests/test-cases/large-data/test3.c new file mode 100644 index 0000000..b7ca398 --- /dev/null +++ b/ld64/unit-tests/test-cases/large-data/test3.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bigarray3[500000000]; // just under 2GB array +int small3; + +int getbig3() +{ + return bigarray3[0]; +} + + +int getsmall3() +{ + return small3; +} diff --git a/ld64/unit-tests/test-cases/large-data/test4.c b/ld64/unit-tests/test-cases/large-data/test4.c new file mode 100644 index 0000000..d879c5c --- /dev/null +++ b/ld64/unit-tests/test-cases/large-data/test4.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bigarray4[500000000]; // just under 2GB array +int small4; + +int getbig4() +{ + return bigarray4[0]; +} + + +int getsmall4() +{ + return small4; +} diff --git a/ld64/unit-tests/test-cases/late-link-error/Makefile b/ld64/unit-tests/test-cases/late-link-error/Makefile new file mode 100644 index 0000000..e3a0d65 --- /dev/null +++ b/ld64/unit-tests/test-cases/late-link-error/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# The point of this test is a sanity check that if +# ld errors out during linking, that no output file is remaining +# + +run: all + +all: + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} link_error.s -dynamiclib -o link_error-${ARCH} 2> fail.log + ${FAIL_IFF} cat link_error-${ARCH} 2> fail.log + +clean: + rm link_error-* fail.log diff --git a/ld64/unit-tests/test-cases/late-link-error/comment.txt b/ld64/unit-tests/test-cases/late-link-error/comment.txt new file mode 100644 index 0000000..716686d --- /dev/null +++ b/ld64/unit-tests/test-cases/late-link-error/comment.txt @@ -0,0 +1,2 @@ +The point of this test is a sanity check that if +ld errors out during linking, that no output file is remaining diff --git a/ld64/unit-tests/test-cases/late-link-error/link_error.s b/ld64/unit-tests/test-cases/late-link-error/link_error.s new file mode 100644 index 0000000..05d80f1 --- /dev/null +++ b/ld64/unit-tests/test-cases/late-link-error/link_error.s @@ -0,0 +1,22 @@ + + +#if __ppc__ || __ppc64__ + ; illegal absolute load +_foo: lis r2,ha16(_strcmp) +#endif + +#if __i386__ + // illegal absolute load +_foo: movl _strcmp, %eax +#endif + + +#if __x86_64__ + // illegal external load +_foo: movl _strcmp(%rip), %eax +#endif + +#if __arm__ + ; illegal absolute load +_foo: ldr r2, _strcmp +#endif diff --git a/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile b/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile new file mode 100644 index 0000000..975ad29 --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Verify that -lazy_library fails if an objc class is referenced +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -dynamiclib -o libfoo.dylib -framework Foundation + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.m -Wl,-lazy_library,libfoo.dylib -o main -framework Foundation 2> fail.log + ${CC} ${CCFLAGS} main.m libfoo.dylib -o main -framework Foundation + ${PASS_IFF_GOOD_MACHO} main + + + +clean: + rm libfoo.dylib main rm fail.log diff --git a/ld64/unit-tests/test-cases/lazy-dylib-objc/foo.h b/ld64/unit-tests/test-cases/lazy-dylib-objc/foo.h new file mode 100644 index 0000000..eaa591f --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib-objc/foo.h @@ -0,0 +1,9 @@ + +#include + + +@interface Foo : NSObject + + + +@end diff --git a/ld64/unit-tests/test-cases/lazy-dylib-objc/foo.m b/ld64/unit-tests/test-cases/lazy-dylib-objc/foo.m new file mode 100644 index 0000000..35a63b1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib-objc/foo.m @@ -0,0 +1,8 @@ + +#include "foo.h" + +@implementation Foo + + + +@end diff --git a/ld64/unit-tests/test-cases/lazy-dylib-objc/main.m b/ld64/unit-tests/test-cases/lazy-dylib-objc/main.m new file mode 100644 index 0000000..0f11f68 --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib-objc/main.m @@ -0,0 +1,12 @@ +#include +#include + +#include "foo.h" + + +int main() +{ + [[Foo alloc] init]; + return 0; +} + diff --git a/ld64/unit-tests/test-cases/lazy-dylib/Makefile b/ld64/unit-tests/test-cases/lazy-dylib/Makefile new file mode 100644 index 0000000..e3d0d6a --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify that -lazy_library works for function calls +# but fails for data references +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad.c -Wl,-lazy_library,libfoo.dylib -o bad 2> fail.log + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad2.c -Wl,-lazy_library,libfoo.dylib -o bad2 2> fail.log + + + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libfoo.dylib main bad bad2 fail.log + diff --git a/ld64/unit-tests/test-cases/lazy-dylib/bad.c b/ld64/unit-tests/test-cases/lazy-dylib/bad.c new file mode 100644 index 0000000..59a13fb --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib/bad.c @@ -0,0 +1,12 @@ +#include +#include + + +extern int data; + +static int* pd = &data; + +int main() +{ + return *pd; +} diff --git a/ld64/unit-tests/test-cases/lazy-dylib/bad2.c b/ld64/unit-tests/test-cases/lazy-dylib/bad2.c new file mode 100644 index 0000000..ffd2ce1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib/bad2.c @@ -0,0 +1,13 @@ +#include +#include + + +extern int foo(); + +int main() +{ + int (*func)() = foo; + if ( func != NULL ) + (*func)(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/lazy-dylib/foo.c b/ld64/unit-tests/test-cases/lazy-dylib/foo.c new file mode 100644 index 0000000..c23c9dc --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib/foo.c @@ -0,0 +1,5 @@ + +int data = 5; + +int foo() { return 1; } +int bar() { return 1; } diff --git a/ld64/unit-tests/test-cases/lazy-dylib/main.c b/ld64/unit-tests/test-cases/lazy-dylib/main.c new file mode 100644 index 0000000..8546854 --- /dev/null +++ b/ld64/unit-tests/test-cases/lazy-dylib/main.c @@ -0,0 +1,18 @@ +#include +#include + + +extern int foo(); +extern int bar(); + +int main() +{ + // two regular external function calls + void* x = malloc(16); + free(x); + // two lazy dylib external function calls + int result = foo(); + fprintf(stderr, "foo() returned %d\n", result); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment/Makefile b/ld64/unit-tests/test-cases/literals-coalesce-alignment/Makefile new file mode 100644 index 0000000..15f3307 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o + + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-align-3-${ARCH}.o | grep 'align:' > align-3 + + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-r-${ARCH}.o | grep 'align:' > align-r + + ${PASS_IFF} diff align-3 align-r + +clean: + rm -rf *.o align-3 align-r diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s b/ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s new file mode 100644 index 0000000..0dacbad --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L21: .ascii "hello\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s b/ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s new file mode 100644 index 0000000..d2661dc --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 3 +L21: .ascii "hello\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment2/Makefile b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/Makefile new file mode 100644 index 0000000..c3c8c80 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +LD=ld + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o + + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-align-3-${ARCH}.o > align-3 + + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-r-${ARCH}.o > align-r + ${PASS_IFF} diff -C 6 align-3 align-r + +clean: + rm -rf *.o align-3 align-r diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt new file mode 100644 index 0000000..3b2e3c7 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that when two cstrings are coalesced that the one with greater alignment is used. diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s new file mode 100644 index 0000000..1a55cf6 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L20: .asciz "XXX" +L22: .ascii "hell\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s new file mode 100644 index 0000000..211aa72 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 2 +L21: .ascii "hell\0" + .align 13 +L99: .ascii "\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment3/Makefile b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/Makefile new file mode 100644 index 0000000..cac20b9 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +#LD=ld64 + +# +# The point of this test is to verify that when two cstrings +# are coalesced that the one with greater alignment is used. +# + +run: all + +all: + ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o + ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o + + ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-align-3-${ARCH}.o > align-3 + + ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-r-${ARCH}.o > align-r + + ${PASS_IFF} diff -C 6 align-3 align-r + +clean: + rm -rf *.o align-3 align-r diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt new file mode 100644 index 0000000..3b2e3c7 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that when two cstrings are coalesced that the one with greater alignment is used. diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s new file mode 100644 index 0000000..1a55cf6 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .cstring +L20: .asciz "XXX" +L22: .ascii "hell\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s new file mode 100644 index 0000000..211aa72 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .cstring + .align 2 +L21: .ascii "hell\0" + .align 13 +L99: .ascii "\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce/Makefile b/ld64/unit-tests/test-cases/literals-coalesce/Makefile new file mode 100644 index 0000000..dcf1dbe --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that literals are uniqued. +# After running ld -r all duplicates should be removed. +# + +run: all + +all: + ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-${ARCH}.o | grep 'name:'| uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} + ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-r-${ARCH}.o | grep 'name:' | uniq -d | ${PASS_IFF_EMPTY} + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/literals-coalesce/literals.s b/ld64/unit-tests/test-cases/literals-coalesce/literals.s new file mode 100644 index 0000000..6e5febc --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce/literals.s @@ -0,0 +1,69 @@ +/* + * 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@ + */ + + .literal16 +L01:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654321 + +L02:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654322 + +L03:.long 22345678 + .long 87654321 + .long 12345678 + .long 87654321 + +L04:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654321 + + + .literal8 +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +L3: .long 22345678 + .long 87654321 + +L4: .long 12345678 + .long 87654321 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +L13:.long 22345678 +L14:.long 12345678 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +L23: .ascii "there\0" +L24: .ascii "hello\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce2/Makefile.newtest b/ld64/unit-tests/test-cases/literals-coalesce2/Makefile.newtest new file mode 100644 index 0000000..0f9d1b5 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce2/Makefile.newtest @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify that literals are uniqued. +# After running ld -r all duplicates should be removed. +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -only literals-${ARCH}.o | uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} + ${FAIL_IF_ERROR} ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o + ${PASS_IFF} ./test.sh literals-r-${ARCH}.o + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/literals-coalesce2/comment.txt b/ld64/unit-tests/test-cases/literals-coalesce2/comment.txt new file mode 100644 index 0000000..56cea21 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce2/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that literals are uniqued. After running ld -r all duplicates should be removed. diff --git a/ld64/unit-tests/test-cases/literals-coalesce2/literals.s b/ld64/unit-tests/test-cases/literals-coalesce2/literals.s new file mode 100644 index 0000000..b8d4354 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce2/literals.s @@ -0,0 +1,48 @@ +/* + * 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@ + */ + + .literal8 + +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +L3: .long 22345678 + .long 87654321 + +L4: .long 12345678 + .long 87654321 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +L13:.long 22345678 +L14:.long 12345678 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +L23: .ascii "there\0" +L24: .ascii "hello\0" diff --git a/ld64/unit-tests/test-cases/literals-coalesce2/test.sh b/ld64/unit-tests/test-cases/literals-coalesce2/test.sh new file mode 100755 index 0000000..57a36b7 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-coalesce2/test.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +SZ=`size "$1" | tail -n 1 | sed 's,\([0-9]*\).*,\1,'` +[ "$SZ" ] && [ "$SZ" = 54 ] && exit 0 +exit 1 diff --git a/ld64/unit-tests/test-cases/llvm-integration/Makefile b/ld64/unit-tests/test-cases/llvm-integration/Makefile new file mode 100644 index 0000000..42c71e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/Makefile @@ -0,0 +1,289 @@ +## +# Copyright (c) 2006-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. + +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 + +# +# Test the we set the stack execution bit properly. + +run: + if [ -f /Developer/usr/bin/llvm-gcc-4.2 ] ; then \ + $(MAKE) all ; \ + else \ + ${PASS_IFF} /usr/bin/true ; \ + fi + +all: zero one two three four five six seven eight nine ten \ + eleven twelve thirteen fourteen fifteen sixteen seventeen \ + eighteen nineteen twenty + + +zero: + # + # llvm : a.c : Dfoo3 + # llvm : b.c : Dfoo2 + # 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 + ${PASS_IFF_GOOD_MACHO} main.exe + +one: + # + # llvm : a1.c : Dfoo3, Ufoo4 + # llvm : b1.c : Dfoo2, Ufoo4 + # 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 + ${PASS_IFF_GOOD_MACHO} main1.exe + +two: + # + # llvm : a2.c : Dfoo3, Ufoo4 + # llvm : b2.c : Dfoo2, Dfoo4 + # 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 + ${PASS_IFF_GOOD_MACHO} main2.exe + +three: + # + # llvm : a3.c : Dfoo1, Dbar + # llvm : b3.c : Dfoo2, Ubar + # 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 + ${PASS_IFF_GOOD_MACHO} main3.exe + +four: + # + # llvm : a4.c : Dfoo3, Ufoo4 + # llvm : b4.c : Dfoo2, DLmyfoo, Ufoo4 + # 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 + ${PASS_IFF_GOOD_MACHO} main4.exe + +five: + # + # llvm : a5.c : Dfoo1, Ufoo2, Ufoo3 + # llvm : b5.c : Dfoo2 + # MachO : main5.c : Dfoo3, Ufoo1 + # + #echo "Five..." + ${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 + ${OTOOL} -tV main5.exe | grep foo3 | ${PASS_IFF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main5.exe + +six: + # + # llvm : a6.c : Dfoo1, Dfoo2 + # 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 + ${PASS_IFF_GOOD_MACHO} main6.exe + ${OTOOL} -tV main6.exe | grep foo2 | ${PASS_IFF_EMPTY} + +seven: + # + # llvm : a7.c : Dfoo1, Dfoo2, Ufoo3 + # llvm : b7.c : Dfoo3, ufoo2 + # 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 + ${PASS_IFF_GOOD_MACHO} main7.exe + +eight: + # + # llvm : a8.c : Dfoo1, Dfoo2 + # 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 + ${OTOOL} -tV main8.exe | grep foo2 | ${PASS_IFF_EMPTY} + ${OTOOL} -tV main8.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY} + +nine: + # + # llvm : a9.c : Dfoo1, Dfoo2, Dfoo3, Ufoo3, Ufoo4 + # 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 + ${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} + +ten: + # + # llvm : a10.c + # llvm : b10.c + # 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 + ${PASS_IFF_GOOD_MACHO} main10.exe + +eleven: + # + # llvm : a11.c + # 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 + ${PASS_IFF_GOOD_MACHO} main11.exe + +twelve: + # + # llvm : a12.c + # MachO : main12.c + # + #echo "Tweleve..." + ${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 + ${PASS_IFF_GOOD_MACHO} main12.exe + +thirteen: + # + # llvm : a13.cc + # 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 + +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 + ${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 + ${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 + ${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 + ${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 + ${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 b17.o + ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o + file ab17.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 + ${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o + ObjectDump -nm a18-rkpe.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} + ObjectDump -nm a18-rkpe.o | grep _func_hidden2 | grep " hidden" | ${FAIL_IF_EMPTY} + #echo verify ld -r makes hidden symbols internal (except for commons) + ${LD} -arch ${ARCH} -r a18.o -o a18-r.o + #ObjectDump -nm a18-r.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} + #ObjectDump -nm a18-r.o | grep _func_hidden2 | grep " internal" | ${FAIL_IF_EMPTY} + +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 + 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 + ar cru lib20.a a20.o b20.o + ${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe + nm main20.exe | grep _foo | ${PASS_IFF_STDIN} + + + + +clean: + rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a.c b/ld64/unit-tests/test-cases/llvm-integration/a.c new file mode 100644 index 0000000..0c96178 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a.c @@ -0,0 +1,5 @@ +int foo3() +{ + return 21; +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a1.c b/ld64/unit-tests/test-cases/llvm-integration/a1.c new file mode 100644 index 0000000..f9fb403 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a1.c @@ -0,0 +1,10 @@ +#include +#include +#include +extern int foo4(); +int foo3() +{ +/* printf ("%s\n",strerror(errno)); */ + return foo4(); +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a10.c b/ld64/unit-tests/test-cases/llvm-integration/a10.c new file mode 100644 index 0000000..0dc181e --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a10.c @@ -0,0 +1,5 @@ +extern void foo(void); + +void foo(void) +{ +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a11.c b/ld64/unit-tests/test-cases/llvm-integration/a11.c new file mode 100644 index 0000000..e95dc40 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a11.c @@ -0,0 +1,6 @@ +#include +void foo3(void) +{ + fputc ('x', stderr); + printf ("\n"); +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a12.c b/ld64/unit-tests/test-cases/llvm-integration/a12.c new file mode 100644 index 0000000..80bc1e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a12.c @@ -0,0 +1,8 @@ +#include "a12.h" + +enum E e[1000]; +void foo(void) +{ + e[1] = ONE; +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a12.h b/ld64/unit-tests/test-cases/llvm-integration/a12.h new file mode 100644 index 0000000..be43955 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a12.h @@ -0,0 +1,8 @@ +enum E + { + ZERO, + ONE + }; + +extern enum E e[1000]; +extern void foo(void); diff --git a/ld64/unit-tests/test-cases/llvm-integration/a13.cc b/ld64/unit-tests/test-cases/llvm-integration/a13.cc new file mode 100644 index 0000000..edc938a --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a13.cc @@ -0,0 +1,3 @@ +#include "a13.h" + +A::~A() {} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a13.h b/ld64/unit-tests/test-cases/llvm-integration/a13.h new file mode 100644 index 0000000..bc00448 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a13.h @@ -0,0 +1,7 @@ +#include + +class A { + public: + virtual ~A(); + void foo() { printf ("Hi\n"); } +}; diff --git a/ld64/unit-tests/test-cases/llvm-integration/a14.c b/ld64/unit-tests/test-cases/llvm-integration/a14.c new file mode 100644 index 0000000..8f4ea09 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a14.c @@ -0,0 +1 @@ +int X __attribute__((visibility("hidden"))) = 14; diff --git a/ld64/unit-tests/test-cases/llvm-integration/a15.c b/ld64/unit-tests/test-cases/llvm-integration/a15.c new file mode 100644 index 0000000..a25431b --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a15.c @@ -0,0 +1,3 @@ +void __attribute__((visibility("hidden"))) foo() +{ +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a17.c b/ld64/unit-tests/test-cases/llvm-integration/a17.c new file mode 100644 index 0000000..3cd06fd --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a17.c @@ -0,0 +1,4 @@ + +int a = 0; +int func_a() { return a; } + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a18.c b/ld64/unit-tests/test-cases/llvm-integration/a18.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a18.c @@ -0,0 +1,18 @@ + + + +static int data_static1 = 1; +static int data_static2 = 2; + +void func_global1() { ++data_static1; } +void func_global2() { ++data_static2; } + +void __attribute__((visibility("hidden"))) func_hidden1() {} +void __attribute__((visibility("hidden"))) func_hidden2() {} + +int common_global1; +int common_global2; + +int __attribute__((visibility("hidden"))) common_hidden1; +int __attribute__((visibility("hidden"))) common_hidden2; + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a2.c b/ld64/unit-tests/test-cases/llvm-integration/a2.c new file mode 100644 index 0000000..0eb20c5 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a2.c @@ -0,0 +1,6 @@ +extern int foo4(void); +int foo3() +{ + return foo4(); +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a20.c b/ld64/unit-tests/test-cases/llvm-integration/a20.c new file mode 100644 index 0000000..cd52955 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a20.c @@ -0,0 +1,2 @@ +void foo() {} +void bar() {} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a3.c b/ld64/unit-tests/test-cases/llvm-integration/a3.c new file mode 100644 index 0000000..040ca5f --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a3.c @@ -0,0 +1,6 @@ +int bar; +int foo1() +{ + return bar; +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a4.c b/ld64/unit-tests/test-cases/llvm-integration/a4.c new file mode 100644 index 0000000..0eb20c5 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a4.c @@ -0,0 +1,6 @@ +extern int foo4(void); +int foo3() +{ + return foo4(); +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/a5.c b/ld64/unit-tests/test-cases/llvm-integration/a5.c new file mode 100644 index 0000000..bcb22d9 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a5.c @@ -0,0 +1,10 @@ +extern int foo2(void); +extern int foo3(void); + +int foo1() +{ + int i = 42; + if (foo2()) + i = foo3(); + return i; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a6.c b/ld64/unit-tests/test-cases/llvm-integration/a6.c new file mode 100644 index 0000000..b621453 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a6.c @@ -0,0 +1,10 @@ + +int foo1() +{ + return 42; +} + +int foo2() +{ + return 21; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a7.c b/ld64/unit-tests/test-cases/llvm-integration/a7.c new file mode 100644 index 0000000..560919a --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a7.c @@ -0,0 +1,11 @@ +extern int foo3(void); + +int foo1(void) +{ + return foo3(); +} + +int foo2(void) +{ + return 42; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a8.c b/ld64/unit-tests/test-cases/llvm-integration/a8.c new file mode 100644 index 0000000..47352a7 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a8.c @@ -0,0 +1,23 @@ + +static signed int i = 0; +extern int foo1(void); +extern void foo2(void); + +void foo2(void) { + + i = -1; + +} + +static int foo3() { + return 10; +} + +int foo1(void) +{ + int data = 0; + if (i < 0) + data = foo3(); + data += 42; + return data; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a9.c b/ld64/unit-tests/test-cases/llvm-integration/a9.c new file mode 100644 index 0000000..da2c8fa --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a9.c @@ -0,0 +1,25 @@ + +static signed int i = 0; +extern int foo1(void); +extern void foo2(void); +extern void foo4(void); + +void foo2(void) { + + i = -1; + +} + +static int foo3() { + foo4(); + return 10; +} + +int foo1(void) +{ + int data = 0; + if (i < 0) + data = foo3(); + data += 42; + return data; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a9.list b/ld64/unit-tests/test-cases/llvm-integration/a9.list new file mode 100644 index 0000000..583bc2f --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/a9.list @@ -0,0 +1,3 @@ +_foo1 +_main +_bar diff --git a/ld64/unit-tests/test-cases/llvm-integration/b.c b/ld64/unit-tests/test-cases/llvm-integration/b.c new file mode 100644 index 0000000..61c92dc --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b.c @@ -0,0 +1,3 @@ +int foo2() { + return 21; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b1.c b/ld64/unit-tests/test-cases/llvm-integration/b1.c new file mode 100644 index 0000000..5158abe --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b1.c @@ -0,0 +1,4 @@ +extern int foo4(); +int foo2() { + return foo4(); +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b10.c b/ld64/unit-tests/test-cases/llvm-integration/b10.c new file mode 100644 index 0000000..d899aa9 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b10.c @@ -0,0 +1,7 @@ +#include "b10.h" +extern void foo(void); + +struct my_struct my_hooks = { + foo +}; + diff --git a/ld64/unit-tests/test-cases/llvm-integration/b10.h b/ld64/unit-tests/test-cases/llvm-integration/b10.h new file mode 100644 index 0000000..fcb50d2 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b10.h @@ -0,0 +1,6 @@ +struct my_struct +{ + void (*f)(void); +}; + +extern struct my_struct my_hooks; diff --git a/ld64/unit-tests/test-cases/llvm-integration/b14.c b/ld64/unit-tests/test-cases/llvm-integration/b14.c new file mode 100644 index 0000000..59c5120 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b14.c @@ -0,0 +1,7 @@ +#include + +int Y; +extern int X __attribute__((visibility("hidden"))); +void foo() { + printf ("%d\n", X); +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b15.c b/ld64/unit-tests/test-cases/llvm-integration/b15.c new file mode 100644 index 0000000..13c88d7 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b15.c @@ -0,0 +1,8 @@ +extern void foo(); +void bar() { + foo(); +} + +void __attribute__((visibility("hidden"))) f2() +{} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/b17.c b/ld64/unit-tests/test-cases/llvm-integration/b17.c new file mode 100644 index 0000000..47d4cc3 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b17.c @@ -0,0 +1,4 @@ +int b = 0; +int func_b() { return b; } + + diff --git a/ld64/unit-tests/test-cases/llvm-integration/b2.c b/ld64/unit-tests/test-cases/llvm-integration/b2.c new file mode 100644 index 0000000..a20f6e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b2.c @@ -0,0 +1,9 @@ +extern int foo4(void); + +int foo4(void) +{ + return 21; +} +int foo2() { + return foo4(); +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b20.c b/ld64/unit-tests/test-cases/llvm-integration/b20.c new file mode 100644 index 0000000..f6c0ed3 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b20.c @@ -0,0 +1 @@ +void frob() {} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b3.c b/ld64/unit-tests/test-cases/llvm-integration/b3.c new file mode 100644 index 0000000..31e7ee8 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b3.c @@ -0,0 +1,4 @@ +extern int bar; +int foo2() { + return bar; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b4.c b/ld64/unit-tests/test-cases/llvm-integration/b4.c new file mode 100644 index 0000000..9437ad0 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b4.c @@ -0,0 +1,13 @@ +extern int foo4(void); + +int foo4(void) +{ + return 21; +} +static int myfoo() +{ + return foo4(); +} +int foo2() { + return myfoo(); +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b5.c b/ld64/unit-tests/test-cases/llvm-integration/b5.c new file mode 100644 index 0000000..a105df9 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b5.c @@ -0,0 +1,4 @@ +int foo2(void) +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/b7.c b/ld64/unit-tests/test-cases/llvm-integration/b7.c new file mode 100644 index 0000000..d34f91a --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/b7.c @@ -0,0 +1,7 @@ +extern int foo2(void); +extern int foo3(void); + +int foo3(void) +{ + return foo2(); +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/c15.c b/ld64/unit-tests/test-cases/llvm-integration/c15.c new file mode 100644 index 0000000..0089d64 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/c15.c @@ -0,0 +1,9 @@ +extern void foo(); +int main() { + foo(); + return 0; +} + +void __attribute__((visibility("hidden"))) f2() +{ +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main.c b/ld64/unit-tests/test-cases/llvm-integration/main.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main.c @@ -0,0 +1,9 @@ +extern int foo2(); +extern int foo3(); +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main1.c b/ld64/unit-tests/test-cases/llvm-integration/main1.c new file mode 100644 index 0000000..ccad10d --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main1.c @@ -0,0 +1,13 @@ +extern int foo2(); +extern int foo3(); +int foo4() +{ + return 21; +} +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main10.c b/ld64/unit-tests/test-cases/llvm-integration/main10.c new file mode 100644 index 0000000..dabcdcf --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main10.c @@ -0,0 +1,10 @@ +#include "b10.h" + +int main() +{ + struct my_struct *mh = &my_hooks; + + mh->f(); + + return 0; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main11.c b/ld64/unit-tests/test-cases/llvm-integration/main11.c new file mode 100644 index 0000000..82ef7ff --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main11.c @@ -0,0 +1,7 @@ + +extern void foo3(void); +int main() +{ + foo3(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main12.c b/ld64/unit-tests/test-cases/llvm-integration/main12.c new file mode 100644 index 0000000..4de8725 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main12.c @@ -0,0 +1,7 @@ +#include "a12.h" +int main() +{ + e[0] = ZERO; + foo(); + return e[0]; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main13.cc b/ld64/unit-tests/test-cases/llvm-integration/main13.cc new file mode 100644 index 0000000..697d81b --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main13.cc @@ -0,0 +1,8 @@ +#include "a13.h" + +int main() +{ + A a; + a.foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main16.c b/ld64/unit-tests/test-cases/llvm-integration/main16.c new file mode 100644 index 0000000..67112aa --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main16.c @@ -0,0 +1,8 @@ + +int tent; +int global = 5; + +int foo() { return tent + global; } + +int main() { foo(); return 0; } + diff --git a/ld64/unit-tests/test-cases/llvm-integration/main19.c b/ld64/unit-tests/test-cases/llvm-integration/main19.c new file mode 100644 index 0000000..e09c1c9 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main19.c @@ -0,0 +1,8 @@ +extern int foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/main2.c b/ld64/unit-tests/test-cases/llvm-integration/main2.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main2.c @@ -0,0 +1,9 @@ +extern int foo2(); +extern int foo3(); +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main20.c b/ld64/unit-tests/test-cases/llvm-integration/main20.c new file mode 100644 index 0000000..624e009 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main20.c @@ -0,0 +1,7 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main3.c b/ld64/unit-tests/test-cases/llvm-integration/main3.c new file mode 100644 index 0000000..a5058fe --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main3.c @@ -0,0 +1,13 @@ +extern int foo1(); +extern int foo2(); +extern int bar; +int main(){ + int i; + bar = 14; + i = foo1() + foo2() + bar; + if (i == 42) + return 0; + else + return 1; + +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main4.c b/ld64/unit-tests/test-cases/llvm-integration/main4.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main4.c @@ -0,0 +1,9 @@ +extern int foo2(); +extern int foo3(); +int main(){ + int i = foo3() + foo2(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main5.c b/ld64/unit-tests/test-cases/llvm-integration/main5.c new file mode 100644 index 0000000..28d551d --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main5.c @@ -0,0 +1,16 @@ + +extern int foo1(void); + +int foo3(void) +{ + return 42; +} + +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main6.c b/ld64/unit-tests/test-cases/llvm-integration/main6.c new file mode 100644 index 0000000..3d00382 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main6.c @@ -0,0 +1,10 @@ +extern int foo1(); + +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main7.c b/ld64/unit-tests/test-cases/llvm-integration/main7.c new file mode 100644 index 0000000..22427e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main7.c @@ -0,0 +1,10 @@ +extern int foo1(void); + +int main(void) +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main8.c b/ld64/unit-tests/test-cases/llvm-integration/main8.c new file mode 100644 index 0000000..a9c924d --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main8.c @@ -0,0 +1,11 @@ +extern int foo1(void); +extern void foo2(void); + +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/llvm-integration/main9.c b/ld64/unit-tests/test-cases/llvm-integration/main9.c new file mode 100644 index 0000000..b44bf6e --- /dev/null +++ b/ld64/unit-tests/test-cases/llvm-integration/main9.c @@ -0,0 +1,14 @@ +extern int foo1(void); +extern void foo2(void); + +void foo4(void) +{ +} +int main() +{ + int i = foo1(); + if (i == 42) + return 0; + else + return 1; +} diff --git a/ld64/unit-tests/test-cases/loader_path/Makefile b/ld64/unit-tests/test-cases/loader_path/Makefile new file mode 100644 index 0000000..126c10d --- /dev/null +++ b/ld64/unit-tests/test-cases/loader_path/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a sanity check that an indirect +# library loaded with @loader_path works +# +# ld64 should handle linking against dylibs that have @loader_path based dylib load commands +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name @loader_path/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm *.dylib main diff --git a/ld64/unit-tests/test-cases/loader_path/bar.c b/ld64/unit-tests/test-cases/loader_path/bar.c new file mode 100644 index 0000000..a307157 --- /dev/null +++ b/ld64/unit-tests/test-cases/loader_path/bar.c @@ -0,0 +1,6 @@ + +int bar() +{ + return 1; +} + diff --git a/ld64/unit-tests/test-cases/loader_path/foo.c b/ld64/unit-tests/test-cases/loader_path/foo.c new file mode 100644 index 0000000..8c2179d --- /dev/null +++ b/ld64/unit-tests/test-cases/loader_path/foo.c @@ -0,0 +1,7 @@ + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/ld64/unit-tests/test-cases/loader_path/main.c b/ld64/unit-tests/test-cases/loader_path/main.c new file mode 100644 index 0000000..829ca5e --- /dev/null +++ b/ld64/unit-tests/test-cases/loader_path/main.c @@ -0,0 +1,8 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/Makefile b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/Makefile new file mode 100644 index 0000000..8fc14f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/Makefile @@ -0,0 +1,75 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# This test case checks -non_global_symbols_no_strip_list and -non_global_symbols_strip_list +# with and without wildcards +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c -o main + ${FAIL_IF_BAD_MACHO} main + nm -j main > main.nm + # build stripping a.list + ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_strip_list,a.list -o main-a + ${FAIL_IF_BAD_MACHO} main-a + nm -j main-a > main-a.nm + diff main.nm main-a.nm | egrep '<|>' > a.diff + diff a.diff a.expect | ${FAIL_IF_STDIN} + # build but strip at .o file level a.list + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r -arch ${ARCH} main.o foo.o -o all-a.o -non_global_symbols_strip_list a.list + ${CC} ${CCFLAGS} all-a.o -Wl,-non_global_symbols_strip_list,a.list -o main-a + ${FAIL_IF_BAD_MACHO} main-a + nm -j main-a > main-a.nm + diff main.nm main-a.nm | egrep '<|>' > a.diff + diff a.diff a.expect | ${FAIL_IF_STDIN} + # build stripping b.list + ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_strip_list,b.list -o main-b + ${FAIL_IF_BAD_MACHO} main-b + nm -j main-b > main-b.nm + diff main.nm main-b.nm | egrep '<|>' > b.diff + diff b.diff b.expect | ${FAIL_IF_STDIN} + # build but strip at .o file level b.list + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r -arch ${ARCH} main.o foo.o -o all-b.o -non_global_symbols_strip_list b.list + ${CC} ${CCFLAGS} all-b.o -Wl,-non_global_symbols_strip_list,b.list -o main-b + ${FAIL_IF_BAD_MACHO} main-b + nm -j main-b > main-b.nm + diff main.nm main-b.nm | egrep '<|>' > b.diff + diff b.diff b.expect | ${FAIL_IF_STDIN} + # build stripping c.list + ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_no_strip_list,c.list -o main-c + nm -m main-c | grep non-external | grep -v my | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main-c + + +clean: + rm -rf main main.nm main-a main-a.nm a.diff main-b main-b.nm b.diff main-c all-a.o all-b.o foo.o main.o diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.expect b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.expect new file mode 100644 index 0000000..9ecbf53 --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.expect @@ -0,0 +1,2 @@ +< _myglobal +< _xmyglobal2 diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.list b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.list new file mode 100644 index 0000000..16c59a2 --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/a.list @@ -0,0 +1,2 @@ +_myglobal +_xmyglobal2 diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.expect b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.expect new file mode 100644 index 0000000..f1d15e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.expect @@ -0,0 +1,3 @@ +< _myfunction +< _myglobal2 +< _xmyglobal2 diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.list b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.list new file mode 100644 index 0000000..97170ac --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/b.list @@ -0,0 +1,2 @@ +*2 +_myf*on diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/c.list b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/c.list new file mode 100644 index 0000000..be715cd --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/c.list @@ -0,0 +1 @@ +*my* diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/foo.c b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/foo.c new file mode 100644 index 0000000..42e6cb7 --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/foo.c @@ -0,0 +1,11 @@ + + +int __attribute__((visibility("hidden"))) myglobal = 3; +int __attribute__((visibility("hidden"))) myglobal2 = 3; +int __attribute__((visibility("hidden"))) xmyglobal = 3; +int __attribute__((visibility("hidden"))) xmyglobal2 = 3; + +void __attribute__((visibility("hidden"))) myfunction(int x) { } + + + diff --git a/ld64/unit-tests/test-cases/local-symbol-partial-stripping/main.c b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/main.c new file mode 100644 index 0000000..013bc55 --- /dev/null +++ b/ld64/unit-tests/test-cases/local-symbol-partial-stripping/main.c @@ -0,0 +1,11 @@ +#include + +extern int myglobal; +extern void myfunction(int); + +int main() +{ + myfunction(myglobal); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/main-stripped/Makefile b/ld64/unit-tests/test-cases/main-stripped/Makefile new file mode 100644 index 0000000..03756ff --- /dev/null +++ b/ld64/unit-tests/test-cases/main-stripped/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a dynamically referenced symbol is always exported +# + +run: all + +all: + ${CC} main.c -o main-${ARCH} -exported_symbols_list main.exp + ${FAIL_IF_BAD_MACHO} main-${ARCH} + nm -m main-${ARCH} | grep _magicSymbol | grep "referenced dynamically" | ${PASS_IFF_STDIN} + +clean: + rm main-* diff --git a/ld64/unit-tests/test-cases/main-stripped/main.c b/ld64/unit-tests/test-cases/main-stripped/main.c new file mode 100644 index 0000000..1e71f1b --- /dev/null +++ b/ld64/unit-tests/test-cases/main-stripped/main.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// set magic "dynamically referenced" bit on magicSymbol +int magicSymbol = 1; +asm(".desc _magicSymbol, 0x10"); + + +int main() +{ + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/main-stripped/main.exp b/ld64/unit-tests/test-cases/main-stripped/main.exp new file mode 100644 index 0000000..4eb9e89 --- /dev/null +++ b/ld64/unit-tests/test-cases/main-stripped/main.exp @@ -0,0 +1 @@ +_main diff --git a/ld64/unit-tests/test-cases/missing-option-args/Makefile b/ld64/unit-tests/test-cases/missing-option-args/Makefile new file mode 100644 index 0000000..81c3f58 --- /dev/null +++ b/ld64/unit-tests/test-cases/missing-option-args/Makefile @@ -0,0 +1,98 @@ +## +# Copyright (c) 2007 Apple, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that missing arguments don't cause ld to crash +# This tests 64-bit arguments only +# + + +OUTPUT=2>/dev/null +LDCMD=${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} ${OUTPUT} + +run: all + +all: + ${FAIL_IF_SUCCESS} ${LD} -arch 2>/dev/null + ${LDCMD} -filelist + ${LDCMD} -o + ${LDCMD} -read_only_relocs + ${LDCMD} -sect_diff_relocs + ${LDCMD} -weak_reference_mismatches + ${LDCMD} -l + ${LDCMD} -weak-l + ${LDCMD} -weak-library + ${LDCMD} -L + ${LDCMD} -syslibroot + ${LDCMD} -framework + ${LDCMD} -framework name, + ${LDCMD} -weak_framework + ${LDCMD} -weak_framework name + ${LDCMD} -weak_framework name, + ${LDCMD} -F + ${LDCMD} -dylib_file + ${LDCMD} -dylib_file install_name + ${LDCMD} -sectcreate segname sectname + ${LDCMD} -sectorder + ${LDCMD} -sectorder segname sectname + ${LDCMD} -u + ${LDCMD} -e + ${LDCMD} -i + ${LDCMD} -idefinition: + ${LDCMD} -undefined + ${LDCMD} -U + ${LDCMD} -commons + ${LDCMD} -warn_commons + ${LDCMD} -exported_symbols_list + ${LDCMD} -unexported_symbols_list + ${LDCMD} -filelist + ${LDCMD} -filelist listfile, + ${LDCMD} -headerpad + ${LDCMD} -A + ${LDCMD} -dylib_install_name + ${LDCMD} -umbrella + ${LDCMD} -allowable_client + ${LDCMD} -client_name + ${LDCMD} -sub_umbrella + ${LDCMD} -sub_library + ${LDCMD} -init + ${LDCMD} -dylinker_install_name + ${LDCMD} -macosx_version_min + ${LDCMD} -final_output + ${LDCMD} -seg1addr + ${LDCMD} -pagezero_size + ${LDCMD} -dylib_compatibility_version + ${LDCMD} -stack_addr + ${LDCMD} -stack_size + ${LDCMD} -sectcreate + ${LDCMD} -sectcreate segname + ${LDCMD} -sectalign + ${LDCMD} -sectalign segname + ${LDCMD} -sectalign segname sectname + ${LDCMD} -sectorder segname + ${LDCMD} -dylib_current_version + ${PASS_IFF} true + +clean: diff --git a/ld64/unit-tests/test-cases/missing-option-args/comment.txt b/ld64/unit-tests/test-cases/missing-option-args/comment.txt new file mode 100644 index 0000000..8000102 --- /dev/null +++ b/ld64/unit-tests/test-cases/missing-option-args/comment.txt @@ -0,0 +1 @@ +Verify that missing arguments don't cause ld to crash diff --git a/ld64/unit-tests/test-cases/multiple-entry-points/Makefile b/ld64/unit-tests/test-cases/multiple-entry-points/Makefile new file mode 100644 index 0000000..136bcf6 --- /dev/null +++ b/ld64/unit-tests/test-cases/multiple-entry-points/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} test.s -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_sort test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_sort test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/multiple-entry-points/comment.txt b/ld64/unit-tests/test-cases/multiple-entry-points/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/multiple-entry-points/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/unit-tests/test-cases/multiple-entry-points/test.s b/ld64/unit-tests/test-cases/multiple-entry-points/test.s new file mode 100644 index 0000000..4559893 --- /dev/null +++ b/ld64/unit-tests/test-cases/multiple-entry-points/test.s @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + .text + .align 2 + + .globl _foo + .globl _foo2 + .globl _foo3 +_foo: +_foo2: +_foo3: + nop + + + + .align 2 +_bar: + nop + + + .align 2 + .globl _xx + .globl __xx +_xx: +__xx: + nop + + + .align 2 +_ok: + nop + diff --git a/ld64/unit-tests/test-cases/no-dynamic-common/Makefile.newtest b/ld64/unit-tests/test-cases/no-dynamic-common/Makefile.newtest new file mode 100644 index 0000000..d0f7df8 --- /dev/null +++ b/ld64/unit-tests/test-cases/no-dynamic-common/Makefile.newtest @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to determine if +# common symbols are not allowed with MH_DYLIB output format with the -multi_module option +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o + ${PASS_IFF_ERROR} libtool -dynamic -o libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null + +clean: + rm -rf *.o *.a diff --git a/ld64/unit-tests/test-cases/no-dynamic-common/a.c b/ld64/unit-tests/test-cases/no-dynamic-common/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/ld64/unit-tests/test-cases/no-dynamic-common/a.c @@ -0,0 +1,7 @@ +extern int common_variable; + +int +main(int argc, char **argv) +{ + return common_variable; +} diff --git a/ld64/unit-tests/test-cases/no-dynamic-common/comment.txt b/ld64/unit-tests/test-cases/no-dynamic-common/comment.txt new file mode 100644 index 0000000..a5a960d --- /dev/null +++ b/ld64/unit-tests/test-cases/no-dynamic-common/comment.txt @@ -0,0 +1 @@ +The point of this test is to determine if common symbols are not allowed with MH_DYLIB output format with the -multi_module option diff --git a/ld64/unit-tests/test-cases/no-dynamic-common/test.c b/ld64/unit-tests/test-cases/no-dynamic-common/test.c new file mode 100644 index 0000000..f34267a --- /dev/null +++ b/ld64/unit-tests/test-cases/no-dynamic-common/test.c @@ -0,0 +1,25 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int common_variable; diff --git a/ld64/unit-tests/test-cases/no-uuid/Makefile b/ld64/unit-tests/test-cases/no-uuid/Makefile new file mode 100644 index 0000000..5d52553 --- /dev/null +++ b/ld64/unit-tests/test-cases/no-uuid/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set emit LC_UUID correctly +# + +run: all + +all: + +# Test main executable built with dwarf has uuid + ${CC} ${CCFLAGS} foo.c -o foo -gdwarf-2 + ${FAIL_IF_BAD_MACHO} foo + ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} + rm -f foo + +# Test main executable built with stabs has uuid + ${CC} ${CCFLAGS} foo.c -o foo -gfull -gstabs+ + ${FAIL_IF_BAD_MACHO} foo + ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} + +# Test main executable built with dwarf and -no_uuid does not have uuid + ${CC} ${CCFLAGS} foo.c -o foo -Wl,-no_uuid -gdwarf-2 + ${FAIL_IF_BAD_MACHO} foo + ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN} + +# Test ld -r of stabs file has no uuid + ${CC} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ + ${LD} -arch ${ARCH} foo.o -r -o foo2.o + ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN} + +# Test ld -r of two files one with uuid produces a uuid + ${CC} ${CCFLAGS} foo.c -c -o foo.o -gdwarf-2 + ${LD} -arch ${ARCH} foo.o -r -o foo2.o + ${CC} ${CCFLAGS} bar.c -c -gstabs+ -o bar.o + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + ${OTOOL} -hlv foobar.o | grep LC_UUID | ${PASS_IFF_STDIN} + +clean: + rm -rf foo foo.o foo2.o bar.o foobar.o foo.dSYM diff --git a/ld64/unit-tests/test-cases/no-uuid/bar.c b/ld64/unit-tests/test-cases/no-uuid/bar.c new file mode 100644 index 0000000..cbefe0f --- /dev/null +++ b/ld64/unit-tests/test-cases/no-uuid/bar.c @@ -0,0 +1,4 @@ +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/no-uuid/comment.txt b/ld64/unit-tests/test-cases/no-uuid/comment.txt new file mode 100644 index 0000000..269bbbd --- /dev/null +++ b/ld64/unit-tests/test-cases/no-uuid/comment.txt @@ -0,0 +1 @@ +Test the we set emit LC_UUID correctly diff --git a/ld64/unit-tests/test-cases/no-uuid/foo.c b/ld64/unit-tests/test-cases/no-uuid/foo.c new file mode 100644 index 0000000..57ed6ba --- /dev/null +++ b/ld64/unit-tests/test-cases/no-uuid/foo.c @@ -0,0 +1,4 @@ +int main (void) +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/non-lazy-r/Makefile b/ld64/unit-tests/test-cases/non-lazy-r/Makefile new file mode 100644 index 0000000..3c14103 --- /dev/null +++ b/ld64/unit-tests/test-cases/non-lazy-r/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that non-lazy-pointers are properly handled by -r +# + + +all: all-${ARCH} + +all-ppc: hasnl + +all-ppc64: hasnl + +all-i386: hasnl + +all-armv6: hasnl + +all-x86_64: all-true + +all-true: + ${PASS_IFF} true + + +hasnl: + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${CC} ${CCFLAGS} -c other.c -o other.o + ${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo + # make sure there are two indirect symbols: _foo and LOCAL + otool -Iv fooall.o | grep "2 entries" | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN} + # make sure re-parsed correctly + ${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/non-lazy-r/foo.c b/ld64/unit-tests/test-cases/non-lazy-r/foo.c new file mode 100644 index 0000000..1fa325e --- /dev/null +++ b/ld64/unit-tests/test-cases/non-lazy-r/foo.c @@ -0,0 +1,12 @@ + + + +extern int foo; + +int getfoo() { return foo; } + + +extern int other; + +int getother() { return other; } + diff --git a/ld64/unit-tests/test-cases/non-lazy-r/other.c b/ld64/unit-tests/test-cases/non-lazy-r/other.c new file mode 100644 index 0000000..6420437 --- /dev/null +++ b/ld64/unit-tests/test-cases/non-lazy-r/other.c @@ -0,0 +1,2 @@ +int foo = 2; +int other = 3; diff --git a/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile b/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile new file mode 100644 index 0000000..44023e6 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify no GSYM for .objc_category_* +# Linker should not make GSYM debug note for .objc_category_* symbols# +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation + nm -ap test | grep GSYM | grep category | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test test.dSYM diff --git a/ld64/unit-tests/test-cases/objc-category-debug-notes/test.m b/ld64/unit-tests/test-cases/objc-category-debug-notes/test.m new file mode 100644 index 0000000..3f45e25 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-debug-notes/test.m @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +@interface NSObject (stuff) +@end + +@implementation NSObject (stuff) +@end + +@interface NSObject (other) +@end + +@implementation NSObject (other) +- (id) init { return self; } +@end + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/objc-exported_symbols_list/Makefile b/ld64/unit-tests/test-cases/objc-exported_symbols_list/Makefile new file mode 100644 index 0000000..454a412 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-exported_symbols_list/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Test that ObjC class exports can be suppressed +# + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.m -framework Foundation -exported_symbols_list foo.exp -o libfoo.dylib + nm -m libfoo.dylib | grep Foo | grep ') external' | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep Bar | grep non-external | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf *~ libfoo.dylib diff --git a/ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.exp b/ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.exp new file mode 100644 index 0000000..720c52d --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.exp @@ -0,0 +1 @@ +.objc_class_name_Foo diff --git a/ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.m b/ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.m new file mode 100644 index 0000000..d39c8f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-exported_symbols_list/foo.m @@ -0,0 +1,18 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + + + +@interface Bar : NSObject +@end + +@implementation Bar +@end + + + diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile new file mode 100644 index 0000000..0abe7d5 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile @@ -0,0 +1,84 @@ +## +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + + +# +# Validate that the linker catches illegal combinations of .o files +# compiled with different GC settings. +# + +test: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + + ${CC} ${CCFLAGS} foo.m -c -o foo-gc.o -fobjc-gc + ${FAIL_IF_BAD_OBJ} foo-gc.o + + ${CC} ${CCFLAGS} foo.m -c -o foo-gc-only.o -fobjc-gc-only + ${FAIL_IF_BAD_OBJ} foo-gc-only.o + + ${CC} ${CCFLAGS} bar.m -c -o bar.o + ${FAIL_IF_BAD_OBJ} bar.o + + ${CC} ${CCFLAGS} bar.m -c -o bar-gc.o -fobjc-gc + ${FAIL_IF_BAD_OBJ} bar-gc.o + + ${CC} ${CCFLAGS} bar.m -c -o bar-gc-only.o -fobjc-gc-only + ${FAIL_IF_BAD_OBJ} bar-gc-only.o + + # check RR + RR -> RR + ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + + # check GC/RR + GC/RR -> GC/RR + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY} + + # check GC + GC -> GC + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} + + # check RR + GC/RR -> RR + ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + + # check GC + GC/RR -> GC + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} + + # check RR + GC -> error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log + + ${PASS_IFF} true + +clean: + rm -rf foo*.o bar*.o libfoobar.dylib fail.log diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/bar.m b/ld64/unit-tests/test-cases/objc-gc-checks/bar.m new file mode 100644 index 0000000..5c98709 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-gc-checks/bar.m @@ -0,0 +1,11 @@ + +@interface Bar { + int f; +} +- (void) doit; +@end + +@implementation Bar +- (void) doit { } +@end + diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/comment.txt b/ld64/unit-tests/test-cases/objc-gc-checks/comment.txt new file mode 100644 index 0000000..953da58 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-gc-checks/comment.txt @@ -0,0 +1 @@ +Validate that the linker catches illegal combintations of .o files compiled with different GC settings diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/foo.m b/ld64/unit-tests/test-cases/objc-gc-checks/foo.m new file mode 100644 index 0000000..e13367e --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-gc-checks/foo.m @@ -0,0 +1,12 @@ + +@interface Foo { + int f; +} +- (void) doit; +@end + + +@implementation Foo +- (void) doit { } +@end + diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/runtime.c b/ld64/unit-tests/test-cases/objc-gc-checks/runtime.c new file mode 100644 index 0000000..df4aeef --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-gc-checks/runtime.c @@ -0,0 +1,2 @@ +void _objc_empty_cache() {} +void _objc_empty_vtable() {} diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile new file mode 100644 index 0000000..9edc357 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify an Objective-C object file when run through +# ld -r is unaltered. +# __cls_refs section losing S_LITERAL_POINTERS section type +# +# note: i386 and ppc objc use some anonymous zerofill that moves and needs to be ignore to compare +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.o + ObjectDump -no_content test.o | grep -v zero-fill-at> test.dump + + ${LD} -arch ${ARCH} -r test.o -o test-r.o + ObjectDump -no_content test-r.o | grep -v zero-fill-at > test-r.dump + + diff test.dump test-r.dump | ${PASS_IFF_EMPTY} + +clean: + rm -rf test.o test.dump test-r.o test-r.dump diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers/test.m b/ld64/unit-tests/test-cases/objc-literal-pointers/test.m new file mode 100644 index 0000000..a5c1ea8 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-literal-pointers/test.m @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +void test() +{ + // two class references + // two selector references + [NSObject superclass]; + [NSString description]; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/objc-references/Makefile b/ld64/unit-tests/test-cases/objc-references/Makefile new file mode 100644 index 0000000..a11f1d0 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-references/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify an Objective-C object file +# is parsed to find the proper class references +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o + ${FAIL_IF_BAD_OBJ} test.${ARCH}.o + + ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_BAD_OBJ} test-r.${ARCH}.o + + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSObject' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSData' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSArray' | ${FAIL_IF_EMPTY} + nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSString' | ${PASS_IFF_STDIN} + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/objc-references/comment.txt b/ld64/unit-tests/test-cases/objc-references/comment.txt new file mode 100644 index 0000000..ce67b71 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-references/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify an Objective-C object file is parsed to find the proper class references diff --git a/ld64/unit-tests/test-cases/objc-references/test.m b/ld64/unit-tests/test-cases/objc-references/test.m new file mode 100644 index 0000000..d845227 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-references/test.m @@ -0,0 +1,52 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +@interface Foo : NSObject +- (NSString*) foo; +@end + + +@implementation Foo +- (NSString*) foo +{ + return [NSString stringWithUTF8String:"hello"]; +} +@end + + +@interface Bar : NSData +- (NSArray*) bar; +@end + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/ld64/unit-tests/test-cases/objc-selector-coalescing/Makefile b/ld64/unit-tests/test-cases/objc-selector-coalescing/Makefile new file mode 100644 index 0000000..982f41d --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-selector-coalescing/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Test that two ObjC translation units that use the same selector +# link together. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.m other.m -o main -framework Foundation + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/ld64/unit-tests/test-cases/objc-selector-coalescing/main.m b/ld64/unit-tests/test-cases/objc-selector-coalescing/main.m new file mode 100644 index 0000000..a266128 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-selector-coalescing/main.m @@ -0,0 +1,7 @@ +#include + + +NSString* other() +{ + return [NSString stringWithUTF8String:"hello"]; +} diff --git a/ld64/unit-tests/test-cases/objc-selector-coalescing/other.m b/ld64/unit-tests/test-cases/objc-selector-coalescing/other.m new file mode 100644 index 0000000..77fd926 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-selector-coalescing/other.m @@ -0,0 +1,10 @@ + +#include + + +int main() +{ + [NSString stringWithUTF8String:"hello"]; + return 0; +} + diff --git a/ld64/unit-tests/test-cases/operator-new/Makefile b/ld64/unit-tests/test-cases/operator-new/Makefile new file mode 100644 index 0000000..8abf3e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/operator-new/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +run: all + +all: + # verify if operator new is overridden that WEAK_DEFINES is set + ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx + otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} + # verify if operator new is not overridden that WEAK_DEFINES is not set + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx + otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY} + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx new file mode 100644 index 0000000..3c99e35 --- /dev/null +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + + +// +// This test case verifies overriding operator new sets the MH_WEAK_DEFINES bit +// + +#if OP_NEW +void* operator new(size_t s) throw (std::bad_alloc) +{ + return malloc(s);; +} +#endif + +int main() +{ + return 0; +} + diff --git a/ld64/unit-tests/test-cases/order_file-ans/Makefile b/ld64/unit-tests/test-cases/order_file-ans/Makefile new file mode 100644 index 0000000..23fe568 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-ans/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that -order_file can be used to order symbols with anonymous name spaces +# + +run: all + +all: + ${CXX} ${CXXFLAGS} main.cxx -DANCHOR=1 -o main -Wl,-order_file -Wl,main.order + ${FAIL_IF_BAD_MACHO} main + nm -n -g -j main | grep "_GLOBAL__N" > main.actual + ${PASS_IFF} diff main.actual main.expected + + +clean: + rm -rf main main.actual diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.cxx b/ld64/unit-tests/test-cases/order_file-ans/main.cxx new file mode 100644 index 0000000..b0412f9 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-ans/main.cxx @@ -0,0 +1,62 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +#if ANCHOR + int anchor = 4; +#endif + +namespace { + struct myanonstruct { int a; }; +} + +// function defined in anonymous namespace +namespace { + void foo() { } +} + +// function that has an anonymous namespace parameter +void bar(myanonstruct* x) { } + + +// function in anonymous namespace that has an anonymous namespace parameter +namespace { + void baz(myanonstruct* x) { } +} + +// nested namespace +namespace wow { + namespace { + void inner() { } + } +} + + + + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.expected b/ld64/unit-tests/test-cases/order_file-ans/main.expected new file mode 100644 index 0000000..75e104f --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-ans/main.expected @@ -0,0 +1,4 @@ +__Z3barPN17_GLOBAL__N_anchor12myanonstructE +__ZN3wow17_GLOBAL__N_anchor5innerEv +__ZN17_GLOBAL__N_anchor3bazEPNS_12myanonstructE +__ZN17_GLOBAL__N_anchor3fooEv diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.order b/ld64/unit-tests/test-cases/order_file-ans/main.order new file mode 100644 index 0000000..36dd786 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-ans/main.order @@ -0,0 +1,4 @@ +__Z3barPN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C12myanonstructE +__ZN3wow95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C5innerEv +__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3bazEPNS_12myanonstructE +__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3fooEv diff --git a/ld64/unit-tests/test-cases/order_file/Makefile b/ld64/unit-tests/test-cases/order_file/Makefile new file mode 100644 index 0000000..21ae0ea --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check -order_file. +# The main1 test verifies that C functions can be re-ordered +# The main2 test verifies that a block of assembly is not moves en mas +# The main1 test verifies that an order file with spaces and comments works +# + +run: all + +all: + as -arch ${ARCH} -L extra.s -o extra.o + ${CC} ${CCFLAGS} main.c extra.o -o main1 -Wl,-order_file -Wl,main1.order + ${FAIL_IF_BAD_MACHO} main1 + nm -n -g -j main1 | grep "_main" > main1.nm + ${PASS_IFF} diff main1.nm main1.expected + + ${CC} ${CCFLAGS} main.c extra.o -o main2 -Wl,-order_file -Wl,main2.order + ${FAIL_IF_BAD_MACHO} main2 + nm -n -j main2 | egrep '^_[a-z]+[0-9]$$' > main2.nm + ${PASS_IFF} diff main2.nm main2.expected + + ${CC} -arch ${ARCH} -c main.c -o main.o + ${CC} ${CCFLAGS} main.o extra.o -o main3 -Wl,-order_file -Wl,main3.order + ${FAIL_IF_BAD_MACHO} main3 + nm -n -g -j main3 | grep "_main" > main3.nm + ${PASS_IFF} diff main3.nm main3.expected + + + + +clean: + rm -rf main1 *.nm main2 *.o warnings.log main3 diff --git a/ld64/unit-tests/test-cases/order_file/extra.s b/ld64/unit-tests/test-cases/order_file/extra.s new file mode 100644 index 0000000..90166ce --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/extra.s @@ -0,0 +1,24 @@ + + + .text + + .globl _foo1 +_foo1: nop + + .globl _aaa2 +_aaa2: +_bbb2: +_ccc2: + nop + + .globl _bbb3 +_aaa3: +_bbb3: +_ccc3: + nop + + +_aaa4: + nop + + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/order_file/main.c b/ld64/unit-tests/test-cases/order_file/main.c new file mode 100644 index 0000000..5643b45 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} + +void main2() {} +void main3() {} +void main4() {} diff --git a/ld64/unit-tests/test-cases/order_file/main1.expected b/ld64/unit-tests/test-cases/order_file/main1.expected new file mode 100644 index 0000000..04d128b --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main1.expected @@ -0,0 +1,4 @@ +_main4 +_main3 +_main +_main2 diff --git a/ld64/unit-tests/test-cases/order_file/main1.order b/ld64/unit-tests/test-cases/order_file/main1.order new file mode 100644 index 0000000..06b34d5 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main1.order @@ -0,0 +1,4 @@ +_main4 +_main3 + + diff --git a/ld64/unit-tests/test-cases/order_file/main2.expected b/ld64/unit-tests/test-cases/order_file/main2.expected new file mode 100644 index 0000000..8aca65c --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main2.expected @@ -0,0 +1,11 @@ +_main3 +_foo1 +_aaa2 +_bbb2 +_ccc2 +_aaa3 +_bbb3 +_ccc3 +_aaa4 +_main4 +_main2 diff --git a/ld64/unit-tests/test-cases/order_file/main2.order b/ld64/unit-tests/test-cases/order_file/main2.order new file mode 100644 index 0000000..87f89e6 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main2.order @@ -0,0 +1,6 @@ +_main3 +_aaa3 +_main4 + + + diff --git a/ld64/unit-tests/test-cases/order_file/main3.expected b/ld64/unit-tests/test-cases/order_file/main3.expected new file mode 100644 index 0000000..04d128b --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main3.expected @@ -0,0 +1,4 @@ +_main4 +_main3 +_main +_main2 diff --git a/ld64/unit-tests/test-cases/order_file/main3.order b/ld64/unit-tests/test-cases/order_file/main3.order new file mode 100644 index 0000000..d135527 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main3.order @@ -0,0 +1,8 @@ + +# spaces before and after main4 +main.o: _main4 +# +main.o: _main3# trailing comment +# + + diff --git a/ld64/unit-tests/test-cases/prebound-main/Makefile b/ld64/unit-tests/test-cases/prebound-main/Makefile new file mode 100644 index 0000000..79ef53e --- /dev/null +++ b/ld64/unit-tests/test-cases/prebound-main/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# 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: + ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 + otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main diff --git a/ld64/unit-tests/test-cases/prebound-main/main.c b/ld64/unit-tests/test-cases/prebound-main/main.c new file mode 100644 index 0000000..251979e --- /dev/null +++ b/ld64/unit-tests/test-cases/prebound-main/main.c @@ -0,0 +1,3 @@ + +int main() { return 0; } + diff --git a/ld64/unit-tests/test-cases/prebound-split-seg/Makefile b/ld64/unit-tests/test-cases/prebound-split-seg/Makefile new file mode 100644 index 0000000..07bce59 --- /dev/null +++ b/ld64/unit-tests/test-cases/prebound-split-seg/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is to build a prebound split-seg library +# + +run: all + +all: + ${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 diff --git a/ld64/unit-tests/test-cases/prebound-split-seg/address_table b/ld64/unit-tests/test-cases/prebound-split-seg/address_table new file mode 100644 index 0000000..b611ca8 --- /dev/null +++ b/ld64/unit-tests/test-cases/prebound-split-seg/address_table @@ -0,0 +1,4 @@ +# comment +0x91000000 0xA1000000 /foo/bar/libbar.dylib +# + diff --git a/ld64/unit-tests/test-cases/prebound-split-seg/bar.c b/ld64/unit-tests/test-cases/prebound-split-seg/bar.c new file mode 100644 index 0000000..46b7269 --- /dev/null +++ b/ld64/unit-tests/test-cases/prebound-split-seg/bar.c @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int x = 3; +int* xp = &x; + + +int bar() +{ + return *xp; +} + +void* pbar = &bar; + diff --git a/ld64/unit-tests/test-cases/private-non-lazy/Makefile b/ld64/unit-tests/test-cases/private-non-lazy/Makefile new file mode 100644 index 0000000..be0ba42 --- /dev/null +++ b/ld64/unit-tests/test-cases/private-non-lazy/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that a non-lazy-pointer +# in foo.o to a private-extern symbol in bar.o will +# properly survive ld -r +# + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${FAIL_IF_BAD_OBJ} foo.o + + ${CC} ${CCFLAGS} -c bar.c -o bar.o + ${FAIL_IF_BAD_OBJ} bar.o + + ${LD} -r foo.o bar.o -o foobar.o -arch ${ARCH} + ${FAIL_IF_BAD_OBJ} foobar.o + + ${CC} ${CCFLAGS} hello.c foobar.o -o hello + ${FAIL_IF_BAD_MACHO} hello + + ${LD} -r foo.o bar.o -o foobar2.o -arch ${ARCH} -keep_private_externs + ${FAIL_IF_BAD_OBJ} foobar2.o + + ${CC} ${CCFLAGS} hello.c foobar2.o -o hello2 + ${PASS_IFF_GOOD_MACHO} hello2 + +clean: + rm -rf *.o hello hello2 diff --git a/ld64/unit-tests/test-cases/private-non-lazy/bar.c b/ld64/unit-tests/test-cases/private-non-lazy/bar.c new file mode 100644 index 0000000..601dc69 --- /dev/null +++ b/ld64/unit-tests/test-cases/private-non-lazy/bar.c @@ -0,0 +1,3 @@ + +int __attribute__((visibility("hidden"))) foo = 0; + diff --git a/ld64/unit-tests/test-cases/private-non-lazy/comment.txt b/ld64/unit-tests/test-cases/private-non-lazy/comment.txt new file mode 100644 index 0000000..e6d11c0 --- /dev/null +++ b/ld64/unit-tests/test-cases/private-non-lazy/comment.txt @@ -0,0 +1 @@ +The point of this test is to check that a non-lazy-pointer in foo.o to a private-extern symbol in bar.o will properly survive ld -r diff --git a/ld64/unit-tests/test-cases/private-non-lazy/foo.c b/ld64/unit-tests/test-cases/private-non-lazy/foo.c new file mode 100644 index 0000000..6816d0b --- /dev/null +++ b/ld64/unit-tests/test-cases/private-non-lazy/foo.c @@ -0,0 +1,7 @@ + + +extern int foo; + +int getfoo() { return foo; } + + diff --git a/ld64/unit-tests/test-cases/private-non-lazy/hello.c b/ld64/unit-tests/test-cases/private-non-lazy/hello.c new file mode 100644 index 0000000..20dccc4 --- /dev/null +++ b/ld64/unit-tests/test-cases/private-non-lazy/hello.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int getfoo(); + +int main() +{ + return getfoo(); +} diff --git a/ld64/unit-tests/test-cases/re-export-cases/Makefile b/ld64/unit-tests/test-cases/re-export-cases/Makefile new file mode 100644 index 0000000..64ec112 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-cases/Makefile @@ -0,0 +1,167 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test all the different ways that re-exports can be specified and implemented +# + + +run: all + +all: + +# -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 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -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} + + +# -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 + 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 + ${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 + ${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 + 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 + ${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 + ${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 + ${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 + otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} + otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} + +# -reexport_library 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_library,libbar.dylib -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-l 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 -Wl,-reexport-lbar -L. -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} + +# -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 + 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. -Wl,-reexport_framework,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} + +# -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" -mmacosx-version-min=10.5 + ${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 + ${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 -mmacosx-version-min=10.4 + ${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 + ${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} + + +# -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 + ${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 + ${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 + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework diff --git a/ld64/unit-tests/test-cases/re-export-cases/bar.c b/ld64/unit-tests/test-cases/re-export-cases/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-cases/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-cases/baz.c b/ld64/unit-tests/test-cases/re-export-cases/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-cases/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-cases/foo.c b/ld64/unit-tests/test-cases/re-export-cases/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-cases/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-flag/Makefile b/ld64/unit-tests/test-cases/re-export-flag/Makefile new file mode 100644 index 0000000..82228ee --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-flag/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that the MH_NO_REEXPORTED_DYLIBS bit is set in dylibs with no re-exports +# + +run: all + +all: +# build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + +# build library the re-exports base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -sub_library libbar +# test that foo does not have MH_NO_REEXPORTED_DYLIBS bit + ${FAIL_IF_BAD_MACHO} libfoo.dylib + +# build libray that links with base but does not re-export it + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + otool -hv libfoo2.dylib | grep NO_REEXPORTED_DYLIBS | ${PASS_IFF_STDIN} + +clean: + rm -rf *.dylib diff --git a/ld64/unit-tests/test-cases/re-export-flag/bar.c b/ld64/unit-tests/test-cases/re-export-flag/bar.c new file mode 100644 index 0000000..34e5666 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-flag/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-flag/foo.c b/ld64/unit-tests/test-cases/re-export-flag/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-flag/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations/Makefile b/ld64/unit-tests/test-cases/re-export-optimizations/Makefile new file mode 100644 index 0000000..a1dfd88 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a public re-exported library is automatically added as a dependent +# unless nothing is used from it. +# + + +run: all + +all: + +# -sub_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.4 + 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.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 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.5 + 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 + otool -L main | grep libbar | ${FAIL_IF_STDIN} + + + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libbar.dylib libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/re-export-optimizations/bar.c b/ld64/unit-tests/test-cases/re-export-optimizations/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations/foo.c b/ld64/unit-tests/test-cases/re-export-optimizations/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations/main.c b/ld64/unit-tests/test-cases/re-export-optimizations/main.c new file mode 100644 index 0000000..2b85b0e --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations/main.c @@ -0,0 +1,10 @@ + +extern void bar(); + +int main() +{ +#if CALL_BAR + bar(); +#endif + return 0; +} diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile b/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile new file mode 100644 index 0000000..2560a86 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that @loader_path and @executable_path can be resolved finding indirect dylibs +# + + +run: all + +all: + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -install_name '@loader_path/libfoo.dylib' -o hide/libfoo.dylib + ${FAIL_IF_BAD_MACHO} hide/libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib -install_name '@executable_path/hide/libbar.dylib' + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide + ${FAIL_IF_BAD_MACHO} hide/libwrap.dylib + ${CC} ${CCFLAGS} main.c -o main hide/libwrap.dylib + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd`/main + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd` + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib libwrap.dylib main libmain.dylib diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/bar.c b/ld64/unit-tests/test-cases/re-export-relative-paths/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/foo.c b/ld64/unit-tests/test-cases/re-export-relative-paths/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/main.c b/ld64/unit-tests/test-cases/re-export-relative-paths/main.c new file mode 100644 index 0000000..367c6cb --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/main.c @@ -0,0 +1,11 @@ +extern int foo(); +extern int bar(); +extern int wrap(); + +int main() +{ + foo(); + bar(); + wrap(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/wrap.c b/ld64/unit-tests/test-cases/re-export-relative-paths/wrap.c new file mode 100644 index 0000000..d3cdd85 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/wrap.c @@ -0,0 +1,2 @@ +int wrap() { return 0; } + diff --git a/ld64/unit-tests/test-cases/read-only-relocs/Makefile b/ld64/unit-tests/test-cases/read-only-relocs/Makefile new file mode 100644 index 0000000..02ed1df --- /dev/null +++ b/ld64/unit-tests/test-cases/read-only-relocs/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that ld can linke a dylib built with -mdynamic-no-pic +# + + +SHELL = bash # use bash shell so we can redirect just stderr + +NO_PIC = +STATIC = + +ifeq (${ARCH},i386) + NO_PIC = -mdynamic-no-pic + STATIC = -static +else + ifeq (${ARCH},ppc) + NO_PIC = -mdynamic-no-pic + STATIC = -mdynamic-no-pic + endif +endif + + + +all: + # build libfoo.dylib as regular dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + # build libtest.dylib using -mdynamic-no-pic and -read_only_relocs suppress + ${CC} ${CCFLAGS} test.c -c ${NO_PIC} + ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + # build libtest.dylib using -static and -read_only_relocs suppress + ${CC} ${CCFLAGS} test.c -c ${STATIC} + ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + # build main using -static and -read_only_relocs suppress + ${CC} ${CCFLAGS} test.c -c ${STATIC} + ${CC} ${CCFLAGS} test.o libfoo.dylib -o foo -read_only_relocs suppress -Wl,-w + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf test.o libfoo.dylib libtest.dylib foo diff --git a/ld64/unit-tests/test-cases/read-only-relocs/foo.c b/ld64/unit-tests/test-cases/read-only-relocs/foo.c new file mode 100644 index 0000000..bef1580 --- /dev/null +++ b/ld64/unit-tests/test-cases/read-only-relocs/foo.c @@ -0,0 +1,6 @@ + +int b=0; + +void func() {} + + diff --git a/ld64/unit-tests/test-cases/read-only-relocs/test.c b/ld64/unit-tests/test-cases/read-only-relocs/test.c new file mode 100644 index 0000000..f3484e2 --- /dev/null +++ b/ld64/unit-tests/test-cases/read-only-relocs/test.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int a=0; +extern int b; +extern void func(); + +int main() +{ + func(); + return a+b; +} + diff --git a/ld64/unit-tests/test-cases/rebase-basic/Makefile b/ld64/unit-tests/test-cases/rebase-basic/Makefile new file mode 100644 index 0000000..ba262aa --- /dev/null +++ b/ld64/unit-tests/test-cases/rebase-basic/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to see that a dylib +# run through the rebase tool is the same as if +# the dylib was originally built at that address +# + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o + + ${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o + + ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation + ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib + + ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo-alt.${ARCH}.dylib + + rebase -arch ${ARCH} -low_address 0x12340000 libfoo.${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib + + ${PASS_IFF} diff libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib + +clean: + rm *.o *.dylib diff --git a/ld64/unit-tests/test-cases/rebase-basic/bar.m b/ld64/unit-tests/test-cases/rebase-basic/bar.m new file mode 100644 index 0000000..ff4f2ac --- /dev/null +++ b/ld64/unit-tests/test-cases/rebase-basic/bar.m @@ -0,0 +1,13 @@ +#include + +@interface Bar : NSObject + +-(void) blah; + +@end + +@implementation Bar + +-(void) blah {} + +@end \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/rebase-basic/comment.txt b/ld64/unit-tests/test-cases/rebase-basic/comment.txt new file mode 100644 index 0000000..013eb45 --- /dev/null +++ b/ld64/unit-tests/test-cases/rebase-basic/comment.txt @@ -0,0 +1 @@ +The point of this test is to see that a dylib run through the rebase tool is the same as if the dylib was originally built at that address diff --git a/ld64/unit-tests/test-cases/rebase-basic/foo.c b/ld64/unit-tests/test-cases/rebase-basic/foo.c new file mode 100644 index 0000000..15d4ae6 --- /dev/null +++ b/ld64/unit-tests/test-cases/rebase-basic/foo.c @@ -0,0 +1,14 @@ + +int foo() { return 10; } + +void* foop = &foo; + +int glob = 5; + +int* globp = &glob; + + +int big[3000]; + + + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/relocs-asm/Makefile b/ld64/unit-tests/test-cases/relocs-asm/Makefile new file mode 100644 index 0000000..c231ac4 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-asm/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 + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${ASMFLAGS} relocs-asm.s -c -o relocs-asm.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content relocs-asm.${ARCH}.o > relocs-asm.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs relocs-asm.${ARCH}.o -o relocs-asm-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content relocs-asm-r.${ARCH}.o > relocs-asm-r.${ARCH}.o.dump + + ${PASS_IFF} diff relocs-asm.${ARCH}.o.dump relocs-asm-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-asm/comment.txt b/ld64/unit-tests/test-cases/relocs-asm/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-asm/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s new file mode 100644 index 0000000..06e10e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#if __arm__ + .text + .align 2 + + .globl _test_loads +_test_loads: + @ PIC load of a + ldr r0, L6 +L0: + ldr r0, [pc, r0] + + @ PIC load of c + ldr r0, L6+4 +L1: + ldr r0, [pc, r0] + + @ sorta-absolute load of a + ldr r0, L6+8 + ldr r0, [r0, #0] + + @ sorta-absolute load of c + ldr r0, L6+12 + ldr r0, [r0, #0] + + @ sorta-absolute load of external + ldr r0, L6+16 + ldr r0, [r0, #0] + + @ PIC load of a + addend ?? + bx lr + +L6: + .long _a-(L0+8) + .long _c-(L1+8) + .long _a + .long _c + .long _ax + +_test_calls: + @ call internal + bl _test_branches + + @ call internal + addend + bl _test_branches+0x19000 + + @ call external + bl _external + + @ call external + addend + bl _external+0x19000 + + +_test_branches: + @ call internal + bne _test_calls + + @ call internal + addend + bne _test_calls+16 + + @ call external + bne _external + + @ call external + addend + bne _external+16 +#endif + +#if __ppc__ || __ppc64__ + + .text + .align 2 + + .globl _test_loads +_test_loads: + stmw r30,-8(r1) + stwu r1,-48(r1) +Lpicbase: + + ; PIC load of a + addis r2,r10,ha16(_a-Lpicbase) + lwz r2,lo16(_a-Lpicbase)(r2) + + ; PIC load of c + addis r2,r10,ha16(_c-Lpicbase) + lwz r2,lo16(_c-Lpicbase)(r2) + + ; absolute load of a + lis r2,ha16(_a) + lwz r2,lo16(_a)(r2) + + ; absolute load of c + lis r2,ha16(_c) + lwz r2,lo16(_c)(r2) + + ; absolute load of external + lis r2,ha16(_ax) + lwz r2,lo16(_ax)(r2) + + ; absolute lea of external + lis r2,hi16(_ax) + ori r2,r2,lo16(_ax) + + + ; PIC load of a + addend + addis r2,r10,ha16(_a+0x19000-Lpicbase) + lwz r2,lo16(_a+0x19000-Lpicbase)(r2) + + ; absolute load of a + addend + lis r2,ha16(_a+0x19000) + lwz r2,lo16(_a+0x19000)(r2) + + ; lea of a + addend + lis r2,ha16(_a+0x19000) + addi r2,r2,lo16(_a+0x19000) + + ; alt lea of a + addend + lis r2,hi16(_a+0x19000) + ori r2,r2,lo16(_a+0x19000) + + ; absolute load of external + addend + lis r2,ha16(_ax+0x19000) + lwz r2,lo16(_ax+0x19000)(r2) + + ; absolute lea of external + addend + lis r2,hi16(_ax+0x19000) + ori r2,r2,lo16(_ax+0x19000) + + + ; PIC load of a + addend + addis r2,r10,ha16(_a+0x09000-Lpicbase) + lwz r2,lo16(_a+0x09000-Lpicbase)(r2) + + ; absolute load of a + addend + lis r2,ha16(_a+0x09000) + lwz r2,lo16(_a+0x09000)(r2) + + ; lea of a + addend + lis r2,ha16(_a+0x09000) + addi r2,r2,lo16(_a+0x09000) + + ; alt lea of a + addend + lis r2,hi16(_a+0x09000) + ori r2,r2,lo16(_a+0x09000) + + ; absolute load of external + addend + lis r2,ha16(_ax+0x09000) + lwz r2,lo16(_ax+0x09000)(r2) + + ; absolute lea of external + addend + lis r2,hi16(_ax+0x09000) + ori r2,r2,lo16(_ax+0x09000) + + blr + + +_test_calls: + ; call internal + bl _test_branches + + ; call internal + addend + bl _test_branches+0x19000 + + ; call external + bl _external + + ; call external + addend + bl _external+0x19000 + + +_test_branches: + ; call internal + bne _test_calls + + ; call internal + addend + bne _test_calls+16 + + ; call external + bne _external + + ; call external + addend + bne _external+16 +#endif + + + +#if __i386__ + .text + .align 2 + + .globl _test_loads +_test_loads: + pushl %ebp +Lpicbase: + + # PIC load of a + movl _a-Lpicbase(%ebx), %eax + + # absolute load of a + movl _a, %eax + + # absolute load of external + movl _ax, %eax + + # absolute lea of external + leal _ax, %eax + + + # PIC load of a + addend + movl _a-Lpicbase+0x19000(%ebx), %eax + + # absolute load of a + addend + movl _a+0x19000(%ebx), %eax + + # absolute load of external + addend + movl _ax+0x19000(%ebx), %eax + + # absolute lea of external + addend + leal _ax+0x1900, %eax + + ret + + +_test_calls: + # call internal + call _test_branches + + # call internal + addend + call _test_branches+0x19000 + + # call external + call _external + + # call external + addend + call _external+0x19000 + + +_test_branches: + # call internal + jne _test_calls + + # call internal + addend + jne _test_calls+16 + + # call external + jne _external + + # call external + addend + jne _external+16 + +_pointer_diffs: + nop + call _get_ret_eax +1: movl _foo-1b(%eax),%esi + movl _foo+10-1b(%eax),%esi + movl _test_branches-1b(%eax),%esi + movl _test_branches+3-1b(%eax),%esi + +_word_relocs: + callw _pointer_diffs + +_byte_relocs: + mov $100, %ecx +c_1: + loop c_1 + mov $100, %ecx +c_2: + sub $(1), %ecx + jcxz c_2 + +#endif + + + +#if __x86_64__ + .text + .align 2 + + .globl _test_loads +_test_loads: + + # PIC load of a + movl _a(%rip), %eax + + # PIC load of a + addend + movl _a+0x1234(%rip), %eax + + # PIC lea + leaq _a(%rip), %rax + + # PIC lea through GOT + movq _a@GOTPCREL(%rip), %rax + + # PIC access of GOT + pushq _a@GOTPCREL(%rip) + + # PIC lea external through GOT + movq _ax@GOTPCREL(%rip), %rax + + # PIC external access of GOT + pushq _ax@GOTPCREL(%rip) + + # 1-byte store + movb $0x12, _a(%rip) + movb $0x12, _a+2(%rip) + movb $0x12, L0(%rip) + + # 4-byte store + movl $0x12345678, _a(%rip) + movl $0x12345678, _a+4(%rip) + movl $0x12345678, L0(%rip) + + # test local labels + lea L1(%rip), %rax + movl L0(%rip), %eax + + ret + + +_test_calls: + # call internal + call _test_branches + + # call internal + addend + call _test_branches+0x19000 + + # call external + call _external + + # call external + addend + call _external+0x19000 + + +_test_branches: + # call internal + jne _test_calls + + # call internal + addend + jne _test_calls+16 + + # call external + jne _external + + # call external + addend + jne _external+16 + +_byte_relocs: + mov $100, %ecx +c_1: + loop _byte_relocs + nop + +#endif + + + + # test that pointer-diff relocs are preserved + .text +_test_diffs: + .align 2 +Llocal2: + .long 0 + .long Llocal2-_test_branches + .long . - _test_branches + .long . - _test_branches + 8 + .long _test_branches - . + .long _test_branches - . + 8 + .long _test_branches - . - 8 +#if __ppc64__ + .quad Llocal2-_test_branches +#endif + +_foo: nop + + .align 2 +_distance_from_foo: + .long 0 + .long . - _foo + .long . - 8 - _foo + + +_distance_to_foo: + .long _foo - . + .long _foo - . + 4 + + +_distance_to_here: + .long _foo - _distance_to_here + .long _foo - _distance_to_here - 4 + .long _foo - _distance_to_here - 12 + .long 0 + + +#if __x86_64__ + .data +L0: .quad _test_branches +_prev: + .quad _test_branches+4 +L1: .quad _test_branches - _test_diffs + .quad _test_branches - _test_diffs + 4 + .long _test_branches - _test_diffs +# .long LCL0-. ### assembler bug: should SUB/UNSIGNED with content= LCL0-24, or single pc-rel SIGNED reloc with content = LCL0-.+4 + .quad L1 + .quad L0 + .quad _test_branches - . + .quad _test_branches - L1 + .quad L1 - _prev + +# the following generates: _foo cannot be undefined in a subtraction expression +# but it should be ok (it will be a linker error if _foo and _bar are not in same linkage unit) +# .quad _foo - _bar ### assembler bug + + .section __DATA,__data2 +LCL0: .long 2 + + +#endif + + + .data +_a: + .long 0 + +_b: +#if __ppc__ || __i386__ || __arm__ + .long _test_calls + .long _test_calls+16 + .long _external + .long _external+16 +#elif __ppc64__ || __x86_64__ + .quad _test_calls + .quad _test_calls+16 + .quad _external + .quad _external+16 +#endif + + # test that reloc sizes are the same +Llocal3: + .long 0 + +Llocal4: + .long 0 + + .long Llocal4-Llocal3 + +Lfiller: + .space 0x9000 +_c: + .long 0 + diff --git a/ld64/unit-tests/test-cases/relocs-c/Makefile b/ld64/unit-tests/test-cases/relocs-c/Makefile new file mode 100644 index 0000000..7428127 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-c/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +ifeq (${ARCH},x86_64) + ADDR_SHIFT = 0x1FF000000 +else + ADDR_SHIFT = 0xF0000000 +endif + + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${FAIL_IF_ERROR} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -seg1addr ${ADDR_SHIFT} -o test2-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test2-r.${ARCH}.o > test2-r.${ARCH}.o.dump + ${PASS_IFF} diff test.${ARCH}.o.dump test2-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-c/test.c b/ld64/unit-tests/test-cases/relocs-c/test.c new file mode 100644 index 0000000..b877760 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-c/test.c @@ -0,0 +1,76 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +static int foo; + +int __attribute__((visibility("hidden"))) foofoo; + +static int uninit_static; +static int init_static = 1; + int __attribute__((visibility("hidden"))) uninit_hidden; + int __attribute__((visibility("hidden"))) init_hidden = 1; + int uninit_global; + int init_global = 1; +extern int extern_global; +extern int __attribute__((visibility("hidden"))) extern_hidden; + +static int uninit_static_array[4]; +static int init_static_array[4] = {1,2,3,4}; + int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; + int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; + int uninit_global_array[4]; + int init_global_array[4] = {1,2,3,4}; +extern int extern_global_array[4]; + +int test1() { return uninit_static; } +int test2() { return init_static; } +int test3() { return uninit_hidden; } +int test4() { return init_hidden; } +int test5() { return uninit_global; } +int test6() { return init_global; } +int test7() { return extern_global; } +int test8() { return extern_hidden; } + +int test_array1() { return uninit_static_array[2]; } +int test_array2() { return init_static_array[2]; } +int test_array3() { return uninit_hidden_array[2]; } +int test_array4() { return init_hidden_array[2]; } +int test_array5() { return uninit_global_array[2]; } +int test_array6() { return init_global_array[2]; } +int test_array7() { return extern_global_array[2]; } + +static int foo2; +int test9() { return foo2; } + + +int* p_init_global = &init_global; +void* p_test1 = (void*)&test1; +unsigned char pad = 2; +unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... + +int func() __attribute__((visibility("hidden"))); +int func() { return foo; } + +int func2() { return func() + 1; } + diff --git a/ld64/unit-tests/test-cases/relocs-c2/Makefile b/ld64/unit-tests/test-cases/relocs-c2/Makefile new file mode 100644 index 0000000..767b210 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-c2/Makefile @@ -0,0 +1,58 @@ +## +# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# +# Currently for ppc64 the .o's alternate! in content +# + + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + #grep "plus" test.${ARCH}.o.dump | ${FAIL_IF_STDIN} + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + #grep "plus" test-r.${ARCH}.o.dump | ${FAIL_IF_STDIN} + + ${LD} -arch ${ARCH} -r -keep_private_externs test-r.${ARCH}.o -o test-r-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r-r.${ARCH}.o > test-r-r.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test-r-r.${ARCH}.o -o test-r-r-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r-r-r.${ARCH}.o > test-r-r-r.${ARCH}.o.dump + + ${PASS_IFF} diff -c -w test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-c2/comment.txt b/ld64/unit-tests/test-cases/relocs-c2/comment.txt new file mode 100644 index 0000000..2499674 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-c2/comment.txt @@ -0,0 +1,5 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes + +Currently for ppc64 the .o's alternate! in content diff --git a/ld64/unit-tests/test-cases/relocs-c2/test.c b/ld64/unit-tests/test-cases/relocs-c2/test.c new file mode 100644 index 0000000..b877760 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-c2/test.c @@ -0,0 +1,76 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +static int foo; + +int __attribute__((visibility("hidden"))) foofoo; + +static int uninit_static; +static int init_static = 1; + int __attribute__((visibility("hidden"))) uninit_hidden; + int __attribute__((visibility("hidden"))) init_hidden = 1; + int uninit_global; + int init_global = 1; +extern int extern_global; +extern int __attribute__((visibility("hidden"))) extern_hidden; + +static int uninit_static_array[4]; +static int init_static_array[4] = {1,2,3,4}; + int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; + int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; + int uninit_global_array[4]; + int init_global_array[4] = {1,2,3,4}; +extern int extern_global_array[4]; + +int test1() { return uninit_static; } +int test2() { return init_static; } +int test3() { return uninit_hidden; } +int test4() { return init_hidden; } +int test5() { return uninit_global; } +int test6() { return init_global; } +int test7() { return extern_global; } +int test8() { return extern_hidden; } + +int test_array1() { return uninit_static_array[2]; } +int test_array2() { return init_static_array[2]; } +int test_array3() { return uninit_hidden_array[2]; } +int test_array4() { return init_hidden_array[2]; } +int test_array5() { return uninit_global_array[2]; } +int test_array6() { return init_global_array[2]; } +int test_array7() { return extern_global_array[2]; } + +static int foo2; +int test9() { return foo2; } + + +int* p_init_global = &init_global; +void* p_test1 = (void*)&test1; +unsigned char pad = 2; +unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... + +int func() __attribute__((visibility("hidden"))); +int func() { return foo; } + +int func2() { return func() + 1; } + diff --git a/ld64/unit-tests/test-cases/relocs-literals/Makefile b/ld64/unit-tests/test-cases/relocs-literals/Makefile new file mode 100644 index 0000000..a9ca5ef --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-literals/test.c b/ld64/unit-tests/test-cases/relocs-literals/test.c new file mode 100644 index 0000000..2d199d0 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals/test.c @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + + +// rdar://problem/4732996 +const char* stringFutz(int x) { + return "hello" + 0x1000 + x; +} + +const char* usesAddend = "teststr" + 0x2000; diff --git a/ld64/unit-tests/test-cases/relocs-literals2/Makefile b/ld64/unit-tests/test-cases/relocs-literals2/Makefile new file mode 100644 index 0000000..23e4a82 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals2/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# +ifneq (${ARCH},x86_64) + PIC=-mdynamic-no-pic +endif + +run: all + +all: + ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-literals2/test.c b/ld64/unit-tests/test-cases/relocs-literals2/test.c new file mode 100644 index 0000000..2d199d0 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals2/test.c @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + + +// rdar://problem/4732996 +const char* stringFutz(int x) { + return "hello" + 0x1000 + x; +} + +const char* usesAddend = "teststr" + 0x2000; diff --git a/ld64/unit-tests/test-cases/relocs-literals3/Makefile b/ld64/unit-tests/test-cases/relocs-literals3/Makefile new file mode 100644 index 0000000..e0fa4ad --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals3/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} -Os -mdynamic-no-pic test.c -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff -C 6 test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-literals3/comment.txt b/ld64/unit-tests/test-cases/relocs-literals3/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals3/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/unit-tests/test-cases/relocs-literals3/test.c b/ld64/unit-tests/test-cases/relocs-literals3/test.c new file mode 100644 index 0000000..31e87c2 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-literals3/test.c @@ -0,0 +1,47 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +const char* foo = "foo"; +const char* const bar = "bar"; + +const char charArray1[] = "charArray1"; +static const char charArray2[] = "charArray2"; + + +const char* getString() { return "string"; } +const char* getString2() { return charArray2; } +const char* getString3() { return charArray1; } +const char* getString4() { return foo; } + + +float f1 = 3.0; +double d1 = 3.0; +long double ld1 = 3.0; + + + +float getSingle() { return 1.0; } +double getDouble() { return 2.0; } +long double getLongDouble() { return 3.0; } + diff --git a/ld64/unit-tests/test-cases/relocs-objc/Makefile b/ld64/unit-tests/test-cases/relocs-objc/Makefile new file mode 100644 index 0000000..0f8846d --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-objc/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify a .o file can round-trip +# through ld -r correctly. The ObjectDump utility is used +# dump a "canonical" textual representation of a .o file. +# The before and after .o files are then diff'ed. +# No differences means this test passes +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/relocs-objc/comment.txt b/ld64/unit-tests/test-cases/relocs-objc/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-objc/comment.txt @@ -0,0 +1,3 @@ +The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used +dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. +No differences means this test passes diff --git a/ld64/unit-tests/test-cases/relocs-objc/test.m b/ld64/unit-tests/test-cases/relocs-objc/test.m new file mode 100644 index 0000000..1ca2157 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-objc/test.m @@ -0,0 +1,59 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +@interface Foo : NSObject +{ + int ivar; +} +- (id) init; +- (void) foo; +@end + + +@implementation Foo +- (id) init +{ + self = [super init]; + return self; +} + +- (void) foo +{ + [self class]; +} +@end + + + +@interface Base +@end + + +@implementation Base +@end + + + diff --git a/ld64/unit-tests/test-cases/segment-order/Makefile b/ld64/unit-tests/test-cases/segment-order/Makefile new file mode 100644 index 0000000..76b4e95 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-order/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate linker puts non-standard segments in order of discovery +# + +all: + ${CC} ${CCFLAGS} main.c segKKK.s segJJJ.s segLLL.s -o main + nm -j -n main | grep _sym_ > symbol.order + ${FAIL_IF_ERROR} diff symbol.order expected.order + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main symbol.order diff --git a/ld64/unit-tests/test-cases/segment-order/expected.order b/ld64/unit-tests/test-cases/segment-order/expected.order new file mode 100644 index 0000000..e05b042 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-order/expected.order @@ -0,0 +1,3 @@ +_sym_kkk +_sym_jjj +_sym_lll diff --git a/ld64/unit-tests/test-cases/segment-order/main.c b/ld64/unit-tests/test-cases/segment-order/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-order/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/ld64/unit-tests/test-cases/segment-order/segJJJ.s b/ld64/unit-tests/test-cases/segment-order/segJJJ.s new file mode 100644 index 0000000..d9f5f71 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-order/segJJJ.s @@ -0,0 +1,7 @@ + + .section __JJJ,__jjj +_sym_jjj: .space 128 + + + + diff --git a/ld64/unit-tests/test-cases/segment-order/segKKK.s b/ld64/unit-tests/test-cases/segment-order/segKKK.s new file mode 100644 index 0000000..70b1952 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-order/segKKK.s @@ -0,0 +1,7 @@ + + .section __KKK,__kkk +_sym_kkk: .space 128 + + + + diff --git a/ld64/unit-tests/test-cases/segment-order/segLLL.s b/ld64/unit-tests/test-cases/segment-order/segLLL.s new file mode 100644 index 0000000..045eea4 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-order/segLLL.s @@ -0,0 +1,7 @@ + + .section __LLL,__lll +_sym_lll: .space 128 + + + + diff --git a/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile b/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile new file mode 100644 index 0000000..ee6a0e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Sanity check that -slow_stubs for i386 leaves no __IMPORT segment +# + +run: all + + + +all: + ${CC} ${CCFLAGS} hello.c -o hello -Wl,-slow_stubs + size -l hello | grep __IMPORT | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib -Wl,-slow_stubs + size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} hello + +clean: + rm hello libhello.dylib diff --git a/ld64/unit-tests/test-cases/slow-x86-stubs/hello.c b/ld64/unit-tests/test-cases/slow-x86-stubs/hello.c new file mode 100644 index 0000000..fe3b0df --- /dev/null +++ b/ld64/unit-tests/test-cases/slow-x86-stubs/hello.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} diff --git a/ld64/unit-tests/test-cases/special-labels/Makefile b/ld64/unit-tests/test-cases/special-labels/Makefile new file mode 100644 index 0000000..060f12e --- /dev/null +++ b/ld64/unit-tests/test-cases/special-labels/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# automatically strips labels starting with 'l' and 'L' +# + +run: all + +all: + as -arch ${ARCH} -L extra.s -o extra.o + ${CC} ${CCFLAGS} main.c extra.o -o main + nm main | grep "lother" | ${FAIL_IF_STDIN} + nm main | grep "L123" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.o diff --git a/ld64/unit-tests/test-cases/special-labels/extra.s b/ld64/unit-tests/test-cases/special-labels/extra.s new file mode 100644 index 0000000..0755508 --- /dev/null +++ b/ld64/unit-tests/test-cases/special-labels/extra.s @@ -0,0 +1,9 @@ + + + .data + +_foo: .long 0 +lother: .long 0 +L123: .long 0 +_bar: .long 0 + diff --git a/ld64/unit-tests/test-cases/special-labels/main.c b/ld64/unit-tests/test-cases/special-labels/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/ld64/unit-tests/test-cases/special-labels/main.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/stabs-coalesce/Makefile b/ld64/unit-tests/test-cases/stabs-coalesce/Makefile new file mode 100644 index 0000000..6e11c59 --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# removes the stabs associated with a copy of a coalesced +# function that was removed. +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: hello.o other.o + ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} + ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH} + nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count + echo " 1" > one + ${PASS_IFF} diff stabs-hello-foo-count one + +hello.o : hello.cxx + ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ + ${FAIL_IF_BAD_OBJ} $@ + +other.o : other.cxx + ${CXX} ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ + ${FAIL_IF_BAD_OBJ} $@ + +clean: + rm -rf stabs-hello-* *.o *.stabs stabs-hello-foo-count one diff --git a/ld64/unit-tests/test-cases/stabs-coalesce/comment.txt b/ld64/unit-tests/test-cases/stabs-coalesce/comment.txt new file mode 100644 index 0000000..f22b9a1 --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-coalesce/comment.txt @@ -0,0 +1,3 @@ +The point of this test is a sanity check that ld removes the stabs associated with a copy of a coalesced +function that was removed. Running nm through stabs-filter.pl produces connonical stabs +that can be diffed against a checked in know good set of stabs diff --git a/ld64/unit-tests/test-cases/stabs-coalesce/header.h b/ld64/unit-tests/test-cases/stabs-coalesce/header.h new file mode 100644 index 0000000..378308f --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-coalesce/header.h @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); diff --git a/ld64/unit-tests/test-cases/stabs-coalesce/hello.cxx b/ld64/unit-tests/test-cases/stabs-coalesce/hello.cxx new file mode 100644 index 0000000..33bf273 --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-coalesce/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/stabs-coalesce/other.cxx b/ld64/unit-tests/test-cases/stabs-coalesce/other.cxx new file mode 100644 index 0000000..ee97d7d --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-coalesce/other.cxx @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "header.h" + +int uninit; +int init = 1; +static int suninit; +static int sinit=0; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + + diff --git a/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile b/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile new file mode 100644 index 0000000..5318933 --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test that file paths in a stab reference ends with a / +# if there is no terminating /, gdb does not recognize this as a file path +# The provided files coalesced1a.o coalesced1b.o are ppc64 linked +# rdar://problem/4565088 + +run: all + +all: + $(CXX) -gstabs+ main.c -o outfile + ${FAIL_IF_BAD_MACHO} outfile + nm -ap outfile | ${PASS_IFF} grep '.*\.*test-cases.*/$$' + +clean: + rm outfile* diff --git a/ld64/unit-tests/test-cases/stabs-directory-slash/main.c b/ld64/unit-tests/test-cases/stabs-directory-slash/main.c new file mode 100644 index 0000000..54dc4c5 --- /dev/null +++ b/ld64/unit-tests/test-cases/stabs-directory-slash/main.c @@ -0,0 +1,3 @@ +main() +{ +} diff --git a/ld64/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest b/ld64/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest new file mode 100644 index 0000000..a9e452f --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest @@ -0,0 +1,77 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test the ld commands -stack_addr, -stack_size +# Test using -stack_addr only + + +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xC0000000 + STACK_SIZE = 0x04000000 + STACK_TOP = 0xbc000000 +else +#ifeq (${ARCH},x86_64) + STACK_ADDR = 0x0007fff5fc000000 + STACK_TOP = 0x00007fff57000000 + STACK_SIZE = 0x0000000005000000 +#else + #STACK_ADDR = 0x0007ffff00000000 + #STACK_TOP = 0x0007fffefb000000 + #STACK_SIZE = 0x0000000005000000 +#endif +endif + + +run: all + +all: +# info seems to not work, use warning: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c main.c -o main-${ARCH}.o + + + ${FAIL_IF_ERROR} ${LD} -arch ${ARCH} \ + -stack_addr ${STACK_ADDR} \ + -lcrt1.o -lSystem \ + main-${ARCH}.o -o main \ + 2>lderr.out + +# Can check warning if desired. +#ifeq (,${findstring 64,$(ARCH)}) +# grep "warning no -stack_size specified using the default size" lderr.out | ${FAIL_IF_EMPTY} +#else +# grep "failed: -stack_addr must be used with -stack_size" lderr.out | ${FAIL_IF_EMPTY} +#endif + + +# Check for __UNIXSTACK section in object, check that it has the correct value + ${FAIL_IF_ERROR} ${OTOOL} -l main>ldcmds.out + (echo '1,/^[ ]*segname __UNIXSTACK$$/-d'; echo '/^[ ]*segname /,$$d'; echo w; echo q) | ed ldcmds.out >/dev/null + grep __UNIXSTACK ldcmds.out | ${FAIL_IF_EMPTY} + grep " vmsize[ ]*${STACK_SIZE}" ldcmds.out | ${FAIL_IF_EMPTY} + grep " vmaddr[ ]*${STACK_TOP}" ldcmds.out | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.o *.err *.out main diff --git a/ld64/unit-tests/test-cases/stack_addr_no_size/comment.txt b/ld64/unit-tests/test-cases/stack_addr_no_size/comment.txt new file mode 100644 index 0000000..da74f89 --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_addr_no_size/comment.txt @@ -0,0 +1,11 @@ +Test the ld commands -stack_addr, -stack_size (3939852 and 4729162) + + +-stack_addr value +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. + + +-stack_size value +Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . + + diff --git a/ld64/unit-tests/test-cases/stack_addr_no_size/main.c b/ld64/unit-tests/test-cases/stack_addr_no_size/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_addr_no_size/main.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/stack_addr_size/Makefile b/ld64/unit-tests/test-cases/stack_addr_size/Makefile new file mode 100644 index 0000000..670f014 --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_addr_size/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test the ld option -stack_addr and -stack_size used together + +ifeq ($(ARCH),armv6) + STACK_ADDR = 0x2C000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0x27000000 +else +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xCC000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0xc7000000 +else + STACK_ADDR = 0x110000000 + STACK_TOP = 0x000000010b000000 + STACK_SIZE = 0x0000000005000000 +endif +endif + +run: all + + + +all: + ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR} + # Check for __UNIXSTACK section in object, check that it has the correct value + 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 + +clean: + rm -rf main main.otool diff --git a/ld64/unit-tests/test-cases/stack_addr_size/comment.txt b/ld64/unit-tests/test-cases/stack_addr_size/comment.txt new file mode 100644 index 0000000..da74f89 --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_addr_size/comment.txt @@ -0,0 +1,11 @@ +Test the ld commands -stack_addr, -stack_size (3939852 and 4729162) + + +-stack_addr value +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. + + +-stack_size value +Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . + + diff --git a/ld64/unit-tests/test-cases/stack_addr_size/main.c b/ld64/unit-tests/test-cases/stack_addr_size/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_addr_size/main.c @@ -0,0 +1,29 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/stack_size_no_addr/Makefile b/ld64/unit-tests/test-cases/stack_size_no_addr/Makefile new file mode 100644 index 0000000..6134c7e --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_size_no_addr/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test the ld option -stack_size adds a custom stack segment + +ifeq ($(ARCH),armv6) + STACK_ADDR = 0x30000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0x2b000000 +else +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xC0000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0xbb000000 +else + STACK_ADDR = 0x0007fff5fc000000 + STACK_TOP = 0x00007fff57000000 + STACK_SIZE = 0x0000000005000000 +endif +endif + + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} + # Check for __UNIXSTACK section in object, check that it has the correct value + 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 + +clean: + rm -rf main main.otool diff --git a/ld64/unit-tests/test-cases/stack_size_no_addr/comment.txt b/ld64/unit-tests/test-cases/stack_size_no_addr/comment.txt new file mode 100644 index 0000000..5933975 --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_size_no_addr/comment.txt @@ -0,0 +1,11 @@ +Test the ld commands -stack_addr, -stack_size + + +-stack_addr value +Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. + + +-stack_size value +Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . + + diff --git a/ld64/unit-tests/test-cases/stack_size_no_addr/main.c b/ld64/unit-tests/test-cases/stack_size_no_addr/main.c new file mode 100644 index 0000000..4aaef3a --- /dev/null +++ b/ld64/unit-tests/test-cases/stack_size_no_addr/main.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#if __x86_64__ +static char buffer[8000000000]; +#elif __arm__ +static char buffer[100000000]; +#else +static char buffer[2000000000]; +#endif + +int main() +{ + return buffer[0]; +} diff --git a/ld64/unit-tests/test-cases/static-executable/Makefile b/ld64/unit-tests/test-cases/static-executable/Makefile new file mode 100644 index 0000000..2d71eff --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable/Makefile @@ -0,0 +1,35 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that ld can link a static executable +# + +all: + ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test diff --git a/ld64/unit-tests/test-cases/static-executable/test.c b/ld64/unit-tests/test-cases/static-executable/test.c new file mode 100644 index 0000000..27fe88d --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable/test.c @@ -0,0 +1,11 @@ + +int foo() +{ + return 0; +} + + +int entry() +{ + return foo(); +} diff --git a/ld64/unit-tests/test-cases/static-strip/Makefile.newtest b/ld64/unit-tests/test-cases/static-strip/Makefile.newtest new file mode 100644 index 0000000..323200e --- /dev/null +++ b/ld64/unit-tests/test-cases/static-strip/Makefile.newtest @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a static executable (requires non-public archives) +# + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} test.c -static -o test-${ARCH} -L/usr/local/lib/system -lc_static -lm_static -Wl,-new_linker + ${FAIL_IF_BAD_MACHO} test-${ARCH} + ${FAIL_IF_ERROR} strip test-${ARCH} + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm -rf test-* diff --git a/ld64/unit-tests/test-cases/static-strip/comment.txt b/ld64/unit-tests/test-cases/static-strip/comment.txt new file mode 100644 index 0000000..bc535a3 --- /dev/null +++ b/ld64/unit-tests/test-cases/static-strip/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a static executable (requires non-public archives) diff --git a/ld64/unit-tests/test-cases/static-strip/test.c b/ld64/unit-tests/test-cases/static-strip/test.c new file mode 100644 index 0000000..ab472fb --- /dev/null +++ b/ld64/unit-tests/test-cases/static-strip/test.c @@ -0,0 +1,28 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/strip-test2/Makefile b/ld64/unit-tests/test-cases/strip-test2/Makefile new file mode 100644 index 0000000..778770e --- /dev/null +++ b/ld64/unit-tests/test-cases/strip-test2/Makefile @@ -0,0 +1,70 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test strip: symbols referenced by indirect symbol table entries that can'tÊ +# be stripped in: +# __ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +# __ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +# __ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +# __ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +# __ZN9__gnu_cxx13new_allocatorIiED2Ev +# __ZNSt6vectorIiSaIiEEC1ERKS0_ +# __ZNSaIiEC1ERKS_ +# __ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +# __ZNSaIiED2Ev +# __ZNSt12_Vector_baseIiSaIiEED2Ev +# __ZNSaIiEC1Ev +# __ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +# __ZNSt6vectorIiSaIiEED1Ev +# __ZNSaIiED1Ev +# __ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +# __ZN9__gnu_cxx13new_allocatorIiEC2Ev +# __ZNSaIiEC2ERKS_ +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev + + +run: all + + +all: + $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_BAD_MACHO} main + ${FAIL_IF_ERROR} nm -j main >main-no-strip.nm + $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_BAD_MACHO} main + + # Make sure there are no symbols in the stripped file that aren't + # in the unstripped + nm -j main | comm -23 - main-no-strip.nm | ${FAIL_IF_STDIN} + + # Now make sure that all the __Z symbols exist + strip_cnt=`nm -j main | comm -12 - main-no-strip.nm | grep -c __Z`; \ + nostrip_cnt=`nm -j main|grep -c __Z`; \ + [ x"$$strip_cnt" = x"$$nostrip_cnt" ] + @echo PASS $$UNIT_TEST_NAME + +clean: + rm -rf *.o main-* main diff --git a/ld64/unit-tests/test-cases/strip-test2/comment.txt b/ld64/unit-tests/test-cases/strip-test2/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/ld64/unit-tests/test-cases/strip-test2/comment.txt @@ -0,0 +1,21 @@ +Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) + +__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +__ZN9__gnu_cxx13new_allocatorIiED2Ev +__ZNSt6vectorIiSaIiEEC1ERKS0_ +__ZNSaIiEC1ERKS_ +__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +__ZNSaIiED2Ev +__ZNSt12_Vector_baseIiSaIiEED2Ev +__ZNSaIiEC1Ev +__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +__ZNSt6vectorIiSaIiEED1Ev +__ZNSaIiED1Ev +__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +__ZN9__gnu_cxx13new_allocatorIiEC2Ev +__ZNSaIiEC2ERKS_ +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/unit-tests/test-cases/strip-test2/main.cxx b/ld64/unit-tests/test-cases/strip-test2/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/ld64/unit-tests/test-cases/strip-test2/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/ld64/unit-tests/test-cases/strip-test3/Makefile.newtest b/ld64/unit-tests/test-cases/strip-test3/Makefile.newtest new file mode 100644 index 0000000..c1ad4c4 --- /dev/null +++ b/ld64/unit-tests/test-cases/strip-test3/Makefile.newtest @@ -0,0 +1,71 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Test strip: symbols referenced by indirect symbol table entries that can'tÊ +# be stripped in: +# __ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +# __ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +# __ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +# __ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +# __ZN9__gnu_cxx13new_allocatorIiED2Ev +# __ZNSt6vectorIiSaIiEEC1ERKS0_ +# __ZNSaIiEC1ERKS_ +# __ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +# __ZNSaIiED2Ev +# __ZNSt12_Vector_baseIiSaIiEED2Ev +# __ZNSaIiEC1Ev +# __ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +# __ZNSt6vectorIiSaIiEED1Ev +# __ZNSaIiED1Ev +# __ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +# __ZN9__gnu_cxx13new_allocatorIiEC2Ev +# __ZNSaIiEC2ERKS_ +# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev + + +run: all + + +all: + ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -o main + ${FAIL_IF_BAD_MACHO} main + ${FAIL_IF_ERROR} nm -j main >main-no-strip.nm + ${FAIL_IF_ERROR} strip main + ${FAIL_IF_BAD_MACHO} main + ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -s -o main + ${PASS_IFF_GOOD_MACHO} main + + # Make sure there are no symbols in the stripped file that aren't + # in the unstripped + nm -j main | comm -23 - main-no-strip.nm | ${FAIL_IF_STDIN} + + # Now make sure that all the __Z symbols exist + strip_cnt=`nm -j main | comm -12 - main-no-strip.nm | grep -c __Z`; \ + nostrip_cnt=`nm -j main|grep -c __Z`; \ + [ x"$$strip_cnt" = x"$$nostrip_cnt" ] + @echo PASS $$UNIT_TEST_NAME +clean: + rm -rf *.o main main-* *.nm *.out diff --git a/ld64/unit-tests/test-cases/strip-test3/comment.txt b/ld64/unit-tests/test-cases/strip-test3/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/ld64/unit-tests/test-cases/strip-test3/comment.txt @@ -0,0 +1,21 @@ +Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) + +__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi +__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv +__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ +__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim +__ZN9__gnu_cxx13new_allocatorIiED2Ev +__ZNSt6vectorIiSaIiEEC1ERKS0_ +__ZNSaIiEC1ERKS_ +__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ +__ZNSaIiED2Ev +__ZNSt12_Vector_baseIiSaIiEED2Ev +__ZNSaIiEC1Ev +__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ +__ZNSt6vectorIiSaIiEED1Ev +__ZNSaIiED1Ev +__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ +__ZN9__gnu_cxx13new_allocatorIiEC2Ev +__ZNSaIiEC2ERKS_ +__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/unit-tests/test-cases/strip-test3/main.cxx b/ld64/unit-tests/test-cases/strip-test3/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/ld64/unit-tests/test-cases/strip-test3/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/ld64/unit-tests/test-cases/strip_local/Makefile b/ld64/unit-tests/test-cases/strip_local/Makefile new file mode 100644 index 0000000..f32267c --- /dev/null +++ b/ld64/unit-tests/test-cases/strip_local/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# This test case checks merges two .o files. One uses +# a lazy and non-lazy pointer to access the second. +# The result then has local symbols stripped. So the +# end result is a .o file with a lazy and non-lazy pointer to +# anonymous stuff. +# +# + + +run: all + +all: + ${CC} ${CCFLAGS} hello.c -c + ${FAIL_IF_BAD_OBJ} hello.o + + ${CC} ${CCFLAGS} foo.c -c + ${FAIL_IF_BAD_OBJ} foo.o + + ${LD} -r hello.o foo.o -o hellofoo.o + ${FAIL_IF_BAD_OBJ} hellofoo.o + + strip -x hellofoo.o -o hellofoostripped.o + ${CC} ${CCFLAGS} hellofoostripped.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.o main diff --git a/ld64/unit-tests/test-cases/strip_local/foo.c b/ld64/unit-tests/test-cases/strip_local/foo.c new file mode 100644 index 0000000..defa5eb --- /dev/null +++ b/ld64/unit-tests/test-cases/strip_local/foo.c @@ -0,0 +1,8 @@ + + +int __attribute__((visibility("hidden"))) data = 3; + +void __attribute__((visibility("hidden"))) func(int x) { } + + + diff --git a/ld64/unit-tests/test-cases/strip_local/hello.c b/ld64/unit-tests/test-cases/strip_local/hello.c new file mode 100644 index 0000000..2deed42 --- /dev/null +++ b/ld64/unit-tests/test-cases/strip_local/hello.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int data; +extern void func(int); + +int main() +{ + func(data); +} + diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile new file mode 100644 index 0000000..c0647b3 --- /dev/null +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ifeq "${ARCH}" "i386" + POINTER_SEGMENT = __IMPORT + POINTER_SECTION = __pointers +else + POINTER_SEGMENT = __DATA + POINTER_SECTION = __nl_symbol_ptr +endif + + +# +# Test that using strip -R to selectively strip symbol names +# of of a .o file still works with ld. +# + +run: all + +all: + ${CC} ${CCFLAGS} a.c -c -o a.o + ${CC} ${CCFLAGS} b.c -c -o b.o + ${CC} ${CCFLAGS} c.c -c -o c.o + ${CC} ${CCFLAGS} func.c -c -o func.o + ${LD} -arch ${ARCH} -r a.o b.o c.o -o most.o + strip -x -R strip.list most.o -o most.stripped.o + ${CC} ${CCFLAGS} most.stripped.o func.o -dynamiclib -o dylib1 + ${LD} -arch ${ARCH} -r most.stripped.o func.o -o all.o + ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 + otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers + otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers + ${PASS_IFF} diff dylib1.pointers dylib2.pointers + +clean: + rm -rf *.o dylib1 dylib2 *.pointers diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/a.c b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/a.c new file mode 100644 index 0000000..141b6d9 --- /dev/null +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/a.c @@ -0,0 +1,7 @@ + +int aData = 0; + +void a() +{ + ++aData; +} diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/b.c b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/b.c new file mode 100644 index 0000000..9608402 --- /dev/null +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/b.c @@ -0,0 +1,12 @@ + +int bData = 0; + +void b() +{ + ++bData; +} + +void bb() +{ + ++bData; +} diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/c.c b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/c.c new file mode 100644 index 0000000..db8276a --- /dev/null +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/c.c @@ -0,0 +1,11 @@ +extern void b(); +extern void bb(); + +extern void func(void*); + + +void c() +{ + func(&b); + func(&bb); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/func.c b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/func.c new file mode 100644 index 0000000..5724811 --- /dev/null +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/func.c @@ -0,0 +1 @@ +void func(void* x) {} diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list new file mode 100644 index 0000000..77ac6e9 --- /dev/null +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list @@ -0,0 +1,2 @@ +_b +_bb diff --git a/ld64/unit-tests/test-cases/stub-generation-weak/Makefile b/ld64/unit-tests/test-cases/stub-generation-weak/Makefile new file mode 100644 index 0000000..e7cf05c --- /dev/null +++ b/ld64/unit-tests/test-cases/stub-generation-weak/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld generates correct stubs when some are weak_import +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + otool -Iv main | grep '_foo' | ${FAIL_IF_EMPTY} + otool -Iv main | grep '_bar' | ${FAIL_IF_EMPTY} + otool -Iv main | grep '_baz' | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/stub-generation-weak/foo.c b/ld64/unit-tests/test-cases/stub-generation-weak/foo.c new file mode 100644 index 0000000..0122e53 --- /dev/null +++ b/ld64/unit-tests/test-cases/stub-generation-weak/foo.c @@ -0,0 +1,5 @@ + +void foo() {} +void bar() {} +void baz() {} + diff --git a/ld64/unit-tests/test-cases/stub-generation-weak/main.c b/ld64/unit-tests/test-cases/stub-generation-weak/main.c new file mode 100644 index 0000000..1803c6a --- /dev/null +++ b/ld64/unit-tests/test-cases/stub-generation-weak/main.c @@ -0,0 +1,40 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void foo() __attribute__((weak_import)); +extern void bar() __attribute__((weak_import)); +extern void baz(); + +int main() +{ + if ( &foo != 0 ) { + foo(); + bar(); + } + baz(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/stub-generation/Makefile b/ld64/unit-tests/test-cases/stub-generation/Makefile new file mode 100644 index 0000000..9e6ecc0 --- /dev/null +++ b/ld64/unit-tests/test-cases/stub-generation/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld generates correct stubs +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable -o libtest.dylib + # only stub should be to _test + otool -Iv libtest.dylib | grep '1 entries' | ${FAIL_IF_EMPTY} + otool -Iv libtest.dylib | grep '_test' | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + + +clean: + rm libtest.dylib diff --git a/ld64/unit-tests/test-cases/stub-generation/test.c b/ld64/unit-tests/test-cases/stub-generation/test.c new file mode 100644 index 0000000..4573622 --- /dev/null +++ b/ld64/unit-tests/test-cases/stub-generation/test.c @@ -0,0 +1,40 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +const char kMyStr[] = "hello"; + +int test() +{ + return 10; +} + + +const char* getstr() +{ + test(); + return kMyStr; +} + + diff --git a/ld64/unit-tests/test-cases/switch-jump-table/Makefile b/ld64/unit-tests/test-cases/switch-jump-table/Makefile new file mode 100644 index 0000000..aacd78d --- /dev/null +++ b/ld64/unit-tests/test-cases/switch-jump-table/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -mdynamic-no-pic jump table in the middle of +# a function does not cause relocations. +# +# SPEC2000/eon built with -mdynamic-no-pic won't run +# + +run: all + +all: + # check jump table in a weak function + ${CC} ${CCFLAGS} main.c switch.s -o main + 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 + 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 + 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 + ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-pie -read_only_relocs suppress + otool -rv main | grep "External relocation" | ${FAIL_IF_STDIN} + otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -f main diff --git a/ld64/unit-tests/test-cases/switch-jump-table/interpose.exp b/ld64/unit-tests/test-cases/switch-jump-table/interpose.exp new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/ld64/unit-tests/test-cases/switch-jump-table/interpose.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/ld64/unit-tests/test-cases/switch-jump-table/main.c b/ld64/unit-tests/test-cases/switch-jump-table/main.c new file mode 100644 index 0000000..f44b624 --- /dev/null +++ b/ld64/unit-tests/test-cases/switch-jump-table/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/switch-jump-table/switch.s b/ld64/unit-tests/test-cases/switch-jump-table/switch.s new file mode 100644 index 0000000..12b559f --- /dev/null +++ b/ld64/unit-tests/test-cases/switch-jump-table/switch.s @@ -0,0 +1,49 @@ + + .section __TEXT,__textcoal_nt,coalesced,pure_instructions + + + +/* + Simulate a switch statement in a weak function compiled + to a jump table +*/ + .globl _foo + .weak_definition _foo +_foo: + nop + nop +#if __arm__ || __i386__ + .long L1 + .long L2 + .long L3 +#endif + nop +L1: nop +L2: nop +L3: nop + nop + + +/* + Simulate a switch statement in a regular function compiled + to a jump table +*/ + .text + .globl _bar +_bar: nop + nop + nop + nop +#if __arm__ || __i386__ + .long L5 + .long L6 + .long L7 +#endif + nop +L5: nop +L6: nop +L7: nop + nop + + + diff --git a/ld64/unit-tests/test-cases/symbol-moving/Makefile b/ld64/unit-tests/test-cases/symbol-moving/Makefile new file mode 100644 index 0000000..fc85d7b --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/Makefile @@ -0,0 +1,93 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test magic $ld$ symbols which tell ld to view exported symbols +# differently than dyld sees them. +# +# In this test case aaa and bbb both moved between libfoo and libar +# between 10.4 and 10.5. +# + + +run: all + +all: + # In this test case aaa and bbb both moved between libfoo and libar + # between 10.4 and 10.5. + ${CC} ${CCFLAGS} -dynamiclib bar.c bbb.c anotb.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c aaa.c bnota.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c -o main-10.4 libfoo.dylib libbar.dylib -mmacosx-version-min=10.4 + nm -m main-10.4 | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} + nm -m main-10.4 | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.4a libbar.dylib libfoo.dylib -mmacosx-version-min=10.4 + nm -m main-10.4a | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} + nm -m main-10.4a | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5 libfoo.dylib libbar.dylib -mmacosx-version-min=10.5 + nm -m main-10.5 | grep _aaa | grep libfoo | ${FAIL_IF_EMPTY} + nm -m main-10.5 | grep _bbb | grep libbar | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5a libbar.dylib libfoo.dylib -mmacosx-version-min=10.5 + nm -m main-10.5a | grep _aaa | grep libfoo | ${FAIL_IF_EMPTY} + nm -m main-10.5a | grep _bbb | grep libbar | ${FAIL_IF_EMPTY} + # In this test case aaa and bbb both moved between subframeworks of Foo and Bar + # between 10.4 and 10.5. + mkdir -p Frameworks/Foo.framework/Frameworks/subFoo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.c aaa.c -o Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo \ + -install_name /System/Library/Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo \ + -umbrella Foo + ${CC} ${CCFLAGS} -dynamiclib bnota.c -o Frameworks/Foo.framework/Foo \ + -install_name /System/Library/Frameworks/Frameworks/Foo.framework/Foo \ + Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo + mkdir -p Frameworks/Bar.framework/Frameworks/subBar.framework + ${CC} ${CCFLAGS} -dynamiclib bar.c bbb.c -o Frameworks/Bar.framework/Frameworks/subBar.framework/subBar \ + -install_name /System/Library/Frameworks/Bar.framework/Frameworks/subBar.framework/subBar \ + -umbrella Bar + ${CC} ${CCFLAGS} -dynamiclib anotb.c -o Frameworks/Bar.framework/Bar \ + -install_name /System/Library/Frameworks/Frameworks/Bar.framework/Bar \ + Frameworks/Bar.framework/Frameworks/subBar.framework/subBar + ${CC} ${CCFLAGS} main.c -o main-10.4 -framework Foo -framework Bar -mmacosx-version-min=10.4 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.4 | grep _aaa | grep " Bar" | ${FAIL_IF_EMPTY} + nm -m main-10.4 | grep _bbb | grep " Foo" | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.4a -framework Bar -framework Foo -mmacosx-version-min=10.4 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.4a | grep _aaa | grep " Bar" | ${FAIL_IF_EMPTY} + nm -m main-10.4a | grep _bbb | grep " Foo" | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5 -framework Foo -framework Bar -mmacosx-version-min=10.5 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.5 | grep _aaa | grep " Foo" | ${FAIL_IF_EMPTY} + nm -m main-10.5 | grep _bbb | grep " Bar" | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main-10.5a -framework Bar -framework Foo -mmacosx-version-min=10.5 \ + -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks + nm -m main-10.5a | grep _aaa | grep " Foo" | ${FAIL_IF_EMPTY} + nm -m main-10.5a | grep _bbb | grep " Bar" | ${FAIL_IF_EMPTY} + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a diff --git a/ld64/unit-tests/test-cases/symbol-moving/aaa.c b/ld64/unit-tests/test-cases/symbol-moving/aaa.c new file mode 100644 index 0000000..71123eb --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/aaa.c @@ -0,0 +1,3 @@ + +void aaa() {} + diff --git a/ld64/unit-tests/test-cases/symbol-moving/anotb.c b/ld64/unit-tests/test-cases/symbol-moving/anotb.c new file mode 100644 index 0000000..60fcf64 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/anotb.c @@ -0,0 +1,26 @@ + + +#define SYMBOL_IS_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_IS_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.5$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.5$_" #sym ); const char sym##_tmp = 0; + + +// 10.4 10.5 +// aaa libbar libfoo +// bbb libfoo libbar +// + +// bbb is new here in 10.5. It was elsewhere in 10.4 +SYMBOL_NOT_HERE_IN_10_4(bbb) + +// aaa was here in 10.4 and move elsewhere +SYMBOL_IS_HERE_IN_10_4(aaa) + diff --git a/ld64/unit-tests/test-cases/symbol-moving/bar.c b/ld64/unit-tests/test-cases/symbol-moving/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/unit-tests/test-cases/symbol-moving/bbb.c b/ld64/unit-tests/test-cases/symbol-moving/bbb.c new file mode 100644 index 0000000..b6e9cc0 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/bbb.c @@ -0,0 +1 @@ +void bbb() {} diff --git a/ld64/unit-tests/test-cases/symbol-moving/bnota.c b/ld64/unit-tests/test-cases/symbol-moving/bnota.c new file mode 100644 index 0000000..d29b878 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/bnota.c @@ -0,0 +1,25 @@ +#define SYMBOL_IS_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_IS_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$add$os10.5$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_4(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.4$_" #sym ); const char sym##_tmp = 0; + +#define SYMBOL_NOT_HERE_IN_10_5(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.5$_" #sym ); const char sym##_tmp = 0; + + +// 10.4 10.5 +// aaa libbar libfoo +// bbb libfoo libbar +// + + +// bbb was here in 10.4 and move elsewhere +SYMBOL_IS_HERE_IN_10_4(bbb) + +// aaa is new here in 10.5. It was elsewhere in 10.4 +SYMBOL_NOT_HERE_IN_10_4(aaa) + diff --git a/ld64/unit-tests/test-cases/symbol-moving/foo.c b/ld64/unit-tests/test-cases/symbol-moving/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/ld64/unit-tests/test-cases/symbol-moving/main.c b/ld64/unit-tests/test-cases/symbol-moving/main.c new file mode 100644 index 0000000..902e908 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-moving/main.c @@ -0,0 +1,17 @@ + +extern void foo(); +extern void bar(); + +extern void aaa(); +extern void bbb(); + + +int main() +{ + foo(); + bar(); + aaa(); + bbb(); + + return 0; +} diff --git a/ld64/unit-tests/test-cases/tentative-and-archive/Makefile b/ld64/unit-tests/test-cases/tentative-and-archive/Makefile new file mode 100644 index 0000000..e8a4a0e --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Test how tentative definitions interact with archives +# main.c has a tenative definition for _var which +# should *not* cause libfoo.a(foo.o) to be loaded. +# +# ld crashes building XsanFS +# -undefined dynamic_lookup causes spurious extra symbols +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${CC} ${CCFLAGS} main.c libfoo.a -o main + ${CC} ${CCFLAGS} main.c libfoo.a -o main -undefined dynamic_lookup + nm -m main | grep "looked up" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main libfoo.a foo.o diff --git a/ld64/unit-tests/test-cases/tentative-and-archive/foo.c b/ld64/unit-tests/test-cases/tentative-and-archive/foo.c new file mode 100644 index 0000000..a2254b5 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive/foo.c @@ -0,0 +1,6 @@ + +extern void bar(); + +void foo() { bar(); } + +int var = 9; diff --git a/ld64/unit-tests/test-cases/tentative-and-archive/main.c b/ld64/unit-tests/test-cases/tentative-and-archive/main.c new file mode 100644 index 0000000..e5046a0 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive/main.c @@ -0,0 +1,8 @@ + +int var; + +int main() +{ + var = 3; + return 0; +} diff --git a/ld64/unit-tests/test-cases/tentative-and-dylib/Makefile b/ld64/unit-tests/test-cases/tentative-and-dylib/Makefile new file mode 100644 index 0000000..0ed1fa4 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-dylib/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Test how tentative definitions interact with dylibs +# + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-reexport_library,libbar.dylib + # verify -warn_commons works + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-warn_commons 2> warnings.log + grep "using common symbol" warnings.log | ${FAIL_IF_EMPTY} + # verify -commons use_dylibs works + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,use_dylibs + nm -m main | grep _var | grep libfoo | ${FAIL_IF_EMPTY} + # verify -commons ignore_dylibs works + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,ignore_dylibs + nm -m main | grep _var | grep __DATA | ${FAIL_IF_EMPTY} + # verify -commons error works + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,error 2> warnings.log + # verify -commons use_dylibs works with indirect dylibs + ${CC} ${CCFLAGS} main.c -Dvar=bar libfoo.dylib -o main -Wl,-commons,use_dylibs + nm -m main | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main libfoo.dylib libbar.dylib warnings.log diff --git a/ld64/unit-tests/test-cases/tentative-and-dylib/bar.c b/ld64/unit-tests/test-cases/tentative-and-dylib/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-dylib/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/unit-tests/test-cases/tentative-and-dylib/foo.c b/ld64/unit-tests/test-cases/tentative-and-dylib/foo.c new file mode 100644 index 0000000..c1bb919 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-dylib/foo.c @@ -0,0 +1,2 @@ +void foo() {} +int var = 9; diff --git a/ld64/unit-tests/test-cases/tentative-and-dylib/main.c b/ld64/unit-tests/test-cases/tentative-and-dylib/main.c new file mode 100644 index 0000000..e5046a0 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-dylib/main.c @@ -0,0 +1,8 @@ + +int var; + +int main() +{ + var = 3; + return 0; +} diff --git a/ld64/unit-tests/test-cases/tentative-to-real-hidden/Makefile b/ld64/unit-tests/test-cases/tentative-to-real-hidden/Makefile new file mode 100644 index 0000000..833d676 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real-hidden/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify that -r -d -exported_symbol_list uses proper relocations for hidden +# newly defined (no longer tentative) definitions. +# + +ifneq (${ARCH},x86_64) + BETTER_NOT_FIND = _tent +else + # x86_64 uses a different style of relocations, so external relocs are ok to have + BETTER_NOT_FIND = blahblah +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${FAIL_IF_BAD_OBJ} test.o + + ${LD} -arch ${ARCH} -d -r test.o -exported_symbol _tent1 -o test-r.o + otool -rv test-r.o | grep ${BETTER_NOT_FIND} | ${PASS_IFF_EMPTY} + + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/tentative-to-real-hidden/test.c b/ld64/unit-tests/test-cases/tentative-to-real-hidden/test.c new file mode 100644 index 0000000..c9cf479 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real-hidden/test.c @@ -0,0 +1,11 @@ + +// tentative definitions +int tent1; +int tent2; +int __attribute__((visibility("hidden"))) tent3; + +// initialized to point to tentative definitions +int* pa = &tent1; +int* pb = &tent2; +int* pc = &tent3; + diff --git a/ld64/unit-tests/test-cases/tentative-to-real/Makefile b/ld64/unit-tests/test-cases/tentative-to-real/Makefile new file mode 100644 index 0000000..092aa2e --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The point of this test is to verify that -r -d +# will transform a tentative definition into a real one. +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${FAIL_IF_BAD_OBJ} test.${ARCH}.o + + ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r.${ARCH}.o | grep tentative | ${FAIL_IF_EMPTY} + + ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r-d.${ARCH}.o | grep tentative | ${PASS_IFF_EMPTY} + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/tentative-to-real/comment.txt b/ld64/unit-tests/test-cases/tentative-to-real/comment.txt new file mode 100644 index 0000000..de80bea --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real/comment.txt @@ -0,0 +1 @@ +The point of this test is to verify that -r -d will transform a tentative definition into a real one. diff --git a/ld64/unit-tests/test-cases/tentative-to-real/test.c b/ld64/unit-tests/test-cases/tentative-to-real/test.c new file mode 100644 index 0000000..87360fc --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real/test.c @@ -0,0 +1,3 @@ + +// a tentative definition +int a; diff --git a/ld64/unit-tests/test-cases/thumb-blx/Makefile b/ld64/unit-tests/test-cases/thumb-blx/Makefile new file mode 100644 index 0000000..65b3131 --- /dev/null +++ b/ld64/unit-tests/test-cases/thumb-blx/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify the linker parses call sites correctly. +# The tricky case is thumb, which uses a blx to call to +# the arm stubs. This test verifies that there is no +# +2 error by checking for "plus" and that when the file +# is regenerated through ld -r that the dumped output +# remains unchanged. +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.o > test.o.dump + # verify no +2 errors + grep "plus" test.o.dump | ${FAIL_IF_STDIN} + # verify .o file can be regenerated to an equivalent state + ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.o > test-r.o.dump + # verify final linked image has no +2 errors + ${CC} ${CCFLAGS} test.o -o test + otool -tV -p _main test | grep blx | grep -v _malloc | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} test-r.o -o test-r + otool -tV -p _main test-r | grep blx | grep -v _malloc | ${FAIL_IF_STDIN} + ${PASS_IFF} diff test.o.dump test-r.o.dump + +clean: + rm -rf test.o test-r.o test.o.dump test-r.o.dump test test-r diff --git a/ld64/unit-tests/test-cases/thumb-blx/test.c b/ld64/unit-tests/test-cases/thumb-blx/test.c new file mode 100644 index 0000000..ce0359f --- /dev/null +++ b/ld64/unit-tests/test-cases/thumb-blx/test.c @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +int main() +{ + malloc(1); + malloc(2); + malloc(3); + malloc(4); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile new file mode 100644 index 0000000..eebcc37 --- /dev/null +++ b/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -U and -undefined dynamic_lookup work +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup + nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} main.c -o main -Wl,-U,_foo + nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} main.c -o main -flat_namespace -Wl,-U,_foo + nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm main diff --git a/ld64/unit-tests/test-cases/undefined-dynamic-lookup/main.c b/ld64/unit-tests/test-cases/undefined-dynamic-lookup/main.c new file mode 100644 index 0000000..5de972f --- /dev/null +++ b/ld64/unit-tests/test-cases/undefined-dynamic-lookup/main.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile new file mode 100644 index 0000000..789a304 --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a late loaded hidden symbol from an archive does not conflict +# with a symbol previously found in a dylib. +# gcc 4.2: DejaGnu failures due to libgcc visibility issues with -m64 -mmacosx-version-min=10.4 (G5) +# + +SHELL = bash # use bash shell so we can redirect just stderr + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -c bar.c -o bar.o + libtool -static bar.o -o libbar.a + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib libbar.a 2>warning.log + cat warning.log | ${PASS_IFF_EMPTY} + + +clean: + rm -f libfoo.dylib bar.o libbar.a main warning.log diff --git a/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c new file mode 100644 index 0000000..276f502 --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c @@ -0,0 +1,11 @@ + + +void __attribute__((weak,visibility("hidden"))) foo() +{ + +} + + +void bar() +{ +} diff --git a/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c new file mode 100644 index 0000000..3d8616a --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c @@ -0,0 +1,5 @@ + + +void __attribute__((weak)) foo() +{ +} diff --git a/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c new file mode 100644 index 0000000..8c9c95c --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); + + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/visibility-warning/Makefile b/ld64/unit-tests/test-cases/visibility-warning/Makefile new file mode 100644 index 0000000..43bdcd8 --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that two weak symbols with different visibility causes a warning +# and a weak and strong with different visibilities do not cause a warning. +# Spurious link warnings for inline members of C++ template classes +# + +SHELL = bash # use bash shell so we can redirect just stderr + + +run: all + +all: + ${CC} ${CCFLAGS} -c foo_weak_hidden.c -o foo_weak_hidden.o + ${CC} ${CCFLAGS} -c foo_weak.c -o foo_weak.o + ${CC} ${CCFLAGS} -c foo.c -o foo.o + ${CC} ${CCFLAGS} -c foo_hidden.c -o foo_hidden.o + # weak default and weak hidden should warn + ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib 2> warnings.log + grep visibility warnings.log | ${FAIL_IF_EMPTY} + # weak hidden and strong should not warn + ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib 2> warnings.log + grep visibility warnings.log | ${FAIL_IF_STDIN} + # weak default and strong hidden should not warn + ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib 2> warnings.log + grep visibility warnings.log | ${FAIL_IF_STDIN} + # weak default and weak hidden but -w should not warn + ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -w -o libfoo.dylib 2> warnings.log + cat warnings.log | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib foo_weak_hidden.o foo_weak.o foo.o foo_hidden.o warnings.log diff --git a/ld64/unit-tests/test-cases/visibility-warning/foo.c b/ld64/unit-tests/test-cases/visibility-warning/foo.c new file mode 100644 index 0000000..1624757 --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/ld64/unit-tests/test-cases/visibility-warning/foo_hidden.c b/ld64/unit-tests/test-cases/visibility-warning/foo_hidden.c new file mode 100644 index 0000000..cac53ce --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning/foo_hidden.c @@ -0,0 +1,5 @@ + + +void __attribute__((visibility("hidden"))) foo() +{ +} diff --git a/ld64/unit-tests/test-cases/visibility-warning/foo_weak.c b/ld64/unit-tests/test-cases/visibility-warning/foo_weak.c new file mode 100644 index 0000000..3d8616a --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning/foo_weak.c @@ -0,0 +1,5 @@ + + +void __attribute__((weak)) foo() +{ +} diff --git a/ld64/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c b/ld64/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c new file mode 100644 index 0000000..8d461e6 --- /dev/null +++ b/ld64/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c @@ -0,0 +1,5 @@ + + +void __attribute__((weak, visibility("hidden"))) foo() +{ +} diff --git a/ld64/unit-tests/test-cases/weak-def-ordinal/Makefile b/ld64/unit-tests/test-cases/weak-def-ordinal/Makefile new file mode 100644 index 0000000..222a82d --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-ordinal/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# libfoo.dylib has weak defintiion of _foo +# libbar.dylib has strong defintiion of _foo +# +# Tests that if you link against libfoo.dylib and libbar.dylib +# that the two-level-namespace ordinal is set to the non-weak definition +# +# ld should keep looking when it finds a weak definition in a dylib# +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib libbar.dylib + nm -m main | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} + nm -m main | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm libfoo.dylib libbar.dylib main diff --git a/ld64/unit-tests/test-cases/weak-def-ordinal/bar.c b/ld64/unit-tests/test-cases/weak-def-ordinal/bar.c new file mode 100644 index 0000000..ae61731 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-ordinal/bar.c @@ -0,0 +1,6 @@ + +int aaa() +{ + return 1; +} + diff --git a/ld64/unit-tests/test-cases/weak-def-ordinal/foo.c b/ld64/unit-tests/test-cases/weak-def-ordinal/foo.c new file mode 100644 index 0000000..d371654 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-ordinal/foo.c @@ -0,0 +1,11 @@ + +int __attribute__((weak)) aaa() +{ + return 0; +} + +int __attribute__((weak)) bbb() +{ + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak-def-ordinal/main.c b/ld64/unit-tests/test-cases/weak-def-ordinal/main.c new file mode 100644 index 0000000..74726fb --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-ordinal/main.c @@ -0,0 +1,35 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern void aaa(); +extern void bbb(); + +int main() +{ + aaa(); + bbb(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak_dylib/Makefile b/ld64/unit-tests/test-cases/weak_dylib/Makefile new file mode 100644 index 0000000..0a00a39 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_dylib/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 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 + otool -lv main | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/unit-tests/test-cases/weak_dylib/bar.c b/ld64/unit-tests/test-cases/weak_dylib/bar.c new file mode 100644 index 0000000..261a806 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_dylib/bar.c @@ -0,0 +1,9 @@ + + +#include "bar.h" + +void bar1() {} +void bar2() {} +void bar3() {} +void bar4() {} + diff --git a/ld64/unit-tests/test-cases/weak_dylib/bar.h b/ld64/unit-tests/test-cases/weak_dylib/bar.h new file mode 100644 index 0000000..7ea2ef3 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_dylib/bar.h @@ -0,0 +1,9 @@ + + +extern void bar1(); +extern void bar2() __attribute__((weak_import)); +extern void bar3(); +extern void bar4() __attribute__((weak_import)); + + + diff --git a/ld64/unit-tests/test-cases/weak_dylib/foo.c b/ld64/unit-tests/test-cases/weak_dylib/foo.c new file mode 100644 index 0000000..aa25da5 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_dylib/foo.c @@ -0,0 +1,9 @@ + + +#include "foo.h" + +void foo1() {} +void foo2() {} +void foo3() {} +void foo4() {} + diff --git a/ld64/unit-tests/test-cases/weak_dylib/foo.h b/ld64/unit-tests/test-cases/weak_dylib/foo.h new file mode 100644 index 0000000..54b3e36 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_dylib/foo.h @@ -0,0 +1,6 @@ + + +extern void foo1(); +extern void foo2() __attribute__((weak_import)); +extern void foo3(); +extern void foo4() __attribute__((weak_import)); diff --git a/ld64/unit-tests/test-cases/weak_dylib/main.c b/ld64/unit-tests/test-cases/weak_dylib/main.c new file mode 100644 index 0000000..cb61aeb --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_dylib/main.c @@ -0,0 +1,22 @@ + +#include "foo.h" +#include "bar.h" + +void* p; + +int main (void) +{ + // non-lazy reference to foo2 + p = &foo2; + // lazy reference to foo4 + foo4(); + + // non-lazy reference to bar2 + p = &bar2; + // lazy reference to bar4 and bar1 + bar4(); + bar1(); + + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak_import/Makefile b/ld64/unit-tests/test-cases/weak_import/Makefile new file mode 100644 index 0000000..d1fa1f3 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import/Makefile @@ -0,0 +1,62 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib + ${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib + + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null + otool -rv main-${ARCH} | grep _data6 > /dev/null + ${FAIL_IF_BAD_MACHO} main-${ARCH} + + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null + otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null + ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib + +clean: + rm -rf *.dylib main-* diff --git a/ld64/unit-tests/test-cases/weak_import/foo.c b/ld64/unit-tests/test-cases/weak_import/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/ld64/unit-tests/test-cases/weak_import/foo.h b/ld64/unit-tests/test-cases/weak_import/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/unit-tests/test-cases/weak_import/main.c b/ld64/unit-tests/test-cases/weak_import/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/ld64/unit-tests/test-cases/weak_import2/Makefile.newtest b/ld64/unit-tests/test-cases/weak_import2/Makefile.newtest new file mode 100644 index 0000000..5e51b89 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import2/Makefile.newtest @@ -0,0 +1,58 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null + nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null + ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib + +clean: + rm -rf *.dylib main-* *.o diff --git a/ld64/unit-tests/test-cases/weak_import2/comment.txt b/ld64/unit-tests/test-cases/weak_import2/comment.txt new file mode 100644 index 0000000..5be42d8 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import2/comment.txt @@ -0,0 +1 @@ +Test the weak_import attribute works diff --git a/ld64/unit-tests/test-cases/weak_import2/foo.c b/ld64/unit-tests/test-cases/weak_import2/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import2/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/ld64/unit-tests/test-cases/weak_import2/foo.h b/ld64/unit-tests/test-cases/weak_import2/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import2/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/unit-tests/test-cases/weak_import2/foo1.c b/ld64/unit-tests/test-cases/weak_import2/foo1.c new file mode 100644 index 0000000..4580a87 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import2/foo1.c @@ -0,0 +1,10 @@ + + +void func2() {} +void func4() {} + + +int data2 = 0; // foo.c also has weak_import initialized +int data4; // foo.c also has weak_import uninitialized +int data6 = 0; // foo.c also has weak_import + diff --git a/ld64/unit-tests/test-cases/weak_import2/main.c b/ld64/unit-tests/test-cases/weak_import2/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import2/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/ld64/unit-tests/test-cases/weak_import3/Makefile b/ld64/unit-tests/test-cases/weak_import3/Makefile new file mode 100644 index 0000000..98a2779 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import3/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + + ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo1-${ARCH}.o + + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib 2>/dev/null + +clean: + rm -rf *.o *.dylib main-* diff --git a/ld64/unit-tests/test-cases/weak_import3/comment.txt b/ld64/unit-tests/test-cases/weak_import3/comment.txt new file mode 100644 index 0000000..5be42d8 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import3/comment.txt @@ -0,0 +1 @@ +Test the weak_import attribute works diff --git a/ld64/unit-tests/test-cases/weak_import3/foo.c b/ld64/unit-tests/test-cases/weak_import3/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import3/foo.c @@ -0,0 +1,17 @@ + + +#include "foo.h" + +void func1() {} +void func2() {} +void func3() {} +void func4() {} + + +int data1 = 0; +int data2 = 0; // weak_import initialized +int data3; +int data4; // weak_import uninitialized +int data5 = 0; +int data6 = 0; // weak_import + diff --git a/ld64/unit-tests/test-cases/weak_import3/foo.h b/ld64/unit-tests/test-cases/weak_import3/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import3/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/unit-tests/test-cases/weak_import3/foo1.c b/ld64/unit-tests/test-cases/weak_import3/foo1.c new file mode 100644 index 0000000..392a5b7 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import3/foo1.c @@ -0,0 +1,4 @@ +#include "foo.h" + +int data4; // foo.c also has weak_import uninitialized + diff --git a/ld64/unit-tests/test-cases/weak_import3/main.c b/ld64/unit-tests/test-cases/weak_import3/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import3/main.c @@ -0,0 +1,20 @@ + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main (void) +{ + // make non-lazy reference to func3 and func4 + if ( &func3 == &func4 ) { + // make lazy reference to func3 and func4 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + diff --git a/ld64/unit-tests/test-cases/why_live/Makefile b/ld64/unit-tests/test-cases/why_live/Makefile new file mode 100644 index 0000000..9c4811e --- /dev/null +++ b/ld64/unit-tests/test-cases/why_live/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Text -why_live option with dead code stripping +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c bar.c -o main -dead_strip -Wl,-why_live,_bar 2>call_chains + grep _bar call_chains | ${FAIL_IF_EMPTY} + grep _foo call_chains | ${FAIL_IF_EMPTY} + grep _main call_chains | ${FAIL_IF_EMPTY} + grep _frob call_chains | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main call_chains diff --git a/ld64/unit-tests/test-cases/why_live/bar.c b/ld64/unit-tests/test-cases/why_live/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/unit-tests/test-cases/why_live/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/unit-tests/test-cases/why_live/foo.c b/ld64/unit-tests/test-cases/why_live/foo.c new file mode 100644 index 0000000..9a2edf5 --- /dev/null +++ b/ld64/unit-tests/test-cases/why_live/foo.c @@ -0,0 +1,12 @@ + +extern void bar(); + +void foo() +{ + bar(); +} + +void frob() +{ + bar(); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/why_live/main.c b/ld64/unit-tests/test-cases/why_live/main.c new file mode 100644 index 0000000..a5a79d5 --- /dev/null +++ b/ld64/unit-tests/test-cases/why_live/main.c @@ -0,0 +1,7 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/zero-fill/Makefile b/ld64/unit-tests/test-cases/zero-fill/Makefile new file mode 100644 index 0000000..d6c0639 --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -o test-${ARCH} + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm -rf test-* diff --git a/ld64/unit-tests/test-cases/zero-fill/test.c b/ld64/unit-tests/test-cases/zero-fill/test.c new file mode 100644 index 0000000..cfdc08c --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill/test.c @@ -0,0 +1,51 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data +int bigarray1[256]; +int bigarray2[256]; +int bigarray3[256]; +int bigarray4[256]; +int bigarray5[256]; +int bigarray6[256]; +static int staticbigarray1[256]; +static int staticbigarray2[256]; +static int staticbigarray3[256]; +static int staticbigarray4[256]; +static int staticbigarray5[256]; +static int staticbigarray6[256]; + +int main() +{ + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} + diff --git a/ld64/unit-tests/test-cases/zero-fill2/Makefile b/ld64/unit-tests/test-cases/zero-fill2/Makefile new file mode 100644 index 0000000..b011e70 --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill2/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -o test + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test diff --git a/ld64/unit-tests/test-cases/zero-fill2/comment.txt b/ld64/unit-tests/test-cases/zero-fill2/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill2/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/zero-fill2/test.c b/ld64/unit-tests/test-cases/zero-fill2/test.c new file mode 100644 index 0000000..219cdc2 --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill2/test.c @@ -0,0 +1,58 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data + +#if __LP64__ + #define BOOST 100UL +#else + #define BOOST 1 +#endif + +int bigarray1[256]; +int bigarray2[2560]; +int bigarray3[25600]; +int bigarray4[256000]; +int bigarray5[2560000]; +int bigarray6[256000000*BOOST]; +static int staticbigarray1[256]; +static int staticbigarray2[2560]; +static int staticbigarray3[25600]; +static int staticbigarray4[256000]; +static int staticbigarray5[2560000]; +static int staticbigarray6[25600000*BOOST]; + +int main() +{ + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} + diff --git a/ld64/unit-tests/test-cases/zero-fill3/Makefile b/ld64/unit-tests/test-cases/zero-fill3/Makefile new file mode 100644 index 0000000..6266019 --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill3/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a program with a large zero-fill section +# + +run: test-run-${ARCH} + +# i386 catches the problem in the assembler phase +test-run-i386: + ${PASS_IFF} true + +test-run-ppc test-run-ppc64: test-${ARCH} + +test-run-x86_64 test-ppc64: + ${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: + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null + +clean: + rm -rf test-* *.o *.s *.i diff --git a/ld64/unit-tests/test-cases/zero-fill3/comment.txt b/ld64/unit-tests/test-cases/zero-fill3/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill3/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/zero-fill3/test.c b/ld64/unit-tests/test-cases/zero-fill3/test.c new file mode 100644 index 0000000..64ac72a --- /dev/null +++ b/ld64/unit-tests/test-cases/zero-fill3/test.c @@ -0,0 +1,63 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// if we used one big array, the linker would page align it +// but we want to test a non-page align big chunk of zero-fill data +int bigarray1[256]; +int bigarray2[2560]; +int bigarray3[25600]; +int bigarray4[256000]; +int bigarray5[2560000]; +int bigarray7[16777216L+1]; +int bigarray8[2*16777216L+1]; +int bigarray9[4*16777216L+1]; +int bigarray10[8*16777216L+1]; +int bigarray11[16*16777216L+1]; +int bigarray99[2147483647U/SHRINK]; +static int staticbigarray1[256]; +static int staticbigarray2[2560]; +static int staticbigarray3[25600]; +static int staticbigarray4[256000]; +static int staticbigarray5[2560000]; +static int staticbigarray6[25600000]; +//static int staticbigarray99[2147483647U/SHRINK]; + +int main() +{ + bigarray5[10] = 4; + bigarray7[10] = 4; + bigarray8[10] = 4; + bigarray9[10] = 4; + bigarray10[10] = 4; + bigarray11[10] = 4; + bigarray99[10] = 4; + staticbigarray1[10] = 4; + staticbigarray2[10] = 4; + staticbigarray3[10] = 4; + staticbigarray4[10] = 4; + staticbigarray5[10] = 4; + staticbigarray6[10] = 4; + return 0; +} From 2ed6f2bd56fe832fba9c4c5ee7f611f27d26dd3e Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:30:43 +0100 Subject: [PATCH 02/48] 95-2-12 --- ld64/ChangeLog | 4672 ++++++++- ld64/FireOpal/APPLE_LICENSE | 367 - ld64/FireOpal/ChangeLog | 542 -- ld64/FireOpal/doc/man/man1/ld.1 | 676 -- ld64/FireOpal/doc/man/man1/ld64.1 | 1 - ld64/FireOpal/doc/man/man1/rebase.1 | 39 - ld64/FireOpal/ld64.xcodeproj/project.pbxproj | 788 -- ld64/FireOpal/src/ArchiveReader.hpp | 454 - ld64/FireOpal/src/ExecutableFile.h | 70 - ld64/FireOpal/src/LTOReader.hpp | 684 -- ld64/FireOpal/src/MachOFileAbstraction.hpp | 925 -- ld64/FireOpal/src/MachOReaderDylib.hpp | 926 -- ld64/FireOpal/src/ObjectDump.cpp | 497 - ld64/FireOpal/src/ObjectFile.h | 349 - ld64/FireOpal/src/Options.cpp | 3150 ------ ld64/FireOpal/src/debugline.c | 546 -- ld64/FireOpal/src/ld.cpp | 3778 -------- ld64/FireOpal/src/machochecker.cpp | 965 -- ld64/FireOpal/unit-tests/README | 28 - .../unit-tests/bin/exit-non-zero-pass.pl | 27 - .../unit-tests/bin/fail-if-exit-non-zero.pl | 16 - .../unit-tests/bin/fail-if-exit-zero.pl | 22 - .../unit-tests/bin/fail-if-no-stdin.pl | 22 - ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl | 22 - .../unit-tests/bin/fail-iff-exit-zero.pl | 29 - .../unit-tests/bin/make-recursive-newtest.pl | 127 - .../FireOpal/unit-tests/bin/make-recursive.pl | 123 - ld64/FireOpal/unit-tests/bin/mkld | 73 - .../unit-tests/bin/pass-iff-exit-non-zero.pl | 29 - .../unit-tests/bin/pass-iff-exit-zero.pl | 23 - .../unit-tests/bin/pass-iff-no-stdin.pl | 23 - .../FireOpal/unit-tests/bin/pass-iff-stdin.pl | 24 - ld64/FireOpal/unit-tests/bin/result-filter.pl | 131 - .../unit-tests/bin/rm-stale-test-logs | 36 - ld64/FireOpal/unit-tests/clean-tests | 63 - .../unit-tests/include/common.makefile | 76 - ld64/FireOpal/unit-tests/include/test.h | 35 - ld64/FireOpal/unit-tests/proctor-run | 204 - ld64/FireOpal/unit-tests/run-all-unit-tests | 35 - .../unit-tests/run-all-unit-tests-debug | 26 - ld64/FireOpal/unit-tests/src/Makefile | 9 - .../unit-tests/src/results-to-xml.cpp | 260 - .../unit-tests/src/xmlparser/xmlparser.1 | 79 - .../unit-tests/src/xmlparser/xmlparser.m | 25 - .../xmlparser.xcodeproj/project.pbxproj | 218 - .../src/xmlparser/xmlparser_Prefix.pch | 7 - .../test-cases/16-byte-alignment/Makefile | 44 - .../test-cases/16-byte-alignment/comment.txt | 1 - .../test-cases/16-byte-alignment/tl_test2.c | 43 - .../test-cases/absolute-symbol/Makefile | 40 - .../test-cases/absolute-symbol/abs.s | 3 - .../test-cases/absolute-symbol/main.c | 5 - .../test-cases/alias-command-line/Makefile | 53 - .../test-cases/alias-command-line/aliases.s | 45 - .../test-cases/alias-command-line/aliases.txt | 6 - .../test-cases/alias-objects/Makefile | 44 - .../test-cases/alias-objects/aliases.s | 43 - .../test-cases/align-modulus/Makefile | 40 - .../test-cases/align-modulus/align.s | 36 - .../test-cases/align-modulus/comment.txt | 2 - .../unit-tests/test-cases/align-modulus/foo.c | 32 - .../test-cases/align-modulus/foo.exp | 1 - .../test-cases/allow-stack-execute/Makefile | 46 - .../allow-stack-execute/comment.txt | 1 - .../test-cases/allow-stack-execute/foo.c | 4 - .../test-cases/allowable-client/Makefile | 110 - .../test-cases/allowable-client/bar.c | 6 - .../test-cases/allowable-client/baz.c | 6 - .../test-cases/allowable-client/comment.txt | 1 - .../test-cases/allowable-client/main.c | 6 - .../test-cases/archive-ObjC/Makefile | 49 - .../unit-tests/test-cases/archive-ObjC/bar.c | 2 - .../unit-tests/test-cases/archive-ObjC/baz.m | 8 - .../unit-tests/test-cases/archive-ObjC/foo.m | 8 - .../unit-tests/test-cases/archive-ObjC/main.c | 31 - .../test-cases/archive-basic/Makefile | 46 - .../unit-tests/test-cases/archive-basic/bar.c | 1 - .../test-cases/archive-basic/comment.txt | 1 - .../unit-tests/test-cases/archive-basic/foo.c | 1 - .../test-cases/archive-basic/main.c | 32 - .../test-cases/archive-duplicate/Makefile | 45 - .../test-cases/archive-duplicate/bar.c | 1 - .../test-cases/archive-duplicate/foo.c | 1 - .../test-cases/archive-duplicate/main.c | 32 - .../test-cases/archive-weak/Makefile | 51 - .../unit-tests/test-cases/archive-weak/bar.c | 1 - .../unit-tests/test-cases/archive-weak/baz.c | 11 - .../test-cases/archive-weak/comment.txt | 7 - .../unit-tests/test-cases/archive-weak/foo.c | 13 - .../unit-tests/test-cases/archive-weak/main.c | 42 - .../unit-tests/test-cases/auto-arch/Makefile | 40 - .../unit-tests/test-cases/auto-arch/hello.c | 29 - .../test-cases/blank-stubs/Makefile | 61 - .../test-cases/blank-stubs/comment.txt | 1 - .../unit-tests/test-cases/blank-stubs/main.c | 5 - .../test-cases/branch-islands/extra.c | 8 - .../test-cases/branch-islands/hello.c | 10 - .../test-cases/branch-islands/space.s | 39 - .../test-cases/bundle_loader/Makefile | 55 - .../unit-tests/test-cases/bundle_loader/bar.c | 31 - .../test-cases/bundle_loader/bundle.c | 31 - .../test-cases/bundle_loader/main.c | 30 - .../test-cases/cfstring-coalesce/Makefile | 52 - .../test-cases/cfstring-coalesce/bar.c | 7 - .../test-cases/cfstring-coalesce/foo.c | 19 - .../test-cases/cfstring-utf16/bar.m | 7 - .../test-cases/cfstring-utf16/foo.m | 20 - .../test-cases/commons-alignment/Makefile | 37 - .../test-cases/commons-alignment/foo.s | 2 - .../commons-coalesced-dead_strip/Makefile | 42 - .../commons-coalesced-dead_strip/a.c | 4 - .../commons-coalesced-dead_strip/b.c | 4 - .../commons-coalesced-dead_strip/c.c | 3 - .../commons-coalesced-dead_strip/c.h | 4 - .../test-cases/commons-mixed/Makefile | 46 - .../unit-tests/test-cases/commons-mixed/bar.c | 2 - .../unit-tests/test-cases/commons-mixed/foo.c | 2 - .../unit-tests/test-cases/commons-order/bar.c | 3 - .../unit-tests/test-cases/commons-order/baz.c | 3 - .../test-cases/commons-order/expected.order | 8 - .../unit-tests/test-cases/commons-order/foo.c | 3 - .../cpu-sub-types-preference/Makefile | 96 - .../test-cases/cpu-sub-types-preference/foo.c | 25 - .../test-cases/cpu-sub-types/Makefile | 157 - .../test-cases/cpu-sub-types/comment.txt | 2 - .../unit-tests/test-cases/cpu-sub-types/foo.c | 3 - .../test-cases/cpu-sub-types/main.c | 10 - .../dead_strip-archive-global/Makefile | 43 - .../dead_strip-archive-global/foo.c | 12 - .../test-cases/dead_strip-archive/Makefile | 43 - .../test-cases/dead_strip-archive/comment.txt | 1 - .../test-cases/dead_strip-archive/foo.c | 7 - .../test-cases/dead_strip-archive/main.c | 37 - .../test-cases/dead_strip-init-archive/bar.c | 4 - .../unit-tests/test-cases/dead_strip/Makefile | 49 - .../test-cases/dead_strip/comment.txt | 5 - .../test-cases/dead_strip/deadwood.c | 11 - .../unit-tests/test-cases/dead_strip/main.c | 32 - .../unit-tests/test-cases/dead_strip/main.exp | 1 - .../test-cases/dead_strip_dylibs/foo.c | 4 - .../test-cases/dead_strip_dylibs/main.c | 10 - .../dead_strip_section_attribute/Makefile | 40 - .../dead_strip_section_attribute/comment.txt | 2 - .../dead_strip_section_attribute/main.c | 42 - .../dtrace-static-probes-coalescing/Makefile | 59 - .../dtrace-static-probes-coalescing/Number.d | 3 - .../dtrace-static-probes-coalescing/a.cxx | 8 - .../dtrace-static-probes-coalescing/header.h | 11 - .../dtrace-static-probes-coalescing/x.cxx | 6 - .../test-cases/dtrace-static-probes/Makefile | 60 - .../test-cases/dtrace-static-probes/bar.d | 7 - .../dtrace-static-probes/comment.txt | 1 - .../test-cases/dtrace-static-probes/foo.d | 8 - .../test-cases/dtrace-static-probes/main.c | 29 - .../dwarf-archive-all_load/Makefile | 45 - .../test-cases/dwarf-archive-all_load/bar.c | 2 - .../dwarf-archive-all_load/comment.txt | 2 - .../dwarf-archive-all_load/expected-stabs | 24 - .../dwarf-archive-all_load/stabs-filter.pl | 25 - .../test-cases/dwarf-debug-notes-r/Makefile | 59 - .../test-cases/dwarf-debug-notes-r/bar.cxx | 4 - .../dwarf-debug-notes-r/comment.txt | 5 - .../dwarf-debug-notes-r/expected-stabs | 24 - .../test-cases/dwarf-debug-notes-r/foo.cxx | 4 - .../test-cases/dwarf-debug-notes-r/main.cxx | 4 - .../dwarf-debug-notes-r/stabs-filter.pl | 25 - .../test-cases/dwarf-debug-notes/Makefile | 50 - .../test-cases/dwarf-debug-notes/comment.txt | 4 - .../dwarf-debug-notes/expected-stabs | 33 - .../test-cases/dwarf-debug-notes/header.h | 8 - .../test-cases/dwarf-debug-notes/hello.cxx | 33 - .../test-cases/dwarf-debug-notes/other.cxx | 27 - .../dwarf-debug-notes/stabs-filter.pl | 25 - .../test-cases/dwarf-ignore/Makefile | 39 - .../test-cases/dwarf-ignore/comment.txt | 1 - .../test-cases/dwarf-ignore/hello.c | 29 - .../test-cases/dwarf-strip/Makefile | 40 - .../test-cases/dwarf-strip/comment.txt | 1 - .../unit-tests/test-cases/dwarf-strip/hello.c | 29 - .../test-cases/dylib-aliases/Makefile | 47 - .../unit-tests/test-cases/dylib-aliases/bar.c | 1 - .../unit-tests/test-cases/dylib-aliases/foo.c | 1 - .../test-cases/dylib-aliases/main.c | 8 - .../test-cases/dylib-re-export-cycle/Makefile | 52 - .../test-cases/dylib-re-export-cycle/bar.c | 1 - .../test-cases/dylib-re-export-cycle/foo.c | 1 - .../test-cases/dylib-re-export-cycle/main.c | 6 - .../test-cases/dylib_file-missing/Makefile | 42 - .../test-cases/dylib_file-missing/bar.c | 13 - .../test-cases/dylib_file-missing/foo.c | 7 - .../test-cases/dylib_file-missing/main.c | 15 - .../unit-tests/test-cases/dylib_file/Makefile | 46 - .../unit-tests/test-cases/dylib_file/bar.c | 13 - .../test-cases/dylib_file/comment.txt | 1 - .../unit-tests/test-cases/dylib_file/foo.c | 7 - .../unit-tests/test-cases/dylib_file/main.c | 15 - .../unit-tests/test-cases/dylib_init/Makefile | 36 - .../unit-tests/test-cases/dylib_init/foo.c | 2 - .../test-cases/eh-coalescing-r/foo.cxx | 32 - .../test-cases/eh-coalescing-r/func.h | 35 - .../test-cases/eh-coalescing/Makefile | 50 - .../test-cases/eh-coalescing/bar.cxx | 31 - .../test-cases/eh-coalescing/foo.cxx | 33 - .../test-cases/eh-coalescing/foo2.cxx | 31 - .../test-cases/eh-strip-test/Makefile | 34 - .../test-cases/eh-strip-test/comment.txt | 21 - .../test-cases/eh-strip-test/main.cxx | 6 - .../unit-tests/test-cases/eh_frame/Makefile | 47 - .../unit-tests/test-cases/eh_frame/bar.cxx | 38 - .../unit-tests/test-cases/eh_frame/foo.cxx | 38 - .../test-cases/empty-object/Makefile | 40 - .../unit-tests/test-cases/empty-object/main.c | 1 - .../unit-tests/test-cases/end-label/bar.s | 7 - .../unit-tests/test-cases/end-label/foo.s | 11 - .../foo.c | 34 - .../exported-symbols-wildcards/Makefile | 78 - .../exported-symbols-wildcards/expect1 | 2 - .../exported-symbols-wildcards/expect2 | 3 - .../exported-symbols-wildcards/expect3 | 4 - .../exported-symbols-wildcards/expect4 | 6 - .../exported-symbols-wildcards/expect5 | 3 - .../exported-symbols-wildcards/expect6 | 4 - .../exported-symbols-wildcards/expect7 | 2 - .../exported-symbols-wildcards/expect8 | 3 - .../exported-symbols-wildcards/foo.c | 55 - .../exported-symbols-wildcards/list5 | 2 - .../exported_symbols_list-eol/Makefile | 41 - .../exported_symbols_list-eol/expected.nm | 2 - .../exported_symbols_list-eol/test.c | 18 - .../exported_symbols_list-eol/test.exp | 1 - .../exported_symbols_list-hidden/Makefile | 41 - .../exported_symbols_list-hidden/test.c | 18 - .../exported_symbols_list-hidden/test.exp | 4 - .../exported_symbols_list-r/Makefile | 55 - .../exported_symbols_list-r/test-bad.exp | 3 - .../test-cases/exported_symbols_list-r/test.c | 18 - .../exported_symbols_list-r/test.exp | 2 - .../external-reloc-sorting/Makefile | 40 - .../test-cases/external-reloc-sorting/foo.c | 5 - .../test-cases/external-reloc-sorting/main.c | 39 - .../unit-tests/test-cases/filelist/Makefile | 47 - .../test-cases/filelist/comment.txt | 1 - .../unit-tests/test-cases/filelist/hello.c | 29 - .../unit-tests/test-cases/flat-dylib/Makefile | 40 - .../unit-tests/test-cases/flat-dylib/main.c | 33 - .../flat-indirect-undefines/Makefile | 49 - .../test-cases/flat-indirect-undefines/bar.c | 4 - .../test-cases/flat-indirect-undefines/foo.c | 8 - .../unit-tests/test-cases/flat-main/Makefile | 40 - .../unit-tests/test-cases/flat-main/main.c | 33 - .../test-cases/got-elimination/Makefile | 50 - .../test-cases/got-elimination/bar.c | 28 - .../test-cases/got-elimination/foo.c | 42 - .../unit-tests/test-cases/header-pad/Makefile | 38 - .../test-cases/header-pad/comment.txt | 1 - .../unit-tests/test-cases/header-pad/hello.c | 29 - .../test-cases/hello-world/Makefile | 38 - .../test-cases/hello-world/comment.txt | 1 - .../unit-tests/test-cases/hello-world/hello.c | 29 - .../implicit-common2/Makefile.newtest | 47 - .../test-cases/implicit-common2/a.c | 7 - .../test-cases/implicit-common2/comment.txt | 1 - .../test-cases/implicit-common2/test.c | 26 - .../test-cases/implicit-common3/Makefile | 44 - .../test-cases/implicit-common3/a.c | 8 - .../test-cases/implicit-common3/comment.txt | 1 - .../test-cases/implicit-common3/test.c | 37 - .../implicit-common4/Makefile.newtest | 45 - .../test-cases/implicit-common4/a.c | 7 - .../test-cases/implicit-common4/comment.txt | 1 - .../test-cases/implicit-common4/test.c | 26 - .../implicit-common5/Makefile.newtest | 41 - .../test-cases/implicit-common5/a.c | 7 - .../test-cases/implicit-common5/comment.txt | 1 - .../test-cases/implicit-common5/test.c | 25 - .../test-cases/implicit_dylib/Makefile | 48 - .../test-cases/implicit_dylib/bar.c | 7 - .../test-cases/implicit_dylib/foo.c | 5 - .../test-cases/indirect-dylib/Makefile | 46 - .../test-cases/indirect-dylib/bar.c | 31 - .../test-cases/indirect-dylib/comment.txt | 4 - .../test-cases/indirect-dylib/foo.c | 31 - .../test-cases/indirect-dylib/main.c | 33 - .../test-cases/indirect-path-search/Makefile | 106 - .../test-cases/indirect-path-search/baz.c | 5 - .../test-cases/indirect-path-search/foo.c | 4 - .../test-cases/indirect-path-search/main.c | 8 - .../test-cases/interposable_list/Makefile | 47 - .../test-cases/interposable_list/test.c | 57 - .../test-cases/interposable_list/test.exp | 2 - .../unit-tests/test-cases/large-data/Makefile | 50 - .../unit-tests/test-cases/large-data/test1.c | 42 - .../unit-tests/test-cases/large-data/test2.c | 37 - .../unit-tests/test-cases/large-data/test3.c | 37 - .../unit-tests/test-cases/large-data/test4.c | 37 - .../test-cases/late-link-error/Makefile | 41 - .../test-cases/late-link-error/comment.txt | 2 - .../test-cases/late-link-error/link_error.s | 22 - .../test-cases/lazy-dylib-objc/foo.h | 9 - .../test-cases/lazy-dylib-objc/foo.m | 8 - .../test-cases/lazy-dylib-objc/main.m | 12 - .../unit-tests/test-cases/lazy-dylib/bad.c | 12 - .../unit-tests/test-cases/lazy-dylib/bad2.c | 13 - .../unit-tests/test-cases/lazy-dylib/foo.c | 5 - .../unit-tests/test-cases/lazy-dylib/main.c | 18 - .../literals-coalesce-alignment/Makefile | 46 - .../cstring-align-0.s | 26 - .../cstring-align-3.s | 26 - .../literals-coalesce-alignment2/Makefile | 47 - .../literals-coalesce-alignment2/comment.txt | 1 - .../cstring-align-0.s | 27 - .../cstring-align-3.s | 28 - .../literals-coalesce-alignment3/Makefile | 48 - .../literals-coalesce-alignment3/comment.txt | 1 - .../cstring-align-0.s | 27 - .../cstring-align-3.s | 28 - .../test-cases/literals-coalesce/Makefile | 40 - .../test-cases/literals-coalesce/literals.s | 69 - .../literals-coalesce2/Makefile.newtest | 40 - .../test-cases/literals-coalesce2/comment.txt | 1 - .../test-cases/literals-coalesce2/literals.s | 48 - .../test-cases/literals-coalesce2/test.sh | 5 - .../test-cases/llvm-integration/Makefile | 289 - .../test-cases/llvm-integration/a.c | 5 - .../test-cases/llvm-integration/a1.c | 10 - .../test-cases/llvm-integration/a10.c | 5 - .../test-cases/llvm-integration/a11.c | 6 - .../test-cases/llvm-integration/a12.c | 8 - .../test-cases/llvm-integration/a12.h | 8 - .../test-cases/llvm-integration/a13.cc | 3 - .../test-cases/llvm-integration/a13.h | 7 - .../test-cases/llvm-integration/a14.c | 1 - .../test-cases/llvm-integration/a15.c | 3 - .../test-cases/llvm-integration/a17.c | 4 - .../test-cases/llvm-integration/a18.c | 18 - .../test-cases/llvm-integration/a2.c | 6 - .../test-cases/llvm-integration/a20.c | 2 - .../test-cases/llvm-integration/a3.c | 6 - .../test-cases/llvm-integration/a4.c | 6 - .../test-cases/llvm-integration/a5.c | 10 - .../test-cases/llvm-integration/a6.c | 10 - .../test-cases/llvm-integration/a7.c | 11 - .../test-cases/llvm-integration/a8.c | 23 - .../test-cases/llvm-integration/a9.c | 25 - .../test-cases/llvm-integration/a9.list | 3 - .../test-cases/llvm-integration/b.c | 3 - .../test-cases/llvm-integration/b1.c | 4 - .../test-cases/llvm-integration/b10.c | 7 - .../test-cases/llvm-integration/b10.h | 6 - .../test-cases/llvm-integration/b14.c | 7 - .../test-cases/llvm-integration/b15.c | 8 - .../test-cases/llvm-integration/b17.c | 4 - .../test-cases/llvm-integration/b2.c | 9 - .../test-cases/llvm-integration/b20.c | 1 - .../test-cases/llvm-integration/b3.c | 4 - .../test-cases/llvm-integration/b4.c | 13 - .../test-cases/llvm-integration/b5.c | 4 - .../test-cases/llvm-integration/b7.c | 7 - .../test-cases/llvm-integration/c15.c | 9 - .../test-cases/llvm-integration/main.c | 9 - .../test-cases/llvm-integration/main1.c | 13 - .../test-cases/llvm-integration/main10.c | 10 - .../test-cases/llvm-integration/main11.c | 7 - .../test-cases/llvm-integration/main12.c | 7 - .../test-cases/llvm-integration/main13.cc | 8 - .../test-cases/llvm-integration/main16.c | 8 - .../test-cases/llvm-integration/main19.c | 8 - .../test-cases/llvm-integration/main2.c | 9 - .../test-cases/llvm-integration/main3.c | 13 - .../test-cases/llvm-integration/main4.c | 9 - .../test-cases/llvm-integration/main5.c | 16 - .../test-cases/llvm-integration/main6.c | 10 - .../test-cases/llvm-integration/main7.c | 10 - .../test-cases/llvm-integration/main8.c | 11 - .../test-cases/llvm-integration/main9.c | 14 - .../test-cases/loader_path/Makefile | 46 - .../unit-tests/test-cases/loader_path/bar.c | 6 - .../unit-tests/test-cases/loader_path/foo.c | 7 - .../local-symbol-partial-stripping/Makefile | 75 - .../local-symbol-partial-stripping/a.expect | 2 - .../local-symbol-partial-stripping/a.list | 2 - .../local-symbol-partial-stripping/b.expect | 3 - .../local-symbol-partial-stripping/b.list | 2 - .../local-symbol-partial-stripping/c.list | 1 - .../local-symbol-partial-stripping/foo.c | 11 - .../local-symbol-partial-stripping/main.c | 11 - .../test-cases/lto-weak-native-override/foo.c | 6 - .../test-cases/main-stripped/Makefile | 38 - .../test-cases/main-stripped/main.c | 34 - .../test-cases/main-stripped/main.exp | 1 - .../test-cases/missing-option-args/Makefile | 98 - .../missing-option-args/comment.txt | 1 - .../test-cases/multiple-entry-points/Makefile | 46 - .../multiple-entry-points/comment.txt | 3 - .../no-dynamic-common/Makefile.newtest | 39 - .../test-cases/no-dynamic-common/a.c | 7 - .../test-cases/no-dynamic-common/comment.txt | 1 - .../test-cases/no-dynamic-common/test.c | 25 - .../unit-tests/test-cases/no-uuid/Makefile | 63 - .../unit-tests/test-cases/no-uuid/bar.c | 4 - .../unit-tests/test-cases/no-uuid/comment.txt | 1 - .../unit-tests/test-cases/no-uuid/foo.c | 4 - .../unit-tests/test-cases/non-lazy-r/Makefile | 61 - .../unit-tests/test-cases/non-lazy-r/foo.c | 12 - .../unit-tests/test-cases/non-lazy-r/other.c | 2 - .../objc-exported_symbols_list/foo.exp | 1 - .../objc-exported_symbols_list/foo.m | 18 - .../test-cases/objc-gc-checks/Makefile | 84 - .../test-cases/objc-gc-checks/bar.m | 11 - .../test-cases/objc-gc-checks/comment.txt | 1 - .../test-cases/objc-gc-checks/foo.m | 12 - .../test-cases/objc-gc-checks/runtime.c | 2 - .../test-cases/objc-literal-pointers/test.m | 33 - .../test-cases/objc-references/Makefile | 47 - .../test-cases/objc-references/comment.txt | 1 - .../test-cases/objc-references/test.m | 52 - .../objc-selector-coalescing/main.m | 7 - .../objc-selector-coalescing/other.m | 10 - .../test-cases/operator-new/main.cxx | 47 - .../test-cases/order_file-ans/Makefile | 40 - .../test-cases/order_file-ans/main.cxx | 62 - .../test-cases/order_file-ans/main.expected | 4 - .../test-cases/order_file-ans/main.order | 4 - .../unit-tests/test-cases/order_file/Makefile | 57 - .../unit-tests/test-cases/order_file/extra.s | 24 - .../unit-tests/test-cases/order_file/main.c | 33 - .../test-cases/order_file/main1.expected | 4 - .../test-cases/order_file/main1.order | 4 - .../test-cases/order_file/main2.expected | 11 - .../test-cases/order_file/main2.order | 6 - .../test-cases/order_file/main3.expected | 4 - .../test-cases/order_file/main3.order | 8 - .../test-cases/prebound-main/main.c | 3 - .../test-cases/prebound-split-seg/Makefile | 39 - .../prebound-split-seg/address_table | 4 - .../test-cases/prebound-split-seg/bar.c | 36 - .../test-cases/private-non-lazy/bar.c | 3 - .../test-cases/private-non-lazy/comment.txt | 1 - .../test-cases/private-non-lazy/foo.c | 7 - .../test-cases/private-non-lazy/hello.c | 31 - .../test-cases/re-export-cases/Makefile | 167 - .../test-cases/re-export-cases/bar.c | 5 - .../test-cases/re-export-cases/baz.c | 5 - .../test-cases/re-export-cases/foo.c | 4 - .../test-cases/re-export-flag/Makefile | 48 - .../test-cases/re-export-flag/bar.c | 5 - .../test-cases/re-export-flag/foo.c | 4 - .../test-cases/re-export-optimizations/bar.c | 5 - .../test-cases/re-export-optimizations/foo.c | 4 - .../re-export-relative-paths/Makefile | 49 - .../test-cases/re-export-relative-paths/bar.c | 5 - .../test-cases/re-export-relative-paths/foo.c | 4 - .../re-export-relative-paths/main.c | 11 - .../re-export-relative-paths/wrap.c | 2 - .../test-cases/read-only-relocs/Makefile | 63 - .../test-cases/read-only-relocs/foo.c | 6 - .../test-cases/read-only-relocs/test.c | 34 - .../test-cases/rebase-basic/Makefile | 53 - .../unit-tests/test-cases/rebase-basic/bar.m | 13 - .../test-cases/rebase-basic/comment.txt | 1 - .../unit-tests/test-cases/rebase-basic/foo.c | 14 - .../unit-tests/test-cases/relocs-asm/Makefile | 46 - .../test-cases/relocs-asm/comment.txt | 3 - .../test-cases/relocs-asm/relocs-asm.s | 471 - .../unit-tests/test-cases/relocs-c/Makefile | 57 - .../unit-tests/test-cases/relocs-c/test.c | 76 - .../unit-tests/test-cases/relocs-c2/Makefile | 58 - .../test-cases/relocs-c2/comment.txt | 5 - .../unit-tests/test-cases/relocs-c2/test.c | 76 - .../test-cases/relocs-literals/Makefile | 47 - .../test-cases/relocs-literals/test.c | 54 - .../test-cases/relocs-literals2/Makefile | 50 - .../test-cases/relocs-literals2/test.c | 54 - .../test-cases/relocs-literals3/Makefile | 47 - .../test-cases/relocs-literals3/comment.txt | 3 - .../test-cases/relocs-literals3/test.c | 47 - .../test-cases/relocs-objc/Makefile | 47 - .../test-cases/relocs-objc/comment.txt | 3 - .../test-cases/segment-order/expected.order | 3 - .../test-cases/segment-order/main.c | 4 - .../test-cases/segment-order/segJJJ.s | 7 - .../test-cases/segment-order/segKKK.s | 7 - .../test-cases/segment-order/segLLL.s | 7 - .../test-cases/slow-x86-stubs/Makefile | 42 - .../test-cases/slow-x86-stubs/hello.c | 7 - .../test-cases/special-labels/extra.s | 9 - .../test-cases/special-labels/main.c | 29 - .../test-cases/stabs-coalesce/Makefile | 52 - .../test-cases/stabs-coalesce/comment.txt | 3 - .../test-cases/stabs-coalesce/header.h | 31 - .../test-cases/stabs-coalesce/hello.cxx | 33 - .../test-cases/stabs-coalesce/other.cxx | 41 - .../test-cases/stabs-directory-slash/Makefile | 39 - .../test-cases/stabs-directory-slash/main.c | 3 - .../stack_addr_no_size/Makefile.newtest | 77 - .../test-cases/stack_addr_no_size/comment.txt | 11 - .../test-cases/stack_addr_no_size/main.c | 29 - .../test-cases/stack_addr_size/Makefile | 57 - .../test-cases/stack_addr_size/comment.txt | 11 - .../test-cases/stack_addr_size/main.c | 29 - .../test-cases/stack_size_no_addr/Makefile | 56 - .../test-cases/stack_size_no_addr/comment.txt | 11 - .../test-cases/stack_size_no_addr/main.c | 37 - .../test-cases/static-executable/Makefile | 35 - .../test-cases/static-executable/test.c | 11 - .../test-cases/static-strip/Makefile.newtest | 40 - .../test-cases/static-strip/comment.txt | 1 - .../unit-tests/test-cases/static-strip/test.c | 28 - .../test-cases/strip-test2/Makefile | 70 - .../test-cases/strip-test2/comment.txt | 21 - .../test-cases/strip-test2/main.cxx | 6 - .../test-cases/strip-test3/Makefile.newtest | 71 - .../test-cases/strip-test3/comment.txt | 21 - .../test-cases/strip-test3/main.cxx | 6 - .../test-cases/strip_local/Makefile | 53 - .../unit-tests/test-cases/strip_local/foo.c | 8 - .../unit-tests/test-cases/strip_local/hello.c | 33 - .../stripped-indirect-symbol-table/Makefile | 57 - .../stripped-indirect-symbol-table/a.c | 7 - .../stripped-indirect-symbol-table/b.c | 12 - .../stripped-indirect-symbol-table/c.c | 11 - .../stripped-indirect-symbol-table/func.c | 1 - .../stripped-indirect-symbol-table/strip.list | 2 - .../test-cases/stub-generation-weak/foo.c | 5 - .../test-cases/stub-generation-weak/main.c | 40 - .../test-cases/stub-generation/Makefile | 41 - .../test-cases/stub-generation/test.c | 40 - .../test-cases/switch-jump-table/Makefile | 56 - .../switch-jump-table/interpose.exp | 2 - .../test-cases/switch-jump-table/switch.s | 49 - .../test-cases/symbol-moving/Makefile | 93 - .../unit-tests/test-cases/symbol-moving/aaa.c | 3 - .../test-cases/symbol-moving/anotb.c | 26 - .../unit-tests/test-cases/symbol-moving/bbb.c | 1 - .../test-cases/symbol-moving/bnota.c | 25 - .../test-cases/symbol-moving/main.c | 17 - .../test-cases/tentative-and-archive/foo.c | 6 - .../test-cases/tentative-and-archive/main.c | 8 - .../test-cases/tentative-and-dylib/Makefile | 56 - .../test-cases/tentative-and-dylib/foo.c | 2 - .../test-cases/tentative-and-dylib/main.c | 8 - .../tentative-to-real-hidden/Makefile | 52 - .../tentative-to-real-hidden/test.c | 11 - .../test-cases/tentative-to-real/Makefile | 45 - .../test-cases/tentative-to-real/comment.txt | 1 - .../test-cases/tentative-to-real/test.c | 3 - .../unit-tests/test-cases/thumb-blx/Makefile | 54 - .../unit-tests/test-cases/thumb-blx/test.c | 36 - .../undefined-dynamic-lookup/Makefile | 47 - .../undefined-dynamic-lookup/main.c | 32 - .../Makefile | 46 - .../visibility-warning-dylib-v-archive/bar.c | 11 - .../visibility-warning-dylib-v-archive/foo.c | 5 - .../visibility-warning-dylib-v-archive/main.c | 11 - .../test-cases/visibility-warning/Makefile | 57 - .../test-cases/visibility-warning/foo.c | 5 - .../visibility-warning/foo_hidden.c | 5 - .../test-cases/visibility-warning/foo_weak.c | 5 - .../visibility-warning/foo_weak_hidden.c | 5 - .../test-cases/weak-def-ordinal/Makefile | 50 - .../test-cases/weak-def-ordinal/bar.c | 6 - .../test-cases/weak-def-ordinal/foo.c | 11 - .../test-cases/weak-def-ordinal/main.c | 35 - .../unit-tests/test-cases/weak_dylib/Makefile | 49 - .../unit-tests/test-cases/weak_dylib/bar.c | 9 - .../unit-tests/test-cases/weak_dylib/bar.h | 9 - .../unit-tests/test-cases/weak_dylib/foo.c | 9 - .../unit-tests/test-cases/weak_dylib/foo.h | 6 - .../unit-tests/test-cases/weak_dylib/main.c | 22 - .../test-cases/weak_import/Makefile | 62 - .../unit-tests/test-cases/weak_import/foo.c | 17 - .../unit-tests/test-cases/weak_import/foo.h | 16 - .../unit-tests/test-cases/weak_import/main.c | 20 - .../test-cases/weak_import2/Makefile.newtest | 58 - .../test-cases/weak_import2/comment.txt | 1 - .../unit-tests/test-cases/weak_import2/foo.c | 17 - .../unit-tests/test-cases/weak_import2/foo.h | 16 - .../unit-tests/test-cases/weak_import2/foo1.c | 10 - .../unit-tests/test-cases/weak_import2/main.c | 20 - .../test-cases/weak_import3/Makefile | 43 - .../test-cases/weak_import3/comment.txt | 1 - .../unit-tests/test-cases/weak_import3/foo.c | 17 - .../unit-tests/test-cases/weak_import3/foo.h | 16 - .../unit-tests/test-cases/weak_import3/foo1.c | 4 - .../unit-tests/test-cases/weak_import3/main.c | 20 - .../unit-tests/test-cases/why_live/Makefile | 44 - .../unit-tests/test-cases/why_live/bar.c | 1 - .../unit-tests/test-cases/why_live/foo.c | 12 - .../unit-tests/test-cases/why_live/main.c | 7 - .../unit-tests/test-cases/zero-fill/test.c | 51 - .../unit-tests/test-cases/zero-fill2/Makefile | 38 - .../test-cases/zero-fill2/comment.txt | 1 - .../unit-tests/test-cases/zero-fill2/test.c | 58 - .../unit-tests/test-cases/zero-fill3/Makefile | 50 - .../test-cases/zero-fill3/comment.txt | 1 - .../unit-tests/test-cases/zero-fill3/test.c | 63 - ld64/doc/man/man1/dyldinfo.1 | 47 + ld64/doc/man/man1/ld.1 | 56 +- ld64/doc/man/man1/unwinddump.1 | 22 + ld64/ld64.xcodeproj/project.pbxproj | 495 +- ld64/src/Architectures.hpp | 88 - ld64/src/FileAbstraction.hpp | 145 - ld64/src/MachOReaderRelocatable.hpp | 4583 --------- ld64/src/MachOWriterExecutable.hpp | 8579 ----------------- ld64/src/OpaqueSection.hpp | 199 - ld64/src/Options.h | 368 - ld64/src/SectCreate.h | 43 - .../abstraction}/FileAbstraction.hpp | 0 .../MachOFileAbstraction.hpp | 299 +- ld64/src/abstraction/MachOTrie.hpp | 320 + ld64/src/debugline.h | 109 - ld64/src/dwarf2.h | 85 - .../src => src/ld}/Architectures.hpp | 18 +- ld64/src/{ => ld}/ArchiveReader.hpp | 39 +- ld64/src/{ => ld}/ExecutableFile.h | 8 +- ld64/src/{ => ld}/LTOReader.hpp | 53 +- ld64/src/{ => ld}/MachOReaderDylib.hpp | 156 +- .../src => src/ld}/MachOReaderRelocatable.hpp | 1694 +++- .../src => src/ld}/MachOWriterExecutable.hpp | 4064 +++++++- ld64/src/{ => ld}/ObjectFile.h | 53 +- .../src => src/ld}/OpaqueSection.hpp | 0 ld64/src/{ => ld}/Options.cpp | 614 +- ld64/{FireOpal/src => src/ld}/Options.h | 56 +- ld64/{FireOpal/src => src/ld}/SectCreate.h | 0 ld64/src/{ => ld}/debugline.c | 3 +- ld64/{FireOpal/src => src/ld}/debugline.h | 0 ld64/{FireOpal/src => src/ld}/dwarf2.h | 5 + ld64/src/{ => ld}/ld.cpp | 991 +- ld64/src/{ => other}/ObjectDump.cpp | 25 +- ld64/src/other/PruneTrie.cpp | 100 + ld64/src/other/dyldinfo.cpp | 1460 +++ ld64/src/{ => other}/machochecker.cpp | 22 +- .../func.h => src/other/prune_trie.h} | 43 +- ld64/{FireOpal/src => src/other}/rebase.cpp | 196 +- ld64/src/other/unwinddump.cpp | 926 ++ ld64/src/rebase.cpp | 945 -- ld64/unit-tests/include/common.makefile | 73 +- ld64/unit-tests/run-all-unit-tests | 7 +- .../test-cases/archive-force-load}/Makefile | 36 +- .../test-cases/archive-force-load}/bar.c | 0 .../test-cases/archive-force-load/bat.c | 1 + .../test-cases/archive-force-load}/baz.c | 1 + .../test-cases/archive-force-load}/foo.c | 0 .../test-cases/archive-force-load}/main.c | 3 +- .../test-cases/branch-distance}/Makefile | 19 +- .../test-cases/branch-distance/bar.s | 21 + .../test-cases/branch-distance/foo.s | 33 + .../test-cases/cfstring-coalesce/Makefile | 10 +- .../test-cases/cfstring-coalesce/bar.c | 1 + .../test-cases/cfstring-coalesce/foo.c | 3 + .../coalesce_weak_def_in_dylib}/Makefile | 34 +- .../coalesce_weak_def_in_dylib/foo.c | 4 + .../coalesce_weak_def_in_dylib/main.c | 17 + .../code-signed-object-file/Makefile | 26 + .../test-cases/code-signed-object-file}/foo.c | 5 - .../test-cases/cstring-alt-segment}/Makefile | 14 +- .../test-cases/cstring-alt-segment/custom.s | 8 + .../test-cases/cstring-alt-segment}/main.c | 5 +- .../cstring-custom-section}/Makefile | 31 +- .../test-cases/cstring-custom-section/bar.s | 31 + .../test-cases/cstring-custom-section/foo.s | 31 + .../test-cases/cstring-labels}/Makefile | 42 +- .../test-cases/cstring-labels/bar.c | 2 + .../test-cases/cstring-labels/foo.c | 6 + .../dead_strip-archive-eh}/Makefile | 22 +- .../test-cases/dead_strip-archive-eh/bar.cxx | 14 + .../test-cases/dead_strip-archive-eh/foo.cxx | 13 + .../dead_strip-archive-eh/main.cxx} | 1 + .../dead_strip-archive-weak}/Makefile | 16 +- .../test-cases/dead_strip-archive-weak/foo.c | 13 + .../test-cases/dead_strip-archive-weak/main.c | 27 + .../dead_strip-r_symbol_desc}/Makefile | 32 +- .../dead_strip-r_symbol_desc}/main.c | 16 +- .../dead_strip-weak-coalesce}/Makefile | 16 +- .../test-cases/dead_strip-weak-coalesce/baz.c | 7 + .../test-cases/dead_strip-weak-coalesce/foo.c | 25 + .../dead_strip-weak-coalesce/main.c | 13 + .../dead_strippable_dylib}/Makefile | 18 +- .../test-cases/dead_strippable_dylib}/bar.c | 0 .../test-cases/dead_strippable_dylib}/baz.c | 0 .../test-cases/dead_strippable_dylib}/foo.c | 0 .../test-cases/dead_strippable_dylib}/main.c | 2 +- .../test-cases/dtrace-static-probes/main.c | 9 +- .../dwarf-archive-all_load/Makefile | 4 +- .../dwarf-debug-notes/expected-stabs | 6 +- .../eh-coalescing-no-labels/Makefile | 51 + .../test-cases/eh-coalescing-no-labels/bar.c | 18 + .../test-cases/eh-coalescing-no-labels/baz.c | 18 + .../test-cases/eh-coalescing-no-labels/foo.c | 18 + .../test-cases/eh-coalescing-r/baz.cxx} | 2 +- .../test-cases/eh-stripped-symbols/Makefile | 23 + .../test-cases/eh-stripped-symbols/keep.exp | 3 + .../test-cases/eh-stripped-symbols/main.cxx} | 32 +- ld64/unit-tests/test-cases/eh_frame/Makefile | 1 + ld64/unit-tests/test-cases/end-label/foo.s | 2 + .../exported-symbols-wildcards/Makefile | 24 +- .../exported_symbols_list-eol/Makefile | 4 +- ld64/unit-tests/test-cases/filelist/Makefile | 24 +- .../test-cases/got-elimination/Makefile | 4 +- .../test-cases/implicit-common3/Makefile | 44 - .../test-cases/implicit-common3/a.c | 8 - .../test-cases/implicit-common3/comment.txt | 1 - .../test-cases/implicit-common3/test.c | 37 - .../test-cases/indirect-dylib/Makefile | 2 +- .../test-cases/init-order}/Makefile | 16 +- ld64/unit-tests/test-cases/init-order/bar.cxx | 16 + .../test-cases/init-order/expected-order.txt | 20 + ld64/unit-tests/test-cases/init-order/foo.cxx | 14 + .../unit-tests/test-cases/init-order/main.cxx | 18 + .../unit-tests/test-cases/kext-basic/Makefile | 29 + .../unit-tests/test-cases/kext-basic/mykext.c | 18 + .../test-cases/kext-basic/mykextinfo.c | 12 + .../test-cases/llvm-integration/Makefile | 22 +- .../test-cases/lto-archive-dylib}/Makefile | 26 +- .../test-cases/lto-archive-dylib/foo.c | 5 + .../test-cases/lto-archive-dylib/main.c} | 3 +- .../test-cases/lto-llvm-options/Makefile | 0 .../test-cases/lto-llvm-options/main.c | 0 .../test-cases/lto-preload-pie}/Makefile | 28 +- .../unit-tests/test-cases/lto-preload-pie/a.c | 4 + .../unit-tests/test-cases/lto-preload-pie/b.c | 1 + .../test-cases/lto-preload-pie/main.c | 11 + .../lto-weak-native-override/Makefile | 0 .../test-cases/lto-weak-native-override/foo.c | 6 + .../lto-weak-native-override/main.c | 0 .../test-cases/no-data-bundle}/Makefile | 12 +- .../test-cases/no-data-bundle/foo.c | 6 + .../test-cases/no-object-symbols/Makefile | 46 + .../test-cases/no-object-symbols/empty.s | 2 + .../unit-tests/test-cases/non-lazy-r/Makefile | 6 +- ld64/unit-tests/test-cases/non-lazy-r/foo.c | 5 + ld64/unit-tests/test-cases/non-lazy-r/other.c | 1 + .../objc-literal-pointers-strip}/Makefile | 24 +- .../objc-literal-pointers-strip}/test.m | 47 +- .../test-cases/objc-literal-pointers/Makefile | 6 +- .../test-cases/operator-new/Makefile | 5 +- .../test-cases/operator-new/main.cxx | 2 +- .../test-cases/order_file-ans/Makefile | 6 +- .../test-cases/order_file-ans/main.cxx | 5 +- .../test-cases/order_file-ans/main.expected | 8 +- .../test-cases/order_file-ans/main.order | 8 +- .../test-cases/prebound-main/Makefile | 26 +- .../Makefile | 35 +- .../re-export-optimizations-indirect}/bar.c | 0 .../re-export-optimizations-indirect}/foo.c | 0 .../re-export-optimizations-indirect}/main.c | 2 - .../re-export-optimizations-indirect/middle.c | 3 + .../re-export-optimizations-indirect/other.c | 1 + .../test-cases/rebase-basic/Makefile | 12 +- .../test-cases/relocs-asm/relocs-asm.s | 26 + .../relocs-neg-from-local}/Makefile | 30 +- .../test-cases/relocs-neg-from-local}/test.s | 45 +- .../test-cases/section-names-long}/Makefile | 20 +- .../test-cases/section-names-long/a.s | 9 + .../test-cases/section-names-long/b.s | 9 + .../test-cases/section-names-long/c.s | 11 + .../test-cases/section-names-long}/main.c | 0 .../test-cases/shared-cache-dylib}/Makefile | 21 +- .../test-cases/shared-cache-dylib}/foo.c | 0 .../test-cases/slow-x86-stubs/Makefile | 6 +- .../stripped-indirect-symbol-table/Makefile | 14 +- .../test-cases/switch-jump-table/Makefile | 17 +- .../test-cases/symbol-moving/Makefile | 2 +- .../test-cases/tentative-and-archive/Makefile | 19 +- .../test-cases/tentative-and-archive}/bar.c | 0 .../test-cases/tentative-and-archive/main.c | 1 + .../test-cases/thumb-pointer/Makefile | 49 + .../unit-tests/test-cases/thumb-pointer/bar.c | 5 + .../unit-tests/test-cases/thumb-pointer/foo.c | 14 + .../unexported_symbols_list-r}/Makefile | 14 +- .../unexported_symbols_list-r/foo.c | 9 + .../unexported_symbols_list-r/foo.exp | 3 + .../test-cases/weak-def-flag}/Makefile | 26 +- .../test-cases/weak-def-flag/main.c | 13 + .../test-cases/weak_import-force}/Makefile | 28 +- .../test-cases/weak_import-force/bar.c | 9 + .../test-cases/weak_import-force/foo.c | 9 + .../test-cases/weak_import-force/main.c | 31 + .../test-cases/weak_import/Makefile | 10 +- 779 files changed, 16086 insertions(+), 47021 deletions(-) delete mode 100644 ld64/FireOpal/APPLE_LICENSE delete mode 100644 ld64/FireOpal/ChangeLog delete mode 100644 ld64/FireOpal/doc/man/man1/ld.1 delete mode 100644 ld64/FireOpal/doc/man/man1/ld64.1 delete mode 100644 ld64/FireOpal/doc/man/man1/rebase.1 delete mode 100644 ld64/FireOpal/ld64.xcodeproj/project.pbxproj delete mode 100644 ld64/FireOpal/src/ArchiveReader.hpp delete mode 100644 ld64/FireOpal/src/ExecutableFile.h delete mode 100644 ld64/FireOpal/src/LTOReader.hpp delete mode 100644 ld64/FireOpal/src/MachOFileAbstraction.hpp delete mode 100644 ld64/FireOpal/src/MachOReaderDylib.hpp delete mode 100644 ld64/FireOpal/src/ObjectDump.cpp delete mode 100644 ld64/FireOpal/src/ObjectFile.h delete mode 100644 ld64/FireOpal/src/Options.cpp delete mode 100644 ld64/FireOpal/src/debugline.c delete mode 100644 ld64/FireOpal/src/ld.cpp delete mode 100644 ld64/FireOpal/src/machochecker.cpp delete mode 100644 ld64/FireOpal/unit-tests/README delete mode 100755 ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/make-recursive.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/mkld delete mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/result-filter.pl delete mode 100755 ld64/FireOpal/unit-tests/bin/rm-stale-test-logs delete mode 100755 ld64/FireOpal/unit-tests/clean-tests delete mode 100644 ld64/FireOpal/unit-tests/include/common.makefile delete mode 100644 ld64/FireOpal/unit-tests/include/test.h delete mode 100755 ld64/FireOpal/unit-tests/proctor-run delete mode 100755 ld64/FireOpal/unit-tests/run-all-unit-tests delete mode 100755 ld64/FireOpal/unit-tests/run-all-unit-tests-debug delete mode 100644 ld64/FireOpal/unit-tests/src/Makefile delete mode 100644 ld64/FireOpal/unit-tests/src/results-to-xml.cpp delete mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 delete mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m delete mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj delete mode 100644 ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch delete mode 100644 ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order delete mode 100644 ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs delete mode 100755 ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx delete mode 100755 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx delete mode 100755 ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/empty-object/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/end-label/bar.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/end-label/foo.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/filelist/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/filelist/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/flat-main/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test1.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test2.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test3.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/large-data/test4.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s delete mode 100755 ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-references/test.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/extra.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main1.order delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main2.order delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected delete mode 100644 ld64/FireOpal/unit-tests/test-cases/order_file/main3.order delete mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table delete mode 100644 ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m delete mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order delete mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/special-labels/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/static-executable/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/static-strip/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp delete mode 100644 ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s delete mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/bar.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/foo.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/why_live/main.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt delete mode 100644 ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c create mode 100644 ld64/doc/man/man1/dyldinfo.1 create mode 100644 ld64/doc/man/man1/unwinddump.1 delete mode 100644 ld64/src/Architectures.hpp delete mode 100644 ld64/src/FileAbstraction.hpp delete mode 100644 ld64/src/MachOReaderRelocatable.hpp delete mode 100644 ld64/src/MachOWriterExecutable.hpp delete mode 100644 ld64/src/OpaqueSection.hpp delete mode 100644 ld64/src/Options.h delete mode 100644 ld64/src/SectCreate.h rename ld64/{FireOpal/src => src/abstraction}/FileAbstraction.hpp (100%) rename ld64/src/{ => abstraction}/MachOFileAbstraction.hpp (76%) create mode 100644 ld64/src/abstraction/MachOTrie.hpp delete mode 100644 ld64/src/debugline.h delete mode 100644 ld64/src/dwarf2.h rename ld64/{FireOpal/src => src/ld}/Architectures.hpp (80%) rename ld64/src/{ => ld}/ArchiveReader.hpp (90%) rename ld64/src/{ => ld}/ExecutableFile.h (88%) rename ld64/src/{ => ld}/LTOReader.hpp (93%) rename ld64/src/{ => ld}/MachOReaderDylib.hpp (86%) rename ld64/{FireOpal/src => src/ld}/MachOReaderRelocatable.hpp (74%) rename ld64/{FireOpal/src => src/ld}/MachOWriterExecutable.hpp (67%) rename ld64/src/{ => ld}/ObjectFile.h (86%) rename ld64/{FireOpal/src => src/ld}/OpaqueSection.hpp (100%) rename ld64/src/{ => ld}/Options.cpp (83%) rename ld64/{FireOpal/src => src/ld}/Options.h (86%) rename ld64/{FireOpal/src => src/ld}/SectCreate.h (100%) rename ld64/src/{ => ld}/debugline.c (99%) rename ld64/{FireOpal/src => src/ld}/debugline.h (100%) rename ld64/{FireOpal/src => src/ld}/dwarf2.h (97%) rename ld64/src/{ => ld}/ld.cpp (80%) rename ld64/src/{ => other}/ObjectDump.cpp (94%) create mode 100644 ld64/src/other/PruneTrie.cpp create mode 100644 ld64/src/other/dyldinfo.cpp rename ld64/src/{ => other}/machochecker.cpp (97%) rename ld64/{FireOpal/unit-tests/test-cases/eh-coalescing/func.h => src/other/prune_trie.h} (53%) rename ld64/{FireOpal/src => src/other}/rebase.cpp (83%) create mode 100644 ld64/src/other/unwinddump.cpp delete mode 100644 ld64/src/rebase.cpp rename ld64/{FireOpal/unit-tests/test-cases/tentative-and-archive => unit-tests/test-cases/archive-force-load}/Makefile (62%) rename ld64/{FireOpal/unit-tests/test-cases/symbol-moving => unit-tests/test-cases/archive-force-load}/bar.c (100%) create mode 100644 ld64/unit-tests/test-cases/archive-force-load/bat.c rename ld64/{FireOpal/unit-tests/test-cases/dwarf-archive-all_load => unit-tests/test-cases/archive-force-load}/baz.c (93%) rename ld64/{FireOpal/unit-tests/test-cases/dwarf-archive-all_load => unit-tests/test-cases/archive-force-load}/foo.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/switch-jump-table => unit-tests/test-cases/archive-force-load}/main.c (88%) rename ld64/{FireOpal/unit-tests/test-cases/branch-islands => unit-tests/test-cases/branch-distance}/Makefile (76%) create mode 100644 ld64/unit-tests/test-cases/branch-distance/bar.s create mode 100644 ld64/unit-tests/test-cases/branch-distance/foo.s rename ld64/{FireOpal/unit-tests/test-cases/segment-order => unit-tests/test-cases/coalesce_weak_def_in_dylib}/Makefile (66%) create mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c create mode 100644 ld64/unit-tests/test-cases/code-signed-object-file/Makefile rename ld64/{FireOpal/unit-tests/test-cases/dead_strip-init-archive => unit-tests/test-cases/code-signed-object-file}/foo.c (73%) rename ld64/{FireOpal/unit-tests/test-cases/objc-selector-coalescing => unit-tests/test-cases/cstring-alt-segment}/Makefile (76%) create mode 100644 ld64/unit-tests/test-cases/cstring-alt-segment/custom.s rename ld64/{FireOpal/unit-tests/test-cases/flat-indirect-undefines => unit-tests/test-cases/cstring-alt-segment}/main.c (61%) rename ld64/{FireOpal/unit-tests/test-cases/cfstring-utf16 => unit-tests/test-cases/cstring-custom-section}/Makefile (56%) create mode 100644 ld64/unit-tests/test-cases/cstring-custom-section/bar.s create mode 100644 ld64/unit-tests/test-cases/cstring-custom-section/foo.s rename ld64/{FireOpal/unit-tests/test-cases/private-non-lazy => unit-tests/test-cases/cstring-labels}/Makefile (52%) create mode 100644 ld64/unit-tests/test-cases/cstring-labels/bar.c create mode 100644 ld64/unit-tests/test-cases/cstring-labels/foo.c rename ld64/{FireOpal/unit-tests/test-cases/lazy-dylib-objc => unit-tests/test-cases/dead_strip-archive-eh}/Makefile (70%) create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-eh/bar.cxx create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-eh/foo.cxx rename ld64/{FireOpal/unit-tests/test-cases/implicit_dylib/main.c => unit-tests/test-cases/dead_strip-archive-eh/main.cxx} (98%) rename ld64/{FireOpal/unit-tests/test-cases/dead_strip-init-archive => unit-tests/test-cases/dead_strip-archive-weak}/Makefile (73%) create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-weak/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-weak/main.c rename ld64/{FireOpal/unit-tests/test-cases/prebound-main => unit-tests/test-cases/dead_strip-r_symbol_desc}/Makefile (64%) rename ld64/{FireOpal/unit-tests/test-cases/dead_strip-archive-global => unit-tests/test-cases/dead_strip-r_symbol_desc}/main.c (87%) rename ld64/{FireOpal/unit-tests/test-cases/special-labels => unit-tests/test-cases/dead_strip-weak-coalesce}/Makefile (70%) create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c rename ld64/{FireOpal/unit-tests/test-cases/dead_strip_dylibs => unit-tests/test-cases/dead_strippable_dylib}/Makefile (74%) rename ld64/{FireOpal/unit-tests/test-cases/dead_strip_dylibs => unit-tests/test-cases/dead_strippable_dylib}/bar.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/dead_strip_dylibs => unit-tests/test-cases/dead_strippable_dylib}/baz.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/allowable-client => unit-tests/test-cases/dead_strippable_dylib}/foo.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/loader_path => unit-tests/test-cases/dead_strippable_dylib}/main.c (100%) create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-no-labels/bar.c create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-no-labels/baz.c create mode 100644 ld64/unit-tests/test-cases/eh-coalescing-no-labels/foo.c rename ld64/{FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx => unit-tests/test-cases/eh-coalescing-r/baz.cxx} (98%) create mode 100644 ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile create mode 100644 ld64/unit-tests/test-cases/eh-stripped-symbols/keep.exp rename ld64/{FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m => unit-tests/test-cases/eh-stripped-symbols/main.cxx} (82%) delete mode 100644 ld64/unit-tests/test-cases/implicit-common3/Makefile delete mode 100644 ld64/unit-tests/test-cases/implicit-common3/a.c delete mode 100644 ld64/unit-tests/test-cases/implicit-common3/comment.txt delete mode 100644 ld64/unit-tests/test-cases/implicit-common3/test.c rename ld64/{FireOpal/unit-tests/test-cases/objc-exported_symbols_list => unit-tests/test-cases/init-order}/Makefile (70%) create mode 100644 ld64/unit-tests/test-cases/init-order/bar.cxx create mode 100644 ld64/unit-tests/test-cases/init-order/expected-order.txt create mode 100644 ld64/unit-tests/test-cases/init-order/foo.cxx create mode 100644 ld64/unit-tests/test-cases/init-order/main.cxx create mode 100644 ld64/unit-tests/test-cases/kext-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/kext-basic/mykext.c create mode 100644 ld64/unit-tests/test-cases/kext-basic/mykextinfo.c rename ld64/{FireOpal/unit-tests/test-cases/lazy-dylib => unit-tests/test-cases/lto-archive-dylib}/Makefile (68%) create mode 100644 ld64/unit-tests/test-cases/lto-archive-dylib/foo.c rename ld64/{FireOpal/unit-tests/test-cases/llvm-integration/main20.c => unit-tests/test-cases/lto-archive-dylib/main.c} (79%) rename ld64/{FireOpal => }/unit-tests/test-cases/lto-llvm-options/Makefile (100%) rename ld64/{FireOpal => }/unit-tests/test-cases/lto-llvm-options/main.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/eh-coalescing-r => unit-tests/test-cases/lto-preload-pie}/Makefile (64%) create mode 100644 ld64/unit-tests/test-cases/lto-preload-pie/a.c create mode 100644 ld64/unit-tests/test-cases/lto-preload-pie/b.c create mode 100644 ld64/unit-tests/test-cases/lto-preload-pie/main.c rename ld64/{FireOpal => }/unit-tests/test-cases/lto-weak-native-override/Makefile (100%) create mode 100644 ld64/unit-tests/test-cases/lto-weak-native-override/foo.c rename ld64/{FireOpal => }/unit-tests/test-cases/lto-weak-native-override/main.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/zero-fill => unit-tests/test-cases/no-data-bundle}/Makefile (78%) create mode 100644 ld64/unit-tests/test-cases/no-data-bundle/foo.c create mode 100644 ld64/unit-tests/test-cases/no-object-symbols/Makefile create mode 100644 ld64/unit-tests/test-cases/no-object-symbols/empty.s rename ld64/{FireOpal/unit-tests/test-cases/objc-literal-pointers => unit-tests/test-cases/objc-literal-pointers-strip}/Makefile (68%) rename ld64/{FireOpal/unit-tests/test-cases/relocs-objc => unit-tests/test-cases/objc-literal-pointers-strip}/test.m (76%) rename ld64/{FireOpal/unit-tests/test-cases/re-export-optimizations => unit-tests/test-cases/re-export-optimizations-indirect}/Makefile (53%) rename ld64/{FireOpal/unit-tests/test-cases/indirect-path-search => unit-tests/test-cases/re-export-optimizations-indirect}/bar.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/blank-stubs => unit-tests/test-cases/re-export-optimizations-indirect}/foo.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/re-export-optimizations => unit-tests/test-cases/re-export-optimizations-indirect}/main.c (73%) create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c rename ld64/{FireOpal/unit-tests/test-cases/end-label => unit-tests/test-cases/relocs-neg-from-local}/Makefile (64%) rename ld64/{FireOpal/unit-tests/test-cases/multiple-entry-points => unit-tests/test-cases/relocs-neg-from-local}/test.s (79%) rename ld64/{FireOpal/unit-tests/test-cases/commons-order => unit-tests/test-cases/section-names-long}/Makefile (58%) create mode 100644 ld64/unit-tests/test-cases/section-names-long/a.s create mode 100644 ld64/unit-tests/test-cases/section-names-long/b.s create mode 100644 ld64/unit-tests/test-cases/section-names-long/c.s rename ld64/{FireOpal/unit-tests/test-cases/commons-order => unit-tests/test-cases/section-names-long}/main.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip => unit-tests/test-cases/shared-cache-dylib}/Makefile (54%) rename ld64/{FireOpal/unit-tests/test-cases/symbol-moving => unit-tests/test-cases/shared-cache-dylib}/foo.c (100%) rename ld64/{FireOpal/unit-tests/test-cases/tentative-and-dylib => unit-tests/test-cases/tentative-and-archive}/bar.c (100%) create mode 100644 ld64/unit-tests/test-cases/thumb-pointer/Makefile create mode 100644 ld64/unit-tests/test-cases/thumb-pointer/bar.c create mode 100644 ld64/unit-tests/test-cases/thumb-pointer/foo.c rename ld64/{FireOpal/unit-tests/test-cases/objc-category-debug-notes => unit-tests/test-cases/unexported_symbols_list-r}/Makefile (74%) create mode 100644 ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.c create mode 100644 ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.exp rename ld64/{FireOpal/unit-tests/test-cases/operator-new => unit-tests/test-cases/weak-def-flag}/Makefile (65%) create mode 100644 ld64/unit-tests/test-cases/weak-def-flag/main.c rename ld64/{FireOpal/unit-tests/test-cases/stub-generation-weak => unit-tests/test-cases/weak_import-force}/Makefile (65%) create mode 100644 ld64/unit-tests/test-cases/weak_import-force/bar.c create mode 100644 ld64/unit-tests/test-cases/weak_import-force/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import-force/main.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog index 1a79a07..79dcc59 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,542 +1,4534 @@ -2008-07-10 Nick Kledzik +----- Tagged ld64-95.2.12 - * src/LTOReader.hpp: improve missing symbol error message +2009-07-02 Nick Kledzik + creation of __unwind_info section can fail if hundreds of functions cannot be compact encoded -2008-07-08 Nick Kledzik - ld: add support for mllvm LTO options - * src/Options.cpp: support -mllvm option - * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options - * src/ld.cpp: pass llvmOptions to optimize() - * src/Options.h: add fLLVMOptions - * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() - * src/ObjectFile.h: add llvmOptions parameter to optimize() - * unit-tests/test-cases/lto-llvm-options: add test case - +----- Tagged ld64-95.2.11 -2008-06-04 Nick Kledzik +2009-06-19 Nick Kledzik - * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + Link Time Optimization errors out when targeting < 10.6 -2008-06-04 Nick Kledzik - * src/ObjectFile.h: add deadAtoms parameter to optimize() - * src/ld.cpp: ditto - * src/ArchiveReader.hpp: ditto - * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs - * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away - * unit-tests/test-cases/lto-weak-native-override: add test case +----- Tagged ld64-95.2.10 +2009-04-02 Nick Kledzik -2008-06-04 Nick Kledzik + corrupt metaclass entry in dynamic library + * src/ld/ld.cpp: change Section constructor to copy segment and section names - LTO : 176.gcc and 177.mesa build failure at -O4 - * src/LTOReader.hpp: make sure internal is returned by getAtoms() - * unit-tests/test-cases/lto-archive-dylib: update test case +----- Tagged ld64-95.2.9 -2008-05-06 Nick Kledzik +2009-04-02 Nick Kledzik - ARM ld should take W bit off of maxprot for __TEXT segment - * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments + Update ld64 for new triples introduced in 6654669 to support ARM LLVM + * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples -2008-05-06 Nick Kledzik +----- Tagged ld64-95.2.8 - encryptable images may not be signable - * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section +2009-03-24 Nick Kledzik + anonymous functions have the compact unwind info computed wrong + * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom ------ Tagged ld64-85 (Xcode 3.1) -2008-04-29 Nick Kledzik +----- Tagged ld64-95.2.7 - * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include +2009-03-11 Nick Kledzik + AddressBook incorrectly gets _objc_msgSend from WebKit + * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib + that is already explictly or implicitly linked. + * unit-tests/test-cases/re-export-optimizations-indirect: add test case + -2008-04-29 Nick Kledzik +2009-03-10 Nick Kledzik - ld doesn't honor "rightmost" -syslibroot argument - * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + dyld weak linking optimization leaves some symbols unbound + * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference + to a symbol in a dylib that is a weak definition + * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case -2008-04-29 Nick Kledzik - - GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files - * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment - * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment +2009-03-10 Nick Kledzik + many OS i386 OS dylibs still have __IMPORT segment + * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr + * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem -2008-04-17 Nick Kledzik - * src/MachOReaderRelocatable.hpp: better cpu subtype support +----- Tagged ld64-95.2.6 +2009-02-27 Nick Kledzik -2008-04-14 Nick Kledzik - - ld64 has bad ARM branch island check - * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail + ld might set MH_WEAK_DEFINES when it should not + * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef + that will be exported when computing MH_WEAK_DEFINES + * unit-tests/test-cases/operator-new: updated to reproduce issue -2008-04-10 Nick Kledzik +----- Tagged ld64-95.2.5 - * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs +2009-02-24 Nick Kledzik + + x86_64 obj-c runtime confused when static lib is stripped + * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings + * unit-tests/test-cases/objc-literal-pointers-strip: added test case ------ Tagged ld64-84.4 +----- Tagged ld64-95.2.4 -2008-04-10 Nick Kledzik +2009-02-23 Nick Kledzik - SPEC2000/eon built with -mdynamic-no-pic won't run - * src/Architectures.hpp: added arm::kReadOnlyPointer - * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer - * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer - * src/machochecker.cpp: allow MH_PIE bit - * unit-tests/test-cases/switch-jump-table: added test cases + * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs + + +2009-02-18 Nick Kledzik + + Writer::symbolIndex() uses a linear search and does not scale + * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better ------ Tagged ld64-84.3 +2009-02-18 Nick Kledzik -2008-04-09 Nick Kledzik + Use new compact encodings that handle all register permutations + * src/ld/Architectures.hpp: add kSectionOffset24 + * src/ld/ObjectFile.h: add getFDE() + * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding + * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed + * src/other/unwinddump.cpp: update unwinddump output to display register save set - -undefined dynamic_lookup busted - * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates - * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup +2009-02-16 Nick Kledzik ------ Tagged ld64-84.2 + runtime error with bundle for 10.5 that has weak external symols + * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions + -2008-04-04 Nick Kledzik +2009-02-15 Nick Kledzik - * src/ld.cpp: don't add .eh symbols to symbol table in -r mode - * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing + i386 relocation error with negative offsets from local labels + * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label + * unit-tests/test-cases/relocs-neg-from-local: add test case ------ Tagged ld64-84.1 +2009-02-12 Nick Kledzik -2008-03-28 Nick Kledzik + -dead_strip inhibits weak coalescing in no_dead_strip section + * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms + * unit-tests/test-cases/dead_strip-weak-coalesce: added test case - ld should prefer architecture-specific variant over generic in fat object file - * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture - * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files - * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc + +2009-02-12 Nick Kledzik + + x86_64 weak_import broken for initialized data + * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader::addRelocReference() + * src/other/dyldinfo.cpp: update to display weak_import attribute + * unit-tests/test-cases/weak_import: updated test case ------ Tagged ld64-84 +2009-02-06 Nick Kledzik -2008-03-28 Nick Kledzik + ld parsing of __eh_frame unwind information is slow + * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 - * src/LTOReader.hpp: don't print lto version, if lto is unavailable - -2008-03-26 Nick Kledzik +----- Tagged ld64-95.2.3 - Add LD_WARN_COMMONS to BigBear builds - * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file - +2009-02-04 Nick Kledzik -2008-03-26 Nick Kledzik + ld: warning: can't add line info to anonymous symbol + * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs - Need encryption tag in mach-o file - linker should adjust arm final linked images so __text is never on the same page as the load commands - * src/MachOFileAbstraction.hpp: add support for encryption_info_command - * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption - * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom - * src/machochecker.cpp: validate LC_ENCRYPTION_INFO - -2008-03-25 Nick Kledzik +----- Tagged ld64-95.2.2 - ld64 does not recognize LLVM bitcode archive files - * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp - * src/ArchiveReader.hpp: sniff each member and instantiate correct reader - * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader - * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp - * unit-tests/test-cases/llvm-integration: added test case +2009-02-02 Nick Kledzik + ld -r does not preserve the N_NO_DEAD_STRIP bit + * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() + * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case -2008-03-25 Nick Kledzik - ld64 should switch to new libLTO.dylib interface - Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc - * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface - * unit-tests/test-cases/llvm-integration: update and comment - * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib - * src/ld.cpp: rework and simplify Linker::optimize() - * src/ObjectDump.cpp: Add -nm option - +----- Tagged ld64-95.2.1 -2008-03-25 Nick Kledzik +2009-01-29 Nick Kledzik - * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem - * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem + ld coalesces C strings in different segments + * src/ld/MachOReaderRelocatable.hpp: only do standard coalescing on __cstring section if is in __TEXT segment + * unit-tests/test-cases/cstring-alt-segment: add test case + +2009-01-29 Nick Kledzik -2008-03-24 Nick Kledzik + gcc DejaGnu failure: building longcall/dylib library + * src/ld/MachOWriterExecutable.hpp: if no __DATA sections insert non-lazy pointers at end of __TEXT segment + * unit-tests/test-cases/no-data-bundle: added test case - Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 - * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. +----- Tagged ld64-95.2 -2008-03-21 Nick Kledzik +2009-01-06 Nick Kledzik - * src/Options.cpp: warn if -seg1addr value is not page aligned + strip -S fails with "new trie is larger than original" + * src/other/PruneTrie.cpp: don't align trie more than original trie was aligned -2008-03-21 Nick Kledzik +----- Tagged ld64-95.1 - Move ARM support outside of __OPEN_SOURCE__ - * src/ld.cpp: remove __OPEN_SOURCE__ around arm support - * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support - * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h - +2008-12-21 Nick Kledzik ------ Tagged ld64-83.2 - -2008-03-15 Nick Kledzik - - ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results - * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files - * unit-tests/test-cases/objc-exported_symbols_list: added test case + * src/ld/MachOWriterExecutable.hpp: in new linkedit format, make sure only exported symbols + make it into weak binding info ------ Tagged ld64-83.1 +----- Tagged ld64-95 -2008-03-14 Nick Kledzik +2008-12-18 Nick Kledzik - -iphone_version_min ==> -iphoneos_version_min - * src/Options.cpp: support -iphoneos_version_min as well + * src/ld/Options.cpp: move check for fSharedRegionEligible until fPrebind has stabilized ------ Tagged ld64-83 - -2008-03-10 Nick Kledzik +2008-12-18 Nick Kledzik - ld needs to strip iphone_version_min option if invoking ld_classic - * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic + Generate new compressed LINKEDIT when targeting 10.6 + * src/ld/Options.cpp: turn on compressed LINKEDIT by default -2008-03-04 Nick Kledzik +----- Tagged ld64-94.1 - ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) - * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs - * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework - * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools - * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() - * src/ld.cpp: pass lazy helper atom to writer - * doc/man/man1/ld.1: document new options - * unit-tests/test-cases/lazy-dylib-objc: add test case - * unit-tests/test-cases/lazy-dylib: add test case - +2008-12-16 Nick Kledzik ------ Tagged ld64-82.7 + * src/ld/Options.cpp: Fix -F handling in buildSearchPaths() -2008-03-07 Nick Kledzik - duplicate symbol literal-pointer@__OBJC@__message_refs@... - * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak - * unit-tests/test-cases/objc-selector-coalescing: added test case +----- Tagged ld64-94 + +2008-12-15 Nick Kledzik + * doc/man/man1/ld.1: document new options ------ Tagged ld64-82.6 -2008-03-04 Nick Kledzik +2008-12-15 Nick Kledzik - ld crashes building XsanFS for Snow Leopard Builds - * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() - * unit-tests/test-cases/tentative-and-archive: added test case + linker should enforce all .o files have same sub-type, and ignore sub-type of dylibs + * doc/man/man1/ld.1: update man page about -allow_sub_type_mismatches + * src/ld/ld.cpp: call validFile() with new arguments + * src/ld/MachOReaderRelocatable.hpp: add new arguments to validFile() + * src/ld/Options.cpp: Support LD_ALLOW_CPU_SUBTYPE_MISMATCHES and -allow_sub_type_mismatches -2008-03-04 Nick Kledzik - ld64 should not force building with gcc 4.0 - * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 - +2008-12-15 Nick Kledzik -2008-02-29 Nick Kledzik + -syslibroot should skip standard search paths not in the SDK + * src/ld/Options.cpp: in buildSearchPaths() if an SDK is specified don't add + standard search paths not in the SDK. - Simulator frameworks are being build split-seg and not prebound - * src/Options.cpp: only splitseg if prebound +2008-12-15 Nick Kledzik -2008-02-29 Nick Kledzik + ld: remove "can't make compact unwind encoding" warning + * src/ld/ObjectFile.h: add fWarnCompactUnwind + * src/ld/Options.cpp: -warn_compact_unwind --> fWarnCompactUnwind + * src/ld/MachOReaderRelocatable.hpp: test fWarnCompactUnwind before warning - Linker should not make GSYM debug note for .objc_category_* symbols - * src/ld.cpp: suppress GSYM debug notes for absolute symbols - * unit-tests/test-cases/objc-category-debug-notes: added test case +2008-12-15 Nick Kledzik -2008-02-29 Nick Kledzik + Add dtrace usdt support for arm to ld64 + * src/ld/MachOWriterExecutable.hpp: handle arm::kDtraceIsEnabledSite + * unit-tests/test-cases/dtrace-static-probes: use is-enabled in test case - non-ASCII CFString support is broken - * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring - * unit-tests/test-cases/cfstring-utf16: add test case +----- Tagged ld64-93 -2008-02-25 Nick Kledzik +2008-12-11 Nick Kledzik - ld -r -x - * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels + * src/ld/ObjectFile.h: add fIPhoneVersionMin to track min iPhoneOS version + * src/ld/Options.cpp: use fIPhoneVersionMin + +2008-12-11 Nick Kledzik ------ Tagged ld64-82.5 + non-lazy pointer to non-global tentative definition encoded wrong + * src/ld/MachOWriterExecutable.hpp: don't use INDIRECT_SYMBOL_LOCAL for tentative definitions + * unit-tests/test-cases/non-lazy-r: updated test case -2008-02-12 Nick Kledzik - x86_64: -stack_size failure when large __bss is used - * src/ld.cpp: only move section already in __DATA segment to new __huge section - * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section +2008-12-11 Nick Kledzik + + kernel fails to boot when ld64 used for intermediate ld -r step + * src/ld/MachOWriterExecutable.hpp: in -r mode when generating a scattered sect-diff reloc for + i386/arm, special case when from target is not the atom + the relocation is in. + * unit-tests/test-cases/relocs-asm: update test case ------ Tagged ld64-82.4 +2008-12-11 Nick Kledzik -2008-02-06 Nick Kledzik - - comdat warnings with ld -r of C++ .o files - * unit-tests/test-cases/eh-coalescing-r: added test case - * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static - + * src/ld/ld.cpp: handle new __program_vars section + * src/ld/MachOWriterExecutable.hpp: handle inserting synthesized sections when there is no __dyld section -2008-02-06 Devang Patel - LTO of Bom framework with -dead_strip causes ld(1) crash - * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. - * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. - * unit-tests/test-cases/llvm-integration/a15.c: New. - * unit-tests/test-cases/llvm-integration/b15.c: New. - * unit-tests/test-cases/llvm-integration/c15.c: New. - -2008-02-05 Nick Kledzik +2008-12-11 Nick Kledzik - * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used + * src/ld/MachOReaderRelocatable.hpp: Fix getDescription() to work when direct reference is to anonymous atom + + +2008-12-10 Nick Kledzik + + * src/ld/Options.cpp: enable LD_FORCE_NO_PREBIND to be used with arm + + +2008-12-10 Nick Kledzik ------ Tagged ld64-82.3 + Developer tool to print the new compressed LINKEDIT information + * src/other/dyldinfo.cpp: fix typo in usage() -2008-02-04 Nick Kledzik - ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves - * src/ObjectFile.h: add 10.6 - * src/Options.cpp: add 10.6 support - * src/MachOReaderDylib.hpp: recognize $os10.6$ - +2008-12-05 Nick Kledzik ------ Tagged ld64-82.2 + SnowLeopard kernel should compile warning free + * src/ld/MachOReaderRelocatable.hpp: correct parse two global labels at end of section and make one an alias + * unit-tests/test-cases/end-label: update test case -2008-01-30 Devang Patel - Can't build 64-bit Intel binaries with LTO - ld64 fails to build with llvm-gcc-4.2 - * src/LLVMReader.hpp: Fix character count typo in strncmp call. - Use const char * to initialize temp. string. - * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction - instead of hard coding /Developer. - ------ Tagged ld64-82.1 +2008-12-04 Nick Kledzik -2008-01-23 Nick Kledzik + Better warning than "PPC_RELOC_JBSR should not be using an external relocation" + * src/ld/MachOReaderRelocatable.hpp: issue warning with .o path if it was compiled with -mlong-branch - * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs + +2008-12-04 Nick Kledzik + linker should not map __pointers -> __nl_symbol_ptr unless actually making new LINKEDIT + * src/ld/ObjectFile.h: add fMakeCompressedDyldInfo for readers to see + * src/ld/Options.cpp: set fMakeCompressedDyldInfo for readers to see + * src/ld/MachOReaderRelocatable.hpp: check fMakeCompressedDyldInfo -2008-01-22 Nick Kledzik - ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files - * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs - * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs - * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files +2008-12-02 Nick Kledzik + * src/ld/debugline.c: fix error handling in line_open() ------ Tagged ld64-82 - -2008-01-18 Nick Kledzik - Bad grammar used in ld warning: cannot exported hidden symbol - * src/ld.cpp: fix typo in warning string +2008-11-26 Nick Kledzik + + vtable with thumb entries broke after ld -r + * src/ld/MachOReaderRelocatable.hpp: if target of reloc is thumb, mask thumb bit off addend + * unit-tests/test-cases/thumb-pointer: added test case + + +2008-11-26 Nick Kledzik + + * src/ld/Option.cpp: Fix how crashreporterBuffer is created to not miss some arguments -2008-01-16 Nick Kledzik +2008-11-24 Nick Kledzik - Bundle Loader does not work anymore when loader is a bundle - ld warns of incorrect architecture when linking a bundle to a bundle - * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages - * unit-tests/test-cases/bundle_loader: update test case - + Security.framework has some duplicate FDEs for some functions + * src/ld/ld.cpp: remove fDeadAtoms from fLiveAtoms when there are weak atoms overriden by late loads + * unit-tests/test-cases/dead_strip-archive-eh: added test case + + +----- Tagged ld64-92 + +2008-11-21 Nick Kledzik + + * src/ld/MachOReaderDylib.hpp: if export_size is zero, no need to parse trie + * src/abstraction/MachOTrie.hpp: gracefully handle empty trie -2008-01-16 Nick Kledzik + +2008-11-21 Nick Kledzik + + strip(1) support for new compressed LINKEDIT information + * ld64.xcodeproj/project.pbxproj: build and install new libprunetrie.a + * src/other/prune_trie.h: added + * src/other/PruneTrie.cpp: implements prune_trie() - ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) - * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S + +2008-11-21 Nick Kledzik + + * src/ld/ld.cpp: if an export file is used and all weak symbols are masked, don't set WEAK_DEFINES + * unit-tests/test-cases/weak-def-flag: added test case + + +2008-11-20 Nick Kledzik + + Generate new compressed LINKEDIT when targeting 10.6 + * src/ld/MachOWriterExecutable.hpp: support generating new compressed format + * src/ld/MachOReaderRelocatable.hpp: new compress format implies non-lazy pointers in __DATA for i386 + * src/ld/MachOReaderDylib.hpp: support linking aginst new format + * src/ld/Options.cpp: suppport -exported_symbols_order and -no_compact_linkedit + * src/ld/ld.cpp: track which atoms have weak counter parts in dylibs + * src/other/dyldinfo.cpp: added tool to display new LINKEDIT format + * ld64.xcodeproj/project.pbxproj: add dyldinfo tool + * unit-tests/*: lots of fixes to work with new format + + +2008-11-20 Nick Kledzik + + ld64 should preserve N_WEAK_REF when linking MH_KEXT_BUNDLEs + * src/ld/MachOWriterExecutable.hpp: set up fWeakImportMap in synthesizeKextGOT() + + +2008-11-19 Nick Kledzik + + VideoToolbox.framework has bad __TEXT.__eh_frame info + * src/ld/Options.cpp: add -no_eh_labels option for use with -r + * src/ld/MachOWriterExecutable.hpp: generate correct x86_64 labeless relocs in -r mode + * src/ld/MachOReaderRelocatable.hpp: now ignore all labels and relocations in + __TEXT/__eh_frame section and rely on getCFIs() from libunwind + * unit-tests/test-cases/eh-coalescing-no-labels: add test case + + +2008-11-19 Nick Kledzik + + LTO doesn't like dtrace symbols + * src/ld/LTOReader.hpp: ignore __dtrace_probe undefines in bitcode files -2008-01-16 Nick Kledzik +2008-11-14 Nick Kledzik - if ld crashes while writing output file, it should delete the half written file - * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete - output file on failure. + * src/abstraction/MachOFileAbstraction.hpp: fix to work with 10.5 headers + +----- Tagged ld64-91 -2008-01-16 Devang Patel +2008-11-07 Nick Kledzik - * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. + Remove COMPACT_UNWIND_SUPPORT conditionalizing + +2008-11-06 Nick Kledzik -2008-01-16 Nick Kledzik + Reorganize source layout. ld sources are now in "ld", + and other tools are in "other". + - GC-supported library can't be linked into GC-required executable - * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and - allow gc-compatible code to be linked into anything. - * unit-tests/test-cases/objc-gc-checks: update test case +2008-11-05 Nick Kledzik + * ld64.xcodeproj/project.pbxproj: start installing unwinddump tool + * src/UnwindDump.cpp: support -arch option + * doc/man/man1/unwinddump.1: create man page -2008-01-15 Nick Kledzik - no debug notes for custom named data - * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore - * unit-tests/test-cases/dwarf-debug-notes: update test case +2008-11-05 Nick Kledzik + + linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries + * src/ld.cpp: in synthesizeDebugNotes() set other field of OSO to be subtype + + +2008-11-05 Nick Kledzik + + Need a linker option to load all objects from one library + * src/Options.cpp: support -force_load option + * src/ArchiveReader.hpp: Add fForceLoad ivar + * doc/man/man1/ld.1: update man page with -force_load option + * unit-tests/test-cases/archive-force-load: add test case + + +2008-11-05 Nick Kledzik + + Dtrace Probe Warnings: SnowLeopard kernel should compile warning free + * src/ld.cpp: don't generate GSYM stabs for old style __dtrace_probe + * src/MachOReaderRelocatable.hpp: fix test for deciding if a symbol is an alias ------ Tagged ld64-81.5 -2008-01-14 Devang Patel +2008-11-04 Nick Kledzik - llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 - * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references - after optimization. - * src/ld.cpp: Resolve additional unbounded references after optimization. + ADOBE: XCODE: ld: duplicate typeinfo in executable + * src/ld.cpp: in dead-strip mode, record overriden symbols and later rebind all uses + * unit-tests/test-cases/dead_strip-archive-weak: add test case -2008-01-14 Nick Kledzik +2008-11-03 Nick Kledzik - PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes - * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs - * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs + support increased branch range in Thumb-2 + * src/MachOReaderRelocatable.hpp: handle full branch range in addRelocReference() + * unit-tests/test-cases/branch-distance: added test case +2008-10-31 Devang Patel -2008-01-11 Nick Kledzik + Sqlite 3.5.4 built with lvm-gcc-4.2 -O4 fails regression test + * src/LTOReader.hpp: Use real atom scope when real atom is available. + Preserve globals while optimizing an executable. - PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" - * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions +2008-10-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support all encodings in getEncodedP() + + +----- Tagged ld64-90 + +2008-10-30 Nick Kledzik + + icc has dwarf unwind info that is different than gcc + * src/MachOReaderRelocatable.hpp: support more encodings in getEncodedP() + + +2008-10-23 Nick Kledzik + + build ld64 for x86_64 + * ld64.xcodeproj/project.pbxproj: add X86_64 to valid archs -2008-01-11 Nick Kledzik +2008-10-23 Nick Kledzik - * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list + * ld64.xcodeproj/project.pbxproj: use generated @$(DERIVED_FILE_DIR)/linker_opts for extra + linker options. This allows linker to be built if LTO headers and libs are missing. + + +2008-10-23 Nick Kledzik + + Linker warning not shown in the Xcode build log + * src/Options.cpp: add colon to format string in warning() + + +----- Tagged ld64-89.3 + +2008-10-24 Nick Kledzik + ld64-89 broke TOT OpenGL libProgrammability x86_64 build + * src/MachOReaderRelocatable.hpp: add cast in getEncodedP() -2008-01-11 Nick Kledzik - ld64(1) man page uses ambiguous term "suffix" - * doc/man/man1/ld.1: make meaning of "suffix" more explicit +----- Tagged ld64-89.2 + +2008-10-23 Nick Kledzik + + SnowLeopard: Libsystem built with ld64-89.1 causes crashes + * src/MachOReaderRelocatable.hpp: when FDE information causes __text atom to be split, make the + atoms follow-on pairs. + + +----- Tagged ld64-89.1 + +2008-10-22 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: for x86_64 __eh_frame force direct references -2008-01-11 Nick Kledzik +2008-10-21 Nick Kledzik - Obj-C Symbols in Leopard Can't Be Weak Linked - * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines - to dylibs to support Mac OS X 10.3.x dyld - + * src/ObjectDump.cpp: Use getContentType() to see if content type is a cstring + -2008-01-11 Nick Kledzik +----- Tagged ld64-89 - Unknown error with linker (dyld: unknown external relocation type) - * src/ld.cpp: fix crash when SO stabs are not balanced +2008-10-21 Nick Kledzik + + 10A180 with QT-1119 roots: iTunes and QuickTime cannot play back purchased videos + linker should not need .eh labels + * src/MachOWriterExecutable.hpp: use kCFIType to set section attributes + * src/MachOReaderRelocatable.hpp: use libunwind's CFITuple to parse __eh_frame content + * src/ld.cpp: Add adjustScope() phase instead of demoting scope within symboltable.add() + * unit-tests/test-cases/eh-stripped-symbols: added test case + + +----- Tagged ld64-88.1 + +2008-10-16 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT + * src/MachOWriterExecutable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT + + +2008-09-30 Nick Kledzik + + OBJC2: Reorder __DATA,__objc_* sections by writedness + * src/ld.cpp: change sorting order of Sections + + +2008-09-29 Nick Kledzik + + Executable produced by XCode 3.2 on 10.6 crashes on 10.3.9 + * src/MachOWriterExecutable.hpp: set objc_module_info_addr field of module table -2008-01-11 Devang Patel +----- Tagged ld64-88 - LTO does not work if expected output is a dynamic library - * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate - visibility info. +2008-09-25 Nick Kledzik -2000-01-10 Nick Kledzik + kexts need to be built as MH_BUNDLE mach-o files + * src/ld.cpp: use getUndefinedProxyAtom() with kKextBundle + * src/MachOFileAbstraction.hpp: add MH_KEXT_BUNDLE + * src/Options.cpp: support -kext for all architectures + * src/MachOWriterExecutable.hpp: support kKextBundle to make a bundle like kext + * unit-tests/test-cases/kext-basic: added test case + - __cls_refs section is losing S_LITERAL_POINTERS section type - * src/MachOWriterExecutable.hpp: special case __cls_refs section - * unit-tests/test-cases/objc-literal-pointers: add test case +2008-09-25 Nick Kledzik + ld invoking wrong ld_classic + * src/Options.cpp: first look for ld_classic relative to ld itself + -2008-01-03 Nick Kledzik +2008-09-25 Nick Kledzik - wrong EH information might be used - Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom - has kGroupSubordinate references to the other atoms in the group. If the signature atom - is coalesced away, the linker follows kGroupSubordinate references and throws away the - other members of the group. - * unit-tests/test-cases/eh-coalescing: added test case - * src/ld.cpp: added markDead() and use propagate to subordinates - * src/Architectures.hpp: added kGroupSubordinate - * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom - and if used, from .eh atom to its LSDA atom. - * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp + ld fails to link references from 32 bit code into 64 bit code + Desired 32-bit absolute relocation + * src/Architectures.hpp: add x86_64::kPointer32 + * src/MachOReaderRelocatable.hpp: support X86_64_RELOC_UNSIGNED with length=2 + * src/MachOWriterExecutable.hpp: support x86_64::kPointer32 + * unit-tests/test-cases/relocs-asm/relocs-asm.s: added 32-bit pointer tests + ------ Tagged ld64-81.4.1 +2008-09-25 Nick Kledzik + + Should be able to mark dylibs as auto-dead-dylib-strip + * src/Options.h: add fMarkDeadStrippableDylib + * src/MachOReaderDylib.hpp: check MH_DEAD_STRIPPABLE_DYLIB + * src/ObjectFile.h: add deadStrippable() + * src/MachOFileAbstraction.hpp: add MH_DEAD_STRIPPABLE_DYLIB + * src/Options.cpp: support -mark_dead_strippable_dylib + * src/MachOWriterExecutable.hpp: test reader->deadStrippable(), set MH_DEAD_STRIPPABLE_DYLIB + * doc/man/man1/ld.1: update man page + * unit-tests/test-cases/dead_strippable_dylib: added test case + -2007-12-19 Devang Patel +2008-09-25 Nick Kledzik - * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. + ER: Add -seg_page_size option + * src/Options.cpp: add -seg_page_size option + * src/MachOWriterExecutable.hpp: use new page size info when laying out segments + * doc/man/man1/ld.1: update man page -2007-12-19 Devang Patel - * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). +2008-09-24 Nick Kledzik + + -arch_errors_fatal not working + * src/ld.cpp: check fOptions.errorOnOtherArchFiles() + * src/Options.cpp: turn -arch_errors_fatal into fOptions.errorOnOtherArchFiles() + + +2008-09-24 Nick Kledzik + + CrashTracer: [USER] 1 crash in ld at ld: 0x5ce02 + * src/ld.cpp: abort if resolve() finds an unresolved reference, rather than allow a future crash -2007-12-19 Devang Patel - print LLVM LTO version number in verbose mode - * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. - * src/Options.cpp: Use printLLVMVersion() in verbose mode. +2008-09-24 Nick Kledzik -2007-12-19 Devang Patel + linker crashes linking X86-64 with -fwritable-strings + * src/MachOReaderRelocatable.hpp: handle unbound cfstring references + * unit-tests/test-cases/cfstring-coalesce: update test case + - print LLVM LTO version number in verbose mode - * src/Options.h: Add verbose() method to check fVerbose flag. - * src/LLVMReader.hpp: Print LLVM version string in verbose mode. +2008-09-24 Nick Kledzik + + ld64: bl out of range (-17147704 max is +/-16M) on ppc + * src/MachOWriterExecutable.hpp: tweak branch island regions to be every 14MB instead of 15MB ------ Tagged ld64-81.4 -2007-12-18 Devang Patel +2008-09-24 Nick Kledzik - * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. + -filelist fails with comma in path + * src/Options.cpp: in loadFileList() first try without special comma meaning + * unit-tests/test-cases/filelist/Makefile: update test case + ------ Tagged ld64-81.3 +2008-09-23 Nick Kledzik -2007-12-17 Nick Kledzik + nop not used when aligning functions in -r mode + * src/MachOWriterExecutable.hpp: change check for when to pad with nops to not test segment's name + - * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths +2008-09-23 Nick Kledzik + + "-pie can only be used when linking a main executable" should be a warning, not an error + * src/Options.cpp: make -pie on a dylib or bundle be a warning instead of an error + +2008-09-23 Nick Kledzik -2007-12-17 Devang Patel + * src/MachOReaderRelocatable.hpp: add warning if dwarf cannot be encoded as compact unwind + - * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to - dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. +2008-09-18 Nick Kledzik + * src/LTOReader.hpp: re-enable use of lto_codegen_debug_options() -2007-12-14 Nick Kledzik - gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) - * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs - * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static +2008-09-16 Nick Kledzik + ld does not always set S_CSTRING_LITERALS on __TEXT,__cstring + * src/MachOReaderRelocatable.hpp: add getContentType() to SymbolAtom + * src/MachOWriterExecutable.hpp: for x86_64 don't override named cstrings with LC* name + -2007-12-14 Devang Patel +2008-09-10 Nick Kledzik - Enable Link Time Optimization in Opal - * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. - * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. - * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. - * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. - - -2007-12-13 Nick Kledzik + * Options.cpp: add __crashreporter_info__ to communicate command line to crash reporter + * ld64.xcodeproj/project.pbxproj: leave local symbols in ld to provide better crash reports - SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... - * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly + +2008-09-08 Nick Kledzik + + 161569 GCC 4.2 - breakpoints no longer work for a large number of functions + * src/MachOReaderRelocatable.hpp: support DW_FORM_strp out-of-line strings when parsing line table + + +2008-09-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix compact unwind personality for dyld and -slow_stubs ------ Tagged ld64-81.2 +2008-08-29 Nick Kledzik + + -weak_library no longer forces uses to be weak_import + * src/MachOWriterExecutable.hpp: use fWeakImport on dylib to force proxy atoms into fWeakImportMap + * unit-tests/test-cases/weak_import-force: added test case + + +2008-08-29 Nick Kledzik + + linker should order __DATA segment to reduce dyld dirtied pages + * src/Options.cpp: add fOrderData and support -no_data_order + * src/ld.cpp: modify tweakLayout() to sort atoms with relocations to start of __data section + + +2008-08-27 Nick Kledzik + + * src/Options.cpp: back out + + +----- Tagged ld64-87.5 + +2008-08-26 Nick Kledzik + + some projects show _Unwind_Resume coming from libSystem.B.dylib + * src/Options.cpp: swap any early symlinks to libSystem with libgcc_s + + +----- Tagged ld64-87.4 + +2008-08-25 Nick Kledzik + + some projects show _Unwind_Resume coming from libSystem.B.dylib + * src/Options.cpp: swap any early libSystem with libgcc_s + + +2008-08-15 Nick Kledzik + + Unable to build ppc debug builds (linker out of range error) + * src/MachOWriterExecutable.hpp: in addPPCBranchIslands() look ahead so large atoms don't push out branch islands + + +----- Tagged ld64-87.3.1 + +2008-09-08 Nick Kledzik + + i386 dylibs have incorrect personality pointers when put in dyld shared cache + * src/MachOWriterExecutable.hpp: in addCrossSegmentRef() handle kImageOffset32 to __IMPORT segment + + +----- Tagged ld64-87.3 + +2008-08-09 Nick Kledzik + + work around compiler gcc_except_table alignment + * src/ObjectFile.h: change getLSDA() to return a reference instead of an atom + * src/MachOReaderRelocatable.hpp: special case __eh_frame 64-bit pointer diff relocations + * src/MachOWriterExecutable.hpp: track lsda offset when creating __unwind_info section + * src/UnwindDump.cpp: log when LDSA content does not start with 0xFF + +----- Tagged ld64-87.2 + +2008-08-07 Nick Kledzik + + 10A141: libuwind falls back to dwarf and makes whole system super slow + * src/MachOWriterExecutable.hpp: Fix sign extension bug with x86_64::kPointerDiff24 + * src/UnwindDump.cpp: warn about mangled LSDA entries when dumping unwind section + + +----- Tagged ld64-87.1 + +2008-08-03 Nick Kledzik + + * src/LTOReader.hpp: Don't use lto_codegen_debug_options until newer libLTO.dylib is available + + +----- Tagged ld64-87 + +2008-07-21 Nick Kledzik + + * src/Options.cpp: Always set fAutoOrderInitializers=false for dyld + + +2008-07-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix when regular vs compressed __unwind_info pages are generated + * src/UnwindDump.cpp: fix function name decoding in regular pages + + +2008-07-21 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: don't allow ld to build for x86_64 until libdtrace.dylib is available + + +2008-07-18 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't crash if debug_line section has no line table + + +2008-07-18 Nick Kledzik + + Duplicate probe firings in Security.framework + * src/LTOReader.hpp: optimize() now returns atoms optimized away + * src/ObjectFile.h: optimize() should return if it did anything + * src/ArchiveReader.hpp: pass through optimize() result + * src/ld.cpp: rework dtrace probe processing as a new pass to prevent double counting + + +2008-07-15 Nick Kledzik + + automatically order initializers to start of __TEXT + * src/Options.cpp: add -no_order_inits option + * src/MachOReaderRelocatable.hpp: merge __StaticInit into __text + * src/ObjectFile.h: add fAutoOrderInitializers + * src/ld.cpp: sort initializer to start of __text and terminators to end + * doc/man/man1/ld.1: add doc about -no_order_inits + * unit-tests/test-cases/init-order: add test case + +2008-07-15 Nick Kledzik + + Only add LC_SEGMENT_SPLIT_INFO to dylibs that might be in the shared cache + * src/MachOWriterExecutable.hpp: re-layout load commands after split-seg data computed + * src/Options.cpp: non-public install name will disable split-seg load command + + +2008-07-14 Nick Kledzik + + ld -r for x86_64 is changing visibility of cstring constants + * src/MachOWriterExecutable.hpp: force x86_64 cstring labels to be local in -r mode + * unit-tests/test-cases/cstring-label: added test case + + +2008-07-11 Nick Kledzik + + ld not adding updating LC_SEGMENT_SPLIT_INFO with __unwind_info section + * src/MachOWriterExecutable.hpp: run createSplitSegContent() after __unwind_info section is created + +2008-07-10 Nick Kledzik + + * src/LTOReader.hpp: improve missing symbol error message + + +2008-07-09 Nick Kledzik + + linker should order __DATA segment to reduce dyld dirtied pages + * src/ld.cpp: first phase, order sections + + +2008-07-08 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: remove "coal" sections when creating a final linked image + + +2008-07-08 Nick Kledzik + + ld: add support for mllvm LTO options + * src/Options.cpp: support -mllvm option + * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options + * src/ld.cpp: pass llvmOptions to optimize() + * src/Options.h: add fLLVMOptions + * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() + * src/ObjectFile.h: add llvmOptions parameter to optimize() + * unit-tests/test-cases/lto-llvm-options: add test case + + +2008-07-07 Nick Kledzik + + Linker fails with: 24-bit pointer diff out of range in unwind info in unwind info from... + * src/MachOWriterExecutable.hpp: fix when to fallback to uncompressed unwind info + + +2008-07-03 Nick Kledzik + + ld crash with gcc-4.0 code that uses a zero sized array + * src/MachOReaderRelocatable.hpp: handle zero size atom in a zero sized section + + +2008-07-03 Nick Kledzik + + ld crashes when bad ppc relocs are found + * src/MachOReaderRelocatable.hpp: change all missing PAIR warnings to errors + + +2008-07-02 Nick Kledzik + + when linking a kext the static linker should leave a pad in the headers to allow code signing + * src/MachOWriterExecutable.hpp: add padding for load commands in object files + * unit-tests/test-cases/code-signed-object-file: added test case + + +2008-07-02 Nick Kledzik + + LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype + * src/MachOWriterExecutable.hpp: correctly set segment size info in object files + * unit-tests/test-cases/no-object-symbols: add test case + + +2008-06-26 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: enable ld and rebase targets to build for x86_64 + * src/rebase.cpp: remove unused fRelocBase field that was not 64-bit clean + * src/MachOReaderRelocatable.hpp: fix getEncodedP() to be 64-bit clean + + +----- Tagged ld64-86.3 + +2008-06-17 Nick Kledzik + + * src/ld.cpp: fix loadUndefines() to double check undefine symbol was not already loaded + + +----- Tagged ld64-86.2 + +2008-06-14 Nick Kledzik + + * srd/ld.cpp: Add NULL check in getTentativesNames() + + +----- Tagged ld64-86.1 + +2008-06-06 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix header padding calculation for dyld + + +----- Tagged ld64-86 + +2008-06-04 Nick Kledzik + + * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + + +2008-06-04 Nick Kledzik + + * src/ObjectFile.h: add deadAtoms parameter to optimize() + * src/ld.cpp: ditto + * src/ArchiveReader.hpp: ditto + * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs + * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away + * unit-tests/test-cases/lto-weak-native-override: add test case + + +2008-06-04 Nick Kledzik + + LTO : 176.gcc and 177.mesa build failure at -O4 + * src/LTOReader.hpp: make sure internal is returned by getAtoms() + * unit-tests/test-cases/lto-archive-dylib: update test case + + +2008-06-03 Nick Kledzik + + fix for 5613343 need to search for definitions for common symbols is broken + * src/ld.cpp: modify loadUndefines() to check for undefines in all files and tentative definitions but only in archives + * src/machochecker.cpp: check for undefine symbols and external symbols with same name + * unit-tests/test-cases/tentative-and-archive: update test case + + +2008-06-03 Nick Kledzik + + linker produces wrong result for 16-bit call relocations + * src/MachOReaderRelocatable.hpp: properly parse i386 scattered relocs for word sized pc-rel vanilla + * src/MachOWriterExecutable.hpp: propery compute displacement for x86::kPCRel16 + * unit-tests/test-cases/relocs-asm: update test case with callw instructions + + +2008-06-03 Nick Kledzik + + Building kext x86_64 with unexported symbols file causes linking problems + * src/MachOWriterExecutable.hpp: better check when creating undefined proxy atoms + * unit-tests/test-cases/unexported_symbols_list-r: added test case + + +2008-06-02 Nick Kledzik + + S_CSTRING_LITERALS section type not preserved in executable + * src/ObjectFile.h: added ContentType + * src/MachOReaderRelocatable.hpp: set ContentType for anonymous string literals + * src/MachOWriterExecutable.hpp: set S_CSTRING_LITERALS if ContentType is kCStringType + * unit-tests/test-cases/cstring-custom-section: added test case + + +2008-06-02 Nick Kledzik + + linker should produce __unwind_info section in final linked images + * src/ld.cpp: sort __unwind_info then __eh_frame section to end of __TEXT + * src/Architectures.hpp: add kImageOffset32 and kPointerDiff24 + * src/ObjectFile.h: add compact unwind info support + * src/MachOReaderRelocatable.hpp: add compact unwind info support + * src/MachOFileAbstraction.hpp: add C++ wrappers for unwind section layout + * src/UnwindDump.cpp: new tool for dumping __unwind_info section + * src/MachOWriterExecutable.hpp: create __unwind_info section when needed + * src/ObjectDump.cpp: print unwind info + + +2008-06-02 Nick Kledzik + + * unit-tests/test-cases/llvm-integration: split out some test cases + * unit-tests/test-cases/lto-preload-pie: added + * unit-tests/test-cases/lto-archive-dylib: added + + +2008-05-30 Nick Kledzik + + * unit-tests: fixes to build all tests with with gcc-4.2 on SnowLeopard + + +2008-05-30 Nick Kledzik + + support -preload option to generate MH_PRELOAD binaries compatible with mtoc(1) and EFI + * src/ld.cpp: add entryPoint parameter to optimize() + * src/ArchiveReader.hpp: ditto + * src/ObjectFile.h: ditto + * src/LTOReader.hpp: use entryPoint parameter to optimize() + * src/Options.h: add kPreload and segment alignment + * src/Options.cpp: support -preload and -segalign + * src/MachOWriterExecutable.hpp: support kPreload and non-page aligned segments + + +2008-05-30 Nick Kledzik + + ld should warn if passed -r and also dylibs + * src/ld.cpp: check for spurious dylibs in Linker::addDylib() + + +----- Tagged ld64-85.6 + +2008-11-01 Nick Kledzik + + support increased branch range in Thumb-2 + * src/MachOWriterExecutable.hpp: in fixUpReferenceFinal() support new longer branch range + + +2008-11-01 Nick Kledzik + + ld warning: unknown option to -iphoneos_version_min, not 1.x or 2.x + * src/Options.cpp: In setIPhoneVersionMin() support 3.x + + +----- Tagged ld64-85.5 + +2008-09-17 Nick Kledzik + + vtable pointers can be missing thumb bit + * src/MachOWriterExecutable.hpp: Writer::fixUpReferenceFinal() OR in the 1 bit if the target + of a arm::kReadOnlyPointer is thumb. + + +----- Tagged ld64-85.4 + +2008-08-11 Nick Kledzik + + ld should ignore LD_PREBIND when processing a static archive + * src/MachOWriterExecutable.hpp: in setImportNlist() never use N_PBUD for object files + +----- Tagged ld64-85.3 + +2008-07-14 Nick Kledzik + + Prebinding busted in DTSB + * src/Options.cpp: check for libstdc++.6.0.[49] in seg_addr_table + + +----- Tagged ld64-85.2 + +2008-05-06 Nick Kledzik + + ARM ld should take W bit off of maxprot for __TEXT segment + * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments + + +2008-05-06 Nick Kledzik + + encryptable images may not be signable + * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section + + +----- Tagged ld64-85 (Xcode 3.1) + +2008-04-29 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include + + +2008-04-29 Nick Kledzik + + ld doesn't honor "rightmost" -syslibroot argument + * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + + +2008-04-29 Nick Kledzik + + GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files + * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment + * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment + + +2008-04-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better cpu subtype support + + +2008-04-14 Nick Kledzik + + ld64 has bad ARM branch island check + * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail + + +2008-04-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs + + +----- Tagged ld64-84.4 + +2008-04-10 Nick Kledzik + + SPEC2000/eon built with -mdynamic-no-pic won't run + * src/Architectures.hpp: added arm::kReadOnlyPointer + * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer + * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer + * src/machochecker.cpp: allow MH_PIE bit + * unit-tests/test-cases/switch-jump-table: added test cases + + +----- Tagged ld64-84.3 + +2008-04-09 Nick Kledzik + + -undefined dynamic_lookup busted + * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates + * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup + + +----- Tagged ld64-84.2 + +2008-04-04 Nick Kledzik + + * src/ld.cpp: don't add .eh symbols to symbol table in -r mode + * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing + + +----- Tagged ld64-84.1 + +2008-03-28 Nick Kledzik + + ld should prefer architecture-specific variant over generic in fat object file + * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture + * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files + * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc + + +----- Tagged ld64-84 + +2008-03-28 Nick Kledzik + + * src/LTOReader.hpp: don't print lto version, if lto is unavailable + + +2008-03-26 Nick Kledzik + + Add LD_WARN_COMMONS to BigBear builds + * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file + + +2008-03-26 Nick Kledzik + + Need encryption tag in mach-o file + linker should adjust arm final linked images so __text is never on the same page as the load commands + * src/MachOFileAbstraction.hpp: add support for encryption_info_command + * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption + * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom + * src/machochecker.cpp: validate LC_ENCRYPTION_INFO + + +2008-03-25 Nick Kledzik + + ld64 does not recognize LLVM bitcode archive files + * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp + * src/ArchiveReader.hpp: sniff each member and instantiate correct reader + * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader + * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp + * unit-tests/test-cases/llvm-integration: added test case + + +2008-03-25 Nick Kledzik + + ld64 should switch to new libLTO.dylib interface + Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc + * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface + * unit-tests/test-cases/llvm-integration: update and comment + * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib + * src/ld.cpp: rework and simplify Linker::optimize() + * src/ObjectDump.cpp: Add -nm option + + +2008-03-25 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem + * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem + + +2008-03-24 Nick Kledzik + + Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 + * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. + + +2008-03-21 Nick Kledzik + + * src/Options.cpp: warn if -seg1addr value is not page aligned + + +2008-03-21 Nick Kledzik + + Move ARM support outside of __OPEN_SOURCE__ + * src/ld.cpp: remove __OPEN_SOURCE__ around arm support + * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support + * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check + * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support + * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h + + +----- Tagged ld64-83.2 + +2008-03-15 Nick Kledzik + + ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results + * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files + * unit-tests/test-cases/objc-exported_symbols_list: added test case + + +----- Tagged ld64-83.1 + +2008-03-14 Nick Kledzik + + -iphone_version_min ==> -iphoneos_version_min + * src/Options.cpp: support -iphoneos_version_min as well + + +----- Tagged ld64-83 + +2008-03-10 Nick Kledzik + + ld needs to strip iphone_version_min option if invoking ld_classic + * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic + + +2008-03-04 Nick Kledzik + + ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) + * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs + * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework + * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools + * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() + * src/ld.cpp: pass lazy helper atom to writer + * doc/man/man1/ld.1: document new options + * unit-tests/test-cases/lazy-dylib-objc: add test case + * unit-tests/test-cases/lazy-dylib: add test case + + +----- Tagged ld64-82.7 + +2008-03-07 Nick Kledzik + + duplicate symbol literal-pointer@__OBJC@__message_refs@... + * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak + * unit-tests/test-cases/objc-selector-coalescing: added test case + + +----- Tagged ld64-82.6 + +2008-03-04 Nick Kledzik + + ld crashes building XsanFS for Snow Leopard Builds + * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() + * unit-tests/test-cases/tentative-and-archive: added test case + +2008-03-04 Nick Kledzik + + ld64 should not force building with gcc 4.0 + * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 + + +2008-02-29 Nick Kledzik + + Simulator frameworks are being build split-seg and not prebound + * src/Options.cpp: only splitseg if prebound + + +2008-02-29 Nick Kledzik + + Linker should not make GSYM debug note for .objc_category_* symbols + * src/ld.cpp: suppress GSYM debug notes for absolute symbols + * unit-tests/test-cases/objc-category-debug-notes: added test case + + +2008-02-29 Nick Kledzik + + non-ASCII CFString support is broken + * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring + * unit-tests/test-cases/cfstring-utf16: add test case + + +2008-02-25 Nick Kledzik + + ld -r -x + * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels + + +----- Tagged ld64-82.5 + +2008-02-12 Nick Kledzik + + x86_64: -stack_size failure when large __bss is used + * src/ld.cpp: only move section already in __DATA segment to new __huge section + * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section + + +----- Tagged ld64-82.4 + +2008-02-06 Nick Kledzik + + comdat warnings with ld -r of C++ .o files + * unit-tests/test-cases/eh-coalescing-r: added test case + * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static + + +2008-02-06 Devang Patel + + LTO of Bom framework with -dead_strip causes ld(1) crash + * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. + * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. + * unit-tests/test-cases/llvm-integration/a15.c: New. + * unit-tests/test-cases/llvm-integration/b15.c: New. + * unit-tests/test-cases/llvm-integration/c15.c: New. + +2008-02-05 Nick Kledzik + + * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used + +----- Tagged ld64-82.3 + +2008-02-04 Nick Kledzik + + ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves + * src/ObjectFile.h: add 10.6 + * src/Options.cpp: add 10.6 support + * src/MachOReaderDylib.hpp: recognize $os10.6$ + + +----- Tagged ld64-82.2 + +2008-01-30 Devang Patel + + Can't build 64-bit Intel binaries with LTO + ld64 fails to build with llvm-gcc-4.2 + * src/LLVMReader.hpp: Fix character count typo in strncmp call. + Use const char * to initialize temp. string. + * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction + instead of hard coding /Developer. + +----- Tagged ld64-82.1 + +2008-01-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs + + +2008-01-22 Nick Kledzik + + ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files + * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs + * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs + * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files + + +----- Tagged ld64-82 + +2008-01-18 Nick Kledzik + + Bad grammar used in ld warning: cannot exported hidden symbol + * src/ld.cpp: fix typo in warning string + + +2008-01-16 Nick Kledzik + + Bundle Loader does not work anymore when loader is a bundle + ld warns of incorrect architecture when linking a bundle to a bundle + * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages + * unit-tests/test-cases/bundle_loader: update test case + + +2008-01-16 Nick Kledzik + + ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) + * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S + + +2008-01-16 Nick Kledzik + + if ld crashes while writing output file, it should delete the half written file + * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete + output file on failure. + + +2008-01-16 Devang Patel + + * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. + + +2008-01-16 Nick Kledzik + + GC-supported library can't be linked into GC-required executable + * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and + allow gc-compatible code to be linked into anything. + * unit-tests/test-cases/objc-gc-checks: update test case + + +2008-01-15 Nick Kledzik + + no debug notes for custom named data + * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore + * unit-tests/test-cases/dwarf-debug-notes: update test case + +----- Tagged ld64-81.5 + +2008-01-14 Devang Patel + + llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 + * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references + after optimization. + * src/ld.cpp: Resolve additional unbounded references after optimization. + + +2008-01-14 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes + * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs + * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs + + +2008-01-11 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" + * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing + * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions + + +2008-01-11 Nick Kledzik + + * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list + + +2008-01-11 Nick Kledzik + + ld64(1) man page uses ambiguous term "suffix" + * doc/man/man1/ld.1: make meaning of "suffix" more explicit + + +2008-01-11 Nick Kledzik + + Obj-C Symbols in Leopard Can't Be Weak Linked + * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines + to dylibs to support Mac OS X 10.3.x dyld + + +2008-01-11 Nick Kledzik + + Unknown error with linker (dyld: unknown external relocation type) + * src/ld.cpp: fix crash when SO stabs are not balanced + + +2008-01-11 Devang Patel + + LTO does not work if expected output is a dynamic library + * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate + visibility info. + +2000-01-10 Nick Kledzik + + __cls_refs section is losing S_LITERAL_POINTERS section type + * src/MachOWriterExecutable.hpp: special case __cls_refs section + * unit-tests/test-cases/objc-literal-pointers: add test case + + +2008-01-03 Nick Kledzik + + wrong EH information might be used + Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom + has kGroupSubordinate references to the other atoms in the group. If the signature atom + is coalesced away, the linker follows kGroupSubordinate references and throws away the + other members of the group. + * unit-tests/test-cases/eh-coalescing: added test case + * src/ld.cpp: added markDead() and use propagate to subordinates + * src/Architectures.hpp: added kGroupSubordinate + * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom + and if used, from .eh atom to its LSDA atom. + * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp + +----- Tagged ld64-81.4.1 + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. + * src/Options.cpp: Use printLLVMVersion() in verbose mode. + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/Options.h: Add verbose() method to check fVerbose flag. + * src/LLVMReader.hpp: Print LLVM version string in verbose mode. + +----- Tagged ld64-81.4 + +2007-12-18 Devang Patel + + * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. + +----- Tagged ld64-81.3 + +2007-12-17 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths + + +2007-12-17 Devang Patel + + * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to + dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. + + +2007-12-14 Nick Kledzik + + gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) + * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs + * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static + + +2007-12-14 Devang Patel + + Enable Link Time Optimization in Opal + * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. + * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. + * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. + * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. + + +2007-12-13 Nick Kledzik + + SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... + * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly + +----- Tagged ld64-81.2 + + + +2007-12-07 Nick Kledzik + + support 8-bit relocations for i386 + * src/Architectures.hpp: add kPCRel8 + * src/MachOReaderRelocatable.hpp: support 8-bit pc-rel relocations for intel + * src/MachOWriterExecutable.hpp: support 8-bit pc-rel relocations for intel + * unit-tests/test-cases/relocs-asm: add test cases + + +----- Tagged ld64-81.1 + +2007-12-06 Nick Kledzik + + * src/MachOReaderDylib.hpp: rework cycle detection to remove some false positives + + +2007-12-05 Nick Kledzik + + Duplicate probe firings in Security.framework + * src/ld.cpp: check dtrace probe sites are not in fDeadAtoms before using + * unit-tests/test-cases/dtrace-static-probes-coalescing: add test case + + +2007-12-05 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix CFString coalescing to work with -fwritable-strings + * unit-tests/test-cases/cfstring-coalesce: add -fwritable-strings to test case + + +----- Tagged ld64-81 + +2007-11-15 Nick Kledzik + + ld64 should support runtime text relocations + * src/MachOWriterExecutable.hpp: add generatesLocalTextReloc() and generatesExternalTextReloc() + * src/Options.cpp: process -read_only_relocs option + * src/Options.h: add allowTextRelocs() and warnAboutTextRelocs() + * src/MachOReaderRelocatable.hpp: add hasLongBranchStubs() + * src/machochecker.cpp: allow relocs in read only segments, if section flags are set + * unit-tests/test-cases/read-only-relocs: update test case + + +2007-11-08 Devang Patel + + * ld64.xcodeproj/project.pbxproj: add new build phase "build configure.h" for + ld target. + * src/ld.cpp: Include "configure.h" + + +----- Tagged ld64-80.11 + +2008-02-12 Nick Kledzik + + Wrong section name for objc info for ARM when OBJC2 is used + * src/MachOWriterExecutable.hpp: switch segment/section name for ARM objc2 image info + +----- Tagged ld64-80.10 + +2008-02-11 Nick Kledzik + + ld64 does not support -aspen_version_min 2.0 + * src/Options.cpp: allow 2.x for -aspen_version_min + + +2008-02-11 Nick Kledzik + + ld_classic: unknown flag: -aspen_version_min + * src/Options.cpp: change -aspen_version_min x.x to -macosx_version_min 10.5 when invoking ld_classic + + +----- Tagged ld64-80.9 + +2008-01-29 Nick Kledzik + + -iphone_version_min ==> -aspen_version_min + * src/Options.cpp: support -aspen_version_min + + +----- Tagged ld64-80.8 + +2008-01-10 Nick Kledzik + + * src/Options.cpp: support transition to new objc ABI for ARM by allowing old .objc_class_name_* + style names in export files and map them to new _OBJC_CLASS_$_ style names. + + +----- Tagged ld64-80.7 + +2008-01-02 Nick Kledzik + + BigBear5A18 isn't fully prebound + * src/Options.cpp: make fNeedsModuleTable true for arm + +----- Tagged ld64-80.6 + +2007-11-30 Nick Kledzik + + -iphone_version_min + * src/Options.cpp: handle -iphone_version_min option + + +----- Tagged ld64-80.5 + +2007-11-26 Nick Kledzik + + need to special case some dylibs in seg_addr_table + * src/Options.cpp: retry seg_add_table lookup for a couple of unusual dylibs + + +----- Tagged ld64-80.4 + +2007-11-06 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix parsing of external and scattered thumb branch22 relocs + * unit-tests/test-cases/thumb-blx: add test case to keep blx issues from coming back + +----- Tagged ld64-80.3 + +2007-11-03 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: remove recalc of dstAddr which could cause thumb branches to be +2 + * src/MachOWriterExecutable.hpp: remove incorrect test for relocateableExternal + +----- Tagged ld64-80.2 + +2007-11-01 Nick Kledzik + + * src/ld.cpp: hack my own prototype for log2() until math.h is cleaned up + + +----- Tagged ld64-80.1 + +2007-11-01 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: add HEADER_SEARCH_PATHS for cross builds + * src/ld.cpp: temporarily disable LLVM_SUPPORT + * src/MachOWriterExecutable.hpp: Don't use CC_MD5() directly + + +2007-10-26 Nick Kledzik + + Cannot build with libm_static.a statically linked + * src/MachOWriterExecutable.hpp: Fix makesExternalRelocatableReference() for -r -d case + * unit-tests/test-cases/tentative-to-real-hidden: add test case + + +----- Tagged ld64-80 + +2007-10-24 Nick Kledzik + + linker should probably warn about trying to export a hidden symbol + * src/ld.cpp: if using -exported_symbols_list check each hidden atom as it is added to symbol table + * src/Options.h,.cpp: add hasExportMaskList() + * unit-tests/test-cases/exported_symbols_list-hidden: added test case + + +2007-10-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: keep old style dtrace probes externel for kernel builds + + +2007-10-23 Nick Kledzik + + unify error and warning messages + -w should suppress warnings + * src/ld.cpp: use warning() function + * src/Options.h: remove emitWarnings() + * src/MachOReaderDylib.hpp: use warning() function + * src/MachOReaderRelocatable.hpp: use warning() function + * src/Options.cpp: use and implement warning() + * src/MachOWriterExecutable.hpp: use warning() function + * unit-tests/test-cases/visibility-warning: verify -w suppresses warnings + + +2007-10-23 Devang Patel + + * src/ld.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/LLVMReader.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/MachOReaderDylib.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/ObjectFile.h: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/MachOReaderRelocatable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check + * src/MachOWriterExecutable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/ObjectDump.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. + + +2007-10-22 Nick Kledzik + + * src/Options.cpp: add support for LD_DEAD_STRIP and LD_WARN_COMMONS + * src/MachOReaderRelocatable.hpp: fix problem with -dead_strip of ObjC literal pointers + + +2007-10-22 Nick Kledzik + + * src/Options.cpp: have -static arm code link with ld_classic (for now) + + +2007-10-22 Nick Kledzik + + Recognize all arm architectures + * src/MachOReaderRelocatable.hpp: add support for all ARM sub-types + * unit-tests/test-cases/cpu-sub-types: add test cases for all combinations of ARM sub-types + + +2007-10-19 Nick Kledzik + + * src/*: merge in arm support + * unit-tests/test-cases/*: fix to work for arm and thumb + +----- Tagged ld64-79 + +2007-10-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: if -r mode, always set custom alignment (SET_COMM_ALIGN) on common symbols + * unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile: fix warning + * unit-tests/test-cases/static-executable/Makefile: fix spurious failure + + +2007-10-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix edge case in branch island generation + + +2007-10-12 Nick Kledzik + + Add option to create old, slow stubs for i386 + * src/ObjectFile.h/.cpp: support -read_only_stubs + * src/MachOWriterExecutable.hpp: enhance StubAtom to support old style __symbol_stub/__la_symbol_ptr stubs + * unit-tests/test-cases/slow-x86-stubs: add test case + + +2007-10-12 Nick Kledzik + + ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + * src/Options.cpp: in findFileUsingPaths() don't search for embedded dylibs + * unit-tests/test-cases/indirect-path-search/Makefile: added case for a dylib embedded in a framework + + +2007-10-11 Nick Kledzik + + add option to disable implicit load commands for indirectly used public dylibs + * src/Options.cpp: add support for -no_implicit_dylibs + * src/ObjectFile.h: add fImplicitlyLinkPublicDylibs + * src/MachOReaderDylib.hpp: test fImplicitlyLinkPublicDylibs before hoisting an implicitly linked dylib + * unit-tests/test-cases/implicit_dylib: add test case + + +2007-10-11 Nick Kledzik + + -interposable_list + * src/Options.h/cpp: Add fInterposeList and fInterposeMode to support -interposable_list + * src/MachOWriterExecutable.hpp: pass symbol name to fOptions.interposable() + * unit-tests/test-cases/interposable_list: add test case + + +2007-10-10 Nick Kledzik + + If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + * src/MachOWriterExecutable.hpp: automatically use LC_LOAD_WEAK_DYLIB if all symbols used from a dylib are weak_import + * unit-tests/test-cases/weak_dylib: added test case + + +2007-10-10 Nick Kledzik + + linker does not error when dylib ordinal exceeds 250 + * src/MachOWriterExecutable.hpp: error out if ordinals exceed max allowed + + +2007-10-10 Nick Kledzik + + overriding 'operator new' or 'operator delete' fails if no weak symbols are present + * src/ld.cpp: at end of checkUndefines() search dylibs for weak versions of any global external symbols + * src/ObjectFile.h: add hasWeakExternals() method to Reader + * src/MachOReaderDylib.hpp: implement hasWeakExternals() method in Reader + * src/ExecutableFile.h: add overridesDylibWeakDefines parameter to write() + * src/MachOWriterExecutable.hpp: use overridesDylibWeakDefines parameter to write() + * unit-tests/test-cases/operator-new: add test case + + +2007-10-05 Nick Kledzik + + No warning about tentative definition conflicting with dylib definition + .comm variables in shared library, worked with XCode 2.4.1, broken with XCode 3? + * src/ld.cpp: at end of checkUndefines() verify if any remaining commons conflict with dylibs + * doc/man/man1/ld.1: document -commons and -warn_commons options + * unit-tests/test-cases/tentative-and-dylib: added test case + + +2007-10-05 Nick Kledzik + + NS/CFString constants are not dead strippable + * src/MachOReaderRelocatable.hpp: break up __cfstring section into one atom per cfstring, make them coalesable + * unit-tests/test-cases/cfstring-coalesce: added test case + + +2007-10-05 Nick Kledzik + + Dead stripping + exported symbols list using wildcards doesn't seem to do the right thing + * src/Options.cpp/h: add hasWildCardExportRestrictList() + * src/ld.cpp: if dead stripping code and have wildcard exports, add all global atoms matching wildcards as roots + * unit-tests/test-cases/exported-symbols-wildcards-dead_strip: added test case + + +2007-10-04 Nick Kledzik + + ld shouldn't search /Network/Library/Frameworks by default + * src/Options.cpp: remove /Network/Library/Frameworks/ from default search path + * doc/man/man1/ld.1: document the change + + +2007-10-04 Nick Kledzik + + all binaries should get LD_UUID load commands, not just those with DWARF symbols + * src/ld.cpp: default fCreateUUID to be true for non object file output types + * unit-tests/test-cases/no-uuid/Makefile: update test case to match new rules + + +----- Tagged ld64-78 + +2007-09-27 Nick Kledzik + + range check load commands + * src/MachOReaderDylib.hpp: check that load commands all fit in load command size from header + * src/MachOReaderRelocatable.hpp: check that load commands all fit in load command size from header + + +2007-09-27 Nick Kledzik + + Xc8M2540a: ld64 crashes when linking Pascal program + * src/ld.cpp: fix findAtomAndOffset() to handle where there are no function atoms + + +2007-09-27 Nick Kledzik + + ADOBE Xcode 3: ld -dead_strip does not work with -init from an archive + * src/ld.cpp: add bool parameter to entryPoint() so -init atom not looked for too soon + * unit-tests/test-cases/dead_strip-init-archive: added test case + + +2007-09-26 Nick Kledzik + + Spurious link warnings for inline members of C++ template classes + * src/ld.cpp: check definition kinds before warning about visibility mismatches + * unit-tests/test-cases/visibility-warning: added test case + + +2007-09-26 Nick Kledzik + + an empty .o file with zero load commands will crash linker + * src/MachOReaderRelocatable.hpp: have Reader constructor return early of no load commands + * unit-tests/test-cases/empty-object: added test case + + +2007-09-26 Nick Kledzik + + 9a527: ppc64 branch islands fail with 4GB pagezeo + * src/MachOWriterExecutable.hpp: start range calculations at start of __text not at zero. + + +----- Tagged ld64-77 (Xcode 3.0) + +2007-07-23 Nick Kledzik + + Kernel is linked with some global symbols unsorted + * src/MachOWriterExecutable.hpp: Add NListNameSorter to allow global atoms and extra labels to be sorted + + +2007-07-20 Nick Kledzik + + Can't do objc_msgSendSuper dispatches after loading a Fix&Continue bundle + * src/MachOWriterExecutable.hpp: when calculating what kind of reloc to use, never use an + external reloc to reference 32-bit ObjC symbols. + + +2007-07-20 Nick Kledzik + + Runtime crash with ICC math library on Leopard + * src/MachOReaderRelocatable.hpp: detect if section starts with a symbol that is not + aligned to section and correct it. + + +----- Tagged ld64-76 + +2007-06-29 Nick Kledzik + + export hiding does not work for frameworks + * src/MachOReaderDylib.hpp: fix checks in isPublicLocation() + * unit-tests/test-cases/symbol-moving: update to test frameworks as well as dylibs + + +2007-06-27 Nick Kledzik + + linker should use undefines from flat dylibs when linking a main flat + * src/ObjectFile.h: added fLinkingMainExecutable + * src/Options.cpp: set up fLinkingMainExecutable + * src/MachOReaderDylib.hpp: when linking a main executable for flat namespace, the reader for + any loaded flat namespace dylib will have a new atoms that has references to all undefined + symbols in the dylib + * unit-tests/test-cases/flat-indirect-undefines: added test case + * doc/man/man1/ld.1: update man page to describe when dylib undefines are used + + +2007-06-27 Nick Kledzik + + OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found + * src/MachOReaderDylib.hpp: add assertNoReExportCycles() method + * unit-tests/test-cases/dylib-re-export-cycle: added test case + + +2007-06-27 Nick Kledzik + + ld64 has slightly different warning message formats than the old ld + * src/ld.cpp: standardize all warning messages to start with "ld: warning" + * src/MachOWriterExecutable.hpp: ditto + * src/MachOReaderRelocatable.hpp: ditto + * src/MachOReaderDylib.hpp:ditto + + +2007-06-26 Nick Kledzik + + -dead_strip can cause duplicate external commons + * src/ld.cpp: don't use discarded coalesced global atoms as dead strip roots + * src/machochecker.cpp: error if duplicate external symbols + * unit-tests/test-cases/commons-coalesced-dead_strip: added test case + + +2007-06-26 Nick Kledzik + + update man page that linker does not search indirect libraries with two-level namespace + * doc/man/man1/ld.1: add new "Indirect dynamic libraries" section to man page + + +2007-06-26 Nick Kledzik + + Xc9A466: Exports file cannot use Mac line ends + * src/Options.cpp: check for \r or \n when parsing .exp files + * unit-tests/test-cases/exported_symbols_list-eol: added test case + + +----- Tagged ld64-75 + +2007-05-31 Nick Kledzik + + Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB + * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 + + +----- Tagged ld64-74.5 + +2007-05-31 Nick Kledzik + + set OSO timestamp to zero for when building in buildit + * src/ld.cpp: check for RC_RELEASE and if exists set all OSO timestamps to zero + + +2007-05-30 Nick Kledzik + + BUILD_STABS now causes ld of xnu to bus error + * src/ld.cpp: Change || to && in collectStabs() + + +----- Tagged ld64-74.4 + +2007-05-18 Nick Kledzik + + static probes don't work with libraries in dyld shared cache + * src/OpaqueSection.hpp: the __TEXT segment is executable + + +----- Tagged ld64-74.3 + +2007-05-16 Nick Kledzik + + ppc: linker adds stubs to cstring references + * src/MachOWriterExecutable.hpp: update ppc stubableReference() to only allow high/low references + to be stubed if they reference a symbol in some other dylib. + * unit-tests/test-cases/stub-generation: added test case + + +2007-05-16 Nick Kledzik + + ppc64: need to make LOCAL indirect symbol table entry for now local symbol + * src/MachOWriterExecutable.hpp: factored local tests into indirectSymbolIsLocal() + * unit-tests/test-cases/non-lazy-r: added test case + + +2007-05-15 Nick Kledzik + + ld64 drops fix&continue bit in __OBJC, __image_info. + * src/MachOReaderRelocatable.hpp: implement objcReplacementClasses() + + +2007-05-15 Nick Kledzik + + support __image_info in __DATA segment for 64-bits + * src/MachOReaderRelocatable.hpp: use strncmp() for __objc_imageinfo since it is 16 bytes long + * src/MachOWriterExecutable.hpp: specialize segment/section names for synthesized objc image info section + + +2007-05-15 Nick Kledzik + + * unit-tests/include/common.makefile: set COMPILER_PATH so harness works with latest compiler + + +----- Tagged ld64-74.2 + +2007-05-11 Nick Kledzik + + ld64-74.1 breaks libstdc++ DejaGnu test (G5 only) + * src/MachOWriterExecutable.hpp: don't stub a reference if the target offset is non-zero + + +----- Tagged ld64-74.1 + +2007-05-09 Nick Kledzik + + * src/Options.h: add emitWarnings() + * src/Options.cpp: wire up -w to emitWarnings() + + +2007-05-09 Nick Kledzik + + ld64 won't link wine (regression from Tiger) + * src/Architectures.hpp: add x86::kPointerDiff16 and x86::kPCRel16 + * src/MachOReaderRelocatable.hpp: add support to parse new relocs + * src/MachOWriterExecutable.hpp: add support fo new relocs + + +2007-05-08 Nick Kledzik + + need way for ld and dyld to see different exported symbols in a dylib + * src/MachOReaderDylib.hpp: update parse and use $ld$ symbols + * src/Options.h: move VersionMin to ReaderOptions + * src/ObjectFile.h: move VersionMin to ReaderOptions + * src/Options.cpp: move VersionMin to ReaderOptions + * src/MachOWriterExecutable.hpp: move VersionMin to ReaderOptions + * unit-tests/test-cases/symbol-moving: added test case + + +2007-05-03 Nick Kledzik + + typo in error message for linking -pie + * src/MachOWriterExecutable.hpp: fix typo in error messages + + +----- Tagged ld64-74 + +2007-05-03 Nick Kledzik + + ld64 can't find @executable _path relative dylibs from our umbrella frameworks + ld64 should handle linking against dylibs that have @loader_path based dylib load commands + * src/ObjectFile.h: add from parameter to findDylib() + * src/MachOReaderDylib.hpp: supply from parameter to findDylib() + * src/ld.cpp: use from parameter for @loader_path substitution in findDylib() + * unit-tests/test-cases/re-export-relative-paths: added test case + + +2007-05-02 Nick Kledzik + + * src/ObjectFile.h: add fLogObjectFiles and fLogAllFiles + * src/Options.cpp: hook up -t to fLogAllFiles and -whatsloaded to fLogObjectFiles + * src/MachOReaderDylib.hpp: log if fLogAllFiles + * src/MachOReaderRelocatable.hpp: log if fLogObjectFiles or fLogAllFiles + * src/MachOReaderArchive.hpp: log if fLogAllFiles + * doc/man/man1/ld.1: update man page + + +2007-05-02 Nick Kledzik + + typo in message, frameowrk + * src/Options.cpp: fix typo + + +2007-05-01 Nick Kledzik + + "ld" man page is missing the description for many options + * doc/man/man1/ld.1: add documentation on all obsolete options + + +2007-05-01 Nick Kledzik + + ld doesn't handle -mlong-branch .o files that have had local symbols stripped + warning about dwarf line info with -mlong-branch + * src/MachOReaderRelocatable.hpp: don't lop -mlong-branch stubs off end of functions + * src/MachOWriterExecutable.hpp: allow code references besides BR24 to be stubable + + +2007-04-30 Nick Kledzik + + unable to link VTK because __textcoal_nt too large + * src/MachOReaderRelocatable.hpp: when doing a final link map __textcoal_nt to __text + + +2007-04-30 Nick Kledzik + + ld does not report error when -r is used and exported symbols are not defined. + ld leaves global common symbols not in exported symbols list. + * src/ld.cpp: stop special casing -r mode in checkUndefines() + * src/MachOWriterExecutable.hpp: don't create proxy atom in -r mode if it is supposed to be exported. + mark tentative definitions are private extern in -r mode even without -keep_private_externs + * unit-tests/test-cases/exported_symbols_list-r: added test case + + +2007-04-27 Nick Kledzik + + ld should keep looking when it finds a weak definition in a dylib + * src/ld.cpp: modified addJustInTimeAtoms() to keep looking when a weak defintion is found + * unit-tests/test-cases/weak-def-ordinal: added test case + + +2007-04-27 Nick Kledzik + + better error message for indirect dylibs missing required architecture + * src/ld.cpp: when loading indirect dylib add path to error messages + + +2007-04-25 Nick Kledzik + + the i386 slice of dyld does not need __IMPORT segment + * src/ObjectFile.h: add fForDyld + * src/Options.cpp: set up fForDyld + * src/MachOReaderRelocatable.hpp: if fForDyld, change __IMPORT segment to __DATA + * src/MachOWriterExecutable.hpp: recognize __DATA/__pointers in dyld as a non-lazy section + + +2007-04-24 Nick Kledzik + + ppc64: need to make LOCAL indirect symbol table entry for now local symbol + * src/MachOWriterExecutable.hpp: use INDIRECT_SYMBOL_LOCAL for any non-global symbol + * unit-tests/test-cases/strip_local: update test case + + +2007-04-24 Nick Kledzik + + ld64 -sectorder and -order_file files don't accept white space following the : + * src/Options.cpp: prune white space after colon and before symbol name + * unit-tests/test-cases/order_file: update test case to have a space after the colon + + +2007-04-24 Nick Kledzik + + ld64 corrupts debug symbol table entries, nm doesn't print them + * src/MachOWriterExecutable.hpp: properly set ilocalsym in module table + + +2007-04-24 Nick Kledzik + + support __image_info in __DATA segment for 64-bits + * src/MachOReaderRelocatable.hpp: look for new objc info section name too + + +2007-04-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix -non_global_symbols_strip_list to work with -r + * unit-tests/test-cases/local-symbol-partial-stripping: update test case + + + +----- Tagged ld64-73.7 + +2007-05-10 Nick Kledzik + + can't use dtrace static probes in x86_64 dylib + * src/MachOWriterExecutable.hpp: x86_64:kPointerDiff32 is ok in shared region + * unit-tests/test-cases/dtrace-static-probes: update to build dylib too + + +2007-05-09 Nick Kledzik + + 9A430: using -dead_strip with static dtrace probes causes ld to crash + * src/ld.cpp: fix markLive() to look at right name in dtrace probe refernce + * unit-tests/test-cases/dtrace-static-probes: added -dead_strip case + + +----- Tagged ld64-73.6 + +2007-04-17 Nick Kledzik + + Add options to do partial stripping of local symbols + * src/MachOWriterExecutable.hpp: use fOptions.keepLocalSymbol() + * src/Options.cpp: implement -non_global_symbols_no_strip_list and -non_global_symbols_strip_list + * src/Options.h: replace stripLocalSymbols() with localSymbolHandling() and keepLocalSymbol() + * doc/man/man1/ld.1: document -non_global_symbols_no_strip_list and -non_global_symbols_strip_list + * unit-tests/test-cases/local-symbol-partial-stripping: added test case + + +----- Tagged ld64-73.5 + +2007-04-17 Nick Kledzik + + ld64-73.3 XBS logging incorrectly reporting "direct" dynamic libraries + * src/ld.cpp: restore direct vs indirect library for LD_TRACE_DYLIBS logging + + +2007-04-16 Nick Kledzik + + data initialized to a weak imported symbol is missing relocation + * src/MachOWriterExecutable.hpp: check for A::kPointerWeakImport in buildExecutableFixups() + * unit-tests/test-cases/weak_import: updated test case to catch this problem + + +2007-04-13 Nick Kledzik + + Support -U + * src/MachOWriterExecutable.hpp: create proxies for -U symbols + * src/Options.cpp: process -U + * src/Options.h: add allowedUndefined() and someAllowedUndefines() + * src/ld.cpp: create proxies for -U symbols + * doc/man/man1/ld.1: document -U and -undefined options + * unit-tests/test-cases/undefined-dynamic-lookup: added test case + + +----- Tagged ld64-73.4 + +2007-04-12 Nick Kledzik + + ld changes needed to support read-only DOF + * src/Options.cpp: remove -read_only_dof + * src/Options.h: remove fReadOnlyDOFs + * src/ld.cpp: only generate read-only DOF sections + + +----- Tagged ld64-73.3.1 + +2007-04-13 Nick Kledzik + + -framework vecLib -framework Accelerate causes bad ordinals + * src/MachOWriterExecutable.hpp: fix bug optimizeDylibReferences() when there are two readers with same install name + + +----- Tagged ld64-73.3 + +2007-04-03 Nick Kledzik + + * src/ld.cpp: read-only-dofs should use 32-bit offsets for x86_64 + * src/MachOReaderDylib.hpp: if "public" re-export is not marked implict, still mark it as re-exported + + +2007-04-02 Nick Kledzik + + if replacement file for -dylib_file is missing, warn instead of error + * src/ld.cpp: a try/catch to turn -dylib_file error into a warning. + * unit-tests/test-cases/dylib_file-missing: add test case + * doc/man/man1/ld.1: update man page about -dead_strip_dylibs + + +----- Tagged ld64-73.2 + +2007-03-31 Nick Kledzik + + ld64-73: atom sorting error with duplicate zero sized bss symbols + * src/MachOReaderRelocatable.hpp: suppress warning on sorting zero size zero fill atoms + +2007-03-31 Nick Kledzik + + ld64-73 fails anything linking with -lm + * src/ld.cpp: when processing dylbs that are sylinks ensure that fDylibMap contains all paths + * src/MachOWriterExecutable.hpp: when dead stripping dylibs and renumbering ordinals make sure + aliases dylib get renumbered too + * unit-tests/test-cases/dylib-aliases: added + + +----- Tagged ld64-73.1 + +2007-03-30 Nick Kledzik + + * src/MachOWriterExecutable.hpp: back out use of LC_REEXPORT_DYLIB until rdar://problem/5009909 is in build fleet + + +----- Tagged ld64-73 + +2007-03-30 Nick Kledzik + + ER: -dead_strip_dylibs + linker should add implicit load commands for indirectly used public dylibs + * src/ObjectFile.h: change dylib reader interface to implictly/explicitlyLinked + * src/ld.cpp: use new dylib reader interface + * src/Options.h: add deadStripDylibs() + * src/Options.cpp: support -dead_strip_dylibs + * src/MachOReaderDylib.hpp: use new dylib reader interface + * src/MachOWriterExecutable.hpp: remove dylib load commands for unused dylibs and alter ordinals + * unit-tests/test-cases/re-export-optimizations: added + * unit-tests/test-cases/dead_strip_dylibs: added + + +2007-03-30 Nick Kledzik + + * src/Options.cpp: enable -lfoo to search for libfoo.so as well as libfoo.dylib, + remove seg addr table hack for transitioning to new linker + +2007-03-30 Nick Kledzik + + ADOBE XCODE3: Linker is slow with large C++ .o files + * src/MachOReaderRelocatable.hpp: the compiler generates stubs to weak functions in the + same translation unit. Don't treat those like the spurios stubs to static functions. + + +2007-03-29 Nick Kledzik + + ld64 should link mach_kernel during xnu builds to support dtrace + * src/MachOReaderRelocatable.hpp: To handle duplicate labels properly, rework how atoms sizes are set + by iterating through sorted fAtoms rather than fAddrToAtom, . Change default alignment of commons + to be the natural alignment of the size rounded up to the closest power of two and max it at 12. + Build atoms in reverse symbol table order so that global atoms are constructed before locals. + This assures that if there is a global and local label at the same location, the global label + will become the atom's name and the local will be an alias. Properly handle a label + at the end of a section. Handle R_ABS in relocations. Handle sect-diff relocs with addends. + Don't auto-strip 'l' symbols in static executables (mach_kernel). + * src/OpaqueSection.hpp: opaque_section now has an ordinal + * src/ld.cpp: opaque_section now requires an ordinal + * src/ObjectFile.h: add ReaderOptions.fForStatic + * src/Options.cpp: set fForStatic when building a static executable + * src/MachOWriterExecutable.hpp: add from atom to StubAtom. Properly write out i386 + sect-diff relocs with addends. properly write out ppc PICbase relocs where pic base + is not in the atom. + + +2007-03-27 Nick Kledzik + + Typo in ld man page (-exported_symbols_list) + * doc/man/man1/ld.1: fix typo + + +2007-03-26 Nick Kledzik + + consider generating LC_UUID from a checksum of the file + * src/Options.h: change emitUUID() to getUUIDMode() + * src/Options.cpp: support -random_uuid + * src/MachOWriterExecutable.hpp: set uuid to be md5 hash of entire output file + + +2007-03-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: restructure writeAtoms() to copy all atoms in memory if possible + + +2007-03-24 Nick Kledzik + + ld -r of stripped .o file can incorrectly merge non-lazy pointers + * src/MachOWriterExecutable.hpp: when generating a .o file, non-lazy pointer with target offsets should be + encoded as LOCAL in the indirect symbol table + * unit-tests/test-cases/stripped-indirect-symbol-table: added test case + + +2007-03-23 Nick Kledzik + + SWB: ld64-72 errors building with gcc-4.2 + * src/MachOReaderDylib.hpp: add curly brackets in switch cases + * src/MachOWriterExecutable.hpp: rearrange classes so there are no template specialization forward references + + +2007-03-23 Nick Kledzik + + * src/ld.cpp: fix -print_statistics when using -dead_strip + + +2007-03-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: generate better names for non-lazy pointers to the interior of atoms + + +2007-03-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: speed up ld -r a little by reversing relocs en mas + + +2007-03-16 Nick Kledzik + + ld Bus Error on missing command line arguments + * src/Options.cpp: check next argv[] is not NULL + + +2007-03-16 Nick Kledzik + + need to be able to order symbols in anonymous namespaces + * src/ld.cpp: add logic to do fuzzy matching of symbols with anonymous namespace usage + * unit-tests/test-cases/order_file-ans: added test case + + +2007-03-16 Nick Kledzik + + headerpad_max_install_names deprecated for 64-bit + * src/ld.cpp: make sure dylib load command order matches command line order + * src/Options.h: add maxMminimumHeaderPad() + * src/Options.cpp: add maxMminimumHeaderPad() set by -headerpad_max_install_names + * src/src/MachOWriterExecutable.hpp: check maxMminimumHeaderPad() + * doc/man/man1/ld.1: update man page about -headerpad_max_install_names + + +2007-03-16 Nick Kledzik + + Linker returns success although exported symbols are undefined. + * src/ld.cpp: turn missing symbols back into an error + + +2007-03-16 Nick Kledzik + + ld64 should handle linking against dylibs that have @loader_path based dylib load commands + * unit-tests/test-cases/loader_path: added test case + + +2007-03-16 Nick Kledzik + + linker should add implicit load commands for indirectly used public dylibs + Indirect libraries should be found using -F and -L options + Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB + * src/ld.cpp: reworked all dylib processing. Readers can now add the dylib list. + * src/Options.h: add findFileUsingPaths() + * src/MachOReaderDylib.hpp: look in re-exported children instead of requring linker to do that + * src/ObjectFile.h: add processIndirectLibraries(), remove getDependentLibraryPaths() + * src/machochecker.cpp: support LC_REEXPORT_DYLIB + * src/ExecutableFile.h: simplify DyLibUsed + * src/Options.cpp: add findFileUsingPaths(). add new re-export options + * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 + * doc/man/man1/ld.1: updated with new re-export options + * unit-tests/test-cases/indirect-path-search: added tests that -F and -L work with indirect dylibs + * unit-tests/test-cases/re-export-cases: added tests for all combinations of re-exporting + + +2007-03-14 Nick Kledzik + + sort external relocations to optimize dyld performance + * src/MachOWriterExecutable.hpp: added ExternalRelocSorter + * src/machochecker.cpp: verify external relocations are grouped by symbol number + * unit-tests/test-cases/external-reloc-sorting: added test case + + +----- Tagged ld64-72 + +2007-03-06 Nick Kledzik + + * src/Options.cpp: ignore .objc_category_name_* symbols in .exp files + + +2007-03-06 Nick Kledzik + + * src/Options.cpp: stop special casing mach_kernel and instead requre kernel to be built with -new_linker + + +2007-03-06 Nick Kledzik + + ld64-72 (experimental) is causing DejaGnu test failures + * src/MachOWriterExecutable.hpp: add optimizableGOTReferenceKind() to track GOT uses that cannot be optimized + + +2007-03-06 Nick Kledzik + + minimum header padding should be 32 to allow code signing + * src/Options.cpp: initialize fMinimumHeaderPad to 32 + * src/MachOWriterExecutable.hpp: better calculation of header padding + + +2007-03-06 Nick Kledzik + + Linker crashes with -flat_namespace against two-level dylibs that might have re-exports + * src/ld.cpp: flat namespace should not allow NULL indirect readers + + +2007-03-06 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't error on S_COALESCED sections with anonymous atoms + * src/MachOWriterExecutable.hpp: set MH_PIE bit when linking -pie + * ld64.xcodeproj/project.pbxproj: don't echo environment when running unit test + + +2007-03-01 Nick Kledzik + + * doc/man/man1/ld.1: Add descriptions to all "rarely used options" + + +2007-03-01 Nick Kledzik + + Remove support for Essential Symbols: Warn about use of -Sp option; remove man page entry + * src/Options.cpp: make -Sp obsolete + * doc/man/man1/ld.1: make -Sp obsolete + + +2007-03-01 Nick Kledzik + + Support -pie + * src/Options.h: Add positionIndependentExecutable() + * src/Options.cpp: Support -pie option to set positionIndependentExecutable() + * src/MachOWriterExecutable: if -pie is used, add extra local relocations and error if any + absolute addressing is used + + +2007-03-01 Nick Kledzik + + ld64 should link mach_kernel during xnu builds to support dtrace + * src/ld.cpp: Ensure segments are laid out in discovery order. Add support for kAbsoluteSymbol. + Warn when merging symbols of different visiblity. Warn when a tentative definition + is replaced by one a real definition with a smaller size. Lay out __common section + so that ones built with -fno-commons come before regular commons. + * src/ObjectFile.h: remove SegmentOffset ivar and getter/setters + * src/machochecker.cpp: allow images with no r/w segments + * src/MachOReaderRelocatable: Add AbsoluteAtom. Sort tentative definitions by name instead of by size + Add support for custom commons alignment. + * src/Options.cpp: Fix spurious -sectalign warnings. Don't use ld_classic when linking mach_kernel + * src/MachOWriterExecutable.hpp: Support kAbsoluteSymbol atoms. In -r mode, set custom alignment + for commons if alignment is not its size. Support global __dtrace_probe labels. + * src/ObjectDump.cpp: add support for kAbsoluteSymbol atoms. + * unit-tests/test-cases/commons-alignment: Added test case for custom commons alignment + * unit-tests/test-cases/absolute-symbol: Added test case for basic absolute symbols + * unit-tests/test-cases/segment-order: Added test case that segments lay out in discovery order + * unit-tests/test-cases/commons-order: Added test case that commons lay out correctly + * unit-tests/test-cases/end-label: Added test case that a label used to mark the end of a section does not + get associcated with the next section. + + +2007-02-23 Nick Kledzik + + gcc-5005: DejaGnu failures due to -frepo + * src/ld.cpp: Add quotes to referenced from name to make collect2 and -frepo happy + + +2007-02-22 Nick Kledzik + + * src/MachOWriterExecutable.hpp: rework how padding after load commands is calculated + + +2007-02-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: extend special case of __mh_execute_header to static executables too + + +2007-02-21 Nick Kledzik + + gcc link map option ( "-M" ) should be redirectable to file + * doc/man/man1/ld.1: added -map option description + * src/Options.h: added generatedMapPath() + * src/Options.cpp: set up generatedMapPath() if -map option is used + * src/MachOWriterExecutable.hpp: add writeMap() method to generate map file + + +2007-02-19 Nick Kledzik + + Implement GOT Load elimination optimization + * src/ld.cpp: track size of all atoms and if > 2GB sort large zero-fill atoms to end + * src/MachOWriterExecutable.hpp: If image size < 2GB, only generate GOT entries if value must be + updatable by dyld. If > 2GB, only eliminate GOT entries to non-zero-fill atoms. Any use + of an eliminated GOT entry has its code changed from MOVQ _foo@GOT(%rip) to LEAQ _foo(%rip). + * unit-tests/test-cases/large-data: added + * unit-tests/test-cases/got-elimination: added + + +----- Tagged ld64-71.2 + +2007-02-13 Nick Kledzik + + new ld ignores -segprot option + * src/Options.h: expose customSegmentProtections() + * src/Options.cpp: parse -segprot option and populate customSegmentProtections() + * src/MachOWriterExecutable.hpp: use customSegmentProtections() + + +2007-02-13 Nick Kledzik + + i386 -stack_addr doesn't work + * src/MachOWriterExecutable.hpp: use correct offset into thread state record + + +----- Tagged ld64-71.1 + +2007-02-07 Nick Kledzik + + * src/ld.cpp: sort __OBJC2 segment to be next to __OBJC segment + + +2007-02-07 Nick Kledzik + + * src/Options.cpp: change missing -seg_addr_table from an error to a warning + + +2007-02-06 Nick Kledzik + + Leopard 9A357: -dylib_file broken? + * src/MachOWriterExecutable.hpp: remove use of fInstallPathOverride + * src/Options.cpp: wire up -dylib_file option + * src/Options.h: remove fInstallPathOverride. add fDylibOverrides + * src/ld.cpp: check dylibOverrides() for indirect libraries + * unit-tests/test-cases/dylib_file: add test case + + +2007-02-05 Nick Kledzik + + * src/MachOReaderDylib.hpp: don't warn about zero size __image_info sections + + +2007-02-04 Rick Balocca + Enable the failing cases for missing command line arguments + +2007-02-04 Rick Balocca + Make sure that all .o's are checked by ObjectDump + and all macho are checked by machochecker + +2007-02-04 Rick Balocca + Fix an endian problem with machochecker + Fix blank-stubs Makefile + +----- Tagged ld64-71 + +2007-02-02 Rick Balocca + blank-stubs test case: handle the case of a native ppc compile--this + sets the subtype, which must be passed to lipo + +2007-02-01 Rick Balocca + make cpu-sub-types test more robust + +2007-02-01 Rick Balocca + auto-arch tests were resulting in a false FAILs + +2007-02-01 Rick Balocca + test cpu-sub-types was resulting in a false FAIL + +2007-02-01 Nick Kledzik + + STD:VSC: c99 -o writes to file that does not have write permission + * src/MachOWriterExecutable.hpp: check file is writable before using it + +2007-02-01 Nick Kledzik + + debug map (N_OSO) timestamps for object files in ranlib archive are incorrect + * src/MachOReaderArchive.hpp: parse modTime for .o files out of archive header + +2007-01-31 Nick Kledzik + + 9A354: ld -all_load does *NOT* produce the same dSYM as *.o or -u + * src/ld.cpp: when using -all_load don't assume that all atoms have same reader + * unit-tests/test-cases/dwarf-archive-all_load: added + +----- Tagged ld64-70.1 + +2007-01-31 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in addObjectRelocs_powerpc() mask scattered r_address to 16-bits + +----- Tagged ld64-70 + + +2007-01-30 Nick Kledzik + + linker should verify GC consistency of modules being linked into library + Support cpu-sub-types for ppc + * src/ObjectFile.h: Add getObjCConstraint() and getCpuConstraint() + * src/MachOReaderRelocatable.hpp: don't make atom for __image_info section, instead parse constaints + * src/MachOReaderDylib.hpp: look at __image_info content to get constaints + * src/ld.cpp: add updateContraints() and checkObjc() + * src/MachOWriterExecutable.hpp: add ObjCInfoAtom to sythesize __image_info content + + +2007-01-28 Nick Kledzik + + src/*: remove ObjectFile::requiresFollowOnAtom() method + + +2007-01-28 Nick Kledzik + + src/ld.cpp: enable LLVM_SUPPORT by default + src/LLVMReader.hpp: don't use absolute paths for llvm headers and libraries + + +2007-01-26 Rick Balocca + * src/ObjectDump.cpp: The usage() message was incorrect. + + +2007-01-25 Rick Balocca + * unit-tests/test-cases/zero-fill3: It was reporting FAIL on ld64 error return. + It should have been checking for non-error return. + + +2007-01-24 Nick Kledzik + + x86 fast stubs should not cross 64-byte boundries + * src/MachOWriterExecutable.hpp: for x86, 64-byte align __jump_table section + and make 64-btye crossing stubs be empty entries with indirect symbol table + entry of INDIRECT_SYMBOL_ABS + + +2007-01-19 Nick Kledzik + + * src/Options.h: add readOnlyx86Stubs() + * src/Options.cpp: support -read_only_stubs + * src/MachOWriterExecutable.hpp: make __IMPORT segment not writable if -read_only_stubs is used + + +2007-01-16 Eric Christopher + + ld64 --help isn't recognized + * src/Options.cpp (Options::parse): Support --help and -help. + + +2007-01-15 Nick Kledzik + + * src/MachOFileAbstraction.hpp: add range checking on macho_scattered_relocation_info::set_r_address() + + +2007-01-14 Nick Kledzik + + Support wildcards in contents of -exported_symbols_list + * src/Options.h: add SetWithWildcards class + * src/Options.cpp: add -exported_symbol and -unexported_symbol and use SetWithWildcards + * doc/man/man1/ld.1: add -exported_symbol and wildcard explanation + * unit-tests/test-cases/exported-symbols-wildcards: added test case + + +2007-01-10 Nick Kledzik + + [U]SDT probes should use C calling convention + * src/Options.cpp: Add -read_only_dof + * src/ld.cpp: create __dof section(s) based on probe and isenabled sites + * src/MachOReaderRelocatable.hpp: parse new sdt 2.0 probes encoded in .o files + * src/MachOWriterExecutable.hpp: handle regenerating dtrace probes into .o files + * unit-tests/test-cases/dtrace-static-probes: added test case + + +----- Tagged ld64-69.8 + +2007-01-30 Nick Kledzik + + Support LD_FORCE_NO_SEG_ADDR_TABLE + * src/Options.cpp: Support LD_FORCE_NO_SEG_ADDR_TABLE + + +----- Tagged ld64-69.7 + +2007-01-25 Nick Kledzik + + Leopard9A351: CFM Apps Are Broken because CFM glue is missing + * src/MachOReaderRelocatable.hpp: check S_ATTR_NO_DEAD_STRIP in dontDeadStrip() + + +----- Tagged ld64-69.6 + +2007-01-24 Nick Kledzik + + LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive + * src/ld.cpp: create and use logArchive() + + +----- Tagged ld64-69.5 + +2007-01-22 Nick Kledzik + + 9A350: can't link ppc programs with ld_classic + * src/Options.cpp: Remove support for LD_NO_CLASSIC_LINKER. Add support for -classic_linker + + +----- Tagged ld64-69.4 + +2007-01-17 Nick Kledzik + + QTComponents does not link with ld64 + * src/MachOReaderRelocatable.hpp: handle N_RSYM and N_PSYM stabs + + +----- Tagged ld64-69.3 + +2007-01-03 Nick Kledzik + + * src/Options.cpp: If the same dylib is specified twice and the second is specified weak, make it weak + + +----- Tagged ld64-69.2 + +2006-12-18 Nick Kledzik + + -dead_strip without -exported_symbols_list should not strip global functions from archives + * src/ld.cpp: when adding a .o file from an archive, add all its global symbols to live roots + * unit-tests/test-cases/dead_strip-archive: added + + +2006-12-18 Nick Kledzik + + flat_namespace main executables do not need to indirect interior references + * src/MachOWriterExecutable.hpp: don't indirect references to global symbols in main executables + * unit-tests/test-cases/flat-main: updated to test for indirection + * unit-tests/test-cases/flat-dylib: added + + +----- Tagged ld64-69.1 + +2006-12-15 Nick Kledzik + + -flat_namespace does not work with -mdynamic-no-pic + * src/MachOWriterExecutable.hpp: rework checking for use of ppc absolute addressing to allow them as long as + the target is within the same linkage unit. + + +2006-12-15 Nick Kledzik + + -ObjC should only load .o with .objc_ symbols + * src/Options.cpp: remove warning from -ObjC and have it instead set fLoadAllObjcObjectsFromArchives + * src/MachOReaderArchive.hpp: when -ObjC is used, preload all .o files from archives that contain .objc_ symbols + + +----- Tagged ld64-69 + +2006-12-13 Nick Kledzik + + prebound interior pointers must be non-zero + * src/MachOWriterExecutable.hpp: in fixUpReference_powerpc() set lazy pointers bound to with the dylib to + their target value. Properly set REFERENCE_FLAG_UNDEFINED_* flags in reference table and n_desc + + +2006-12-09 Nick Kledzik + + ld64 fails to detect error that ld_classic does + * src/MachOWriterExecutable.hpp: check for absolute reloc to an external symbol + * src/MachOReaderRelocatable.hpp: ignore -mlong-branch stubs in .o files + + +2006-12-09 Nick Kledzik + + symbols with REFERENCED_DYNAMICALLY should never be stripped + * src/MachOWriterExecutable.hpp: update Writer::shouldExport() to check for kSymbolTableInAndNeverStrip + * unit-tests/test-cases/main-stripped: add test that dynamically referenced symbol cannot be stripped + + +2006-12-08 Nick Kledzik + + * unit-tests/test-cases/allowable-client: add variant test cases (e.g. CoreServices_profile) + * src/ld.cpp: allow frameworks with variant install names (e.g. CoreServices_profile) to be private clients + + +2006-12-08 Nick Kledzik + + * doc/man/man1/ld.1: rewrite man page + * src/Options.h: add warnObsolete() + * src/Options.cpp: use warnObsolete() on many options. Make nonWeak the weak-mis-match default. + Make -ObjC mean -all_load. + +----- Tagged ld64-68.3 + +2006-12-05 Nick Kledzik + + * src/ld.cpp: allow umbrella frameworks to have variant install names (e.g. CoreServices_profile) and still link + + +----- Tagged ld64-68.2 + +2006-12-05 Nick Kledzik + + * src/MachOWriterExecutable.cpp: Use N_PBUD in the symbol table for undefined symbols in prebound dylibs + + +----- Tagged ld64-68.1 + +2006-12-01 Nick Kledzik + + * src/Options.cpp: always generate module tables for 32-bit architectures so that ld_classic + can link against them + + +----- Tagged ld64-68 + +2006-12-01 Nick Kledzik + + seg_addr_table needs matching fuzziness + * src/Options.cpp: special case a how a dozen dylib are looked up in the seg_addr_table + + +2006-12-01 Nick Kledzik + + * src/Options.cpp: have all -static links for 32-bit archs roll over to ld_classic unless + LD_NO_CLASSIC_LINKER_STATIC is set. + * unit-tests/bin/make-recursive.pl: set LD_NO_CLASSIC_LINKER_STATIC for unit tests + + +2006-11-29 Nick Kledzik + + ld64-67: QTComponents fails to build + * src/MachOReaderRelocatable.hpp: don't error out when a local non-lazy pointer does not point to a symbol + * unit-tests/test-cases/strip_local: added test case + + +2006-11-28 Nick Kledzik + + Need a way to mark libraries usable by dynamic linker but unusable by static linker + * src/Options.cpp: allow -client_name to be used with main executables + * src/ld.cpp: generalize -allowable_client. Any dylib can now restrict who can link against it. As a convention + linking with -allowable_client '!' will mean no one can statically link with the dylib. It can still be loaded + dynamically, or by any existing clients, but no new clients can link with it. + * unit-tests/test-cases/allowable-client/Makefile: enable previously commented out test cases. Add test cases + of a dylib that allows no clients and just one client + +2006-11-27 Nick Kledzik + + -final_output should be used if -install_name not used + * src/Options.cpp: fall back to using -final_output for install name + + +----- Tagged ld64-67 + +2006-11-17 Nick Kledzik + + * src/MachOWriterExecutable.hpp: support __IMPORT segment being slide independently of __DATA segment in shared cache + + +2006-11-16 Nick Kledzik + + 9a303: ld -filelist Bus Error + * src/Options.cpp: add check that -filelist is followed by an argument + + +2006-11-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: when building split-seg dylibs, LINKEDIT goes in read-only side + + +2006-11-15 Nick Kledzik + + * src/MachOWriterExecutable.hpp: set proper attributes for __eh_frame in ld -r mode + * unit-tests/test-cases/eh_frame: added test case + + +2006-11-10 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: redirect references to static weak stubs to the real target + + +2006-11-09 Nick Kledzik + + * src/MachOWriterExecutable.hpp: r_address is offset from first LC_SEGMENT vmaddr - not from segment with lowest address + + +----- Tagged ld64-66.1 + +2006-11-09 Nick Kledzik + + * src/MachOWriterExecutable.hpp: initialize fModuleInfoAtom to zero + + +2006-11-08 Nick Kledzik + + FSF GCC's libjava doesn't link with Ochre ld64 + * src/MachOReaderRelocatable.hpp: ignore debug_line section if debug_info section is missing or empty + +----- Tagged ld64-66 + +2006-11-07 Nick Kledzik + + SWB: d64-65 does not built usage split-seg dylibs + * src/MachOWriterExecutable.hpp: when prebinding split-seg correctly set r_address fields and on + disk values for external relocations + * unit-tests/test-cases/prebound-split-seg: added test case + + +2006-11-03 Nick Kledzik + + * src/MachOReaderDylib.hpp: don't report dependent libraries if MH_NO_REEXPORTED_DYLIBS bit is set + * src/MachOWriterExecutable.hpp: set MH_NO_REEXPORTED_DYLIBS bit if dylib does not logically re-export any other dylibs + * unit-tests/test-cases/re-export-flag: added test case + * src/machochecker.cpp: validate use of MH_NO_REEXPORTED_DYLIBS + + +2006-11-02 Nick Kledzik + + Mysterious messages from ld64 with MACOSX_DEPLOYMENT_TARGET = 10.5 + * src/MachOWriterExecutable.hpp: kPointerWeakImport is a valid reference type to cross segments + + +2006-11-02 Nick Kledzik + + * src/Options.cpp,h: Add support for -rpath + * src/MachOFileAbstraction.hpp: add macho_rpath_command + * src/MachOWriterExecutable.hpp: add RPathLoadCommandsAtom to create LC_RPATH for each -rpath + + +----- Tagged ld64-65 + +2006-10-30 Nick Kledzik + + x86_64 default stack_addr is wrong + * src/Options.cpp: change default 64-bit stack location when using -stack_size + + +2006-10-30 Nick Kledzik + + dylibs need modules for 10.3 and for ld_classic in Salt + * src/MachOWriterExecutable.hpp: add ModuleInfoLinkEditAtom to create module table stuff + * src/Options.cpp,h: Add needsModuleTable() + * src/MachOFileAbstraction.hpp: Add macho_dylib_module, macho_dylib_reference, and macho_dylib_table_of_contents + + +2006-10-27 Nick Kledzik + + * unit-tests/test-cases/no-uuid/Makefile: add -gstabs+ to be compatible with latest compiler + * unit-tests/test-cases/stabs-coalesce/Makefile: add -gstabs+ to be compatible with latest compiler + + +2006-10-26 Nick Kledzik + + i386 -mdynamic-no-pic switch statement jump table is out of line + * src/MachOWriterExecutable.hpp: for i386 don't check for direct references to weak symbols + + +2006-10-26 Devang Patel + + * src/LLVMReader.hpp: Supply final output file path to optimizer. + +2006-10-26 Devang Patel + + * src/ObjectFile.h: Make setSection* methods virtual. + * src/LLVMReader.hpp: Override setSection* methods. + +2006-10-26 Devang Patel + + * unit-tests/test-case/llvm-integration/a13.h: New. + * unit-tests/test-case/llvm-integration/a13.cc: New. + * unit-tests/test-case/llvm-integration/main13.cc: New. + +2006-10-26 Devang Patel + + * src/options.h, src/options.cpp: Add -save-temps command line option. + * src/LLVMReader.hpp: Use saveTemps option. + + +2006-10-26 Devang Patel + + * src/LLVMReader.hpp: Remove invalid module from memory. + +2006-10-26 Devang Patel + + * src/LLVMReader.hpp: Collect symbol alignment info from LLVM optimizer. + +2006-10-21 Eric Christopher + + * src/ld.cpp (Linker::Linker): Check for LD_NO_CLASSIC_LINKER before + invoking ld_classic. + * unit-tests/test-cases/relocs-literals/Makefile: Run for -mdynamic-no-pic + and pic. + * unit-tests/test-cases/static-executable/Makefile: Skip for 64-bit. Add + -dead_strip to command line. + +----- Tagged ld64-64.2 + +2006-10-19 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: stop copying LLVMReader.hpp into man1 directory + +----- Tagged ld64-64.1 + +2006-10-19 Nick Kledzik + + ld64-63.1 erroneously coalesces an empty string with a non-empty string + * src/MachOReaderRelocatable.hpp: rework cstring parsing to not assume all strings are start + at section alignment boundaries, and when coalescing empty strings always use one with greatest + alignment requirement + * src/MachOWriterExecutable.hpp: in -r mode, don't pad end of cstring section + * src/ObjectFile.h: correctly name leadingZeros() as trailingZeros() + * src/ld.cpp: leadingZeros() --> trailingZeros() + + +2006-10-18 Eric Christopher + + * unit-tests/test-cases/read-only-relocs/Makefile: Skip for x86_64. + * unit-tests/test-cases/llvm-integration/Makefile: Skip if llvm isn't + present. + +2006-10-18 Nick Kledzik + + ld64 change required to go with assembler cstring change + ld64 should error when a local relocation references an address outside its section + * src/MachOReaderRelocatable.hpp: for x86_64 in order to work with local or external relocations to cstrings + change parser to allow atoms with a pending name that is resolved after references are instantiated. + Make direct references to kRegularDefinition atoms. + * src/MachOWriterExecutable.hpp: in -r mode for x86_64 generate L* labels for cstrings and use external relocations + * unit-tests/test-cases/relocs-literals/test.c: add two cases of cstring literal plus addend + + +2006-10-06 Nick Kledzik + + check MACOSX_DEPLOYMENT_TARGET if -macosx_version_min is not used + * src/Options.cpp: if -macosx_version_min is not used, check MACOSX_DEPLOYMENT_TARGET, if + that is unused, default to 10.5 + +----- Tagged ld64-64 + +2006-10-06 Nick Kledzik + + crash in ppc64 program - bl to saveFP, but saveFP is too far away? + * src/MachOWriterExecutable.hpp: in addPPCBranchIslands(), properly account for growth of __text + + +2006-10-06 Nick Kledzik + + Linker-defined alias converts reference into definition and generates error. + * src/MachOReaderRelocatable.hpp: only alias symbols actually in the symbol table + + +2006-10-06 Nick Kledzik + + * unit-tests/test-cases/dwarf-debug-notes/Makefile: crt1.o no longer has stabs, so don't need to strip it + * unit-tests/test-cases/dwarf-debug-notes-r/Makefile: crt1.o no longer has stabs, so don't need to strip it + + +2006-10-06 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: rework dwarf line parsing to fix warnings that starting + showing up with gcc-5421 + + +2006-10-05 Eric Christopher + + ld64 needs to support libtool options + * src/Options.cpp (Options::parse): Add -noall_load, -install_name, + -current_version and -compatibility_version. + +2006-10-03 Eric Christopher + + * src/Options.cpp (Options::gotoClassicLinker): Use execvp + to call ld_classic. + +2006-10-03 Eric Christopher + + * unit-tests/test-cases/tentative-to-real/Makefile: Clean up after tests. + +2006-10-03 Eric Christopher + + * unit-tests/include/common.makefile (VALID_ARCHS): Add x86_64. + (OTOOL): Remove munging based on ARCH. + +2006-09-29 Nick Kledzik + + problem merging .o files built with and without -fno-common + src/Options.*: make MakeTentativeDefinitionsReal a reader option + src/ObjectFile.h: make MakeTentativeDefinitionsReal a reader option + src/MachOWriterExecutable.hpp: make MakeTentativeDefinitionsReal a reader option + src/MachOReaderRelocatable.hpp: only assign a section name of __common to + tentative defintions when making a final linked image + + +2006-09-28 Nick Kledzik + + src/Options.h/.cpp: add support for -segaddr option + src/MachOWriterExecutable.hpp: In Writer::assignFileOffsets(), use -segaddr info + + +2006-09-28 Nick Kledzik + + Emit new CPU subtypes for ppc64 and x86-64 when targeting 10.5 or later + src/MachOWriterExecutable.hpp: set high bit of cpusubtype of 64-bit main executables when targeting 10.5 or later + + +2006-09-28 Devang Patel + + Add LLVM LTO support + src/LLVMReader.hpp: New file. + src/ld.cpp: Add optimization phase. Use LLVM LTO. + unit-tests/test-cases/llvm-integration: New tests. + +2006-09-27 Nick Kledzik + + ld64.xcodeproj/project.pbxproj: remove accidental install of source file into man1 + + +2006-09-25 Nick Kledzik + + src/Architectures.hpp: add kPointerDiff16 for ppc and ppc64 + src/MachOReaderRelocatable.hpp: support kPointerDiff16 + src/MachOWriterExecutable.hpp: support kPointerDiff16 + +----- Tagged ld64-63.1 + +2006-09-22 Nick Kledzik + + src/MachOWriterExecutable.hpp: include stubs in LC_SEGMENT_SPLIT_INFO + + +2006-09-21 Nick Kledzik + + src/Options.cpp: disable split-seg dylibs for 64-bit architectures + + +2006-09-19 Nick Kledzik + + src/MachOReaderRelocatable.hpp: rework __cstring parsing to better handle mixed alignment cstrings + src/MachOWriterExecutable.hpp: in -r mode, make all __cstrings aligned to section alignment + + +2006-09-19 Nick Kledzik + + src/MachOWriterExecutable.hpp: rework encoding of LC_SEGMENT_SPLIT_INFO + + +2006-09-19 Nick Kledzik + + src/Options.cpp: check for -search_paths_first in first pass + + +----- Tagged ld64-63 + +2006-09-15 Nick Kledzik + + src/Options.cpp: since the ld64 will repeatedly search an archive, and some project list archives + multiple times on command line to work with traditional linkers, automatically ignore duplicate libraries + unit-tests/test-cases/archive-duplicate: added test case + + +2006-09-15 Nick Kledzik + + src/Options.cpp: support -r -static + src/MachOWriterExecutable.hpp: support -r -static an don't generate LC_DYSYMTAB + + +2006-09-14 Nick Kledzik + + src/MachOWriterExecutable.hpp: in -r mode references to weak symbols should not create external relocations + as that can cause nmedit to errror later. + + +2006-09-13 Nick Kledzik + + ld64: Handle .objc_class_name exports specially + src/Options.cpp: add hack so that .objc_class_name_XXX in -exported_symbols_list imples _OBJC_CLASS_$_XXX + src/ld.cpp: add hack to supporess errors about .objc_class_name_XXX or _OBJC_CLASS_$_XXX being undefined + + +2006-09-12 Nick Kledzik + + Support -prebind when targeting ppc and OS < 10.4 + src/Options.h: add splitSeg() and baseWritableAddress() + src/Options.cpp: Add support for -seg_addr_table and LD_SEG_ADDR_TABLE, and -prebind and LD_PREBIND. + src/src/MachOWriterExecutable.hpp: support split-seg and canonical prebound files to be generated + + +2006-09-11 Nick Kledzik + + Linking a dylib or binary from identical binaries should produce the same output + src/MachOWriterExecutable.hpp: set the timestamps to be constant + + +2006-09-11 Nick Kledzik + + Linker support for ordering all sections and symbols + src/Options.cpp: Add -order_file_statistics. Allow architecture prefixes in order files + src/ld.cpp: Use fOptions.printOrderFileStatistics() + + +2006-09-11 Nick Kledzik + + Support -sectorder + unit-tests/test-cases/order_file: added test case + src/ld.cpp: Implement order file support in Linker::sortAtoms() + src/Options.h: add Options.orderedSymbols() + src/Options.cpp: add parseOrderFile(), implement -order_file + + +2006-09-07 Nick Kledzik + + need -i for 64-bit (or equivalent) + Support -i for aliasing exported symbols + unit-tests/test-cases/alias-objects: added + unit-tests/test-cases/alias-command-line: added + src/ObjectFile.h: Added Atom::getOrdinal() as new way to sort atoms. Added ReaderOptions.fAliases + src/MachOReaderRelocatable.hpp: Added SymbolAliasAtom to handle multiple symbols to same address + src/MachOReaderArchive.hpp: implement Atom::getOrdinal() to space out atom ordinals across member objects + src/Options.cpp: support -i, -alias, -alias_list. Move search of /Network/Library/Frameworks to after /System/Library/Frameworks + src/MachOWriterExecutable.hpp: pad out seg_info data. Implement getOrdinal(). + src/ObjectDump.cpp: call constructors directly instead of using make() wrapper + + +2006-09-01 Nick Kledzik + + Need the ability to tag libraries/plug-ins with security attributes + src/MachOReaderDylib.hpp: add warning if using -root_safe or -setuid_safe and link against dylib that is not + src/ObjectFile.h: add ReaderOption fRootSafe and fSetuidSafe + src/Options.cpp: handle -root_safe or -setuid_safe command line options + src/MachOWriterExecutable.hpp: set MH_ROOT_SAFE and MH_SETUID_SAFE flags + + +2006-08-31 Nick Kledzik + + src/ld.cpp: Add Linker::processDTrace() for processing dtrace static probes + src/OpaqueSection.hpp: renamed, add symbol name, add ability to add references + ld64.xcodeproj/project.pbxproj: remove SectCreate.cpp, add OpaqueSection.hpp + + +2006-08-28 Nick Kledzik + + Add convention for removing symbols at link time + Assembler -L option causes ld64 to split stubs + unit-tests/test-cases/special-labels: added test case + src/MachOReaderRelocatable.hpp: ignore L* labels, make l* labels as kSymbolTableNotIn + + +2006-08-28 Nick Kledzik + + src/lObjectFile.h: refactor isTargetUnbound() into getTargetBinding() + src/ld.cpp: create __dof section in final linked images from dtrace static probes + src/Architectures.hpp: add kDtraceProbe + src/Options.h/cpp: Add support for -dtrace + src/machochecker.cpp: support LC_SEGMENT_SPLIT_INFO + src/MachOWriterExecutable.hpp: support kDtraceProbe + src/MachOReaderRelocatable.hpp: suppport kDtraceProbe + + +2006-08-25 Nick Kledzik + + generate LC_SEGMENT_SPLIT_INFO for 10.5 or later dylibs + src/Options.h&.cpp: implement sharedRegionEligible() to control when LC_SEGMENT_SPLIT_INFO is added + src/MachOFileAbstraction.hpp: add macho_linkedit_data_command + src/MachOWriterExecutable.hpp: generate LC_SEGMENT_SPLIT_INFO load command and linkedit content + +----- Tagged ld64-62 + +2006-08-15 Nick Kledzik + + wrong error message when symbol is found in unused indirect library + src/ld.cpp: remove indirect libraries if they are not re-exported + unit-tests/test-cases/indirect-dylib: added test case + + +2006-08-15 Nick Kledzik + + alignment needs to be richer + src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info + src/ld.cpp: modify SymbolTable::add() to work with new Alignment type + src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers() + src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address + src/ObjectDump.cpp: print richer Alignment info + unit-tests/test-cases/align-modulus: added test case + + +2006-08-11 Nick Kledzik + + remove OPEN_SOURCE conditionals around x86_64 support + + +2006-07-31 Nick Kledzik + + ld64 while linking cc1 [ when dead_strip is ON] + src/ld.cpp: Add ivar fAtomsWithUnresolvedReferences to track atoms not initially resolvable + unit-tests/test-cases/dead_strip-archive: added test case + + +2006-07-31 Nick Kledzik + + x86_64: instructions with immediate and rip-relative operands need to use new relocation types + src/MachOWriterExecutable.hpp: generate new reloc types in -r mode + src/MachOReaderRelocatable.hpp: parse new reloc types + unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type + + +2006-07-18 Nick Kledzik + + src/MachOReaderRelocatable.hpp: suppress warning about dwarf info parsing for one benign no-op case + the compiler emits when there are not functions in the __text section + + +2006-07-17 Nick Kledzik + + faster debug note generation + src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a + pass per .o file. Added timing info for collectDebugInfo() to -print_statistics + unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r + unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order + + +2006-07-17 Nick Kledzik + + ld64 VSIZE is 1.18GB when building Finder ppc64 + src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped + +----- Tagged ld64-61.1 + +2006-07-11 Nick Kledzik + + ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name + src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message + +2006-07-11 Nick Kledzik + + If -arch is missing, rollover to ld_classic does not happen + src/Options.h: make gotoClassicLinker() public + src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386 + +----- Tagged ld64-61 + +2006-06-29 Nick Kledzik + + ld64 should be renamed to ld + src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen + src/ld.cpp: alter version string + ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64 + doc/man/man1/ld.1: added + +----- Tagged ld64-60 + +2006-06-28 Nick Kledzik + + Can't link large ppc64 program: ld64 says "bl out of range" + MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions + and properly chain together branch islands + MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references + only when done + +2006-06-28 Nick Kledzik + + MySQL-36 fails to build with ld64-59 + src/MachOReaderRelocatable.hpp: back out fix for 4585335 + src/MachOWriterExecutable.hpp: back out fix for 4585335 + +2006-06-27 Nick Kledzik + + src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how + dwarf debug notes are formed. + +2006-06-23 Nick Kledzik + + + + ld64 doesn't support variant linking -framework fw,_debug + src/Options.cpp: enhance findFramework() to support suffixes + +----- Tagged ld64-59 + +2006-06-22 Nick Kledzik + + ld64 lost DWARF debug notes + src/MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later + unit-tests/test-cases/dwarf-debug-notes-r: added test case + +2006-06-21 Nick Kledzik + + python 64-bit address miscalculation + src/MachOReaderRelocatable.hpp: change getTargetOffset() to sign extend the 32-bit value to 64-bits + +2006-06-21 Nick Kledzik + + ld64 seems to offset things incorrectly when using -r + src/MachOWriterExecutable.hpp: in -r mode, virtual sections should not increment address + + +----- Tagged ld64-58 + +2006-06-16 Nick Kledzik + + src/rebase.cpp: fix page alignment problem + src/rebase.cpp: fix endianess problem with local non-lazy pointers + +2006-06-15 Nick Kledzik + + src/rebase.cpp: fix to build in CurryWeed + ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed + +2006-06-15 Nick Kledzik + + Support .objc_class_name_* symbols + src/ObjectFile.h: Add kSymbolTableInAsAbsolute + src/MachOReaderRelocatable.hpp: synthesize references to required objc classes + src/MachOWriterExecutable.hpp: write objc_class_name as absolute symbol + unit-tests/test-cases/objc-references: added + +2006-06-15 Nick Kledzik + + SECTION_ATTRIBUTES unset in ppc64 mach-o header + src/MachOWriterExecutable.hpp: add section attribute for sections with code + +2006-06-15 Nick Kledzik + + ld64 bogus duplicate symbol name linking GNU libobjc + src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes + +2006-06-15 Nick Kledzik + + x86_64: ".align" directive not honored + src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size + +2006-06-14 Nick Kledzik + + jump table into middle of weak symbol causes error + src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols + src/MachOWriterExecutable.hpp: do not error on absolute references to interior of weak symbols + +2006-06-13 Nick Kledzik + + src/Options.cpp: allow -image_base as an alias for -seg1addr + +2006-06-13 Nick Kledzik + + implement -d + src/Options.h: add fMakeTentativeDefinitionsReal + src/Options.cpp: set fMakeTentativeDefinitionsReal if -d option is found + src/MachOWriterExecutable.hpp: turn tentative into real definition if makeTentativeDefinitionsReal + unit-tests/test-cases/btentative-to-real: added test case + +2006-06-13 Nick Kledzik + + implement -bundle_loader + src/Options.h: add fBundleLoader bit to DynamicLibraryOptions + src/Options.cpp: handle -bundle_loader + src/ld.cpp: pass fBundleLoader bit to MachOReaderDylib + src/MachOReaderDylib.hpp: support reading MH_EXECUTE files if fBundleLoader is set + src/MachOWriterExecutable.hpp: set bundle loader ordinal as EXECUTABLE_ORDINAL + unit-tests/test-cases/bundle_loader: added test case + +2006-06-12 Nick Kledzik + + -syslibroot can cause "can't find ordinal for imported" error + src/MachOReaderDylib.hpp: in Reader::reExports() compare install path in addition to load path + + +2006-06-10 Nick Kledzik + + Need rebasing tool + src/rebase.cpp: added + unit-tests/test-cases/rebase-basic: added + doc/man/man1/rebase.1: added + ld64.xcodeproj/project.pbxproj: added rebase target. changed all targets to build with dwarf + + +2006-06-10 Nick Kledzik + + src/machochecker.cpp: add some ppc reloc sanity checking + +----- Tagged ld64-57 + +2006-06-06 Nick Kledzik + + ld64 is not adding a final '/' char on the initial directory-name SO stab debug map entry + ld.cpp: Change Linker::synthesizeStabs() to assure directory SO always has a trailing slash + unit-tests/test-cases/dwarf-debug-notes/expected-stabs: update with trailing / + +2006-06-06 Nick Kledzik + + -sectcreate of a 0-byte section fails + MachOWriterExecutable.cpp: Don't error out on zero length segments + MachOWriterExecutable.cpp: For ppc64 reloc base address is the first writable segment iff + there is a writable segment >4GB from base address + +2006-06-04 Eric Christopher + + Radar 4560240 + Radar 3964999 + * src/ld.cpp (createReader): Fixed error message. + (resolve): Ditto. + (resolveFrom): Ditto. + (checkUndefines): Ditto. + +----- Tagged ld64-56 + +2006-05-23 Nick Kledzik + + No debug notes for ObjC methods when linking with ld64 + ld.cpp: don't limit debug notes to functions starting with underscore + +2006-05-22 Nick Kledzik + + ld64 spends much time in mach_o::relocatable::Reader::findAtomByName + * src/MachOReaderRelocatable.hpp: add makeReferenceToSymbol() so that x86_64 does not need to do by-name lookups + +2006-05-22 Nick Kledzik + + remove inferring warning + * ld.cpp: Remove "inferring" warning. If a link failed and now arch was specifed add which arch was + inferred to error message + +2006-05-19 Nick Kledzik + + ld64 does not honor -arch_multiple + * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message + +2006-05-19 Nick Kledzik + + Support S_16BYTE_LITERALS section types + * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS + * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS + +2006-05-19 Nick Kledzik + + "warning can't parse dwarf compilation unit info" warnings building debug + * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing + +----- Tagged ld64-55 + +2006-05-18 Nick Kledzik + + Default the pagezero size to 4GB for x86-64 + * src/Options.cpp: Chnage default the pagezero size to 4GB for x86-64 + +2006-05-18 Nick Kledzik + + x86_64 CarbonCore fails to link with "atom not found in symbolIndex" + * src/MachOWriterExecutable.hpp: in buildObjectFileFixups() don't call addObjectRelocs() on kNoFixUp references + +2006-05-18 Nick Kledzik + + ld64: .section defaults to read-only + * src/MachOReaderRelocatable.hpp: default unknown segments to r/w + +2006-05-18 Nick Kledzik + + -fvisibility=hidden causes crashes for x86_64 + * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions + +2006-05-12 Nick Kledzik + + * src/Architectures.hpp: add x86::kAbsolute32 + * src/MachOReaderRelocatable.hpp: generate x86::kAbsolute32 for mdynamic-no-pic instructions + * src/MachOWriterExecutable.hpp: process x86::kAbsolute32 reference kind + +----- Tagged ld64-54 + +2006-05-11 Nick Kledzik + + CF-393 failes to link for x86_64 + * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer::fixUpReferenceRelocatable + +2006-05-11 Nick Kledzik + + warning arch x86_64 not found using i386 + * src/ld.cpp: remove hack to allow x86_64 to link against i386 dylibs + + +2006-05-10 Nick Kledzik + + x86_64: .objc_class_name symbol names scrambled + * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections + + +2006-05-08 Nick Kledzik + + Support -dead_strip + * src/Options.h/cpp: implement -why_load and -why_live. Enable -dead_strip. + * src/MachOReaderArchive.hpp: implement -why_load + * src/MachOReaderRelocatable.hpp: suppress GCC_except_table* symbols in final output + * src/ld.cpp: implement dead code stripping + * unit-tests/test-cases/dead_strip: added + +----- Tagged ld64-53 + +2006-05-05 Nick Kledzik + + * src/Options.cpp: make 10.4 be minimum OS version for newer architectures + +2006-05-05 Nick Kledzik + + N_SO symbols in 64-bit builds have a zero address for n.n_value + * src/ld.cpp: for SO stabs, associate first and last atom in the SO range + * src/MachOWriterExecutable.hpp: use atom associated with SO stab to set ins n_value + +2006-05-05 Nick Kledzik + + * MachOWriterExecutable.hpp: fix end FUN stab to have length of function + + +2006-05-02 Nick Kledzik + + 64-bit main executables should have 4GB zero page by default + * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64 + 64 bit: apps with -mdynamic-no-pic seg fault when page zero > 4GB + * src/MachOWriterExecutable.cpp: rework pagezero for ppc64 so that if any mdynamic-no-pic code + is found, the code is kept in the low 2GB, and a new segment is create to map away up to 4GB. + +2006-05-02 Nick Kledzik + + * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack + overlaps shared region + +----- Tagged ld64-52.1 + +2006-05-01 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle + the __data section too. + +----- Tagged ld64-52 + +2006-04-28 Nick Kledzik + + 64-bit: 9A152 TextEdit crashes in dlopen on bring-up + * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection + +2006-04-28 Nick Kledzik + + 64 Bit: Development build of ppc64 TextEdit gets confused about static variables + * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol + + + +2006-04-21 Nick Kledzik + + * src/Options.cpp: fix default address for ppc64 custom stack + * src/MachOWriterExecutable.cpp: fix set up of ppc64 custom stack + + +2006-04-14 Nick Kledzik + + * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name + +----- Tagged ld64-51.1 + +2006-04-13 Nick Kledzik + + 64-bit: 9A152 TextEdit crashes in dlopen on bring-up + * src/MachOReaderRelocatable.hpp: when detecting anonymous non-lazy-pointers disqualify data + that points to static or global symbols + * src/ld.cpp: print version of ld64 in error messages + + +----- Tagged ld64-51 + +2006-04-11 Nick Kledzik + + exported symbols not properly stripped + * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope() + +2006-03-31 Nick Kledzik + + ld64 fails when linking debug ppc64 HIToolbox + * src/MachOReaderRelocatable.hpp: handle anonymous non-lazy pointers encoded with local relocations + * src/MachOWriterExecutable.hpp: in -r mode, only generated INDIRECT_SYMBOL_LOCAL for non-lazy targets that + + +2006-03-31 Nick Kledzik + + ld64 should remove generated file if link errors out + * src/MachOWriterExecutable.hpp: catch exceptions in Writer::write(), delete output file, and rethrow + + +----- Tagged ld64-50 + + +2006-03-29 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols + * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space + +2006-03-28 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info + +----- Tagged ld64-49.1 + +2006-03-25 Nick Kledzik + + * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB + +----- Tagged ld64-49 + +2006-03-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: dyld is allowed to have synthesized non-lazy pointers + ld64 is after processing bad GSYM stabs + * src/MachOReaderRelocatable.hpp: if a GSYM is found that does not match any data symbol, suppress it + +2006-03-23 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceFinal() fix when x86::kPointer is for an + external relocation + +2006-03-23 Nick Kledzik + + * src/Options.cpp: change macosx-min-version to default to a per-architecture setting + add warning if -pagezero_size is not page aligned + * src/MachOWriterExecutable.hpp: properly handle external relocations for ppc64 with 4GB pagezero + * src/machochecker.cpp: sanity check relocation records + +----- Tagged ld64-48 + +2006-03-21 Nick Kledzik + + 64bit: passing function pointer to another function passes the wrong function address + * src/MachOReaderRelocatable.hpp: when processing a non-lazy pointer to a static function, don't accidentally + match it to a STAB symbol. + +2006-03-21 Nick Kledzik + + .eh symbols make up 13% of libstdc++'s stripped binary size + * src/ObjectFile.h: add ReaderOptions.fForFinalLinkedImage + * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage + * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image + +2006-03-21 Nick Kledzik + + ld64 does not parse optional second argument to -filelist + * unit-tests/test-cases/filelist: added + * src/Options.cpp: in Options::loadFileList() handle comma option + + +----- Tagged ld64-47.1 + + +----- Tagged ld64-47 + + +----- Tagged ld64-46 + +2006-03-10 Nick Kledzik + + ld64 should figure out architecture from .o files + * unit-tests/test-cases/auto-arch: added + * src/ld.cpp: added Linker::inferArchitecture() to scan .o files are infer architecture to link + * src/MachOReaderArchive.hpp: enhanced validFile() to look deeper into archive and really valdate + * src/MachOWriterExecutable.hpp: stop using fOptions.architecture() + * src/Options.cpp: stop defaulting to ppc64 + + +2006-03-09 Nick Kledzik + + Need "intentionally left blank" dylib stubs + * unit-tests/include/common.makefile: add VALID_ARCHS + * unit-tests/run-all-unit-tests: set up VALID_ARCHS + * unit-tests/test-cases/blank-stubs: add test case + * src/ld.cpp: in addDylib(), detect and ignore blank stubs + * src/MachOReaderDylib.hpp: in constructor, handle blank stubs + +2006-03-09 Nick Kledzik + + crash in stub with 2GB pagezero + * src/MachOWriterExecutable.hpp: StubAtom can't be no-pic if a large zero-page is used + +2006-03-06 Nick Kledzik + + * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two + +----- Tagged ld64-45 + + +2006-03-06 Nick Kledzik + + LP64/9A122: ld64: hang when trying to link DiscRecording framework + * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion + + +----- Tagged ld64-44 + +2006-03-04 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix again test for detection of anonymous non-lazy-pointer. + Error out if .o file contains old __DWARFA style dwarf. + +2006-03-02 Nick Kledzik + + * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. + +----- Tagged ld64-43 + + +2006-03-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: tighten detection of anonymous non-lazy-pointer + +----- Tagged ld64-42 + +2006-02-28 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment + +2006-02-28 Nick Kledzik + + SWB: ld64-37 (can't resolve symbol ___dso_handle) + * src/MachOWriterExecutable.hpp: add class DsoHandleAtom + +2006-02-28 Nick Kledzik + + * unit-tests/test-cases/literals-coalesce-alignment: added test case + * src/ld.cpp: when coalescing strings pick one with greater alignment + ld64: CG link failed because lo14 reference to anonymous non-lazy-pointer not aligned + * unit-tests/test-cases/relocs-c/test.c: tweak to fail like 4458660 + * src/MachOReaderRelocatable.hpp: detect anonymous non-lazy-pointer and transform into real non-lazy-pointers + +----- Tagged ld64-41 + +2006-02-24 Nick Kledzik + + * src/Options.cpp: Warning about -no_dead_strip_inits_and_terms and -i options. + Fix -weak-l option. + +----- Tagged ld64-40 + +2006-02-24 Nick Kledzik + + Leopard9A113: ppc64 libstdc++.dylib initializer crashes in pthread_once + * unit-tests/test-cases/multiple-entry-points: added + * src/MachOReaderRelocatable.hpp: make sure that if there are multiple symbols with the same + address, that we properly make zero length atoms for all but last symbol + +2006-02-24 Nick Kledzik + + * src/Options.cpp: ld64 doesn't realpath(3) B&I tracing paths + +2006-02-24 Nick Kledzik + + * src/Options.cpp: 9A110: ld64 can't deal with section names >16 chars + +2006-02-23 Nick Kledzik + + * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations + * src/Options.cpp: use vector.reserve() to minimize re-allocations + * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations + * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations + * src/ld.cpp: use vector.reserve() to minimize re-allocations + +2006-02-23 Nick Kledzik + + ld64 creates corrupt executables (and has malloc errors) with -headerpad option + * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom::setSize() to update fLargestAtomSize + * unit-tests/test-cases/header-pad: added + +2006-02-23 Nick Kledzik + + ld64 creates invalid static executables + * src/MachOWriterExecutable.hpp: Change MachHeaderAtom::copyRawContent() to create correct header + for static executables. Change SymbolTableLoadCommandsAtom to skip LC_DYSYMTAB for static executables + * src/machochecker.cpp: Add tests that static executables are well formed + * unit-tests/test-cases/static-executable: added + +2006-02-22 Nick Kledzik + + * src/Options.cpp: chnage printf on unknown arg to a throw + +----- Tagged ld64-39 + +2006-02-20 Nick Kledzik + + * unit-tests/test-cases/read-only-relocs: added new test case + * src/MachOWriterExecutable.hpp: detect and error on relocs in read-only sections + * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs + +2006-02-20 Nick Kledzik + + * unit-tests/test-cases/stabs-coalesce: added new test case + * src/ld.cpp.hpp: in collectStabs removed unused stabs + +----- Tagged ld64-38 + +2006-02-17 Nick Kledzik + + * src/MachOWriterExecutable.hpp: set correct n_sect field of stabs + +2006-02-15 Nick Kledzik + + * src/MachOReaderArchive.hpp: with -all_load skip over both kinds of SYMDEFs + * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case + +----- Tagged ld64-37 + +2006-02-13 Eric Christopher + + * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. + Adjust whitespace. + +2006-02-13 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceRelocatable() fix kPCRel32 for external case + +2006-02-13 Nick Kledzik + + * unit-tests/test-cases/zero-fill: added + * src/machochecker.cpp: check that S_ZEROFILL have no file offset + * src/MachOWriterExecutable.hpp: rework assignFileOffsets() to fix rdar://problem/4441145 + +2006-02-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix use of first zero-length c-string in .o file + +2006-02-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix uninitialized fAlignment + +2006-02-12 Nick Kledzik + + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add pointer-diff cases + * src/Architectures.hpp: make size explicit in ppc/ppc64 kPointerDiff + * src/MachOReaderRelocatable.hpp: don't allow kPointerDiff64 for ppc (just ppc64) + * src/MachOWriterExecutable.cpp: set proper r_length for ld -r of kPointerDiff + +----- Tagged ld64-36 + +2006-02-08 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions + +2006-02-08 Nick Kledzik + + * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section + Keep S_COALESCED attribute for __eh_frame + +2006-02-08 Nick Kledzik + + * src/ld.cpp: Temporarily turn allowable client errors into warnings + * unit-tests/test-cases/allowable-clientMakefile: Temporarily let warnings be ok for above + * src/MachOWriterExecutable.hpp: fix ld -r to not use external relocations for symbols make static + +2006-02-08 Nick Kledzik + + * src/ld.cpp: A sibling in an umbrella can always link with its other siblings + * unit-tests/test-cases/allowable-client: add test case for above + +2006-02-08 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support LOCAL non-lazy pointers to hidden symbols + * src/machochecker.cpp: verify indirect symbol table + * unit-tests/test-cases/private-non-lazy: added test case + +2006-02-07 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix calculation of file offsets in ld -r mode + * src/machochecker.cpp: verify segment file offsets are within file + +----- Tagged ld64-35 + +2006-02-06 Nick Kledzik + + * ld.cpp: allow parent of sub-framework to link + * unit-tests/test-cases/allowable-client/Makefile: added cases for parent and clients of parent + +2006-02-04 Nick Kledzik + + * unit-tests/test-cases/relocs-c/test.c: added some array cases + * src/MachOReaderRelocatable.hpp: factor out makeReferenceToEH() + * src/MachOWriterExecutable.hpp: add initial support for non-lazy pointer synthesis + +----- Tagged ld64-34 + +2006-02-04 Nick Kledzik + + * src/ld.cpp: fix -no_arch_warnings + fix -undefined warning + Do BINCL/EINCL optimization for gfull stabs + Implement "essential symbols" for stabs (-Sp) + Fix allowable clients to only test on direct libraries + * src/MachOReaderRelocatable.hpp: support BINCL/EINCL stabs + +2006-02-03 Nick Kledzik + + * src/machochecker.cpp: add code to check load command alignment + * src/MachOWriterExecutable.hpp: make load command alignment depend on architecture + +2006-02-03 Nick Kledzik + + * unit-tests/test-cases/literals-coalesce: added + * src/MachOReaderRelocatable.hpp: assure all targets of low14 ppc relocs are at least 4-byte alignmented + +----- Tagged ld64-33 + +2006-02-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals + * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 + +----- Tagged ld64-32 + +2006-02-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms + +2006-02-02 Nick Kledzik + + * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one + * unit-tests/test-cases/archive-weak: add test case for above + * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself + * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list + +2006-02-01 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 + +2006-02-01 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms + +----- Tagged ld64-31 + +2006-02-01 Eric Christopher + + * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling... + * unit-tests/include/common.makefile: ... here. + * unit-tests/bin/fail-if-stdin.pl: New. + * unit-tests/test-cases/no-uuid: Ditto. + * src/ld.cpp (Linker::) Add fCreateUUID. + (::Linker): Initialize. + (::collectStabs): Use. Set if dwarf or we have a UUID already. + (::writeOutput): Pass as argument to Writer::write along with option. + * src/Options.h (Option::emitUUID): Declare. + (Option::fEmitUUID): Ditto. + * src/Options.cpp (Option::emitUUID): New. + (parse): Handle -no_uuid. + * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID. + * src/ExecutableFile.h (Writer::Write): Add createUUID boolean. + * src/MachOWriterExecutable: Add UUID forward declaration. + (fUUIDAtom): New. + (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size + to zero at start. + (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID. + (MachHeaderAtom::copyRawContent): Don't count a load command if its size is + 0. + (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit. + + +2006-01-31 Nick Kledzik + + * unit-tests/test-cases/dwarf-debug-notes : Added + * src/ld.cpp: don't generate debug note for .eh symbols + * src/MachOReaderRelocatable.hpp: make dwarf line info to atom matching faster and better + +2006-01-31 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard + * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers + +2006-01-31 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better error message for bad relocs + * src/ObjectDump.cpp: add emacs tab settings + * src/SectCreate.h: ditto + * src/SectCreate.cpp: ditto + * src/machochecker.cpp: ditto + * src/ExecutableFile.h: ditto + +2006-01-30 Eric Christopher + + * src/ExecutableFile.h: Indent. + +2006-01-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: performance improvements + * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore + +2006-01-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above + +2006-01-29 Nick Kledzik + + * unit-tests/test-cases/weak_import: added test case + * src/ld.cpp: move code for weak_import mismatch to writer + * src/ObjectFile.h: remove ImportWeakness methods + * src/MachOReaderDylib.hpp: ditto + * src/SectCreate.cpp: ditto + * src/Architectures.hpp: add new ReferenceKinds for weak_imports + * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds + * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches + +2006-01-29 Nick Kledzik + + * src/Options.cpp: verify -allow_stack_execute is only used on main executables + +2006-01-29 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff + * src/debugline.c: sync with latest dwarf reader from Geoff + +2006-01-27 Eric Christopher + + * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. + +2006-01-27 Eric Christopher + + * src/Options.h (Options): Add hasExecutableStack, fExecutableStack. + * src/Options.cpp (Options::hasExecutableStack): New. + (Options::parse): Parse -allow_stack_execute. + * src/MachOWriterExecutable.hpp (MachHeaderAtom::copyRawContent): + Implement MH_ALLOW_STACK_EXECUTION. + * unit-tests/include/common.makefile (FAIL_IF_EMPTY): New. + * unit-tests/bin/fail-if-no-stdin.pl: New file. + * unit-tests/test-cases/allow-stack-execute: New directory. + +2006-01-27 Nick Kledzik + + * src/MachOFileAbstraction.hpp: rely on latest system headers + * src/MachOWriterExecutable.hpp: fix ppc stubs. + wrote new relocationNeededInFinalLinkedImage() to replace common code + +2006-01-27 Eric Christopher + + * src/ld.cpp (logTraceInfo): New. + (Linker::addArchive): Use. + (Linker::addDylib): Ditto. + * src/ObjectFile (ReaderOptions::fTraceOutputFile): New. + * src/MachOReaderArchive.hpp (Reader::Reader): Move trace + logging to Linker::addArchive. + * src/Options.cpp (parsePreCommandLineEnvironment): Check + LD_PRINT_FILE if tracing dylibs or archives. + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state + +2006-01-26 Nick Kledzik + + Rewrite all stabs processing. + Move sythesize of debug notes into ld.cpp + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs + +2006-01-25 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: special case building in Curry + +2006-01-25 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis + +2006-01-24 Eric Christopher + + * src/ld.cpp (Linker::createReaders): Change logging title to XBS. + (Linker::addDylib): Ditto. + * src/MachOReaderArchive.hpp (Reader::Reader): Ditto. + * src/Options.h (fPrintOptions): New. + * src/Options.cpp (Options::Options): Initialize above. + (Options::checkForFile): Change logging title to XBS. + (Options::findFramework): Ditto. + (Options::parse): Add log for options. + (Options::parsePreCommandLineEnvironmentSettings): Add LD_TRACE_ARCHIVES, + LD_TRACE_DYLIBS, and LD_PRINT_OPTIONS. + +2006-01-24 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better C++ eh parsing + +2006-01-23 Eric Christopher + + * unit-tests/bin/fail-if-exit-zero.pl: New. + * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use. + * unit-tests/allowable-client: New test. + * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib. + * src/Options.h (allowableClients): New. + (clientName): Ditto. + (fAllowableClients): Ditto. + (fClientName): Ditto. + * src/Options.cpp: Implement above. + (parse): Handle -allowable_client and -client_name. + * src/MachOReaderDylib.hpp (getAllowableClients): New. + (fAllowableClients): Ditto. + (Reader): Process LC_SUB_CLIENT load command. + * src/ObjectFile.h (parentUmbrella): New. + (getAllowableClients): New. + * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New. + +2006-01-23 Nick Kledzik + + * unit-tests/test-cases/archive-basic: added + * src/ld.cpp: fix shadowed local variable + * src/FileAbstraction.hpp: ld64 shouldn't inline when building debug + +2006-01-23 Nick Kledzik + + * src/ld.cpp: fix symbol not found error message + * src/MachOReaderDylib.hpp: add logging to hash table + * src/MachOReaderRelocatable.hpp: enable stabs processing. Handle static functions with stubs + handle labeled cstrings. + * src/MachOWriterExecutable.hpp: properly suppress atoms not in symbol table. fix low14 error check. + add StubAtomHelper. + * unit-tests/test-cases/relocs-literals/test.c: add more interesting edge cases + +2006-01-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes + +2006-01-16 Nick Kledzik + + * src/debugline.{sh}: added + * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf + * src/MachOWriterExecutable.hpp: fix lazy pointer section + * src/ObjectDump.hpp: Fix conditionalization + * unit-tests/test-cases/dwarf-strip: added + +2006-01-11 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 + * src/ObjectDump.hpp: Support -arch option + +2006-01-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs for ppc64 + * src/MachOFileAbstraction.hpp: fix typo for macho_routines + * ld64.xcodeproj/project.pbxproj: add machochecker target + * src/machochecker.cpp: new skeleton for checking mach-o file bit + * unit-tests/: Add support for running machochecker + +2006-01-10 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed + * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime + +2006-01-09 Nick Kledzik + + * track modification time of .o files so that sythesized OSO stab will have it + +2006-01-09 Nick Kledzik + + * src/MachOFileAbstraction.hpp: add macho_uuid_command + * src/MachOWriterExecutable.cpp: add UUID load command to generated files + +2006-01-09 Nick Kledzik + + * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped + * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped + +2006-01-05 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support new relocations + +2006-01-05 Nick Kledzik + + * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB + * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor + +2006-01-05 Nick Kledzik + + refactor: transform Atom::dontStripName() to getSymbolTableInclusion() + * src/ld.cpp: pass dyld_stub_binding_helper to writer + * src/MachOReaderRelocatable.hpp: update synthesized stabs + Ignore stubs and lazy pointers in .o files + Support initializers and terminators + * src/MachOWriterExecutable.hpp: synthesize stubs and lazy pointers as needed + * ld64.xcodeproj/project.pbxproj: change Release target to build with dwarf + +2006-01-03 Eric Christopher + + * src/Options.h (multipleDefinitionsInDylibs): Declare. + (overridingDefinitionInDependentDylib): Ditto. + (warnOnMultipleDefinitionsInObjectFiles): Ditto. + (multiplyDefined): Remove. + (multiplyDefinedUnused): Ditto. + (fMultiplyDefined): Ditto. + (fWarnOnMultiplyDefined): New. + (fMultiplyDefinedDynamic): Ditto. + * src/Options.cpp (Options::Options): Initialize above. + (overridingDefinitionInDependentDylib): New. + (multipleDefinitionsInDylibs): Ditto. + (warnOnMultipleDefinitionsInObjectFiles): Ditto. + (parse): Update comments. Fix parsing of -y option. + Update error message for -dead_strip. Parse above + options. + +2006-01-02 Nick Kledzik + + * Refactor: move Atom::writeContent() to Writer + +2005-12-23 Nick Kledzik + + * Reworked, simplify, and document test harness + * unit-tests/README: Added + +2005-12-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fixes for Objective-C + * unit-tests/test-cases/relocs-objc: Added + +2005-12-22 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair + * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf + +2005-12-21 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Fix parsing of literal sections + * src/MachOWriterExecutable.hpp: Fix writing of literal sections + * unit-tests/test-cases/relocs-literals: Added + +2005-12-15 Eric Christopher + + * src/Options.h (enum Treatment): New. + (enum PICTreatment): Delete. + (enum VersionMin): New. + (prebind): Declare. + (macosxVersionMin): Ditto. + (multiplyDefined): Ditto. + (multiplyDefinedUnused): Ditto. + (setVersionMin): Ditto. + (setPICTreatment): Delete. + (setReadOnlyRelocTreatment): Ditto. + (picTreatment): Adjust return type. + (parseTreatment): New. + (fPrebind): Ditto. + (fVersionMin): Ditto. + (fPICTreatment): Change type. + (fMultiplyDefined): New. + (fMultiplyDefinedUnused): Ditto. + (fLimitUndefinedSymbols): Ditto. + + * src/Options.cpp: Fix whitespace. Add comments on options. + (Options::Options): Add initializers for new variables. + (Options::prebind): New. + (Options::macosxVersionMin): Ditto. + (Options::parseTreatment): Ditto. + (Options::setVersionMin): Ditto. + (Options::setReadOnlyRelocTreatment): Delete. + (Options::setPICTreatment): Ditto. + (Options::Parse): Update for above. Add comments. + +2005-12-15 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Add comments about dwarf + +2005-12-14 Nick Kledzik + + * src/ELFFileAbstraction.hpp: Added + * src/ELFReaderRelocatable.hpp: Added + * Lot of fixes for new architecture + * Added __OPEN_SOURCE__ to "Preprocessor Macros" to disable new architecture support by default + +2005-12-13 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections + * unit-tests/test-cases/dwarf-ignore: added + +2005-12-12 Nick Kledzik + + * Added test harness and three initial tests: + relocs-asm, relocs-c, and hello-world + +2005-12-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Massive refactoring: + Now there are three Atom classes, Chopping into Atoms + is done on label boundaries or by knowledge of special + sections, Share lots of ppc/ppc64 code. + Stabs process code is temporarily disabled. + +2005-12-12 Nick Kledzik + + * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort + +2005-12-11 Eric Christopher + + * src/Options.cpp: Reformat. + * src/Options.h: Ditto. + +2005-12-07 Eric Christopher + + * src/MachOReaderRelocatable.hpp (Atom::getAlignment): + When calculating alignment of an Atom, take into account + the alignment from which we pulled the Atom. + +2005-12-06 Nick Kledzik + + * src/Options.cpp src/Options.h: Add design comments + +2005-12-05 Eric Christopher + + * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and + i386 linkers. + +2005-12-05 Eric Christopher + + * ChangeLog: New file. + +2005-12-02 Nick Kledzik + + * src/ObjectFile.h: Add design comments + +2005-11-30 Nick Kledzik + + * Fix uses of __OPEN_SOURCE__ + +2005-11-28 Nick Kledzik + + * Refactor Atom to use getDefinitionKind() + +2005-11-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode + +2005-11-18 Nick Kledzik + + * x86 tweaks + +2005-11-18 Nick Kledzik + + * src/ObjectDump.cpp: make work with command line arguments + +2005-11-18 Nick Kledzik + + * Massive rework to remove preprocessor conditionals and use templates + +2005-11-14 Nick Kledzik + + * Created new Subversion repository for ld64 from cvs tag ld64-27.2 diff --git a/ld64/FireOpal/APPLE_LICENSE b/ld64/FireOpal/APPLE_LICENSE deleted file mode 100644 index fe81a60..0000000 --- a/ld64/FireOpal/APPLE_LICENSE +++ /dev/null @@ -1,367 +0,0 @@ -APPLE PUBLIC SOURCE LICENSE -Version 2.0 - August 6, 2003 - -Please read this License carefully before downloading this software. -By downloading or using this software, you are agreeing to be bound by -the terms of this License. If you do not or cannot agree to the terms -of this License, please do not download or use the software. - -1. General; Definitions. This License applies to any program or other -work which Apple Computer, Inc. ("Apple") makes publicly available and -which contains a notice placed by Apple identifying such program or -work as "Original Code" and stating that it is subject to the terms of -this Apple Public Source License version 2.0 ("License"). As used in -this License: - -1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is -the grantor of rights, (i) claims of patents that are now or hereafter -acquired, owned by or assigned to Apple and (ii) that cover subject -matter contained in the Original Code, but only to the extent -necessary to use, reproduce and/or distribute the Original Code -without infringement; and (b) in the case where You are the grantor of -rights, (i) claims of patents that are now or hereafter acquired, -owned by or assigned to You and (ii) that cover subject matter in Your -Modifications, taken alone or in combination with Original Code. - -1.2 "Contributor" means any person or entity that creates or -contributes to the creation of Modifications. - -1.3 "Covered Code" means the Original Code, Modifications, the -combination of Original Code and any Modifications, and/or any -respective portions thereof. - -1.4 "Externally Deploy" means: (a) to sublicense, distribute or -otherwise make Covered Code available, directly or indirectly, to -anyone other than You; and/or (b) to use Covered Code, alone or as -part of a Larger Work, in any way to provide a service, including but -not limited to delivery of content, through electronic communication -with a client other than You. - -1.5 "Larger Work" means a work which combines Covered Code or portions -thereof with code not governed by the terms of this License. - -1.6 "Modifications" mean any addition to, deletion from, and/or change -to, the substance and/or structure of the Original Code, any previous -Modifications, the combination of Original Code and any previous -Modifications, and/or any respective portions thereof. When code is -released as a series of files, a Modification is: (a) any addition to -or deletion from the contents of a file containing Covered Code; -and/or (b) any new file or other representation of computer program -statements that contains any part of Covered Code. - -1.7 "Original Code" means (a) the Source Code of a program or other -work as originally made available by Apple under this License, -including the Source Code of any updates or upgrades to such programs -or works made available by Apple under this License, and that has been -expressly identified by Apple as such in the header file(s) of such -work; and (b) the object code compiled from such Source Code and -originally made available by Apple under this License. - -1.8 "Source Code" means the human readable form of a program or other -work that is suitable for making modifications to it, including all -modules it contains, plus any associated interface definition files, -scripts used to control compilation and installation of an executable -(object code). - -1.9 "You" or "Your" means an individual or a legal entity exercising -rights under this License. For legal entities, "You" or "Your" -includes any entity which controls, is controlled by, or is under -common control with, You, where "control" means (a) the power, direct -or indirect, to cause the direction or management of such entity, -whether by contract or otherwise, or (b) ownership of fifty percent -(50%) or more of the outstanding shares or beneficial ownership of -such entity. - -2. Permitted Uses; Conditions & Restrictions. Subject to the terms -and conditions of this License, Apple hereby grants You, effective on -the date You accept this License and download the Original Code, a -world-wide, royalty-free, non-exclusive license, to the extent of -Apple's Applicable Patent Rights and copyrights covering the Original -Code, to do the following: - -2.1 Unmodified Code. You may use, reproduce, display, perform, -internally distribute within Your organization, and Externally Deploy -verbatim, unmodified copies of the Original Code, for commercial or -non-commercial purposes, provided that in each instance: - -(a) You must retain and reproduce in all copies of Original Code the -copyright and other proprietary notices and disclaimers of Apple as -they appear in the Original Code, and keep intact all notices in the -Original Code that refer to this License; and - -(b) You must include a copy of this License with every copy of Source -Code of Covered Code and documentation You distribute or Externally -Deploy, and You may not offer or impose any terms on such Source Code -that alter or restrict this License or the recipients' rights -hereunder, except as permitted under Section 6. - -2.2 Modified Code. You may modify Covered Code and use, reproduce, -display, perform, internally distribute within Your organization, and -Externally Deploy Your Modifications and Covered Code, for commercial -or non-commercial purposes, provided that in each instance You also -meet all of these conditions: - -(a) You must satisfy all the conditions of Section 2.1 with respect to -the Source Code of the Covered Code; - -(b) You must duplicate, to the extent it does not already exist, the -notice in Exhibit A in each file of the Source Code of all Your -Modifications, and cause the modified files to carry prominent notices -stating that You changed the files and the date of any change; and - -(c) If You Externally Deploy Your Modifications, You must make -Source Code of all Your Externally Deployed Modifications either -available to those to whom You have Externally Deployed Your -Modifications, or publicly available. Source Code of Your Externally -Deployed Modifications must be released under the terms set forth in -this License, including the license grants set forth in Section 3 -below, for as long as you Externally Deploy the Covered Code or twelve -(12) months from the date of initial External Deployment, whichever is -longer. You should preferably distribute the Source Code of Your -Externally Deployed Modifications electronically (e.g. download from a -web site). - -2.3 Distribution of Executable Versions. In addition, if You -Externally Deploy Covered Code (Original Code and/or Modifications) in -object code, executable form only, You must include a prominent -notice, in the code itself as well as in related documentation, -stating that Source Code of the Covered Code is available under the -terms of this License with information on how and where to obtain such -Source Code. - -2.4 Third Party Rights. You expressly acknowledge and agree that -although Apple and each Contributor grants the licenses to their -respective portions of the Covered Code set forth herein, no -assurances are provided by Apple or any Contributor that the Covered -Code does not infringe the patent or other intellectual property -rights of any other entity. Apple and each Contributor disclaim any -liability to You for claims brought by any other entity based on -infringement of intellectual property rights or otherwise. As a -condition to exercising the rights and licenses granted hereunder, You -hereby assume sole responsibility to secure any other intellectual -property rights needed, if any. For example, if a third party patent -license is required to allow You to distribute the Covered Code, it is -Your responsibility to acquire that license before distributing the -Covered Code. - -3. Your Grants. In consideration of, and as a condition to, the -licenses granted to You under this License, You hereby grant to any -person or entity receiving or distributing Covered Code under this -License a non-exclusive, royalty-free, perpetual, irrevocable license, -under Your Applicable Patent Rights and other intellectual property -rights (other than patent) owned or controlled by You, to use, -reproduce, display, perform, modify, sublicense, distribute and -Externally Deploy Your Modifications of the same scope and extent as -Apple's licenses under Sections 2.1 and 2.2 above. - -4. Larger Works. You may create a Larger Work by combining Covered -Code with other code not governed by the terms of this License and -distribute the Larger Work as a single product. In each such instance, -You must make sure the requirements of this License are fulfilled for -the Covered Code or any portion thereof. - -5. Limitations on Patent License. Except as expressly stated in -Section 2, no other patent rights, express or implied, are granted by -Apple herein. Modifications and/or Larger Works may require additional -patent licenses from Apple which Apple may grant in its sole -discretion. - -6. Additional Terms. You may choose to offer, and to charge a fee for, -warranty, support, indemnity or liability obligations and/or other -rights consistent with the scope of the license granted herein -("Additional Terms") to one or more recipients of Covered Code. -However, You may do so only on Your own behalf and as Your sole -responsibility, and not on behalf of Apple or any Contributor. You -must obtain the recipient's agreement that any such Additional Terms -are offered by You alone, and You hereby agree to indemnify, defend -and hold Apple and every Contributor harmless for any liability -incurred by or claims asserted against Apple or such Contributor by -reason of any such Additional Terms. - -7. Versions of the License. Apple may publish revised and/or new -versions of this License from time to time. Each version will be given -a distinguishing version number. Once Original Code has been published -under a particular version of this License, You may continue to use it -under the terms of that version. You may also choose to use such -Original Code under the terms of any subsequent version of this -License published by Apple. No one other than Apple has the right to -modify the terms applicable to Covered Code created under this -License. - -8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in -part pre-release, untested, or not fully tested works. The Covered -Code may contain errors that could cause failures or loss of data, and -may be incomplete or contain inaccuracies. You expressly acknowledge -and agree that use of the Covered Code, or any portion thereof, is at -Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND -WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND -APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE -PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM -ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT -NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF -MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR -PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD -PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST -INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE -FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, -THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR -ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO -ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE -AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. -You acknowledge that the Covered Code is not intended for use in the -operation of nuclear facilities, aircraft navigation, communication -systems, or air traffic control machines in which case the failure of -the Covered Code could lead to death, personal injury, or severe -physical or environmental damage. - -9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO -EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, -SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING -TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR -ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, -TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF -APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY -REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF -INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY -TO YOU. In no event shall Apple's total liability to You for all -damages (other than as may be required by applicable law) under this -License exceed the amount of fifty dollars ($50.00). - -10. Trademarks. This License does not grant any rights to use the -trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", -"QuickTime", "QuickTime Streaming Server" or any other trademarks, -service marks, logos or trade names belonging to Apple (collectively -"Apple Marks") or to any trademark, service mark, logo or trade name -belonging to any Contributor. You agree not to use any Apple Marks in -or as part of the name of products derived from the Original Code or -to endorse or promote products derived from the Original Code other -than as expressly permitted by and in strict compliance at all times -with Apple's third party trademark usage guidelines which are posted -at http://www.apple.com/legal/guidelinesfor3rdparties.html. - -11. Ownership. Subject to the licenses granted under this License, -each Contributor retains all rights, title and interest in and to any -Modifications made by such Contributor. Apple retains all rights, -title and interest in and to the Original Code and any Modifications -made by or on behalf of Apple ("Apple Modifications"), and such Apple -Modifications will not be automatically subject to this License. Apple -may, at its sole discretion, choose to license such Apple -Modifications under this License, or on different terms from those -contained in this License or may choose not to license them at all. - -12. Termination. - -12.1 Termination. This License and the rights granted hereunder will -terminate: - -(a) automatically without notice from Apple if You fail to comply with -any term(s) of this License and fail to cure such breach within 30 -days of becoming aware of such breach; - -(b) immediately in the event of the circumstances described in Section -13.5(b); or - -(c) automatically without notice from Apple if You, at any time during -the term of this License, commence an action for patent infringement -against Apple; provided that Apple did not first commence -an action for patent infringement against You in that instance. - -12.2 Effect of Termination. Upon termination, You agree to immediately -stop any further use, reproduction, modification, sublicensing and -distribution of the Covered Code. All sublicenses to the Covered Code -which have been properly granted prior to termination shall survive -any termination of this License. Provisions which, by their nature, -should remain in effect beyond the termination of this License shall -survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, -12.2 and 13. No party will be liable to any other for compensation, -indemnity or damages of any sort solely as a result of terminating -this License in accordance with its terms, and termination of this -License will be without prejudice to any other right or remedy of -any party. - -13. Miscellaneous. - -13.1 Government End Users. The Covered Code is a "commercial item" as -defined in FAR 2.101. Government software and technical data rights in -the Covered Code include only those rights customarily provided to the -public as defined in this License. This customary commercial license -in technical data and software is provided in accordance with FAR -12.211 (Technical Data) and 12.212 (Computer Software) and, for -Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- -Commercial Items) and 227.7202-3 (Rights in Commercial Computer -Software or Computer Software Documentation). Accordingly, all U.S. -Government End Users acquire Covered Code with only those rights set -forth herein. - -13.2 Relationship of Parties. This License will not be construed as -creating an agency, partnership, joint venture or any other form of -legal association between or among You, Apple or any Contributor, and -You will not represent to the contrary, whether expressly, by -implication, appearance or otherwise. - -13.3 Independent Development. Nothing in this License will impair -Apple's right to acquire, license, develop, have others develop for -it, market and/or distribute technology or products that perform the -same or similar functions as, or otherwise compete with, -Modifications, Larger Works, technology or products that You may -develop, produce, market or distribute. - -13.4 Waiver; Construction. Failure by Apple or any Contributor to -enforce any provision of this License will not be deemed a waiver of -future enforcement of that or any other provision. Any law or -regulation which provides that the language of a contract shall be -construed against the drafter will not apply to this License. - -13.5 Severability. (a) If for any reason a court of competent -jurisdiction finds any provision of this License, or portion thereof, -to be unenforceable, that provision of the License will be enforced to -the maximum extent permissible so as to effect the economic benefits -and intent of the parties, and the remainder of this License will -continue in full force and effect. (b) Notwithstanding the foregoing, -if applicable law prohibits or restricts You from fully and/or -specifically complying with Sections 2 and/or 3 or prevents the -enforceability of either of those Sections, this License will -immediately terminate and You must immediately discontinue any use of -the Covered Code and destroy all copies of it that are in your -possession or control. - -13.6 Dispute Resolution. Any litigation or other dispute resolution -between You and Apple relating to this License shall take place in the -Northern District of California, and You and Apple hereby consent to -the personal jurisdiction of, and venue in, the state and federal -courts within that District with respect to this License. The -application of the United Nations Convention on Contracts for the -International Sale of Goods is expressly excluded. - -13.7 Entire Agreement; Governing Law. This License constitutes the -entire agreement between the parties with respect to the subject -matter hereof. This License shall be governed by the laws of the -United States and the State of California, except that body of -California law concerning conflicts of law. - -Where You are located in the province of Quebec, Canada, the following -clause applies: The parties hereby confirm that they have requested -that this License and all related documents be drafted in English. Les -parties ont exige que le present contrat et tous les documents -connexes soient rediges en anglais. - -EXHIBIT A. - -"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights -Reserved. - -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." diff --git a/ld64/FireOpal/ChangeLog b/ld64/FireOpal/ChangeLog deleted file mode 100644 index 1a79a07..0000000 --- a/ld64/FireOpal/ChangeLog +++ /dev/null @@ -1,542 +0,0 @@ - -2008-07-10 Nick Kledzik - - * src/LTOReader.hpp: improve missing symbol error message - - -2008-07-08 Nick Kledzik - - ld: add support for mllvm LTO options - * src/Options.cpp: support -mllvm option - * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options - * src/ld.cpp: pass llvmOptions to optimize() - * src/Options.h: add fLLVMOptions - * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() - * src/ObjectFile.h: add llvmOptions parameter to optimize() - * unit-tests/test-cases/lto-llvm-options: add test case - - -2008-06-04 Nick Kledzik - - * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message - -2008-06-04 Nick Kledzik - - * src/ObjectFile.h: add deadAtoms parameter to optimize() - * src/ld.cpp: ditto - * src/ArchiveReader.hpp: ditto - * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs - * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away - * unit-tests/test-cases/lto-weak-native-override: add test case - - -2008-06-04 Nick Kledzik - - LTO : 176.gcc and 177.mesa build failure at -O4 - * src/LTOReader.hpp: make sure internal is returned by getAtoms() - * unit-tests/test-cases/lto-archive-dylib: update test case - - -2008-05-06 Nick Kledzik - - ARM ld should take W bit off of maxprot for __TEXT segment - * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments - - -2008-05-06 Nick Kledzik - - encryptable images may not be signable - * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section - - ------ Tagged ld64-85 (Xcode 3.1) - -2008-04-29 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include - - -2008-04-29 Nick Kledzik - - ld doesn't honor "rightmost" -syslibroot argument - * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots - - -2008-04-29 Nick Kledzik - - GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files - * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment - * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment - - -2008-04-17 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: better cpu subtype support - - -2008-04-14 Nick Kledzik - - ld64 has bad ARM branch island check - * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail - - -2008-04-10 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs - - ------ Tagged ld64-84.4 - -2008-04-10 Nick Kledzik - - SPEC2000/eon built with -mdynamic-no-pic won't run - * src/Architectures.hpp: added arm::kReadOnlyPointer - * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer - * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer - * src/machochecker.cpp: allow MH_PIE bit - * unit-tests/test-cases/switch-jump-table: added test cases - - ------ Tagged ld64-84.3 - -2008-04-09 Nick Kledzik - - -undefined dynamic_lookup busted - * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates - * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup - - ------ Tagged ld64-84.2 - -2008-04-04 Nick Kledzik - - * src/ld.cpp: don't add .eh symbols to symbol table in -r mode - * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing - - ------ Tagged ld64-84.1 - -2008-03-28 Nick Kledzik - - ld should prefer architecture-specific variant over generic in fat object file - * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture - * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files - * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc - - ------ Tagged ld64-84 - -2008-03-28 Nick Kledzik - - * src/LTOReader.hpp: don't print lto version, if lto is unavailable - - -2008-03-26 Nick Kledzik - - Add LD_WARN_COMMONS to BigBear builds - * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file - - -2008-03-26 Nick Kledzik - - Need encryption tag in mach-o file - linker should adjust arm final linked images so __text is never on the same page as the load commands - * src/MachOFileAbstraction.hpp: add support for encryption_info_command - * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption - * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom - * src/machochecker.cpp: validate LC_ENCRYPTION_INFO - - -2008-03-25 Nick Kledzik - - ld64 does not recognize LLVM bitcode archive files - * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp - * src/ArchiveReader.hpp: sniff each member and instantiate correct reader - * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader - * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp - * unit-tests/test-cases/llvm-integration: added test case - - -2008-03-25 Nick Kledzik - - ld64 should switch to new libLTO.dylib interface - Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc - * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface - * unit-tests/test-cases/llvm-integration: update and comment - * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib - * src/ld.cpp: rework and simplify Linker::optimize() - * src/ObjectDump.cpp: Add -nm option - - -2008-03-25 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem - * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem - - -2008-03-24 Nick Kledzik - - Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 - * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. - - -2008-03-21 Nick Kledzik - - * src/Options.cpp: warn if -seg1addr value is not page aligned - - -2008-03-21 Nick Kledzik - - Move ARM support outside of __OPEN_SOURCE__ - * src/ld.cpp: remove __OPEN_SOURCE__ around arm support - * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support - * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h - - ------ Tagged ld64-83.2 - -2008-03-15 Nick Kledzik - - ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results - * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files - * unit-tests/test-cases/objc-exported_symbols_list: added test case - - ------ Tagged ld64-83.1 - -2008-03-14 Nick Kledzik - - -iphone_version_min ==> -iphoneos_version_min - * src/Options.cpp: support -iphoneos_version_min as well - - ------ Tagged ld64-83 - -2008-03-10 Nick Kledzik - - ld needs to strip iphone_version_min option if invoking ld_classic - * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic - - -2008-03-04 Nick Kledzik - - ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) - * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs - * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework - * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools - * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() - * src/ld.cpp: pass lazy helper atom to writer - * doc/man/man1/ld.1: document new options - * unit-tests/test-cases/lazy-dylib-objc: add test case - * unit-tests/test-cases/lazy-dylib: add test case - - ------ Tagged ld64-82.7 - -2008-03-07 Nick Kledzik - - duplicate symbol literal-pointer@__OBJC@__message_refs@... - * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak - * unit-tests/test-cases/objc-selector-coalescing: added test case - - ------ Tagged ld64-82.6 - -2008-03-04 Nick Kledzik - - ld crashes building XsanFS for Snow Leopard Builds - * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() - * unit-tests/test-cases/tentative-and-archive: added test case - -2008-03-04 Nick Kledzik - - ld64 should not force building with gcc 4.0 - * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 - - -2008-02-29 Nick Kledzik - - Simulator frameworks are being build split-seg and not prebound - * src/Options.cpp: only splitseg if prebound - - -2008-02-29 Nick Kledzik - - Linker should not make GSYM debug note for .objc_category_* symbols - * src/ld.cpp: suppress GSYM debug notes for absolute symbols - * unit-tests/test-cases/objc-category-debug-notes: added test case - - -2008-02-29 Nick Kledzik - - non-ASCII CFString support is broken - * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring - * unit-tests/test-cases/cfstring-utf16: add test case - - -2008-02-25 Nick Kledzik - - ld -r -x - * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels - - ------ Tagged ld64-82.5 - -2008-02-12 Nick Kledzik - - x86_64: -stack_size failure when large __bss is used - * src/ld.cpp: only move section already in __DATA segment to new __huge section - * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section - - ------ Tagged ld64-82.4 - -2008-02-06 Nick Kledzik - - comdat warnings with ld -r of C++ .o files - * unit-tests/test-cases/eh-coalescing-r: added test case - * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static - - -2008-02-06 Devang Patel - - LTO of Bom framework with -dead_strip causes ld(1) crash - * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. - * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. - * unit-tests/test-cases/llvm-integration/a15.c: New. - * unit-tests/test-cases/llvm-integration/b15.c: New. - * unit-tests/test-cases/llvm-integration/c15.c: New. - -2008-02-05 Nick Kledzik - - * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used - ------ Tagged ld64-82.3 - -2008-02-04 Nick Kledzik - - ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves - * src/ObjectFile.h: add 10.6 - * src/Options.cpp: add 10.6 support - * src/MachOReaderDylib.hpp: recognize $os10.6$ - - ------ Tagged ld64-82.2 - -2008-01-30 Devang Patel - - Can't build 64-bit Intel binaries with LTO - ld64 fails to build with llvm-gcc-4.2 - * src/LLVMReader.hpp: Fix character count typo in strncmp call. - Use const char * to initialize temp. string. - * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction - instead of hard coding /Developer. - ------ Tagged ld64-82.1 - -2008-01-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs - - -2008-01-22 Nick Kledzik - - ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files - * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs - * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs - * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files - - ------ Tagged ld64-82 - -2008-01-18 Nick Kledzik - - Bad grammar used in ld warning: cannot exported hidden symbol - * src/ld.cpp: fix typo in warning string - - -2008-01-16 Nick Kledzik - - Bundle Loader does not work anymore when loader is a bundle - ld warns of incorrect architecture when linking a bundle to a bundle - * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages - * unit-tests/test-cases/bundle_loader: update test case - - -2008-01-16 Nick Kledzik - - ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) - * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S - - -2008-01-16 Nick Kledzik - - if ld crashes while writing output file, it should delete the half written file - * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete - output file on failure. - - -2008-01-16 Devang Patel - - * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. - - -2008-01-16 Nick Kledzik - - GC-supported library can't be linked into GC-required executable - * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and - allow gc-compatible code to be linked into anything. - * unit-tests/test-cases/objc-gc-checks: update test case - - -2008-01-15 Nick Kledzik - - no debug notes for custom named data - * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore - * unit-tests/test-cases/dwarf-debug-notes: update test case - ------ Tagged ld64-81.5 - -2008-01-14 Devang Patel - - llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 - * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references - after optimization. - * src/ld.cpp: Resolve additional unbounded references after optimization. - - -2008-01-14 Nick Kledzik - - PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes - * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs - * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs - - -2008-01-11 Nick Kledzik - - PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" - * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions - - -2008-01-11 Nick Kledzik - - * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list - - -2008-01-11 Nick Kledzik - - ld64(1) man page uses ambiguous term "suffix" - * doc/man/man1/ld.1: make meaning of "suffix" more explicit - - -2008-01-11 Nick Kledzik - - Obj-C Symbols in Leopard Can't Be Weak Linked - * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines - to dylibs to support Mac OS X 10.3.x dyld - - -2008-01-11 Nick Kledzik - - Unknown error with linker (dyld: unknown external relocation type) - * src/ld.cpp: fix crash when SO stabs are not balanced - - -2008-01-11 Devang Patel - - LTO does not work if expected output is a dynamic library - * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate - visibility info. - -2000-01-10 Nick Kledzik - - __cls_refs section is losing S_LITERAL_POINTERS section type - * src/MachOWriterExecutable.hpp: special case __cls_refs section - * unit-tests/test-cases/objc-literal-pointers: add test case - - -2008-01-03 Nick Kledzik - - wrong EH information might be used - Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom - has kGroupSubordinate references to the other atoms in the group. If the signature atom - is coalesced away, the linker follows kGroupSubordinate references and throws away the - other members of the group. - * unit-tests/test-cases/eh-coalescing: added test case - * src/ld.cpp: added markDead() and use propagate to subordinates - * src/Architectures.hpp: added kGroupSubordinate - * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom - and if used, from .eh atom to its LSDA atom. - * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp - ------ Tagged ld64-81.4.1 - -2007-12-19 Devang Patel - - * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. - -2007-12-19 Devang Patel - - * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). - -2007-12-19 Devang Patel - - print LLVM LTO version number in verbose mode - * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. - * src/Options.cpp: Use printLLVMVersion() in verbose mode. - -2007-12-19 Devang Patel - - print LLVM LTO version number in verbose mode - * src/Options.h: Add verbose() method to check fVerbose flag. - * src/LLVMReader.hpp: Print LLVM version string in verbose mode. - ------ Tagged ld64-81.4 - -2007-12-18 Devang Patel - - * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. - ------ Tagged ld64-81.3 - -2007-12-17 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths - - -2007-12-17 Devang Patel - - * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to - dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. - - -2007-12-14 Nick Kledzik - - gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) - * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs - * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static - - -2007-12-14 Devang Patel - - Enable Link Time Optimization in Opal - * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. - * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. - * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. - * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. - - -2007-12-13 Nick Kledzik - - SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... - * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly - ------ Tagged ld64-81.2 - diff --git a/ld64/FireOpal/doc/man/man1/ld.1 b/ld64/FireOpal/doc/man/man1/ld.1 deleted file mode 100644 index a8b0188..0000000 --- a/ld64/FireOpal/doc/man/man1/ld.1 +++ /dev/null @@ -1,676 +0,0 @@ -.Dd December 8, 2006 -.Dt ld 1 -.Os Darwin -.Sh NAME -.Nm ld -.Nd "linker" -.Sh SYNOPSIS -.Nm -files... -.Op options -.Op Fl o Ar outputfile -.Sh DESCRIPTION -The -.Nm ld -command combines several object files and libraries, resolves references, and -produces an ouput file. -.Nm ld -can produce a final linked image (executable, dylib, or bundle), or with the -r -option, produce another object file. If the -o option is not used, the output -file produced is named "a.out". -.Ss Universal -The linker accepts universal (multiple-architecture) input files, but -always creates a "thin" (single-architecture), standard Mach-O output file. -The architecture for the output file is specified using the -arch option. -If this option is not used, -.Nm ld -attempts to determine the output architecture by examining the object -files in command line order. The first "thin" -architecture determines that of the output file. If no input -object file is a "thin" file, the native 32-bit architecture for the host is used. -.Pp -Usually, -.Nm ld -is not used directly. Instead the -.Xr gcc(1) -compiler driver invokes -.Nm ld. -The compiler driver can be passed multiple -arch options and it will create a -universal final linked image by invoking -.Nm ld -multiple times and then running -.Xr lipo(1) -merge the outputs into a universal file. -.Ss Layout -The object files are loaded in the order in which they are specified on the -command line. The segments and the sections in those segments will appear in -the output file in the order they are encountered in the object files being linked. -All zero fill sections will appear after all non-zero fill sections in their segments. -Sections created from files with the -sectcreate option will be laid out at after -sections from .o files. The use of the -order_file option will alter the layout -rules above, and move the symbols specified to start of their section. -.Ss Libraries -A static library (aka static archive) is a collection of .o files with a table of contents -that lists the global symbols in the .o files. -.Nm ld -will only pull .o files out of a static library if needed to resolve some symbol reference. -Unlike traditional linkers, -.Nm ld -will continually search a static library while linking. There is no need to specify a static -library multiple times on the command line. -.Pp -A dynamic library (aka dylib or framework) is a final linked image. Putting a dynamic -library on the command line causes two things: 1) The generated final linked image -will have encoded that it depends on that dynamic library. 2) Exported symbols from the -dynamic library are used to resolve references. -.Pp -Both dynamic and static libraries are searched as they appear on the command line. -.Ss Search paths -.Nm ld -maintains a list of directories to search for a library or framework to use. The default -library search path is /usr/lib then /usr/local/lib. The -L option will add a new library search -path. The default framework search path is /Library/Frameworks then /System/Library/Frameworks. -(Note: previously, /Network/Library/Frameworks was at the end of the default path. If you need -that functionality, you need to explicitly add -F/Network/Library/Frameworks). -The -F option will a new framework search path. The -Z option will remove -the standard search paths. The -syslibroot option will prepend a prefix to all search -paths. -.Ss Two-level namespace -By default all references resolved to a dynamic library record the library to which -they were resolved. At runtime, dyld uses that information to directly resolve -symobls. The alternative is to use the -flat_namespace option. With flat namespace, -the library is not recorded. At runtime, dyld will search each dynamic library in load -order when resolving symbols. This is slower, but more like how other operating systems -resolve symbols. -.Ss Indirect dynamic libraries -If the command line specifies to link against dylib A, and when dylib A was built it linked -against dylib B, then B is considered an indirect dylib. -When linking for two-level namespace, ld does not look at indirect dylibs, except when -re-exported by a direct dylibs. On the other hand when linking for flat namespace, -ld does load all indirect dylibs and uses them to resolve references. -Even though indirect dylibs are specified via a full path, -.Nm ld -first uses the specified search paths to locate each indirect dylib. If one cannot -be found using the search paths, the full path is used. -.Ss Dynamic libraries undefines -When linking for two-level namespace, -.Nm ld -does not verify that undefines in dylibs actually -exist. But when linking for flat namespace, -.Nm ld -does check that all undefines from all loaded dylibs have a matching definition. -This is sometimes used to force selected functions to be loaded from a static library. -.Sh OPTIONS -.Ss Options that control the kind of output -.Bl -tag -.It Fl execute -The default. Produce a mach-o main executable that has file type MH_EXECUTE. -.It Fl dylib -Produce a mach-o shared library that has file type MH_DYLIB. -.It Fl bundle -Produce a mach-o bundle that has file type MH_BUNDLE. -.It Fl r -Merges object files to produce another mach-o object file with file type MH_OBJECT. -.It Fl dylinker -Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when building dyld. -.It Fl dynamic -The default. Implied by -dynamiclib, -bundle, or -execute -.It Fl static -Produces a mach-o file that does not use the dyld. Only used building the kernel. -.It Fl arch Ar arch_name -Specifies which architecture (e.g. ppc, ppc64, i386, x86_64) the output file should be. -.It Fl o Ar path -Specifies the name and location of the output file. If not specified, `a.out' is used. -.El -.Ss Options that control libraries -.Bl -tag -.It Fl l Ns x -This option tells the linker to search for libx.dylib or libx.a in the library search path. -If string x is of the form y.o, then that file is searched for in the same places, but without -prepending `lib' or appending `.a' or `.dylib' to the filename. -.It Fl weak-l Ns Ar x -This is the same as the -lx but forces the library and all references to it to be marked as weak imports. -That is, the library is allowed to be missing at runtime. -.It Fl weak_library Ar path_to_library -This is the same as listing a file name path to a library on the link line except that it forces the -library and all references to it to be marked as weak imports. -.It Fl reexport-l Ns Ar x -This is the same as the -lx but specifies that the all symbols in library x should be available to -clients linking to the library being created. This was previously done with a separate -sub_library option. -.It Fl reexport_library Ar path_to_library -This is the same as listing a file name path to a library on the link line and it specifies that the -all symbols in library path should be available to clients linking to the library being created. -This was previously done with a separate -sub_library option. -.It Fl lazy-l Ns Ar x -This is the same as the -lx but it is only for shared libraries and the linker -will construct glue code so that the shared library is not loaded until -the first function in it is called. -.It Fl lazy_library Ar path_to_library -This is the same as listing a file name path to a shared library on the link line -except that the linker will construct glue code so that the shared library is not -loaded until the first function in it is called. -.It Fl L Ns dir -Add -.Ar dir -to the list of directories in which to search for libraries. -Directories specified with -L are searched in the order they appear on the command line -and before the default search path. -.It Fl Z -Do not search the standard directories when searching for libraries and frameworks. -.It Fl syslibroot Ar rootdir -Prepend -.Ar rootdir -to all search paths when searching for libraries or frameworks. -.It Fl search_paths_first -By default the -lx and -weak-lx options first search for a file of the form `libx.dylib' in each directory -in the library search path, then a file of the form `libx.a' is searched for in the library search paths. -This option changes it so that in each path `libx.dylib' is searched for then `libx.a' before the -next path in the library search path is searched. -.It Fl framework Ar name[,suffix] -This option tells the linker to search for `name.framework/name' the framework search path. -If the optional suffix is specified the framework is first searched for the name with the suffix and then without -(e.g. look for `name.framework/name_suffix' first, if not there try `name.framework/name'). -.It Fl weak_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] but forces the framework and all -references to it to be marked as weak imports. -.It Fl reexport_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] but also specifies that the -all symbols in that framework should be available to clients linking to the library being created. -This was previously done with a separate -sub_umbrella option. -.It Fl lazy_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] except that the linker will -construct glue code so that the framework is not -loaded until the first function in it is called. You cannot directly access -data or Objective-C classes in a frameworked linked this way. -.It Fl F Ns dir -Add -.Ar dir -to the list of directories in which to search for frameworks. -Directories specified with -F are searched in the order they appear on the command line -and before the default search path. -.It Fl all_load -Loads all members of static archive libraries. -.It Fl ObjC -Loads all members of static archive libraries that implement an Objective-C class or category. -.El -.Ss Options that control additional content -.Bl -tag -.It Fl sectcreate Ar segname sectname file -The section -.Ar sectname -in the segment -.Ar segname -is created from the contents of file -.Ar file. -The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname) -from any other input. -.It Fl filelist Ar file[,dirname] -Specifies that the linker should link the files listed in -.Ar file . -This is an alternative to listing the files on the command line. -The file names are listed one per line separated only by newlines. (Spaces and tabs are assumed to be part of the file name.) -If the optional directory name, -.Ar dirname -is specified, it is prepended to each name in the list file. -.It Fl dtrace Ar file -Enables dtrace static probes when producing a final linked image. The file -.Ar file -must be a DTrace script which declares the static probes. -.El -.Ss Options that control optimizations -.Bl -tag -.It Fl dead_strip -Remove functions and data that are unreachable by the entry point or exported symbols. -.It Fl dead_strip_dylibs -Remove dylibs that are unreachable by the entry point or exported symbols. That is, -suppresses the generation of load command commands for dylibs which supplied no -symbols during the link. This option should not be used when linking against a dylib which -is required at runtime for some indirect reason such as the dylib has an important initializer. -.It Fl order_file Ar file -Alters the order in which functions and data are laid out. For each section in the output file, -any symbol in that section that are specified in the order file -.Ar file -is moved to the start of its section and laid out in the same order as in the order file -.Ar file . -Order files are text files with one symbol name per line. Lines starting with a # are comments. -A symbol name may be optionally preceded with its object file leafname and a colon (e.g. foo.o:_foo). -This is useful for static functions/data that occur in multiple files. -A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). -This enables you to have one order file that works for multiple architectures. -Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file. -.It Fl macosx_version_min Ar version -This is set to indicate the oldest Mac OS X version that that the output is to be used on. Specifying -a later version enables the linker to assumes features of that OS in the output file. The format of -.Ar version -is a Mac OS X version number such as 10.4 or 10.5 -.It Fl image_base Ar address -Specifies the perferred load address for a dylib or bundle. The argument -.Ar address -is a hexadecimal number with an optional leading 0x. By choosing non-overlapping address for all -dylibs and bundles that a program loads, launch time can be improved because dyld will not need to -"rebase" the image (that is, adjust pointers within the image to work at the loaded address). -It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs. -It will then choose non-overlapping addresses for the list and rebase them all. -This option is also called -seg1addr for compatibility. -.It Fl no_implicit_dylibs -When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs -that are implicitly linked to make the two-level namespace -encoding more efficient for dyld. For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. -If you link with -framework Cocoa and use a symbol from Foundation, the linker will implicitly add a load -command to load Foundation and encode the symbol as coming from Foundation. If you use this option, -the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then -at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation. -.El -.Ss Options when creating a dynamic library (dylib) -.Bl -tag -.It Fl install_name Ar name -Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library -will record that path as the way dyld should locate this library. If this option is not specified, then -the -o path will be used. This option is also called -dylib_install_name for compatibility. -.It Fl compatibility_version Ar number -Specifies the compatibility version number of the library. When a library is loaded by dyld, the -compatibility version is checked and if the program's version is greater that the library's version, it is an error. -The format of -.Ar number -is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, -and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. -If the compatibility version number is not specified, it has a value of 0 and no checking is done when the library is used. -This option is also called -dylib_compatibility_version for compatibility. -.It Fl current_version Ar number -Specifies the current version number of the library. The current version of the library can be obtained -programmatically by the user of the library so it can determine exactly which version of the library it is using. -The format of -.Ar number -is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, -and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. -If the version number is not specified, it has a value of 0. -This option is also called -dylib_current_version for compatibility. -.El -.Ss Options when creating a main executable -.Bl -tag -.It Fl pie -This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5, the OS -will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled -with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some -security. -.It Fl pagezero_size Ar size -By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence -will cause a bus error if a NULL pointer is dereferenced. The argument -.Ar size -is a hexadecimal number with an optional leading 0x. If -.Ar size -is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size -is 4KB. On 64-bit architectures, the default size if 4GB. The ppc64 architecture has some special cases. Since Mac -OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless --macosx_version_min is 10.5 or later. Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the -code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero -size is set to 4KB and then a new unredable trailing segment is created after the code, filling up the lower 4GB. -.It Fl stack_size Ar size -Specifies the maximum stack size for the main thread in a program. Without this option a program has a 8MB stack. -The argument -.Ar size -is a hexadecimal number with an optional leading 0x. The -.Ar size -should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. -.It Fl allow_stack_execute -Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. -.El -.Ss Options when creating a bundle -.Bl -tag -.It Fl bundle_loader Ar executable -This specifies the -.Ar executable -that will be loading the bundle output file being linked. -Undefined symbols from the bundle are checked against the specified -.Ar executable -like it was one of the -dynamic libraries the bundle was linked with. -.El -.Ss Options when creating an object file -.Bl -tag -.It Fl keep_private_externs -Don't turn private external (aka visibility=hidden) symbols into static symbols, -but rather leave them as private external in the resulting object file. -.It Fl d -Force definition of common symbols. That is, transform tentative defintions into real definitions. -.El -.Ss Options that control symbol resolution -.Bl -tag -.It Fl exported_symbols_list Ar filename -The specified -.Ar filename -contains a list of global symbol names that will remain as global symbols in the output file. -All other global symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) -and will not be global in the output file. The symbol names listed in filename must be one per line. -Leading and trailing white space are not part of the symbol name. -Lines starting with # are ignored, as are lines with only white space. -Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. -The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches -any single lower case letter from 'a' to 'z'. -.It Fl exported_symbol Ar symbol -The specified -.Ar symbol -is added to the list of global symbols names that will remain as global symbols in the output file. This -option can be used multiple times. For short lists, this can be more convenient than creating a file and using --exported_symbols_list. -.It Fl unexported_symbols_list Ar file -The specified -.Ar filename -contains a list of global symbol names that will not remain as global symbols in the output file. -The symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) and will not be global -in the output file. The symbol names listed in filename must be one per line. -Leading and trailing white space are not part of the symbol name. -Lines starting with # are ignored, as are lines with only white space. -Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. -The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches -any single lower case letter from 'a' to 'z'. -.It Fl unexported_symbol Ar symbol -The specified -.Ar symbol -is added to the list of global symbols names that will not remain as global symbols in the output file. This -option can be used multiple times. For short lists, this can be more convenient than creating a file and using --unexported_symbols_list. -.It Fl alias Ar symbol_name Ar alternate_symbol_name -Create an alias named -.Ar alternate_symbol_name -for the symbol -.Ar symbol_name . -By default the alias symbol has global visibility. This option was previous the -idef:indir option. -.It Fl alias_list Ar filename -The specified -.Ar filename -contains a list of aliases. The symbol name and its alias are on one line, separated by whitespace. -Lines starting with # are ignored. -.It Fl flat_namespace -Alters how symbols are resolved at build time and runtime. With -two_levelnamespace (the default), the linker -only searches dylibs on the command line for symbols, and records in which dylib they were found. With -flat_namespace, -the linker searches all dylibs on the command line and all dylibs those original dylibs depend on. The linker -does not record which dylib an external symbol came from, so at runtime dyld again searches all images and uses -the first definition it finds. In addition, any undefines in loaded flat_namespace dylibs must be resolvable -at build time. -.It Fl u Ar symbol_name -Specified that symbol -.Ar symbol_name -must be defined for the link to succeed. This is useful to force selected functions to be loaded -from a static library. -.It Fl U Ar symbol_name -Specified that it is ok for -.Ar symbol_name -to have no definition. With -two_levelnamespace, the resulting symbol will be marked dynamic_lookup which -means dyld will search all loaded images. -.It Fl undefined Ar treatment -Specifies how undefined symbols are to be treated. Options are: error, warning, suppress, or dynamic_lookup. The -default is error. -.It Fl rpath Ar path -Add -.Ar path -to the runpath search path list for image being created. At runtime, dyld uses the runpath when searching -for dylibs whose load path begins with @rpath/. -.It Fl commons Ar treatment -Specifies how commons (aka tentative definitions) are resolved with respect to dylibs. Options are: -ignore_dylibs, use_dylibs, error. The default is ignore_dylibs which means the linker will turn a tentative -definition in an object file into a real definition and not even check dylibs for conflicts. The dylibs -option means the linker should check linked dylibs for definitions and use them to replace tentative definitions -from object files. The error option means the linker should issu an error whenever a tentative definition in an -object file conflicts with an external symbol in a linked dylib. See also -warn_commons. -.El -.Ss Options for introspecting the linker -.Bl -tag -.It Fl why_load -Log why each object file in a static library is loaded. That is, what symbol was needed. Also called -whyload -for compatibility. -.It Fl why_live Ar symbol_name -Logs a chain of references to -.Ar symbol_name . -Only applicable with -dead_strip . -It can help debug why something that you think should be dead strip removed is not removed. -.It Fl print_statistics -Logs information about the amount of memory and time the linker used. -.It Fl t -Logs each file (object, archive, or dylib) the linker loads. Useful for debugging problems with search paths where the wrong library is loaded. -.It Fl whatsloaded -Logs just object files the linker loads. -.It Fl order_file_statistics -Logs information about the processing of a -order_file. -.It Fl map Ar map_file_path -Writes a map file to the specified path which details all symbols and their addresses in the output image. -.El -.Ss Options for controling symbol table optimizations -.Bl -tag -.It Fl S -Do not put debug information (STABS or DWARF) in the output file. -.It Fl x -Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and -getting symbol names in back traces, but are not used at runtime. If -x is used with -r -non-global symbol names are not removed, but instead replaced with a unique, duumy name -that will be automatically removed when linked into a final linked image. This -allows dead code stripping, which uses symbols to break up code and data, to -work properly and provides the security of having source symbol names removed. -.It Fl non_global_symbols_strip_list Ar filename -The specified -.Ar filename -contains a list of non-global symbol names that should be removed from the output file's symbol table. All other -non-global symbol names will remain in the output files symbol table. See -exported_symbols_list for syntax and use -of wildcards. -.It Fl non_global_symbols_no_strip_list Ar filename -The specified -.Ar filename -contains a list of non-global symbol names that should be remain in the output file's symbol table. All other -symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use -of wildcards. -.El -.Ss Rarely used Options -.Bl -tag -.It Fl v -Prints the version of the linker. -.It Fl no_uuid -Do not generate an LC_UUID load command in the output file. -.It Fl root_safe -Sets the MH_ROOT_SAFE bit in the mach header of the output file. -.It Fl setuid_safe -Sets the MH_SETUID_SAFE bit in the mach header of the output file. -.It Fl interposable -Indirects access to all to exported symbols when creating a dynamic library. -.It Fl init Ar symbol_name -The specified symbol_name will be run as the first initializer. Only used when creating a dynamic library. -.It Fl sub_library Ar library_name -The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc. -Only used when creating a dynamic library. -.It Fl sub_umbrella Ar framework_name -The specified framework will be re-exported. Only used when creating a dynamic library. -.It Fl allowable_client Ar name -Restricts what can link against the dynamic library being created. -.It Fl client_name Ar name -Enables a bundle to link against a dylib that was built with -allowable_client. -The name specified must match one of the -allowable_client names specified when the dylib was created. -.It Fl umbrella Ar framework_name -Specifies that the dylib being linked is re-exported through an umbrella framework of the specified name. -.It Fl headerpad Ar size -Specifies the minimum space for future expansion of the load commands. Only useful if intend to run -install_name_tool to alter the load commands later. Size is a hexadecimal number. -.It Fl headerpad_max_install_names -Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. -Only useful if intend to run install_name_tool to alter the load commands later. Size is a hexadecimal number. -.It Fl bind_at_load -Sets a bit in the mach header of the resulting binary which tells dyld to bind all symbols when the binary is loaded, rather than lazily. -.It Fl force_flat_namespace -Sets a bit in the mach header of the resulting binary which tells dyld to not only use flat namespace for the binary, -but force flat namespace binding on all dylibs and bundles loaded in the process. Can only be used when linking main executables. -.It Fl sectalign Ar segname Ar sectname Ar value -The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal -number that must be an integral power of 2. -.It Fl stack_addr Ar address -Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to a page boundary. -.It Fl segprot Ar segname Ar max_prot Ar init_prot -Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively. -The values for max and init are any combination of the characters `r' (for read), `w' (for write), `x' (for execute) and `-' (no access). -.It Fl seg_addr_table Ar filename -Specifies a file containing base addresses for dynamic libraries. Each line of the file is a hexadecimal base address -followed by whitespace then the install name of the corresponding dylib. The # character denotes a comment. -.It Fl segs_read_write_addr Ar address -Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address -specified is a hexadecimal number that indicates the base address for the read-write segments. -.It Fl segs_read_only_addr Ar address -Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address -specified is a hexadecimal number that indicates the base address for the read-only segments. -.It Fl segaddr Ar name Ar address -Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number -that is a multiple of 4K page size. -.It Fl dylib_file Ar install_name:file_name -Specifies that a dynamic shared library is in a different location than its standard location. Use this option -when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other -than its default location. install_name specifies the path where the library normally resides. file_name specifies -the path of the library you want to use instead. For example, if you link to a library that depends upon the dynamic -library libsys and you have libsys installed in a nondefault location, you would use this option: --dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib. -.It Fl prebind -The created output file will be in the prebound format. This was used in Mac OS X 10.3 and earlier to improve launch performance. -.It Fl weak_reference_mismatches Ar treatment -Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another. The valid -treatments are: error, weak, or non-weak. The default is non-weak. -.It Fl read_only_relocs Ar treatment -Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages. The compiler will -normally never generate such code. -.It Fl force_cpusubtype_ALL -The is only applicable with -arch ppc. It tells the linker to ignore the PowerPC cpu requirements (e.g. G3, G4 or G5) encoded -in the object files and mark the resulting binary as runnable on any PowerPC cpu. -.It Fl dylinker_install_name Ar path -Only used when building dyld. -.It Fl no_arch_warnings -Suppresses warning messages about files that have the wrong architecture for the -arch flag -.It Fl arch_errors_fatal -Turns into errors, warnings about files that have the wrong architecture for the -arch flag. -.It Fl e Ar symbol_name -Specifies the entry point of a main executable. By default the entry name is "start" which is found in crt1.o which contains -the glue code need to set up and call main(). -.It Fl w -Suppress all warning messages -.It Fl final_output Ar name -Specifies the install name of a dylib if -install_name is not used. This option is used by gcc driver when it is invoked -with multiple -arch arguments. -.It Fl arch_multiple -Specifes that the linker should augment error and warning messages with the architecture name. This option is used by gcc -driver when it is invoked with multiple -arch arguments. -.It Fl twolevel_namespace_hints -Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the -libraries being linked against have not changed. -.It Fl dot Ar path -Create a file a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. -.It Fl keep_relocs -Add section based relocation records to a final linked image. These relocations are ignored at runtime by dyld. -.It Fl warn_stabs -Print a warning when the linker cannot do a BINCL/EINCL optimzation because the compiler put a bad stab symbol inside -a BINCL/EINCL range. -.It Fl warn_commons -Print a warning whenever the a tentative definition in an object file is found and a external symbol by the same name -is also found in a linked dylib. This often means that the extern keyword is missing from a variable declaration -in a header file. -.It Fl read_only_stubs -[i386 only] Makes the __IMPORT segment of a final linked images read-only. This option makes a program slightly more -secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code. The downside -is the dyld must use mprotect() to temporily make the segment writable while it is binding the stubs. -.It Fl slow_stubs -[i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which -calls through a lazy pointer in the __DATA segment. -.It Fl interposable_list Ar filename -The specified -.Ar filename -contains a list of global symbol names that should always be accessed indirectly. For instance, if libSystem.dylib -is linked such that _malloc is interposable, then calls to malloc() from within libSystem will go through a dyld -stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc -interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed -(not interposed) because they would be direct calls. -.El -.Ss Obsolete Options -.Bl -tag -.It Fl segalign Ar value -All segments must be page aligned. This option is obsolete. -.It Fl seglinkedit -Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. -.It Fl noseglinkedit -This is the default. This option is obsolete. -.It Fl fvmlib -Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. -.It Fl preload -Preload executables (MH_PRELOAD) are no longer supported. This option is obsolete. -.It Fl sectobjectsymbols Ar segname Ar sectname -Adding a local label at a section start is no longer supported. This option is obsolete. -.It Fl nofixprebinding -The MH_NOFIXPREBINDING bit of mach_headers has been ignored since Mac OS X 10.3.9. This option is obsolete. -.It Fl noprebind_all_twolevel_modules -Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. -.It Fl prebind_all_twolevel_modules -Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. -.It Fl prebind_allow_overlap -When using -prebind, the linker allows overlapping by default, so this option is obsolete. -.It Fl noprebind -LD_PREBIND is no longer supported as a way to force on prebinding, so there no longer needs to -be a command line way to override LD_PREBIND. This option is obsolete. -.It Fl sect_diff_relocs Ar treatment -This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into -a main executable, but the false positive rate generated too much noise to make the option useful. -This option is obsolete. -.It Fl run_init_lazily -This option was removed in Mac OS X 10.2. -.It Fl single_module -This is now the default so does not need to be specified. -.It Fl multi_module -Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. -.It Fl no_dead_strip_inits_and_terms -The linker never dead strips initialzation and termination routines. They are considered "roots" of the dead strip graph. -.It Fl A Ar basefile -Obsolete incremental load format. This option is obsolete. -.It Fl b -Used with -A option to strip base file's symbols. This option is obsolete. -..It Fl M -Obsolete option to produce a load map. Use -map option instead. -.It Fl Sn -Don't strip any symbols. This is the default. This option is obsolete. -.It Fl Si -Optimize stabs debug symbols to remove duplicates. This is the default. This option is obsolete. -.It Fl Sp -Write minimal stabs which causes the debugger to open and read the original .o file for full stabs. -This style of debugging is obsolete in Mac OS X 10.5. This option is obsolete. -.It Fl X -Strip local symbols that being the 'L'. This is the default. This option is obsolete. -.It Fl s -Completely strip the output, including removing the symbol table. This file format variant is no longer supported. -This option is obsolete. -.It Fl m -Don't treat multiple definitions as an error. This is no longer supported. This option is obsolete. -.It Fl y Ns symbol -Display each file in which -.Ar symbol -is used. This was previously used to debug where an undefined symbol was used, but the linker now -automatically prints out all usages. The -why_live option can also be used to display what kept -a symbol from being dead striped. This option is obsolete. -.It Fl Y Ar number -Used to control how many occurances of each symbol specifed with -y would be shown. This option is obsolete. -.It Fl nomultidefs -Only used when linking an umbrella framework. Sets the MH_NOMULTIDEFS bit in the mach_header. The MH_NOMULTIDEFS -bit has been obsolete since Mac OS X 10.4. This option is obsolete. -.It Fl multiply_defined_unused Ar treatment -Previously provided a way to warn or error if any of the symbol definitions in the output file matched any -definitions in dynamic library being linked. This option is obsolete. -.It Fl multiply_defined Ar treatment -Previously provided a way to warn or error if any of the symbols used from a dynamic library were also -available in another linked dynamic library. This option is obsolete. -.It Fl private_bundle -Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle -contained a definition that conflicted with a symbol in the main executable. The linker no longer -errors on such conflicts. This option is obsolete. -.It Fl noall_load -This is the default. This option is obsolete. -.It Fl seg_addr_table_filename Ar path -Use -.Ar path -instead of the install name of the library for matching an entry in the seg_addr_table. This option is obsolete. -.It Fl sectorder Ar segname sectname orderfile -Replaced by more general -order_file option. -.It Fl sectorder_detail -Produced extra logging about which entries from a sectorder entries were used. Replaced by -order_file_statistics. -This option is obsolete. -.El -.Sh SEE ALSO -as(1), ar(1), cc(1), nm(1), otool(1) lipo(1), -arch(3), dyld(3), Mach-O(5), strip(1), rebase(1) diff --git a/ld64/FireOpal/doc/man/man1/ld64.1 b/ld64/FireOpal/doc/man/man1/ld64.1 deleted file mode 100644 index 615b0e5..0000000 --- a/ld64/FireOpal/doc/man/man1/ld64.1 +++ /dev/null @@ -1 +0,0 @@ -.so man1/ld.1 diff --git a/ld64/FireOpal/doc/man/man1/rebase.1 b/ld64/FireOpal/doc/man/man1/rebase.1 deleted file mode 100644 index 6743a96..0000000 --- a/ld64/FireOpal/doc/man/man1/rebase.1 +++ /dev/null @@ -1,39 +0,0 @@ -.Dd June 6, 2006 -.Dt rebase 1 -.Os Darwin -.Sh NAME -.Nm rebase -.Nd "Changes base address of dylibs and bundles" -.Sh SYNOPSIS -.Nm -.Op Fl low_address Ar addr -.Op Fl high_address Ar addr -.Op Fl arch Ar arch -.Op Fl v -.Ar file(s) -.Sh DESCRIPTION -The base address of an image (dylib or bundle) is the preferred address for it to be loaded. By -default all images are built with a base address of zero. At runtime, if the -preferred memory range is already occupied, dyld will "slide" the image to a new address range. -There is a small cost to the slide, as dyld must do some fix ups. -The rebase tool takes a list of images and adjust their base address to be non-overlapping. If no -low or high address is specified, the a suitable address range is choosen for the architecture. -.Pp -The options are as follows: -.Bl -tag -width indent -.It Fl low_address Ar addr -Force the base address for the first image to be -.Ar addr -(specified in hex). Each subsequent file gets the next available base address. -.It Fl high_address Ar addr -Force the base address for the last image to be such that when that image is loaded it occupies -memory up to -.Ar addr -(specified in hex). Each preceeding file gets the previous available base address. -.It Fl arch Ar arch -Only rebase the specified architecture. Other architectures in a universal image are left as is. -.It Fl v -Verbose. Print information about rebasing done. -.El -.Sh SEE ALSO -.Xr ld 1 diff --git a/ld64/FireOpal/ld64.xcodeproj/project.pbxproj b/ld64/FireOpal/ld64.xcodeproj/project.pbxproj deleted file mode 100644 index 5f8cd7f..0000000 --- a/ld64/FireOpal/ld64.xcodeproj/project.pbxproj +++ /dev/null @@ -1,788 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 42; - objects = { - -/* Begin PBXAggregateTarget section */ - F96D5368094A2754008E9EE8 /* unit-tests */ = { - isa = PBXAggregateTarget; - buildConfigurationList = F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */; - buildPhases = ( - F96D5367094A2754008E9EE8 /* ShellScript */, - ); - dependencies = ( - F96D536A094A275D008E9EE8 /* PBXTargetDependency */, - F96D536C094A275F008E9EE8 /* PBXTargetDependency */, - F96904890A4333AC00B77D2A /* PBXTargetDependency */, - F9EA73970974999B008B4F1D /* PBXTargetDependency */, - ); - name = "unit-tests"; - productName = "unit-tests"; - }; - F9B1A2670A3A567B00DA8FAB /* all */ = { - isa = PBXAggregateTarget; - buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */; - buildPhases = ( - ); - dependencies = ( - F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, - F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */, - ); - name = all; - productName = all; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; - F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; - F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; - F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; - F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; - F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; - F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; - F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; - F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; - F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9FCC3F10A54A75600CEB866 /* ld64.1 */; }; -/* End PBXBuildFile section */ - -/* Begin PBXBuildRule section */ - F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; - fileType = sourcecode.c; - isEditable = 1; - outputFiles = ( - ); - }; - F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; - fileType = sourcecode.cpp; - isEditable = 1; - outputFiles = ( - ); - }; -/* End PBXBuildRule section */ - -/* Begin PBXContainerItemProxy section */ - F96904880A4333AC00B77D2A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9023C3006D5A227001BBF46 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; - remoteInfo = rebase; - }; - F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9023C3006D5A227001BBF46 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F9023C3806D5A23E001BBF46; - remoteInfo = ld; - }; - F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9023C3006D5A227001BBF46 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F971EED206D5ACF60041D381; - remoteInfo = ObjectDump; - }; - F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9023C3006D5A227001BBF46 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F9023C3806D5A23E001BBF46; - remoteInfo = ld; - }; - F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9023C3006D5A227001BBF46 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; - remoteInfo = rebase; - }; - F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9023C3006D5A227001BBF46 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F9EA72CA097454A6008B4F1D; - remoteInfo = machocheck; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - F97F5025070D0B6300B9FCD7 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1; - dstSubfolderSpec = 0; - files = ( - F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */, - F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 1; - }; - F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1; - dstSubfolderSpec = 0; - files = ( - F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/LTOReader.hpp; sourceTree = ""; }; - C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; - F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ExecutableFile.h; sourceTree = ""; }; - F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld.cpp; sourceTree = ""; }; - F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ObjectFile.h; sourceTree = ""; }; - F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/FileAbstraction.hpp; sourceTree = ""; }; - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/MachOFileAbstraction.hpp; sourceTree = ""; }; - F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/Architectures.hpp; sourceTree = ""; }; - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/MachOReaderDylib.hpp; sourceTree = ""; }; - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/MachOReaderRelocatable.hpp; sourceTree = ""; }; - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/MachOWriterExecutable.hpp; sourceTree = ""; }; - F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; - F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = ""; }; - F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; - F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/OpaqueSection.hpp; sourceTree = ""; }; - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ArchiveReader.hpp; sourceTree = SOURCE_ROOT; }; - F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; - F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = ""; }; - F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = ""; }; - F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; - F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/machochecker.cpp; sourceTree = ""; }; - F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/debugline.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = ""; }; - F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; - F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/rebase.cpp; sourceTree = ""; }; - F9FCC3F10A54A75600CEB866 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - F9023C3706D5A23E001BBF46 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F971EED106D5ACF60041D381 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9EA72C9097454A6008B4F1D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9EC77EC0A2F85F6002A3E39 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - F9023C2C06D5A227001BBF46 = { - isa = PBXGroup; - children = ( - C02A29DE0953B26E001FB8C1 /* ChangeLog */, - F933DC37092A82480083EAC8 /* Architectures.hpp */, - F933D9460929277C0083EAC8 /* FileAbstraction.hpp */, - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */, - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */, - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */, - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, - F9023C4106D5A254001BBF46 /* ObjectFile.h */, - F98D26850AA779BD00416316 /* OpaqueSection.hpp */, - F9023C3F06D5A254001BBF46 /* ld.cpp */, - F9C0D48A06DD1E1B001C7193 /* Options.cpp */, - F9C0D48B06DD1E1B001C7193 /* Options.h */, - F9EA7583097882F3008B4F1D /* debugline.h */, - F9EA7582097882F3008B4F1D /* debugline.c */, - F9EA72D4097454FF008B4F1D /* machochecker.cpp */, - F971EED706D5AD240041D381 /* ObjectDump.cpp */, - F9EC78050A2F8674002A3E39 /* rebase.cpp */, - F97F5028070D0BB200B9FCD7 /* ld.1 */, - F9FCC3F10A54A75600CEB866 /* ld64.1 */, - F9B1A2580A3A448800DA8FAB /* rebase.1 */, - F9023C3A06D5A23E001BBF46 /* Products */, - ); - sourceTree = ""; - }; - F9023C3A06D5A23E001BBF46 /* Products */ = { - isa = PBXGroup; - children = ( - F9023C3906D5A23E001BBF46 /* ld */, - F971EED306D5ACF60041D381 /* ObjectDump */, - F9EA72CB097454A6008B4F1D /* machocheck */, - F9EC77EE0A2F85F6002A3E39 /* rebase */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - F9023C3806D5A23E001BBF46 /* ld */ = { - isa = PBXNativeTarget; - buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; - buildPhases = ( - 0B12F6A50CE39466008ABCAE /* build configure.h */, - F9023C3606D5A23E001BBF46 /* Sources */, - F9023C3706D5A23E001BBF46 /* Frameworks */, - F97F5025070D0B6300B9FCD7 /* CopyFiles */, - F9FCC3EF0A54A4ED00CEB866 /* Run Script */, - ); - buildRules = ( - F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, - F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */, - ); - dependencies = ( - ); - name = ld; - productName = ld64; - productReference = F9023C3906D5A23E001BBF46 /* ld */; - productType = "com.apple.product-type.tool"; - }; - F971EED206D5ACF60041D381 /* ObjectDump */ = { - isa = PBXNativeTarget; - buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */; - buildPhases = ( - F971EED006D5ACF60041D381 /* Sources */, - F971EED106D5ACF60041D381 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = ObjectDump; - productName = ObjectDump; - productReference = F971EED306D5ACF60041D381 /* ObjectDump */; - productType = "com.apple.product-type.tool"; - }; - F9EA72CA097454A6008B4F1D /* machocheck */ = { - isa = PBXNativeTarget; - buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; - buildPhases = ( - F9EA72C8097454A6008B4F1D /* Sources */, - F9EA72C9097454A6008B4F1D /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = machocheck; - productName = machocheck; - productReference = F9EA72CB097454A6008B4F1D /* machocheck */; - productType = "com.apple.product-type.tool"; - }; - F9EC77ED0A2F85F6002A3E39 /* rebase */ = { - isa = PBXNativeTarget; - buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */; - buildPhases = ( - F9EC77EB0A2F85F6002A3E39 /* Sources */, - F9EC77EC0A2F85F6002A3E39 /* Frameworks */, - F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = rebase; - productName = rebase; - productReference = F9EC77EE0A2F85F6002A3E39 /* rebase */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - F9023C3006D5A227001BBF46 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; - compatibilityVersion = "Xcode 2.4"; - hasScannedForEncodings = 0; - mainGroup = F9023C2C06D5A227001BBF46; - productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - F9B1A2670A3A567B00DA8FAB /* all */, - F9023C3806D5A23E001BBF46 /* ld */, - F9EC77ED0A2F85F6002A3E39 /* rebase */, - F971EED206D5ACF60041D381 /* ObjectDump */, - F9EA72CA097454A6008B4F1D /* machocheck */, - F96D5368094A2754008E9EE8 /* unit-tests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXShellScriptBuildPhase section */ - 0B12F6A50CE39466008ABCAE /* build configure.h */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "build configure.h"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/configure.h", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\nelse\n\techo \"#undef LTO_SUPPORT\t\" > ${DERIVED_FILE_DIR}/configure.h\nfi\n"; - showEnvVarsInLog = 0; - }; - F96D5367094A2754008E9EE8 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - 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"; - showEnvVarsInLog = 0; - }; - F9FCC3EF0A54A4ED00CEB866 /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "cd ${DSTROOT}/usr/bin\nln -s ld ld64"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - F9023C3606D5A23E001BBF46 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, - F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, - F9EA7584097882F3008B4F1D /* debugline.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F971EED006D5ACF60041D381 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */, - F9EA75BC09788857008B4F1D /* debugline.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9EA72C8097454A6008B4F1D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9EC77EB0A2F85F6002A3E39 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - F96904890A4333AC00B77D2A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F9EC77ED0A2F85F6002A3E39 /* rebase */; - targetProxy = F96904880A4333AC00B77D2A /* PBXContainerItemProxy */; - }; - F96D536A094A275D008E9EE8 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F9023C3806D5A23E001BBF46 /* ld */; - targetProxy = F96D5369094A275D008E9EE8 /* PBXContainerItemProxy */; - }; - F96D536C094A275F008E9EE8 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F971EED206D5ACF60041D381 /* ObjectDump */; - targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */; - }; - F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F9023C3806D5A23E001BBF46 /* ld */; - targetProxy = F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */; - }; - F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F9EC77ED0A2F85F6002A3E39 /* rebase */; - targetProxy = F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */; - }; - F9EA73970974999B008B4F1D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F9EA72CA097454A6008B4F1D /* machocheck */; - targetProxy = F9EA73960974999B008B4F1D /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - F933D91C09291AC90083EAC8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEAD_CODE_STRIPPING = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_DYNAMIC_NO_PIC = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; - GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; - GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; - GCC_WARN_PEDANTIC = NO; - GCC_WARN_SHADOW = NO; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; - GCC_WARN_UNINITIALIZED_AUTOS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_PARAMETER = NO; - GCC_WARN_UNUSED_VALUE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(DEVELOPER_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/include", - ); - INSTALL_PATH = /usr/bin; - MACOSX_DEPLOYMENT_TARGET = ""; - OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; - PREBINDING = NO; - PRODUCT_NAME = ld; - SECTORDER_FLAGS = ""; - VERSIONING_SYSTEM = "apple-generic"; - WARNING_CFLAGS = "-Wall"; - }; - name = Debug; - }; - F933D91D09291AC90083EAC8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_DYNAMIC_NO_PIC = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 3; - GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; - GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; - GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; - GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; - GCC_WARN_PEDANTIC = NO; - GCC_WARN_SHADOW = NO; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; - GCC_WARN_UNINITIALIZED_AUTOS = NO; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_PARAMETER = NO; - GCC_WARN_UNUSED_VALUE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(DEVELOPER_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/include", - ); - INSTALL_PATH = /usr/bin; - OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; - PREBINDING = NO; - PRODUCT_NAME = ld; - SECTORDER_FLAGS = ""; - VALID_ARCHS = "i386 ppc"; - VERSIONING_SYSTEM = "apple-generic"; - WARNING_CFLAGS = "-Wall"; - }; - name = Release; - }; - F933D92009291AC90083EAC8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/include"; - INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; - OTHER_REZFLAGS = ""; - PREBINDING = NO; - PRODUCT_NAME = ObjectDump; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); - }; - name = Debug; - }; - F933D92109291AC90083EAC8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = s; - INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; - OTHER_REZFLAGS = ""; - PREBINDING = NO; - PRODUCT_NAME = ObjectDump; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); - }; - name = Release; - }; - F933D92409291AC90083EAC8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_DYNAMIC_NO_PIC = NO; - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - }; - name = Debug; - }; - F933D92509291AC90083EAC8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_DYNAMIC_NO_PIC = NO; - GCC_TREAT_WARNINGS_AS_ERRORS = NO; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/SDKs/Extra/usr/include"; - }; - name = Release; - }; - F96D536E094A2773008E9EE8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - PRODUCT_NAME = "unit-tests"; - }; - name = Debug; - }; - F96D536F094A2773008E9EE8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - PRODUCT_NAME = "unit-tests"; - }; - name = Release; - }; - F9B1A26D0A3A568700DA8FAB /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - PRODUCT_NAME = all; - }; - name = Debug; - }; - F9B1A26E0A3A568700DA8FAB /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - PRODUCT_NAME = all; - ZERO_LINK = NO; - }; - name = Release; - }; - F9EA72D0097454D5008B4F1D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = "$(HOME)/bin"; - PREBINDING = NO; - PRODUCT_NAME = machocheck; - }; - name = Debug; - }; - F9EA72D1097454D5008B4F1D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - INSTALL_PATH = "$(HOME)/bin"; - PREBINDING = NO; - PRODUCT_NAME = machocheck; - }; - name = Release; - }; - F9EC77F10A2F8616002A3E39 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_DYNAMIC_NO_PIC = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = "$(HOME)/bin"; - PREBINDING = NO; - PRODUCT_NAME = rebase; - }; - name = Debug; - }; - F9EC77F20A2F8616002A3E39 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; - GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; - INSTALL_PATH = /usr/bin; - PREBINDING = NO; - PRODUCT_NAME = rebase; - VALID_ARCHS = "i386 ppc"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F933D91C09291AC90083EAC8 /* Debug */, - F933D91D09291AC90083EAC8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F933D92009291AC90083EAC8 /* Debug */, - F933D92109291AC90083EAC8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F933D92409291AC90083EAC8 /* Debug */, - F933D92509291AC90083EAC8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F96D536E094A2773008E9EE8 /* Debug */, - F96D536F094A2773008E9EE8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F9B1A26D0A3A568700DA8FAB /* Debug */, - F9B1A26E0A3A568700DA8FAB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F9EA72D0097454D5008B4F1D /* Debug */, - F9EA72D1097454D5008B4F1D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F9EC77F10A2F8616002A3E39 /* Debug */, - F9EC77F20A2F8616002A3E39 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = F9023C3006D5A227001BBF46 /* Project object */; -} diff --git a/ld64/FireOpal/src/ArchiveReader.hpp b/ld64/FireOpal/src/ArchiveReader.hpp deleted file mode 100644 index a195090..0000000 --- a/ld64/FireOpal/src/ArchiveReader.hpp +++ /dev/null @@ -1,454 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_ARCHIVE__ -#define __OBJECT_FILE_ARCHIVE__ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "ObjectFile.h" -#include "MachOReaderRelocatable.hpp" -#if LTO_SUPPORT - #include "LTOReader.hpp" -#endif - -namespace archive { - -typedef const struct ranlib* ConstRanLibPtr; - -template -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength); - Reader(const uint8_t fileContent[], uint64_t fileLength, - const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime(){ return fModTime; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabs() { return NULL; } - virtual void optimize(std::vector&, std::vector&, - std::vector&, const std::set&, - uint32_t, ObjectFile::Reader* writer, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs); - -private: - static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength); - static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength); - static cpu_type_t architecture(); - - - class Entry : ar_hdr - { - public: - const char* getName() const; - time_t getModTime() const; - const uint8_t* getContent() const; - uint32_t getContentSize() const; - const Entry* getNext() const; - private: - bool hasLongName() const; - unsigned int getLongNameSpace() const; - - }; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; - - typedef typename A::P P; - typedef typename A::P::E E; - - const struct ranlib* ranlibHashSearch(const char* name); - ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); - void dumpTableOfContents(); - void buildHashTable(); - - const char* fPath; - time_t fModTime; - const ObjectFile::ReaderOptions& fOptions; - uint32_t fOrdinalBase; - const uint8_t* fFileContent; - uint64_t fFileLength; - const struct ranlib* fTableOfContents; - uint32_t fTableOfContentCount; - const char* fStringPool; - std::vector fAllAtoms; - std::vector fInstantiatedReaders; - std::set fInstantiatedEntries; - std::set fPossibleEntries; - NameToEntryMap fHashTable; - - static std::vector fgEmptyList; -}; - -template -std::vector Reader::fgEmptyList; - - -template -bool Reader::Entry::hasLongName() const -{ - return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); -} - -template -unsigned int Reader::Entry::getLongNameSpace() const -{ - char* endptr; - long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); - return result; -} - -template -const char* Reader::Entry::getName() const -{ - if ( this->hasLongName() ) { - int len = this->getLongNameSpace(); - static char longName[256]; - strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); - longName[len] = '\0'; - return longName; - } - else { - static char shortName[20]; - strncpy(shortName, this->ar_name, 16); - shortName[16] = '\0'; - char* space = strchr(shortName, ' '); - if ( space != NULL ) - *space = '\0'; - return shortName; - } -} - -template -time_t Reader::Entry::getModTime() const -{ - char temp[14]; - strncpy(temp, this->ar_date, 12); - temp[12] = '\0'; - char* endptr; - return (time_t)strtol(temp, &endptr, 10); -} - - -template -const uint8_t* Reader::Entry::getContent() const -{ - if ( this->hasLongName() ) - return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); - else - return ((uint8_t*)this) + sizeof(ar_hdr); -} - - -template -uint32_t Reader::Entry::getContentSize() const -{ - char temp[12]; - strncpy(temp, this->ar_size, 10); - temp[10] = '\0'; - char* endptr; - long size = strtol(temp, &endptr, 10); - // long name is included in ar_size - if ( this->hasLongName() ) - size -= this->getLongNameSpace(); - return size; -} - - -template -const class Reader::Entry* Reader::Entry::getNext() const -{ - const uint8_t* p = this->getContent() + getContentSize(); - p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align - return (class Reader::Entry*)p; -} - - -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC64; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_I386; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_X86_64; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_ARM; } - - -template -bool Reader::validMachOFile(const uint8_t* fileContent, uint64_t fileLength) -{ - return mach_o::relocatable::Reader::validFile(fileContent); -} - -template -bool Reader::validLTOFile(const uint8_t* fileContent, uint64_t fileLength) -{ -#if LTO_SUPPORT - return lto::Reader::validFile(fileContent, fileLength, architecture()); -#else - return false; -#endif -} - - - -template -bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength) -{ - // must have valid archive header - if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) - return false; - - // peak at first .o file and verify it is correct architecture - const Entry* const start = (Entry*)&fileContent[8]; - const Entry* const end = (Entry*)&fileContent[fileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - // skip option table-of-content member - if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) - continue; - // archive is valid if first .o file is valid - return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize())); - } - // empty archive - return true; -} - -template -Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL), - fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL) -{ - fPath = strdup(path); - fFileContent = fileContent; - fFileLength = fileLength; - - if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) - throw "not an archive"; - - // write out path for -whatsloaded option - if ( options.fLogAllFiles ) - printf("%s\n", path); - - if ( !options.fFullyLoadArchives ) { - const Entry* const firstMember = (Entry*)&fFileContent[8]; - if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { - const uint8_t* contents = firstMember->getContent(); - uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); - fTableOfContents = (const struct ranlib*)&contents[4]; - fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); - fStringPool = (const char*)&contents[ranlibArrayLen+8]; - if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) - || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) - throw "malformed archive, perhaps wrong architecture"; - this->buildHashTable(); - } - else - throw "archive has no table of contents"; - } -} - - -template -ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) -{ - const char* memberName = member->getName(); - char memberPath[strlen(fPath) + strlen(memberName)+4]; - strcpy(memberPath, fPath); - strcat(memberPath, "("); - strcat(memberPath, memberName); - strcat(memberPath, ")"); - //fprintf(stderr, "using %s from %s\n", memberName, fPath); - try { - // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive - uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent; - if ( validMachOFile(member->getContent(), member->getContentSize()) ) { - return new typename mach_o::relocatable::Reader::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase); - } -#if LTO_SUPPORT - else if ( validLTOFile(member->getContent(), member->getContentSize()) ) { - return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture()); - } -#endif - throw "not a valid archive member"; - } - catch (const char* msg) { - throwf("in %s, %s", memberPath, msg); - } -} - - -template -std::vector& Reader::getAtoms() -{ - if ( fOptions.fFullyLoadArchives ) { - // build vector of all atoms from all .o files in this archive - const Entry* const start = (Entry*)&fFileContent[8]; - const Entry* const end = (Entry*)&fFileContent[fFileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) - continue; - if ( fOptions.fWhyLoad ) - printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); - ObjectFile::Reader* r = this->makeObjectReaderForMember(p); - std::vector& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - fInstantiatedReaders.push_back(r); - } - return fAllAtoms; - } - else if ( fOptions.fLoadAllObjcObjectsFromArchives ) { - // build vector of all atoms from all .o files containing objc classes in this archive - for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) { - if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { - const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) - printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName()); - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - std::vector& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - fInstantiatedReaders.push_back(r); - } - } - } - return fAllAtoms; - } - else { - // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed - return fgEmptyList; - } -} - -template -void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, const std::set& deadAtoms, - uint32_t nextOrdinal, ObjectFile::Reader* writer, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) -{ - for(std::vector::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) { - (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, nextOrdinal, writer, llvmOptions, - allGlobalsAReDeadStripRoots, okind, verbose, saveTemps, outputFilePath, pie, allowTextRelocs); - } -} - - - -template -ConstRanLibPtr Reader::ranlibHashSearch(const char* name) -{ - class NameToEntryMap::iterator pos = fHashTable.find(name); - if ( pos != fHashTable.end() ) - return pos->second; - else - return NULL; -} - -template -void Reader::buildHashTable() -{ - // walk through list backwards, adding/overwriting entries - // this assures that with duplicates those earliest in the list will be found - for (int i = fTableOfContentCount-1; i >= 0; --i) { - const struct ranlib* entry = &fTableOfContents[i]; - const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; - const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; - //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); - fHashTable[entryName] = entry; - fPossibleEntries.insert(member); - } -} - -template -void Reader::dumpTableOfContents() -{ - for (unsigned int i=0; i < fTableOfContentCount; ++i) { - const struct ranlib* e = &fTableOfContents[i]; - printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); - } -} - -template -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - if ( fOptions.fFullyLoadArchives ) { - return NULL; - } - else { - const struct ranlib* result = NULL; - // do a hash search of table of contents looking for requested symbol - result = ranlibHashSearch(name); - if ( result != NULL ) { - const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) - printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - fInstantiatedReaders.push_back(r); - return new std::vector(r->getAtoms()); - } - } - //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); - return NULL; - } -} - - - - - -}; // namespace archive - - -#endif // __OBJECT_FILE_ARCHIVE__ diff --git a/ld64/FireOpal/src/ExecutableFile.h b/ld64/FireOpal/src/ExecutableFile.h deleted file mode 100644 index b0b760d..0000000 --- a/ld64/FireOpal/src/ExecutableFile.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __EXECUTABLEFILE__ -#define __EXECUTABLEFILE__ - -#include -#include - -#include "ObjectFile.h" -#include "Options.h" - - -namespace ExecutableFile { - - struct DyLibUsed - { - ObjectFile::Reader* reader; - DynamicLibraryOptions options; - }; - - class Writer : public ObjectFile::Reader - { - public: - virtual ~Writer() {}; - - virtual const char* getPath() = 0; - virtual std::vector& getAtoms() = 0; - virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; - virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses) = 0; - virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; - virtual uint64_t write(std::vector& atoms, - std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool createUUID, bool canScatter, - ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, - bool overridesDylibWeakDefines) = 0; - - protected: - Writer(std::vector&) {}; - }; - -}; - -#endif // __EXECUTABLEFILE__ diff --git a/ld64/FireOpal/src/LTOReader.hpp b/ld64/FireOpal/src/LTOReader.hpp deleted file mode 100644 index 2736f43..0000000 --- a/ld64/FireOpal/src/LTOReader.hpp +++ /dev/null @@ -1,684 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __LTO_READER_H__ -#define __LTO_READER_H__ - -#include -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ObjectFile.h" -#include "Options.h" - -#include "llvm-c/lto.h" - - -namespace lto { - - -// -// Reference handles Atom references. These references facilitate -// symbol resolution. -// - -class Reference : public ObjectFile::Reference -{ -public: - Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { } - Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { } - - bool isTargetUnbound() const { return fTargetAtom == NULL; } - bool isFromTargetUnbound() const { return true; } - uint8_t getKind() const { return 0; } - uint64_t getFixUpOffset() const { return 0; } - const char * getTargetName() const { return fTargetName; } - ObjectFile::Atom& getTarget() const { return *fTargetAtom; } - uint64_t getTargetOffset() const { return 0; } - bool hasFromTarget() const { return false; } - ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } - const char * getFromTargetName() const { return NULL; } - uint64_t getFromTargetOffset() const { return 0; } - TargetBinding getTargetBinding() const; - TargetBinding getFromTargetBinding() const { return kDontBind; } - void setTarget (ObjectFile::Atom& a, uint64_t offset) - { fTargetAtom = &a; } - void setFromTarget(ObjectFile::Atom &a) { } - const char * getDescription() const; - -private: - const char * fTargetName; - ObjectFile::Atom * fTargetAtom; -}; - - -ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const -{ - if ( fTargetAtom == NULL ) - return kUnboundByName; - else if ( fTargetName == NULL ) - return kBoundDirectly; - else - return kBoundByName; -} - -const char* Reference::getDescription() const -{ - static char temp[256]; - strcpy(temp, "reference to "); - if ( fTargetName != NULL ) - strcat(temp, fTargetName); - else - strcat(temp, fTargetAtom->getDisplayName()); - return temp; -} - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - - static Segment fgBootstrapSegment; - -private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; -}; - -Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false); - - - - -// -// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, -// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After -// optimization is performed, real Atoms are created for these symobls. However these real Atoms -// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate -// methods to real atom. -// -class Atom : public ObjectFile::Atom -{ -public: - Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom); - - ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; } - bool getTranslationUnitSource (const char **dir, const char **name) const - { return fRealAtom->getTranslationUnitSource(dir, name); } - const char * getName () const { return fName; } - const char * getDisplayName() const { return this->getName(); } - Scope getScope() const { return fScope; } - DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } - SymbolTableInclusion getSymbolTableInclusion() const - { return fRealAtom->getSymbolTableInclusion(); } - bool dontDeadStrip() const { return false; } - bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); } - bool isThumb() const { return false; } - uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); } - std::vector& getReferences() const - { return (fRealAtom ? fRealAtom->getReferences() : (std::vector&)fReferences); } - bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); } - const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); } - // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection. - class ObjectFile::Section * getSection() const { return fSection; } - ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); } - uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); } - ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); } - std::vector* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); } - ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); } - void copyRawContent(uint8_t buffer[]) const - { if (fRealAtom) fRealAtom->copyRawContent(buffer); } - void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; } - - void setRealAtom (ObjectFile::Atom *atom) - { fRealAtom = atom; } - ObjectFile::Atom * getRealAtom() { return fRealAtom; } - void addReference(ObjectFile::Reference *ref) - { fReferences.push_back(ref); } - - void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); } - void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); } - -private: - class Reader& fOwner; - const char* fName; - ObjectFile::Atom::Scope fScope; - ObjectFile::Atom::DefinitionKind fKind; - uint8_t fAlignment; - ObjectFile::Atom* fRealAtom; - std::vector fReferences; -}; - - -Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom) -: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL) -{ - // every Atom references the InternalAtom for its reader - fReferences.push_back(new Reference(internalAtom)); -} - - -// -// ld64 only tracks non-internal symbols from an llvm bitcode file. -// We model this by having an InternalAtom which represent all internal functions and data. -// All non-interal symbols from a bitcode file are represented by a Atom -// and each Atom has a reference to the InternalAtom. The InternalAtom -// also has references to each symbol external to the bitcode file. -// -class InternalAtom : public ObjectFile::Atom -{ -public: - InternalAtom(class Reader& owner) : fOwner(owner) {} - - ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; } - bool getTranslationUnitSource (const char **dir, const char **name) const - { return false; } - const char * getName () const { return "__llvm-internal-atom"; } - const char * getDisplayName() const { return "llvm bitcode"; } - Scope getScope() const { return scopeTranslationUnit; } - DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; } - bool dontDeadStrip() const { return false; } - bool isZeroFill() const { return false; } - bool isThumb() const { return false; } - uint64_t getSize() const { return 0; } - std::vector& getReferences() const { return (std::vector&)fReferences; } - bool mustRemainInSection() const { return false; } - const char * getSectionName() const { return NULL; } - class ObjectFile::Section * getSection() const { return NULL; } - ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; } - uint32_t getOrdinal() const { return 0; } - ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - std::vector* getLineInfo() const { return NULL; } - ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - void copyRawContent(uint8_t buffer[]) const { } - void setScope(Scope s) { } - - void addReference(const char* targetName); - -private: - class Reader& fOwner; - std::vector fReferences; -}; - - -void InternalAtom::addReference(const char* name) -{ - fReferences.push_back(new Reference(name)); -} - - - - -class RemovableAtoms -{ -public: - RemovableAtoms(std::set& iAtoms) : fAtoms(iAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fAtoms.count(atom) != 0 ); - } - -private: - std::set& fAtoms; -}; - - - -// -// LLVM bitcode file reader -// -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); - static bool loaded() { return (::lto_get_version() != NULL); } - Reader(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, - const ObjectFile::ReaderOptions&, cpu_type_t arch); - virtual ~Reader(); - - virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return fModTime; } - virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; } - virtual std::vector* getStabs() { return NULL; } - virtual void optimize(std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, const std::set&, - uint32_t nextInputOrdinal, - ObjectFile::Reader* writer, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - int outputKind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs); - -private: - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; - - ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal); - static const char* tripletPrefixForArch(cpu_type_t); - - cpu_type_t fArchitecture; - const char* fPath; - time_t fModTime; - lto_module_t fModule; - std::vector fAtoms; - InternalAtom fInternalAtom; - const ObjectFile::ReaderOptions& fReaderOptions; - static std::set fgReaders; - static bool fgOptimized; -}; - -bool Reader::fgOptimized = false; -std::set Reader::fgReaders; - - -Reader::~Reader() -{ - if ( fModule != NULL ) - ::lto_module_dispose(fModule); -} - -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, cpu_type_t arch) - : fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options) -{ - fgReaders.insert(this); - - fModule = ::lto_module_create_from_memory(fileContent, fileLength); - if ( fModule == NULL ) - throwf("could not parse object file %s: %s", path, lto_get_error_message()); - - fAtoms.push_back(&fInternalAtom); - - uint32_t count = ::lto_module_get_num_symbols(fModule); - for (uint32_t i=0; i < count; ++i) { - const char* name = ::lto_module_get_symbol_name(fModule, i); - lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i); - - ObjectFile::Atom::DefinitionKind kind; - switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { - case LTO_SYMBOL_DEFINITION_REGULAR: - kind = ObjectFile::Atom::kRegularDefinition; - break; - case LTO_SYMBOL_DEFINITION_TENTATIVE: - kind = ObjectFile::Atom::kTentativeDefinition; - break; - case LTO_SYMBOL_DEFINITION_WEAK: - kind = ObjectFile::Atom::kWeakDefinition; - break; - case LTO_SYMBOL_DEFINITION_UNDEFINED: - kind = ObjectFile::Atom::kExternalDefinition; - break; - default: - throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); - } - - // make LLVM atoms for definitions and a reference for undefines - if ( kind != ObjectFile::Atom::kExternalDefinition ) { - ObjectFile::Atom::Scope scope; - switch ( attr & LTO_SYMBOL_SCOPE_MASK) { - case LTO_SYMBOL_SCOPE_INTERNAL: - scope = ObjectFile::Atom::scopeTranslationUnit; - break; - case LTO_SYMBOL_SCOPE_HIDDEN: - scope = ObjectFile::Atom::scopeLinkageUnit; - break; - case LTO_SYMBOL_SCOPE_DEFAULT: - scope = ObjectFile::Atom::scopeGlobal; - break; - default: - throwf("unknown scope for symbol %s in bitcode file %s", name, path); - } - // only make atoms for non-internal symbols - if ( scope == ObjectFile::Atom::scopeTranslationUnit ) - continue; - uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); - // make Atom - fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom)); - } - else { - // add to list of external references - fInternalAtom.addReference(name); - } - } -} - -const char* Reader::tripletPrefixForArch(cpu_type_t arch) -{ - switch (arch) { - case CPU_TYPE_POWERPC: - return "powerpc-"; - case CPU_TYPE_POWERPC64: - return "powerpc64-"; - case CPU_TYPE_I386: - return "i386-"; - case CPU_TYPE_X86_64: - return "x86_64-"; - case CPU_TYPE_ARM: - return "arm-"; - } - return ""; -} - -bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) -{ - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); -} - -void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, const std::set& deadAtoms, - uint32_t nextInputOrdinal, ObjectFile::Reader* writer, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - int okind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) -{ - // this method is call on all Readers. We want the first call to trigger optimization - // across all Readers and the subsequent calls to do nothing. - if ( fgOptimized ) - return; - fgOptimized = true; - - Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency - - // print out LTO version string if -v was used - if ( verbose ) - fprintf(stderr, "%s\n", lto_get_version()); - - // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); - for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - if ( ::lto_codegen_add_module(generator, (*it)->fModule) ) - throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message()); - } - - // add any -mllvm command line options - for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { - ::lto_codegen_debug_options(generator, *it); - } - - // the linker must preserve all globals in dylibs and flat images - const bool globalsNeedPreserving = allGlobalsAReDeadStripRoots || fReaderOptions.fFlatNamespace; - - // The atom graph uses directed edges (references). Collect all references where - // originating atom is not part of any LTO Reader. This allows optimizer to optimize an - // external (i.e. not originated from same .o file) reference if all originating atoms are also - // defined in llvm bitcode file. - CStringSet nonLLVMRefs; - CStringToAtom llvmAtoms; - bool hasNonllvmAtoms = false; - for (std::vector::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - // only look at references come from an atom that is not an llvm atom - if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) { - // remember if we've seen any atoms not from an llvm reader and not from the writer - if ( atom->getFile() != writer ) - hasNonllvmAtoms = true; - std::vector& refs = atom->getReferences(); - for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { - ObjectFile::Reference* ref = *ri; - // add target name to set if target is an llvm atom - if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) { - nonLLVMRefs.insert(ref->getTargetName()); - } - } - } - else { - const char* name = atom->getName(); - if ( name != NULL ) - llvmAtoms[name] = (Atom*)atom; - } - } - // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions - // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced - // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead - // atom so that the linker can replace it with the mach-o one later. - CStringToAtom deadllvmAtoms; - for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) { - const char* name = atom->getName(); - ::lto_codegen_add_must_preserve_symbol(generator, name); - deadllvmAtoms[name] = (Atom*)atom; - } - } - - - // tell code generator about symbols that must be preserved - for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { - const char* name = it->first; - Atom* atom = it->second; - // Include llvm Symbol in export list if it meets one of following two conditions - // 1 - globals need preserving and atom scope is global (and not linkage unit). - // 2 - included in nonLLVMRefs set. - // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( globalsNeedPreserving && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - } - - // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) - if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) { - if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { - // HACK, no good way to tell linker we are all done, so just quit - exit(0); - } - warning("could not produce merged bitcode file"); - } - - // if requested, save off merged bitcode file - if ( saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } - - // set code-gen model - lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - switch ( outputKind ) { - case Options::kDynamicExecutable: - if ( pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: // ?? Is this appropriate ? - case Options::kDyld: - if ( allowTextRelocs ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - break; - case Options::kStaticExecutable: - model = LTO_CODEGEN_PIC_MODEL_STATIC; - break; - } - if ( ::lto_codegen_set_pic_model(generator, model) ) - throwf("could not create set codegen model: %s", lto_get_error_message()); - - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); - - // if requested, save off temp mach-o file - if ( saveTemps ) { - char tempMachoPath[MAXPATHLEN]; - strcpy(tempMachoPath, outputFilePath); - strcat(tempMachoPath, ".lto.o"); - int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if ( fd != -1) { - ::write(fd, machOFile, machOFileLen); - ::close(fd); - } - } - - // parse generated mach-o file into a MachOReader - ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal); - - // sync generated mach-o atoms with existing atoms ld knows about - std::vector machoAtoms = machoReader->getAtoms(); - for (std::vector::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - const char* name = atom->getName(); - if ( name != NULL ) { - CStringToAtom::iterator pos = llvmAtoms.find(name); - if ( pos != llvmAtoms.end() ) { - // turn Atom into a proxy for this mach-o atom - pos->second->setRealAtom(atom); - } - else { - // an atom of this name was not in the allAtoms list the linker gave us - if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { - // this corresponding to an atom that the linker coalesced away. Ignore it - // Make sure there any dependent atoms are also marked dead - std::vector& refs = atom->getReferences(); - for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { - ObjectFile::Reference* ref = *ri; - if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX - ObjectFile::Atom* targ = &ref->getTarget(); - deadllvmAtoms[targ->getName()] = (Atom*)atom; - } - } - } - else - { - // this is something new that lto conjured up, tell ld its new - newAtoms.push_back(atom); - } - } - } - else { - // ld only knew about named atoms, so this one must be new - newAtoms.push_back(atom); - } - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* ref = *rit; - const char* targetName = ref->getTargetName(); - CStringToAtom::iterator pos; - if (targetName != NULL) { - switch ( ref->getTargetBinding() ) { - case ObjectFile::Reference::kUnboundByName: - // accumulate unbounded references so that ld can bound them. - additionalUndefines.push_back(targetName); - break; - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - // If mach-o atom is referencing another mach-o atom then - // reference is not going through Atom proxy. Fix it here to ensure that all - // llvm symbol references always go through Atom proxy. - pos = llvmAtoms.find(targetName); - if ( pos != llvmAtoms.end() ) - ref->setTarget(*pos->second, ref->getTargetOffset()); - break; - case ObjectFile::Reference::kDontBind: - break; - } - } - } - } - - // Remove InternalAtoms from ld - std::set deletedAtoms; - for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - deletedAtoms.insert(&((*it)->fInternalAtom)); - } - // Remove Atoms from ld if code generator optimized them away - for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { - // check if setRealAtom() called on this Atom - if ( li->second->getRealAtom() == NULL ) - deletedAtoms.insert(li->second); - } - allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), RemovableAtoms(deletedAtoms)), allAtoms.end()); -} - - -ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal) -{ - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - } - throw "LLVM LTO, file is not of required architecture"; -} - -}; // namespace lto - - -void printLTOVersion(Options &opts) { - const char* vers = lto_get_version(); - if ( vers != NULL ) - fprintf(stderr, "%s\n", vers); -} - - -#endif - diff --git a/ld64/FireOpal/src/MachOFileAbstraction.hpp b/ld64/FireOpal/src/MachOFileAbstraction.hpp deleted file mode 100644 index 83a8ee1..0000000 --- a/ld64/FireOpal/src/MachOFileAbstraction.hpp +++ /dev/null @@ -1,925 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ -*/ -#ifndef __MACH_O_FILE_ABSTRACTION__ -#define __MACH_O_FILE_ABSTRACTION__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "FileAbstraction.hpp" -#include "Architectures.hpp" - -// stuff that will eventually go away once newer cctools headers are widespread -#ifndef LC_LAZY_LOAD_DYLIB - #define LC_LAZY_LOAD_DYLIB 0x20 -#endif -#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS - #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 -#endif -#ifndef CPU_SUBTYPE_ARM_V5TEJ - #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) -#endif -#ifndef CPU_SUBTYPE_ARM_XSCALE - #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) -#endif -#ifndef CPU_SUBTYPE_ARM_V7 - #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) -#endif -#ifndef N_ARM_THUMB_DEF - #define N_ARM_THUMB_DEF 0x0008 -#endif -enum reloc_type_arm -{ - ARM_RELOC_VANILLA, /* generic relocation as discribed above */ - ARM_RELOC_PAIR, /* the second relocation entry of a pair */ - ARM_RELOC_SECTDIFF, /* a PAIR follows with subtract symbol value */ - ARM_RELOC_LOCAL_SECTDIFF, /* like ARM_RELOC_SECTDIFF, but the symbol - referenced was local. */ - ARM_RELOC_PB_LA_PTR,/* prebound lazy pointer */ - ARM_RELOC_BR24, /* 24 bit branch displacement (to a word address) */ - ARM_THUMB_RELOC_BR22, /* 22 bit branch displacement (to a half-word - address) */ -}; - -#ifndef LC_ENCRYPTION_INFO - #define LC_ENCRYPTION_INFO 0x21 - struct encryption_info_command { - uint32_t cmd; - uint32_t cmdsize; - uint32_t cryptoff; /* file offset of encrypted range */ - uint32_t cryptsize; /* file size of encrypted range */ - uint32_t cryptid; /* which enryption system, 0 means not-encrypted yet */ - }; -#endif - - -// -// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness -// - - - -// -// mach-o file header -// -template struct macho_header_content {}; -template <> struct macho_header_content > { mach_header fields; }; -template <> struct macho_header_content > { mach_header_64 fields; }; -template <> struct macho_header_content > { mach_header fields; }; -template <> struct macho_header_content > { mach_header_64 fields; }; - -template -class macho_header { -public: - uint32_t magic() const INLINE { return E::get32(header.fields.magic); } - void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } - - uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } - void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } - - uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } - void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } - - uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } - void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } - - uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } - void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } - - uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } - void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } - - uint32_t flags() const INLINE { return E::get32(header.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } - - uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } - void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } - - typedef typename P::E E; -private: - macho_header_content

header; -}; - - -// -// mach-o load command -// -template -class macho_load_command { -public: - uint32_t cmd() const INLINE { return E::get32(command.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } - - typedef typename P::E E; -private: - load_command command; -}; - - -// -// mach-o segment load command -// -template struct macho_segment_content {}; -template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; -template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; -template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; -template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; - -template -class macho_segment_command { -public: - uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } - - const char* segname() const INLINE { return segment.fields.segname; } - void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } - - uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } - void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } - - uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } - void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } - - uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } - void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } - - uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } - void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } - - uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } - void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } - - uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } - void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } - - uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } - void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } - - uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } - - enum { - CMD = macho_segment_content

::CMD - }; - - typedef typename P::E E; -private: - macho_segment_content

segment; -}; - - -// -// mach-o section -// -template struct macho_section_content {}; -template <> struct macho_section_content > { section fields; }; -template <> struct macho_section_content > { section_64 fields; }; -template <> struct macho_section_content > { section fields; }; -template <> struct macho_section_content > { section_64 fields; }; - -template -class macho_section { -public: - const char* sectname() const INLINE { return section.fields.sectname; } - void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } - - const char* segname() const INLINE { return section.fields.segname; } - void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } - - uint64_t addr() const INLINE { return P::getP(section.fields.addr); } - void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } - - uint64_t size() const INLINE { return P::getP(section.fields.size); } - void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } - - uint32_t offset() const INLINE { return E::get32(section.fields.offset); } - void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } - - uint32_t align() const INLINE { return E::get32(section.fields.align); } - void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } - - uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } - void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } - - uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } - void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } - - uint32_t flags() const INLINE { return E::get32(section.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } - - uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } - void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } - - uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } - void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } - - typedef typename P::E E; -private: - macho_section_content

section; -}; - - -// -// mach-o dylib load command -// -template -class macho_dylib_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } - void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } - - uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } - void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } - - uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } - void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } - - uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } - void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } - - const char* name() const INLINE { return (const char*)&fields + name_offset(); } - void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - dylib_command fields; -}; - - -// -// mach-o dylinker load command -// -template -class macho_dylinker_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } - void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } - - const char* name() const INLINE { return (const char*)&fields + name_offset(); } - void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - dylinker_command fields; -}; - - -// -// mach-o sub_framework load command -// -template -class macho_sub_framework_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } - void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } - - const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } - void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_framework_command fields; -}; - - -// -// mach-o sub_client load command -// -template -class macho_sub_client_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } - void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } - - const char* client() const INLINE { return (const char*)&fields + client_offset(); } - void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_client_command fields; -}; - - -// -// mach-o sub_umbrella load command -// -template -class macho_sub_umbrella_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } - void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } - - const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } - void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_umbrella_command fields; -}; - - -// -// mach-o sub_library load command -// -template -class macho_sub_library_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } - void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } - - const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } - void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_library_command fields; -}; - - -// -// mach-o uuid load command -// -template -class macho_uuid_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); } - - typedef typename P::E E; -private: - uuid_command fields; -}; - - -// -// mach-o routines load command -// -template struct macho_routines_content {}; -template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; -template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; -template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; -template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; - -template -class macho_routines_command { -public: - uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } - - uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } - void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } - - uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } - void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } - - uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } - void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } - - uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } - void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } - - uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } - void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } - - uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } - void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } - - uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } - void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } - - uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } - void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } - - typedef typename P::E E; - enum { - CMD = macho_routines_content

::CMD - }; -private: - macho_routines_content

routines; -}; - - -// -// mach-o symbol table load command -// -template -class macho_symtab_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t symoff() const INLINE { return E::get32(fields.symoff); } - void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } - - uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } - void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } - - uint32_t stroff() const INLINE { return E::get32(fields.stroff); } - void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } - - uint32_t strsize() const INLINE { return E::get32(fields.strsize); } - void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } - - - typedef typename P::E E; -private: - symtab_command fields; -}; - - -// -// mach-o dynamic symbol table load command -// -template -class macho_dysymtab_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } - void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } - - uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } - void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } - - uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } - void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } - - uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } - void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } - - uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } - void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } - - uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } - void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } - - uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } - void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } - - uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } - void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } - - uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } - void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } - - uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } - void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } - - uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } - void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } - - uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } - void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } - - uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } - void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } - - uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } - void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } - - uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } - void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } - - uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } - void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } - - uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } - void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } - - uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } - void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } - - typedef typename P::E E; -private: - dysymtab_command fields; -}; - - - - -// -// mach-o module table entry (for compatibility with old ld/dyld) -// -template struct macho_dylib_module_content {}; -template <> struct macho_dylib_module_content > { struct dylib_module fields; }; -template <> struct macho_dylib_module_content > { struct dylib_module fields; }; -template <> struct macho_dylib_module_content > { struct dylib_module_64 fields; }; -template <> struct macho_dylib_module_content > { struct dylib_module_64 fields; }; - -template -class macho_dylib_module { -public: - uint32_t module_name() const INLINE { return E::get32(module.fields.module_name); } - void set_module_name(uint32_t value) INLINE { E::set32(module.fields.module_name, value); } - - uint32_t iextdefsym() const INLINE { return E::get32(module.fields.iextdefsym); } - void set_iextdefsym(uint32_t value) INLINE { E::set32(module.fields.iextdefsym, value); } - - uint32_t nextdefsym() const INLINE { return E::get32(module.fields.nextdefsym); } - void set_nextdefsym(uint32_t value) INLINE { E::set32(module.fields.nextdefsym, value); } - - uint32_t irefsym() const INLINE { return E::get32(module.fields.irefsym); } - void set_irefsym(uint32_t value) INLINE { E::set32(module.fields.irefsym, value); } - - uint32_t nrefsym() const INLINE { return E::get32(module.fields.nrefsym); } - void set_nrefsym(uint32_t value) INLINE { E::set32(module.fields.nrefsym, value); } - - uint32_t ilocalsym() const INLINE { return E::get32(module.fields.ilocalsym); } - void set_ilocalsym(uint32_t value) INLINE { E::set32(module.fields.ilocalsym, value); } - - uint32_t nlocalsym() const INLINE { return E::get32(module.fields.nlocalsym); } - void set_nlocalsym(uint32_t value) INLINE { E::set32(module.fields.nlocalsym, value); } - - uint32_t iextrel() const INLINE { return E::get32(module.fields.iextrel); } - void set_iextrel(uint32_t value) INLINE { E::set32(module.fields.iextrel, value); } - - uint32_t nextrel() const INLINE { return E::get32(module.fields.nextrel); } - void set_nextrel(uint32_t value) INLINE { E::set32(module.fields.nextrel, value); } - - uint16_t iinit() const INLINE { return E::get32(module.fields.iinit_iterm) & 0xFFFF; } - uint16_t iterm() const INLINE { return E::get32(module.fields.iinit_iterm) > 16; } - void set_iinit_iterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.iinit_iterm, (term<<16) | (init &0xFFFF)); } - - uint16_t ninit() const INLINE { return E::get32(module.fields.ninit_nterm) & 0xFFFF; } - uint16_t nterm() const INLINE { return E::get32(module.fields.ninit_nterm) > 16; } - void set_ninit_nterm(uint16_t init, uint16_t term) INLINE { E::set32(module.fields.ninit_nterm, (term<<16) | (init &0xFFFF)); } - - uint64_t objc_module_info_addr() const INLINE { return P::getP(module.fields.objc_module_info_addr); } - void set_objc_module_info_addr(uint64_t value) INLINE { P::setP(module.fields.objc_module_info_addr, value); } - - uint32_t objc_module_info_size() const INLINE { return E::get32(module.fields.objc_module_info_size); } - void set_objc_module_info_size(uint32_t value) INLINE { E::set32(module.fields.objc_module_info_size, value); } - - - typedef typename P::E E; -private: - macho_dylib_module_content

module; -}; - - -// -// mach-o dylib_reference entry -// -template -class macho_dylib_reference { -public: - uint32_t isym() const INLINE { return E::getBits(fields, 0, 24); } - void set_isym(uint32_t value) INLINE { E::setBits(fields, value, 0, 24); } - - uint8_t flags() const INLINE { return E::getBits(fields, 24, 8); } - void set_flags(uint8_t value) INLINE { E::setBits(fields, value, 24, 8); } - - typedef typename P::E E; -private: - uint32_t fields; -}; - - - -// -// mach-o two-level hints load command -// -template -class macho_dylib_table_of_contents { -public: - uint32_t symbol_index() const INLINE { return E::get32(fields.symbol_index); } - void set_symbol_index(uint32_t value) INLINE { E::set32(fields.symbol_index, value); } - - uint32_t module_index() const INLINE { return E::get32(fields.module_index); } - void set_module_index(uint32_t value) INLINE { E::set32(fields.module_index, value); } - - typedef typename P::E E; -private: - dylib_table_of_contents fields; -}; - - - -// -// mach-o two-level hints load command -// -template -class macho_twolevel_hints_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t offset() const INLINE { return E::get32(fields.offset); } - void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } - - uint32_t nhints() const INLINE { return E::get32(fields.nhints); } - void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } - - typedef typename P::E E; -private: - twolevel_hints_command fields; -}; - - -// -// mach-o threads load command -// -template -class macho_thread_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t flavor() const INLINE { return E::get32(fields_flavor); } - void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } - - uint32_t count() const INLINE { return E::get32(fields_count); } - void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } - - uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } - void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } - - typedef typename P::E E; - typedef typename P::uint_t pint_t; -private: - struct thread_command fields; - uint32_t fields_flavor; - uint32_t fields_count; - pint_t thread_registers[1]; -}; - - -// -// mach-o misc data -// -template -class macho_linkedit_data_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } - void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } - - uint32_t datasize() const INLINE { return E::get32(fields.datasize); } - void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } - - - typedef typename P::E E; -private: - struct linkedit_data_command fields; -}; - - -// -// mach-o rpath -// -template -class macho_rpath_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t path_offset() const INLINE { return E::get32(fields.path.offset); } - void set_path_offset(uint32_t value) INLINE { E::set32(fields.path.offset, value); } - - const char* path() const INLINE { return (const char*)&fields + path_offset(); } - void set_path_offset() INLINE { set_path_offset(sizeof(fields)); } - - - typedef typename P::E E; -private: - struct rpath_command fields; -}; - - - -// -// mach-o symbol table entry -// -template struct macho_nlist_content {}; -template <> struct macho_nlist_content > { struct nlist fields; }; -template <> struct macho_nlist_content > { struct nlist_64 fields; }; -template <> struct macho_nlist_content > { struct nlist fields; }; -template <> struct macho_nlist_content > { struct nlist_64 fields; }; - -template -class macho_nlist { -public: - uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } - void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } - - uint8_t n_type() const INLINE { return entry.fields.n_type; } - void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } - - uint8_t n_sect() const INLINE { return entry.fields.n_sect; } - void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } - - uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } - void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } - - uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } - void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } - - typedef typename P::E E; -private: - macho_nlist_content

entry; -}; - - - -// -// mach-o relocation info -// -template -class macho_relocation_info { -public: - uint32_t r_address() const INLINE { return E::get32(address); } - void set_r_address(uint32_t value) INLINE { E::set32(address, value); } - - uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } - void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } - - bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } - void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } - - uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } - void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } - - bool r_extern() const INLINE { return E::getBits(other, 27, 1); } - void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } - - uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } - void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } - - void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } - - typedef typename P::E E; -private: - uint32_t address; - uint32_t other; -}; - - -// -// mach-o scattered relocation info -// The bit fields are always in big-endian order (see mach-o/reloc.h) -// -template -class macho_scattered_relocation_info { -public: - bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } - void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } - - bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } - void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } - - uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } - void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } - - uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } - void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } - - uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } - void set_r_address(uint32_t x) { if ( x > 0x00FFFFFF ) throw "scattered reloc r_address too large"; - uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } - - uint32_t r_value() const INLINE { return E::get32(value); } - void set_r_value(uint32_t x) INLINE { E::set32(value, x); } - - uint32_t r_other() const INLINE { return other; } - - void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } - - typedef typename P::E E; -private: - uint32_t other; - uint32_t value; -}; - - - -// -// mach-o encyrption info load command -// -template -class macho_encryption_info_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); } - void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); } - - uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); } - void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); } - - uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); } - void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); } - - typedef typename P::E E; -private: - encryption_info_command fields; -}; - - - -#endif // __MACH_O_FILE_ABSTRACTION__ - - diff --git a/ld64/FireOpal/src/MachOReaderDylib.hpp b/ld64/FireOpal/src/MachOReaderDylib.hpp deleted file mode 100644 index eb8e844..0000000 --- a/ld64/FireOpal/src/MachOReaderDylib.hpp +++ /dev/null @@ -1,926 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_DYLIB_MACH_O__ -#define __OBJECT_FILE_DYLIB_MACH_O__ - -#include -#include -#include -#include - - -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "ObjectFile.h" - -// -// -// To implement architecture xxx, you must write template specializations for the following method: -// Reader::validFile() -// -// - - - - -namespace mach_o { -namespace dylib { - - -// forward reference -template class Reader; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } -private: - const char* fName; -}; - - -// -// An ExportAtom has no content. It exists so that the linker can track which imported -// symbols came from which dynamic libraries. -// -template -class ExportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const { return fName; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual bool dontDeadStrip() const { return false; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return "._imports"; } - virtual Segment& getSegment() const { return fgImportSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const {} - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - typedef typename A::P P; - - ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal) - : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {} - virtual ~ExportAtom() {} - - ObjectFile::Reader& fOwner; - const char* fName; - uint32_t fOrdinal; - bool fWeakDefinition; - - static std::vector fgEmptyReferenceList; - static Segment fgImportSegment; -}; - -template -Segment ExportAtom::fgImportSegment("__LINKEDIT"); - -template -std::vector ExportAtom::fgEmptyReferenceList; - - - -class ImportReference : public ObjectFile::Reference -{ -public: - ImportReference(const char* name) - : fTarget(NULL), fTargetName(strdup(name)) {} - virtual ~ImportReference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return 0; } - virtual uint64_t getFixUpOffset() const { return 0; } - virtual const char* getTargetName() const { return fTargetName; } - virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } - virtual uint64_t getTargetOffset() const { return 0; } - virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } - virtual const char* getFromTargetName() const { return NULL; } - virtual uint64_t getFromTargetOffset() const { return 0; } - virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; } - virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } - virtual const char* getDescription() const { return "dylib import reference"; } - -private: - const ObjectFile::Atom* fTarget; - const char* fTargetName; -}; - - -// -// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace -// the imports of all flat dylibs are checked -// -template -class ImportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return "flat-imports"; } - virtual const char* getDisplayName() const { return "flat_namespace undefines"; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return false; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return "._imports"; } - virtual Segment& getSegment() const { return fgImportSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const {} - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - typedef typename A::P P; - - ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector& imports) - : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); } - virtual ~ImportAtom() {} - void makeReferences(std::vector& imports) { - for (std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { - fReferences.push_back(new ImportReference(*it)); - } - } - - - ObjectFile::Reader& fOwner; - uint32_t fOrdinal; - std::vector fReferences; - - static Segment fgImportSegment; -}; - -template -Segment ImportAtom::fgImportSegment("__LINKEDIT"); - - - - -// -// The reader for a dylib extracts all exported symbols names from the memory-mapped -// dylib, builds a hash table, then unmaps the file. This is an important memory -// savings for large dylibs. -// -template -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, bool executableOrDylib); - Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const DynamicLibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, - uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabs() { return NULL; } - virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; } - virtual const char* getInstallPath() { return fDylibInstallPath; } - virtual uint32_t getTimestamp() { return fDylibTimeStamp; } - virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } - virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } - virtual void processIndirectLibraries(DylibHander* handler); - virtual void setExplicitlyLinked() { fExplicitlyLinked = true; } - virtual bool explicitlyLinked() { return fExplicitlyLinked; } - virtual bool implicitlyLinked() { return fImplicitlyLinked; } - virtual bool providedExportAtom() { return fProvidedAtom; } - virtual const char* parentUmbrella() { return fParentUmbrella; } - virtual std::vector* getAllowableClients(); - virtual bool hasWeakExternals() { return fHasWeakExports; } - virtual bool isLazyLoadedDylib() { return fLazyLoaded; } - - virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } - -protected: - - struct ReExportChain { ReExportChain* prev; Reader* reader; }; - - void assertNoReExportCycles(ReExportChain*); - -private: - typedef typename A::P P; - typedef typename A::P::E E; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; - typedef __gnu_cxx::hash_set, CStringEquals> NameSet; - typedef typename NameToAtomMap::iterator NameToAtomMapIterator; - - struct PathAndFlag { const char* path; bool reExport; }; - - bool isPublicLocation(const char* path); - void addSymbol(const char* name, bool weak, uint32_t ordinal); - - const char* fPath; - const char* fParentUmbrella; - std::vector fAllowableClients; - const char* fDylibInstallPath; - uint32_t fDylibTimeStamp; - uint32_t fDylibtCurrentVersion; - uint32_t fDylibCompatibilityVersion; - uint32_t fReExportedOrdinal; - std::vector fDependentLibraryPaths; - NameToAtomMap fAtoms; - NameSet fIgnoreExports; - bool fNoRexports; - bool fHasWeakExports; - const bool fLinkingFlat; - const bool fLinkingMainExecutable; - bool fExplictReExportFound; - bool fExplicitlyLinked; - bool fImplicitlyLinked; - bool fProvidedAtom; - bool fImplicitlyLinkPublicDylibs; - bool fLazyLoaded; - ObjectFile::Reader::ObjcConstraint fObjcContraint; - std::vector fReExportedChildren; - const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin; - std::vector fFlatImports; - - static bool fgLogHashtable; - static std::vector fgEmptyAtomList; -}; - -template -std::vector Reader::fgEmptyAtomList; -template -bool Reader::fgLogHashtable = false; - - -template -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const DynamicLibraryOptions& dylibOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), - fDylibCompatibilityVersion(0), fLinkingFlat(options.fFlatNamespace), - fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), - fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), - fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), - fObjcContraint(ObjectFile::Reader::kObjcNone), - fDeploymentVersionMin(options.fVersionMin) -{ - // sanity check - if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) - throw "not a valid mach-o object file"; - - fPath = strdup(path); - - const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); - const macho_load_command

* const cmdsEnd = (macho_load_command

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

) + header->sizeofcmds()); - - // write out path for -whatsloaded option - if ( options.fLogAllFiles ) - printf("%s\n", path); - - if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) ) - warning("using -root_safe but linking against %s which is not root safe", path); - - if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) ) - warning("using -setuid_safe but linking against %s which is not setuid safe", path); - - // a "blank" stub has zero load commands - if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { - // no further processing needed - munmap((caddr_t)fileContent, fileLength); - return; - } - - - // optimize the case where we know there is no reason to look at indirect dylibs - fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); - fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); - bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; - - // pass 1 builds list of all dependent libraries - const macho_load_command

* cmd = cmds; - if ( trackDependentLibraries ) { - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_REEXPORT_DYLIB: - fExplictReExportFound = true; - // fall into next case - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - PathAndFlag entry; - entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); - entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); - fDependentLibraryPaths.push_back(entry); - break; - } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - } - - // pass 2 determines re-export info - const macho_dysymtab_command

* dynamicInfo = NULL; - const macho_nlist

* symbolTable = NULL; - const char* strings = NULL; - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); - strings = (char*)header + symtab->stroff(); - } - break; - case LC_DYSYMTAB: - dynamicInfo = (macho_dysymtab_command

*)cmd; - break; - case LC_ID_DYLIB: - { - macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; - fDylibInstallPath = strdup(dylibID->name()); - fDylibTimeStamp = dylibID->timestamp(); - fDylibtCurrentVersion = dylibID->current_version(); - fDylibCompatibilityVersion = dylibID->compatibility_version(); - } - break; - case LC_SUB_UMBRELLA: - if ( trackDependentLibraries ) { - const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - const char* dylibName = it->path; - const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - it->reExport = true; - } - } - break; - case LC_SUB_LIBRARY: - if ( trackDependentLibraries) { - const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - const char* dylibName = it->path; - const char* lastSlash = strrchr(dylibName, '/'); - const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) - leafStart = dylibName; - const char* firstDot = strchr(leafStart, '.'); - int len = strlen(leafStart); - if ( firstDot != NULL ) - len = firstDot - leafStart; - if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - it->reExport = true; - } - } - break; - case LC_SUB_FRAMEWORK: - fParentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); - break; - case macho_segment_command

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

*)cmd)->segname(), "__OBJC") == 0 ) { - const macho_segment_command

* segment = (macho_segment_command

*)cmd; - const macho_section

* const sectionsStart = (macho_section

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

)); - const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( strcmp(sect->sectname(), "__image_info") == 0 ) { - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); - if ( (sect->size() >= 8) && (contents[0] == 0) ) { - uint32_t flags = E::get32(contents[1]); - if ( (flags & 4) == 4 ) - fObjcContraint = ObjectFile::Reader::kObjcGC; - else if ( (flags & 2) == 2 ) - fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - else - fObjcContraint = ObjectFile::Reader::kObjcRetainRelease; - } - else if ( sect->size() > 0 ) { - warning("can't parse __OBJC/__image_info section in %s", fPath); - } - } - } - } - } - - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - - // Process the rest of the commands here. - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SUB_CLIENT: - const char *temp = strdup(((macho_sub_client_command

*)cmd)->client()); - fAllowableClients.push_back(temp); - break; - } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); - } - - // validate minimal load commands - if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) - throwf("dylib %s missing LC_ID_DYLIB load command", path); - if ( symbolTable == NULL ) - throw "binary missing LC_SYMTAB load command"; - if ( dynamicInfo == NULL ) - throw "binary missing LC_DYSYMTAB load command"; - - // if linking flat and this is a flat dylib, create one atom that references all imported symbols - if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { - std::vector importNames; - importNames.reserve(dynamicInfo->nundefsym()); - const macho_nlist

* start = &symbolTable[dynamicInfo->iundefsym()]; - const macho_nlist

* end = &start[dynamicInfo->nundefsym()]; - for (const macho_nlist

* sym=start; sym < end; ++sym) { - importNames.push_back(&strings[sym->n_strx()]); - } - fFlatImports.push_back(new ImportAtom(*this, ordinalBase++, importNames)); - } - - // build hash table - if ( dynamicInfo->tocoff() == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); - const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; - const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; - fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count - uint32_t index = ordinalBase; - for (const macho_nlist

* sym=start; sym < end; ++sym, ++index) { - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, index); - } - fReExportedOrdinal = index; - } - else { - int32_t count = dynamicInfo->ntoc(); - fAtoms.resize(count); // set initial bucket count - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, path); - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + dynamicInfo->tocoff()); - for (int32_t i = 0; i < count; ++i) { - const uint32_t index = E::get32(toc[i].symbol_index); - const macho_nlist

* sym = &symbolTable[index]; - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, ordinalBase+i); - } - fReExportedOrdinal = ordinalBase + count; - } - - - // unmap file - munmap((caddr_t)fileContent, fileLength); -} - - - -template -void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) -{ - // symbols that start with $ld$ are meta-data to the static linker - // need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { - // $ld$ $ $ - const char* symAction = &name[4]; - const char* symCond = strchr(symAction, '$'); - if ( symCond != NULL ) { - ObjectFile::ReaderOptions::VersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinUnset; - if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { - switch ( symCond[6] - '0' ) { - case 0: - case 1: - symVersionCondition = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - symVersionCondition = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - symVersionCondition = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - symVersionCondition = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - symVersionCondition = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - symVersionCondition = ObjectFile::ReaderOptions::k10_6; - break; - } - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weak, ordinal); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); - } - } - } - else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); - } - } - else { - warning("bad symbol version: %s in dylib %s", name, this->getPath()); - } - } - else { - warning("bad symbol condition: %s in dylib %s", name, this->getPath()); - } - } - - // add symbol as possible export if we are not supposed to ignore it - if ( fIgnoreExports.count(name) == 0 ) { - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = weak; - bucket.ordinal = ordinal; - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[strdup(name)] = bucket; - } -} - - -template -std::vector& Reader::getAtoms() -{ - return fFlatImports; -} - - -template -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - std::vector* atoms = NULL; - - NameToAtomMapIterator pos = fAtoms.find(name); - if ( pos != fAtoms.end() ) { - if ( pos->second.atom == NULL ) { - // instantiate atom and update hash table - pos->second.atom = new ExportAtom(*this, name, pos->second.weak, pos->second.ordinal); - fProvidedAtom = true; - if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); - } - // return a vector of one atom - atoms = new std::vector; - atoms->push_back(pos->second.atom); - } - else { - if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); - // if not supposed to ignore this export, see if I have it - if ( fIgnoreExports.count(name) == 0 ) { - // look in children that I re-export - for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { - //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); - std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); - if ( childAtoms != NULL ) { - // make a new atom that says this reader is the owner - bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); - // return a vector of one atom - ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); - fProvidedAtom = true; - atoms = new std::vector; - atoms->push_back(newAtom); - delete childAtoms; - return atoms; - } - } - } - } - return atoms; -} - - - -template -bool Reader::isPublicLocation(const char* path) -{ - // -no_implicit_dylibs disables this optimization - if ( ! fImplicitlyLinkPublicDylibs ) - return false; - - // /usr/lib is a public location - if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) ) - return true; - - // /System/Library/Frameworks/ is a public location - if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { - const char* frameworkDot = strchr(&path[27], '.'); - // but only top level framework - // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true - // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false - if ( frameworkDot != NULL ) { - int frameworkNameLen = frameworkDot - &path[27]; - if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) - return true; - } - } - - return false; -} - -template -void Reader::processIndirectLibraries(DylibHander* handler) -{ - if ( fLinkingFlat ) { - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - handler->findDylib(it->path, this->getPath()); - } - } - else if ( fNoRexports ) { - // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do - } - else { - // two-level, might have re-exports - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - if ( it->reExport ) { - //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path); - // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); - if ( isPublicLocation(child->getInstallPath()) ) { - // promote this child to be automatically added as a direct dependent if this already is - if ( this->explicitlyLinked() || this->implicitlyLinked() ) { - //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); - ((Reader*)child)->setImplicitlyLinked(); - } - else - fReExportedChildren.push_back(child); - } - else { - // add all child's symbols to me - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - else if ( !fExplictReExportFound ) { - // see if child contains LC_SUB_FRAMEWORK with my name - ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); - const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - const char* parentName = this->getPath(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { - // add all child's symbols to me - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - } - } - } - - // check for re-export cycles - ReExportChain chain; - chain.prev = NULL; - chain.reader = this; - this->assertNoReExportCycles(&chain); -} - -template -void Reader::assertNoReExportCycles(ReExportChain* prev) -{ - // recursively check my re-exported dylibs - ReExportChain chain; - chain.prev = prev; - chain.reader = this; - for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { - ObjectFile::Reader* child = *it; - // check child is not already in chain - for (ReExportChain* p = prev; p != NULL; p = p->prev) { - if ( p->reader == child ) { - throwf("cycle in dylib re-exports with %s", child->getPath()); - } - } - ((Reader*)(*it))->assertNoReExportCycles(&chain); - } -} - - -template -std::vector* Reader::getAllowableClients() -{ - std::vector* result = new std::vector; - for (typename std::vector::iterator it = fAllowableClients.begin(); - it != fAllowableClients.end(); - it++) { - result->push_back(*it); - } - return (fAllowableClients.size() != 0 ? result : NULL); -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_I386 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -}; // namespace dylib -}; // namespace mach_o - - -#endif // __OBJECT_FILE_DYLIB_MACH_O__ diff --git a/ld64/FireOpal/src/ObjectDump.cpp b/ld64/FireOpal/src/ObjectDump.cpp deleted file mode 100644 index a06c7a2..0000000 --- a/ld64/FireOpal/src/ObjectDump.cpp +++ /dev/null @@ -1,497 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include - -#include "MachOReaderRelocatable.hpp" - -#define LTO_SUPPORT 0 - -#if LTO_SUPPORT - #include "LTOReader.hpp" -#endif - -static bool sDumpContent= true; -static bool sDumpStabs = false; -static bool sSort = true; -static bool sNMmode = false; -static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; -static const char* sMatchName; -static int sPrintRestrict; -static int sPrintAlign; -static int sPrintName; - - - __attribute__((noreturn)) -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - -void warning(const char* format, ...) -{ - va_list list; - fprintf(stderr, "warning: "); - va_start(list, format); - vfprintf(stderr, format, list); - va_end(list); - fprintf(stderr, "\n"); -} - -static void dumpStabs(std::vector* stabs) -{ - // debug info - printf("stabs: (%lu)\n", stabs->size()); - for (std::vector::iterator it = stabs->begin(); it != stabs->end(); ++it ) { - ObjectFile::Reader::Stab& stab = *it; - const char* code = "?????"; - switch (stab.type) { - case N_GSYM: - code = " GSYM"; - break; - case N_FNAME: - code = "FNAME"; - break; - case N_FUN: - code = " FUN"; - break; - case N_STSYM: - code = "STSYM"; - break; - case N_LCSYM: - code = "LCSYM"; - break; - case N_BNSYM: - code = "BNSYM"; - break; - case N_OPT: - code = " OPT"; - break; - case N_RSYM: - code = " RSYM"; - break; - case N_SLINE: - code = "SLINE"; - break; - case N_ENSYM: - code = "ENSYM"; - break; - case N_SSYM: - code = " SSYM"; - break; - case N_SO: - code = " SO"; - break; - case N_OSO: - code = " OSO"; - break; - case N_LSYM: - code = " LSYM"; - break; - case N_BINCL: - code = "BINCL"; - break; - case N_SOL: - code = " SOL"; - break; - case N_PARAMS: - code = "PARMS"; - break; - case N_VERSION: - code = " VERS"; - break; - case N_OLEVEL: - code = "OLEVL"; - break; - case N_PSYM: - code = " PSYM"; - break; - case N_EINCL: - code = "EINCL"; - break; - case N_ENTRY: - code = "ENTRY"; - break; - case N_LBRAC: - code = "LBRAC"; - break; - case N_EXCL: - code = " EXCL"; - break; - case N_RBRAC: - code = "RBRAC"; - break; - case N_BCOMM: - code = "BCOMM"; - break; - case N_ECOMM: - code = "ECOMM"; - break; - case N_LENG: - code = "LENG"; - break; - } - printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string); - } -} - - -static void dumpAtomLikeNM(ObjectFile::Atom* atom) -{ - uint32_t size = atom->getSize(); - - const char* visibility; - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: - visibility = "internal"; - break; - case ObjectFile::Atom::scopeLinkageUnit: - visibility = "hidden "; - break; - case ObjectFile::Atom::scopeGlobal: - visibility = "global "; - break; - default: - visibility = " "; - break; - } - - const char* kind; - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - kind = "regular "; - break; - case ObjectFile::Atom::kTentativeDefinition: - kind = "tentative"; - break; - case ObjectFile::Atom::kWeakDefinition: - kind = "weak "; - break; - case ObjectFile::Atom::kAbsoluteSymbol: - kind = "absolute "; - break; - default: - kind = " "; - break; - } - - printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName()); -} - - -static void dumpAtom(ObjectFile::Atom* atom) -{ - if(sMatchName && strcmp(sMatchName, atom->getDisplayName())) - return; - - //printf("atom: %p\n", atom); - - // name - if(!sPrintRestrict || sPrintName) - printf("name: %s\n", atom->getDisplayName()); - - // scope - if(!sPrintRestrict) - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: - printf("scope: translation unit\n"); - break; - case ObjectFile::Atom::scopeLinkageUnit: - printf("scope: linkage unit\n"); - break; - case ObjectFile::Atom::scopeGlobal: - printf("scope: global\n"); - break; - default: - printf("scope: unknown\n"); - } - - // kind - if(!sPrintRestrict) - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - printf("kind: regular\n"); - break; - case ObjectFile::Atom::kWeakDefinition: - printf("kind: weak\n"); - break; - case ObjectFile::Atom::kTentativeDefinition: - printf("kind: tentative\n"); - break; - case ObjectFile::Atom::kExternalDefinition: - printf("kind: import\n"); - break; - case ObjectFile::Atom::kExternalWeakDefinition: - printf("kind: weak import\n"); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - printf("kind: absolute symbol\n"); - break; - default: - printf("kind: unknown\n"); - } - - // segment and section - if(!sPrintRestrict && (atom->getSectionName() != NULL) ) - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); - - // attributes - if(!sPrintRestrict) { - printf("attrs: "); - if ( atom->dontDeadStrip() ) - printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); - if ( atom->isThumb() ) - printf("thumb "); - printf("\n"); - } - - // size - if(!sPrintRestrict) - printf("size: 0x%012llX\n", atom->getSize()); - - // alignment - if(!sPrintRestrict || sPrintAlign) - printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); - - // content - if (!sPrintRestrict && sDumpContent ) { - uint64_t size = atom->getSize(); - if ( size < 4096 ) { - uint8_t content[size]; - atom->copyRawContent(content); - printf("content: "); - if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { - printf("\""); - for (unsigned int i=0; i < size; ++i) { - if(content[i]<'!' || content[i]>=127) - printf("\\%o", content[i]); - else - printf("%c", content[i]); - } - printf("\""); - } - else { - for (unsigned int i=0; i < size; ++i) - printf("%02X ", content[i]); - } - } - printf("\n"); - } - - // references - if(!sPrintRestrict) { - std::vector& references = atom->getReferences(); - const int refCount = references.size(); - printf("references: (%u)\n", refCount); - for (int i=0; i < refCount; ++i) { - ObjectFile::Reference* ref = references[i]; - printf(" %s\n", ref->getDescription()); - } - } - - // line info - if(!sPrintRestrict) { - std::vector* lineInfo = atom->getLineInfo(); - if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) { - printf("line info: (%lu)\n", lineInfo->size()); - for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { - printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); - } - } - } - - if(!sPrintRestrict) - printf("\n"); -} - -struct AtomSorter -{ - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) - { - if ( left == right ) - return false; - return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); - } -}; - - -static void dumpFile(ObjectFile::Reader* reader) -{ - // stabs debug info - if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) { - std::vector* stabs = reader->getStabs(); - if ( stabs != NULL ) - dumpStabs(stabs); - } - - // get all atoms - std::vector atoms = reader->getAtoms(); - - // make copy of vector and sort (so output is canonical) - std::vector sortedAtoms(atoms); - if ( sSort ) - std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter()); - - for(std::vector::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) { - if ( sNMmode ) - dumpAtomLikeNM(*it); - else - dumpAtom(*it); - } -} - - -static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) -{ - struct stat stat_buf; - - int fd = ::open(path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("cannot open file: %s", path); - ::fstat(fd, &stat_buf); - uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - ::close(fd); - const mach_header* mh = (mach_header*)p; - if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - const struct fat_header* fh = (struct fat_header*)p; - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { - p = p + OSSwapBigToHostInt32(archs[i].offset); - mh = (struct mach_header*)p; - } - } - } - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); -#if LTO_SUPPORT - if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) { - return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0); - } -#endif - - throwf("not a mach-o object file: %s", path); -} - -static -void -usage() -{ - fprintf(stderr, "ObjectDump options:\n" - "\t-no_content\tdon't dump contents\n" - "\t-stabs\t\tdump stabs\n" - "\t-arch aaa\tonly dump info about arch aaa\n" - "\t-only sym\tonly dump info about sym\n" - "\t-align\t\tonly print alignment info\n" - "\t-name\t\tonly print symbol names\n" - ); -} - -int main(int argc, const char* argv[]) -{ - if(argc<2) { - usage(); - return 0; - } - - ObjectFile::ReaderOptions options; - try { - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - if ( arg[0] == '-' ) { - if ( strcmp(arg, "-no_content") == 0 ) { - sDumpContent = false; - } - else if ( strcmp(arg, "-nm") == 0 ) { - sNMmode = true; - } - else if ( strcmp(arg, "-stabs") == 0 ) { - sDumpStabs = true; - } - else if ( strcmp(arg, "-no_sort") == 0 ) { - sSort = false; - } - else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = ++i -#include -#include -#include - - - -// -// These classes represent the abstract Atoms and References that are the basis of the linker. -// An Atom and a Reference correspond to a Node and Edge in graph theory. -// -// A Reader is a class which parses an object file and presents it as Atoms and References. -// All linking operations are done on Atoms and References. This makes the linker file -// format independent. -// -// A Writer takes a vector of Atoms with all References resolved and produces an executable file. -// -// - - - -namespace ObjectFile { - - -struct LineInfo -{ - uint32_t atomOffset; - const char* fileName; - uint32_t lineNumber; -}; - - -class ReaderOptions -{ -public: - ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), - fLinkingMainExecutable(false), fSlowx86Stubs(false), - fForFinalLinkedImage(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), - fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), - fImplicitlyLinkPublicDylibs(true), fLogObjectFiles(false), fLogAllFiles(false), - fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), - fTraceOutputFile(NULL), fVersionMin(kMinUnset) {} - enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; - enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; - - struct AliasPair { - const char* realName; - const char* alias; - }; - - bool fFullyLoadArchives; - bool fLoadAllObjcObjectsFromArchives; - bool fFlatNamespace; - bool fLinkingMainExecutable; - bool fSlowx86Stubs; - bool fForFinalLinkedImage; - bool fForStatic; - bool fForDyld; - bool fMakeTentativeDefinitionsReal; - bool fWhyLoad; - bool fRootSafe; - bool fSetuidSafe; - DebugInfoStripping fDebugInfoStripping; - bool fImplicitlyLinkPublicDylibs; - bool fLogObjectFiles; - bool fLogAllFiles; - bool fTraceDylibs; - bool fTraceIndirectDylibs; - bool fTraceArchives; - const char* fTraceOutputFile; - VersionMin fVersionMin; - std::vector fAliases; -}; - - -class Reader -{ -public: - enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; - struct Stab - { - class Atom* atom; - uint8_t type; - uint8_t other; - uint16_t desc; - uint32_t value; - const char* string; - }; - enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC }; - enum CpuConstraint { kCpuAny = 0 }; - - class DylibHander - { - public: - virtual ~DylibHander() {} - virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0; - }; - - - static Reader* createReader(const char* path, const ReaderOptions& options); - - virtual const char* getPath() = 0; - virtual time_t getModificationTime() = 0; - virtual DebugInfoKind getDebugInfoKind() = 0; - virtual std::vector& getAtoms() = 0; - virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; - virtual std::vector* getStabs() = 0; - virtual ObjcConstraint getObjCConstraint() { return kObjcNone; } - virtual uint32_t updateCpuConstraint(uint32_t current) { return current; } - virtual bool objcReplacementClasses() { return false; } - - // For relocatable object files only - virtual bool canScatterAtoms() { return true; } - virtual void optimize(std::vector&, std::vector&, - std::vector&, const std::set&, - uint32_t, ObjectFile::Reader* writer, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) { } - virtual bool hasLongBranchStubs() { return false; } - - // For Dynamic Libraries only - virtual const char* getInstallPath() { return NULL; } - virtual uint32_t getTimestamp() { return 0; } - virtual uint32_t getCurrentVersion() { return 0; } - virtual uint32_t getCompatibilityVersion() { return 0; } - virtual void processIndirectLibraries(DylibHander* handler) { } - virtual void setExplicitlyLinked() { } - virtual bool explicitlyLinked() { return false; } - virtual bool implicitlyLinked() { return false; } - virtual bool providedExportAtom() { return false; } - virtual const char* parentUmbrella() { return NULL; } - virtual std::vector* getAllowableClients() { return NULL; } - virtual bool hasWeakExternals() { return false; } - virtual bool isLazyLoadedDylib() { return false; } - -protected: - Reader() {} - virtual ~Reader() {} -}; - -class Segment -{ -public: - virtual const char* getName() const = 0; - virtual bool isContentReadable() const = 0; - virtual bool isContentWritable() const = 0; - virtual bool isContentExecutable() const = 0; - - uint64_t getBaseAddress() const { return fBaseAddress; } - void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } - virtual bool hasFixedAddress() const { return false; } - -protected: - Segment() : fBaseAddress(0) {} - virtual ~Segment() {} - uint64_t fBaseAddress; -}; - -class Reference; - -class Section -{ -public: - unsigned int getIndex() { return fIndex; } - uint64_t getBaseAddress() { return fBaseAddress; } - void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } - void* fOther; - -protected: - Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} - uint64_t fBaseAddress; - unsigned int fIndex; -}; - - -struct Alignment -{ - Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} - uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } - uint16_t powerOf2; - uint16_t modulus; -}; - -// -// An atom is the fundamental unit of linking. A C function or global variable is an atom. -// An atom has content and some attributes. The content of a function atom is the instructions -// that implement the function. The content of a global variable atom is its initial bits. -// -// Name: -// The name of an atom is the label name generated by the compiler. A C compiler names foo() -// as _foo. A C++ compiler names foo() as __Z3foov. -// The name refers to the first byte of the content. An atom cannot have multiple entry points. -// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. -// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. -// -// Scope: -// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond -// to the C visibility of static, hidden, default. -// -// DefinitionKind: -// An atom is one of five defintion kinds: -// regular Most atoms. -// weak C++ compiler makes some functions weak if there might be multiple copies -// that the linker needs to coalesce. -// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. -// It could be a prototype or it could be a definition. -// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists -// so that all References can be resolved. -// external-weak Same as external, but the definition in the dylib is weak. -// -// SymbolTableInclusion: -// An atom may or may not be in the symbol table in an object file. -// in Most atoms for functions or global data -// not-in Anonymous atoms such literal c-strings, or other compiler generated data -// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) -// -// Ordinal: -// When a reader is created it is given a base ordinal number. All atoms created by the reader -// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal -// values are used by the linker to sort the atom graph when producing the output file. -// -class Atom -{ -public: - enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; - enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; - enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; - - virtual Reader* getFile() const = 0; - virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; - virtual const char* getName() const = 0; - virtual const char* getDisplayName() const = 0; - virtual Scope getScope() const = 0; - virtual DefinitionKind getDefinitionKind() const = 0; - virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; - virtual bool dontDeadStrip() const = 0; - virtual bool isZeroFill() const = 0; - virtual bool isThumb() const = 0; - virtual uint64_t getSize() const = 0; - virtual std::vector& getReferences() const = 0; - virtual bool mustRemainInSection() const = 0; - virtual const char* getSectionName() const = 0; - virtual Segment& getSegment() const = 0; - virtual Atom& getFollowOnAtom() const = 0; - virtual uint32_t getOrdinal() const = 0; - virtual std::vector* getLineInfo() const = 0; - virtual Alignment getAlignment() const = 0; - virtual void copyRawContent(uint8_t buffer[]) const = 0; - virtual void setScope(Scope) = 0; - - - uint64_t getSectionOffset() const { return fSectionOffset; } - uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } - class Section* getSection() const { return fSection; } - - virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } - virtual void setSection(class Section* sect) { fSection = sect; } - -protected: - Atom() : fSectionOffset(0), fSection(NULL) {} - virtual ~Atom() {} - - uint64_t fSectionOffset; - class Section* fSection; -}; - - -// -// A Reference is a directed edge to another Atom. When an instruction in -// the content of an Atom refers to another Atom, that is represented by a -// Reference. -// -// There are two kinds of references: direct and by-name. With a direct Reference, -// the target is bound by the Reader that created it. For instance a reference to a -// static would produce a direct reference. A by-name reference requires the linker -// to find the target Atom with the required name in order to be bound. -// -// For a link to succeed all References must be bound. -// -// A Reference has an optional "from" target. This is used when the content to fix-up -// is the difference of two Atom address. For instance, if a pointer sized data Atom -// is to contain A - B, then the Atom would have on Reference with a target of "A" and -// a from-target of "B". -// -// A Reference also has a fix-up-offset. This is the offset into the content of the -// Atom holding the reference where the fix-up (relocation) will be applied. -// -// -// -class Reference -{ -public: - enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind }; - - virtual TargetBinding getTargetBinding() const = 0; - virtual TargetBinding getFromTargetBinding() const = 0; - virtual uint8_t getKind() const = 0; - virtual uint64_t getFixUpOffset() const = 0; - virtual const char* getTargetName() const = 0; - virtual Atom& getTarget() const = 0; - virtual uint64_t getTargetOffset() const = 0; - virtual Atom& getFromTarget() const = 0; - virtual const char* getFromTargetName() const = 0; - virtual uint64_t getFromTargetOffset() const = 0; - - virtual void setTarget(Atom&, uint64_t offset) = 0; - virtual void setFromTarget(Atom&) = 0; - virtual const char* getDescription() const = 0; - -protected: - Reference() {} - virtual ~Reference() {} -}; - - -}; // namespace ObjectFile - - -#endif // __OBJECTFILE__ diff --git a/ld64/FireOpal/src/Options.cpp b/ld64/FireOpal/src/Options.cpp deleted file mode 100644 index eb244ed..0000000 --- a/ld64/FireOpal/src/Options.cpp +++ /dev/null @@ -1,3150 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#include -#include -#include -#include -#include - -#include "configure.h" -#include "Options.h" -#include "Architectures.hpp" -#include "MachOFileAbstraction.hpp" - -extern void printLTOVersion(Options &opts); - - -static bool sEmitWarnings = true; -static const char* sWarningsSideFilePath = NULL; -static FILE* sWarningsSideFile = NULL; - -void warning(const char* format, ...) -{ - if ( sEmitWarnings ) { - va_list list; - if ( sWarningsSideFilePath != NULL ) { - if ( sWarningsSideFile == NULL ) - sWarningsSideFile = fopen(sWarningsSideFilePath, "a"); - } - va_start(list, format); - fprintf(stderr, "ld warning: "); - vfprintf(stderr, format, list); - fprintf(stderr, "\n"); - if ( sWarningsSideFile != NULL ) { - fprintf(sWarningsSideFile, "ld warning: "); - vfprintf(sWarningsSideFile, format, list); - fprintf(sWarningsSideFile, "\n"); - fflush(sWarningsSideFile); - } - va_end(list); - } -} - -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - -Options::Options(int argc, const char* argv[]) - : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable), - fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), - fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), - fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0), - fBaseWritableAddress(0), fSplitSegs(false), - fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), - fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), - fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), - fClientName(NULL), - fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), - fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(32), - fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), - fVerbose(false), fKeepRelocations(false), fWarnStabs(false), - fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), - fSharedRegionEligible(false), fPrintOrderFileStatistics(false), - fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), - fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), - fUsingLazyDylibLinking(false), fEncryptable(true), fSaveTempFiles(false) -{ - this->checkForClassic(argc, argv); - this->parsePreCommandLineEnvironmentSettings(); - this->parse(argc, argv); - this->parsePostCommandLineEnvironmentSettings(); - this->reconfigureDefaults(); - this->checkIllegalOptionCombinations(); -} - -Options::~Options() -{ -} - -const ObjectFile::ReaderOptions& Options::readerOptions() -{ - return fReaderOptions; -} - - -const char* Options::getOutputFilePath() -{ - return fOutputFile; -} - -std::vector& Options::getInputFiles() -{ - return fInputFiles; -} - -Options::OutputKind Options::outputKind() -{ - return fOutputKind; -} - -bool Options::bindAtLoad() -{ - return fBindAtLoad; -} - -bool Options::prebind() -{ - return fPrebind; -} - -bool Options::fullyLoadArchives() -{ - return fReaderOptions.fFullyLoadArchives; -} - -Options::NameSpace Options::nameSpace() -{ - return fNameSpace; -} - -const char* Options::installPath() -{ - if ( fDylibInstallName != NULL ) - return fDylibInstallName; - else if ( fFinalName != NULL ) - return fFinalName; - else - return fOutputFile; -} - -uint32_t Options::currentVersion() -{ - return fDylibCurrentVersion; -} - -uint32_t Options::compatibilityVersion() -{ - return fDylibCompatVersion; -} - -const char* Options::entryName() -{ - return fEntryName; -} - -uint64_t Options::baseAddress() -{ - return fBaseAddress; -} - -bool Options::keepPrivateExterns() -{ - return fKeepPrivateExterns; -} - -bool Options::interposable(const char* name) -{ - switch ( fInterposeMode ) { - case kInterposeNone: - return false; - case kInterposeAllExternal: - return true; - case kInterposeSome: - return fInterposeList.contains(name); - } - throw "internal error"; -} - -bool Options::needsModuleTable() -{ - return fNeedsModuleTable; -} - -bool Options::ignoreOtherArchInputFiles() -{ - return fIgnoreOtherArchFiles; -} - -bool Options::forceCpuSubtypeAll() -{ - return fForceSubtypeAll; -} - -bool Options::traceDylibs() -{ - return fReaderOptions.fTraceDylibs; -} - -bool Options::traceArchives() -{ - return fReaderOptions.fTraceArchives; -} - -Options::UndefinedTreatment Options::undefinedTreatment() -{ - return fUndefinedTreatment; -} - -ObjectFile::ReaderOptions::VersionMin Options::macosxVersionMin() -{ - return fReaderOptions.fVersionMin; -} - -Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() -{ - return fWeakReferenceMismatchTreatment; -} - -const char* Options::umbrellaName() -{ - return fUmbrellaName; -} - -std::vector& Options::allowableClients() -{ - return fAllowableClients; -} - -const char* Options::clientName() -{ - return fClientName; -} - -uint64_t Options::zeroPageSize() -{ - return fZeroPageSize; -} - -bool Options::hasCustomStack() -{ - return (fStackSize != 0); -} - -uint64_t Options::customStackSize() -{ - return fStackSize; -} - -uint64_t Options::customStackAddr() -{ - return fStackAddr; -} - -bool Options::hasExecutableStack() -{ - return fExecutableStack; -} - -std::vector& Options::initialUndefines() -{ - return fInitialUndefines; -} - -bool Options::printWhyLive(const char* symbolName) -{ - return ( fWhyLive.find(symbolName) != fWhyLive.end() ); -} - - -const char* Options::initFunctionName() -{ - return fInitFunctionName; -} - -const char* Options::dotOutputFile() -{ - return fDotOutputFile; -} - -bool Options::hasExportRestrictList() -{ - return (fExportMode != kExportDefault); -} - -bool Options::hasExportMaskList() -{ - return (fExportMode == kExportSome); -} - - -bool Options::hasWildCardExportRestrictList() -{ - // has -exported_symbols_list which contains some wildcards - return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); -} - - -bool Options::allGlobalsAreDeadStripRoots() -{ - // -exported_symbols_list means globals are not exported by default - if ( fExportMode == kExportSome ) - return false; - // - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - // by default unused globals in a main executable are stripped - return false; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - return true; - } - return false; -} - -uint32_t Options::minimumHeaderPad() -{ - return fMinimumHeaderPad; -} - -std::vector& Options::extraSections() -{ - return fExtraSections; -} - -std::vector& Options::sectionAlignments() -{ - return fSectionAlignments; -} - -Options::CommonsMode Options::commonsMode() -{ - return fCommonsMode; -} - -bool Options::warnCommons() -{ - return fWarnCommons; -} - -bool Options::keepRelocations() -{ - return fKeepRelocations; -} - -bool Options::warnStabs() -{ - return fWarnStabs; -} - -const char* Options::executablePath() -{ - return fExecutablePath; -} - -Options::DeadStripMode Options::deadStrip() -{ - return fDeadStrip; -} - -bool Options::shouldExport(const char* symbolName) -{ - switch (fExportMode) { - case kExportSome: - return fExportSymbols.contains(symbolName); - case kDontExportSome: - return ! fDontExportSymbols.contains(symbolName); - case kExportDefault: - return true; - } - throw "internal error"; -} - -bool Options::keepLocalSymbol(const char* symbolName) -{ - switch (fLocalSymbolHandling) { - case kLocalSymbolsAll: - return true; - case kLocalSymbolsNone: - return false; - case kLocalSymbolsSelectiveInclude: - return fLocalSymbolsIncluded.contains(symbolName); - case kLocalSymbolsSelectiveExclude: - return ! fLocalSymbolsExcluded.contains(symbolName); - } - throw "internal error"; -} - -void Options::parseArch(const char* architecture) -{ - if ( architecture == NULL ) - throw "-arch must be followed by an architecture string"; - if ( strcmp(architecture, "ppc") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; - } - else if ( strcmp(architecture, "ppc64") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC64; - fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; - } - else if ( strcmp(architecture, "i386") == 0 ) { - fArchitecture = CPU_TYPE_I386; - fSubArchitecture = CPU_SUBTYPE_I386_ALL; - } - else if ( strcmp(architecture, "x86_64") == 0 ) { - fArchitecture = CPU_TYPE_X86_64; - fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; - } - else if ( strcmp(architecture, "arm") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_ALL; - } - // compatibility support for cpu-sub-types - else if ( strcmp(architecture, "ppc750") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_750; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "ppc7400") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "ppc7450") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "ppc970") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_970; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "armv6") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V6; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "armv5") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "armv4t") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V4T; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "xscale") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; - fHasPreferredSubType = true; - } - else if ( strcmp(architecture, "armv7") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V7; - fHasPreferredSubType = true; - } - else - throwf("unknown/unsupported architecture name for: -arch %s", architecture); -} - -bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) -{ - struct stat statBuffer; - char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; - sprintf(possiblePath, format, dir, rootName); - bool found = (stat(possiblePath, &statBuffer) == 0); - if ( fTraceDylibSearching ) - printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); - if ( found ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return true; - } - return false; -} - - -Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) -{ - FileInfo result; - const int rootNameLen = strlen(rootName); - // if rootName ends in .o there is no .a vs .dylib choice - if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; - if ( checkForFile("%s/%s", dir, rootName, result) ) - return result; - } - } - else { - bool lookForDylibs = ( fOutputKind != Options::kDyld); - switch ( fLibrarySearchMode ) { - case kSearchAllDirsForDylibsThenAllDirsForArchives: - // first look in all directories for just for dylibs - if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; - if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) - return result; - } - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; - if ( checkForFile("%s/lib%s.so", dir, rootName, result) ) - return result; - } - } - // next look in all directories for just for archives - if ( !dylibsOnly ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; - if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) - return result; - } - } - break; - - case kSearchDylibAndArchiveInEachDir: - // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; - if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) - return result; - if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) - return result; - if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) ) - return result; - } - break; - } - } - throwf("library not found for -l%s", rootName); -} - -Options::FileInfo Options::findFramework(const char* frameworkName) -{ - if ( frameworkName == NULL ) - throw "-framework missing next argument"; - char temp[strlen(frameworkName)+1]; - strcpy(temp, frameworkName); - const char* name = temp; - const char* suffix = NULL; - char* comma = strchr(temp, ','); - if ( comma != NULL ) { - *comma = '\0'; - suffix = &comma[1]; - } - return findFramework(name, suffix); -} - -Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) -{ - struct stat statBuffer; - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - // ??? Shouldn't we be using String here and just initializing it? - // ??? Use str.c_str () to pull out the string for the stat call. - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, rootName); - strcat(possiblePath, ".framework/"); - strcat(possiblePath, rootName); - if ( suffix != NULL ) { - char realPath[PATH_MAX]; - // no symlink in framework to suffix variants, so follow main symlink - if ( realpath(possiblePath, realPath) != NULL ) { - strcpy(possiblePath, realPath); - strcat(possiblePath, suffix); - } - } - bool found = (stat(possiblePath, &statBuffer) == 0); - if ( fTraceDylibSearching ) - printf("[Logging for XBS]%sfound framework: '%s'\n", - (found ? " " : " not "), possiblePath); - if ( found ) { - FileInfo result; - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return result; - } - } - // try without suffix - if ( suffix != NULL ) - return findFramework(rootName, NULL); - else - throwf("framework not found %s", rootName); -} - -Options::FileInfo Options::findFile(const char* path) -{ - FileInfo result; - struct stat statBuffer; - - // if absolute path and not a .o file, the use SDK prefix - if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { - const int pathLen = strlen(path); - for (std::vector::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { - // ??? Shouldn't we be using String here? - const char* sdkPathDir = *it; - const int sdkPathDirLen = strlen(sdkPathDir); - char possiblePath[sdkPathDirLen+pathLen+4]; - strcpy(possiblePath, sdkPathDir); - if ( possiblePath[sdkPathDirLen-1] == '/' ) - possiblePath[sdkPathDirLen-1] = '\0'; - strcat(possiblePath, path); - if ( stat(possiblePath, &statBuffer) == 0 ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return result; - } - } - } - // try raw path - if ( stat(path, &statBuffer) == 0 ) { - result.path = strdup(path); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return result; - } - - // try @executable_path substitution - if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { - char newPath[strlen(fExecutablePath) + strlen(path)]; - strcpy(newPath, fExecutablePath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &path[17]); - else - strcpy(newPath, &path[17]); - if ( stat(newPath, &statBuffer) == 0 ) { - result.path = strdup(newPath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return result; - } - } - - // not found - throwf("file not found: %s", path); -} - -Options::FileInfo Options::findFileUsingPaths(const char* path) -{ - FileInfo result; - - const char* lastSlash = strrchr(path, '/'); - const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1]; - - // Is this in a framework? - // /path/Foo.framework/Foo ==> true (Foo) - // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) - // /path/Foo.framework/Resources/Bar ==> false - bool isFramework = false; - if ( lastSlash != NULL ) { - char frameworkDir[strlen(leafName) + 20]; - strcpy(frameworkDir, "/"); - strcat(frameworkDir, leafName); - strcat(frameworkDir, ".framework/"); - if ( strstr(path, frameworkDir) != NULL ) - isFramework = true; - } - - // These are abbreviated versions of the routines findFramework and findLibrary above - // because we already know the final name of the file that we're looking for and so - // don't need to try variations, just paths. We do need to add the additional bits - // onto the framework path though. - if ( isFramework ) { - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, leafName); - strcat(possiblePath, ".framework"); - - //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName); - if ( checkForFile("%s/%s", possiblePath, leafName, result) ) - return result; - } - } - else { - // if this is a .dylib inside a framework, do not search -L paths - // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - int leafLen = strlen(leafName); - bool embeddedDylib = ( (leafLen > 6) - && (strcmp(&leafName[leafLen-6], ".dylib") == 0) - && (strstr(path, ".framework/") != NULL) ); - if ( !embeddedDylib ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; - //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); - if ( checkForFile("%s/%s", dir, leafName, result) ) - return result; - } - } - } - - // If we didn't find it fall back to findFile. - return findFile(path); -} - - -void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath) -{ - FILE* file = fopen(segAddrPath, "r"); - if ( file == NULL ) { - warning("-seg_addr_table file cannot be read: %s", segAddrPath); - return; - } - - char path[PATH_MAX]; - uint64_t firstColumAddress = 0; - uint64_t secondColumAddress = 0; - bool hasSecondColumn = false; - while ( fgets(path, PATH_MAX, file) != NULL ) { - path[PATH_MAX-1] = '\0'; - char* eol = strchr(path, '\n'); - if ( eol != NULL ) - *eol = '\0'; - // ignore lines not starting with 0x number - if ( (path[0] == '0') && (path[1] == 'x') ) { - char* p; - firstColumAddress = strtoull(path, &p, 16); - while ( isspace(*p) ) - ++p; - // see if second column is a number - if ( (p[0] == '0') && (p[1] == 'x') ) { - secondColumAddress = strtoull(p, &p, 16); - hasSecondColumn = true; - while ( isspace(*p) ) - ++p; - } - while ( isspace(*p) ) - ++p; - if ( p[0] == '/' ) { - // remove any trailing whitespace - for(char* end = eol-1; (end > p) && isspace(*end); --end) - *end = '\0'; - // see if this line is for the dylib being linked - if ( strcmp(p, installPath) == 0 ) { - fBaseAddress = firstColumAddress; - if ( hasSecondColumn ) { - fBaseWritableAddress = secondColumAddress; - fSplitSegs = true; - } - break; // out of while loop - } - } - } - } - - fclose(file); -} - -void Options::loadFileList(const char* fileOfPaths) -{ - FILE* file; - const char* comma = strrchr(fileOfPaths, ','); - const char* prefix = NULL; - if ( comma != NULL ) { - prefix = comma+1; - int realFileOfPathsLen = comma-fileOfPaths; - char realFileOfPaths[realFileOfPathsLen+1]; - strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); - realFileOfPaths[realFileOfPathsLen] = '\0'; - file = fopen(realFileOfPaths, "r"); - if ( file == NULL ) - throwf("-filelist file not found: %s\n", realFileOfPaths); - } - else { - file = fopen(fileOfPaths, "r"); - if ( file == NULL ) - throwf("-filelist file not found: %s\n", fileOfPaths); - } - - char path[PATH_MAX]; - while ( fgets(path, PATH_MAX, file) != NULL ) { - path[PATH_MAX-1] = '\0'; - char* eol = strchr(path, '\n'); - if ( eol != NULL ) - *eol = '\0'; - if ( prefix != NULL ) { - char builtPath[strlen(prefix)+strlen(path)+2]; - strcpy(builtPath, prefix); - strcat(builtPath, "/"); - strcat(builtPath, path); - fInputFiles.push_back(findFile(builtPath)); - } - else { - fInputFiles.push_back(findFile(path)); - } - } - fclose(file); -} - -bool Options::SetWithWildcards::hasWildCards(const char* symbol) -{ - // an exported symbol name containing *, ?, or [ requires wildcard matching - return ( strpbrk(symbol, "*?[") != NULL ); -} - -void Options::SetWithWildcards::insert(const char* symbol) -{ - if ( hasWildCards(symbol) ) - fWildCard.push_back(symbol); - else - fRegular.insert(symbol); -} - -bool Options::SetWithWildcards::contains(const char* symbol) -{ - // first look at hash table on non-wildcard symbols - if ( fRegular.find(symbol) != fRegular.end() ) - return true; - // next walk list of wild card symbols looking for a match - for(std::vector::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { - if ( wildCardMatch(*it, symbol) ) - return true; - } - return false; -} - - -bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) -{ - ++p; // find end - const char* b = p; - while ( *p != '\0' ) { - if ( *p == ']') { - const char* e = p; - // found beginining [ and ending ] - unsigned char last = '\0'; - for ( const char* s = b; s < e; ++s ) { - if ( *s == '-' ) { - unsigned char next = *(++s); - if ( (last <= c) && (c <= next) ) - return true; - ++s; - } - else { - if ( *s == c ) - return true; - last = *s; - } - } - return false; - } - ++p; - } - return false; -} - -bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) -{ - const char* s = symbol; - for (const char* p = pattern; *p != '\0'; ++p) { - switch ( *p ) { - case '*': - if ( p[1] == '\0' ) - return true; - for (const char* t = s; *t != '\0'; ++t) { - if ( wildCardMatch(&p[1], t) ) - return true; - } - return false; - case '?': - if ( *s == '\0' ) - return false; - ++s; - break; - case '[': - if ( ! inCharRange(p, *s) ) - return false; - ++s; - break; - default: - if ( *s != *p ) - return false; - ++s; - } - } - return (*s == '\0'); -} - - -void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) -{ - // read in whole file - int fd = ::open(fileOfExports, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open %s file: %s", option, fileOfExports); - struct stat stat_buf; - ::fstat(fd, &stat_buf); - char* p = (char*)malloc(stat_buf.st_size); - if ( p == NULL ) - throwf("can't process %s file: %s", option, fileOfExports); - - if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) - throwf("can't read %s file: %s", option, fileOfExports); - - ::close(fd); - - // parse into symbols and add to hash_set - char * const end = &p[stat_buf.st_size]; - enum { lineStart, inSymbol, inComment } state = lineStart; - char* symbolStart = NULL; - for (char* s = p; s < end; ++s ) { - switch ( state ) { - case lineStart: - if ( *s =='#' ) { - state = inComment; - } - else if ( !isspace(*s) ) { - state = inSymbol; - symbolStart = s; - } - break; - case inSymbol: - if ( (*s == '\n') || (*s == '\r') ) { - *s = '\0'; - // removing any trailing spaces - char* last = s-1; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - set.insert(symbolStart); - symbolStart = NULL; - state = lineStart; - } - break; - case inComment: - if ( (*s == '\n') || (*s == '\r') ) - state = lineStart; - break; - } - } - if ( state == inSymbol ) { - warning("missing line-end at end of file \"%s\"", fileOfExports); - int len = end-symbolStart+1; - char* temp = new char[len]; - strlcpy(temp, symbolStart, len); - - // remove any trailing spaces - char* last = &temp[len-2]; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - set.insert(temp); - } - - // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table -} - -void Options::parseAliasFile(const char* fileOfAliases) -{ - // read in whole file - int fd = ::open(fileOfAliases, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open alias file: %s", fileOfAliases); - struct stat stat_buf; - ::fstat(fd, &stat_buf); - char* p = (char*)malloc(stat_buf.st_size+1); - if ( p == NULL ) - throwf("can't process alias file: %s", fileOfAliases); - - if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) - throwf("can't read alias file: %s", fileOfAliases); - p[stat_buf.st_size] = '\n'; - ::close(fd); - - // parse into symbols and add to fAliases - ObjectFile::ReaderOptions::AliasPair pair; - char * const end = &p[stat_buf.st_size+1]; - enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; - int lineNumber = 1; - for (char* s = p; s < end; ++s ) { - switch ( state ) { - case lineStart: - if ( *s =='#' ) { - state = inComment; - } - else if ( !isspace(*s) ) { - state = inRealName; - pair.realName = s; - } - break; - case inRealName: - if ( *s == '\n' ) { - warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); - ++lineNumber; - state = lineStart; - } - else if ( isspace(*s) ) { - *s = '\0'; - state = inBetween; - } - break; - case inBetween: - if ( *s == '\n' ) { - warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); - ++lineNumber; - state = lineStart; - } - else if ( ! isspace(*s) ) { - state = inAliasName; - pair.alias = s; - } - break; - case inAliasName: - if ( *s =='#' ) { - *s = '\0'; - // removing any trailing spaces - char* last = s-1; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - fReaderOptions.fAliases.push_back(pair); - state = inComment; - } - else if ( *s == '\n' ) { - *s = '\0'; - // removing any trailing spaces - char* last = s-1; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - fReaderOptions.fAliases.push_back(pair); - state = lineStart; - } - break; - case inComment: - if ( *s == '\n' ) - state = lineStart; - break; - } - } - - // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases -} - - - -void Options::setUndefinedTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; - - if ( strcmp(treatment, "warning") == 0 ) - fUndefinedTreatment = kUndefinedWarning; - else if ( strcmp(treatment, "error") == 0 ) - fUndefinedTreatment = kUndefinedError; - else if ( strcmp(treatment, "suppress") == 0 ) - fUndefinedTreatment = kUndefinedSuppress; - else if ( strcmp(treatment, "dynamic_lookup") == 0 ) - fUndefinedTreatment = kUndefinedDynamicLookup; - else - throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; -} - -Options::Treatment Options::parseTreatment(const char* treatment) -{ - if ( treatment == NULL ) - return kNULL; - - if ( strcmp(treatment, "warning") == 0 ) - return kWarning; - else if ( strcmp(treatment, "error") == 0 ) - return kError; - else if ( strcmp(treatment, "suppress") == 0 ) - return kSuppress; - else - return kInvalid; -} - -void Options::setMacOSXVersionMin(const char* version) -{ - if ( version == NULL ) - throw "-macosx_version_min argument missing"; - - if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - int num = version[3] - '0'; - switch ( num ) { - case 0: - case 1: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; - break; - default: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; - break; - } - } - else { - warning("unknown option to -macosx_version_min, not 10.x"); - } -} - -void Options::setIPhoneVersionMin(const char* version) -{ - if ( version == NULL ) - throw "-iphoneos_version_min argument missing"; - - if ( ((strncmp(version, "1.", 2) == 0) || (strncmp(version, "2.", 2) == 0)) && isdigit(version[2]) ) { - int num = version[2] - '0'; - switch ( num ) { - case 2: - // TODO: store deployment version - break; - default: - break; - } - } - else { - warning("unknown option to -iphoneos_version_min, not 1.x or 2.x"); - } -} - -void Options::setWeakReferenceMismatchTreatment(const char* treatment) -{ - if ( treatment == NULL ) - throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; - - if ( strcmp(treatment, "error") == 0 ) - fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; - else if ( strcmp(treatment, "weak") == 0 ) - fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; - else if ( strcmp(treatment, "non-weak") == 0 ) - fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; - else - throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; -} - -Options::CommonsMode Options::parseCommonsTreatment(const char* mode) -{ - if ( mode == NULL ) - throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; - - if ( strcmp(mode, "ignore_dylibs") == 0 ) - return kCommonsIgnoreDylibs; - else if ( strcmp(mode, "use_dylibs") == 0 ) - return kCommonsOverriddenByDylibs; - else if ( strcmp(mode, "error") == 0 ) - return kCommonsConflictsDylibsError; - else - throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; -} - -void Options::addDylibOverride(const char* paths) -{ - if ( paths == NULL ) - throw "-dylib_file must followed by two colon separated paths"; - const char* colon = strchr(paths, ':'); - if ( colon == NULL ) - throw "-dylib_file must followed by two colon separated paths"; - int len = colon-paths; - char* target = new char[len+2]; - strncpy(target, paths, len); - target[len] = '\0'; - DylibOverride entry; - entry.installName = target; - entry.useInstead = &colon[1]; - fDylibOverrides.push_back(entry); -} - -uint64_t Options::parseAddress(const char* addr) -{ - char* endptr; - uint64_t result = strtoull(addr, &endptr, 16); - return result; -} - -uint32_t Options::parseProtection(const char* prot) -{ - uint32_t result = 0; - for(const char* p = prot; *p != '\0'; ++p) { - switch(tolower(*p)) { - case 'r': - result |= VM_PROT_READ; - break; - case 'w': - result |= VM_PROT_WRITE; - break; - case 'x': - result |= VM_PROT_EXECUTE; - break; - case '-': - break; - default: - throwf("unknown -segprot lettter in %s", prot); - } - } - return result; -} - - - -// -// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz -// -// -uint32_t Options::parseVersionNumber(const char* versionString) -{ - unsigned long x = 0; - unsigned long y = 0; - unsigned long z = 0; - char* end; - x = strtoul(versionString, &end, 10); - if ( *end == '.' ) { - y = strtoul(&end[1], &end, 10); - if ( *end == '.' ) { - z = strtoul(&end[1], &end, 10); - } - } - if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed version number: %s", versionString); - - return (x << 16) | ( y << 8 ) | z; -} - -static const char* cstringSymbolName(const char* orderFileString) -{ - char* result; - asprintf(&result, "cstring=%s", orderFileString); - // convert escaped characters - char* d = result; - for(const char* s=result; *s != '\0'; ++s, ++d) { - if ( *s == '\\' ) { - ++s; - switch ( *s ) { - case 'n': - *d = '\n'; - break; - case 't': - *d = '\t'; - break; - case 'v': - *d = '\v'; - break; - case 'b': - *d = '\b'; - break; - case 'r': - *d = '\r'; - break; - case 'f': - *d = '\f'; - break; - case 'a': - *d = '\a'; - break; - case '\\': - *d = '\\'; - break; - case '?': - *d = '\?'; - break; - case '\'': - *d = '\r'; - break; - case '\"': - *d = '\"'; - break; - case 'x': - // hexadecimal value of char - { - ++s; - char value = 0; - while ( isxdigit(*s) ) { - value *= 16; - if ( isdigit(*s) ) - value += (*s-'0'); - else - value += ((toupper(*s)-'A') + 10); - ++s; - } - *d = value; - } - break; - default: - if ( isdigit(*s) ) { - // octal value of char - char value = 0; - while ( isdigit(*s) ) { - value = (value << 3) + (*s-'0'); - ++s; - } - *d = value; - } - } - } - else { - *d = *s; - } - } - *d = '\0'; - return result; -} - -void Options::parseOrderFile(const char* path, bool cstring) -{ - // read in whole file - int fd = ::open(path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open order file: %s", path); - struct stat stat_buf; - ::fstat(fd, &stat_buf); - char* p = (char*)malloc(stat_buf.st_size+1); - if ( p == NULL ) - throwf("can't process order file: %s", path); - if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) - throwf("can't read order file: %s", path); - ::close(fd); - p[stat_buf.st_size] = '\n'; - - // parse into vector of pairs - char * const end = &p[stat_buf.st_size+1]; - enum { lineStart, inSymbol, inComment } state = lineStart; - char* symbolStart = NULL; - for (char* s = p; s < end; ++s ) { - switch ( state ) { - case lineStart: - if ( *s =='#' ) { - state = inComment; - } - else if ( !isspace(*s) || cstring ) { - state = inSymbol; - symbolStart = s; - } - break; - case inSymbol: - if ( (*s == '\n') || (!cstring && (*s == '#')) ) { - bool wasComment = (*s == '#'); - *s = '\0'; - // removing any trailing spaces - char* last = s-1; - while ( isspace(*last) ) { - *last = '\0'; - --last; - } - if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { - if ( fArchitecture == CPU_TYPE_POWERPC ) - symbolStart = &symbolStart[4]; - else - symbolStart = NULL; - } - // if there is an architecture prefix, only use this symbol it if matches current arch - else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { - if ( fArchitecture == CPU_TYPE_POWERPC64 ) - symbolStart = &symbolStart[6]; - else - symbolStart = NULL; - } - else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { - if ( fArchitecture == CPU_TYPE_I386 ) - symbolStart = &symbolStart[5]; - else - symbolStart = NULL; - } - else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) { - if ( fArchitecture == CPU_TYPE_X86_64 ) - symbolStart = &symbolStart[7]; - else - symbolStart = NULL; - } - else if ( strncmp(symbolStart, "arm:", 4) == 0 ) { - if ( fArchitecture == CPU_TYPE_ARM ) - symbolStart = &symbolStart[4]; - else - symbolStart = NULL; - } - if ( symbolStart != NULL ) { - char* objFileName = NULL; - char* colon = strstr(symbolStart, ".o:"); - if ( colon != NULL ) { - colon[2] = '\0'; - objFileName = symbolStart; - symbolStart = &colon[3]; - } - // trim leading spaces - while ( isspace(*symbolStart) ) - ++symbolStart; - Options::OrderedSymbol pair; - if ( cstring ) - pair.symbolName = cstringSymbolName(symbolStart); - else - pair.symbolName = symbolStart; - pair.objectFileName = objFileName; - fOrderedSymbols.push_back(pair); - } - symbolStart = NULL; - if ( wasComment ) - state = inComment; - else - state = lineStart; - } - break; - case inComment: - if ( *s == '\n' ) - state = lineStart; - break; - } - } - // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols -} - -void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) -{ - if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) { - parseOrderFile(path, true); - } - else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) { - warning("sorting of __literal[4,8,16] sections not supported"); - } - else { - // ignore section information and append all symbol names to global order file - parseOrderFile(path, false); - } -} - -void Options::addSection(const char* segment, const char* section, const char* path) -{ - if ( strlen(segment) > 16 ) - throw "-seccreate segment name max 16 chars"; - if ( strlen(section) > 16 ) { - char* tmp = strdup(section); - tmp[16] = '\0'; - warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp); - section = tmp; - } - - // read in whole file - int fd = ::open(path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open -sectcreate file: %s", path); - struct stat stat_buf; - ::fstat(fd, &stat_buf); - char* p = (char*)malloc(stat_buf.st_size); - if ( p == NULL ) - throwf("can't process -sectcreate file: %s", path); - if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) - throwf("can't read -sectcreate file: %s", path); - ::close(fd); - - // record section to create - ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; - fExtraSections.push_back(info); -} - -void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) -{ - if ( strlen(segment) > 16 ) - throw "-sectalign segment name max 16 chars"; - if ( strlen(section) > 16 ) - throw "-sectalign section name max 16 chars"; - - // argument to -sectalign is a hexadecimal number - char* endptr; - unsigned long value = strtoul(alignmentStr, &endptr, 16); - if ( *endptr != '\0') - throw "argument for -sectalign is not a hexadecimal number"; - if ( value > 0x8000 ) - throw "argument for -sectalign must be less than or equal to 0x8000"; - if ( value == 0 ) { - warning("zero is not a valid -sectalign"); - value = 1; - } - - // alignment is power of 2 (e.g. page alignment = 12) - uint8_t alignment = (uint8_t)__builtin_ctz(value); - if ( (unsigned long)(1 << alignment) != value ) { - warning("alignment for -sectalign %s %s is not a power of two, using 0x%X", - segment, section, 1 << alignment); - } - - SectionAlignment info = { segment, section, alignment }; - fSectionAlignments.push_back(info); -} - -void Options::addLibrary(const FileInfo& info) -{ - // if this library has already been added, don't add again (archives are automatically repeatedly searched) - for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { - if ( strcmp(info.path, fit->path) == 0 ) { - // if dylib is specified again but weak, record that it should be weak - if ( info.options.fWeakImport ) - fit->options.fWeakImport = true; - return; - } - } - // add to list - fInputFiles.push_back(info); -} - -void Options::warnObsolete(const char* arg) -{ - warning("option %s is obsolete and being ignored", arg); -} - - - - -// -// Process all command line arguments. -// -// The only error checking done here is that each option is valid and if it has arguments -// that they too are valid. -// -// The general rule is "last option wins", i.e. if both -bundle and -dylib are specified, -// whichever was last on the command line is used. -// -// Error check for invalid combinations of options is done in checkIllegalOptionCombinations() -// -void Options::parse(int argc, const char* argv[]) -{ - // pass one builds search list from -L and -F options - this->buildSearchPaths(argc, argv); - - // reduce re-allocations - fInputFiles.reserve(32); - - // pass two parse all other options - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - - if ( arg[0] == '-' ) { - - // Since we don't care about the files passed, just the option names, we do this here. - if (fPrintOptions) - fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); - - if ( (arg[1] == 'L') || (arg[1] == 'F') ) { - // previously handled by buildSearchPaths() - } - // The one gnu style option we have to keep compatibility - // with gcc. Might as well have the single hyphen one as well. - else if ( (strcmp(arg, "--help") == 0) - || (strcmp(arg, "-help") == 0)) { - fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n"); - exit (0); - } - else if ( strcmp(arg, "-arch") == 0 ) { - parseArch(argv[++i]); - } - else if ( strcmp(arg, "-dynamic") == 0 ) { - // default - } - else if ( strcmp(arg, "-static") == 0 ) { - if ( fOutputKind != kObjectFile ) - fOutputKind = kStaticExecutable; - fReaderOptions.fForStatic = true; - } - else if ( strcmp(arg, "-dylib") == 0 ) { - fOutputKind = kDynamicLibrary; - } - else if ( strcmp(arg, "-bundle") == 0 ) { - fOutputKind = kDynamicBundle; - } - else if ( strcmp(arg, "-dylinker") == 0 ) { - fOutputKind = kDyld; - } - else if ( strcmp(arg, "-execute") == 0 ) { - if ( fOutputKind != kStaticExecutable ) - fOutputKind = kDynamicExecutable; - } - else if ( strcmp(arg, "-r") == 0 ) { - fOutputKind = kObjectFile; - } - else if ( strcmp(arg, "-o") == 0 ) { - fOutputFile = argv[++i]; - } - else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { - addLibrary(findLibrary(&arg[2])); - } - // This causes a dylib to be weakly bound at - // link time. This corresponds to weak_import. - else if ( strncmp(arg, "-weak-l", 7) == 0 ) { - FileInfo info = findLibrary(&arg[7]); - info.options.fWeakImport = true; - addLibrary(info); - } - else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { - FileInfo info = findLibrary(&arg[7], true); - info.options.fLazyLoad = true; - addLibrary(info); - fUsingLazyDylibLinking = true; - } - // Avoid lazy binding. - // ??? Deprecate. - else if ( strcmp(arg, "-bind_at_load") == 0 ) { - fBindAtLoad = true; - } - else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { - fNameSpace = kTwoLevelNameSpace; - } - else if ( strcmp(arg, "-flat_namespace") == 0 ) { - fNameSpace = kFlatNameSpace; - } - // Also sets a bit to ensure dyld causes everything - // in the namespace to be flat. - // ??? Deprecate - else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { - fNameSpace = kForceFlatNameSpace; - } - // Similar to --whole-archive. - else if ( strcmp(arg, "-all_load") == 0 ) { - fReaderOptions.fFullyLoadArchives = true; - } - else if ( strcmp(arg, "-noall_load") == 0) { - warnObsolete(arg); - } - // Similar to -all_load - else if ( strcmp(arg, "-ObjC") == 0 ) { - fReaderOptions.fLoadAllObjcObjectsFromArchives = true; - } - // Library versioning. - else if ( (strcmp(arg, "-dylib_compatibility_version") == 0) - || (strcmp(arg, "-compatibility_version") == 0)) { - const char* vers = argv[++i]; - if ( vers == NULL ) - throw "-dylib_compatibility_version missing "; - fDylibCompatVersion = parseVersionNumber(vers); - } - else if ( (strcmp(arg, "-dylib_current_version") == 0) - || (strcmp(arg, "-current_version") == 0)) { - const char* vers = argv[++i]; - if ( vers == NULL ) - throw "-dylib_current_version missing "; - fDylibCurrentVersion = parseVersionNumber(vers); - } - else if ( strcmp(arg, "-sectorder") == 0 ) { - if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) - throw "-sectorder missing

"; - parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); - i += 3; - } - else if ( strcmp(arg, "-order_file") == 0 ) { - parseOrderFile(argv[++i], false); - } - else if ( strcmp(arg, "-order_file_statistics") == 0 ) { - fPrintOrderFileStatistics = true; - } - // ??? Deprecate segcreate. - // -sectcreate puts whole files into a section in the output. - else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { - if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) - throw "-sectcreate missing
"; - addSection(argv[i+1], argv[i+2], argv[i+3]); - i += 3; - } - // Since we have a full path in binary/library names we need to be able to override it. - else if ( (strcmp(arg, "-dylib_install_name") == 0) - || (strcmp(arg, "-dylinker_install_name") == 0) - || (strcmp(arg, "-install_name") == 0)) { - fDylibInstallName = argv[++i]; - if ( fDylibInstallName == NULL ) - throw "-install_name missing "; - } - // Sets the base address of the output. - else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) { - const char* address = argv[++i]; - if ( address == NULL ) - throwf("%s missing
", arg); - fBaseAddress = parseAddress(address); - uint64_t temp = (fBaseAddress+4095) & (-4096); // page align - if ( fBaseAddress != temp ) { - warning("-seg1addr not page aligned, rounding up"); - fBaseAddress = temp; - } - } - else if ( strcmp(arg, "-e") == 0 ) { - fEntryName = argv[++i]; - } - // Same as -@ from the FSF linker. - else if ( strcmp(arg, "-filelist") == 0 ) { - const char* path = argv[++i]; - if ( (path == NULL) || (path[0] == '-') ) - throw "-filelist missing "; - loadFileList(path); - } - else if ( strcmp(arg, "-keep_private_externs") == 0 ) { - fKeepPrivateExterns = true; - } - else if ( strcmp(arg, "-final_output") == 0 ) { - fFinalName = argv[++i]; - } - // Ensure that all calls to exported symbols go through lazy pointers. Multi-module - // just ensures that this happens for cross object file boundaries. - else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { - switch ( fInterposeMode ) { - case kInterposeNone: - case kInterposeAllExternal: - fInterposeMode = kInterposeAllExternal; - break; - case kInterposeSome: - // do nothing, -interposable_list overrides -interposable" - break; - } - } - else if ( strcmp(arg, "-interposable_list") == 0 ) { - fInterposeMode = kInterposeSome; - loadExportFile(argv[++i], "-interposable_list", fInterposeList); - } - // Default for -interposable/-multi_module/-single_module. - else if ( strcmp(arg, "-single_module") == 0 ) { - fInterposeMode = kInterposeNone; - } - else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { - if ( fExportMode == kDontExportSome ) - throw "can't use -exported_symbols_list and -unexported_symbols_list"; - fExportMode = kExportSome; - loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); - } - else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { - if ( fExportMode == kExportSome ) - throw "can't use -unexported_symbols_list and -exported_symbols_list"; - fExportMode = kDontExportSome; - loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); - } - else if ( strcmp(arg, "-exported_symbol") == 0 ) { - if ( fExportMode == kDontExportSome ) - throw "can't use -exported_symbol and -unexported_symbols"; - fExportMode = kExportSome; - fExportSymbols.insert(argv[++i]); - } - else if ( strcmp(arg, "-unexported_symbol") == 0 ) { - if ( fExportMode == kExportSome ) - throw "can't use -unexported_symbol and -exported_symbol"; - fExportMode = kDontExportSome; - fDontExportSymbols.insert(argv[++i]); - } - else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { - if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude ) - throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; - fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; - loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); - } - else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { - if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude ) - throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; - fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; - loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); - } - // ??? Deprecate - else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { - fIgnoreOtherArchFiles = true; - } - else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { - fForceSubtypeAll = true; - } - // Similar to -weak-l but uses the absolute path name to the library. - else if ( strcmp(arg, "-weak_library") == 0 ) { - FileInfo info = findFile(argv[++i]); - info.options.fWeakImport = true; - addLibrary(info); - } - else if ( strcmp(arg, "-lazy_library") == 0 ) { - FileInfo info = findFile(argv[++i]); - info.options.fLazyLoad = true; - addLibrary(info); - fUsingLazyDylibLinking = true; - } - else if ( strcmp(arg, "-framework") == 0 ) { - addLibrary(findFramework(argv[++i])); - } - else if ( strcmp(arg, "-weak_framework") == 0 ) { - FileInfo info = findFramework(argv[++i]); - info.options.fWeakImport = true; - addLibrary(info); - } - else if ( strcmp(arg, "-lazy_framework") == 0 ) { - FileInfo info = findFramework(argv[++i]); - info.options.fLazyLoad = true; - addLibrary(info); - fUsingLazyDylibLinking = true; - } - else if ( strcmp(arg, "-search_paths_first") == 0 ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-undefined") == 0 ) { - setUndefinedTreatment(argv[++i]); - } - // Debugging output flag. - else if ( strcmp(arg, "-arch_multiple") == 0 ) { - fMessagesPrefixedWithArchitecture = true; - } - // Specify what to do with relocations in read only - // sections like .text. Could be errors, warnings, - // or suppressed. Currently we do nothing with the - // flag. - else if ( strcmp(arg, "-read_only_relocs") == 0 ) { - switch ( parseTreatment(argv[++i]) ) { - case kNULL: - case kInvalid: - throw "-read_only_relocs missing [ warning | error | suppress ]"; - case kWarning: - fWarnTextRelocs = true; - fAllowTextRelocs = true; - break; - case kSuppress: - fWarnTextRelocs = false; - fAllowTextRelocs = true; - break; - case kError: - fWarnTextRelocs = false; - fAllowTextRelocs = false; - break; - } - } - else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { - warnObsolete(arg); - ++i; - } - // Warn, error or make strong a mismatch between weak - // and non-weak references. - else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { - setWeakReferenceMismatchTreatment(argv[++i]); - } - // For a deployment target of 10.3 and earlier ld64 will - // prebind an executable with 0s in all addresses that - // are prebound. This can then be fixed up by update_prebinding - // later. Prebinding is less useful on 10.4 and greater. - else if ( strcmp(arg, "-prebind") == 0 ) { - fPrebind = true; - } - else if ( strcmp(arg, "-noprebind") == 0 ) { - warnObsolete(arg); - fPrebind = false; - } - else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-nofixprebinding") == 0 ) { - warnObsolete(arg); - } - // This should probably be deprecated when we respect -L and -F - // when searching for libraries. - else if ( strcmp(arg, "-dylib_file") == 0 ) { - addDylibOverride(argv[++i]); - } - // What to expand @executable_path to if found in dependent dylibs - else if ( strcmp(arg, "-executable_path") == 0 ) { - fExecutablePath = argv[++i]; - if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') ) - throw "-executable_path missing "; - // if a directory was passed, add / to end - // ld64 can't find @executable _path relative dylibs from our umbrella frameworks - struct stat statBuffer; - if ( stat(fExecutablePath, &statBuffer) == 0 ) { - if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) { - char* pathWithSlash = new char[strlen(fExecutablePath)+2]; - strcpy(pathWithSlash, fExecutablePath); - strcat(pathWithSlash, "/"); - fExecutablePath = pathWithSlash; - } - } - } - // Aligns all segments to the power of 2 boundary specified. - else if ( strcmp(arg, "-segalign") == 0 ) { - warnObsolete(arg); - ++i; - } - // Puts a specified segment at a particular address that must - // be a multiple of the segment alignment. - else if ( strcmp(arg, "-segaddr") == 0 ) { - SegmentStart seg; - seg.name = argv[++i]; - if ( (seg.name == NULL) || (argv[i+1] == NULL) ) - throw "-segaddr missing segName Adddress"; - seg.address = parseAddress(argv[++i]); - uint64_t temp = seg.address & (-4096); // page align - if ( (seg.address != temp) ) - warning("-segaddr %s not page aligned, rounding down", seg.name); - fCustomSegmentAddresses.push_back(seg); - } - // ??? Deprecate when we deprecate split-seg. - else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { - fBaseAddress = parseAddress(argv[++i]); - } - // ??? Deprecate when we deprecate split-seg. - else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { - fBaseWritableAddress = parseAddress(argv[++i]); - fSplitSegs = true; - } - // ??? Deprecate when we get rid of basing at build time. - else if ( strcmp(arg, "-seg_addr_table") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-seg_addr_table missing argument"; - fSegAddrTablePath = name; - } - else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { - warnObsolete(arg); - ++i; - } - else if ( strcmp(arg, "-segprot") == 0 ) { - SegmentProtect seg; - seg.name = argv[++i]; - if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) ) - throw "-segprot missing segName max-prot init-prot"; - seg.max = parseProtection(argv[++i]); - seg.init = parseProtection(argv[++i]); - fCustomSegmentProtections.push_back(seg); - } - else if ( strcmp(arg, "-pagezero_size") == 0 ) { - const char* size = argv[++i]; - if ( size == NULL ) - throw "-pagezero_size missing "; - fZeroPageSize = parseAddress(size); - uint64_t temp = fZeroPageSize & (-4096); // page align - if ( (fZeroPageSize != temp) ) - warning("-pagezero_size not page aligned, rounding down"); - fZeroPageSize = temp; - } - else if ( strcmp(arg, "-stack_addr") == 0 ) { - const char* address = argv[++i]; - if ( address == NULL ) - throw "-stack_addr missing
"; - fStackAddr = parseAddress(address); - } - else if ( strcmp(arg, "-stack_size") == 0 ) { - const char* size = argv[++i]; - if ( size == NULL ) - throw "-stack_size missing
"; - fStackSize = parseAddress(size); - uint64_t temp = fStackSize & (-4096); // page align - if ( (fStackSize != temp) ) - warning("-stack_size not page aligned, rounding down"); - } - else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { - fExecutableStack = true; - } - else if ( strcmp(arg, "-sectalign") == 0 ) { - if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) - throw "-sectalign missing
"; - addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); - i += 3; - } - else if ( strcmp(arg, "-sectorder_detail") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { - warnObsolete(arg); - i += 2; - } - else if ( strcmp(arg, "-bundle_loader") == 0 ) { - fBundleLoader = argv[++i]; - if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) - throw "-bundle_loader missing "; - FileInfo info = findFile(fBundleLoader); - info.options.fBundleLoader = true; - fInputFiles.push_back(info); - } - else if ( strcmp(arg, "-private_bundle") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { - // FIX FIX - } - // Use this flag to set default behavior for deployement targets. - else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - setMacOSXVersionMin(argv[++i]); - } - else if ( (strcmp(arg, "-aspen_version_min") == 0) || (strcmp(arg, "-iphone_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { - setIPhoneVersionMin(argv[++i]); - } - else if ( strcmp(arg, "-multiply_defined") == 0 ) { - //warnObsolete(arg); - ++i; - } - else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { - warnObsolete(arg); - ++i; - } - else if ( strcmp(arg, "-nomultidefs") == 0 ) { - warnObsolete(arg); - } - // Display each file in which the argument symbol appears and whether - // the file defines or references it. This option takes an argument - // as -y note that there is no space. - else if ( strncmp(arg, "-y", 2) == 0 ) { - warnObsolete("-y"); - } - // Same output as -y, but output number of undefined symbols only. - else if ( strcmp(arg, "-Y") == 0 ) { - //warnObsolete(arg); - ++i; - } - // This option affects all objects linked into the final result. - else if ( strcmp(arg, "-m") == 0 ) { - warnObsolete(arg); - } - else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { - fReaderOptions.fWhyLoad = true; - } - else if ( strcmp(arg, "-why_live") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-why_live missing symbol name argument"; - fWhyLive.insert(name); - } - else if ( strcmp(arg, "-u") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-u missing argument"; - fInitialUndefines.push_back(name); - } - else if ( strcmp(arg, "-U") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-U missing argument"; - fAllowedUndefined.insert(name); - } - else if ( strcmp(arg, "-s") == 0 ) { - warnObsolete(arg); - fLocalSymbolHandling = kLocalSymbolsNone; - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; - } - else if ( strcmp(arg, "-x") == 0 ) { - fLocalSymbolHandling = kLocalSymbolsNone; - } - else if ( strcmp(arg, "-S") == 0 ) { - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; - } - else if ( strcmp(arg, "-X") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-Si") == 0 ) { - warnObsolete(arg); - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; - } - else if ( strcmp(arg, "-b") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-Sn") == 0 ) { - warnObsolete(arg); - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; - } - else if ( strcmp(arg, "-Sp") == 0 ) { - warnObsolete(arg); - } - else if ( strcmp(arg, "-dead_strip") == 0 ) { - fDeadStrip = kDeadStripOnPlusUnusedInits; - } - else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { - fDeadStrip = kDeadStripOn; - } - else if ( strcmp(arg, "-w") == 0 ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-M") == 0 ) { - // FIX FIX - } - else if ( strcmp(arg, "-headerpad") == 0 ) { - const char* size = argv[++i]; - if ( size == NULL ) - throw "-headerpad missing argument"; - fMinimumHeaderPad = parseAddress(size); - } - else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { - fMaxMinimumHeaderPad = true; - } - else if ( strcmp(arg, "-t") == 0 ) { - fReaderOptions.fLogAllFiles = true; - } - else if ( strcmp(arg, "-whatsloaded") == 0 ) { - fReaderOptions.fLogObjectFiles = true; - } - else if ( strcmp(arg, "-A") == 0 ) { - warnObsolete(arg); - ++i; - } - else if ( strcmp(arg, "-umbrella") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-umbrella missing argument"; - fUmbrellaName = name; - } - else if ( strcmp(arg, "-allowable_client") == 0 ) { - const char* name = argv[++i]; - - if ( name == NULL ) - throw "-allowable_client missing argument"; - - fAllowableClients.push_back(name); - } - else if ( strcmp(arg, "-client_name") == 0 ) { - const char* name = argv[++i]; - - if ( name == NULL ) - throw "-client_name missing argument"; - - fClientName = name; - } - else if ( strcmp(arg, "-sub_umbrella") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-sub_umbrella missing argument"; - fSubUmbellas.push_back(name); - } - else if ( strcmp(arg, "-sub_library") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-sub_library missing argument"; - fSubLibraries.push_back(name); - } - else if ( strcmp(arg, "-init") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-init missing argument"; - fInitFunctionName = name; - } - else if ( strcmp(arg, "-dot") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-dot missing argument"; - fDotOutputFile = name; - } - else if ( strcmp(arg, "-warn_commons") == 0 ) { - fWarnCommons = true; - } - else if ( strcmp(arg, "-commons") == 0 ) { - fCommonsMode = parseCommonsTreatment(argv[++i]); - } - else if ( strcmp(arg, "-keep_relocs") == 0 ) { - fKeepRelocations = true; - } - else if ( strcmp(arg, "-warn_stabs") == 0 ) { - fWarnStabs = true; - } - else if ( strcmp(arg, "-pause") == 0 ) { - fPause = true; - } - else if ( strcmp(arg, "-print_statistics") == 0 ) { - fStatistics = true; - } - else if ( strcmp(arg, "-d") == 0 ) { - fReaderOptions.fMakeTentativeDefinitionsReal = true; - } - else if ( strcmp(arg, "-v") == 0 ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-Z") == 0 ) { - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-syslibroot") == 0 ) { - ++i; - // previously handled by buildSearchPaths() - } - else if ( strcmp(arg, "-no_uuid") == 0 ) { - fUUIDMode = kUUIDNone; - } - else if ( strcmp(arg, "-random_uuid") == 0 ) { - fUUIDMode = kUUIDRandom; - } - else if ( strcmp(arg, "-dtrace") == 0 ) { - const char* name = argv[++i]; - if ( name == NULL ) - throw "-dtrace missing argument"; - fDtraceScriptName = name; - } - else if ( strcmp(arg, "-root_safe") == 0 ) { - fReaderOptions.fRootSafe = true; - } - else if ( strcmp(arg, "-setuid_safe") == 0 ) { - fReaderOptions.fSetuidSafe = true; - } - else if ( strcmp(arg, "-alias") == 0 ) { - ObjectFile::ReaderOptions::AliasPair pair; - pair.realName = argv[++i]; - if ( pair.realName == NULL ) - throw "missing argument to -alias"; - pair.alias = argv[++i]; - if ( pair.alias == NULL ) - throw "missing argument to -alias"; - fReaderOptions.fAliases.push_back(pair); - } - else if ( strcmp(arg, "-alias_list") == 0 ) { - parseAliasFile(argv[++i]); - } - // put this last so that it does not interfer with other options starting with 'i' - else if ( strncmp(arg, "-i", 2) == 0 ) { - const char* colon = strchr(arg, ':'); - if ( colon == NULL ) - throwf("unknown option: %s", arg); - ObjectFile::ReaderOptions::AliasPair pair; - char* temp = new char[colon-arg]; - strlcpy(temp, &arg[2], colon-arg-1); - pair.realName = &colon[1]; - pair.alias = temp; - fReaderOptions.fAliases.push_back(pair); - } - else if ( strcmp(arg, "-save-temps") == 0 ) { - fSaveTempFiles = true; - } - else if ( strcmp(arg, "-rpath") == 0 ) { - const char* path = argv[++i]; - if ( path == NULL ) - throw "missing argument to -rpath"; - fRPaths.push_back(path); - } - else if ( strcmp(arg, "-read_only_stubs") == 0 ) { - fReadOnlyx86Stubs = true; - } - else if ( strcmp(arg, "-slow_stubs") == 0 ) { - fReaderOptions.fSlowx86Stubs = true; - } - else if ( strcmp(arg, "-map") == 0 ) { - fMapPath = argv[++i]; - if ( fMapPath == NULL ) - throw "missing argument to -map"; - } - else if ( strcmp(arg, "-pie") == 0 ) { - fPositionIndependentExecutable = true; - } - else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { - FileInfo info = findLibrary(&arg[11], true); - info.options.fReExport = true; - addLibrary(info); - } - else if ( strcmp(arg, "-reexport_library") == 0 ) { - FileInfo info = findFile(argv[++i]); - info.options.fReExport = true; - addLibrary(info); - } - else if ( strcmp(arg, "-reexport_framework") == 0 ) { - FileInfo info = findFramework(argv[++i]); - info.options.fReExport = true; - addLibrary(info); - } - else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { - fDeadStripDylibs = true; - } - else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { - fReaderOptions.fImplicitlyLinkPublicDylibs = false; - } - else if ( strcmp(arg, "-new_linker") == 0 ) { - // ignore - } - else if ( strcmp(arg, "-no_encryption") == 0 ) { - fEncryptable = false; - } - else if ( strcmp(arg, "-mllvm") == 0 ) { - const char* opts = argv[++i]; - if ( opts == NULL ) - throw "missing argument to -mllvm"; - fLLVMOptions.push_back(opts); - } - else { - throwf("unknown option: %s", arg); - } - } - else { - FileInfo info = findFile(arg); - if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 ) - addLibrary(info); - else - fInputFiles.push_back(info); - } - } - - // if a -lazy option was used, implicitly link in lazydylib1.o - if ( fUsingLazyDylibLinking ) { - addLibrary(findLibrary("lazydylib1.o")); - } -} - - - -// -// -syslibroot is used for SDK support. -// The rule is that all search paths (both explicit and default) are -// checked to see if they exist in the SDK. If so, that path is -// replaced with the sdk prefixed path. If not, that search path -// is used as is. If multiple -syslibroot options are specified -// their directory structures are logically overlayed and files -// from sdks specified earlier on the command line used before later ones. - -void Options::buildSearchPaths(int argc, const char* argv[]) -{ - bool addStandardLibraryDirectories = true; - std::vector libraryPaths; - std::vector frameworkPaths; - libraryPaths.reserve(10); - frameworkPaths.reserve(10); - // scan through argv looking for -L, -F, -Z, and -syslibroot options - for(int i=0; i < argc; ++i) { - if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) - libraryPaths.push_back(&argv[i][2]); - else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) - frameworkPaths.push_back(&argv[i][2]); - else if ( strcmp(argv[i], "-Z") == 0 ) - addStandardLibraryDirectories = false; - else if ( strcmp(argv[i], "-v") == 0 ) { - fVerbose = true; - extern const char ldVersionString[]; - fprintf(stderr, "%s", ldVersionString); - // if only -v specified, exit cleanly - if ( argc == 2 ) { -#if LTO_SUPPORT - printLTOVersion(*this); -#endif - exit(0); - } - } - else if ( strcmp(argv[i], "-syslibroot") == 0 ) { - const char* path = argv[++i]; - if ( path == NULL ) - throw "-syslibroot missing argument"; - fSDKPaths.push_back(path); - } - else if ( strcmp(argv[i], "-search_paths_first") == 0 ) { - // ??? Deprecate when we get -Bstatic/-Bdynamic. - fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; - } - else if ( strcmp(argv[i], "-w") == 0 ) { - sEmitWarnings = false; - } - } - if ( addStandardLibraryDirectories ) { - libraryPaths.push_back("/usr/lib"); - libraryPaths.push_back("/usr/local/lib"); - - frameworkPaths.push_back("/Library/Frameworks/"); - frameworkPaths.push_back("/System/Library/Frameworks/"); - // remove /Network from default search path - //frameworkPaths.push_back("/Network/Library/Frameworks/"); - } - - // Support for configure based hacks - // if last -syslibroot is /, then ignore all syslibroots - if ( fSDKPaths.size() > 0 ) { - if ( strcmp(fSDKPaths.back(), "/") == 0 ) { - fSDKPaths.clear(); - } - } - - // now merge sdk and library paths to make real search paths - fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1)); - for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { - const char* libDir = *it; - bool sdkOverride = false; - if ( libDir[0] == '/' ) { - char betterLibDir[PATH_MAX]; - if ( strstr(libDir, "/..") != NULL ) { - if ( realpath(libDir, betterLibDir) != NULL ) - libDir = strdup(betterLibDir); - } - const int libDirLen = strlen(libDir); - for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - // ??? Should be using string here. - const char* sdkDir = *sdkit; - const int sdkDirLen = strlen(sdkDir); - char newPath[libDirLen + sdkDirLen+4]; - strcpy(newPath, sdkDir); - if ( newPath[sdkDirLen-1] == '/' ) - newPath[sdkDirLen-1] = '\0'; - strcat(newPath, libDir); - struct stat statBuffer; - if ( stat(newPath, &statBuffer) == 0 ) { - fLibrarySearchPaths.push_back(strdup(newPath)); - sdkOverride = true; - } - } - } - if ( !sdkOverride ) - fLibrarySearchPaths.push_back(libDir); - } - - // now merge sdk and framework paths to make real search paths - fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); - for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { - const char* frameworkDir = *it; - bool sdkOverride = false; - if ( frameworkDir[0] == '/' ) { - char betterFrameworkDir[PATH_MAX]; - if ( strstr(frameworkDir, "/..") != NULL ) { - if ( realpath(frameworkDir, betterFrameworkDir) != NULL ) - frameworkDir = strdup(betterFrameworkDir); - } - const int frameworkDirLen = strlen(frameworkDir); - for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - // ??? Should be using string here - const char* sdkDir = *sdkit; - const int sdkDirLen = strlen(sdkDir); - char newPath[frameworkDirLen + sdkDirLen+4]; - strcpy(newPath, sdkDir); - if ( newPath[sdkDirLen-1] == '/' ) - newPath[sdkDirLen-1] = '\0'; - strcat(newPath, frameworkDir); - struct stat statBuffer; - if ( stat(newPath, &statBuffer) == 0 ) { - fFrameworkSearchPaths.push_back(strdup(newPath)); - sdkOverride = true; - } - } - } - if ( !sdkOverride ) - fFrameworkSearchPaths.push_back(frameworkDir); - } - - if ( fVerbose ) { - fprintf(stderr,"Library search paths:\n"); - for (std::vector::iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) - fprintf(stderr,"\t%s\n", *it); - fprintf(stderr,"Framework search paths:\n"); - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) - fprintf(stderr,"\t%s\n", *it); - } -} - -// this is run before the command line is parsed -void Options::parsePreCommandLineEnvironmentSettings() -{ - if ((getenv("LD_TRACE_ARCHIVES") != NULL) - || (getenv("RC_TRACE_ARCHIVES") != NULL)) - fReaderOptions.fTraceArchives = true; - - if ((getenv("LD_TRACE_DYLIBS") != NULL) - || (getenv("RC_TRACE_DYLIBS") != NULL)) { - fReaderOptions.fTraceDylibs = true; - fReaderOptions.fTraceIndirectDylibs = true; - } - - if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { - fTraceDylibSearching = true; - } - - if (getenv("LD_PRINT_OPTIONS") != NULL) - fPrintOptions = true; - - if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) - fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); - - if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) - fPrintOrderFileStatistics = true; - - if (getenv("LD_SPLITSEGS_NEW_LIBRARIES") != NULL) - fSplitSegs = true; - - if (getenv("LD_NO_ENCRYPT") != NULL) - fEncryptable = false; - - sWarningsSideFilePath = getenv("LD_WARN_FILE"); -} - - -// this is run after the command line is parsed -void Options::parsePostCommandLineEnvironmentSettings() -{ - // when building a dynamic main executable, default any use of @executable_path to output path - if ( fExecutablePath == NULL && (fOutputKind == kDynamicExecutable) ) { - fExecutablePath = fOutputFile; - } - - // allow build system to set default seg_addr_table - if ( fSegAddrTablePath == NULL ) - fSegAddrTablePath = getenv("LD_SEG_ADDR_TABLE"); - - // allow build system to turn on prebinding - if ( !fPrebind ) { - fPrebind = ( getenv("LD_PREBIND") != NULL ); - } - - // allow build system to force on dead-code-stripping - if ( fDeadStrip == kDeadStripOff ) { - if ( getenv("LD_DEAD_STRIP") != NULL ) { - switch (fOutputKind) { - case Options::kDynamicLibrary: - case Options::kDynamicExecutable: - case Options::kDynamicBundle: - fDeadStrip = kDeadStripOn; - break; - case Options::kObjectFile: - case Options::kDyld: - case Options::kStaticExecutable: - break; - } - } - } - - // allow build system to force on -warn_commons - if ( getenv("LD_WARN_COMMONS") != NULL ) - fWarnCommons = true; -} - -void Options::reconfigureDefaults() -{ - // sync reader options - switch ( fOutputKind ) { - case Options::kObjectFile: - fReaderOptions.fForFinalLinkedImage = false; - break; - case Options::kDyld: - fReaderOptions.fForDyld = true; - fReaderOptions.fForFinalLinkedImage = true; - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - fReaderOptions.fForFinalLinkedImage = true; - break; - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - fReaderOptions.fLinkingMainExecutable = true; - fReaderOptions.fForFinalLinkedImage = true; - break; - } - - // set default min OS version - if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) { - // if -macosx_version_min not used, try environment variable - const char* envVers = getenv("MACOSX_DEPLOYMENT_TARGET"); - if ( envVers != NULL ) - setMacOSXVersionMin(envVers); - // if -macosx_version_min and environment variable not used assume current OS version - if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; // FIX FIX, this really should be a check of the OS version the linker is running on - } - - // adjust min based on architecture - switch ( fArchitecture ) { - case CPU_TYPE_I386: - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { - //warning("-macosx_version_min should be 10.4 or later for i386"); - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; - } - break; - case CPU_TYPE_POWERPC64: - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { - //warning("-macosx_version_min should be 10.4 or later for ppc64"); - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; - } - break; - case CPU_TYPE_X86_64: - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { - //warning("-macosx_version_min should be 10.4 or later for x86_64"); - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; - } - break; - } - - // disable implicit dylibs when targetting 10.3 - // add option to disable implicit load commands for indirectly used public dylibs - if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_3 ) - fReaderOptions.fImplicitlyLinkPublicDylibs = false; - - - // determine if info for shared region should be added - if ( fOutputKind == Options::kDynamicLibrary ) { - if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) - if ( fArchitecture != CPU_TYPE_ARM ) - fSharedRegionEligible = true; - } - - // allow build system to force linker to ignore seg_addr_table - if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL ) - fSegAddrTablePath = NULL; - - // check for base address specified externally - if ( (fSegAddrTablePath != NULL) && (fOutputKind == Options::kDynamicLibrary) ) { - parseSegAddrTable(fSegAddrTablePath, this->installPath()); - // HACK to support seg_addr_table entries that are physical paths instead of install paths - if ( fBaseAddress == 0 ) { - if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) - parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib"); - - else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 ) - parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib"); - - else if ( strcmp(this->installPath(), "/usr/lib/libutil.dylib") == 0 ) - parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libutil1.0.dylib"); - } - } - - // split segs only allowed for dylibs - if ( fSplitSegs ) { - // split seg only supported for ppc, i386, and arm. - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_I386: - if ( fOutputKind != Options::kDynamicLibrary ) - fSplitSegs = false; - // make sure read and write segments are proper distance apart - if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x10000000) ) - fBaseWritableAddress = fBaseAddress + 0x10000000; - break; - case CPU_TYPE_ARM: - if ( fOutputKind != Options::kDynamicLibrary ) { - fSplitSegs = false; - } - else { - // make sure read and write segments are proper distance apart - if ( fSplitSegs && (fBaseWritableAddress-fBaseAddress != 0x08000000) ) - fBaseWritableAddress = fBaseAddress + 0x08000000; - } - break; - default: - fSplitSegs = false; - fBaseAddress = 0; - fBaseWritableAddress = 0; - } - } - - // disable prebinding depending on arch and min OS version - if ( fPrebind ) { - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_I386: - if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::k10_4 ) { - // in 10.4 only split seg dylibs are prebound - if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) - fPrebind = false; - } - else if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { - // in 10.5 nothing is prebound - fPrebind = false; - } - else { - // in 10.3 and earlier only dylibs and main executables could be prebound - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - // only main executables and dylibs can be prebound - break; - case Options::kStaticExecutable: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - // disable prebinding for everything else - fPrebind = false; - break; - } - } - break; - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - fPrebind = false; - break; - case CPU_TYPE_ARM: - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - // only main executables and dylibs can be prebound - break; - case Options::kStaticExecutable: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - // disable prebinding for everything else - fPrebind = false; - break; - } - break; - } - } - - // only prebound images can be split-seg - if ( fSplitSegs && !fPrebind ) - fSplitSegs = false; - - // figure out if module table is needed for compatibility with old ld/dyld - if ( fOutputKind == Options::kDynamicLibrary ) { - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table - case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table - if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_5 ) - fNeedsModuleTable = true; - break; - case CPU_TYPE_ARM: - fNeedsModuleTable = true; // redo_prebinding requires a module table - break; - } - } - - // -r -x implies -S - if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; - - // only ARM main executables can be encrypted - if ( fOutputKind != Options::kDynamicExecutable ) - fEncryptable = false; - if ( fArchitecture != CPU_TYPE_ARM ) - fEncryptable = false; -} - -void Options::checkIllegalOptionCombinations() -{ - // check -undefined setting - switch ( fUndefinedTreatment ) { - case kUndefinedError: - case kUndefinedDynamicLookup: - // always legal - break; - case kUndefinedWarning: - case kUndefinedSuppress: - // requires flat namespace - if ( fNameSpace == kTwoLevelNameSpace ) - throw "can't use -undefined warning or suppress with -twolevel_namespace"; - break; - } - - // unify -sub_umbrella with dylibs - for (std::vector::iterator it = fSubUmbellas.begin(); it != fSubUmbellas.end(); it++) { - const char* subUmbrella = *it; - bool found = false; - for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { - Options::FileInfo& info = *fit; - const char* lastSlash = strrchr(info.path, '/'); - if ( lastSlash == NULL ) - lastSlash = info.path - 1; - if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { - info.options.fReExport = true; - found = true; - break; - } - } - if ( ! found ) - warning("-sub_umbrella %s does not match a supplied dylib", subUmbrella); - } - - // unify -sub_library with dylibs - for (std::vector::iterator it = fSubLibraries.begin(); it != fSubLibraries.end(); it++) { - const char* subLibrary = *it; - bool found = false; - for (std::vector::iterator fit = fInputFiles.begin(); fit != fInputFiles.end(); fit++) { - Options::FileInfo& info = *fit; - const char* lastSlash = strrchr(info.path, '/'); - if ( lastSlash == NULL ) - lastSlash = info.path - 1; - const char* dot = strchr(&lastSlash[1], '.'); - if ( dot == NULL ) - dot = &lastSlash[strlen(lastSlash)]; - if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { - info.options.fReExport = true; - found = true; - break; - } - } - if ( ! found ) - warning("-sub_library %s does not match a supplied dylib", subLibrary); - } - - // sync reader options - if ( fNameSpace != kTwoLevelNameSpace ) - fReaderOptions.fFlatNamespace = true; - - // check -stack_addr - if ( fStackAddr != 0 ) { - switch (fArchitecture) { - case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: - case CPU_TYPE_ARM: - if ( fStackAddr > 0xFFFFFFFF ) - throw "-stack_addr must be < 4G for 32-bit processes"; - break; - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - break; - } - if ( (fStackAddr & -4096) != fStackAddr ) - throw "-stack_addr must be multiples of 4K"; - if ( fStackSize == 0 ) - throw "-stack_addr must be used with -stack_size"; - } - - // check -stack_size - if ( fStackSize != 0 ) { - switch (fArchitecture) { - case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: - if ( fStackSize > 0xFFFFFFFF ) - throw "-stack_size must be < 4G for 32-bit processes"; - if ( fStackAddr == 0 ) { - fStackAddr = 0xC0000000; - } - if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) - warning("custom stack placement overlaps and will disable shared region"); - break; - case CPU_TYPE_ARM: - if ( fStackSize > 0xFFFFFFFF ) - throw "-stack_size must be < 4G for 32-bit processes"; - if ( fStackAddr == 0 ) - fStackAddr = 0x30000000; - if ( fStackAddr > 0x40000000) - throw "-stack_addr must be < 1G for arm"; - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - if ( fStackAddr == 0 ) { - fStackAddr = 0x00007FFF5C000000LL; - } - break; - } - if ( (fStackSize & -4096) != fStackSize ) - throw "-stack_size must be multiples of 4K"; - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - // custom stack size only legal when building main executable - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - throw "-stack_size option can only be used when linking a main executable"; - } - } - - // check that -allow_stack_execute is only used with main executables - if ( fExecutableStack ) { - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - // -allow_stack_execute size only legal when building main executable - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - throw "-allow_stack_execute option can only be used when linking a main executable"; - } - } - - // check -client_name is only used when making a bundle or main executable - if ( fClientName != NULL ) { - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicBundle: - break; - case Options::kStaticExecutable: - case Options::kDynamicLibrary: - case Options::kObjectFile: - case Options::kDyld: - throw "-client_name can only be used with -bundle"; - } - } - - // check -init is only used when building a dylib - if ( (fInitFunctionName != NULL) && (fOutputKind != Options::kDynamicLibrary) ) - throw "-init can only be used with -dynamiclib"; - - // check -bundle_loader only used with -bundle - if ( (fBundleLoader != NULL) && (fOutputKind != Options::kDynamicBundle) ) - throw "-bundle_loader can only be used with -bundle"; - - // check -dtrace not used with -r - if ( (fDtraceScriptName != NULL) && (fOutputKind == Options::kObjectFile) ) - throw "-dtrace can only be used when creating final linked images"; - - // check -d can only be used with -r - if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) - throw "-d can only be used with -r"; - - // check that -root_safe is not used with -r - if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) ) - throw "-root_safe cannot be used with -r"; - - // check that -setuid_safe is not used with -r - if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) ) - throw "-setuid_safe cannot be used with -r"; - - // make sure all required exported symbols exist - std::vector impliedExports; - for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) { - const char* name = *it; - // never export .eh symbols - const int len = strlen(name); - if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) - warning("ignoring %s in export list", name); - else - fInitialUndefines.push_back(name); - if ( strncmp(name, ".objc_class_name_", 17) == 0 ) { - // rdar://problem/4718189 map ObjC class names to new runtime names - switch (fArchitecture) { - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - case CPU_TYPE_ARM: - char* temp; - asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); - impliedExports.push_back(temp); - asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); - impliedExports.push_back(temp); - break; - } - } - } - for (std::vector::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) { - const char* name = *it; - fExportSymbols.insert(name); - fInitialUndefines.push_back(name); - } - - // make sure that -init symbol exist - if ( fInitFunctionName != NULL ) - fInitialUndefines.push_back(fInitFunctionName); - - // check custom segments - if ( fCustomSegmentAddresses.size() != 0 ) { - // verify no segment is in zero page - if ( fZeroPageSize != ULLONG_MAX ) { - for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { - if ( (it->address >= 0) && (it->address < fZeroPageSize) ) - throwf("-segaddr %s 0x%X conflicts with -pagezero_size", it->name, it->address); - } - } - // verify no duplicates - for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { - for (std::vector::iterator it2 = fCustomSegmentAddresses.begin(); it2 != fCustomSegmentAddresses.end(); ++it2) { - if ( (it->address == it2->address) && (it != it2) ) - throwf("duplicate -segaddr addresses for %s and %s", it->name, it2->name); - } - // a custom segment address of zero will disable the use of a zero page - if ( it->address == 0 ) - fZeroPageSize = 0; - } - } - - if ( fZeroPageSize == ULLONG_MAX ) { - // zero page size not specified on command line, set default - switch (fArchitecture) { - case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: - case CPU_TYPE_ARM: - // first 4KB for 32-bit architectures - fZeroPageSize = 0x1000; - break; - case CPU_TYPE_POWERPC64: - // first 4GB for ppc64 on 10.5 - if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) - fZeroPageSize = 0x100000000ULL; - else - fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page - break; - case CPU_TYPE_X86_64: - // first 4GB for x86_64 on all OS's - fZeroPageSize = 0x100000000ULL; - break; - default: - // if -arch not used, default to 4K zero-page - fZeroPageSize = 0x1000; - } - } - else { - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - // -pagezero_size size only legal when building main executable - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: - case Options::kDyld: - if ( fZeroPageSize != 0 ) - throw "-pagezero_size option can only be used when linking a main executable"; - } - } - - // -dead_strip and -r are incompatible - if ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) ) - throw "-r and -dead_strip cannot be used together"; - - // can't use -rpath unless targeting 10.5 or later - if ( fRPaths.size() > 0 ) { - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) - throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - break; - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kDyld: - throw "-rpath can only be used when creating a dynamic final linked image"; - } - } - - // check -pie is only used when building a dynamic main executable for 10.5 - if ( fPositionIndependentExecutable ) { - if ( fOutputKind != Options::kDynamicExecutable ) - throw "-pie can only be used when linking a main executable"; - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) - throw "-pie can only be used when targeting Mac OS X 10.5 or later"; - } -} - - - -void Options::checkForClassic(int argc, const char* argv[]) -{ - // scan options - bool archFound = false; - bool staticFound = false; - bool dtraceFound = false; - bool rFound = false; - bool creatingMachKernel = false; - bool newLinker = false; - - for(int i=0; i < argc; ++i) { - const char* arg = argv[i]; - if ( arg[0] == '-' ) { - if ( strcmp(arg, "-arch") == 0 ) { - parseArch(argv[++i]); - archFound = true; - } - else if ( strcmp(arg, "-static") == 0 ) { - staticFound = true; - } - else if ( strcmp(arg, "-dtrace") == 0 ) { - dtraceFound = true; - } - else if ( strcmp(arg, "-r") == 0 ) { - rFound = true; - } - else if ( strcmp(arg, "-new_linker") == 0 ) { - newLinker = true; - } - else if ( strcmp(arg, "-classic_linker") == 0 ) { - // ld_classic does not understand this option, so remove it - for(int j=i; j < argc; ++j) - argv[j] = argv[j+1]; - this->gotoClassicLinker(argc-1, argv); - } - else if ( strcmp(arg, "-o") == 0 ) { - const char* outfile = argv[++i]; - if ( (outfile != NULL) && (strstr(outfile, "/mach_kernel") != NULL) ) - creatingMachKernel = true; - } - } - } - - // -dtrace only supported by new linker - if( dtraceFound ) - return; - - if( archFound ) { - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_I386: - case CPU_TYPE_ARM: -// if ( staticFound && (rFound || !creatingMachKernel) ) { - if ( staticFound && !newLinker ) { - // this environment variable will disable use of ld_classic for -static links - if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { - // ld_classic does not support -aspen_version_min, so change - for(int j=0; j < argc; ++j) { - if ( (strcmp(argv[j], "-aspen_version_min") == 0) - || (strcmp(argv[j], "-iphone_version_min") == 0) - || (strcmp(argv[j], "-iphoneos_version_min") == 0) ) { - argv[j] = "-macosx_version_min"; - if ( j < argc-1 ) - argv[j+1] = "10.5"; - break; - } - } - this->gotoClassicLinker(argc, argv); - } - } - break; - } - } - else { - // work around for VSPTool - if ( staticFound ) - this->gotoClassicLinker(argc, argv); - } - -} - -void Options::gotoClassicLinker(int argc, const char* argv[]) -{ - argv[0] = "ld_classic"; - execvp(argv[0], (char**)argv); - fprintf(stderr, "can't exec ld_classic\n"); - exit(1); -} diff --git a/ld64/FireOpal/src/debugline.c b/ld64/FireOpal/src/debugline.c deleted file mode 100644 index ff0e1d9..0000000 --- a/ld64/FireOpal/src/debugline.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef KLD -#include -#include -#include -#include -#include "dwarf2.h" -#include "debugline.h" - -struct line_reader_data -{ - bool little_endian; - - /* From the line number information header. */ - uint8_t minimum_instruction_length; - int8_t line_base; - uint8_t line_range; - uint8_t opcode_base; - const uint8_t * standard_opcode_lengths; - size_t numdir; - const uint8_t * * dirnames; - size_t numfile_orig; - size_t numfile; /* As updated during execution of the table. */ - const uint8_t * * filenames; - - /* Current position in the line table. */ - const uint8_t * cpos; - /* End of this part of the line table. */ - const uint8_t * end; - /* Start of the line table. */ - const uint8_t * init; - - struct line_info cur; -}; - -/* Read in a word of fixed size, which may be unaligned, in the - appropriate endianness. */ -#define read_16(p) (lnd->little_endian \ - ? ((p)[1] << 8 | (p)[0]) \ - : ((p)[0] << 8 | (p)[1])) -#define read_32(p) (lnd->little_endian \ - ? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \ - : ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])) -#define read_64(p) (lnd->little_endian \ - ? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \ - | (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \ - | (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \ - | (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \ - : ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \ - | (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \ - | (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \ - | (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7])) - -/* Skip over a LEB128 value (signed or unsigned). */ -static void -skip_leb128 (struct line_reader_data * leb) -{ - while (leb->cpos != leb->end && *leb->cpos >= 0x80) - leb->cpos++; - if (leb->cpos != leb->end) - leb->cpos++; -} - -/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow - or error. On overflow, skip past the rest of the uleb128. */ -static uint64_t -read_uleb128 (struct line_reader_data * leb) -{ - uint64_t result = 0; - int bit = 0; - - do { - uint64_t b; - - if (leb->cpos == leb->end) - return (uint64_t) -1; - - b = *leb->cpos & 0x7f; - - if (bit >= 64 || b << bit >> bit != b) - result = (uint64_t) -1; - else - result |= b << bit, bit += 7; - } while (*leb->cpos++ >= 0x80); - return result; -} - - -/* Read a SLEB128 into a 64-bit word. Return 0 on overflow or error - (which is not very helpful). On overflow, skip past the rest of - the SLEB128. For negative numbers, this actually overflows when - under -2^62, but since this is used for line numbers that ought to - be OK... */ -static int64_t -read_sleb128 (struct line_reader_data * leb) -{ - const uint8_t * start_pos = leb->cpos; - uint64_t v = read_uleb128 (leb); - uint64_t signbit; - - if (v >= 1ull << 63) - return 0; - if (leb->cpos - start_pos > 9) - return v; - - signbit = 1ull << ((leb->cpos - start_pos) * 7 - 1); - - return v | -(v & signbit); -} - -/* Free a line_reader_data structure. */ -void -line_free (struct line_reader_data * lnd) -{ - if (! lnd) - return; - if (lnd->dirnames) - free (lnd->dirnames); - if (lnd->filenames) - free (lnd->filenames); - free (lnd); -} - -/* Return the pathname of the file in S, or NULL on error. - The result will have been allocated with malloc. */ - -char * -line_file (struct line_reader_data *lnd, uint64_t n) -{ - const uint8_t * prev_pos = lnd->cpos; - size_t filelen, dirlen; - uint64_t dir; - char * result; - - /* I'm not sure if this is actually an error. */ - if (n == 0 - || n > lnd->numfile) - return NULL; - - filelen = strlen ((const char *)lnd->filenames[n - 1]); - lnd->cpos = lnd->filenames[n - 1] + filelen + 1; - dir = read_uleb128 (lnd); - lnd->cpos = prev_pos; - if (dir == 0 - || lnd->filenames[n - 1][0] == '/') - return strdup ((const char *)lnd->filenames[n - 1]); - else if (dir > lnd->numdir) - return NULL; - - dirlen = strlen ((const char *) lnd->dirnames[dir - 1]); - result = malloc (dirlen + filelen + 2); - memcpy (result, lnd->dirnames[dir - 1], dirlen); - result[dirlen] = '/'; - memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen); - result[dirlen + 1 + filelen] = '\0'; - return result; -} - -/* Initialize a state S. Return FALSE on error. */ - -static void -init_state (struct line_info *s) -{ - s->file = 1; - s->line = 1; - s->col = 0; - s->pc = 0; - s->end_of_sequence = false; -} - -/* Read a debug_line section. */ - -struct line_reader_data * -line_open (const uint8_t * debug_line, size_t debug_line_size, - int little_endian) -{ - struct line_reader_data * lnd = NULL; - bool dwarf_size_64; - - uint64_t lnd_length, header_length; - const uint8_t * table_start; - - if (debug_line_size < 12) - return NULL; - - lnd = malloc (sizeof (struct line_reader_data)); - if (! lnd) - goto error; - - lnd->little_endian = little_endian; - lnd->cpos = debug_line; - - lnd_length = read_32 (lnd->cpos); - lnd->cpos += 4; - if (lnd_length == 0xffffffff) - { - lnd_length = read_64 (lnd->cpos); - lnd->cpos += 8; - dwarf_size_64 = true; - } - else if (lnd_length > 0xfffffff0) - /* Not a format we understand. */ - goto error; - else - dwarf_size_64 = false; - - if (debug_line_size < lnd_length + (dwarf_size_64 ? 12 : 4) - || lnd_length < (dwarf_size_64 ? 15 : 11)) - /* Too small. */ - goto error; - - if (read_16 (lnd->cpos) != 2) - /* Unknown line number format. */ - goto error; - lnd->cpos += 2; - - header_length = dwarf_size_64 ? (uint64_t)read_64(lnd->cpos) : (uint64_t)read_32(lnd->cpos); - lnd->cpos += dwarf_size_64 ? 8 : 4; - if (lnd_length < header_length + (lnd->cpos - debug_line) - || header_length < 7) - goto error; - - lnd->minimum_instruction_length = lnd->cpos[0]; - /* Ignore default_is_stmt. */ - lnd->line_base = lnd->cpos[2]; - lnd->line_range = lnd->cpos[3]; - lnd->opcode_base = lnd->cpos[4]; - - if (lnd->opcode_base == 0) - /* Every valid line number program must use at least opcode 0 - for DW_LNE_end_sequence. */ - goto error; - - lnd->standard_opcode_lengths = lnd->cpos + 5; - if (header_length < (uint64_t)(5 + (lnd->opcode_base - 1))) - /* Header not long enough. */ - goto error; - lnd->cpos += 5 + lnd->opcode_base - 1; - lnd->end = debug_line + header_length + (dwarf_size_64 ? 22 : 10); - - /* Make table of offsets to directory names. */ - table_start = lnd->cpos; - lnd->numdir = 0; - while (lnd->cpos != lnd->end && *lnd->cpos) - { - lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); - if (! lnd->cpos) - goto error; - lnd->cpos++; - lnd->numdir++; - } - if (lnd->cpos == lnd->end) - goto error; - lnd->dirnames = malloc (lnd->numdir * sizeof (const uint8_t *)); - if (! lnd->dirnames) - goto error; - lnd->numdir = 0; - lnd->cpos = table_start; - while (*lnd->cpos) - { - lnd->dirnames[lnd->numdir++] = lnd->cpos; - lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; - } - lnd->cpos++; - - /* Make table of offsets to file entries. */ - table_start = lnd->cpos; - lnd->numfile = 0; - while (lnd->cpos != lnd->end && *lnd->cpos) - { - lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos); - if (! lnd->cpos) - goto error; - lnd->cpos++; - skip_leb128 (lnd); - skip_leb128 (lnd); - skip_leb128 (lnd); - lnd->numfile++; - } - if (lnd->cpos == lnd->end) - goto error; - lnd->filenames = malloc (lnd->numfile * sizeof (const uint8_t *)); - if (! lnd->filenames) - goto error; - lnd->numfile = 0; - lnd->cpos = table_start; - while (*lnd->cpos) - { - lnd->filenames[lnd->numfile++] = lnd->cpos; - lnd->cpos = memchr (lnd->cpos, 0, lnd->end - lnd->cpos) + 1; - skip_leb128 (lnd); - skip_leb128 (lnd); - skip_leb128 (lnd); - } - lnd->cpos++; - - lnd->numfile_orig = lnd->numfile; - lnd->cpos = lnd->init = lnd->end; - lnd->end = debug_line + lnd_length + (dwarf_size_64 ? 12 : 4); - - init_state (&lnd->cur); - - return lnd; - - error: - line_free (lnd); - return NULL; -} - -/* Reset back to the beginning. */ -void -line_reset (struct line_reader_data * lnd) -{ - lnd->cpos = lnd->init; - lnd->numfile = lnd->numfile_orig; - init_state (&lnd->cur); -} - -/* Is there no more line data available? */ -int -line_at_eof (struct line_reader_data * lnd) -{ - return lnd->cpos == lnd->end; -} - -static bool -next_state (struct line_reader_data *lnd) -{ - if (lnd->cur.end_of_sequence) - init_state (&lnd->cur); - - for (;;) - { - uint8_t op; - uint64_t tmp; - - if (lnd->cpos == lnd->end) - return false; - op = *lnd->cpos++; - if (op >= lnd->opcode_base) - { - op -= lnd->opcode_base; - - lnd->cur.line += op % lnd->line_range + lnd->line_base; - lnd->cur.pc += (op / lnd->line_range - * lnd->minimum_instruction_length); - return true; - } - else switch (op) - { - case DW_LNS_extended_op: - { - uint64_t sz = read_uleb128 (lnd); - const uint8_t * op = lnd->cpos; - - if ((uint64_t)(lnd->end - op) < sz || sz == 0) - return false; - lnd->cpos += sz; - switch (*op++) - { - case DW_LNE_end_sequence: - lnd->cur.end_of_sequence = true; - return true; - - case DW_LNE_set_address: - if (sz == 9) - lnd->cur.pc = read_64 (op); - else if (sz == 5) - lnd->cur.pc = read_32 (op); - else - return false; - break; - - case DW_LNE_define_file: - { - const uint8_t * * filenames; - filenames = realloc - (lnd->filenames, - (lnd->numfile + 1) * sizeof (const uint8_t *)); - if (! filenames) - return false; - /* Check for zero-termination. */ - if (! memchr (op, 0, lnd->cpos - op)) - return false; - filenames[lnd->numfile++] = op; - lnd->filenames = filenames; - - /* There's other data here, like file sizes and modification - times, but we don't need to read it so skip it. */ - } - break; - - default: - /* Don't understand it, so skip it. */ - break; - } - break; - } - - case DW_LNS_copy: - //fprintf(stderr, "DW_LNS_copy\n"); - return true; - case DW_LNS_advance_pc: - //fprintf(stderr, "DW_LNS_advance_pc\n"); - tmp = read_uleb128 (lnd); - if (tmp == (uint64_t) -1) - return false; - lnd->cur.pc += tmp * lnd->minimum_instruction_length; - break; - case DW_LNS_advance_line: - //fprintf(stderr, "DW_LNS_advance_line\n"); - lnd->cur.line += read_sleb128 (lnd); - break; - case DW_LNS_set_file: - //fprintf(stderr, "DW_LNS_set_file\n"); - lnd->cur.file = read_uleb128 (lnd); - break; - case DW_LNS_set_column: - //fprintf(stderr, "DW_LNS_set_column\n"); - lnd->cur.col = read_uleb128 (lnd); - break; - case DW_LNS_const_add_pc: - //fprintf(stderr, "DW_LNS_const_add_pc\n"); - lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range - * lnd->minimum_instruction_length); - break; - case DW_LNS_fixed_advance_pc: - //fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); - if (lnd->end - lnd->cpos < 2) - return false; - lnd->cur.pc += read_16 (lnd->cpos); - lnd->cpos += 2; - break; - default: - { - /* Don't know what it is, so skip it. */ - int i; - for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++) - skip_leb128 (lnd); - break; - } - } - } -} - - -/* Set RESULT to the next 'interesting' line state, as indicated - by STOP, or return FALSE on error. The final (end-of-sequence) - line state is always considered interesting. */ -int -line_next (struct line_reader_data * lnd, - struct line_info * result, - enum line_stop_constants stop) -{ - for (;;) - { - struct line_info prev = lnd->cur; - - if (! next_state (lnd)) - return false; - - if (lnd->cur.end_of_sequence) - break; - if (stop == line_stop_always) - break; - if ((stop & line_stop_pc) && lnd->cur.pc != prev.pc) - break; - if ((stop & line_stop_pos_mask) && lnd->cur.file != prev.file) - break; - if ((stop & line_stop_pos_mask) >= line_stop_line - && lnd->cur.line != prev.line) - break; - if ((stop & line_stop_pos_mask) >= line_stop_col - && lnd->cur.col != prev.col) - break; - } - *result = lnd->cur; - return true; -} - -/* Find the region (START->pc through END->pc) in the debug_line - information which contains PC. This routine starts searching at - the current position (which is returned as END), and will go all - the way around the debug_line information. It will return false if - an error occurs or if there is no matching region; these may be - distinguished by looking at START->end_of_sequence, which will be - false on error and true if there was no matching region. - You could write this routine using line_next, but this version - will be slightly more efficient, and of course more convenient. */ - -int -line_find_addr (struct line_reader_data * lnd, - struct line_info * start, - struct line_info * end, - uint64_t pc) -{ - const uint8_t * startpos; - struct line_info prev; - - if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) - line_reset (lnd); - - startpos = lnd->cpos; - - do { - prev = lnd->cur; - if (! next_state (lnd)) - { - start->end_of_sequence = false; - return false; - } - if (lnd->cur.end_of_sequence && lnd->cpos == lnd->end) - line_reset (lnd); - if (lnd->cpos == startpos) - { - start->end_of_sequence = true; - return false; - } - } while (lnd->cur.pc <= pc || prev.pc > pc || prev.end_of_sequence); - *start = prev; - *end = lnd->cur; - return true; -} -#endif /* ! KLD */ - diff --git a/ld64/FireOpal/src/ld.cpp b/ld64/FireOpal/src/ld.cpp deleted file mode 100644 index acd18c8..0000000 --- a/ld64/FireOpal/src/ld.cpp +++ /dev/null @@ -1,3778 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -// start temp HACK for cross builds -extern "C" double log2 ( double ); -#define __MATH__ -// end temp HACK for cross builds - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "configure.h" -#include "Options.h" - -#include "ObjectFile.h" - -#include "MachOReaderRelocatable.hpp" -#include "ArchiveReader.hpp" -#include "MachOReaderDylib.hpp" -#include "MachOWriterExecutable.hpp" - - -#if LTO_SUPPORT -#include "LTOReader.hpp" -#endif - - -#include "OpaqueSection.hpp" - - -class CStringComparor -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } -}; - -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - -class Section : public ObjectFile::Section -{ -public: - static Section* find(const char* sectionName, const char* segmentName, bool zeroFill); - static void assignIndexes(); - const char* getName() { return fSectionName; } -private: - Section(const char* sectionName, const char* segmentName, bool zeroFill); - - struct Sorter { - static int segmentOrdinal(const char* segName); - bool operator()(Section* left, Section* right); - }; - - typedef __gnu_cxx::hash_map, CStringEquals> NameToOrdinal; - typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; - //typedef std::map NameToSection; - - const char* fSectionName; - const char* fSegmentName; - bool fZeroFill; - - static NameToSection fgMapping; - static std::vector fgSections; - static NameToOrdinal fgSegmentDiscoverOrder; -}; - -Section::NameToSection Section::fgMapping; -std::vector Section::fgSections; -Section::NameToOrdinal Section::fgSegmentDiscoverOrder; - -Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) - : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill) -{ - this->fIndex = fgSections.size(); - //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); -} - -Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) -{ - NameToSection::iterator pos = fgMapping.find(sectionName); - if ( pos != fgMapping.end() ) { - if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) - return pos->second; - // otherwise same section name is used in different segments, look slow way - for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) - return *it; - } - } - - // does not exist, so make a new one - Section* sect = new Section(sectionName, segmentName, zeroFill); - fgMapping[sectionName] = sect; - fgSections.push_back(sect); - - if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { - // special case __textcoal_nt to be right after __text - find("__textcoal_nt", "__TEXT", false); - } - - // remember segment discovery order - if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) - fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); - - return sect; -} - -int Section::Sorter::segmentOrdinal(const char* segName) -{ - if ( strcmp(segName, "__PAGEZERO") == 0 ) - return 1; - if ( strcmp(segName, "__TEXT") == 0 ) - return 2; - if ( strcmp(segName, "__DATA") == 0 ) - return 3; - if ( strcmp(segName, "__OBJC") == 0 ) - return 4; - if ( strcmp(segName, "__OBJC2") == 0 ) - return 5; - if ( strcmp(segName, "__LINKEDIT") == 0 ) - return INT_MAX; // linkedit segment should always sort last - else - return fgSegmentDiscoverOrder[segName]+6; -} - - -bool Section::Sorter::operator()(Section* left, Section* right) -{ - // Segment is primary sort key - int leftSegOrdinal = segmentOrdinal(left->fSegmentName); - int rightSegOrdinal = segmentOrdinal(right->fSegmentName); - if ( leftSegOrdinal < rightSegOrdinal ) - return true; - if ( leftSegOrdinal > rightSegOrdinal ) - return false; - - // zerofill section sort to the end - if ( !left->fZeroFill && right->fZeroFill ) - return true; - if ( left->fZeroFill && !right->fZeroFill ) - return false; - - // section discovery order is last sort key - return left->fIndex < right->fIndex; -} - -void Section::assignIndexes() -{ - //printf("unsorted sections:\n"); - //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); - //} - - // sort it - std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); - - // assign correct section ordering to each Section object - unsigned int newOrder = 1; - for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) - (*it)->fIndex = newOrder++; - - //printf("sorted sections:\n"); - //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); - //} -} - -class Linker : public ObjectFile::Reader::DylibHander { -public: - Linker(int argc, const char* argv[]); - - const char* getArchPrefix(); - const char* architectureName(); - bool showArchitectureInErrors(); - bool isInferredArchitecture(); - void createReaders(); - void createWriter(); - void addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& ); - void setOutputFile(ExecutableFile::Writer* writer); - void link(); - void optimize(); - - // implemenation from ObjectFile::Reader::DylibHander - virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath); - -private: - struct WhyLiveBackChain - { - WhyLiveBackChain* previous; - const char* name; - }; - - ObjectFile::Reader* createReader(const Options::FileInfo&); - void addAtom(ObjectFile::Atom& atom); - void addAtoms(std::vector& atoms); - void buildAtomList(); - void processDylibs(); - void markDead(ObjectFile::Atom* atom); - void updateConstraints(ObjectFile::Reader* reader); - void loadAndResolve(); - void processDTrace(); - void checkObjC(); - void loadUndefines(); - void checkUndefines(); - void addWeakAtomOverrides(); - void resolveReferences(); - void deadStripResolve(); - void addLiveRoot(const char* name); - ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); - void logArchive(ObjectFile::Reader* reader); - void sortSections(); - void sortAtoms(); - void tweakLayout(); - void writeDotOutput(); - static bool minimizeStab(ObjectFile::Reader::Stab& stab); - static const char* truncateStabString(const char* str); - void collectDebugInfo(); - void writeOutput(); - ObjectFile::Atom* entryPoint(bool orInit); - ObjectFile::Atom* dyldHelper(); - ObjectFile::Atom* dyldLazyLibraryHelper(); - const char* assureFullPath(const char* path); - void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); - void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); - void synthesizeDebugNotes(std::vector& allAtomsByReader); - void printStatistics(); - void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); - char* commatize(uint64_t in, char* out); - void getVMInfo(vm_statistics_data_t& info); - cpu_type_t inferArchitecture(); - void addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName); - void checkDylibClientRestrictions(ObjectFile::Reader* reader); - void logDylib(ObjectFile::Reader* reader, bool indirect); - - void resolve(ObjectFile::Reference* reference); - void resolveFrom(ObjectFile::Reference* reference); - std::vector* addJustInTimeAtoms(const char* name, bool dylibsOnly=false); - void addJustInTimeAtomsAndMarkLive(const char* name); - - ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - - void logTraceInfo(const char* format, ...); - - - class SymbolTable - { - public: - typedef __gnu_cxx::hash_map, CStringEquals> Mapper; - - SymbolTable(Linker&); - void require(const char* name); - bool add(ObjectFile::Atom& atom); - ObjectFile::Atom* find(const char* name); - unsigned int getRequireCount() { return fRequireCount; } - void getNeededNames(bool andWeakDefintions, std::vector& undefines); - bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } - bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } - void setHasExternalWeakDefinitions() { fHasExternalWeakDefinitions = true; } - Mapper::iterator begin() { return fTable.begin(); } - Mapper::iterator end() { return fTable.end(); } - - private: - Linker& fOwner; - Mapper fTable; - unsigned int fRequireCount; - bool fHasExternalTentativeDefinitions; - bool fHasExternalWeakDefinitions; - }; - - class AtomSorter - { - public: - AtomSorter(std::map* map) : fOverriddenOrdinalMap(map) {} - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); - private: - std::map* fOverriddenOrdinalMap; - }; - - typedef std::map SectionOrder; - - struct DTraceProbeInfo { - DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} - const ObjectFile::Atom* atom; - uint32_t offset; - const char* probeName; - }; - typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToReader; - - struct IndirectLibrary { - const char* path; - uint64_t fileLen; - ObjectFile::Reader* reader; - std::set parents; - ObjectFile::Reader* reExportedViaDirectLibrary; - }; - - ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib); - - Options fOptions; - SymbolTable fGlobalSymbolTable; - uint32_t fNextInputOrdinal; - std::vector fInputFiles; - ExecutableFile::Writer* fOutputFile; - InstallNameToReader fDylibMap; - std::map fDylibOptionsMap; - std::set fDylibsProcessed; - ObjectFile::Reader* fBundleLoaderReader; - std::vector fReadersThatHaveSuppliedAtoms; - std::vector fAllAtoms; - std::set fArchiveReaders; - std::set fArchiveReadersLogged; - std::set fDeadAtoms; - std::set fLiveAtoms; - std::set fLiveRootAtoms; - std::vector fStabs; - std::vector fAtomsWithUnresolvedReferences; - std::vector fDtraceProbes; - std::vector fDtraceProbeSites; - std::vector fDtraceIsEnabledSites; - std::map fDtraceAtomToTypes; - bool fCreateUUID; - bool fCanScatter; - SectionOrder fSectionOrder; - cpu_type_t fArchitecture; - const char* fArchitectureName; - bool fArchitectureInferred; - bool fDirectLibrariesComplete; - bool fBiggerThanTwoGigOutput; - uint64_t fOutputFileSize; - uint64_t fTotalZeroFillSize; - uint64_t fTotalSize; - uint64_t fStartTime; - uint64_t fStartCreateReadersTime; - uint64_t fStartCreateWriterTime; - uint64_t fStartBuildAtomsTime; - uint64_t fStartLoadAndResolveTime; - uint64_t fStartSortTime; - uint64_t fStartDebugTime; - uint64_t fStartWriteTime; - uint64_t fEndTime; - uint64_t fTotalObjectSize; - uint64_t fTotalArchiveSize; - uint32_t fTotalObjectLoaded; - uint32_t fTotalArchivesLoaded; - uint32_t fTotalDylibsLoaded; - vm_statistics_data_t fStartVMInfo; - ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint; - ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint; - bool fObjcReplacmentClasses; - bool fAllDirectDylibsLoaded; -}; - - -Linker::Linker(int argc, const char* argv[]) - : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), - fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), - fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), - fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), - fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), - fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny), - fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false) -{ - fStartTime = mach_absolute_time(); - if ( fOptions.printStatistics() ) - getVMInfo(fStartVMInfo); - - fArchitecture = fOptions.architecture(); - if ( fArchitecture == 0 ) { - // -arch not specified, scan .o files to figure out what it should be - fArchitecture = inferArchitecture(); - fArchitectureInferred = true; - } - switch (fArchitecture) { - case CPU_TYPE_POWERPC: - fArchitectureName = "ppc"; - break; - case CPU_TYPE_POWERPC64: - fArchitectureName = "ppc64"; - break; - case CPU_TYPE_I386: - fArchitectureName = "i386"; - break; - case CPU_TYPE_X86_64: - fArchitectureName = "x86_64"; - break; - case CPU_TYPE_ARM: - fArchitectureName = "arm"; - break; - default: - fArchitectureName = "unknown architecture"; - break; - } -} - -const char* Linker::architectureName() -{ - return fArchitectureName; -} - -bool Linker::showArchitectureInErrors() -{ - return fOptions.printArchPrefix(); -} - -bool Linker::isInferredArchitecture() -{ - return fArchitectureInferred; -} - -cpu_type_t Linker::inferArchitecture() -{ - // scan all input files, looking for a thin .o file. - // the first one found is presumably the architecture to link - uint8_t buffer[sizeof(mach_header_64)]; - std::vector& files = fOptions.getInputFiles(); - for (std::vector::iterator it = files.begin(); it != files.end(); ++it) { - int fd = ::open(it->path, O_RDONLY, 0); - if ( fd != -1 ) { - ssize_t amount = read(fd, buffer, sizeof(buffer)); - ::close(fd); - if ( amount >= (ssize_t)sizeof(buffer) ) { - if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch ppc based on %s", it->path); - return CPU_TYPE_POWERPC; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch ppc64 based on %s", it->path); - return CPU_TYPE_POWERPC64; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch i386 based on %s", it->path); - return CPU_TYPE_I386; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch x86_64 based on %s", it->path); - return CPU_TYPE_X86_64; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch arm based on %s", it->path); - return CPU_TYPE_ARM; - } - } - } - } - - // no thin .o files found, so default to same architecture this was built as - warning("-arch not specified"); -#if __ppc__ - return CPU_TYPE_POWERPC; -#elif __i386__ - return CPU_TYPE_I386; -#elif __ppc64__ - return CPU_TYPE_POWERPC64; -#elif __x86_64__ - return CPU_TYPE_X86_64; -#elif __arm__ - return CPU_TYPE_ARM; -#else - #error unknown default architecture -#endif -} - - -void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) -{ - fInputFiles.push_back(reader); - fDylibOptionsMap[reader] = info.options; -} - -void Linker::setOutputFile(ExecutableFile::Writer* writer) -{ - fOutputFile = writer; -} - -class InSet -{ -public: - InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fDeadAtoms.count(atom) != 0 ); - } - -private: - std::set& fDeadAtoms; -}; - -void Linker::loadAndResolve() -{ - fStartLoadAndResolveTime = mach_absolute_time(); - if ( fOptions.deadStrip() == Options::kDeadStripOff ) { - // without dead-code-stripping: - // find atoms to resolve all undefines - this->loadUndefines(); - // verify nothing is missing - this->checkUndefines(); - // once all undefines fulfill, then bind all references - this->resolveReferences(); - // remove atoms weak atoms that have been overridden - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - } - else { - // with dead code stripping: - // start binding references from roots, - this->deadStripResolve(); - // verify nothing is missing - this->checkUndefines(); - } -} - -void Linker::optimize() -{ - // give each reader a chance to do any optimizations - std::vector newAtoms; - std::vector additionalUndefines; - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, fNextInputOrdinal, fOutputFile, - fOptions.llvmOptions(), - fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(), - fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(), - fOptions.allowTextRelocs()); - } - - // add all newly created atoms to fAllAtoms and update symbol table - this->addAtoms(newAtoms); - - // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could - // not have their section set until now. - for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - if ( atom->getSection() == NULL ) - atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); - } - - // resolve new undefines - for(std::vector::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { - const char *targetName = *riter; - //fprintf(stderr, "LTO additional undefine: %s\n", targetName); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL) { - // mark that this symbol is needed - fGlobalSymbolTable.require(targetName); - // try to find it in some library - this->addJustInTimeAtoms(targetName); - } - } - - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - fLiveAtoms.clear(); - this->deadStripResolve(); - } - else { - this->checkUndefines(); - this->resolveReferences(); - } -} - -void Linker::link() -{ - this->buildAtomList(); - this->loadAndResolve(); - this->optimize(); - this->checkObjC(); - this->processDTrace(); - this->tweakLayout(); - this->sortSections(); - this->sortAtoms(); - this->writeDotOutput(); - this->collectDebugInfo(); - this->writeOutput(); - this->printStatistics(); - - if ( fOptions.pauseAtEnd() ) - sleep(10); -} - -void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) -{ - static uint64_t sUnitsPerSecond = 0; - if ( sUnitsPerSecond == 0 ) { - struct mach_timebase_info timeBaseInfo; - if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; - //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); - } - } - if ( partTime < sUnitsPerSecond ) { - uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; - uint32_t milliSeconds = milliSecondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; - uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); - } - else { - uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; - uint32_t seconds = secondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; - uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); - } -} - -char* Linker::commatize(uint64_t in, char* out) -{ - char* result = out; - char rawNum[30]; - sprintf(rawNum, "%llu", in); - const int rawNumLen = strlen(rawNum); - for(int i=0; i < rawNumLen-1; ++i) { - *out++ = rawNum[i]; - if ( ((rawNumLen-i) % 3) == 1 ) - *out++ = ','; - } - *out++ = rawNum[rawNumLen-1]; - *out = '\0'; - return result; -} - -void Linker::getVMInfo(vm_statistics_data_t& info) -{ - mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); - kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, - (host_info_t)&info, &count); - if (error != KERN_SUCCESS) { - bzero(&info, sizeof(vm_statistics_data_t)); - } -} - -void Linker::printStatistics() -{ - fEndTime = mach_absolute_time(); - if ( fOptions.printStatistics() ) { - vm_statistics_data_t endVMInfo; - getVMInfo(endVMInfo); - - uint64_t totalTime = fEndTime - fStartTime; - printTime("ld total time", totalTime, totalTime); - printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); - printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); - printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); - printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime); - printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime); - printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); - printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); - printTime(" write output", fEndTime - fStartWriteTime, totalTime); - fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, - endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); - char temp[40]; - fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp)); - fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp)); - fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded); - fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp)); - } -} - -inline void Linker::addAtom(ObjectFile::Atom& atom) -{ - // add to list of all atoms - fAllAtoms.push_back(&atom); - - if ( fOptions.deadStrip() == Options::kDeadStripOff ) { - // not dead-stripping code, so add atom's references's names to symbol table as to-be-resolved-later - std::vector& references = atom.getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) - fGlobalSymbolTable.require(reference->getTargetName()); - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) - fGlobalSymbolTable.require(reference->getFromTargetName()); - if ( reference->getTargetBinding() == ObjectFile::Reference::kDontBind ) - addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); - } - // update total size info (except for __ZEROPAGE atom) - if ( atom.getSegment().isContentReadable() ) { - fTotalSize += atom.getSize(); - if ( atom.isZeroFill() ) - fTotalZeroFillSize += atom.getSize(); - } - } - else { - if ( atom.dontDeadStrip() ) - fLiveRootAtoms.insert(&atom); - } - - // if in global namespace, add atom itself to symbol table - ObjectFile::Atom::Scope scope = atom.getScope(); - const char* name = atom.getName(); - if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { - // update scope based on export list - if ( fOptions.hasExportRestrictList() ) { - if ( scope == ObjectFile::Atom::scopeGlobal ) { - // check for globals that are downgraded to hidden - bool doExport = fOptions.shouldExport(name); - if ( !doExport ) { - atom.setScope(ObjectFile::Atom::scopeLinkageUnit); - } - } - else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { - // check for hiddens that were requested to be exported - if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { - warning("cannot export hidden symbol %s from %s", name, atom.getFile()->getPath()); - } - } - } - // add to symbol table - if ( fOptions.outputKind() == Options::kObjectFile ) { - // in ld -r mode don't add .eh symbols to symbol table - // instead kGroupSubordinate references will keep them paired - // with their functions. - const char* sectionName = atom.getSectionName(); - if ( (sectionName != NULL) && (strcmp(sectionName, "__eh_frame") != 0) ) - fGlobalSymbolTable.add(atom); - } - else { - fGlobalSymbolTable.add(atom); - } - } - - // record section orders so output file can have same order - if (atom.getSectionName()) - atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); -} - - -void Linker::markDead(ObjectFile::Atom* atom) -{ - fDeadAtoms.insert(atom); - // - // The kGroupSubordinate reference kind is used to model group comdat. - // The "signature" atom in the group has a kGroupSubordinate reference to - // all other members of the group. So, if the signature atom is - // coalesced away, all other atoms in the group should also be removed. - // - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX - ObjectFile::Atom* targetAtom = &(ref->getTarget()); - if ( targetAtom == NULL ) { - warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName()); - } - else { - if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) { - // ok for .eh symbols to be not static in -r mode - if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) ) - warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName()); - } - this->markDead(targetAtom); - } - } - } -} - -void Linker::updateConstraints(ObjectFile::Reader* reader) -{ - // check objc objects were compiled compatibly - ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); - if ( reader->getInstallPath() == NULL ) { - // adding a .o file - switch ( objcAddition ) { - case ObjectFile::Reader::kObjcNone: - break; - case ObjectFile::Reader::kObjcRetainRelease: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); - fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease; - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone ) - fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - break; - case ObjectFile::Reader::kObjcGC: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); - fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC; - break; - } - } - if ( reader->objcReplacementClasses() ) - fObjcReplacmentClasses = true; - - // check cpu sub-types for stricter sub-type - fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint); -} - -inline void Linker::addAtoms(std::vector& atoms) -{ - bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; - bool first = true; - for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { - // usually we only need to get the first atom's reader, but - // with -all_load all atoms from all .o files come come back together - // so we need to scan all atoms - if ( first || scanAll ) { - // update fReadersThatHaveSuppliedAtoms - ObjectFile::Reader* reader = (*it)->getFile(); - if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) - == fReadersThatHaveSuppliedAtoms.end() ) { - fReadersThatHaveSuppliedAtoms.push_back(reader); - updateConstraints(reader); - } - } - this->addAtom(**it); - first = false; - } -} - -void Linker::logArchive(ObjectFile::Reader* reader) -{ - if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) { - fArchiveReadersLogged.insert(reader); - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); - } -} - - -void Linker::buildAtomList() -{ - fStartBuildAtomsTime = mach_absolute_time(); - // add initial undefines from -u option - std::vector& initialUndefines = fOptions.initialUndefines(); - for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { - fGlobalSymbolTable.require(*it); - } - - // writer can contribute atoms - this->addAtoms(fOutputFile->getAtoms()); - - // each reader contributes atoms - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - std::vector& atoms = reader->getAtoms(); - this->addAtoms(atoms); - if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) ) - logArchive(reader); - } - - // extra command line section always at end - std::vector& extraSections = fOptions.extraSections(); - for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { - this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); - fNextInputOrdinal += it->dataLen; - } -} - -static const char* pathLeafName(const char* path) -{ - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - return path; - else - return &shortPath[1]; -} - -void Linker::loadUndefines() -{ - // keep looping until no more undefines were added in last loop - unsigned int undefineCount = 0xFFFFFFFF; - while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { - undefineCount = fGlobalSymbolTable.getRequireCount(); - std::vector undefineNames; - fGlobalSymbolTable.getNeededNames(false, undefineNames); - for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - const char* name = *it; - ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); - if ( (possibleAtom == NULL) - || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) - && (fOptions.outputKind() != Options::kObjectFile) - && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) { - std::vector* atoms = this->addJustInTimeAtoms(name); - if ( atoms != NULL ) - delete atoms; - } - } - } -} - -// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names -class ExportedObjcClass -{ -public: - ExportedObjcClass(Options& opt) : fOptions(opt) {} - - bool operator()(const char* name) const { - if ( fOptions.shouldExport(name) ) { - if ( strncmp(name, ".objc_class_name_", 17) == 0 ) - return true; - if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 ) - return true; - if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 ) - return true; - } - //fprintf(stderr, "%s is not exported\n", name); - return false; - } -private: - Options& fOptions; -}; - - -void Linker::checkUndefines() -{ - // error out on any remaining undefines - bool doPrint = true; - bool doError = true; - switch ( fOptions.undefinedTreatment() ) { - case Options::kUndefinedError: - break; - case Options::kUndefinedDynamicLookup: - doError = false; - break; - case Options::kUndefinedWarning: - doError = false; - break; - case Options::kUndefinedSuppress: - doError = false; - doPrint = false; - break; - } - std::vector unresolvableUndefines; - fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); - - // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names - // ignore unresolved references to Objc class names that are listed in -exported_symbols_list - if ( fOptions.hasExportRestrictList() ) - unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end()); - - const int unresolvableCount = unresolvableUndefines.size(); - int unresolvableExportsCount = 0; - if ( unresolvableCount != 0 ) { - if ( doPrint ) { - if ( fOptions.printArchPrefix() ) - fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); - else - fprintf(stderr, "Undefined symbols:\n"); - for (int i=0; i < unresolvableCount; ++i) { - const char* name = unresolvableUndefines[i]; - fprintf(stderr, " \"%s\", referenced from:\n", name); - // scan all atoms for references - bool foundAtomReference = false; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - if ( strcmp(reference->getTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } - } - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - if ( strcmp(reference->getFromTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } - } - } - } - // scan command line options - if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { - fprintf(stderr, " -exported_symbols_list command line option\n"); - ++unresolvableExportsCount; - } - } - } - if ( doError ) - throw "symbol(s) not found"; - } - - // for each tentative definition in symbol table look for dylib that exports same symbol name - if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - // look for dylibs that export same name as used by global tentative definition - addJustInTimeAtoms(atom->getName(), true); - } - } - } - - // if we have no weak symbols, see if we override some weak symbol in some dylib - if ( !fGlobalSymbolTable.hasExternalWeakDefinitions() ) { - bool done = false; - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); !done && (it != fGlobalSymbolTable.end()); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - const char* name = atom->getName(); - //fprintf(stderr, "looking for dylibs with a weak %s\n", name); - // look for dylibs with weak exports of the same name - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - ObjectFile::Reader* reader = it->second; - if ( reader->hasWeakExternals() ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - // if this is a weak definition in a dylib - if ( (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fGlobalSymbolTable.setHasExternalWeakDefinitions(); - done = true; - break; - } - } - } - } - } - } - } - -} - - - -std::vector* Linker::addJustInTimeAtoms(const char* name, bool dylibsOnly) -{ - // when creating final linked image, writer gets first chance - if ( fOptions.outputKind() != Options::kObjectFile ) { - std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); - return atoms; // found a definition, no need to search anymore - } - } - - // give readers a chance - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - // if this reader is a static archive that has the symbol we need, pull in all atoms in that module - // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. - //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); - bool isDylibReader = (reader->getInstallPath() != NULL); - if ( !dylibsOnly || isDylibReader ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) { - logArchive(reader); - } - // if this is a weak definition in a dylib - if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - // keep looking for a non-weak definition - } - else { - // found a definition, no need to search anymore - return atoms; - } - } - } - } - } - - // for two level namesapce, give all implicitly link dylibs a chance - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->implicitlyLinked() ) { - //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() ); - std::vector* atoms = it->second->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - // if this is a weak definition in a dylib - if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - // keep looking for a non-weak definition - } - else { - // found a definition, no need to search anymore - return atoms; - } - } - } - } - } - - // for flat namespace, give indirect dylibs - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) { - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( ! it->second->explicitlyLinked() ) { - std::vector* atoms = it->second->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - return atoms; // found a definition, no need to search anymore - } - } - } - } - - // writer creates a proxy in two cases: - // 1) ld -r is being used to create a .o file - // 2) -undefined dynamic_lookup is being used - // 3) -U _foo is being used - if ( (fOptions.outputKind() == Options::kObjectFile) - || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && !dylibsOnly) - || (fOptions.someAllowedUndefines() && !dylibsOnly) ) { - ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); - if ( atom != NULL ) { - this->addAtom(*atom); - return NULL; - } - } - //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); - return NULL; -} - -void Linker::resolve(ObjectFile::Reference* reference) -{ - // look in global symbol table - const char* targetName = reference->getTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { - fprintf(stderr, "Undefined symbol: %s\n", targetName); - } - reference->setTarget(*target, reference->getTargetOffset()); -} - -void Linker::resolveFrom(ObjectFile::Reference* reference) -{ - // handle references that have two (from and to) targets - const char* fromTargetName = reference->getFromTargetName(); - ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); - if ( fromTarget == NULL ) { - fprintf(stderr, "Undefined symbol: %s\n", fromTargetName); - } - reference->setFromTarget(*fromTarget); -} - - -void Linker::resolveReferences() -{ - // note: the atom list may grow during this loop as libraries supply needed atoms - for (unsigned int j=0; j < fAllAtoms.size(); ++j) { - ObjectFile::Atom* atom = fAllAtoms[j]; - std::vector& references = atom->getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) - this->resolve(reference); - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) - this->resolveFrom(reference); - } - } -} - - -// used to remove stabs associated with atoms that won't be in output file -class NotInSet -{ -public: - NotInSet(std::set& theSet) : fSet(theSet) {} - - bool operator()(const ObjectFile::Reader::Stab& stab) const { - if ( stab.atom == NULL ) - return false; // leave stabs that are not associated with any atome - else - return ( fSet.count(stab.atom) == 0 ); - } - -private: - std::set& fSet; -}; - - -class NotLive -{ -public: - NotLive(std::set& set) : fLiveAtoms(set) {} - - bool operator()(ObjectFile::Atom*& atom) const { - //if ( fLiveAtoms.count(atom) == 0 ) - // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); - return ( fLiveAtoms.count(atom) == 0 ); - } -private: - std::set& fLiveAtoms; -}; - - -void Linker::addJustInTimeAtomsAndMarkLive(const char* name) -{ - std::vector* atoms = this->addJustInTimeAtoms(name); - if ( atoms != NULL ) { - if ( fOptions.allGlobalsAreDeadStripRoots() ) { - for (std::vector::iterator it=atoms->begin(); it != atoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.name = atom->getDisplayName(); - this->markLive(*atom, &rootChain); - } - } - } - delete atoms; - } -} - -void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) -{ - if ( fLiveAtoms.count(&atom) == 0 ) { - // if -whylive cares about this symbol, then dump chain - if ( (previous->name != NULL) && fOptions.printWhyLive(previous->name) ) { - int depth = 0; - for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { - for(int i=depth; i > 0; --i) - fprintf(stderr, " "); - fprintf(stderr, "%s\n", p->name); - } - } - // set up next chain - WhyLiveBackChain thisChain; - thisChain.previous = previous; - // this atom is live - fLiveAtoms.insert(&atom); - // update total size info (except for __ZEROPAGE atom) - if ( atom.getSegment().isContentReadable() ) { - fTotalSize += atom.getSize(); - if ( atom.isZeroFill() ) - fTotalZeroFillSize += atom.getSize(); - } - // and all atoms it references - std::vector& references = atom.getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - // look in global symbol table - const char* targetName = reference->getTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { - // load archives or dylibs - this->addJustInTimeAtomsAndMarkLive(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setTarget(*target, reference->getTargetOffset()); - } - else { - // mark as undefined, for later error processing - fAtomsWithUnresolvedReferences.push_back(&atom); - fGlobalSymbolTable.require(targetName); - } - } - switch ( reference->getTargetBinding() ) { - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - thisChain.name = reference->getTargetName(); - markLive(reference->getTarget(), &thisChain); - break; - case ObjectFile::Reference::kDontBind: - addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); - break; - case ObjectFile::Reference::kUnboundByName: - // do nothing - break; - } - // do the same as above, for "from target" - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - // look in global symbol table - const char* targetName = reference->getFromTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { - // load archives or dylibs - this->addJustInTimeAtomsAndMarkLive(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setFromTarget(*target); - } - else { - // mark as undefined, for later error processing - fGlobalSymbolTable.require(targetName); - } - } - switch ( reference->getFromTargetBinding() ) { - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - thisChain.name = reference->getFromTargetName(); - markLive(reference->getFromTarget(), &thisChain); - break; - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - // do nothing - break; - } - } - } -} - - -void Linker::addLiveRoot(const char* name) -{ - ObjectFile::Atom* target = fGlobalSymbolTable.find(name); - if ( target == NULL ) { - this->addJustInTimeAtomsAndMarkLive(name); - target = fGlobalSymbolTable.find(name); - } - if ( target != NULL ) - fLiveRootAtoms.insert(target); -} - - -void Linker::deadStripResolve() -{ - // add main() to live roots - ObjectFile::Atom* entryPoint = this->entryPoint(false); - if ( entryPoint != NULL ) - fLiveRootAtoms.insert(entryPoint); - - // add dyld_stub_binding_helper() to live roots - ObjectFile::Atom* dyldHelper = this->dyldHelper(); - if ( dyldHelper != NULL ) - fLiveRootAtoms.insert(dyldHelper); - - // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots - if ( fOptions.usingLazyDylibLinking() ) { - ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper(); - if ( dyldLazyDylibHelper != NULL ) - fLiveRootAtoms.insert(dyldLazyDylibHelper); - } - - // add -exported_symbols_list, -init, and -u entries to live roots - std::vector& initialUndefines = fOptions.initialUndefines(); - for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) - addLiveRoot(*it); - - // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots - // - if ( fOptions.hasWildCardExportRestrictList() ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) - && (fDeadAtoms.count(atom) == 0) - && fOptions.shouldExport(atom->getName()) ) - fLiveRootAtoms.insert(atom); - } - } - - // in some cases, every global scope atom in initial .o files is a root - if ( fOptions.allGlobalsAreDeadStripRoots() ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) - fLiveRootAtoms.insert(atom); - } - } - - // mark all roots as live, and all atoms they reference - for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.name = (*it)->getDisplayName(); - markLive(**it, &rootChain); - } - - // it is possible that there are unresolved references that can be resolved now - // this can happen if the first reference to a common symbol in an archive. - // common symbols are not in the archive TOC, but the .o could have been pulled in later. - // ld64 while linking cc1 [ when dead_strip is ON] - for (std::vector::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { - std::vector& references = (*it)->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); - if ( target != NULL ) { - reference->setTarget(*target, reference->getTargetOffset()); - fLiveAtoms.insert(target); - // by just adding this atom to fLiveAtoms set, we are assuming it has no - // references, which is true for commons. - if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) - warning("internal error %s is not a tentative definition", target->getDisplayName()); - } - } - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); - if ( target != NULL ) { - reference->setFromTarget(*target); - fLiveAtoms.insert(target); - // by just adding this atom to fLiveAtoms set, we are assuming it has no - // references, which is true for commons. - if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) - warning("internal error %s is not a tentative definition", target->getDisplayName()); - } - } - } - } - - // now remove all non-live atoms from fAllAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); -} - -void Linker::checkObjC() -{ - // check dylibs - switch ( fCurrentObjCConstraint ) { - case ObjectFile::Reader::kObjcNone: - // can link against any dylib - break; - case ObjectFile::Reader::kObjcRetainRelease: - // cannot link against GC-only dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->explicitlyLinked() ) { - if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC ) - throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath()); - } - } - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - // can link against GC or RR dylibs - break; - case ObjectFile::Reader::kObjcGC: - // cannot link against RR-only dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->explicitlyLinked() ) { - if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease ) - throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath()); - } - } - break; - } - - // synthesize __OBJC __image_info atom if needed - if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) { - this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses)); - } -} - -void Linker::addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName) -{ - if ( probeName != NULL ) { - if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) - fDtraceProbeSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) - fDtraceIsEnabledSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) - fDtraceAtomToTypes[&atom].insert(probeName); - else if ( fOptions.dTrace() && (strncmp(probeName, "__dtrace_probe$", 15) == 0) ) - fDtraceProbes.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); - } -} - -static uint8_t pointerKind(cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - return ppc::kPointer; - case CPU_TYPE_POWERPC64: - return ppc64::kPointer; - case CPU_TYPE_I386: - return x86::kPointer; - case CPU_TYPE_X86_64: - return x86_64::kPointer; - case CPU_TYPE_ARM: - return arm::kPointer; - } - throw "uknown architecture"; -} - -static uint8_t pcRelKind(cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - return ppc::kPointerDiff32; - case CPU_TYPE_POWERPC64: - return ppc64::kPointerDiff32; - case CPU_TYPE_I386: - return x86::kPointerDiff; - case CPU_TYPE_X86_64: - return x86_64::kPointerDiff32; - case CPU_TYPE_ARM: - return arm::kPointerDiff; - } - throw "uknown architecture"; -} - -typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); -typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); - - -void Linker::processDTrace() -{ - // handle dtrace 2.0 static probes - if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) { - // partition probes by provider name - // The symbol names looks like: - // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] - // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] - ProviderToProbes providerToProbes; - std::vector emptyList; - for(std::vector::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[16]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - for(std::vector::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[20]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - - // create a DOF section for each provider - int dofIndex=1; - CStringSet sectionNamesUsed; - for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { - const char* providerName = pit->first; - const std::vector& probes = pit->second; - - // open library and find dtrace_create_dof() - void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); - if ( handle == NULL ) - throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); - createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); - if ( pCreateDOF == NULL ) - throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); - // build list of typedefs/stability infos for this provider - CStringSet types; - for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { - std::map::iterator pos = fDtraceAtomToTypes.find(it->atom); - if ( pos != fDtraceAtomToTypes.end() ) { - for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { - const char* providerStart = strchr(*sit, '$')+1; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char aProviderName[providerEnd-providerStart+1]; - strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); - if ( strcmp(aProviderName, providerName) == 0 ) - types.insert(*sit); - } - } - } - } - int typeCount = types.size(); - const char* typeNames[typeCount]; - //fprintf(stderr, "types for %s:\n", providerName); - uint32_t index = 0; - for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { - typeNames[index] = *it; - //fprintf(stderr, "\t%s\n", *it); - ++index; - } - - // build list of probe/isenabled sites - const uint32_t probeCount = probes.size(); - const char* probeNames[probeCount]; - const char* funtionNames[probeCount]; - uint64_t offsetsInDOF[probeCount]; - index = 0; - for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { - probeNames[index] = it->probeName; - funtionNames[index] = it->atom->getName(); - offsetsInDOF[index] = 0; - ++index; - } - //fprintf(stderr, "calling libtrace to create DOF\n"); - //for(uint32_t i=0; i < probeCount; ++i) - // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); - // call dtrace library to create DOF section - size_t dofSectionSize; - uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); - if ( p != NULL ) { - char sectionName[18]; - strcpy(sectionName, "__dof_"); - strlcpy(§ionName[6], providerName, 10); - // create unique section name so each DOF is in its own section - if ( sectionNamesUsed.count(sectionName) != 0 ) { - sectionName[15] = '0'; - sectionName[16] = '\0'; - while ( sectionNamesUsed.count(sectionName) != 0 ) - ++sectionName[15]; - } - sectionNamesUsed.insert(sectionName); - char symbolName[strlen(providerName)+64]; - sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); - opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, - "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); - fNextInputOrdinal += dofSectionSize; - // add references - for (uint32_t i=0; i < probeCount; ++i) { - uint64_t offset = offsetsInDOF[i]; - //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); - if ( offset > dofSectionSize ) - throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); - reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); - } - this->addAtoms(reader->getAtoms()); - } - else { - throw "error creating dtrace DOF section"; - } - } - } - // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files - else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) { - const uint32_t probeCount = fDtraceProbes.size(); - const char* labels[probeCount]; - const char* funtionNames[probeCount]; - uint64_t offsetsInDOF[probeCount]; - - // open libray and find dtrace_ld64_create_dof() - void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); - if ( handle == NULL ) - throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror()); - oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof"); - if ( pCreateDOF == NULL ) - throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror()); - - // build argument list - uint32_t index = 0; - for(std::vector::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) { - labels[index] = it->probeName; - funtionNames[index] = it->atom->getName(); - offsetsInDOF[index] = 0; - ++index; - } - size_t dofSectionSize; - // call dtrace library to create DOF section - uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize); - if ( p != NULL ) { - opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal); - fNextInputOrdinal += dofSectionSize; - // add references - for (uint32_t i=0; i < probeCount; ++i) { - uint64_t offset = offsetsInDOF[i]; - if ( offset > dofSectionSize ) - throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX", i, offset, dofSectionSize); - reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset); - } - this->addAtoms(reader->getAtoms()); - } - else { - throw "error created dtrace DOF section"; - } - } -} - - -static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName) -{ - if ( objectFileLeafName == NULL ) - return true; - const char* atomFullPath = atom->getFile()->getPath(); - const char* lastSlash = strrchr(atomFullPath, '/'); - if ( lastSlash != NULL ) { - if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) - return true; - } - else { - if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) - return true; - } - return false; -} - - -static bool usesAnonymousNamespace(const char* symbol) -{ - return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) ); -} - - -// -// convert: -// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv -// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv -// -static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[]) -{ - const char* globPtr = strstr(inSymbol, "_GLOBAL__N_"); - while ( isdigit(*(--globPtr)) ) - ; // loop - char* endptr; - unsigned long length = strtoul(globPtr+1, &endptr, 10); - const char* globEndPtr = endptr + length; - int startLen = globPtr-inSymbol+1; - memcpy(outSymbol, inSymbol, startLen); - outSymbol[startLen] = '-'; - strcpy(&outSymbol[startLen+1], globEndPtr); -} - - -ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) -{ - ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); - if ( atom != NULL ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) - return atom; - } - else { - // slow case. The requested symbol is not in symbol table, so might be static function - static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; - static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace; - static bool built = false; - // build a hash_map the first time - if ( !built ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - const char* name = atom->getName(); - if ( name != NULL) { - if ( usesAnonymousNamespace(name) ) { - // symbol that uses anonymous namespace - char canonicalName[strlen(name)+2]; - canonicalizeAnonymousName(name, canonicalName); - const char* hashName = strdup(canonicalName); - SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName); - if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() ) - hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom; - else - hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL - } - else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // static function or data - SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); - if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) - hashTableOfTranslationUnitScopedSymbols[name] = atom; - else - hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL - } - } - } - //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size()); - built = true; - } - - // look for name in hashTableOfTranslationUnitScopedSymbols - SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName); - if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) { - if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { - //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); - return pos->second; - } - if ( pos->second == NULL ) - // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - const char* name = atom->getName(); - if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { - if ( fOptions.printOrderFileStatistics() ) - warning("%s specified in order_file but it exists in multiple .o files. " - "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); - return atom; - } - } - } - } - } - - // look for name in hashTableOfSymbolsWithAnonymousNamespace - if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) { - // symbol that uses anonymous namespace - char canonicalName[strlen(orderedSymbol.symbolName)+2]; - canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName); - SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName); - if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) { - if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { - //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName); - return pos->second; - } - if ( pos->second == NULL ) - // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - const char* name = atom->getName(); - if ( (name != NULL) && usesAnonymousNamespace(name) ) { - char canonicalAtomName[strlen(name)+2]; - canonicalizeAnonymousName(name, canonicalAtomName); - if ( strcmp(canonicalAtomName, canonicalName) == 0 ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { - if ( fOptions.printOrderFileStatistics() ) - warning("%s specified in order_file but it exists in multiple .o files. " - "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); - return atom; - } - } - } - } - } - } - } - return NULL; -} - - -void Linker::sortSections() -{ - Section::assignIndexes(); -} - - -// -// Linker::sortAtoms() -// -// The purpose of this method is to take the graph of all Atoms and produce an ordered -// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must -// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified -// in an order_file are seqenced as in the order_file and before Atoms not specified, -// 4) Atoms in the same section from the same .o file should be contiguous and sequenced -// in the same order they were in the .o file, 5) Atoms in the same Section but which came -// from different .o files should be sequenced in the same order that the .o files -// were passed to the linker (i.e. command line order). -// -// The way this is implemented is that the linker passes a "base ordinal" to each Reader -// as it is constructed. The reader should construct it Atoms so that calling getOrdinal() -// on its atoms returns a contiguous range of values starting at the base ordinal. Then -// sorting is just sorting by section, then by ordinal. -// -// If an order_file is specified, it gets more complicated. First, an override-ordinal map -// is created. It causes the sort routine to ignore the value returned by getOrdinal() and -// use the override value instead. Next some Atoms must be layed out consecutively -// (e.g. hand written assembly that does not end with return, but rather falls into -// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of -// kFollowOn refernces produces "clusters" of atoms that must stay together. -// If an order_file tries to move one atom, it may need to move a whole cluster. The -// algorithm to do this models clusters using two maps. The "starts" maps maps any -// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a -// cluster to the next Atom in the cluster. With this in place, while processing an -// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is -// given ordinal overrides. -// -void Linker::sortAtoms() -{ - fStartSortTime = mach_absolute_time(); - // if -order_file is used, build map of atom ordinal overrides - std::map* ordinalOverrideMap = NULL; - std::map theOrdinalOverrideMap; - const bool log = false; - if ( fOptions.orderedSymbols().size() != 0 ) { - // first make a pass to find all follow-on references and build start/next maps - // which are a way to represent clusters of atoms that must layout together - std::map followOnStarts; - std::map followOnNexts; - for (std::vector::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) { - ObjectFile::Atom* atom = *ait; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == 1 ) { // FIX FIX - ObjectFile::Atom* targetAtom = &ref->getTarget(); - if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName()); - std::map::iterator startFrom = followOnStarts.find(atom); - std::map::iterator startTo = followOnStarts.find(targetAtom); - if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) { - // this is first time we've seen either atom, make simple cluster of the two - if ( log ) fprintf(stderr, " new cluster\n"); - followOnStarts[atom] = atom; - followOnStarts[targetAtom] = atom; - followOnNexts[atom] = targetAtom; - followOnNexts[targetAtom] = NULL; - } - else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) { - // atom is at end of an existing cluster, so append target to end of cluster - if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName()); - followOnNexts[atom] = targetAtom; - followOnNexts[targetAtom] = NULL; - followOnStarts[targetAtom] = followOnStarts[atom]; - } - else { - // gerneral case of inserting into an existing cluster - if ( followOnNexts[atom] != NULL ) { - // an atom with two follow-ons is illegal - warning("can't order %s because both %s and %s must follow it", - atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName()); - } - else { - // there already exists an atom that says target must be its follow-on - const ObjectFile::Atom* originalStart = startTo->second; - const ObjectFile::Atom* originalPrevious = originalStart; - while ( followOnNexts[originalPrevious] != targetAtom ) - originalPrevious = followOnNexts[originalPrevious]; - bool otherIsAlias = (originalPrevious->getSize() == 0); - bool thisIsAlias = (atom->getSize() == 0); - if ( !otherIsAlias && !thisIsAlias ) { - warning("can't order %s because both %s and %s must preceed it", - targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName()); - } - else if ( otherIsAlias ) { - if ( originalPrevious == originalStart ) { - // other is alias at start of cluster, make this the new start of cluster - if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName()); - followOnNexts[atom] = originalPrevious; - for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) - followOnStarts[nextAtom] = atom; - } - else { - // other is alias in middle of cluster, insert new atom before it - if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); - followOnStarts[atom] = originalStart; - followOnNexts[atom] = originalPrevious; - for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) { - if ( followOnNexts[a] == originalPrevious ) { - followOnNexts[a] = atom; - break; - } - } - } - } - else { - // this is alias, so it can go inbetween originalPrevious and targetAtom - if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); - followOnStarts[atom] = originalStart; - followOnNexts[atom] = followOnNexts[originalPrevious]; - followOnNexts[originalPrevious] = atom; - } - } - } - } - } - } - - if ( log ) { - for(std::map::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it) - fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName()); - - for(std::map::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it) - fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null"); - } - - // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals - ordinalOverrideMap = &theOrdinalOverrideMap; - uint32_t index = 0; - uint32_t matchCount = 0; - std::vector& orderedSymbols = fOptions.orderedSymbols(); - for(std::vector::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) { - ObjectFile::Atom* atom = this->findAtom(*it); - if ( atom != NULL ) { - std::map::iterator start = followOnStarts.find(atom); - if ( start != followOnStarts.end() ) { - // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together - for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) { - std::map::iterator pos = theOrdinalOverrideMap.find(nextAtom); - if ( pos == theOrdinalOverrideMap.end() ) { - theOrdinalOverrideMap[nextAtom] = index++; - if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath()); - } - else { - if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", - atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] ); - } - } - } - else { - theOrdinalOverrideMap[atom] = index; - if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - ++matchCount; - //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName); - } - ++index; - } - if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) { - warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() ); - } - } - - // sort atoms - std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap)); - - //fprintf(stderr, "Sorted atoms:\n"); - //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName()); - //} -} - - -// make sure given addresses are within reach of branches, etc -void Linker::tweakLayout() -{ - // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB - if ( fTotalSize > 0x7F000000 ) { - fBiggerThanTwoGigOutput = true; - - if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 ) - throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); - - // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment - Section* hugeZeroFills = Section::find("__huge", "__DATA", true); - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) ) - atom->setSection(hugeZeroFills); - } - } -} - - -void Linker::writeDotOutput() -{ - const char* dotOutFilePath = fOptions.dotOutputFile(); - if ( dotOutFilePath != NULL ) { - FILE* out = fopen(dotOutFilePath, "w"); - if ( out != NULL ) { - // print header - fprintf(out, "digraph dg\n{\n"); - fprintf(out, "\tconcentrate = true;\n"); - fprintf(out, "\trankdir = LR;\n"); - - // print each atom as a node - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() != fOutputFile ) { - const char* name = atom->getDisplayName(); - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); - } - else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { - char cstring[atom->getSize()+2]; - atom->copyRawContent((uint8_t*)cstring); - fprintf(out, "\taddr%p [ label = \"string: '", atom); - for (const char* s=cstring; *s != '\0'; ++s) { - if ( *s == '\n' ) - fprintf(out, "\\\\n"); - else - fputc(*s, out); - } - fprintf(out, "'\" ];\n"); - } - else { - fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name); - } - } - } - fprintf(out, "\n"); - - // print each reference as an edge - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* fromAtom = *it; - if ( fromAtom->getFile() != fOutputFile ) { - std::vector& references = fromAtom->getReferences(); - std::set seenTargets; - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - ObjectFile::Atom* toAtom = &(reference->getTarget()); - if ( seenTargets.count(toAtom) == 0 ) { - seenTargets.insert(toAtom); - fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); - } - } - } - } - fprintf(out, "\n"); - - // push all imports to bottom of graph - fprintf(out, "{ rank = same; "); - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() != fOutputFile ) - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fprintf(out, "addr%p; ", atom); - } - } - fprintf(out, "};\n "); - - // print footer - fprintf(out, "}\n"); - fclose(out); - } - else { - warning("could not write dot output file: %s", dotOutFilePath); - } - } -} - -ObjectFile::Atom* Linker::entryPoint(bool orInit) -{ - // if main executable, find entry point atom - ObjectFile::Atom* entryPoint = NULL; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDyld: - entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); - if ( entryPoint == NULL ) { - throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); - } - break; - case Options::kDynamicLibrary: - if ( orInit && (fOptions.initFunctionName() != NULL) ) { - entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); - if ( entryPoint == NULL ) { - throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); - } - } - break; - case Options::kObjectFile: - case Options::kDynamicBundle: - entryPoint = NULL; - break; - } - return entryPoint; -} - -ObjectFile::Atom* Linker::dyldHelper() -{ - return fGlobalSymbolTable.find("dyld_stub_binding_helper"); -} - -ObjectFile::Atom* Linker::dyldLazyLibraryHelper() -{ - return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper"); -} - -const char* Linker::assureFullPath(const char* path) -{ - if ( path[0] == '/' ) - return path; - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - char* result; - asprintf(&result, "%s/%s", cwdbuff, path); - if ( result != NULL ) - return result; - } - return path; -} - - -// -// The stab strings are of the form: -// ':' -// but the contain a colon. -// For C++ may contain a double colon (e.g. std::string:f(0,1) ) -// For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) -// -const char* Linker::truncateStabString(const char* str) -{ - enum { start, inObjc } state = start; - for (const char* s = str; *s != 0; ++s) { - char c = *s; - switch (state) { - case start: - if ( c == '[' ) { - state = inObjc; - } - else { - if ( c == ':' ) { - if ( s[1] == ':' ) { - ++s; - } - else { - // found colon - // Duplicate strndup behavior here. - int trunStrLen = s-str+2; - char* temp = new char[trunStrLen+1]; - memcpy(temp, str, trunStrLen); - temp[trunStrLen] = '\0'; - return temp; - } - } - } - break; - case inObjc: - if ( c == ']' ) { - state = start; - } - break; - } - } - // malformed - return str; -} - - -bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) -{ - switch(stab.type){ - case N_GSYM: - case N_STSYM: - case N_LCSYM: - case N_FUN: - // these all need truncated strings - stab.string = truncateStabString(stab.string); - return true; - case N_SO: - case N_OSO: - case N_OPT: - case N_SOL: - // these are included in the minimal stabs, but they keep their full string - return true; - default: - return false; - } -} - - -struct HeaderRange { - std::vector::iterator begin; - std::vector::iterator end; - int parentRangeIndex; - uint32_t sum; - bool sumPrecomputed; - bool useEXCL; - bool cannotEXCL; // because of SLINE, etc stabs -}; - - -typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> PathToSums; - -// hash table that maps header path to a vector of known checksums for that path -static PathToSums sKnownBINCLs; - - -void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) -{ - const bool log = false; - bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); - std::vector* readerStabs = reader->getStabs(); - if ( readerStabs == NULL ) - return; - - if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); - std::vector ranges; - int curRangeIndex = -1; - int count = 0; - ObjectFile::Atom* atomWithLowestOrdinal = NULL; - ObjectFile::Atom* atomWithHighestOrdinal = NULL; - uint32_t highestOrdinal = 0; - uint32_t lowestOrdinal = UINT_MAX; - std::vector > soRanges; - // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums - // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - ++count; - switch ( it->type ) { - case N_BINCL: - { - HeaderRange range; - range.begin = it; - range.end = readerStabs->end(); - range.parentRangeIndex = curRangeIndex; - range.sum = it->value; - range.sumPrecomputed = (range.sum != 0); - range.useEXCL = false; - range.cannotEXCL = false; - curRangeIndex = ranges.size(); - if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string); - ranges.push_back(range); - } - break; - case N_EINCL: - if ( curRangeIndex == -1 ) { - warning("EINCL missing BINCL in %s", reader->getPath()); - } - else { - ranges[curRangeIndex].end = it+1; - if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); - curRangeIndex = ranges[curRangeIndex].parentRangeIndex; - } - break; - case N_FUN: - { - std::map::iterator pos = atomOrdinals.find(it->atom); - if ( pos != atomOrdinals.end() ) { - uint32_t ordinal = pos->second; - if ( ordinal > highestOrdinal ) { - highestOrdinal = ordinal; - atomWithHighestOrdinal = it->atom; - } - if ( ordinal < lowestOrdinal ) { - lowestOrdinal = ordinal; - atomWithLowestOrdinal = it->atom; - } - } - } - // fall through - case N_BNSYM: - case N_ENSYM: - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - case N_STSYM: - case N_LCSYM: - if ( curRangeIndex != -1 ) { - ranges[curRangeIndex].cannotEXCL = true; - if ( fOptions.warnStabs() ) - warning("cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); - } - break; - case N_SO: - if ( (it->string != NULL) && (strlen(it->string) > 0) ) { - // start SO, reset hi/low FUN tracking - atomWithLowestOrdinal = NULL; - atomWithHighestOrdinal = NULL; - highestOrdinal = 0; - lowestOrdinal = UINT_MAX; - } - else { - // end SO, record hi/low atoms for this SO range - soRanges.push_back(std::make_pair(atomWithLowestOrdinal, atomWithHighestOrdinal)); - } - // fall through - default: - if ( curRangeIndex != -1 ) { - if ( ! ranges[curRangeIndex].sumPrecomputed ) { - uint32_t sum = 0; - const char* s = it->string; - char c; - while ( (c = *s++) != 0 ) { - sum += c; - // don't checkusm first number (file index) after open paren in string - if ( c == '(' ) { - while(isdigit(*s)) - ++s; - } - } - ranges[curRangeIndex].sum += sum; - } - } - - } - } - if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); - if ( curRangeIndex != -1 ) - warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath()); - - // if no BINCLs - if ( ranges.size() == 0 ) { - unsigned int soIndex = 0; - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - // copy minimal or all stabs - ObjectFile::Reader::Stab stab = *it; - if ( !minimal || minimizeStab(stab) ) { - if ( stab.type == N_SO ) { - if ( soIndex < soRanges.size() ) { - if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { - // starting SO is associated with first atom - stab.atom = soRanges[soIndex].first; - } - else { - // ending SO is associated with last atom - stab.atom = soRanges[soIndex].second; - ++soIndex; - } - } - } - fStabs.push_back(stab); - } - } - return; - } - - //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); - //for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { - // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); - //} - - // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL - for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { - if ( ! it->cannotEXCL ) { - const char* header = it->begin->string; - uint32_t sum = it->sum; - PathToSums::iterator pos = sKnownBINCLs.find(header); - if ( pos != sKnownBINCLs.end() ) { - std::vector& sums = pos->second; - for(std::vector::iterator sit=sums.begin(); sit != sums.end(); ++sit) { - if (*sit == sum) { - //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath()); - it->useEXCL = true; - break; - } - } - if ( ! it->useEXCL ) { - // have seen this path, but not this checksum - //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header); - sums.push_back(sum); - } - } - else { - // have not seen this path, so add to known BINCLs - std::vector empty; - sKnownBINCLs[header] = empty; - sKnownBINCLs[header].push_back(sum); - //fprintf(stderr, "registering checksum %08X for %s\n", sum, header); - } - } - } - - // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs - curRangeIndex = -1; - const int maxRangeIndex = ranges.size(); - int soIndex = 0; - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - switch ( it->type ) { - case N_BINCL: - for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) { - if ( ranges[i].begin == it ) { - curRangeIndex = i; - HeaderRange& range = ranges[curRangeIndex]; - ObjectFile::Reader::Stab stab = *it; - stab.value = range.sum; // BINCL and EXCL have n_value set to checksum - if ( range.useEXCL ) - stab.type = N_EXCL; // transform BINCL into EXCL - if ( !minimal ) - fStabs.push_back(stab); - break; - } - } - break; - case N_EINCL: - if ( curRangeIndex != -1 ) { - if ( !ranges[curRangeIndex].useEXCL && !minimal ) - fStabs.push_back(*it); - curRangeIndex = ranges[curRangeIndex].parentRangeIndex; - } - break; - default: - if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { - ObjectFile::Reader::Stab stab = *it; - if ( !minimal || minimizeStab(stab) ) { - if ( stab.type == N_SO ) { - if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { - // starting SO is associated with first atom - stab.atom = soRanges[soIndex].first; - } - else { - // ending SO is associated with last atom - stab.atom = soRanges[soIndex].second; - ++soIndex; - } - } - fStabs.push_back(stab); - } - } - } - } - -} - - -// used to prune out atoms that don't need debug notes generated -class NoDebugNoteAtom -{ -public: - NoDebugNoteAtom(const std::map& readersWithDwarfOrdinals) - : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} - - bool operator()(const ObjectFile::Atom* atom) const { - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - return true; - if ( atom->getName() == NULL ) - return true; - if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) - return true; - return false; - } - -private: - const std::map& fReadersWithDwarfOrdinals; -}; - -// used to sort atoms with debug notes -class ReadersWithDwarfSorter -{ -public: - ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, - const std::map& atomOrdinals) - : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} - - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const - { - // first sort by reader - unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; - unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; - if ( leftReaderIndex != rightReaderIndex ) - return (leftReaderIndex < rightReaderIndex); - - // then sort by atom ordinal - unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; - unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; - return leftAtomIndex < rightAtomIndex; - } - -private: - const std::map& fReadersWithDwarfOrdinals; - const std::map& fAtomOrdinals; -}; - - - - - -void Linker::synthesizeDebugNotes(std::vector& allAtomsByReader) -{ - // synthesize "debug notes" and add them to master stabs vector - const char* dirPath = NULL; - const char* filename = NULL; - bool wroteStartSO = false; - bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL); - __gnu_cxx::hash_set, CStringEquals> seenFiles; - for (std::vector::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { - ObjectFile::Atom* atom = *it; - const char* newDirPath; - const char* newFilename; - //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); - if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { - // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) - asprintf((char**)&newDirPath, "%s/", newDirPath); - if ( filename != NULL ) { - // translation unit change, emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } - // new translation unit, emit start SO's - ObjectFile::Reader::Stab dirPathStab; - dirPathStab.atom = NULL; - dirPathStab.type = N_SO; - dirPathStab.other = 0; - dirPathStab.desc = 0; - dirPathStab.value = 0; - dirPathStab.string = newDirPath; - fStabs.push_back(dirPathStab); - ObjectFile::Reader::Stab fileStab; - fileStab.atom = NULL; - fileStab.type = N_SO; - fileStab.other = 0; - fileStab.desc = 0; - fileStab.value = 0; - fileStab.string = newFilename; - fStabs.push_back(fileStab); - // Synthesize OSO for start of file - ObjectFile::Reader::Stab objStab; - objStab.atom = NULL; - objStab.type = N_OSO; - objStab.other = 0; - objStab.desc = 1; - objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); - objStab.string = assureFullPath(atom->getFile()->getPath()); - fStabs.push_back(objStab); - wroteStartSO = true; - // add the source file path to seenFiles so it does not show up in SOLs - seenFiles.insert(newFilename); - } - filename = newFilename; - dirPath = newDirPath; - if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - // Synthesize BNSYM and start FUN stabs - ObjectFile::Reader::Stab beginSym; - beginSym.atom = atom; - beginSym.type = N_BNSYM; - beginSym.other = 1; - beginSym.desc = 0; - beginSym.value = 0; - beginSym.string = ""; - fStabs.push_back(beginSym); - ObjectFile::Reader::Stab startFun; - startFun.atom = atom; - startFun.type = N_FUN; - startFun.other = 1; - startFun.desc = 0; - startFun.value = 0; - startFun.string = atom->getName(); - fStabs.push_back(startFun); - // Synthesize any SOL stabs needed - std::vector* lineInfo = atom->getLineInfo(); - if ( lineInfo != NULL ) { - const char* curFile = NULL; - for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { - if ( it->fileName != curFile ) { - if ( seenFiles.count(it->fileName) == 0 ) { - seenFiles.insert(it->fileName); - ObjectFile::Reader::Stab sol; - sol.atom = 0; - sol.type = N_SOL; - sol.other = 0; - sol.desc = 0; - sol.value = 0; - sol.string = it->fileName; - fStabs.push_back(sol); - } - curFile = it->fileName; - } - } - } - // Synthesize end FUN and ENSYM stabs - ObjectFile::Reader::Stab endFun; - endFun.atom = atom; - endFun.type = N_FUN; - endFun.other = 0; - endFun.desc = 0; - endFun.value = 0; - endFun.string = ""; - fStabs.push_back(endFun); - ObjectFile::Reader::Stab endSym; - endSym.atom = atom; - endSym.type = N_ENSYM; - endSym.other = 1; - endSym.desc = 0; - endSym.value = 0; - endSym.string = ""; - fStabs.push_back(endSym); - } - else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) { - // no stabs for atoms that would not be in the symbol table - } - else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { - // no stabs for absolute symbols - } - else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) { - // no stabs for .eh atoms - } - else { - ObjectFile::Reader::Stab globalsStab; - const char* name = atom->getName(); - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // Synthesize STSYM stab for statics - globalsStab.atom = atom; - globalsStab.type = N_STSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - else { - // Synthesize GSYM stab for other globals - globalsStab.atom = atom; - globalsStab.type = N_GSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } - } - } - - if ( wroteStartSO ) { - // emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } -} - - - - -void Linker::collectDebugInfo() -{ - std::map atomOrdinals; - fStartDebugTime = mach_absolute_time(); - if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { - - // determine mixture of stabs and dwarf - bool someStabs = false; - bool someDwarf = false; - for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - switch ( reader->getDebugInfoKind() ) { - case ObjectFile::Reader::kDebugInfoNone: - break; - case ObjectFile::Reader::kDebugInfoStabs: - someStabs = true; - break; - case ObjectFile::Reader::kDebugInfoDwarf: - someDwarf = true; - fCreateUUID = true; - break; - case ObjectFile::Reader::kDebugInfoStabsUUID: - someStabs = true; - fCreateUUID = true; - break; - default: - throw "Unhandled type of debug information"; - } - } - } - - if ( someDwarf || someStabs ) { - // try to minimize re-allocations - fStabs.reserve(1024); - - // make mapping from atoms to ordinal - uint32_t ordinal = 1; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atomOrdinals[*it] = ordinal++; - } - } - - // process all dwarf .o files as a batch - if ( someDwarf ) { - // make mapping from readers with dwarf to ordinal - std::map readersWithDwarfOrdinals; - uint32_t readerOrdinal = 1; - for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { - readersWithDwarfOrdinals[reader] = readerOrdinal++; - } - } - - // make a vector of atoms - std::vector allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); - // remove those not from a reader that has dwarf - allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), - NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); - // sort by reader then atom ordinal - std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); - // add debug notes for each atom - this->synthesizeDebugNotes(allAtomsByReader); - } - - // process all stabs .o files one by one - if ( someStabs ) { - // get stabs from each reader, in command line order - for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - switch ( reader->getDebugInfoKind() ) { - case ObjectFile::Reader::kDebugInfoDwarf: - case ObjectFile::Reader::kDebugInfoNone: - // do nothing - break; - case ObjectFile::Reader::kDebugInfoStabs: - case ObjectFile::Reader::kDebugInfoStabsUUID: - collectStabs(reader, atomOrdinals); - break; - default: - throw "Unhandled type of debug information"; - } - } - } - // remove stabs associated with atoms that won't be in output - std::set allAtomsSet; - allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); - fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); - } - } -} - -void Linker::writeOutput() -{ - if ( fOptions.forceCpuSubtypeAll() ) - fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; - - fStartWriteTime = mach_absolute_time(); - // tell writer about each segment's atoms - fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), - this->dyldHelper(), this->dyldLazyLibraryHelper(), - fCreateUUID, fCanScatter, - fCurrentCpuConstraint, fBiggerThanTwoGigOutput, - fGlobalSymbolTable.hasExternalWeakDefinitions()); -} - -ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) -{ - // map in whole file - uint64_t len = info.fileLen; - int fd = ::open(info.path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open file, errno=%d", errno); - if ( info.fileLen < 20 ) - throw "file too small"; - - uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - if ( p == (uint8_t*)(-1) ) - throwf("can't map file, errno=%d", errno); - - // if fat file, skip to architecture we want - // Note: fat header is always big-endian - const fat_header* fh = (fat_header*)p; - if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - uint32_t sliceToUse; - bool sliceFound = false; - if ( fOptions.preferSubArchitecture() ) { - // first try to find a slice that match cpu-type and cpu-sub-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture) - && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) { - sliceToUse = i; - sliceFound = true; - break; - } - } - } - if ( !sliceFound ) { - // look for any slice that matches just cpu-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) { - sliceToUse = i; - sliceFound = true; - break; - } - } - } - if ( sliceFound ) { - uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); - len = OSSwapBigToHostInt32(archs[sliceToUse].size); - // if requested architecture is page aligned within fat file, then remap just that portion of file - if ( (fileOffset & 0x00000FFF) == 0 ) { - // unmap whole file - munmap((caddr_t)p, info.fileLen); - // re-map just part we need - p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); - if ( p == (uint8_t*)(-1) ) - throwf("can't re-map file, errno=%d", errno); - } - else { - p = &p[fileOffset]; - } - } - } - ::close(fd); - - switch (fArchitecture) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - break; - } - -#if LTO_SUPPORT - if ( lto::Reader::validFile(p, len, fArchitecture) ) { - return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); - } - else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) { - throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded"; - } -#endif - // error handling - if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - throwf("missing required architecture %s in file", fArchitectureName); - } - else { - throw "file is not of required architecture"; - } -} - -void Linker::logDylib(ObjectFile::Reader* reader, bool indirect) -{ - if ( fOptions.readerOptions().fTraceDylibs ) { - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - if ( indirect ) - logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); - else - logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); - } -} - - - -ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath) -{ - //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); - InstallNameToReader::iterator pos = fDylibMap.find(installPath); - if ( pos != fDylibMap.end() ) { - return pos->second; - } - else { - // allow -dylib_path option to override indirect library to use - for (std::vector::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) { - if ( strcmp(dit->installName,installPath) == 0 ) {\ - try { - Options::FileInfo info = fOptions.findFile(dit->useInstead); - ObjectFile::Reader* reader = this->createReader(info); - fDylibMap[strdup(installPath)] = reader; - this->logDylib(reader, true); - return reader; - } - catch (const char* msg) { - warning("ignoring -dylib_file option, %s", msg); - } - } - } - char newPath[MAXPATHLEN]; - // handle @loader_path - if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { - strcpy(newPath, fromPath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &installPath[13]); - else - strcpy(newPath, &installPath[13]); - installPath = newPath; - } - // note: @executable_path case is handled inside findFileUsingPaths() - // search for dylib using -F and -L paths - Options::FileInfo info = fOptions.findFileUsingPaths(installPath); - try { - ObjectFile::Reader* reader = this->createReader(info); - fDylibMap[strdup(installPath)] = reader; - this->logDylib(reader, true); - return reader; - } - catch (const char* msg) { - throwf("in %s, %s", info.path, msg); - } - } -} - - -void Linker::processDylibs() -{ - fAllDirectDylibsLoaded = true; - - // mark all dylibs initially specified as required and check if they can be used - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - it->second->setExplicitlyLinked(); - this->checkDylibClientRestrictions(it->second); - } - - // keep processing dylibs until no more dylibs are added - unsigned long lastMapSize = 0; - while ( lastMapSize != fDylibMap.size() ) { - lastMapSize = fDylibMap.size(); - // can't iterator fDylibMap while modifying it, so use temp buffer - std::vector currentUnprocessedReaders; - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( fDylibsProcessed.count(it->second) == 0 ) - currentUnprocessedReaders.push_back(it->second); - } - for (std::vector::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) { - fDylibsProcessed.insert(*it); - (*it)->processIndirectLibraries(this); - } - } - - // go back over original dylibs and mark sub frameworks as re-exported - if ( fOptions.outputKind() == Options::kDynamicLibrary ) { - const char* myLeaf = strrchr(fOptions.installPath(), '/'); - if ( myLeaf != NULL ) { - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - const char* childParent = reader->parentUmbrella(); - if ( childParent != NULL ) { - if ( strcmp(childParent, &myLeaf[1]) == 0 ) { - // set re-export bit of info - std::map::iterator pos = fDylibOptionsMap.find(reader); - if ( pos != fDylibOptionsMap.end() ) { - pos->second.fReExport = true; - } - } - } - } - } - } - -} - - - -void Linker::createReaders() -{ - fStartCreateReadersTime = mach_absolute_time(); - std::vector& files = fOptions.getInputFiles(); - const int count = files.size(); - if ( count == 0 ) - throw "no object files specified"; - // add all direct object, archives, and dylibs - for (int i=0; i < count; ++i) { - Options::FileInfo& entry = files[i]; - // ignore /usr/lib/dyld on command line in crt.o build - if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { - try { - this->addInputFile(this->createReader(entry), entry); - } - catch (const char* msg) { - if ( strstr(msg, "architecture") != NULL ) { - if ( fOptions.ignoreOtherArchInputFiles() ) { - // ignore, because this is about an architecture not in use - } - else { - warning("in %s, %s", entry.path, msg); - } - } - else { - throwf("in %s, %s", entry.path, msg); - } - } - } - } - - this->processDylibs(); -} - - - -ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - // remember which readers are archives because they are logged differently - fArchiveReaders.insert(reader); - - // update stats - fTotalArchiveSize += mappedLen; - ++fTotalArchivesLoaded; - return reader; -} - -ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't - if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() ) - fCanScatter = false; - - // update stats - fTotalObjectSize += mappedLen; - ++fTotalObjectLoaded; - return reader; -} - - -void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) -{ - // Check for any restrictions on who can link with this dylib - const char* readerParentName = reader->parentUmbrella() ; - std::vector* clients = reader->getAllowableClients(); - if ( (readerParentName != NULL) || (clients != NULL) ) { - // only dylibs that are in an umbrella or have a client list need verification - const char* installName = fOptions.installPath(); - const char* installNameLastSlash = strrchr(installName, '/'); - bool isParent = false; - bool isSibling = false; - bool isAllowableClient = false; - // There are three cases: - if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) { - // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella - isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 ); - - // hack to support umbrella variants that encode the variant name in the install name - // e.g. CoreServices_profile - if ( !isParent ) { - const char* underscore = strchr(&installNameLastSlash[1], '_'); - if ( underscore != NULL ) { - isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 ); - } - } - - // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent - isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); - } - - if ( !isParent && !isSibling && (clients != NULL) ) { - // case 3) the dylib has a list of allowable clients, and we are creating one of them - const char* clientName = fOptions.clientName(); - int clientNameLen = 0; - if ( clientName != NULL ) { - // use client name as specified on command line - clientNameLen = strlen(clientName); - } - else { - // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) - clientName = installName; - clientNameLen = strlen(clientName); - // starts after last slash - if ( installNameLastSlash != NULL ) - clientName = &installNameLastSlash[1]; - if ( strncmp(clientName, "lib", 3) == 0 ) - clientName = &clientName[3]; - // up to first dot - const char* firstDot = strchr(clientName, '.'); - if ( firstDot != NULL ) - clientNameLen = firstDot - clientName; - // up to first underscore - const char* firstUnderscore = strchr(clientName, '_'); - if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) - clientNameLen = firstUnderscore - clientName; - } - - // Use clientName to check if this dylib is able to link against the allowable clients. - for (std::vector::iterator it = clients->begin(); it != clients->end(); it++) { - if ( strncmp(*it, clientName, clientNameLen) == 0 ) - isAllowableClient = true; - } - } - - if ( !isParent && !isSibling && !isAllowableClient ) { - if ( readerParentName != NULL ) { - throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", - reader->getPath(), readerParentName); - } - else { - throwf("cannot link directly with %s", reader->getPath()); - } - } - } - - -} - -ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { - // this is a "blank" stub - // silently ignore it - return reader; - } - // add to map of loaded dylibs - const char* installPath = reader->getInstallPath(); - if ( installPath != NULL ) { - InstallNameToReader::iterator pos = fDylibMap.find(installPath); - if ( pos == fDylibMap.end() ) { - fDylibMap[strdup(installPath)] = reader; - } - else { - InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); - if ( pos2 == fDylibMap.end() ) - fDylibMap[strdup(reader->getPath())] = reader; - else - warning("duplicate dylib %s", reader->getPath()); - } - } - else if ( info.options.fBundleLoader ) - fBundleLoaderReader = reader; - - // log direct readers - if ( !fAllDirectDylibsLoaded ) - this->logDylib(reader, false); - - // update stats - ++fTotalDylibsLoaded; - - return reader; -} - - -void Linker::logTraceInfo (const char* format, ...) -{ - static int trace_file = -1; - char trace_buffer[MAXPATHLEN * 2]; - char *buffer_ptr; - int length; - ssize_t amount_written; - const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile; - - if(trace_file == -1) { - if(trace_file_path != NULL) { - trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); - if(trace_file == -1) - throwf("Could not open or create trace file: %s", trace_file_path); - } - else { - trace_file = fileno(stderr); - } - } - - va_list ap; - va_start(ap, format); - length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); - va_end(ap); - buffer_ptr = trace_buffer; - - while(length > 0) { - amount_written = write(trace_file, buffer_ptr, length); - if(amount_written == -1) - /* Failure to write shouldn't fail the build. */ - return; - buffer_ptr += amount_written; - length -= amount_written; - } -} - - - -void Linker::createWriter() -{ - fStartCreateWriterTime = mach_absolute_time(); - - // make a vector out of all required dylibs in fDylibMap - std::vector dynamicLibraries; - // need to preserve command line order - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) { - if ( reader == mit->second ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = reader; - dylibInfo.options = fDylibOptionsMap[reader]; - dynamicLibraries.push_back(dylibInfo); - break; - } - } - } - // then add any other dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->implicitlyLinked() ) { - // if not already in dynamicLibraries - bool alreadyInDynamicLibraries = false; - for (std::vector::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) { - if ( dit->reader == it->second ) { - alreadyInDynamicLibraries = true; - break; - } - } - if ( ! alreadyInDynamicLibraries ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = it->second; - std::map::iterator pos = fDylibOptionsMap.find(it->second); - if ( pos != fDylibOptionsMap.end() ) { - dylibInfo.options = pos->second; - } - else { - dylibInfo.options.fWeakImport = false; // FIX ME - dylibInfo.options.fReExport = false; - dylibInfo.options.fBundleLoader = false; - } - dynamicLibraries.push_back(dylibInfo); - } - } - } - if ( fBundleLoaderReader != NULL ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = fBundleLoaderReader; - dylibInfo.options.fWeakImport = false; - dylibInfo.options.fReExport = false; - dylibInfo.options.fBundleLoader = true; - dynamicLibraries.push_back(dylibInfo); - } - - const char* path = fOptions.getOutputFilePath(); - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_POWERPC64: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_I386: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_X86_64: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_ARM: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - default: - throw "unknown architecture"; - } -} - - -Linker::SymbolTable::SymbolTable(Linker& owner) - : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false) -{ -} - -void Linker::SymbolTable::require(const char* name) -{ - //fprintf(stderr, "require(%s)\n", name); - Mapper::iterator pos = fTable.find(name); - if ( pos == fTable.end() ) { - fTable[name] = NULL; - ++fRequireCount; - } -} - -// convenience labels for 2-dimensional switch statement -enum AllDefinitionCombinations { - kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition, - kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition, - kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition, - kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition, - kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol -}; - -bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) -{ - bool useNew = true; - bool checkVisibilityMismatch = false; - const char* name = newAtom.getName(); - if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { - switch ( newAtom.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - fHasExternalTentativeDefinitions = true; - break; - case ObjectFile::Atom::kWeakDefinition: - fHasExternalWeakDefinitions = true; - break; - default: - break; - } - } - //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); - Mapper::iterator pos = fTable.find(name); - ObjectFile::Atom* existingAtom = NULL; - if ( pos != fTable.end() ) - existingAtom = pos->second; - if ( existingAtom != NULL ) { - // already have atom with same name in symbol table - switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { - case kRegAndReg: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kRegAndWeak: - // ignore new weak atom, because we already have a non-weak one - useNew = false; - break; - case kRegAndTent: - // ignore new tentative atom, because we already have a regular one - useNew = false; - checkVisibilityMismatch = true; - if ( newAtom.getSize() > existingAtom->getSize() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "is smaller than the real definition of size %llu from %s", - newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), - existingAtom->getSize(), existingAtom->getFile()->getPath()); - } - break; - case kRegAndExtern: - // ignore external atom, because we already have a one - useNew = false; - break; - case kRegAndExternWeak: - // ignore external atom, because we already have a one - useNew = false; - break; - case kRegAndAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case kWeakAndReg: - // replace existing weak atom with regular one - break; - case kWeakAndWeak: - // have another weak atom, use whichever has largest alignment requirement - // because codegen of some client may require alignment - useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); - checkVisibilityMismatch = true; - break; - case kWeakAndTent: - // replace existing weak atom with tentative one ??? - break; - case kWeakAndExtern: - // keep weak atom, at runtime external one may override - useNew = false; - break; - case kWeakAndExternWeak: - // keep weak atom, at runtime external one may override - useNew = false; - break; - case kWeakAndAbsolute: - // replace existing weak atom with absolute one - break; - case kTentAndReg: - // replace existing tentative atom with regular one - checkVisibilityMismatch = true; - if ( newAtom.getSize() < existingAtom->getSize() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "being replaced by a real definition of size %llu from %s", - newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), - newAtom.getSize(), newAtom.getFile()->getPath()); - } - break; - case kTentAndWeak: - // replace existing tentative atom with weak one ??? - break; - case kTentAndTent: - // use largest - checkVisibilityMismatch = true; - if ( newAtom.getSize() < existingAtom->getSize() ) { - useNew = false; - } - else { - if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) - warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName()); - } - break; - case kTentAndExtern: - case kTentAndExternWeak: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing common symbol %s from %s with true definition from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - } - break; - case kTentAndAbsolute: - // replace tentative with absolute (can't size check because absolutes have no size) - break; - case kExternAndReg: - // replace external atom with regular one - break; - case kExternAndWeak: - // replace external atom with weak one - break; - case kExternAndTent: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - } - break; - case kExternAndExtern: - throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kExternAndExternWeak: - // keep strong dylib atom, ignore weak one - useNew = false; - break; - case kExternAndAbsolute: - // replace external atom with absolute one - break; - case kExternWeakAndReg: - // replace existing weak external with regular - break; - case kExternWeakAndWeak: - // replace existing weak external with weak (let dyld decide at runtime which to use) - break; - case kExternWeakAndTent: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - } - break; - case kExternWeakAndExtern: - // replace existing weak external with external - break; - case kExternWeakAndExternWeak: - // keep existing external weak - useNew = false; - break; - case kExternWeakAndAbsolute: - // replace existing weak external with absolute - break; - case kAbsoluteAndReg: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kAbsoluteAndWeak: - // ignore new weak atom, because we already have a non-weak one - useNew = false; - break; - case kAbsoluteAndTent: - // ignore new tentative atom, because we already have a regular one - useNew = false; - break; - case kAbsoluteAndExtern: - // ignore external atom, because we already have a one - useNew = false; - break; - case kAbsoluteAndExternWeak: - // ignore external atom, because we already have a one - useNew = false; - break; - case kAbsoluteAndAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - } - } - if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) { - warning("%s has different visibility (%s) in %s and (%s) in %s", - newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath()); - } - if ( useNew ) { - fTable[name] = &newAtom; - if ( existingAtom != NULL ) - fOwner.markDead(existingAtom); - } - else { - fOwner.markDead(&newAtom); - } - return useNew; -} - - - -ObjectFile::Atom* Linker::SymbolTable::find(const char* name) -{ - Mapper::iterator pos = fTable.find(name); - if ( pos != fTable.end() ) { - return pos->second; - } - return NULL; -} - - -void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector& undefines) -{ - for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( (it->second == NULL) || (andWeakDefintions && (it->second->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)) ) { - undefines.push_back(it->first); - } - } -} - - - -bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) -{ - if ( left == right ) - return false; - - // first sort by section order (which is already sorted by segment) - unsigned int leftSectionIndex = left->getSection()->getIndex(); - unsigned int rightSectionIndex = right->getSection()->getIndex(); - if ( leftSectionIndex != rightSectionIndex) - return (leftSectionIndex < rightSectionIndex); - - // if a -order_file is specified, then sorting is altered to sort those symbols first - if ( fOverriddenOrdinalMap != NULL ) { - std::map::iterator leftPos = fOverriddenOrdinalMap->find(left); - std::map::iterator rightPos = fOverriddenOrdinalMap->find(right); - std::map::iterator end = fOverriddenOrdinalMap->end(); - if ( leftPos != end ) { - if ( rightPos != end ) { - // both left and right are overridden, so compare overridden ordinals - return leftPos->second < rightPos->second; - } - else { - // left is overridden and right is not, so left < right - return true; - } - } - else { - if ( rightPos != end ) { - // right is overridden and left is not, so right < left - return false; - } - else { - // neither are overridden, do default sort - // fall into default sorting below - } - } - } - - // the __common section can have real or tentative definitions - // we want the real ones to sort before tentative ones - bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); - bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); - if ( leftIsTent != rightIsTent ) - return rightIsTent; - - // lastly sort by atom ordinal. this is already sorted by .o order - return left->getOrdinal() < right->getOrdinal(); -} - - -int main(int argc, const char* argv[]) -{ - const char* archName = NULL; - bool showArch = false; - bool archInferred = false; - try { - // create linker object given command line arguments - Linker ld(argc, argv); - - // save error message prefix - archName = ld.architectureName(); - archInferred = ld.isInferredArchitecture(); - showArch = ld.showArchitectureInErrors(); - - // open all input files - ld.createReaders(); - - // open output file - ld.createWriter(); - - // do linking - ld.link(); - } - catch (const char* msg) { - if ( archInferred ) - fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); - else if ( showArch ) - fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); - else - fprintf(stderr, "ld: %s\n", msg); - return 1; - } - - return 0; -} diff --git a/ld64/FireOpal/src/machochecker.cpp b/ld64/FireOpal/src/machochecker.cpp deleted file mode 100644 index 311809b..0000000 --- a/ld64/FireOpal/src/machochecker.cpp +++ /dev/null @@ -1,965 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" - - - __attribute__((noreturn)) -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - - -template -class MachOChecker -{ -public: - static bool validFile(const uint8_t* fileContent); - static MachOChecker* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) - { return new MachOChecker(fileContent, fileLength, path); } - virtual ~MachOChecker() {} - - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - - typedef __gnu_cxx::hash_set, CStringEquals> StringSet; - - MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); - void checkMachHeader(); - void checkLoadCommands(); - void checkSection(const macho_segment_command

* segCmd, const macho_section

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

* reloc); - void checkLocalReloation(const macho_relocation_info

* reloc); - pint_t relocBase(); - bool addressInWritableSegment(pint_t address); - - const char* fPath; - const macho_header

* fHeader; - uint32_t fLength; - const char* fStrings; - const char* fStringsEnd; - const macho_nlist

* fSymbols; - uint32_t fSymbolCount; - const macho_dysymtab_command

* fDynamicSymbolTable; - const uint32_t* fIndirectTable; - uint32_t fIndirectTableCount; - const macho_relocation_info

* fLocalRelocations; - uint32_t fLocalRelocationsCount; - const macho_relocation_info

* fExternalRelocations; - uint32_t fExternalRelocationsCount; - bool fWriteableSegmentWithAddrOver4G; - const macho_segment_command

* fFirstSegment; - const macho_segment_command

* fFirstWritableSegment; -}; - - - -template <> -bool MachOChecker::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 MachOChecker::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 MachOChecker::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_I386 ) - return false; - switch (header->filetype()) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - case MH_DYLINKER: - return true; - } - return false; -} - -template <> -bool MachOChecker::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_X86_64 ) - return false; - switch (header->filetype()) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - case MH_DYLINKER: - return true; - } - return false; -} - -template <> -bool MachOChecker::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: - return true; - } - return false; -} - -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } - -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) -{ - // sanity check - if ( ! validFile(fileContent) ) - throw "not a mach-o file that can be checked"; - - fPath = strdup(path); - fHeader = (const macho_header

*)fileContent; - - // sanity check header - checkMachHeader(); - - // check load commands - checkLoadCommands(); - - checkIndirectSymbolTable(); - - checkRelocations(); - - checkSymbolTable(); -} - - -template -void MachOChecker::checkMachHeader() -{ - if ( (fHeader->sizeofcmds() + sizeof(macho_header

)) > fLength ) - throw "sizeofcmds in mach_header is larger than file"; - - uint32_t flags = fHeader->flags(); - const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000; - if ( flags & invalidBits ) - throw "invalid bits in mach_header flags"; - if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) - throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs"; -} - -template -void MachOChecker::checkLoadCommands() -{ - // check that all load commands fit within the load command space file - const macho_encryption_info_command

* encryption_info = 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(); - const macho_load_command

* const cmds = (macho_load_command

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

)); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - uint32_t size = cmd->cmdsize(); - if ( (size & this->loadCommandSizeMask()) != 0 ) - throwf("load command #%d has a unaligned size", i); - const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); - if ( endOfCmd > endOfLoadCommands ) - throwf("load command #%d extends beyond the end of the load commands", i); - if ( endOfCmd > endOfFile ) - throwf("load command #%d extends beyond the end of the file", i); - switch ( cmd->cmd() ) { - case macho_segment_command

::CMD: - case LC_SYMTAB: - case LC_UNIXTHREAD: - case LC_DYSYMTAB: - case LC_LOAD_DYLIB: - case LC_ID_DYLIB: - case LC_LOAD_DYLINKER: - case LC_ID_DYLINKER: - case macho_routines_command

::CMD: - case LC_SUB_FRAMEWORK: - case LC_SUB_CLIENT: - case LC_TWOLEVEL_HINTS: - case LC_PREBIND_CKSUM: - case LC_LOAD_WEAK_DYLIB: - case LC_LAZY_LOAD_DYLIB: - case LC_UUID: - case LC_REEXPORT_DYLIB: - case LC_SEGMENT_SPLIT_INFO: - case LC_CODE_SIGNATURE: - break; - case LC_ENCRYPTION_INFO: - encryption_info = (macho_encryption_info_command

*)cmd; - break; - case LC_SUB_UMBRELLA: - case LC_SUB_LIBRARY: - if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS ) - throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; - break; - default: - throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd()); - } - cmd = (const macho_load_command

*)endOfCmd; - } - - // check segments - cmd = cmds; - std::vector > segmentAddressRanges; - std::vector > segmentFileOffsetRanges; - const macho_segment_command

* linkEditSegment = 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; - if ( segCmd->cmdsize() != (sizeof(macho_segment_command

) + segCmd->nsects() * sizeof(macho_section_content

)) ) - throw "invalid segment load command size"; - - // see if this overlaps another segment address range - uint64_t startAddr = segCmd->vmaddr(); - uint64_t endAddr = startAddr + segCmd->vmsize(); - for (typename std::vector >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) { - if ( it->first < startAddr ) { - if ( it->second > startAddr ) - throw "overlapping segment vm addresses"; - } - else if ( it->first > startAddr ) { - if ( it->first < endAddr ) - throw "overlapping segment vm addresses"; - } - else { - throw "overlapping segment vm addresses"; - } - segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); - } - // see if this overlaps another segment file offset range - uint64_t startOffset = segCmd->fileoff(); - uint64_t endOffset = startOffset + segCmd->filesize(); - for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) { - if ( it->first < startOffset ) { - if ( it->second > startOffset ) - throw "overlapping segment file data"; - } - else if ( it->first > startOffset ) { - if ( it->first < endOffset ) - throw "overlapping segment file data"; - } - else { - throw "overlapping segment file data"; - } - segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); - // check is within file bounds - if ( (startOffset > fLength) || (endOffset > fLength) ) - throw "segment file data is past end of file"; - } - // verify it fits in file - if ( startOffset > fLength ) - throw "segment fileoff does not fit in file"; - if ( endOffset > fLength ) - throw "segment fileoff+filesize does not fit in file"; - - // keep LINKEDIT segment - if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) - linkEditSegment = segCmd; - - // cache interesting segments - if ( fFirstSegment == NULL ) - fFirstSegment = segCmd; - if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { - if ( fFirstWritableSegment == NULL ) - fFirstWritableSegment = segCmd; - if ( segCmd->vmaddr() > 0x100000000ULL ) - fWriteableSegmentWithAddrOver4G = true; - } - - // check section ranges - const macho_section

* 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 - if ( sect->addr() < startAddr ) - throwf("section %s vm address not within segment", sect->sectname()); - if ( (sect->addr()+sect->size()) > endAddr ) - throwf("section %s vm address not within segment", sect->sectname()); - if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) { - if ( sect->offset() < startOffset ) - throwf("section %s file offset not within segment", sect->sectname()); - if ( (sect->offset()+sect->size()) > endOffset ) - throwf("section %s file offset not within segment", sect->sectname()); - } - checkSection(segCmd, sect); - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - - // verify there was a LINKEDIT segment - if ( linkEditSegment == NULL ) - throw "no __LINKEDIT segment"; - - // checks for executables - bool isStaticExecutable = false; - if ( fHeader->filetype() == MH_EXECUTE ) { - isStaticExecutable = true; - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch ( cmd->cmd() ) { - case LC_LOAD_DYLINKER: - // the existence of a dyld load command makes a executable dynamic - isStaticExecutable = false; - break; - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - if ( isStaticExecutable ) { - if ( fHeader->flags() != MH_NOUNDEFS ) - throw "invalid bits in mach_header flags for static executable"; - } - } - - // verify encryption info - if ( encryption_info != NULL ) { - if ( fHeader->filetype() != MH_EXECUTE ) - throw "LC_ENCRYPTION_INFO load command is only legal in main executables"; - if ( encryption_info->cryptoff() < (sizeof(macho_header

) + fHeader->sizeofcmds()) ) - throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands"; - if ( (encryption_info->cryptoff() % 4096) != 0 ) - throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned"; - if ( (encryption_info->cryptsize() % 4096) != 0 ) - throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized"; - for (typename std::vector >::iterator it = segmentFileOffsetRanges.begin(); - it != segmentFileOffsetRanges.end(); ++it) { - if ( (it->first <= encryption_info->cryptoff()) && (encryption_info->cryptoff() < it->second) ) { - if ( (encryption_info->cryptoff() + encryption_info->cryptsize()) > it->second ) - throw "LC_ENCRYPTION_INFO load command is not contained within one segment"; - } - } - } - - // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO - cmd = cmds; - bool foundDynamicSymTab = false; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch ( cmd->cmd() ) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - fSymbolCount = symtab->nsyms(); - fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); - if ( symtab->symoff() < linkEditSegment->fileoff() ) - throw "symbol table not in __LINKEDIT"; - if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist

*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "symbol table end not in __LINKEDIT"; - if ( (symtab->symoff() % sizeof(pint_t)) != 0 ) - throw "symbol table start not pointer aligned"; - fStrings = (char*)fHeader + symtab->stroff(); - fStringsEnd = fStrings + symtab->strsize(); - if ( symtab->stroff() < linkEditSegment->fileoff() ) - throw "string pool not in __LINKEDIT"; - if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "string pool extends beyond __LINKEDIT"; - if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed - throw "string pool start not pointer aligned"; - if ( (symtab->strsize() % sizeof(pint_t)) != 0 ) - throw "string pool size not a multiple of pointer size"; - } - break; - case LC_DYSYMTAB: - { - if ( isStaticExecutable ) - throw "LC_DYSYMTAB should not be used in static executable"; - foundDynamicSymTab = true; - fDynamicSymbolTable = (struct macho_dysymtab_command

*)cmd; - fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); - fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); - if ( fIndirectTableCount != 0 ) { - if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() ) - throw "indirect symbol table not in __LINKEDIT"; - if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "indirect symbol table not in __LINKEDIT"; - if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 ) - throw "indirect symbol table not pointer aligned"; - } - fLocalRelocationsCount = fDynamicSymbolTable->nlocrel(); - if ( fLocalRelocationsCount != 0 ) { - fLocalRelocations = (const macho_relocation_info

*)((char*)fHeader + fDynamicSymbolTable->locreloff()); - if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() ) - throw "local relocations not in __LINKEDIT"; - if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "local relocations not in __LINKEDIT"; - if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 ) - throw "local relocations table not pointer aligned"; - } - fExternalRelocationsCount = fDynamicSymbolTable->nextrel(); - if ( fExternalRelocationsCount != 0 ) { - fExternalRelocations = (const macho_relocation_info

*)((char*)fHeader + fDynamicSymbolTable->extreloff()); - if ( fDynamicSymbolTable->extreloff() < linkEditSegment->fileoff() ) - throw "external relocations not in __LINKEDIT"; - if ( (fDynamicSymbolTable->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "external relocations not in __LINKEDIT"; - if ( (fDynamicSymbolTable->extreloff() % sizeof(pint_t)) != 0 ) - throw "external relocations table not pointer aligned"; - } - } - break; - case LC_SEGMENT_SPLIT_INFO: - { - if ( isStaticExecutable ) - throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; - const macho_linkedit_data_command

* info = (struct macho_linkedit_data_command

*)cmd; - if ( info->dataoff() < linkEditSegment->fileoff() ) - throw "split seg info not in __LINKEDIT"; - if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "split seg info not in __LINKEDIT"; - if ( (info->dataoff() % sizeof(pint_t)) != 0 ) - throw "split seg info table not pointer aligned"; - if ( (info->datasize() % sizeof(pint_t)) != 0 ) - throw "split seg info size not a multiple of pointer size"; - } - break; - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - if ( !isStaticExecutable && !foundDynamicSymTab ) - throw "missing dynamic symbol table"; - if ( fStrings == NULL ) - throw "missing symbol table"; - -} - -template -void MachOChecker::checkSection(const macho_segment_command

* segCmd, const macho_section

* sect) -{ - uint8_t sectionType = (sect->flags() & SECTION_TYPE); - if ( sectionType == S_ZEROFILL ) { - if ( sect->offset() != 0 ) - throwf("section offset should be zero for zero-fill section %s", sect->sectname()); - } - - // more section tests here -} - -template -void MachOChecker::checkIndirectSymbolTable() -{ - 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 start = 0; - uint32_t elementSize = 0; - switch ( sect->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - elementSize = sect->reserved2(); - start = sect->reserved1(); - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - elementSize = sizeof(pint_t); - start = sect->reserved1(); - break; - } - if ( elementSize != 0 ) { - uint32_t count = sect->size() / elementSize; - if ( (count*elementSize) != sect->size() ) - throwf("%s section size is not an even multiple of element size", sect->sectname()); - if ( (start+count) > fIndirectTableCount ) - throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount ); - } - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } -} - - -template -void MachOChecker::checkSymbolTable() -{ - // verify no duplicate external symbol names - if ( fDynamicSymbolTable != NULL ) { - StringSet externalNames; - const macho_nlist

* const exportedStart = &fSymbols[fDynamicSymbolTable->iextdefsym()]; - const macho_nlist

* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()]; - for(const macho_nlist

* p = exportedStart; p < exportedEnd; ++p) { - const char* symName = &fStrings[p->n_strx()]; - if ( externalNames.find(symName) != externalNames.end() ) - throwf("duplicate external symbol: %s", symName); - externalNames.insert(symName); - } - } -} - - -template <> -ppc::P::uint_t MachOChecker::relocBase() -{ - if ( fHeader->flags() & MH_SPLIT_SEGS ) - return fFirstWritableSegment->vmaddr(); - else - return fFirstSegment->vmaddr(); -} - -template <> -ppc64::P::uint_t MachOChecker::relocBase() -{ - if ( fWriteableSegmentWithAddrOver4G ) - return fFirstWritableSegment->vmaddr(); - else - return fFirstSegment->vmaddr(); -} - -template <> -x86::P::uint_t MachOChecker::relocBase() -{ - if ( fHeader->flags() & MH_SPLIT_SEGS ) - return fFirstWritableSegment->vmaddr(); - else - return fFirstSegment->vmaddr(); -} - -template <> -x86_64::P::uint_t MachOChecker::relocBase() -{ - // check for split-seg - return fFirstWritableSegment->vmaddr(); -} - -template <> -arm::P::uint_t MachOChecker::relocBase() -{ - if ( fHeader->flags() & MH_SPLIT_SEGS ) - return fFirstWritableSegment->vmaddr(); - else - return fFirstSegment->vmaddr(); -} - - -template -bool MachOChecker::addressInWritableSegment(pint_t address) -{ - 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; - if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) ) { - // if segment is writable, we are fine - if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) - return true; - // could be a text reloc, make sure section bit is set - const macho_section

* 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 ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) { - // found section for this address, if has relocs we are fine - return ( (sect->flags() & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC)) != 0 ); - } - } - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - return false; -} - - -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 2 ) - throw "bad external relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "external relocation address not in writable segment"; - // FIX: check r_symbol -} - -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 3 ) - throw "bad external relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "external relocation address not in writable segment"; - // FIX: check r_symbol -} - -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 2 ) - throw "bad external relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "external relocation address not in writable segment"; - // FIX: check r_symbol -} - - -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 3 ) - throw "bad external relocation length"; - if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "exernal relocation address not in writable segment"; - // FIX: check r_symbol -} - -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 2 ) - throw "bad external relocation length"; - if ( reloc->r_type() != ARM_RELOC_VANILLA ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "external relocation address not in writable segment"; - // FIX: check r_symbol -} - - -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_address() & R_SCATTERED ) { - // scattered - const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; - // FIX - - } - else { - // ignore pair relocs - if ( reloc->r_type() == PPC_RELOC_PAIR ) - return; - // FIX - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); - } -} - - -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 3 ) - throw "bad local relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown local relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad local relocation pc_rel"; - if ( reloc->r_extern() != 0 ) - throw "external relocation found with local relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "local relocation address not in writable segment"; -} - -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - // FIX -} - -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 3 ) - throw "bad local relocation length"; - if ( reloc->r_type() != X86_64_RELOC_UNSIGNED ) - throw "unknown local relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad local relocation pc_rel"; - if ( reloc->r_extern() != 0 ) - throw "external relocation found with local relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "local relocation address not in writable segment"; -} - -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_address() & R_SCATTERED ) { - // scattered - const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; - if ( sreloc->r_length() != 2 ) - throw "bad local scattered relocation length"; - if ( sreloc->r_type() != ARM_RELOC_PB_LA_PTR ) - throw "bad local scattered relocation type"; - } - else { - if ( reloc->r_length() != 2 ) - throw "bad local relocation length"; - if ( reloc->r_extern() != 0 ) - throw "external relocation found with local relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "local relocation address not in writable segment"; - } -} - -template -void MachOChecker::checkRelocations() -{ - // external relocations should be sorted to minimize dyld symbol lookups - // therefore every reloc with the same r_symbolnum value should be contiguous - std::set previouslySeenSymbolIndexes; - uint32_t lastSymbolIndex = 0xFFFFFFFF; - const macho_relocation_info

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

* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) { - this->checkExternalReloation(reloc); - if ( reloc->r_symbolnum() != lastSymbolIndex ) { - if ( previouslySeenSymbolIndexes.count(reloc->r_symbolnum()) != 0 ) - throw "external relocations not sorted"; - previouslySeenSymbolIndexes.insert(lastSymbolIndex); - lastSymbolIndex = reloc->r_symbolnum(); - } - } - - const macho_relocation_info

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

* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { - this->checkLocalReloation(reloc); - } -} - - -static void check(const char* path) -{ - struct stat stat_buf; - - try { - int fd = ::open(path, O_RDONLY, 0); - if ( fd == -1 ) - throw "cannot open file"; - ::fstat(fd, &stat_buf); - uint32_t length = stat_buf.st_size; - uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - if ( p == ((uint8_t*)(-1)) ) - throw "cannot map file"; - ::close(fd); - const mach_header* mh = (mach_header*)p; - if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - const struct fat_header* fh = (struct fat_header*)p; - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - size_t offset = OSSwapBigToHostInt32(archs[i].offset); - size_t size = OSSwapBigToHostInt32(archs[i].size); - unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); - - switch(cputype) { - case CPU_TYPE_POWERPC: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, ppc slice does not contain ppc mach-o"; - break; - case CPU_TYPE_I386: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, i386 slice does not contain i386 mach-o"; - break; - case CPU_TYPE_POWERPC64: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; - break; - case CPU_TYPE_X86_64: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; - break; - case CPU_TYPE_ARM: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, arm slice does not contain arm mach-o"; - break; - default: - throwf("in universal file, unknown architecture slice 0x%x\n", cputype); - } - } - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else { - throw "not a known file type"; - } - } - catch (const char* msg) { - throwf("%s in %s", msg, path); - } -} - - -int main(int argc, const char* argv[]) -{ - try { - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - if ( arg[0] == '-' ) { - if ( strcmp(arg, "-no_content") == 0 ) { - - } - else { - throwf("unknown option: %s\n", arg); - } - } - else { - check(arg); - } - } - } - catch (const char* msg) { - fprintf(stderr, "machocheck failed: %s\n", msg); - return 1; - } - - return 0; -} - - - diff --git a/ld64/FireOpal/unit-tests/README b/ld64/FireOpal/unit-tests/README deleted file mode 100644 index a0fd0a2..0000000 --- a/ld64/FireOpal/unit-tests/README +++ /dev/null @@ -1,28 +0,0 @@ - -The easy way to run all tests is within Xcode. Just select "unit-tests" as the target and click Build. - -When run from within Xcode, the just built linker will be used. If you cd into a test case and run it, the -installed linker (e.g. /usr/bin/ld) will be used. - -Each test case is a directory with a Makefile. The Makefile default target should do whatever work is necessary -to perform the test. If successful is should print "PASS xxx" where xxx is the name of the test case. Otherwise -it should print "FAIL xxx reason". If nothing is printed (for instance a tool crashed), the harness will -automatically print that it failed. The harness will always pass ARCH to the Makefile to specify which -architecture to test. The Makefile should also have a "clean" target which removes and generated files. - - -There are some utility functions for use in Makefiles for generating the PASS/FAIL strings: - - ${PASS_IFF} can be put in front of the last command in the make rule and it will print PASS - if the command returned 0 or FAIL otherwise. Example: - ${PASS_IFF} ${CC} foo.c -o foo - Will print PASS if and only if the compilation succeeded - - ${PASS_IFF_EMPTY} can have data piped into it. It prints PASS if there is no data, otherwise FAIL. - Example: - otool -hv foo.o | grep SUBSECTIONS_VIA_SYMBOLS | ${PASS_IFF_EMPTY} - Will print PASS if and only if the output of otool does not contain SUBSECTIONS_VIA_SYMBOLS - - - - diff --git a/ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl b/ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl deleted file mode 100755 index fcc65eb..0000000 --- a/ld64/FireOpal/unit-tests/bin/exit-non-zero-pass.pl +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# ${PASS_UNLESS} "test name" command -# - -use strict; - -my $string = shift @ARGV; -my $ret = system(@ARGV); -my $exit_value = $ret >> 8; -my $signal_num = $ret & 127; -my $dumped_core = $ret & 128; -my $crashed = $signal_num + $dumped_core; - -if(0 == $exit_value || 0 != $crashed) -{ - printf("FAIL $string\n"); -} -else -{ - printf("PASS $string\n"); -} - -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl b/ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl deleted file mode 100755 index 7fb54b2..0000000 --- a/ld64/FireOpal/unit-tests/bin/fail-if-exit-non-zero.pl +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/perl -w - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -if(system(@ARGV) != 0) -{ - printf("FAIL $test_name\n"); - exit 1; -} - -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl b/ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl deleted file mode 100755 index 7859888..0000000 --- a/ld64/FireOpal/unit-tests/bin/fail-if-exit-zero.pl +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -w - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -my $ret = system(@ARGV); -my $exit_value = $ret >> 8; -my $signal_num = $ret & 127; -my $dumped_core = $ret & 128; -my $crashed = $signal_num + $dumped_core; - -if(0 == $exit_value || 0 != $crashed) -{ - printf("FAIL $test_name\n"); - exit 1; -} - -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl b/ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl deleted file mode 100755 index a44f5d3..0000000 --- a/ld64/FireOpal/unit-tests/bin/fail-if-no-stdin.pl +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# command | ${FAIL_IF_EMPTY} -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -if( eof STDIN ) -{ - printf("FAIL $test_name\n"); - exit 1; -} - -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl b/ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl deleted file mode 100755 index c0bf290..0000000 --- a/ld64/FireOpal/unit-tests/bin/fail-if-stdin.pl +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# command | ${FAIL_IF_STDIN} -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -if( eof STDIN ) -{ - exit 0; -} - -printf("FAIL $test_name\n"); -exit 1; diff --git a/ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl b/ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl deleted file mode 100755 index 6d49a25..0000000 --- a/ld64/FireOpal/unit-tests/bin/fail-iff-exit-zero.pl +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# ${FALL_IFF} command -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -my $ret = system(@ARGV); -my $exit_value = $ret >> 8; -my $signal_num = $ret & 127; -my $dumped_core = $ret & 128; -my $crashed = $signal_num + $dumped_core; - -if(0 == $exit_value || 0 != $crashed) -{ - printf("FAIL $test_name\n"); - exit 1; -} - -printf("PASS $test_name\n"); -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl b/ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl deleted file mode 100755 index 031a45d..0000000 --- a/ld64/FireOpal/unit-tests/bin/make-recursive-newtest.pl +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Data::Dumper; -use File::Find; -use Cwd qw(realpath); - -my @args = @ARGV; - -$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; -$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; - -my $makefiles = -{ - 'makefile' => undef, - 'Makefile' => undef, - 'makefile.newtest' => undef, - 'Makefile.newtest' => undef, -}; - -my $find_opts = -{ - 'wanted' => \&find_callback, -}; - -my $keywords = -{ - 'root' => '', - 'cwd' => '', - 'cmd' => '', - 'exit' => '', - 'stdout' => [], - 'stderr' => [], -}; - -my $keyword; -my $max_keyword_len = 0; -foreach $keyword (keys %$keywords) -{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } -my $delim = ':'; -$max_keyword_len += length($delim) + length(' '); - -my $last_keyword = ''; - -sub print_line -{ - my ($keyword, $val) = @_; - - if(!exists($$keywords{$keyword})) - { - print STDERR "error: keyword $keyword not in \$keywords set\n"; - exit(1); - } - - my $keyword_len = 0; - - if($keyword ne $last_keyword) - { - print("$keyword"); print($delim); - $keyword_len = length($keyword) + length($delim); - } - if($max_keyword_len > $keyword_len) - { - my $num_spaces = $max_keyword_len - $keyword_len; - print(' ' x $num_spaces); - } - print("$val"); - if(0) - { - $last_keyword = $keyword; - } -} - -my $root = '.'; -$root = &realpath($root); -print_line("root", "$root\n"); - -find($find_opts, $root); - -sub find_callback -{ - if(exists($$makefiles{$_})) - { - my $makefile = $_; - my $reldir = $File::Find::dir; - $reldir =~ s|^$root/||; - - &print_line("cwd", "\$root/$reldir\n"); - my $cmd = [ "make" ]; - - push @$cmd, "-f"; - push @$cmd, $makefile; - my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? - &print_line("cmd", "@$cmd\n"); - - open(SAVEOUT, ">&STDOUT") || die("$!"); - open(SAVEERR, ">&STDERR") || die("$!"); - open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); - open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); - - $ENV{UNIT_TEST_NAME} = $reldir; - my $exit = system(@$cmd); - - close(STDOUT) || die("$!"); - close(STDERR) || die("$!"); - open(STDOUT, ">&SAVEOUT") || die("$!"); - open(STDERR, ">&SAVEERR") || die("$!"); - - &print_line("exit", "$exit\n"); - - open(OUT, ") - { - &print_line("stdout", "$_"); - } - close(OUT) || die("$!"); - unlink("/tmp/unit-tests-stdout"); - - open(ERR, ") - { - &print_line("stderr", "$_"); - } - close(ERR) || die("$!"); - } - unlink("/tmp/unit-tests-stderr"); -} diff --git a/ld64/FireOpal/unit-tests/bin/make-recursive.pl b/ld64/FireOpal/unit-tests/bin/make-recursive.pl deleted file mode 100755 index f860985..0000000 --- a/ld64/FireOpal/unit-tests/bin/make-recursive.pl +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Data::Dumper; -use File::Find; -use Cwd qw(realpath); - -my @args = @ARGV; - -$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; -$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; - -my $makefiles = -{ - 'makefile' => undef, - 'Makefile' => undef, -}; - -my $find_opts = -{ - 'wanted' => \&find_callback, -}; - -my $keywords = -{ - 'root' => '', - 'cwd' => '', - 'cmd' => '', - 'exit' => '', - 'stdout' => [], - 'stderr' => [], -}; - -my $keyword; -my $max_keyword_len = 0; -foreach $keyword (keys %$keywords) -{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } -my $delim = ':'; -$max_keyword_len += length($delim) + length(' '); - -my $last_keyword = ''; - -sub print_line -{ - my ($keyword, $val) = @_; - - if(!exists($$keywords{$keyword})) - { - print STDERR "error: keyword $keyword not in \$keywords set\n"; - exit(1); - } - - my $keyword_len = 0; - - if($keyword ne $last_keyword) - { - print("$keyword"); print($delim); - $keyword_len = length($keyword) + length($delim); - } - if($max_keyword_len > $keyword_len) - { - my $num_spaces = $max_keyword_len - $keyword_len; - print(' ' x $num_spaces); - } - print("$val"); - if(0) - { - $last_keyword = $keyword; - } -} - -my $root = '.'; -$root = &realpath($root); -print_line("root", "$root\n"); - -find($find_opts, $root); - -sub find_callback -{ - if(exists($$makefiles{$_})) - { - my $makefile = $_; - my $reldir = $File::Find::dir; - $reldir =~ s|^$root/||; - - &print_line("cwd", "\$root/$reldir\n"); - my $cmd = [ "make" ]; - - my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? - &print_line("cmd", "@$cmd\n"); - - open(SAVEOUT, ">&STDOUT") || die("$!"); - open(SAVEERR, ">&STDERR") || die("$!"); - open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); - open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); - - $ENV{UNIT_TEST_NAME} = $reldir; - my $exit = system(@$cmd); - - close(STDOUT) || die("$!"); - close(STDERR) || die("$!"); - open(STDOUT, ">&SAVEOUT") || die("$!"); - open(STDERR, ">&SAVEERR") || die("$!"); - - &print_line("exit", "$exit\n"); - - open(OUT, ") - { - &print_line("stdout", "$_"); - } - close(OUT) || die("$!"); - unlink("/tmp/unit-tests-stdout"); - - open(ERR, ") - { - &print_line("stderr", "$_"); - } - close(ERR) || die("$!"); - } - unlink("/tmp/unit-tests-stderr"); -} diff --git a/ld64/FireOpal/unit-tests/bin/mkld b/ld64/FireOpal/unit-tests/bin/mkld deleted file mode 100755 index 33aacc1..0000000 --- a/ld64/FireOpal/unit-tests/bin/mkld +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -hide() -{ - $PROCTOR set_hidden $1 1 >/dev/null -} - -if [ -z "$1" ] - then echo "Usage: mkld HOST [ DBPATH ]" >&2 - exit 1 -fi - -if [ -z "$PROCTOR" ] - then PROCTOR=proctor -fi - -DBNAME="$2" -[ -z "$DBNAME" ] && DBNAME=ld -PROCTOR="$PROCTOR $1 $DBNAME" - -$PROCTOR tools gcc g++ objc obj-c++ libstdc++ ld ld ld_classic cctools -$PROCTOR sysattrs \ - ld64="ld64" \ - ld="ld (ld_classic)" \ - gcc="GCC" \ - cctools="cctools" \ - os="OS Build" \ - processor=Processor \ - platform=Platform \ - hostname="Hostname" \ - gcc_opts="gcc options" \ - g++_opts="g++ options" \ - objc_opts="objc options" \ - obj-c++_opts="obj-c++ options" \ - libstdc++_opts="libstdc++ options" \ - LANG="LANG environment variable" \ - LC_CTYPE="LC_CTYPE environment variable" \ - LC_MESSAGES="LC_MESSAGES environment variable" \ - LC_ALL="LC_ALL environment variable" \ - TMPDIR="TMPDIR environment variable" \ - GCC_EXEC_PREFIX="GCC_EXEC_PREFIX environment variable" \ - COMPILER_PATH="COMPILER_PATH environment variable" \ - LIBRARY_PATH="LIBRARY_PATH environment variable" \ - LANG="LANG environment variable" \ - CPATH="CPATH environment variable" \ - C_INCLUDE_PATH="C_INCLUDE_PATH environment variable" \ - CPLUS_INCLUDE_PATH="CPLUS_INCLUDE_PATH environment variable" \ - OBJC_INCLUDE_PATH="OBJC_INCLUDE_PATH environment variable" \ - DEPENDENCIES_OUTPUT="DEPENDENCIES_OUTPUT environment variable" \ - SUNPRO_DEPENDENCIES="SUNPRO_DEPENDENCIES environment variable" \ - -for TOOL in gcc g++ objc obj-c++ libstdc++ - do hide ${TOOL}_opts -done - -hide LANG -hide LC_CTYPE -hide LC_MESSAGES -hide LC_ALL -hide TMPDIR -hide GCC_EXEC_PREFIX -hide COMPILER_PATH -hide LIBRARY_PATH -hide LANG -hide CPATH -hide C_INCLUDE_PATH -hide CPLUS_INCLUDE_PATH -hide OBJC_INCLUDE_PATH -hide DEPENDENCIES_OUTPUT -hide SUNPRO_DEPENDENCIES - -$PROCTOR results PASS=1 XFAIL=1 KFAIL=1 FAIL=0 XPASS=0 KPASS=0 UNRESOLVED=0 TIMEDOUT=0 UNSUPPORTED=0 UNTESTED=0 -$PROCTOR severities logline NOTE WARNING ERROR diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl deleted file mode 100755 index beb76a9..0000000 --- a/ld64/FireOpal/unit-tests/bin/pass-iff-exit-non-zero.pl +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# ${PASS_IFF} command -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -my $ret = system(@ARGV); -my $exit_value = $ret >> 8; -my $signal_num = $ret & 127; -my $dumped_core = $ret & 128; -my $crashed = $signal_num + $dumped_core; - -if(0 == $exit_value || 0 != $crashed) -{ - printf("FAIL $test_name\n"); - exit 1; -} - -printf("PASS $test_name\n"); -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl deleted file mode 100755 index 07854b5..0000000 --- a/ld64/FireOpal/unit-tests/bin/pass-iff-exit-zero.pl +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# ${PASS_IFF} command -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -if(0 != system(@ARGV)) -{ - printf("FAIL $test_name\n"); - exit 1; -} - -printf("PASS $test_name\n"); -exit 0; diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl deleted file mode 100755 index 19b98b4..0000000 --- a/ld64/FireOpal/unit-tests/bin/pass-iff-no-stdin.pl +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# command | ${PASS_IFF_EMPTY} -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -if( eof STDIN ) -{ - printf("PASS $test_name\n"); - exit 0; -} - -printf("FAIL $test_name\n"); -exit 1; diff --git a/ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl b/ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl deleted file mode 100755 index d5ee99f..0000000 --- a/ld64/FireOpal/unit-tests/bin/pass-iff-stdin.pl +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/perl -w - -# -# Usage: -# -# command | ${PASS_IFF_STDIN} -# - -use strict; - -my $test_name = ""; -if ( exists $ENV{UNIT_TEST_NAME} ) { - $test_name = $ENV{UNIT_TEST_NAME}; -} - -if( eof STDIN ) -{ - printf("FAIL $test_name\n"); - exit 1 -} - -printf("PASS $test_name\n"); -exit 0; - diff --git a/ld64/FireOpal/unit-tests/bin/result-filter.pl b/ld64/FireOpal/unit-tests/bin/result-filter.pl deleted file mode 100755 index 8168e79..0000000 --- a/ld64/FireOpal/unit-tests/bin/result-filter.pl +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/perl -w - -use strict; -use Data::Dumper; -use File::Find; -use Cwd; - -$Data::Dumper::Terse = 1; - -my $root = undef; -my $entry = ''; -my $pass_count = 0; -my $total_count = 0; - -# first match "root: " - -# a line starting with "cwd:" marks the beginning of a new test case -# call process_entry() on each test case -while(<>) -{ - if(m/^root:\s+(.*?)$/) - { - $root = $1; - } - elsif(m/^cwd:\s+(.*?)$/) - { - if(length($entry)) - { - &process_entry($root, $entry); - $entry = ''; - } - $entry .= $_; - } - else - { - $entry .= $_; - } -} -# don't forget last test case (no cwd: to mark end) -if(length($entry)) -{ - &process_entry($root, $entry); -} - -# show totals -my $percentage = $pass_count * 100 / $total_count; -printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; - - -sub process_entry -{ - my ($root, $lines) = @_; - - # build an associative array of keys to value(s) - my $lines_seq = [split /\n/, $lines]; - #print Dumper($lines_seq); - my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; - my $line; - foreach $line (@$lines_seq) - { - if($line =~ m/^(\w+):\s+(.*)$/) - { - my $key = $1; - my $val = $2; - if(!exists($$tbl{$key})) - { $$tbl{$key} = ''; } - - if($key eq 'stdout' || $key eq 'stderr') # if type is @array - { - push @{$$tbl{$key}}, $val; - } - else - { - $$tbl{$key} .= $val; - } - } - else - { - print "ERROR: $line"; - } - } - #print Dumper($tbl); - #return; - - my $test_name = $$tbl{cwd}; - if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) - { - $test_name = $1; - } - - #if make failed (exit was non-zero), mark this as a failure - if(0 ne $$tbl{exit}) - { - printf "%-40s FAIL Makefile failure\n", $test_name; - $total_count++; - return; - } - - #if there was any output to stderr, mark this as a failure - foreach $line (@{$$tbl{stderr}}) - { - printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; - $total_count++; - return; - } - - # scan all stdout looking for lines that start with PASS or FAIL - my $seen_result = 0; - foreach $line (@{$$tbl{stdout}}) - { - if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) - { - $total_count++; - if($line =~ m/^PASS.+/) - { - $pass_count++; - } - else - { - # only print failure lines - printf "%-40s %s\n", $test_name, $line; - } - $seen_result = 1; - } - } - if(!$seen_result) - { - printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; - $total_count++; - } -} diff --git a/ld64/FireOpal/unit-tests/bin/rm-stale-test-logs b/ld64/FireOpal/unit-tests/bin/rm-stale-test-logs deleted file mode 100755 index 936dec1..0000000 --- a/ld64/FireOpal/unit-tests/bin/rm-stale-test-logs +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -usage() { - echo Usage: $0 number-of-tests-logs-to-keep - echo where number-of-tests-logs-to-keep must be a non-zero integer - exit -} - -# Usage: if no arguments -[ -z "$1" ] && usage - -# Check if requesting 0 tests to remain! -[ "$1" -ne 0 ] - -# don't test directly--use the result value -# because the command can fail for badly formed integers -[ $? -ne 0 ] && usage - -# get the dir names of all tests in date order -ls -1dtr /tmp/proctor*>/tmp/all$$ 2>/dev/null - -# select the last few to keep -tail -$1 /tmp/all$$>/tmp/keep$$ - -# get a list of the others -DELLIST=`diff /tmp/all$$ /tmp/keep$$|grep '^<'|sed -e 's/^< //'` - -# any work to do? -if [ "$DELLIST" ] -then - echo rm -rf $DELLIST - rm -rf $DELLIST -fi - -# rm the temps -rm /tmp/all$$ /tmp/keep$$ diff --git a/ld64/FireOpal/unit-tests/clean-tests b/ld64/FireOpal/unit-tests/clean-tests deleted file mode 100755 index 4c38b4e..0000000 --- a/ld64/FireOpal/unit-tests/clean-tests +++ /dev/null @@ -1,63 +0,0 @@ -find_makefile() -{ - local j - - MF="" - - if [ ! -d $1 ] - then - return 1 - fi - - for j in Makefile makefile Makefile.newtest makefile.newtest - do - [ -f $1/$j ] && MF=$j - done - - [ "$MF" ] && return 0 - return 1 -} - -find_path_to_test_dir() -{ - # FIND THE PATH TO THE TEST DIR - # SO THAT WE CAN ADD THE BIN DIR INTO - # THE SEARCH PATH - - # remember the top level execution dir - chmod +x "$0" # just in case - - #add path to $0 into search - savedir=$PWD - DIRNAME=`dirname $0` - [ -d "$DIRNAME" ] && cd "$DIRNAME" - PATH=$PATH:$PWD - cd "$savedir" - - chmod +x "$0" # just in case - EXECNAME=`which $0` - DIRNAME=`dirname "$EXECNAME"` - if [ -d "$DIRNAME" ] - then - TEST_HOME_DIR="$DIRNAME" - else - TEST_HOME_DIR="$savedir" # Give up and assume current dir - fi - - PATH="$PATH":"$TEST_HOME_DIR"/bin:"$savedir" -} - -find_path_to_test_dir - -cd "$TEST_HOME_DIR/test-cases" - -for i in * -do - [ -d "$i" ] && - ( - if find_makefile $i - then - make -C $i -f $MF -s -k clean >/dev/null 2>/dev/null - fi - ) -done diff --git a/ld64/FireOpal/unit-tests/include/common.makefile b/ld64/FireOpal/unit-tests/include/common.makefile deleted file mode 100644 index 70e05f8..0000000 --- a/ld64/FireOpal/unit-tests/include/common.makefile +++ /dev/null @@ -1,76 +0,0 @@ -# stuff to include in every test Makefile - -SHELL = /bin/sh - -# set default to be host -ARCH ?= $(shell arch) - -# set default to be all -VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6" - -MYDIR=$(shell cd ../../bin;pwd) - -# if run within Xcode, add the just built tools to the command path -ifdef BUILT_PRODUCTS_DIR - PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH} -else - ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" - RELEASEDIR=$(shell cd ../../../build/Release;pwd) - PATH := ${RELEASEDIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${RELEASEDIR}:${MYDIR}:${COMPILER_PATH} - else - PATH := ${MYDIR}:${PATH}: - COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: - endif -endif -export PATH -export COMPILER_PATH - -LD = ld -OBJECTDUMP = ObjectDump -MACHOCHECK = machocheck -OTOOL = otool - -CC = gcc-4.0 -arch ${ARCH} -CCFLAGS = -Wall -std=c99 -ASMFLAGS = - -CXX = g++-4.0 -arch ${ARCH} -CXXFLAGS = -Wall - -ifeq ($(ARCH),armv6) - LDFLAGS := -syslibroot /Developer/SDKs/Purple - override FILEARCH = arm -else - FILEARCH = $(ARCH) -endif - -ifeq ($(ARCH),thumb) - LDFLAGS := -syslibroot /Developer/SDKs/Purple - CCFLAGS += -mthumb - CXXFLAGS += -mthumb - override ARCH = armv6 - override FILEARCH = arm -else - FILEARCH = $(ARCH) -endif - -RM = rm -RMFLAGS = -rf - -# utilites for Makefiles -PASS_IFF = pass-iff-exit-zero.pl -PASS_IFF_SUCCESS = ${PASS_IFF} -PASS_IFF_EMPTY = pass-iff-no-stdin.pl -PASS_IFF_STDIN = pass-iff-stdin.pl -FAIL_IFF = fail-iff-exit-zero.pl -FAIL_IFF_SUCCESS = ${FAIL_IFF} -PASS_IFF_ERROR = pass-iff-exit-non-zero.pl -FAIL_IF_ERROR = fail-if-exit-non-zero.pl -FAIL_IF_SUCCESS = fail-if-exit-zero.pl -FAIL_IF_EMPTY = fail-if-no-stdin.pl -FAIL_IF_STDIN = fail-if-stdin.pl -PASS_IFF_GOOD_MACHO = ${PASS_IFF} ${MACHOCHECK} -FAIL_IF_BAD_MACHO = ${FAIL_IF_ERROR} ${MACHOCHECK} -FAIL_IF_BAD_OBJ = ${FAIL_IF_ERROR} ${OBJECTDUMP} >/dev/null diff --git a/ld64/FireOpal/unit-tests/include/test.h b/ld64/FireOpal/unit-tests/include/test.h deleted file mode 100644 index faca714..0000000 --- a/ld64/FireOpal/unit-tests/include/test.h +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include - -#define DEFINE_TEST_FUNC(name) \ - static \ - inline \ - void \ - name(const char *format, ...) \ - { \ - va_list args; \ - va_start(args, format); \ - common(stdout, #name, format, args); \ - va_end(args); \ - return; \ - } - -static -inline -void -common(FILE *file, const char *prefix, const char *format, va_list args) -{ - fprintf(file, "%s \"", prefix); - vfprintf(file, format, args); - fprintf(file, "\"\n"); // should check for trailing newline - return; -} - -DEFINE_TEST_FUNC(PASS); -DEFINE_TEST_FUNC(XPASS); -DEFINE_TEST_FUNC(FAIL); -DEFINE_TEST_FUNC(XFAIL); - -DEFINE_TEST_FUNC(UNTESTED); -DEFINE_TEST_FUNC(UNSUPPORTED); -DEFINE_TEST_FUNC(UNRESOLVED); diff --git a/ld64/FireOpal/unit-tests/proctor-run b/ld64/FireOpal/unit-tests/proctor-run deleted file mode 100755 index e7bdea4..0000000 --- a/ld64/FireOpal/unit-tests/proctor-run +++ /dev/null @@ -1,204 +0,0 @@ -#!/bin/sh - -all_archs="ppc ppc64 i386 x86_64" - -sysattr() -{ - echo " " -} - -doresults() -{ - local ver - - echo "" - - echo " " - sysattr cctools "`as&1 |sed 's/.*cctools-//;s/,.*//'`" - sysattr hostname "`hostname`" - sysattr os "`uname -r`" - sysattr platform "`uname -m`" - sysattr ld64 "`ld64 -v 2>&1|sed 's/.*PROJECT://;s/ .*//'`" - sysattr ld "`ld_classic -v 2>&1|sed 's/.*cctools-//;s/ .*//'`" - sysattr gcc "`gcc --version|head -1`" - sysattr processor "`uname -p`" - sysattr LANG "$LANG" - sysattr LC_CTYPE "$LC_CTYPE" - sysattr LC_MESSAGES "$LC_MESSAGES" - sysattr LC_ALL "$LC_ALL" - sysattr TMPDIR "$TMPDIR" - sysattr GCC_EXEC_PREFIX "$GCC_EXEC_PREFIX" - sysattr COMPILER_PATH "$COMPILER_PATH" - sysattr LIBRARY_PATH "$LIBRARY_PATH" - sysattr LANG "$LANG" - sysattr CPATH "$CPATH" - sysattr C_INCLUDE_PATH "$C_INCLUDE_PATH" - sysattr CPLUS_INCLUDE_PATH "$CPLUS_INCLUDE_PATH" - sysattr OBJC_INCLUDE_PATH "$OBJC_INCLUDE_PATH" - sysattr DEPENDENCIES_OUTPUT "$DEPENDENCIES_OUTPUT" - sysattr SUNPRO_DEPENDENCIES "$SUNPRO_DEPENDENCIES" - echo " " - - echo "" - echo "" - echo " " - for i in $* - do - echo " " - cat $i - echo " " - done - - echo " " - echo "" - echo "" - echo "" - - #rm $* -} - -find_path_to_test_dir() -{ - # FIND THE PATH TO THE TEST DIR - # SO THAT WE CAN ADD THE BIN DIR INTO - # THE SEARCH PATH - - # remember the top level execution dir - chmod +x "$0" # just in case - - #add path to $0 into search - local savedir - savedir=$PWD - DIRNAME=`dirname $0` - [ -d "$DIRNAME" ] && cd "$DIRNAME" - PATH=$PATH:$PWD - cd "$savedir" - - chmod +x "$0" # just in case - EXECNAME=`which $0` - DIRNAME=`dirname "$EXECNAME"` - if [ -d "$DIRNAME" ] - then - TEST_HOME_DIR=`cd "$DIRNAME";pwd` - fi - - if [ -z "$TEST_HOME_DIR" ] - then - TEST_HOME_DIR="$savedir" # Give up and assume current dir - fi - - cd "$TEST_HOME_DIR" - cd ../build/Release - - PATH="$PWD":"$TEST_HOME_DIR/bin":"$PATH" - cd "$savedir" -} - -start_time=`date +%s` - -find_path_to_test_dir - -# Execute from the location of the script; or if not found the current loc -[ -d $TEST_HOME_DIR/test-cases ] && cd $TEST_HOME_DIR/test-cases || cd test-cases - -rm-stale-test-logs 3 >/dev/null & - -make -C ../src # make sure the binaries are available - -DATEFORMAT=`date +%F-%H%M | sed -e 's/ //'` -tmpdir=/tmp/proctor$DATEFORMAT - -if ! mkdir $tmpdir >/dev/null 2>/dev/null -then - rm -rf $tmpdir - mkdir $tmpdir -fi - - -linestart=0 -if [ x$1 = x-comment ] -then - shift - comment="$1" - shift -fi - -find_makefile() -{ - local j - - MF="" - - if [ ! -d $1 ] - then - return 1 - fi - - for j in Makefile makefile - do - [ -f $1/$j ] && MF=$j - done - - if [ "$NEWTEST" ] - then - for j in Makefile.newtest makefile.newtest - do - [ -f $1/$j ] && MF=$j - done - fi - - [ "$MF" ] && return 0 - return 1 -} - -one_test() -{ - echo cwd: $1 - echo cmd: $1 ARCH="$arch" - make -f "$MF" -C $1 ARCH="$arch" 2>$tmpdir/stderr >$tmpdir/stdout - result=$? - sed 's/^/stdout: /'<$tmpdir/stdout - sed 's/^/stderr: /'<$tmpdir/stderr - echo exit: $? -} - -if [ "$1" ] -then - i="$1" - for arch in $all_archs - do - rm -f $tmpdir/$arch - if find_makefile $i - then - one_test $i - fi - #fi | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch - linestart=`expr $linestart + 10000` - done | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch -else - for arch in $all_archs - do - rm -f $tmpdir/$arch - for i in * - do - if find_makefile $i - then - one_test $i - fi - done | tee -a $tmpdir/raw | ../bin/results-to-xml $linestart>>$tmpdir/$arch - linestart=`expr $linestart + 10000` - done -fi - -(cd $tmpdir; doresults $all_archs)>$tmpdir/o.xml -../bin/xmlparser $tmpdir/o.xml >/dev/null -if [ $? = 0 ] -then - if ! proctor localhost ld import $tmpdir/o.xml - then - proctor database load failed! - fi -else - echo Test results not loaded: internal xml error! - exit 1 -fi diff --git a/ld64/FireOpal/unit-tests/run-all-unit-tests b/ld64/FireOpal/unit-tests/run-all-unit-tests deleted file mode 100755 index e21c2b3..0000000 --- a/ld64/FireOpal/unit-tests/run-all-unit-tests +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -unset RC_TRACE_DYLIBS -unset RC_TRACE_ARCHIVES -unset LD_TRACE_DYLIBS -unset LD_TRACE_ARCHIVES - -export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib -# cd into test-cases directory -cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` - -[ "$PROCTORRUN" ] && exec ../proctor-run - -all_archs="x86_64 armv6 thumb ppc ppc64 i386 " -valid_archs="x86_64 armv6 ppc ppc64 i386 " - -# clean first -../bin/make-recursive.pl clean > /dev/null - -mkdir /tmp/$$ -for arch in $all_archs -do - echo "" - echo " * * * Running all unit tests for architecture $arch * * *" - - # build architecture - [ "$NEWTEST" ] && NT=-newtest - - ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$valid_archs" | ../bin/result-filter.pl - - # clean up so svn is happy - ../bin/make-recursive.pl ARCH=$arch clean > /dev/null - - echo "" -done diff --git a/ld64/FireOpal/unit-tests/run-all-unit-tests-debug b/ld64/FireOpal/unit-tests/run-all-unit-tests-debug deleted file mode 100755 index a239748..0000000 --- a/ld64/FireOpal/unit-tests/run-all-unit-tests-debug +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -# cd into test-cases directory -cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` - -[ "$PROCTORRUN" ] && exec ../proctor-run - -all_archs="ppc ppc64 i386 x86_64" - -mkdir /tmp/$$ -for arch in $all_archs -do - echo "" - echo " * * * Running all unit tests for architecture $arch * * *" - - # build architecture - [ "$NEWTEST" ] && NT=-newtest - - mkdir /tmp/$$ - ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$all_archs" | tee /tmp/$$/raw | ../bin/result-filter.pl | tee /tmp/$$/sum - - # clean up so svn is happy - ../bin/make-recursive.pl ARCH=$arch clean > /dev/null - - echo "" -done diff --git a/ld64/FireOpal/unit-tests/src/Makefile b/ld64/FireOpal/unit-tests/src/Makefile deleted file mode 100644 index d3bdeab..0000000 --- a/ld64/FireOpal/unit-tests/src/Makefile +++ /dev/null @@ -1,9 +0,0 @@ - -all: ../bin/results-to-xml ../bin/xmlparser - -../bin/results-to-xml: results-to-xml.cpp - g++ -g -O -Wall $< -o ../bin/results-to-xml - -../bin/xmlparser: - cd xmlparser; xcodebuild -alltargets - cp -p xmlparser/build/Release/xmlparser ../bin/. diff --git a/ld64/FireOpal/unit-tests/src/results-to-xml.cpp b/ld64/FireOpal/unit-tests/src/results-to-xml.cpp deleted file mode 100644 index a305347..0000000 --- a/ld64/FireOpal/unit-tests/src/results-to-xml.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include -#include -#include - -using namespace std; - -#define NELEMENTS(a) (sizeof (a)/sizeof *(a)) - -#define NO_RESULT (-1) -#define PASS 1 -#define FAIL 0 - -#define DBG bhole - -void -bhole(...) -{ -} - -class line_category -{ -public: - const char *key; - char line[99999]; - void (*test)(line_category &); - int test_result; -} lc; - -void -deft(line_category &l) -{ - l.test_result = PASS; -} - -void -pass_fail(line_category &l) -{ - if(FAIL!=l.test_result) - l.test_result = strnstr(l.line, "FAIL", 4)? FAIL: PASS; -} - -void -stderr_output(line_category &l) -{ - if(FAIL==l.test_result) - return; - - if(l.line[0]) - l.test_result = FAIL; -} - -void -exit_test(line_category &l) -{ - if(!atoi(l.line)) { - DBG("exit_test(%s)==%d\n", l.line, atoi(l.line)); - l.test_result = PASS; - } -} - -#define STDOUT "stdout: " -#define STDERR "stderr: " -#define CWD "cwd: " -#define CMD "cmd: " -#define SEXIT "exit: " -line_category line_categories[] = { - { CWD, "" , deft, NO_RESULT}, /* must be first */ - { CMD, "", deft, NO_RESULT}, - { STDOUT, "", pass_fail, NO_RESULT}, - { STDERR, "", stderr_output, NO_RESULT }, - { SEXIT, "", exit_test, NO_RESULT }, -}; - -static line_category no_line_category = { "none", "no test", deft, NO_RESULT }; - -line_category & -retrieve(line_category &l, const char *s) -{ - unsigned j; - line_category *lp = &l; - //int final_result = PASS; - - for(j=0; jkey, s)) { - char *p; - - for(p=(char *)lp->line; *p; ++p) { - switch(*p) { - case '\0': - break; - case '"': - case '<': - *p = ' '; - // fall thru - default: - continue; - } - } -DBG("FOUND line_categories[j].line==%s\n", lp->line); - return line_categories[j]; - } - } - - return no_line_category; -} - -void -xml_string_print(FILE *strm, const char *s) -{ - fputc('"', strm); - for( ; ; ++s) { - switch(*s) { - case '\0': - break; - case '&': - fputs("&", strm); - continue; - default: - fputc(*s, strm); - continue; - } - break; - } - fputc('"', strm); -} - -// -// FAIL if stderr non-zero -// FAIL if stdout=="FAIL" -// UNRESOLVED if make exit non-zero -// PASS otherwise -// - -static int cnt; -void -dump_test(void) -{ - unsigned j; - int final_result = PASS; - - for(j=0; j\n"); - - printf(" \n"); - s = retrieve(line_categories[0], STDERR).line; - if(*s) { - printf(" \n"); - } -#if 1 - s = retrieve(line_categories[0], STDOUT).line; - if(*s) { - printf(" \n"); - } -#endif - printf(" \n"); - printf("\n\n"); - - for(j=0; j1) - cnt = atoi(argv[1]); - - for(;;) { - char line[99999]; - int i; - - line[0] = '\0'; - fgets(line, sizeof line, stdin); - if(feof(stdin)) { - dump_test(); - break; - } - - for(i=0; ; ++i) { - size_t len = strlen(line_categories[i].key); - - //DBG("strnstr(%s, %s, %u)\n", line, line_categories[i].key, len); - if(strnstr(line, line_categories[i].key, len)) { - if(firsttime) - firsttime = 0; - else if(0==i) - dump_test(); - - char *lp = &line[len]; - //DBG("%s%s", line_categories[i].key, lp); - strncpy(line_categories[i].line, lp, sizeof line_categories[i].line); - line_categories[i].test(line_categories[i]); - break; - } - - if(i==NELEMENTS(line_categories)-1) { - DBG("BADLINE:%s", line); - break; - } - } - } - return 0; -} diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 deleted file mode 100644 index 6eab06a..0000000 --- a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.1 +++ /dev/null @@ -1,79 +0,0 @@ -.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. -.\"See Also: -.\"man mdoc.samples for a complete listing of options -.\"man mdoc for the short list of editing options -.\"/usr/share/misc/mdoc.template -.Dd 9/18/06 \" DATE -.Dt xmlparser 1 \" Program name and manual section number -.Os Darwin -.Sh NAME \" Section Header - required - don't modify -.Nm xmlparser, -.\" The following lines are read in generating the apropos(man -k) database. Use only key -.\" words here as the database is built based on the words here and in the .ND line. -.Nm Other_name_for_same_program(), -.Nm Yet another name for the same program. -.\" Use .Nm macro to designate other names for the documented program. -.Nd This line parsed for whatis database. -.Sh SYNOPSIS \" Section Header - required - don't modify -.Nm -.Op Fl abcd \" [-abcd] -.Op Fl a Ar path \" [-a path] -.Op Ar file \" [file] -.Op Ar \" [file ...] -.Ar arg0 \" Underlined argument - use .Ar anywhere to underline -arg2 ... \" Arguments -.Sh DESCRIPTION \" Section Header - required - don't modify -Use the .Nm macro to refer to your program throughout the man page like such: -.Nm -Underlining is accomplished with the .Ar macro like this: -.Ar underlined text . -.Pp \" Inserts a space -A list of items with descriptions: -.Bl -tag -width -indent \" Begins a tagged list -.It item a \" Each item preceded by .It macro -Description of item a -.It item b -Description of item b -.El \" Ends the list -.Pp -A list of flags and their descriptions: -.Bl -tag -width -indent \" Differs from above in tag removed -.It Fl a \"-a flag as a list item -Description of -a flag -.It Fl b -Description of -b flag -.El \" Ends the list -.Pp -.\" .Sh ENVIRONMENT \" May not be needed -.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 -.\" .It Ev ENV_VAR_1 -.\" Description of ENV_VAR_1 -.\" .It Ev ENV_VAR_2 -.\" Description of ENV_VAR_2 -.\" .El -.Sh FILES \" File used or created by the topic of the man page -.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact -.It Pa /usr/share/file_name -FILE_1 description -.It Pa /Users/joeuser/Library/really_long_file_name -FILE_2 description -.El \" Ends the list -.\" .Sh DIAGNOSTICS \" May not be needed -.\" .Bl -diag -.\" .It Diagnostic Tag -.\" Diagnostic informtion here. -.\" .It Diagnostic Tag -.\" Diagnostic informtion here. -.\" .El -.Sh SEE ALSO -.\" List links in ascending order by section, alphabetically within a section. -.\" Please do not reference files that do not exist without filing a bug report -.Xr a 1 , -.Xr b 1 , -.Xr c 1 , -.Xr a 2 , -.Xr b 2 , -.Xr a 3 , -.Xr b 3 -.\" .Sh BUGS \" Document known, unremedied bugs -.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m deleted file mode 100644 index a9c82fc..0000000 --- a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.m +++ /dev/null @@ -1,25 +0,0 @@ -#import - -int main(int argc, char *argv[]) { - [[NSAutoreleasePool alloc] init]; - - if(argc != 2) { - NSLog(@"Usage: %s path-to-XML\n", argv[0]); - return 1; - } - NSString *path = [NSString stringWithUTF8String:argv[1]]; - - NSError *err = nil; - NSXMLDocument *doc = [[NSXMLDocument alloc] - initWithContentsOfURL:[NSURL - fileURLWithPath:path] - options:0 - error:&err]; - if(err) { - NSLog(@"ERROR: %@", err); - return 1; - } else { - NSLog(@"Parsed!"); - return 0; - } -} diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj deleted file mode 100644 index 9492293..0000000 --- a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj +++ /dev/null @@ -1,218 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 42; - objects = { - -/* Begin PBXBuildFile section */ - 8DD76F9A0486AA7600D96B5E /* xmlparser.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* xmlparser.m */; settings = {ATTRIBUTES = (); }; }; - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; - 8DD76F9F0486AA7600D96B5E /* xmlparser.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* xmlparser.1 */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - 8DD76F9F0486AA7600D96B5E /* xmlparser.1 in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 08FB7796FE84155DC02AAC07 /* xmlparser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = xmlparser.m; sourceTree = ""; }; - 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 32A70AAB03705E1F00C91783 /* xmlparser_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmlparser_Prefix.pch; sourceTree = ""; }; - 8DD76FA10486AA7600D96B5E /* xmlparser */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xmlparser; sourceTree = BUILT_PRODUCTS_DIR; }; - C6859EA3029092ED04C91782 /* xmlparser.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = xmlparser.1; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 08FB7794FE84155DC02AAC07 /* xmlparser */ = { - isa = PBXGroup; - children = ( - 08FB7795FE84155DC02AAC07 /* Source */, - C6859EA2029092E104C91782 /* Documentation */, - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, - 1AB674ADFE9D54B511CA2CBB /* Products */, - ); - name = xmlparser; - sourceTree = ""; - }; - 08FB7795FE84155DC02AAC07 /* Source */ = { - isa = PBXGroup; - children = ( - 32A70AAB03705E1F00C91783 /* xmlparser_Prefix.pch */, - 08FB7796FE84155DC02AAC07 /* xmlparser.m */, - ); - name = Source; - sourceTree = ""; - }; - 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { - isa = PBXGroup; - children = ( - 08FB779EFE84155DC02AAC07 /* Foundation.framework */, - ); - name = "External Frameworks and Libraries"; - sourceTree = ""; - }; - 1AB674ADFE9D54B511CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8DD76FA10486AA7600D96B5E /* xmlparser */, - ); - name = Products; - sourceTree = ""; - }; - C6859EA2029092E104C91782 /* Documentation */ = { - isa = PBXGroup; - children = ( - C6859EA3029092ED04C91782 /* xmlparser.1 */, - ); - name = Documentation; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 8DD76F960486AA7600D96B5E /* xmlparser */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "xmlparser" */; - buildPhases = ( - 8DD76F990486AA7600D96B5E /* Sources */, - 8DD76F9B0486AA7600D96B5E /* Frameworks */, - 8DD76F9E0486AA7600D96B5E /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = xmlparser; - productInstallPath = "$(HOME)/bin"; - productName = xmlparser; - productReference = 8DD76FA10486AA7600D96B5E /* xmlparser */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 08FB7793FE84155DC02AAC07 /* Project object */ = { - isa = PBXProject; - buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "xmlparser" */; - hasScannedForEncodings = 1; - mainGroup = 08FB7794FE84155DC02AAC07 /* xmlparser */; - projectDirPath = ""; - targets = ( - 8DD76F960486AA7600D96B5E /* xmlparser */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 8DD76F990486AA7600D96B5E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8DD76F9A0486AA7600D96B5E /* xmlparser.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 1DEB927508733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = xmlparser_Prefix.pch; - INSTALL_PATH = "$(HOME)/bin"; - PRODUCT_NAME = xmlparser; - ZERO_LINK = YES; - }; - name = Debug; - }; - 1DEB927608733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = ( - ppc, - i386, - ); - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; - GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = xmlparser_Prefix.pch; - INSTALL_PATH = "$(HOME)/bin"; - PRODUCT_NAME = xmlparser; - }; - name = Release; - }; - 1DEB927908733DD40010E9CD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; - ZERO_LINK = YES; - }; - name = Debug; - }; - 1DEB927A08733DD40010E9CD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; - ZERO_LINK = NO; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "xmlparser" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927508733DD40010E9CD /* Debug */, - 1DEB927608733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "xmlparser" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1DEB927908733DD40010E9CD /* Debug */, - 1DEB927A08733DD40010E9CD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; -} diff --git a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch b/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch deleted file mode 100644 index 530e057..0000000 --- a/ld64/FireOpal/unit-tests/src/xmlparser/xmlparser_Prefix.pch +++ /dev/null @@ -1,7 +0,0 @@ -// -// Prefix header for all source files of the 'xmlparser' target in the 'xmlparser' project. -// - -#ifdef __OBJC__ - #import -#endif diff --git a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile deleted file mode 100644 index a3a256f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that -O2 optimization fails when trying to make a long 16-byte aligned. -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o - - # verify that the alignment is correct in the .o - ObjectDump -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null - - # now verify the executable - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH} - ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai\>' >/dev/null" - ${PASS_IFF_GOOD_MACHO} tl_test2-${ARCH} - -clean: - rm -rf tl_test2-* diff --git a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt deleted file mode 100644 index eb9eaf5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test 16 byte alignment with -O2 optimization. Radar #4662185 diff --git a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c b/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c deleted file mode 100644 index ff27fec..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/16-byte-alignment/tl_test2.c +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include - -int i = 1; -int ai __attribute__ ((aligned (16))) = 1; - -int main() { - - long addr = (long)&ai; - i = ai; - - if (addr & 0xf) { - printf("failed: ai = %p\n", (void *)addr); - return 1; - } - - printf("passed: ai = %p\n", (void *)addr); - return 0; - -} diff --git a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile deleted file mode 100644 index 806c64e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Validate linker processing of absolute symbols -# - -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 - nm -m all.o | grep _myAbs | grep absolute | ${FAIL_IF_EMPTY} - ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker - ${PASS_IFF_GOOD_MACHO} main2 - -clean: - rm -rf main.o abs.o all.o main main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s deleted file mode 100644 index 216867c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/abs.s +++ /dev/null @@ -1,3 +0,0 @@ - - .globl _myAbs - .set _myAbs, 0xfe000000 diff --git a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c b/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c deleted file mode 100644 index eec3f61..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/absolute-symbol/main.c +++ /dev/null @@ -1,5 +0,0 @@ - -extern int* myAbs; - -int main() { return *myAbs; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile deleted file mode 100644 index 9e87329..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify that added aliases to a .o -# file via the command line is the same as doing so in the sources. -# The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${ASMFLAGS} aliases.s -DALIASES=1 -c -o aliases.source.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.source.${ARCH}.o > aliases.source.${ARCH}.o.dump - - ${CC} ${ASMFLAGS} aliases.s -c -o aliases.tmp.${ARCH}.o - ${FAIL_IF_BAD_OBJ} aliases.tmp.${ARCH}.o - - ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -o aliases.cmdline.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.cmdline.${ARCH}.o > aliases.cmdline.${ARCH}.o.dump - ${FAIL_IF_ERROR} diff aliases.source.${ARCH}.o.dump aliases.cmdline.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.file.${ARCH}.o > aliases.file.${ARCH}.o.dump - ${PASS_IFF} diff aliases.source.${ARCH}.o.dump aliases.file.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s deleted file mode 100644 index ffab4a9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.s +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - .text - -_temp: nop - nop - - .globl _foo -_foo: nop - nop - -#if ALIASES - .globl _fooalt - .globl _fooalt2 -/* this should make an alias "_fooalt" for "_foo" */ -_fooalt = _foo -_fooalt2 = _foo -#endif - -_bar: nop - nop - - - .subsections_via_symbols \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt b/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt deleted file mode 100644 index 291f2f7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/alias-command-line/aliases.txt +++ /dev/null @@ -1,6 +0,0 @@ -_foo _fooalt -# comment -_foo _fooalt2 - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile b/ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile deleted file mode 100644 index f4bfdc8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/alias-objects/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify a .o file with aliases -# can round trip through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${ASMFLAGS} aliases.s -c -o aliases.${ARCH}.o - ${LD} -arch ${ARCH} -r -keep_private_externs aliases.${ARCH}.o -o aliases-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.${ARCH}.o > aliases.${ARCH}.o.dump - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.${ARCH}.o > aliases-r.${ARCH}.o.dump - ${PASS_IFF} diff aliases.${ARCH}.o.dump aliases-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s b/ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s deleted file mode 100644 index b21f8c7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/alias-objects/aliases.s +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - .text - -_temp: nop - nop - - .globl _foo -_foo: nop - nop - - .globl _fooalt - .globl _fooalt2 -/* this should make an alias "_fooalt" for "_foo" */ -_fooalt = _foo -_fooalt2 = _foo - -_bar: nop - nop - - - .subsections_via_symbols \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile b/ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile deleted file mode 100644 index 6b6a50f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/align-modulus/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify that the modules of -# symbol _b is maintained. The address for _b must be -# 3 mod 16. Therefore the last hexdigit of the address -# must be 3. - -run: all - -all: - ${CC} ${ASMFLAGS} -dynamiclib -single_module -dead_strip foo.c align.s -exported_symbols_list foo.exp -o foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib - nm foo.${ARCH}.dylib | grep "3 d _b" | ${PASS_IFF_STDIN} - -clean: - rm -rf *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s b/ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s deleted file mode 100644 index a288960..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/align-modulus/align.s +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - .data - .align 0 -_a: .byte 3 - .byte 3 - .byte 3 - .globl _b -_b: .byte 4 ;# address is 3 - .align 4 -L1: .quad 0 -_c: .long 8 - - .subsections_via_symbols - diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt b/ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt deleted file mode 100644 index 240c171..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/align-modulus/comment.txt +++ /dev/null @@ -1,2 +0,0 @@ -The point of this test is to verify that the modules of symbol _b is maintained. The address for _b must be -3 mod 16. Therefore the last hexdigit of the address must be 3. diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c b/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c deleted file mode 100644 index 0b88dff..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -extern char b; -int my = 2; - -char foo() -{ - return my+b; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp b/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp deleted file mode 100644 index 70eefe9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/align-modulus/foo.exp +++ /dev/null @@ -1 +0,0 @@ -_foo diff --git a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile deleted file mode 100644 index 6e0be3f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the we set the stack execution bit properly. -# - -run: all - -all: - -# Test with the flag - ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -Wl,-allow_stack_execute - ${FAIL_IF_BAD_MACHO} foo-${ARCH} - ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${FAIL_IF_EMPTY} - rm -f foo-${ARCH} - -# Test without the flag - ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} - ${FAIL_IF_BAD_MACHO} foo-${ARCH} - ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${PASS_IFF_EMPTY} - -clean: - rm -rf foo-* diff --git a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt deleted file mode 100644 index 525ab2b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test the we set the stack execution bit properly. diff --git a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c b/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c deleted file mode 100644 index 57ed6ba..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allow-stack-execute/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int main (void) -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile b/ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile deleted file mode 100644 index ef293a6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allowable-client/Makefile +++ /dev/null @@ -1,110 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that the -allowable_client and -client options -# work when linking against subframeworks. -# - -run: all - -all: -# build with two allowable_clients - ${CC} ${CCFLAGS} -dynamiclib -umbrella foo -allowable_client bar -allowable_client baz foo.c -o foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib - -# test that -o works - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.${ARCH}.dylib foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} libbar.${ARCH}.dylib - -# test that a framework style output works - mkdir -p bar.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.framework/bar foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} bar.framework/bar - -# test that second -o works - ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.${ARCH}.dylib foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} foo.${ARCH}.dylib - -# test that -o and -install_name works with install_name as an allowable - ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar.${ARCH}.dylib foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib - -# test that -install_name works with variant name - ${CC} ${CCFLAGS} -dynamiclib bar.c -o temp.${ARCH}.dylib -install_name /tmp/libbar_profile foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} temp.${ARCH}.dylib - -# test that -o and -install_name fails with install_name different than allowable - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.${ARCH}.dylib -install_name /tmp/fail.${ARCH}.dylib foo.${ARCH}.dylib >& fail.log - -# test that a bundle and no client_name fails - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -bundle bar.c -o temp.${ARCH}.bundle foo.${ARCH}.dylib >& fail.log - -# test that a bundle and an allowable client_name passes - ${CC} ${CCFLAGS} -bundle bar.c -client_name bar -o bar.${ARCH}.bundle foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} bar.${ARCH}.bundle - -# test umbrella can link against subs - mkdir -p foo.framework - ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo - ${FAIL_IF_BAD_MACHO} foo.framework/foo - -# test umbrella variant can link against subs - mkdir -p foo.framework - ${CC} ${CCFLAGS} -dynamiclib foo.${ARCH}.dylib -o foo.framework/foo_debug -install_name /path/foo.framework/foo_debug - ${FAIL_IF_BAD_MACHO} foo.framework/foo_debug - -# test sibling in umbrella can link against subs - ${CC} ${CCFLAGS} -dynamiclib main.c -umbrella foo foo.${ARCH}.dylib -o ./main.dylib - ${FAIL_IF_BAD_MACHO} main.dylib - -# test anyone can link against umbrella - ${CC} ${CCFLAGS} main.c -o main.${ARCH} -framework foo -F. - ${FAIL_IF_BAD_MACHO} main.${ARCH} - -# test that an executable and no client_name fails - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} foo.${ARCH}.dylib >& fail.log - -# test that an executable and an allowable client_name passes - ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name bar foo.${ARCH}.dylib - ${PASS_IFF_GOOD_MACHO} main.${ARCH} - -# test that a regular dylib can be made unlinkable by using -allowable_client - ${CC} ${CCFLAGS} -dynamiclib foo.c -allowable_client '!' -o unlinkable_foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} unlinkable_foo.${ARCH}.dylib - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} unlinkable_foo.${ARCH}.dylib >& fail.log - -# test that a regular dylib can be made linkable by only specially named clients - ${CC} ${CCFLAGS} -dynamiclib foo.c -allowable_client special -o restrictive_foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} restrictive_foo.${ARCH}.dylib - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main.${ARCH} restrictive_foo.${ARCH}.dylib >& fail.log - ${CC} ${CCFLAGS} main.c -o main.${ARCH} -client_name special restrictive_foo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} main.${ARCH} - -# print final pass - ${PASS_IFF_GOOD_MACHO} foo.${ARCH}.dylib - -clean: - rm -rf *.dylib *.bundle main.???* fail.log *.framework diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c deleted file mode 100644 index dbaeef8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allowable-client/bar.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int foo (); - -int bar (void) -{ - return foo(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c deleted file mode 100644 index dbaeef8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allowable-client/baz.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int foo (); - -int bar (void) -{ - return foo(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt b/ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt deleted file mode 100644 index 0202b5f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allowable-client/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test that the -allowable_client and -client options work when linking against subframeworks. diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c b/ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c deleted file mode 100644 index 7b76173..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/allowable-client/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int foo (); - -int main (void) -{ - return foo(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile deleted file mode 100644 index 892043d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to check that -ObjC loads all (and only) -# .o files that contain Objective-C code. -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.m -c -o foo-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o - ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o - ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o - ${CC} ${CCFLAGS} baz.m -c -o baz-${ARCH}.o - ${FAIL_IF_BAD_OBJ} baz-${ARCH}.o - libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobarbaz-${ARCH}.a - ${CC} ${CCFLAGS} main.c -lfoobarbaz-${ARCH} -L. -o main-${ARCH} -ObjC -framework Foundation -framework CoreFoundation - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm main-${ARCH} | grep "_bar" | ${FAIL_IF_STDIN} - nm main-${ARCH} | grep "_Foo" | ${FAIL_IF_EMPTY} - nm main-${ARCH} | grep "_Baz" | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main-${ARCH} - -clean: - rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c deleted file mode 100644 index d9b468b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/bar.c +++ /dev/null @@ -1,2 +0,0 @@ - -int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m deleted file mode 100644 index 90ae0a1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/baz.m +++ /dev/null @@ -1,8 +0,0 @@ -#include - -@interface Baz : NSObject -@end - -@implementation Baz -@end - diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m deleted file mode 100644 index acba7a4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/foo.m +++ /dev/null @@ -1,8 +0,0 @@ -#include - -@interface Foo : NSObject -@end - -@implementation Foo -@end - diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c deleted file mode 100644 index dc2fbef..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-ObjC/main.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - - -int main() -{ - fprintf(stdout, "hello\n"); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile deleted file mode 100644 index 39c0ef9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-basic/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that .o files -# can be found in archives. -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o - ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o - ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o - libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a - ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -L. -o main-${ARCH} - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm main-${ARCH} | grep "_bar" | ${PASS_IFF_EMPTY} - ${CC} ${CCFLAGS} main.c -all_load -lfoobar-${ARCH} -L. -o main-${ARCH} - ${FAIL_IF_BAD_MACHO} main-${ARCH} - -clean: - rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c deleted file mode 100644 index 7fe6403..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-basic/bar.c +++ /dev/null @@ -1 +0,0 @@ -int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt b/ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt deleted file mode 100644 index 0d34170..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-basic/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that .o files can be found in archives. diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c b/ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c deleted file mode 100644 index a60f28c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-basic/foo.c +++ /dev/null @@ -1 +0,0 @@ -int foo() { return 1; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c deleted file mode 100644 index 57c4c68..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-basic/main.c +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int foo(); - -int main() -{ - fprintf(stdout, "hello\n"); - return foo(); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile deleted file mode 100644 index 61f4bc7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ia an archive -# is listed multiple times, the extras are ignored. -# This is done in some makefiles because the traditional linker -# semantics is to only search an archive once. -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o - ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o - libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a - ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -lfoobar-${ARCH} -L. -o main-${ARCH} -all_load - ${FAIL_IF_BAD_MACHO} main-${ARCH} - ${CC} ${CCFLAGS} main.c ./libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} -all_load - ${PASS_IFF_GOOD_MACHO} main-${ARCH} - -clean: - rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c deleted file mode 100644 index 7fe6403..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/bar.c +++ /dev/null @@ -1 +0,0 @@ -int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c deleted file mode 100644 index a60f28c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/foo.c +++ /dev/null @@ -1 +0,0 @@ -int foo() { return 1; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c deleted file mode 100644 index 57c4c68..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-duplicate/main.c +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int foo(); - -int main() -{ - fprintf(stdout, "hello\n"); - return foo(); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile b/ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile deleted file mode 100644 index 2d53734..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-weak/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test if the linker already has a weak definition -# it does not try to find another copy in an archive -# -# There are two case to test: -# 1) both the main .o files and the archive have the same weak symbol (_foo) -# 2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) -# In both cases the linker should ignore the archive. -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o - ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o - ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o - ${CC} ${CCFLAGS} baz.c -c -o baz-${ARCH}.o - ${FAIL_IF_BAD_OBJ} baz-${ARCH}.o - libtool -static foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o -o libfoobar-${ARCH}.a - ${CC} ${CCFLAGS} main.c foo.c -lfoobar-${ARCH} -L. -o main-${ARCH} - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm -m main-${ARCH} | grep _baz | grep weak | ${PASS_IFF_STDIN} - -clean: - rm -rf main-* *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c deleted file mode 100644 index 7fe6403..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-weak/bar.c +++ /dev/null @@ -1 +0,0 @@ -int bar() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c deleted file mode 100644 index 387ccc1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-weak/baz.c +++ /dev/null @@ -1,11 +0,0 @@ - - - -// intentionally not-weak -int baz() -{ - return 1; -} - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt b/ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt deleted file mode 100644 index 902d22c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-weak/comment.txt +++ /dev/null @@ -1,7 +0,0 @@ -The point of this test if the linker already has a weak definition -it does not try to find another copy in an archive - -There are two case to test: -1) both the main .o files and the archive have the same weak symbol (_foo) -2) main.o has a weak symbol and the archive has a non-weak symbol (_baz) -In both cases the linker should ignore the archive. diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c deleted file mode 100644 index d7003fa..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-weak/foo.c +++ /dev/null @@ -1,13 +0,0 @@ - - -void collisionChecker() { } - - -int __attribute__((weak)) foo() -{ - collisionChecker(); - return 1; -} - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c b/ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c deleted file mode 100644 index d1ce4a9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/archive-weak/main.c +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int foo(); -extern int bar(); - -// intentionally weak -int __attribute__((weak)) baz() -{ - return 1; -} - -int main() -{ - fprintf(stdout, "hello\n"); - return foo() + bar() + baz(); -} - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile b/ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile deleted file mode 100644 index 3b1cc7c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/auto-arch/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# Check that ld can figure out architecture from .o files without -arch option -# - -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} - -clean: - rm -rf hello.o hello diff --git a/ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c b/ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c deleted file mode 100644 index 14d9363..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/auto-arch/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - fprintf(stdout, "hello\n"); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile deleted file mode 100644 index 346e2b7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(VALID_ARCHS)) - -# -# Test that blank stubs are handled properly -# - -run: all - -all: -# build example fully fat dylib - - gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - # handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo - if [ x${ARCH} != xppc ]; \ - then \ - SUB_ARCH=${ARCH}; \ - else \ - SUB_ARCH=`lipo -info libfoo.dylib | sed 's/.*://;s/ppc64 //;s/.* \(ppc[^ ]*\).*/\1/'`; \ - echo SUB_ARCH $$SUB_ARCH; \ - if [ x$$SUB_ARCH = xALL ]; \ - then \ - SUB_ARCH=ppc; \ - fi \ - fi; \ - lipo libfoo.dylib -remove $$SUB_ARCH -output libfoo.dylib - - lipo -create libfoo.dylib -arch_blank ${ARCH} -output libfoo.dylib - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main - ${OTOOL} -L main | grep libfoo | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm -rf libfoo.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt deleted file mode 100644 index 77a8764..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test that blank stubs are handled properly diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c b/ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c deleted file mode 100644 index 6c9f6a4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/main.c +++ /dev/null @@ -1,5 +0,0 @@ - -int main (void) -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c b/ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c deleted file mode 100644 index a1991fe..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/branch-islands/extra.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - - -void foo() -{ - fprintf(stdout, "foo\n"); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c b/ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c deleted file mode 100644 index 89caa9d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/branch-islands/hello.c +++ /dev/null @@ -1,10 +0,0 @@ -#include - -extern void foo(); - -int main() -{ - fprintf(stdout, "hello\n"); - foo(); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s b/ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s deleted file mode 100644 index dd28637..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/branch-islands/space.s +++ /dev/null @@ -1,39 +0,0 @@ - -#if __ppc__ - - .text - -_prejunk: - mr r3,r5 - mr r3,r4 - blr - - -_space1: - .space 15*1024*1024 + 2 - - .align 5 -_junk: - mr r3,r5 - mr r3,r4 - blr - - -_space2: - .space 2*1024*1024 - -#endif - - -#if __arm__ - - -_space1: - .space 32*1024*1024 + 2 - - -#endif - - - .subsections_via_symbols - \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile deleted file mode 100644 index 96d83b8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# Ccheck that ld -bundle_loader works with bundles and executable, -# and _bar is found in the loader and not in libbar.dylib -# - -run: all - -all: - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - # test with loader as main executable - ${CC} ${CCFLAGS} main.c bar.c -o main - ${FAIL_IF_BAD_MACHO} main - ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader main libbar.dylib -o bundle.bundle - ${FAIL_IF_BAD_MACHO} bundle.bundle - nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} - # test with loader as another bundle - ${CC} ${CCFLAGS} -bundle main.c bar.c -o mainbundle - ${FAIL_IF_BAD_MACHO} main - ${CC} ${CCFLAGS} bundle.c -bundle -bundle_loader mainbundle libbar.dylib -o bundle.bundle - ${FAIL_IF_BAD_MACHO} bundle.bundle - nm -m bundle.bundle | grep _bar | grep "from executable" | ${PASS_IFF_STDIN} - # verify error message when linking with raw bundle - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c bar.c -o main bundle.bundle 2> error.log - grep "link with bundle" error.log | ${PASS_IFF_STDIN} - -clean: - rm *.dylib main bundle.bundle mainbundle error.log diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c deleted file mode 100644 index b737953..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bar.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -// funcation called by a loaded bundle -int bar() -{ - return 1; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c deleted file mode 100644 index ba78c28..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/bundle.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int bar(); - -int foo() -{ - return bar(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c b/ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c deleted file mode 100644 index c9e53f9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/bundle_loader/main.c +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile deleted file mode 100644 index c1ca816..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that cfstring literals are coalesced and dead stripped -# There is 3 CFSTR in foo.c and 1 CFSTR in bar.c -# After coalescing and dead stripping there should be only one CFSTR in the output -# - -ifeq (,${findstring 64,$(ARCH)}) - CFSTRING_SIZE = 16 - CFSTRING_SIZE_WRITABLE = 32 -else - CFSTRING_SIZE = 32 - CFSTRING_SIZE_WRITABLE = 64 -endif - - - -run: all - -all: - ${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip - size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} - # now try with -fwritable-strings - ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings - size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE_WRITABLE}" | ${PASS_IFF_STDIN} - -clean: - rm -rf foo foo_writable diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c deleted file mode 100644 index 9a84abd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/bar.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - - -void bar() -{ - CFStringGetLength(CFSTR("live")); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c b/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c deleted file mode 100644 index fe1b30e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cfstring-coalesce/foo.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -extern void bar(); - -void foo() -{ - CFStringGetLength(CFSTR("hello")); - CFStringGetLength(CFSTR("world")); -} - - -int main() -{ - CFStringGetLength(CFSTR("live")); - bar(); - return 0; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m deleted file mode 100644 index bdf5168..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/bar.m +++ /dev/null @@ -1,7 +0,0 @@ -#include - - -void bar() -{ - CFStringGetLength(CFSTR("über")); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m b/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m deleted file mode 100644 index 6e4c268..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/foo.m +++ /dev/null @@ -1,20 +0,0 @@ -#include - -extern void bar(); - -void foo() -{ - CFStringGetLength(CFSTR("hello")); - CFStringGetLength(CFSTR("überhund")); -} - - -int main() -{ - CFStringGetLength(CFSTR("über")); - CFStringGetLength(CFSTR("überhund")); - bar(); - return 0; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile deleted file mode 100644 index d073c0e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-alignment/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Validate linker preserves commons with custom alignment -# - -all: - ${CC} ${CCFLAGS} foo.s -c -o foo.o - nm -m foo.o | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY} - ${LD} foo.o -r -o foo2.o - nm -m foo2.o | grep '(alignment 2^6)' | ${PASS_IFF_STDIN} - -clean: - rm -rf foo.o foo2.o diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s b/ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s deleted file mode 100644 index 03c28fc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-alignment/foo.s +++ /dev/null @@ -1,2 +0,0 @@ - - .comm _mycomm64aligned,15,6 diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile deleted file mode 100644 index 5823a61..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify if a header file is missing an extern that there won't be -# duplicates definitions when using -dead_strip. -# - -run: all - -all: - ${CC} ${CCFLAGS} a.c -c -o a.o - ${CC} ${CCFLAGS} b.c -c -o b.o - ${CC} ${CCFLAGS} c.c -c -o c.o - ${CC} -arch ${ARCH} -dynamiclib a.o b.o c.o -o libabc.dylib -dead_strip - ${PASS_IFF_GOOD_MACHO} libabc.dylib - -clean: - rm -rf a.o b.o c.o libabc.dylib one abc.bar.count diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c deleted file mode 100644 index fea7234..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/a.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "c.h" - -float aa() { return bar; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c deleted file mode 100644 index 12e46e8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/b.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "c.h" - -float bb() { return bar; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c deleted file mode 100644 index 165d5e4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.c +++ /dev/null @@ -1,3 +0,0 @@ - -const float bar = 1.0; - diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h b/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h deleted file mode 100644 index 86a61ae..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-coalesced-dead_strip/c.h +++ /dev/null @@ -1,4 +0,0 @@ - -// missing extern -const float bar; - diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile deleted file mode 100644 index a59a858..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o built with -# commons and one built without commons can be merged. -# -# problem merging .o files built with and without -fno-common -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -c -o foo.${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o - ${CC} ${CCFLAGS} bar.c -c -fno-common -o bar.${ARCH}.o - ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o - ${LD} -arch ${ARCH} -r -keep_private_externs foo.${ARCH}.o bar.${ARCH}.o -o foobar.${ARCH}.o - ${FAIL_IF_BAD_OBJ} foobar.${ARCH}.o - nm -m foobar.${ARCH}.o | grep bar | grep __common | ${PASS_IFF_STDIN} - -clean: - rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c deleted file mode 100644 index 771802c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/bar.c +++ /dev/null @@ -1,2 +0,0 @@ - -int bar; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c b/ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c deleted file mode 100644 index 42e0a8e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-mixed/foo.c +++ /dev/null @@ -1,2 +0,0 @@ - -int foo; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c deleted file mode 100644 index 2d258fc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-order/bar.c +++ /dev/null @@ -1,3 +0,0 @@ -int ddd_common; -int iii_common[4]; -int bbb_common[4]; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c deleted file mode 100644 index 6497d29..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-order/baz.c +++ /dev/null @@ -1,3 +0,0 @@ -int fff_common; -int iii_common[4]; -int ttt_common; diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order b/ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order deleted file mode 100644 index 4738fc2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-order/expected.order +++ /dev/null @@ -1,8 +0,0 @@ -_fff_common -_iii_common -_ttt_common -_aaa_common -_eee_common -_ggg_common -_bbb_common -_ddd_common diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c b/ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c deleted file mode 100644 index ca80a59..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/commons-order/foo.c +++ /dev/null @@ -1,3 +0,0 @@ -int aaa_common; -int ggg_common[4]; -int eee_common; diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile deleted file mode 100644 index 7586a51..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/Makefile +++ /dev/null @@ -1,96 +0,0 @@ -## -# Copyright (c) 2008 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Validate cpu subtype preference -# - -test: test-${ARCH} - -test-ppc64: - ${PASS_IFF} true - -test-i386: - ${PASS_IFF} true - -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 - - # check -arch armv4t pulls out V4 slice - ${LD} -r -arch armv4t foo.o -o fooa.o - otool -hv fooa.o | grep V4T | ${FAIL_IF_EMPTY} - nm fooa.o | grep _aaa | ${FAIL_IF_EMPTY} - - # check -arch armv5 pulls out V5 slice - ${LD} -r -arch armv5 foo.o -o foob.o - otool -hv foob.o | grep V5 | ${FAIL_IF_EMPTY} - nm foob.o | grep _bbb | ${FAIL_IF_EMPTY} - - # check -arch armv6 pulls out V6 slice - ${LD} -r -arch armv6 foo.o -o fooc.o - 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} - - ${PASS_IFF} true - - -test-ppc: - gcc foo.c -arch ppc750 -c -DAAA=1 -o foog3.o - gcc foo.c -arch ppc7400 -c -DBBB=1 -o foog4.o - gcc foo.c -arch ppc970 -c -DCCC=1 -o foog5.o - lipo foog3.o foog4.o foog5.o -create -output foo.o - - # check -arch ppc750 pulls out g3 slice - ${LD} -r -arch ppc750 foo.o -o fooa.o - otool -hv fooa.o | grep ppc750 | ${FAIL_IF_EMPTY} - nm fooa.o | grep _aaa | ${FAIL_IF_EMPTY} - - # check -arch ppc7400 pulls out g4 slice - ${LD} -r -arch ppc7400 foo.o -o foob.o - otool -hv foob.o | grep ppc7400 | ${FAIL_IF_EMPTY} - nm foob.o | grep _bbb | ${FAIL_IF_EMPTY} - - # check -arch ppc970 pulls out g5 slice - ${LD} -r -arch ppc970 foo.o -o fooc.o - otool -hv fooc.o | grep ppc970 | ${FAIL_IF_EMPTY} - nm fooc.o | grep _ccc | ${FAIL_IF_EMPTY} - - ${PASS_IFF} true - - -clean: - rm -f *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c deleted file mode 100644 index 983ed81..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types-preference/foo.c +++ /dev/null @@ -1,25 +0,0 @@ - - -#if AAA - void aaa() {} -#endif - -#if BBB - void bbb() {} -#endif - -#if CCC - void ccc() {} -#endif - -#if DDD - void ddd() {} -#endif - -#if EEE - void eee() {} -#endif - -#if FFFF - void fff() {} -#endif diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile deleted file mode 100644 index 695ffba..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/Makefile +++ /dev/null @@ -1,157 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Validate cpu subtypes processing -# - -test: test-${ARCH} - -test-ppc64: - ${PASS_IFF} true - -test-i386: - ${PASS_IFF} true - -test-x86_64: - ${PASS_IFF} true - -test-armv6: - gcc 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 - ${FAIL_IF_BAD_OBJ} foo-v5.o - gcc 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 - ${FAIL_IF_BAD_OBJ} foo-xscale.o - gcc 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 - ${FAIL_IF_BAD_OBJ} main-v5.o - gcc 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 - ${FAIL_IF_BAD_OBJ} main-xscale.o - - # check V4+V4 -> V4 - ${LD} -r main-v4.o foo-v4.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - 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} - - # 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} - - # 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} - - # check V5+V5 -> V5 - ${LD} -r main-v5.o foo-v5.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - 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} - - # 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} - - # 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 - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} - - ${PASS_IFF} true - - -test-ppc: - gcc foo.c -arch ppc -mmacosx-version-min=10.4 -c -o foo.o - ${FAIL_IF_BAD_OBJ} foo.o - gcc foo.c -arch ppc750 -c -o foo-G3.o - ${FAIL_IF_BAD_OBJ} foo-G3.o - gcc foo.c -arch ppc7400 -c -o foo-G4.o - ${FAIL_IF_BAD_OBJ} foo-G4.o - gcc foo.c -arch ppc970 -c -o foo-G5.o - ${FAIL_IF_BAD_OBJ} foo-G5.o - gcc main.c -arch ppc -mmacosx-version-min=10.4 -c -o main.o - ${FAIL_IF_BAD_OBJ} main.o - gcc main.c -arch ppc970 -c -o main-G5.o - ${FAIL_IF_BAD_OBJ} main-G5.o - - # check ALL+ALL -> ALL - ${LD} -r main.o foo.o -o main-r.o - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ALL | ${FAIL_IF_EMPTY} - - # check G3+ALL -> G3 - ${LD} -r main.o foo-G3.o -o main-r.o - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ppc750 | ${FAIL_IF_EMPTY} - - # check G4+ALL -> G4 - ${LD} -r main.o foo-G4.o -o main-r.o - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ppc7400 | ${FAIL_IF_EMPTY} - - # check G5+ALL -> G5 - ${LD} -r main.o foo-G5.o -o main-r.o - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} - - # check G5+G4 -> G5 - ${LD} -r main-G5.o foo-G4.o -o main-r.o - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} - - # check G4+G5 -> G5 - ${LD} -r foo-G4.o main-G5.o -o main-r.o - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ppc970 | ${FAIL_IF_EMPTY} - - # check -force_cpusubtype_ALL - ${LD} -r main.o foo-G5.o -o main-r.o -force_cpusubtype_ALL - ${FAIL_IF_BAD_OBJ} main-r.o - otool -hv main-r.o | grep ALL | ${PASS_IFF_STDIN} - -clean: - rm -f *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt deleted file mode 100644 index 5e47ece..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/comment.txt +++ /dev/null @@ -1,2 +0,0 @@ -The point of this test is validate cpu subtypes processsing for PowerPC - diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c deleted file mode 100644 index 3695dc9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/foo.c +++ /dev/null @@ -1,3 +0,0 @@ -void foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c b/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c deleted file mode 100644 index b48076d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/cpu-sub-types/main.c +++ /dev/null @@ -1,10 +0,0 @@ -#include - -extern void foo(); -extern void bar(); - -int main() -{ - foo(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile deleted file mode 100644 index ef985b7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Tests that a global symbol in an archive will stil be exported (and not dead stripped). -# - -run: all - -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 -dynamiclib -Os libfoo.a -dead_strip -o libmain.dylib - nm libmain.dylib | grep _bar | ${FAIL_IF_EMPTY} - nm libmain.dylib | grep _baz | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} libmain.dylib - - -clean: - rm -rf *.dylib *.a *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c deleted file mode 100644 index 41478e8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/foo.c +++ /dev/null @@ -1,12 +0,0 @@ - -static int foo_count = 0; -static int bar_count = 0; -static int baz_count = 0; - - -void foo() { ++foo_count; } - -void bar() { ++bar_count; } - -void __attribute__((visibility("hidden"))) - baz() { ++baz_count; } diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile deleted file mode 100644 index 1542d78..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Tests that a common symbol can be used from an archive with -dead_strip. The tricky -# part is that common symbols are not in the table of contents for archives. -# If the linker seens a need for my_common, that won't trigger pulling in the .o -# file from the archive. But the later use of foo will. -# - -run: all - -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 - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf main *.a *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt deleted file mode 100644 index 8dde1c5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test that -dead_strip removes unreference code/data from archives diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c deleted file mode 100644 index be1b438..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/foo.c +++ /dev/null @@ -1,7 +0,0 @@ - - -void foo() {} - - -int my_common; - diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c deleted file mode 100644 index f6a7d5a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive/main.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -extern void foo(); -extern int my_common; - -int main() -{ - // the reference to the common symbol has to be first - my_common += 1; - // refrence to foo is next - foo(); - return 0; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c deleted file mode 100644 index b348aa8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/bar.c +++ /dev/null @@ -1,4 +0,0 @@ - -void bar() {} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile deleted file mode 100644 index 32ac1c5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check -dead_strip -# -# 1) in a main executable, dead globals are removed -# 2) in a dylib/bundle with -exported_symbols_list, dead globals are removed -# 3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} - ${FAIL_IF_BAD_MACHO} dylib-${ARCH} - nm -j dylib-${ARCH} | grep dead | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -o dylib2-${ARCH} - ${FAIL_IF_BAD_MACHO} dylib2-${ARCH} - nm -j dylib2-${ARCH} | grep dead_door_knob | ${FAIL_IF_EMPTY} - nm -j dylib2-${ARCH} | grep deadwood | ${PASS_IFF_EMPTY} - -clean: - rm -rf main-* dylib-* dylib2-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt deleted file mode 100644 index 90a289c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip/comment.txt +++ /dev/null @@ -1,5 +0,0 @@ -The point of this test is a sanity check -dead_strip - -1) in a main executable, dead globals are removed -2) in a dylib/bundle with -exported_symbols_list, dead globals are removed -3) in a dylib/bundle without -exported_symbols_list, dead globals are *not* removed diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c deleted file mode 100644 index 176ec78..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip/deadwood.c +++ /dev/null @@ -1,11 +0,0 @@ - - -// deadwood() is local to its linkage unit and is unsed, -// so reference to undef() is ok - -extern void undef(); - -void dead_wood() __attribute__((visibility("hidden"))); -void dead_wood() { undef(); } - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c deleted file mode 100644 index 029aed9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.c +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int main() -{ - return 0; -} - - - -void dead_door_knob() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp b/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp deleted file mode 100644 index 4eb9e89..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip/main.exp +++ /dev/null @@ -1 +0,0 @@ -_main diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c deleted file mode 100644 index d0cdf47..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c deleted file mode 100644 index 2b85b0e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/main.c +++ /dev/null @@ -1,10 +0,0 @@ - -extern void bar(); - -int main() -{ -#if CALL_BAR - bar(); -#endif - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile deleted file mode 100644 index e6471f7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to check that -dead_strip does not remove -# atoms in sections with the S_ATTR_NO_DEAD_STRIP bit set -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c -dead_strip -o main-${ARCH} - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm -j main-${ARCH} | grep _bar | ${FAIL_IF_STDIN} - nm -j main-${ARCH} | grep _foo | ${PASS_IFF_STDIN} - -clean: - rm -rf main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt deleted file mode 100644 index 6ca56e4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/comment.txt +++ /dev/null @@ -1,2 +0,0 @@ -The point of this test is to check that -dead_strip does not remove -atoms in sections with the S_ATTR_NO_DEAD_STRIP bit set diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c b/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c deleted file mode 100644 index 5fc1fad..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip_section_attribute/main.c +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int main() -{ - return 0; -} - - -// foo should not be dead stripped -void __attribute__ ((section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo() -{ - -} - -// bar should be dead stripped -void __attribute__ ((section ("__DATA,__text2"))) bar() -{ - -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile deleted file mode 100644 index 533169f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that C++ coalescing throws away static probes associated -# with weak functions that are coalesced away. -# Build two programs that both should have the same number of probes -# and hence the same DOF section size -# - -all: simp coal - otool -lv coal | grep -A3 __dof_Number | grep size > coal-dof-size - otool -lv simp | grep -A3 __dof_Number | grep size > simp-dof-size - ${PASS_IFF_SUCCESS} diff coal-dof-size simp-dof-size - -Number.h: Number.d - dtrace -h -s Number.d - -a.o : a.cxx Number.h header.h - ${CXX} ${CXXFLAGS} -c -DFUNC=a a.cxx -o a.o - -b.o : a.cxx Number.h header.h - ${CXX} ${CXXFLAGS} -c -DFUNC=b a.cxx -o b.o - -c.o : a.cxx Number.h header.h - ${CXX} ${CXXFLAGS} -c -DFUNC=c a.cxx -o c.o - -coal : a.o b.o c.o - ${CXX} -dynamiclib a.o b.o c.o -o coal - -simp : x.cxx Number.h - ${CXX} -dynamiclib x.cxx -o simp - - - -clean: - rm -rf coal simp a.o b.o c.o Number.h coal-dof-size simp-dof-size diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d deleted file mode 100644 index 9383fe4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/Number.d +++ /dev/null @@ -1,3 +0,0 @@ -provider Number { - probe hit(int value); -}; diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx deleted file mode 100644 index 8e7e3c6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/a.cxx +++ /dev/null @@ -1,8 +0,0 @@ - -#include - -#include "header.h" - - -void FUNC() { foo(); } - diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h deleted file mode 100644 index 162419b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/header.h +++ /dev/null @@ -1,11 +0,0 @@ - -#include "Number.h" - -#define LOTS_O_PROBES { NUMBER_HIT(1); NUMBER_HIT(2); NUMBER_HIT(3); NUMBER_HIT(4); } - - -inline void foo() { - LOTS_O_PROBES -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx deleted file mode 100644 index b756394..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx +++ /dev/null @@ -1,6 +0,0 @@ - -#include "header.h" - -void f() { LOTS_O_PROBES } - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile deleted file mode 100644 index b59760f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a progam with dtrace static probes -# - -all: run - -run: main main-r main-dead_strip libmain.dylib - ${FAIL_IF_BAD_MACHO} main - ${PASS_IFF_GOOD_MACHO} main-r - -main: main.c foo.h bar.h - ${CC} ${CCFLAGS} main.c -o main - -main-dead_strip: main.c foo.h bar.h - ${CC} ${CCFLAGS} main.c -o main-dead_strip -dead_strip - -main-r: main.c foo.h bar.h - ${CC} ${CCFLAGS} main.c -c -o main.o - #${FAIL_IF_BAD_OBJ} main.o - ${LD} -r main.o -o main-r.o - #${FAIL_IF_BAD_OBJ} main-r.o - ${CC} ${CCFLAGS} main-r.o -o main-r - -libmain.dylib: main.c foo.h bar.h - ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib - -foo.h: foo.d - dtrace -h -s foo.d - -bar.h: bar.d - dtrace -h -s bar.d - -clean: - rm -rf main libmain.dylib main-r main-dead_strip foo.h bar.h *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d deleted file mode 100644 index fab36ea..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/bar.d +++ /dev/null @@ -1,7 +0,0 @@ -typedef int weirdType; - -provider Bar { - probe count1(weirdType); -}; - -#pragma D attributes Evolving/Evolving/Common provider Bar args diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt deleted file mode 100644 index b02ac8b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a progam with dtrace static probes diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d deleted file mode 100644 index 03f1a19..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/foo.d +++ /dev/null @@ -1,8 +0,0 @@ -typedef int weirdType2; - -provider Foo { - probe count1(weirdType2); -}; - - -#pragma D attributes Evolving/Evolving/Common provider Foo args diff --git a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c b/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c deleted file mode 100644 index 8a417e9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dtrace-static-probes/main.c +++ /dev/null @@ -1,29 +0,0 @@ - -#include - -typedef int weirdType; -typedef int weirdType2; - -#include "foo.h" -#include "bar.h" - - -int deadwood() -{ - BAR_COUNT1(2); - return 0; -} - - -int main() { - int a = 1; - - while(a) { - FOO_COUNT1(1); - printf("test\n"); - BAR_COUNT1(2); - sleep(1); - } - - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile deleted file mode 100644 index 8d38d99..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that using -all_load to pull all .o files out of an archive -# proeduces good "debug notes". -# - -run: - ${CC} ${CCFLAGS} -gdwarf-2 foo.c -c -o foo.o - ${FAIL_IF_BAD_OBJ} foo.o - ${CC} ${CCFLAGS} -gdwarf-2 bar.c -c -o bar.o - ${FAIL_IF_BAD_OBJ} bar.o - ${CC} ${CCFLAGS} -gdwarf-2 baz.c -c -o baz.o - ${FAIL_IF_BAD_OBJ} baz.o - libtool -static bar.o baz.o foo.o -o liball.a - ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs - ${FAIL_IF_BAD_MACHO} liball.dylib - nm -fap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs - ${PASS_IFF} diff liball.dylib.stabs expected-stabs - -clean: - rm -rf *.o liball.a liball.dylib liball.dylib.stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c deleted file mode 100644 index 06752f3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/bar.c +++ /dev/null @@ -1,2 +0,0 @@ -void bar() {} - diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt deleted file mode 100644 index 6992acd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/comment.txt +++ /dev/null @@ -1,2 +0,0 @@ -Test that using -all_load to pull all .o files out of an archive -proeduces good "debug notes". diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs deleted file mode 100644 index 0071455..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs +++ /dev/null @@ -1,24 +0,0 @@ -0000 SO CWD/ -0000 SO bar.c -0001 OSO CWD/liball.a(bar.o) -0000 BNSYM -0000 FUN _bar -0000 FUN -0000 ENSYM -0000 SO -0000 SO CWD/ -0000 SO baz.c -0001 OSO CWD/liball.a(baz.o) -0000 BNSYM -0000 FUN _baz -0000 FUN -0000 ENSYM -0000 SO -0000 SO CWD/ -0000 SO foo.c -0001 OSO CWD/liball.a(foo.o) -0000 BNSYM -0000 FUN _foo -0000 FUN -0000 ENSYM -0000 SO diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl b/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl deleted file mode 100755 index 706fd12..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Cwd; - -my $dir = getcwd; -#my $xxx = $ARGV[1]; - -while(<>) -{ - # get stabs lines that match "NNNNNNN - xxx" - if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) - { - # replace any occurances of cwd path with $CWD - my $line = $3; - if($line =~ m/(.*?)$dir(.*?)$/) - { - $line = $1 . "CWD" . $2; - } - - printf "$line\n"; - } -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile deleted file mode 100644 index 1eb6310..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# produces good "debug notes" stabs from dwarf .o files after -# some of the .o files are merged with ld -r. -# Running nm through stabs-filter.pl produces connonical stabs -# that can be diffed against a checked in know good set of stabs -# - -run: all - -all: foobar.o main.o - ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-test-${ARCH} - nm -ap dwarf-test-${ARCH} | ./stabs-filter.pl > dwarf-test-${ARCH}.stabs - ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs - -foobar.o : foo.o bar.o - ${LD} -r -arch ${ARCH} foo.o bar.o -o foobar.o - ${FAIL_IF_BAD_OBJ} foobar.o - -foo.o : foo.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -bar.o : bar.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -main.o : main.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -clean: - rm -rf dwarf-test-* *.o *.stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx deleted file mode 100644 index c3f6a18..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/bar.cxx +++ /dev/null @@ -1,4 +0,0 @@ -int bar() -{ - return 10; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt deleted file mode 100644 index 0f1d0b1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt +++ /dev/null @@ -1,5 +0,0 @@ -The point of this test is a sanity check that ld -produces good "debug notes" stabs from dwarf .o files after -some of the .o files are merged with ld -r. -Running nm through stabs-filter.pl produces connonical stabs -that can be diffed against a checked in know good set of stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs deleted file mode 100644 index f8abc12..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs +++ /dev/null @@ -1,24 +0,0 @@ -0000 SO CWD/ -0000 SO main.cxx -0001 OSO CWD/main.o -0000 BNSYM -0000 FUN _main -0000 FUN -0000 ENSYM -0000 SO -0000 SO CWD/ -0000 SO foo.cxx -0001 OSO CWD/foo.o -0000 BNSYM -0000 FUN __Z3foov -0000 FUN -0000 ENSYM -0000 SO -0000 SO CWD/ -0000 SO bar.cxx -0001 OSO CWD/bar.o -0000 BNSYM -0000 FUN __Z3barv -0000 FUN -0000 ENSYM -0000 SO diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx deleted file mode 100644 index 1f46ef4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/foo.cxx +++ /dev/null @@ -1,4 +0,0 @@ -int foo() -{ - return 10; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx deleted file mode 100644 index f8b643a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/main.cxx +++ /dev/null @@ -1,4 +0,0 @@ -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl deleted file mode 100755 index 706fd12..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes-r/stabs-filter.pl +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Cwd; - -my $dir = getcwd; -#my $xxx = $ARGV[1]; - -while(<>) -{ - # get stabs lines that match "NNNNNNN - xxx" - if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) - { - # replace any occurances of cwd path with $CWD - my $line = $3; - if($line =~ m/(.*?)$dir(.*?)$/) - { - $line = $1 . "CWD" . $2; - } - - printf "$line\n"; - } -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile deleted file mode 100644 index 42f1cb7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# produces good "debug notes" stabs from dwarf .o files -# Running nm through stabs-filter.pl produces connonical stabs -# that can be diffed against a checked in know good set of stabs -# - -run: all - -all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - nm -ap dwarf-hello-${ARCH} | ./stabs-filter.pl > dwarf-hello-${ARCH}.stabs - ${PASS_IFF} diff dwarf-hello-${ARCH}.stabs expected-stabs - -hello.o : hello.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -clean: - rm -rf dwarf-hello-* *.o *.stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt deleted file mode 100644 index 5caf129..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/comment.txt +++ /dev/null @@ -1,4 +0,0 @@ -The point of this test is a sanity check that ld -produces good "debug notes" stabs from dwarf .o files -Running nm through stabs-filter.pl produces connonical stabs -that can be diffed against a checked in know good set of stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs deleted file mode 100644 index 4ab634d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ /dev/null @@ -1,33 +0,0 @@ -0000 SO CWD/ -0000 SO hello.cxx -0001 OSO CWD/hello.o -0000 BNSYM -0000 FUN _main -0000 FUN -0000 ENSYM -0000 BNSYM -0000 FUN __Z3fooi -0000 SOL header.h -0000 FUN -0000 ENSYM -0000 SO -0000 SO CWD/ -0000 SO other.cxx -0001 OSO CWD/other.o -0000 BNSYM -0000 FUN __Z3bari -0000 FUN -0000 ENSYM -0000 BNSYM -0000 FUN .my_non_standard_function_name -0000 FUN -0000 ENSYM -0000 GSYM _init -0000 STSYM .my_non_standard_name -0000 STSYM .my_non_standard_name_static -0000 STSYM __ZZ3bariE8bar_init -0000 GSYM _uninit -0000 STSYM _sinit -0000 STSYM _suninit -0000 STSYM __ZZ3bariE10bar_uninit -0000 SO diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h deleted file mode 100644 index aa960dd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/header.h +++ /dev/null @@ -1,8 +0,0 @@ - - -inline int foo(int x) -{ - return x + 10; -} - -extern int bar(int x); \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx deleted file mode 100644 index 0d508e1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/hello.cxx +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -#include "header.h" - - -int main() -{ - foo(bar(3)); - fprintf(stdout, "hello\n"); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx deleted file mode 100644 index b1b0e77..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/other.cxx +++ /dev/null @@ -1,27 +0,0 @@ - -#include "header.h" - -int uninit; -int init = 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; - -int bar(int x) -{ - static int bar_uninit; - static int bar_init=3; - bar_uninit = x; - scustominit = x; - custom = x; - return 20 + suninit + sinit + - bar_init + bar_uninit + foo(x); -} - -extern void disappear() _asm("lbegone"); -void disappear() {} - -extern void foo() _asm(".my_non_standard_function_name"); -void foo() { disappear(); } - diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl b/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl deleted file mode 100755 index 706fd12..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-debug-notes/stabs-filter.pl +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Cwd; - -my $dir = getcwd; -#my $xxx = $ARGV[1]; - -while(<>) -{ - # get stabs lines that match "NNNNNNN - xxx" - if(m/^([0-9a-f]+) - ([0-9a-f]+) (.*?)$/) - { - # replace any occurances of cwd path with $CWD - my $line = $3; - if($line =~ m/(.*?)$dir(.*?)$/) - { - $line = $1 . "CWD" . $2; - } - - printf "$line\n"; - } -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile deleted file mode 100644 index 94b026c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# strips out the dwarf segment by default -# - -run: all - -all: - ${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} - -clean: - rm -rf dwarf-hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt deleted file mode 100644 index 06822b2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld strips out the dwarf segment by default diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c deleted file mode 100644 index e2f0fe9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-ignore/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - fprintf(stdout, "hello\n"); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile deleted file mode 100644 index 947afa7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -S -# produces no debug notes (stabs) -# - -run: all - -all: - ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o dwarf-hello-${ARCH} - #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - nm -ap dwarf-hello-${ARCH} | grep -e " - " | ${PASS_IFF_EMPTY} - -clean: - rm -rf dwarf-hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt deleted file mode 100644 index 5a23827..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld -S produces no debug notes (stabs) diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c b/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c deleted file mode 100644 index ddca819..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-strip/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - fprintf(stdout, "hello\n"); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile deleted file mode 100644 index 1cf9fa0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that libfoo.dylib can be aliases with a symlink or -# as another dylib and still link -# - -run: all - -all: libfoo.dylib libbar.dylib libbaz.dylib - ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. - ${PASS_IFF_GOOD_MACHO} main - -libfoo.dylib : foo.c - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - -libbar.dylib : bar.c - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name libfoo.dylib - -libbaz.dylib : libfoo.dylib - ln -s libfoo.dylib libbaz.dylib - -clean: - rm -rf *.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c deleted file mode 100644 index 981110f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/bar.c +++ /dev/null @@ -1 +0,0 @@ -void bar() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c deleted file mode 100644 index 2fb55ee..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/foo.c +++ /dev/null @@ -1 +0,0 @@ -void foo() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c deleted file mode 100644 index 03c4b39..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-aliases/main.c +++ /dev/null @@ -1,8 +0,0 @@ -extern void foo(); -extern void bar(); - -int main() { - foo(); - bar(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile deleted file mode 100644 index 617fbfc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# Test that searches for indirect libraries does not cause a cycle -# OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found -# - -run: all - -all: libfoo.dylib - ${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true - grep "cycle" errmsg | ${PASS_IFF_STDIN} - -libfoo.dylib : foo.c libbar.dylib - ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 - -libbar.dylib : bar.c other/libfoo.dylib - ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 - -other/libfoo.dylib : foo.c - mkdir -p other - ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 - - - -clean: - rm -rf other libfoo.dylib libbar.dylib main errmsg diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c deleted file mode 100644 index 981110f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/bar.c +++ /dev/null @@ -1 +0,0 @@ -void bar() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c deleted file mode 100644 index 2fb55ee..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/foo.c +++ /dev/null @@ -1 +0,0 @@ -void foo() { } diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c deleted file mode 100644 index 8273541..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib-re-export-cycle/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void unfindable(); - -int main() { - unfindable(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile deleted file mode 100644 index f6371af..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -PWD = $(shell pwd) -SHELL = bash # use bash shell so we can redirect just stderr - - -# Verify that if -dylib_file option points to a missing, file the link does not fail - -run: all - -all: - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name "${PWD}/libbar.dylib" - ${CC} ${CCFLAGS} foo.c "${PWD}/libbar.dylib" -dynamiclib -o libfoo.dylib -sub_library libbar - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -dylib_file "${PWD}/libbar.dylib:libbar2.dylib" 2>warnings.log - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf *.dylib main warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c deleted file mode 100644 index 2668f9a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/bar.c +++ /dev/null @@ -1,13 +0,0 @@ - - - -void bar() -{ -} - -#if BAR_EXTRA -void bar_extra() -{ -} -#endif - diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c deleted file mode 100644 index aa31241..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/foo.c +++ /dev/null @@ -1,7 +0,0 @@ - -extern void bar(); - -void foo() -{ - bar(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c deleted file mode 100644 index 3f109f2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file-missing/main.c +++ /dev/null @@ -1,15 +0,0 @@ - -extern void foo(); -extern void bar(); -extern void bar_extra(); - -int main() -{ - foo(); - bar(); -#if BAR_EXTRA - bar_extra(); -#endif - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile deleted file mode 100644 index 181eee5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -PWD = $(shell pwd) -SHELL = bash # use bash shell so we can redirect just stderr - - -# Verify that -dylib_file option allows you to replace an indirect dylib - -run: all - -all: - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name "${PWD}/libbar.dylib" - ${CC} ${CCFLAGS} bar.c -DBAR_EXTRA -dynamiclib -o libbar2.dylib -install_name "${PWD}/libbar.dylib" - ${CC} ${CCFLAGS} foo.c "${PWD}/libbar.dylib" -dynamiclib -o libfoo.dylib -sub_library libbar - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main - # verify that if main needs bar_extra, it fails - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DBAR_EXTRA libfoo.dylib -o main 2> fail.log - # verify that if main needs bar_extra, it works with -dylib_file option - ${CC} ${CCFLAGS} main.c -DBAR_EXTRA libfoo.dylib -o main -dylib_file "${PWD}/libbar.dylib:libbar2.dylib" - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf *.dylib main fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c deleted file mode 100644 index 2668f9a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file/bar.c +++ /dev/null @@ -1,13 +0,0 @@ - - - -void bar() -{ -} - -#if BAR_EXTRA -void bar_extra() -{ -} -#endif - diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt b/ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt deleted file mode 100644 index e3bfbb7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Verify that -dylib_file option allows you to replace an indirect dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c deleted file mode 100644 index aa31241..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file/foo.c +++ /dev/null @@ -1,7 +0,0 @@ - -extern void bar(); - -void foo() -{ - bar(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c b/ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c deleted file mode 100644 index 3f109f2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_file/main.c +++ /dev/null @@ -1,15 +0,0 @@ - -extern void foo(); -extern void bar(); -extern void bar_extra(); - -int main() -{ - foo(); - bar(); -#if BAR_EXTRA - bar_extra(); -#endif - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile b/ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile deleted file mode 100644 index 3ccbc85..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_init/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -## -# Copyright (c) 2005-2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Verify -init option creates a LC_ROUTINES load command - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -init __init - otool -lv libfoo.dylib | grep LC_ROUTINES | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} libfoo.dylib - -clean: - rm -rf libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c b/ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c deleted file mode 100644 index 9ba156e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/dylib_init/foo.c +++ /dev/null @@ -1,2 +0,0 @@ -void _init() { -} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx deleted file mode 100644 index 0a8d99a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/foo.cxx +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -#include "func.h" - - -void test() -{ - func(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h deleted file mode 100644 index 4505225..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/func.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- 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@ - */ - -extern int global; -extern void foo(int); - - -// this weak func() will have unwind info and a LSDA -inline int func() -{ - global = 1; - return global; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile deleted file mode 100644 index 0c7fb32..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2008 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# There are two copies of func(). The one from -# foo.o is weak and has an FDE (.eh) and LSDA. -# The one from bar.o is not weak and does not have -# exception info. Verify that since linker uses func() -# from bar.o that it does not use exception info -# from foo.o -# -# wrong EH information might be used -# - -run: all - -all: - ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o - ${CXX} ${CCXXFLAGS} foo2.cxx -c -o foo2.o - ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -fno-exceptions - ${CXX} ${CCXXFLAGS} -dynamiclib foo.o foo2.o bar.o -o libfoobar.dylib -Wl,-map,libfoobar.map - # verify .eh symbol is missing or is from bar.o (file 3) - grep '\[ 2\] __Z4funcv.eh' libfoobar.map | ${FAIL_IF_STDIN} - # verify no LSDA - size -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY} - -clean: - rm foo.o foo2.o bar.o libfoobar.map libfoobar.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx deleted file mode 100644 index 83d1845..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/bar.cxx +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -// this non-seak func() will have no unwind info and no LSDA -int func() { return 0; } - -void foo(int x) {} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx deleted file mode 100644 index ce9939f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo.cxx +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -#include "func.h" - -int global; - -void test() -{ - func(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx deleted file mode 100644 index 4440aed..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/foo2.cxx +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -#include "func.h" - -void test2() -{ - func(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile deleted file mode 100644 index 0b8fcff..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -## -# 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 - -run: all - - -all: - ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -o main - ${FAIL_IF_ERROR} nm -j main | grep '\.eh$$'| ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main -clean: - rm -rf *.o main main-* *.nm diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt deleted file mode 100644 index a99f78e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/comment.txt +++ /dev/null @@ -1,21 +0,0 @@ -Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) - -__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi -__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv -__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ -__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim -__ZN9__gnu_cxx13new_allocatorIiED2Ev -__ZNSt6vectorIiSaIiEEC1ERKS0_ -__ZNSaIiEC1ERKS_ -__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim -__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ -__ZNSaIiED2Ev -__ZNSt12_Vector_baseIiSaIiEED2Ev -__ZNSaIiEC1Ev -__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ -__ZNSt6vectorIiSaIiEED1Ev -__ZNSaIiED1Ev -__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ -__ZN9__gnu_cxx13new_allocatorIiEC2Ev -__ZNSaIiEC2ERKS_ -__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx b/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx deleted file mode 100644 index dd65fef..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh-strip-test/main.cxx +++ /dev/null @@ -1,6 +0,0 @@ -#include -int main() -{ - std::vector stuff; - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile b/ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile deleted file mode 100644 index 6216d8e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh_frame/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# strips .eh symbols out of final linked images, -# even when an intermediate ld -r was used. -# - -run: all - -all: - ${CXX} ${CCXXFLAGS} foo.cxx -dynamiclib -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - nm libfoo.dylib | grep '.eh' | ${FAIL_IF_STDIN} - ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o - ${FAIL_IF_BAD_OBJ} foo.o - ${LD} -r foo.o -o foo2.o - ${FAIL_IF_BAD_OBJ} foo2.o - ${CXX} ${CCXXFLAGS} foo2.o bar.cxx -dynamiclib -o libfoobar.dylib - ${FAIL_IF_BAD_MACHO} libfoobar.dylib - nm libfoobar.dylib | grep '.eh' | ${PASS_IFF_EMPTY} - -clean: - rm *.dylib *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx b/ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx deleted file mode 100644 index 9623da1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh_frame/bar.cxx +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - - -static void bar1() -{ - fprintf(stderr, "hello\n"); -} - -void bar2() -{ - bar1(); - fprintf(stderr, "world\n"); -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx b/ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx deleted file mode 100644 index d1e2dd1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/eh_frame/foo.cxx +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - - -static void foo1() -{ - fprintf(stderr, "hello\n"); -} - -void foo2() -{ - foo1(); - fprintf(stderr, "world\n"); -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile b/ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile deleted file mode 100644 index 4142b8c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/empty-object/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2007 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can handle an empty (no load commands) .o file -# - -run: all - -all: - touch empty.s - as -arch ${ARCH} -n empty.s -o empty.o - ${CC} ${CCFLAGS} main.c empty.o -o main - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm main empty.s empty.o diff --git a/ld64/FireOpal/unit-tests/test-cases/empty-object/main.c b/ld64/FireOpal/unit-tests/test-cases/empty-object/main.c deleted file mode 100644 index 76e8197..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/empty-object/main.c +++ /dev/null @@ -1 +0,0 @@ -int main() { return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/end-label/bar.s b/ld64/FireOpal/unit-tests/test-cases/end-label/bar.s deleted file mode 100644 index db389c4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/end-label/bar.s +++ /dev/null @@ -1,7 +0,0 @@ - - .section __DATA,__other,regular - - .globl _next -_next: - nop - \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/end-label/foo.s b/ld64/FireOpal/unit-tests/test-cases/end-label/foo.s deleted file mode 100644 index 888af5f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/end-label/foo.s +++ /dev/null @@ -1,11 +0,0 @@ - - .data - - .globl _start - .globl _end -_start: - .long 0 - .long 0 -_end: - - diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c deleted file mode 100644 index 6bb8d95..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/foo.c +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -void good() {} -void bad() {} - - -void ABC() {} -void ABD() { good(); } -void DEF() {} -void DEG() { bad(); } - diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile deleted file mode 100644 index 10d1eb1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Tests the use of wildcards in exported symbol lists -# - -run: all - -all: - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*bar' - nm -j -g -f libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f?o' - nm -j -g -f libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*' - nm -j -g -f libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f*o*' - nm -j -g -f libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,_foo -Wl,-exported_symbol -Wl,'_*bar' - nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list list5 - nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-unexported_symbol -Wl,'_*2*' - nm -j -g -f libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[abcdef]o' - nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-f]o' - nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-z]o' - nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-fnop]o' - nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} libfoo.dylib - -clean: - rm libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 deleted file mode 100644 index 75aadb3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect1 +++ /dev/null @@ -1,2 +0,0 @@ -_foo2bar -_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 deleted file mode 100644 index 09883f8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect2 +++ /dev/null @@ -1,3 +0,0 @@ -_fao -_ffo -_foo diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 deleted file mode 100644 index 478bf69..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect3 +++ /dev/null @@ -1,4 +0,0 @@ -_foo -_foo2 -_foo2bar -_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 deleted file mode 100644 index b78c206..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect4 +++ /dev/null @@ -1,6 +0,0 @@ -_fao -_ffo -_foo -_foo2 -_foo2bar -_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 deleted file mode 100644 index cc935a4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect5 +++ /dev/null @@ -1,3 +0,0 @@ -_foo -_foo2bar -_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 deleted file mode 100644 index bdf1d31..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect6 +++ /dev/null @@ -1,4 +0,0 @@ -_fao -_ffo -_foo -_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 deleted file mode 100644 index c691c40..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect7 +++ /dev/null @@ -1,2 +0,0 @@ -_fao -_ffo diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 deleted file mode 100644 index 09883f8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/expect8 +++ /dev/null @@ -1,3 +0,0 @@ -_fao -_ffo -_foo diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c deleted file mode 100644 index bc5be8e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/foo.c +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - - -int foo() -{ - return 1; -} - -int foo2() -{ - return 1; -} - -int foobar() -{ - return 1; -} - -int foo2bar() -{ - return 1; -} - -int fao() -{ - return 1; -} - -int ffo() -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 b/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 deleted file mode 100644 index a6776d2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards/list5 +++ /dev/null @@ -1,2 +0,0 @@ -_foo -_*bar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile deleted file mode 100644 index 25a50b4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# Verify that -exported_symbols_list can be used with a file with Mac (0x0D) line endings -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib - nm -jg libtest.dylib | grep _ > test.nm - diff test.nm expected.nm - ${PASS_IFF_GOOD_MACHO} libtest.dylib - -clean: - rm -rf libtest.dylib test.nm diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm deleted file mode 100644 index b21cbbf..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/expected.nm +++ /dev/null @@ -1,2 +0,0 @@ -_common_global2 -_func_global2 diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c deleted file mode 100644 index c9fdc35..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.c +++ /dev/null @@ -1,18 +0,0 @@ - - - -static int data_static1 = 1; -static int data_static2 = 2; - -void func_global1() { ++data_static1; } -void func_global2() { ++data_static2; } - -void __attribute__((visibility("hidden"))) func_hidden1() {} -void __attribute__((visibility("hidden"))) func_hidden2() {} - -int common_global1; -int common_global2; - -int __attribute__((visibility("hidden"))) common_hidden1; -int __attribute__((visibility("hidden"))) common_hidden2; - diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp deleted file mode 100644 index 6ab1532..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-eol/test.exp +++ /dev/null @@ -1 +0,0 @@ -_func_global2 _common_global2 \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile deleted file mode 100644 index c27287d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# Verify that -exported_symbols_list of a hidden symbol causes a warning -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib 2>warning.log - grep _func_hidden1 warning.log | ${FAIL_IF_EMPTY} - grep _common_hidden1 warning.log | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} libtest.dylib - -clean: - rm -rf libtest.dylib warning.log diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c deleted file mode 100644 index c9fdc35..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.c +++ /dev/null @@ -1,18 +0,0 @@ - - - -static int data_static1 = 1; -static int data_static2 = 2; - -void func_global1() { ++data_static1; } -void func_global2() { ++data_static2; } - -void __attribute__((visibility("hidden"))) func_hidden1() {} -void __attribute__((visibility("hidden"))) func_hidden2() {} - -int common_global1; -int common_global2; - -int __attribute__((visibility("hidden"))) common_hidden1; -int __attribute__((visibility("hidden"))) common_hidden2; - diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp deleted file mode 100644 index f45858b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-hidden/test.exp +++ /dev/null @@ -1,4 +0,0 @@ -_func_global1 -_func_hidden1 -_common_global1 -_common_hidden1 diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile deleted file mode 100644 index 96ef763..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# Verify that -exported_symbols_list can be used with -r -# to reduce visibility of symbols and any missing symbols -# causes an error -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -c -o test.o - ${FAIL_IF_BAD_OBJ} test.o - ${LD} -arch ${ARCH} -r -keep_private_externs test.o -exported_symbols_list test.exp -o test-r.o - ${FAIL_IF_BAD_OBJ} test-r.o - # verify common not in export-list got demoted to private extern - nm -m test-r.o | grep "private external _common_global1" | ${FAIL_IF_EMPTY} - # verify only _common_global1 and _func_global1 changed - nm -m test.o | egrep -v '_common_global1|_func_global1|__eh_frame|__data' > test.nm - nm -m test-r.o | egrep -v '_common_global1|_func_global1|__eh_frame|__data' > test-r.nm - diff test.nm test-r.nm - # verify without -keep_private_externs that commons stay private extern - ${LD} -arch ${ARCH} -r test.o -exported_symbols_list test.exp -o test-rr.o - nm -m test-rr.o | grep _common_hidden | grep ') external' | ${FAIL_IF_STDIN} - nm -m test-rr.o | grep _common_global1 | grep ') external' | ${FAIL_IF_STDIN} - # should error out if told to export unavailable symbol - ${FAIL_IFF_SUCCESS} ${LD} -arch ${ARCH} -r test.o -exported_symbols_list test-bad.exp -o test2.o 2>/dev/null - -clean: - rm -rf test.o test-r.o test-rr.o test.nm test-r.nm test2.o diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp deleted file mode 100644 index 73154ba..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp +++ /dev/null @@ -1,3 +0,0 @@ -_bar -_baz -_foobar diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c deleted file mode 100644 index c9fdc35..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.c +++ /dev/null @@ -1,18 +0,0 @@ - - - -static int data_static1 = 1; -static int data_static2 = 2; - -void func_global1() { ++data_static1; } -void func_global2() { ++data_static2; } - -void __attribute__((visibility("hidden"))) func_hidden1() {} -void __attribute__((visibility("hidden"))) func_hidden2() {} - -int common_global1; -int common_global2; - -int __attribute__((visibility("hidden"))) common_hidden1; -int __attribute__((visibility("hidden"))) common_hidden2; - diff --git a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp b/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp deleted file mode 100644 index 93e7e17..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/exported_symbols_list-r/test.exp +++ /dev/null @@ -1,2 +0,0 @@ -_func_global2 -_common_global2 diff --git a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile deleted file mode 100644 index d9b92d5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to see that external relocations -# are sorted so that dyld only has to look up symbols once. -# The machochecker tool verifies that the relocs are sorted. -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf main libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c deleted file mode 100644 index bc28725..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - -int foo = 1; -int bar = 2; -int baz = 3; - diff --git a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c b/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c deleted file mode 100644 index 6c68c4c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/external-reloc-sorting/main.c +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -// in libfoo.dylib -extern int foo; -extern int bar; -extern int baz; - -// this initialilzed data will result in external relocations -// alternating the values, will create relocs that need sorting -int* array[] = { &foo, &bar, &baz, &foo, &bar, &baz, &foo, &bar, &baz }; - - -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/filelist/Makefile b/ld64/FireOpal/unit-tests/test-cases/filelist/Makefile deleted file mode 100644 index 0b1a743..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/filelist/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -PWD = $(shell pwd) - -# -# The point of this test is to check the two forms of the -# -filelist option -# - -run: all - -all: - ${CC} ${CCFLAGS} -c hello.c -o hello-${ARCH}.o - ${FAIL_IF_BAD_OBJ} hello-${ARCH}.o - echo "${PWD}/hello-${ARCH}.o" > "${PWD}/filelist1" - cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}" - ${FAIL_IF_BAD_MACHO} hello-${ARCH} - echo "hello-${ARCH}.o" > "${PWD}/filelist2" - cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" - ${PASS_IFF_GOOD_MACHO} hello-${ARCH} - -clean: - rm hello-* *.o filelist1 filelist2 diff --git a/ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt b/ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt deleted file mode 100644 index f193b11..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/filelist/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to check the two forms of the -filelist option diff --git a/ld64/FireOpal/unit-tests/test-cases/filelist/hello.c b/ld64/FireOpal/unit-tests/test-cases/filelist/hello.c deleted file mode 100644 index 14d9363..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/filelist/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - fprintf(stdout, "hello\n"); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile deleted file mode 100644 index b1015b2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-dylib/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a small dylib -flat_namespace and -# indirect internal references. -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib -flat_namespace - otool -Iv libmain.dylib | grep _foo | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} libmain.dylib - -clean: - rm *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c deleted file mode 100644 index 6014829..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-dylib/main.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -void foo() {} - - -int main() -{ - foo(); - fprintf(stdout, "hello\n"); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile deleted file mode 100644 index a3715e0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that when linking a main executable for flat-namespace -# that undefines in loaded flat-namespace dylibs are resolved. -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -flat_namespace -dynamiclib -o libfoo.dylib -undefined suppress - ${CC} ${CCFLAGS} bar.c -c -o bar.o - libtool -static bar.o -o libbar.a - # test that linking main executable -twolevel_namespace does not pull in bar() - ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.a -o main - nm -mn main | grep _bar | ${FAIL_IF_STDIN} - # test that linking dylib -flat_namespace does not pull in bar() - ${CC} ${CCFLAGS} main.c -flat_namespace libfoo.dylib libbar.a -dynamiclib -o main.dylib - nm -mn main.dylib | grep _bar | ${FAIL_IF_STDIN} - # test that linking main executable -flat_namespace pulls in bar() - ${CC} ${CCFLAGS} main.c -flat_namespace libfoo.dylib libbar.a -o main_flat - nm -mn main_flat | grep _bar | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main_flat - -clean: - rm libfoo.dylib libbar.a bar.o main main_flat main.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c deleted file mode 100644 index b348aa8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/bar.c +++ /dev/null @@ -1,4 +0,0 @@ - -void bar() {} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c b/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c deleted file mode 100644 index 39df2ea..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/foo.c +++ /dev/null @@ -1,8 +0,0 @@ - -extern void bar(); - -void foo() -{ - bar(); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile b/ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile deleted file mode 100644 index d31b7ab..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-main/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a hello-world program -flat_namespace and -# does not indirect internal references. -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c -o main-${ARCH} -flat_namespace - otool -Iv main-${ARCH} | grep _foo | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main-${ARCH} - -clean: - rm main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-main/main.c b/ld64/FireOpal/unit-tests/test-cases/flat-main/main.c deleted file mode 100644 index 6014829..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/flat-main/main.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -void foo() {} - - -int main() -{ - foo(); - fprintf(stdout, "hello\n"); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile b/ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile deleted file mode 100644 index 3f70e74..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/got-elimination/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that ld can remove non-lazy pointers for x86_64 -# - -all: all-${ARCH} - -all-armv6: all-true - -all-ppc: all-true - -all-ppc64: all-true - -all-i386: all-true - -all-true: - ${PASS_IFF} true - -all-x86_64: - ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib - otool -Iv libfoobar.dylib | grep 0x | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib -flat_namespace - otool -Iv libfoobar.dylib | grep 0x | ${PASS_IFF_STDIN} - -clean: - rm -rf libfoobar.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c b/ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c deleted file mode 100644 index 781c6fd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/got-elimination/bar.c +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int bar1 = 1; -int bar2 = 2; -int bar3 = 3; - diff --git a/ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c b/ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c deleted file mode 100644 index 45675a3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/got-elimination/foo.c +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -extern int bar1; -extern int bar2; // just under 2GB array -extern int bar3; - -int getbar1() -{ - return bar1; -} - -int getbar2() -{ - return bar2; -} - -int getbar3() -{ - return bar3; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile b/ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile deleted file mode 100644 index d8cdd51..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/header-pad/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a hello-world program with no errors (or crashes) -# - -run: all - -all: - ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} -Wl,-headerpad -Wl,0x3000 - ${PASS_IFF_GOOD_MACHO} hello-${ARCH} - -clean: - rm hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt b/ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt deleted file mode 100644 index 79114eb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/header-pad/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a hello-world program with no errors (or crashes) diff --git a/ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c b/ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c deleted file mode 100644 index 14d9363..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/header-pad/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - fprintf(stdout, "hello\n"); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile b/ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile deleted file mode 100644 index a1ade02..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/hello-world/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a hello-world program with no errors (or crashes) -# - -run: all - -all: - ${CC} ${CCFLAGS} hello.c -o hello-${ARCH} - ${PASS_IFF_GOOD_MACHO} hello-${ARCH} - -clean: - rm hello-* diff --git a/ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt b/ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt deleted file mode 100644 index 79114eb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/hello-world/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a hello-world program with no errors (or crashes) diff --git a/ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c b/ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c deleted file mode 100644 index e2f0fe9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/hello-world/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - fprintf(stdout, "hello\n"); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest deleted file mode 100644 index af93da1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/Makefile.newtest +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o - ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null - #ranlib libtest-${ARCH}.a - #${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - #${PASS_IFF_GOOD_MACHO} a-${ARCH} - - ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o a-${ARCH}.o 2>/dev/null - ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a - ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - ${PASS_IFF_GOOD_MACHO} a-${ARCH} - -clean: - rm -rf *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c deleted file mode 100644 index 8f92c9d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/a.c +++ /dev/null @@ -1,7 +0,0 @@ -extern int common_variable; - -int -main(int argc, char **argv) -{ - return common_variable; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c deleted file mode 100644 index 94578c6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common2/test.c +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int common_variable; -extern int main(); diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile deleted file mode 100644 index 6785960..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: all - -all: - ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o - ${FAIL_IF_BAD_OBJ} test-${ARCH}.o - ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o - ${FAIL_IF_BAD_OBJ} a-${ARCH}.o - ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null - ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a - ${CC} ${CCFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - ${PASS_IFF_GOOD_MACHO} a-${ARCH} - -clean: - rm -rf *.o *.a a-* diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c deleted file mode 100644 index 110842f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/a.c +++ /dev/null @@ -1,8 +0,0 @@ -extern int common_var; -int *fn(); - -int -main(int argc, char **argv) -{ - return 0!=&common_var; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c deleted file mode 100644 index e0fd7e8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common3/test.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -struct abc { - int a; - int b; - int c; -} struct_var; - -int common_var; -extern const int defined_var; - -int *fn() -{ - return &common_var; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest deleted file mode 100644 index d70c634..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/Makefile.newtest +++ /dev/null @@ -1,45 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o - ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o - #${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - #${PASS_IFF_GOOD_MACHO} a-${ARCH} - - ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o a-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - ${PASS_IFF_GOOD_MACHO} a-${ARCH} - -clean: - rm -rf *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c deleted file mode 100644 index 8f92c9d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/a.c +++ /dev/null @@ -1,7 +0,0 @@ -extern int common_variable; - -int -main(int argc, char **argv) -{ - return common_variable; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c deleted file mode 100644 index 94578c6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common4/test.c +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int common_variable; -extern int main(); diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest deleted file mode 100644 index d5b6d73..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/Makefile.newtest +++ /dev/null @@ -1,41 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o - ${FAIL_IF_ERROR} libtool -o libtest-${ARCH}.a test-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - ${PASS_IFF_GOOD_MACHO} a-${ARCH} - -clean: - rm -rf *.o *a diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c deleted file mode 100644 index 8f92c9d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/a.c +++ /dev/null @@ -1,7 +0,0 @@ -extern int common_variable; - -int -main(int argc, char **argv) -{ - return common_variable; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c b/ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c deleted file mode 100644 index f34267a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit-common5/test.c +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int common_variable; diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile deleted file mode 100644 index c982885..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -PWD = $(shell pwd) - - -# Verify -no_implicit_dylibs option -# add option to disable implicit load commands for indirectly used public dylibs -# - - -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 - # 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} - # verify that -no_implicit_dylibs causes main to get bar from libfoo - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-no_implicit_dylibs -L. - nm -m main | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c deleted file mode 100644 index e2164a2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/bar.c +++ /dev/null @@ -1,7 +0,0 @@ - - - -void bar() -{ -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c deleted file mode 100644 index 1624757..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - - -void foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile deleted file mode 100644 index e3056a5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# The point of this test is a sanity check that an indirect -# library is not accidentally searched for symbols. -# -# wrong error message when symbol is found in unused indirect library# -# - -run: all - -all: - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main libfoo.dylib 2> fail.log - grep ordinal fail.log | ${PASS_IFF_EMPTY} - -clean: - rm *.dylib main fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c deleted file mode 100644 index f39ee21..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/bar.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -// function called by a loaded bundle -int bar() -{ - return 1; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt deleted file mode 100644 index 311aa79..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/comment.txt +++ /dev/null @@ -1,4 +0,0 @@ -The point of this test is a sanity check that an indirect -library is not accidentally searched for symbols. - - wrong error message when symbol is found in unused indirect library# diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c deleted file mode 100644 index ba78c28..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/foo.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int bar(); - -int foo() -{ - return bar(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c deleted file mode 100644 index 13f57d7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-dylib/main.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern void bar(); - -int main() -{ - bar(); - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile deleted file mode 100644 index e79eb0f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/Makefile +++ /dev/null @@ -1,106 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that -F and -L work when finding indirect libraries -# - - -run: all - -all: - -# build foo that re-exports bar - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar - ${FAIL_IF_BAD_MACHO} libfoo.dylib - -# build an alternate libbar that also has baz - mkdir -p hide - ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/libbar.dylib -install_name libbar.dylib - ${FAIL_IF_BAD_MACHO} hide/libbar.dylib - -# build an executable that depends on a symbol in the alternate bar to validate that -L is used for indirect dylibs - ${CC} ${CCFLAGS} main.c -o main -lfoo -Lhide -L. - ${FAIL_IF_BAD_MACHO} main - - - -# build Foo.framework that re-exports Bar.framework - mkdir -p Bar.framework Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" - ${FAIL_IF_BAD_MACHO} Bar.framework/Bar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar - ${FAIL_IF_BAD_MACHO} Foo.framework/Foo - -# build an alternate Bar.framework that also has baz - mkdir -p hide/Bar.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" - ${FAIL_IF_BAD_MACHO} hide/Bar.framework/Bar - -# build an executable that depends on a symbol in the alternate Bar.framework to validate that -F is used for indirect dylibs - ${CC} ${CCFLAGS} main.c -o main -Fhide -F. -framework Foo - ${FAIL_IF_BAD_MACHO} main - - - -# build foo that links against bar - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - -# build an alternate libbar that also has baz - mkdir -p hide - ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o hide/libbar.dylib -install_name libbar.dylib - ${FAIL_IF_BAD_MACHO} hide/libbar.dylib - -# build a flat executable that depends on a symbol in the alternate bar to validate that -L is used for indirect dylibs - ${CC} ${CCFLAGS} -flat_namespace main.c -o main -lfoo -Lhide -L. - ${FAIL_IF_BAD_MACHO} main - - - -# build Foo.framework that re-exports libbar.dylib embedded in framework - mkdir -p Foo.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c baz.c -o Foo.framework/libbar.dylib -install_name "`pwd`/Foo.framework/libbar.dylib" - ${FAIL_IF_BAD_MACHO} Foo.framework/libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. Foo.framework/libbar.dylib -sub_library libbar - ${FAIL_IF_BAD_MACHO} Foo.framework/Foo - -# build an alternate libbar.dylib that does not have baz - mkdir -p hide - ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib - ${FAIL_IF_BAD_MACHO} hide/libbar.dylib - -# build an executable that depends on a symbol not in the alternate libbar.dylib to validate dylibs embedded in frameworks are not searched for - ${CC} ${CCFLAGS} main.c -o main -Lhide -F. -framework Foo - ${PASS_IFF_GOOD_MACHO} main - - -clean: - - rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework main diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c deleted file mode 100644 index af6a9f8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/baz.c +++ /dev/null @@ -1,5 +0,0 @@ - -int baz (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c deleted file mode 100644 index d0cdf47..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c b/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c deleted file mode 100644 index f02701a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/main.c +++ /dev/null @@ -1,8 +0,0 @@ -extern int foo (); -extern int bar (); -extern int baz (); - -int main (void) -{ - return foo() + bar() + baz(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile b/ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile deleted file mode 100644 index 2fe99a8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/interposable_list/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that -interposable and -interposable_list work -# - -run: all - -all: - # by default, no test* functions should go through stubs - ${CC} ${CCFLAGS} test.c -dynamiclib -o libtest.dylib - # -interposable should make all four test* functions go through stubs - ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable -o libtest.dylib - otool -Iv libtest.dylib | grep '4 entries' | ${FAIL_IF_EMPTY} - otool -Iv libtest.dylib | grep '_test' | ${FAIL_IF_EMPTY} - # -interposable_list should make just two test* functions go through stubs - ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable_list test.exp -o libtest.dylib - otool -Iv libtest.dylib | grep '2 entries' | ${FAIL_IF_EMPTY} - otool -Iv libtest.dylib | grep '_test3' | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} libtest.dylib - - -clean: - rm libtest.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c b/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c deleted file mode 100644 index 937420e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.c +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -const char kMyStr[] = "hello"; - -int test1() -{ - return 10; -} - -int test2() -{ - return 10; -} - -int test3() -{ - return 10; -} - -int test4() -{ - return 10; -} - -const char* getstr() -{ - test1(); - test2(); - test3(); - test4(); - return kMyStr; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp b/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp deleted file mode 100644 index 8f104f1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/interposable_list/test.exp +++ /dev/null @@ -1,2 +0,0 @@ -_test1 -_test2 diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/Makefile b/ld64/FireOpal/unit-tests/test-cases/large-data/Makefile deleted file mode 100644 index 408a2da..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/large-data/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that ld can link > 4GB zero fill section -# - -SHELL = bash # use bash shell so we can redirect just stderr - -ifeq (,${findstring 64,$(ARCH)}) - 32BIT_SHOULD_FAIL = ${FAIL_IF_SUCCESS} -else - 32BIT_SHOULD_FAIL = -endif - - -run: all - -all: - ${CC} ${CCFLAGS} test1.c -c -o test1.o - ${CC} ${CCFLAGS} test2.c -c -o test2.o - ${CC} ${CCFLAGS} test3.c -c -o test3.o - ${CC} ${CCFLAGS} test4.c -c -o test4.o - ${32BIT_SHOULD_FAIL} ${CC} ${CCFLAGS} test1.o test2.o test3.o test4.o -dynamiclib -o libtest.dylib 2> fail.log - ${PASS_IFF} true - -clean: - rm -rf test*.o libtest.dylib fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test1.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test1.c deleted file mode 100644 index 2d9ec94..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/large-data/test1.c +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int mediumarray1[1000]; -int bigarray1[500000000]; // just under 2GB array -int small1; - -int getbig1() -{ - return bigarray1[0]; -} - -int getmedium1() -{ - return mediumarray1[0]; -} - -int getsmall1() -{ - return small1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test2.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test2.c deleted file mode 100644 index d97bed1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/large-data/test2.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int bigarray2[500000000]; // just under 2GB array -int small2; - -int getbig2() -{ - return bigarray2[0]; -} - - -int getsmall2() -{ - return small2; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test3.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test3.c deleted file mode 100644 index b7ca398..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/large-data/test3.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int bigarray3[500000000]; // just under 2GB array -int small3; - -int getbig3() -{ - return bigarray3[0]; -} - - -int getsmall3() -{ - return small3; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/large-data/test4.c b/ld64/FireOpal/unit-tests/test-cases/large-data/test4.c deleted file mode 100644 index d879c5c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/large-data/test4.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int bigarray4[500000000]; // just under 2GB array -int small4; - -int getbig4() -{ - return bigarray4[0]; -} - - -int getsmall4() -{ - return small4; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile b/ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile deleted file mode 100644 index e3a0d65..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/late-link-error/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - -# -# The point of this test is a sanity check that if -# ld errors out during linking, that no output file is remaining -# - -run: all - -all: - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} link_error.s -dynamiclib -o link_error-${ARCH} 2> fail.log - ${FAIL_IFF} cat link_error-${ARCH} 2> fail.log - -clean: - rm link_error-* fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt b/ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt deleted file mode 100644 index 716686d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/late-link-error/comment.txt +++ /dev/null @@ -1,2 +0,0 @@ -The point of this test is a sanity check that if -ld errors out during linking, that no output file is remaining diff --git a/ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s b/ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s deleted file mode 100644 index 05d80f1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/late-link-error/link_error.s +++ /dev/null @@ -1,22 +0,0 @@ - - -#if __ppc__ || __ppc64__ - ; illegal absolute load -_foo: lis r2,ha16(_strcmp) -#endif - -#if __i386__ - // illegal absolute load -_foo: movl _strcmp, %eax -#endif - - -#if __x86_64__ - // illegal external load -_foo: movl _strcmp(%rip), %eax -#endif - -#if __arm__ - ; illegal absolute load -_foo: ldr r2, _strcmp -#endif diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h deleted file mode 100644 index eaa591f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.h +++ /dev/null @@ -1,9 +0,0 @@ - -#include - - -@interface Foo : NSObject - - - -@end diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m deleted file mode 100644 index 35a63b1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/foo.m +++ /dev/null @@ -1,8 +0,0 @@ - -#include "foo.h" - -@implementation Foo - - - -@end diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m deleted file mode 100644 index 0f11f68..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/main.m +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -#include "foo.h" - - -int main() -{ - [[Foo alloc] init]; - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c deleted file mode 100644 index 59a13fb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - - -extern int data; - -static int* pd = &data; - -int main() -{ - return *pd; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c deleted file mode 100644 index ffd2ce1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/bad2.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - - -extern int foo(); - -int main() -{ - int (*func)() = foo; - if ( func != NULL ) - (*func)(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c deleted file mode 100644 index c23c9dc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - -int data = 5; - -int foo() { return 1; } -int bar() { return 1; } diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c deleted file mode 100644 index 8546854..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/main.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - - -extern int foo(); -extern int bar(); - -int main() -{ - // two regular external function calls - void* x = malloc(16); - free(x); - // two lazy dylib external function calls - int result = foo(); - fprintf(stderr, "foo() returned %d\n", result); - bar(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile deleted file mode 100644 index 15f3307..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify that when two cstrings -# are coalesced that the one with greater alignment is used. -# - -run: all - -all: - ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o - ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o - - ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-align-3-${ARCH}.o | grep 'align:' > align-3 - - ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-r-${ARCH}.o | grep 'align:' > align-r - - ${PASS_IFF} diff align-3 align-r - -clean: - rm -rf *.o align-3 align-r diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s deleted file mode 100644 index 0dacbad..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-0.s +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - - .cstring -L21: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s deleted file mode 100644 index d2661dc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment/cstring-align-3.s +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - .cstring - .align 3 -L21: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile deleted file mode 100644 index c3c8c80..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -LD=ld - -# -# The point of this test is to verify that when two cstrings -# are coalesced that the one with greater alignment is used. -# - -run: all - -all: - ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o - ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o - - ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-align-3-${ARCH}.o > align-3 - - ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-r-${ARCH}.o > align-r - ${PASS_IFF} diff -C 6 align-3 align-r - -clean: - rm -rf *.o align-3 align-r diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt deleted file mode 100644 index 3b2e3c7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to verify that when two cstrings are coalesced that the one with greater alignment is used. diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s deleted file mode 100644 index 1a55cf6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - - .cstring -L20: .asciz "XXX" -L22: .ascii "hell\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s deleted file mode 100644 index 211aa72..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - .cstring - .align 2 -L21: .ascii "hell\0" - .align 13 -L99: .ascii "\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile deleted file mode 100644 index cac20b9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -#LD=ld64 - -# -# The point of this test is to verify that when two cstrings -# are coalesced that the one with greater alignment is used. -# - -run: all - -all: - ${CC} ${ASMFLAGS} cstring-align-0.s -c -o cstring-align-0-${ARCH}.o - ${FAIL_IF_BAD_OBJ} cstring-align-0-${ARCH}.o - - ${CC} ${ASMFLAGS} cstring-align-3.s -c -o cstring-align-3-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-align-3-${ARCH}.o > align-3 - - ${LD} -arch ${ARCH} -r cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o -o cstring-r-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -align -only cstring=hello cstring-r-${ARCH}.o > align-r - - ${PASS_IFF} diff -C 6 align-3 align-r - -clean: - rm -rf *.o align-3 align-r diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt deleted file mode 100644 index 3b2e3c7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to verify that when two cstrings are coalesced that the one with greater alignment is used. diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s deleted file mode 100644 index 1a55cf6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - - .cstring -L20: .asciz "XXX" -L22: .ascii "hell\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s deleted file mode 100644 index 211aa72..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - .cstring - .align 2 -L21: .ascii "hell\0" - .align 13 -L99: .ascii "\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile deleted file mode 100644 index dcf1dbe..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify that literals are uniqued. -# After running ld -r all duplicates should be removed. -# - -run: all - -all: - ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-${ARCH}.o | grep 'name:'| uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} - ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-r-${ARCH}.o | grep 'name:' | uniq -d | ${PASS_IFF_EMPTY} - -clean: - rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s deleted file mode 100644 index 6e5febc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce/literals.s +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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@ - */ - - .literal16 -L01:.long 12345678 - .long 87654321 - .long 12345678 - .long 87654321 - -L02:.long 12345678 - .long 87654321 - .long 12345678 - .long 87654322 - -L03:.long 22345678 - .long 87654321 - .long 12345678 - .long 87654321 - -L04:.long 12345678 - .long 87654321 - .long 12345678 - .long 87654321 - - - .literal8 -L1: .long 12345678 - .long 87654321 - -L2: .long 12345678 - .long 87654322 - -L3: .long 22345678 - .long 87654321 - -L4: .long 12345678 - .long 87654321 - - .literal4 -L11:.long 12345678 -L12:.long 12345679 -L13:.long 22345678 -L14:.long 12345678 - - .cstring -L21: .ascii "hello\0" -L22: .ascii "hello,there\0" -L23: .ascii "there\0" -L24: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest deleted file mode 100644 index 0f9d1b5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/Makefile.newtest +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify that literals are uniqued. -# After running ld -r all duplicates should be removed. -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -only literals-${ARCH}.o | uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} - ${FAIL_IF_ERROR} ${LD} -arch ${ARCH} -r literals-${ARCH}.o -o literals-r-${ARCH}.o - ${PASS_IFF} ./test.sh literals-r-${ARCH}.o - -clean: - rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt deleted file mode 100644 index 56cea21..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to verify that literals are uniqued. After running ld -r all duplicates should be removed. diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s deleted file mode 100644 index b8d4354..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/literals.s +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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@ - */ - - .literal8 - -L1: .long 12345678 - .long 87654321 - -L2: .long 12345678 - .long 87654322 - -L3: .long 22345678 - .long 87654321 - -L4: .long 12345678 - .long 87654321 - - .literal4 -L11:.long 12345678 -L12:.long 12345679 -L13:.long 22345678 -L14:.long 12345678 - - .cstring -L21: .ascii "hello\0" -L22: .ascii "hello,there\0" -L23: .ascii "there\0" -L24: .ascii "hello\0" diff --git a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh b/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh deleted file mode 100755 index 57a36b7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/literals-coalesce2/test.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -SZ=`size "$1" | tail -n 1 | sed 's,\([0-9]*\).*,\1,'` -[ "$SZ" ] && [ "$SZ" = 54 ] && exit 0 -exit 1 diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile deleted file mode 100644 index 42c71e4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/Makefile +++ /dev/null @@ -1,289 +0,0 @@ -## -# Copyright (c) 2006-2008 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. - -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 - -# -# Test the we set the stack execution bit properly. - -run: - if [ -f /Developer/usr/bin/llvm-gcc-4.2 ] ; then \ - $(MAKE) all ; \ - else \ - ${PASS_IFF} /usr/bin/true ; \ - fi - -all: zero one two three four five six seven eight nine ten \ - eleven twelve thirteen fourteen fifteen sixteen seventeen \ - eighteen nineteen twenty - - -zero: - # - # llvm : a.c : Dfoo3 - # llvm : b.c : Dfoo2 - # 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 - ${PASS_IFF_GOOD_MACHO} main.exe - -one: - # - # llvm : a1.c : Dfoo3, Ufoo4 - # llvm : b1.c : Dfoo2, Ufoo4 - # 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 - ${PASS_IFF_GOOD_MACHO} main1.exe - -two: - # - # llvm : a2.c : Dfoo3, Ufoo4 - # llvm : b2.c : Dfoo2, Dfoo4 - # 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 - ${PASS_IFF_GOOD_MACHO} main2.exe - -three: - # - # llvm : a3.c : Dfoo1, Dbar - # llvm : b3.c : Dfoo2, Ubar - # 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 - ${PASS_IFF_GOOD_MACHO} main3.exe - -four: - # - # llvm : a4.c : Dfoo3, Ufoo4 - # llvm : b4.c : Dfoo2, DLmyfoo, Ufoo4 - # 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 - ${PASS_IFF_GOOD_MACHO} main4.exe - -five: - # - # llvm : a5.c : Dfoo1, Ufoo2, Ufoo3 - # llvm : b5.c : Dfoo2 - # MachO : main5.c : Dfoo3, Ufoo1 - # - #echo "Five..." - ${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 - ${OTOOL} -tV main5.exe | grep foo3 | ${PASS_IFF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main5.exe - -six: - # - # llvm : a6.c : Dfoo1, Dfoo2 - # 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 - ${PASS_IFF_GOOD_MACHO} main6.exe - ${OTOOL} -tV main6.exe | grep foo2 | ${PASS_IFF_EMPTY} - -seven: - # - # llvm : a7.c : Dfoo1, Dfoo2, Ufoo3 - # llvm : b7.c : Dfoo3, ufoo2 - # 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 - ${PASS_IFF_GOOD_MACHO} main7.exe - -eight: - # - # llvm : a8.c : Dfoo1, Dfoo2 - # 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 - ${OTOOL} -tV main8.exe | grep foo2 | ${PASS_IFF_EMPTY} - ${OTOOL} -tV main8.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY} - -nine: - # - # llvm : a9.c : Dfoo1, Dfoo2, Dfoo3, Ufoo3, Ufoo4 - # 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 - ${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} - -ten: - # - # llvm : a10.c - # llvm : b10.c - # 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 - ${PASS_IFF_GOOD_MACHO} main10.exe - -eleven: - # - # llvm : a11.c - # 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 - ${PASS_IFF_GOOD_MACHO} main11.exe - -twelve: - # - # llvm : a12.c - # MachO : main12.c - # - #echo "Tweleve..." - ${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 - ${PASS_IFF_GOOD_MACHO} main12.exe - -thirteen: - # - # llvm : a13.cc - # 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 - -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 - ${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 - ${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 - ${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 - ${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 - ${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 b17.o - ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o - file ab17.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 - ${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o - ObjectDump -nm a18-rkpe.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} - ObjectDump -nm a18-rkpe.o | grep _func_hidden2 | grep " hidden" | ${FAIL_IF_EMPTY} - #echo verify ld -r makes hidden symbols internal (except for commons) - ${LD} -arch ${ARCH} -r a18.o -o a18-r.o - #ObjectDump -nm a18-r.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} - #ObjectDump -nm a18-r.o | grep _func_hidden2 | grep " internal" | ${FAIL_IF_EMPTY} - -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 - 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 - ar cru lib20.a a20.o b20.o - ${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe - nm main20.exe | grep _foo | ${PASS_IFF_STDIN} - - - - -clean: - rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c deleted file mode 100644 index 0c96178..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a.c +++ /dev/null @@ -1,5 +0,0 @@ -int foo3() -{ - return 21; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c deleted file mode 100644 index f9fb403..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a1.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include -extern int foo4(); -int foo3() -{ -/* printf ("%s\n",strerror(errno)); */ - return foo4(); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c deleted file mode 100644 index 0dc181e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a10.c +++ /dev/null @@ -1,5 +0,0 @@ -extern void foo(void); - -void foo(void) -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c deleted file mode 100644 index e95dc40..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a11.c +++ /dev/null @@ -1,6 +0,0 @@ -#include -void foo3(void) -{ - fputc ('x', stderr); - printf ("\n"); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c deleted file mode 100644 index 80bc1e8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "a12.h" - -enum E e[1000]; -void foo(void) -{ - e[1] = ONE; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h deleted file mode 100644 index be43955..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a12.h +++ /dev/null @@ -1,8 +0,0 @@ -enum E - { - ZERO, - ONE - }; - -extern enum E e[1000]; -extern void foo(void); diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc deleted file mode 100644 index edc938a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.cc +++ /dev/null @@ -1,3 +0,0 @@ -#include "a13.h" - -A::~A() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h deleted file mode 100644 index bc00448..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a13.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -class A { - public: - virtual ~A(); - void foo() { printf ("Hi\n"); } -}; diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c deleted file mode 100644 index 8f4ea09..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a14.c +++ /dev/null @@ -1 +0,0 @@ -int X __attribute__((visibility("hidden"))) = 14; diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c deleted file mode 100644 index a25431b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a15.c +++ /dev/null @@ -1,3 +0,0 @@ -void __attribute__((visibility("hidden"))) foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c deleted file mode 100644 index 3cd06fd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a17.c +++ /dev/null @@ -1,4 +0,0 @@ - -int a = 0; -int func_a() { return a; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c deleted file mode 100644 index c9fdc35..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a18.c +++ /dev/null @@ -1,18 +0,0 @@ - - - -static int data_static1 = 1; -static int data_static2 = 2; - -void func_global1() { ++data_static1; } -void func_global2() { ++data_static2; } - -void __attribute__((visibility("hidden"))) func_hidden1() {} -void __attribute__((visibility("hidden"))) func_hidden2() {} - -int common_global1; -int common_global2; - -int __attribute__((visibility("hidden"))) common_hidden1; -int __attribute__((visibility("hidden"))) common_hidden2; - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c deleted file mode 100644 index 0eb20c5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a2.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int foo4(void); -int foo3() -{ - return foo4(); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c deleted file mode 100644 index cd52955..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a20.c +++ /dev/null @@ -1,2 +0,0 @@ -void foo() {} -void bar() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c deleted file mode 100644 index 040ca5f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a3.c +++ /dev/null @@ -1,6 +0,0 @@ -int bar; -int foo1() -{ - return bar; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c deleted file mode 100644 index 0eb20c5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a4.c +++ /dev/null @@ -1,6 +0,0 @@ -extern int foo4(void); -int foo3() -{ - return foo4(); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c deleted file mode 100644 index bcb22d9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a5.c +++ /dev/null @@ -1,10 +0,0 @@ -extern int foo2(void); -extern int foo3(void); - -int foo1() -{ - int i = 42; - if (foo2()) - i = foo3(); - return i; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c deleted file mode 100644 index b621453..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a6.c +++ /dev/null @@ -1,10 +0,0 @@ - -int foo1() -{ - return 42; -} - -int foo2() -{ - return 21; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c deleted file mode 100644 index 560919a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a7.c +++ /dev/null @@ -1,11 +0,0 @@ -extern int foo3(void); - -int foo1(void) -{ - return foo3(); -} - -int foo2(void) -{ - return 42; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c deleted file mode 100644 index 47352a7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a8.c +++ /dev/null @@ -1,23 +0,0 @@ - -static signed int i = 0; -extern int foo1(void); -extern void foo2(void); - -void foo2(void) { - - i = -1; - -} - -static int foo3() { - return 10; -} - -int foo1(void) -{ - int data = 0; - if (i < 0) - data = foo3(); - data += 42; - return data; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c deleted file mode 100644 index da2c8fa..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.c +++ /dev/null @@ -1,25 +0,0 @@ - -static signed int i = 0; -extern int foo1(void); -extern void foo2(void); -extern void foo4(void); - -void foo2(void) { - - i = -1; - -} - -static int foo3() { - foo4(); - return 10; -} - -int foo1(void) -{ - int data = 0; - if (i < 0) - data = foo3(); - data += 42; - return data; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list deleted file mode 100644 index 583bc2f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/a9.list +++ /dev/null @@ -1,3 +0,0 @@ -_foo1 -_main -_bar diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c deleted file mode 100644 index 61c92dc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b.c +++ /dev/null @@ -1,3 +0,0 @@ -int foo2() { - return 21; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c deleted file mode 100644 index 5158abe..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b1.c +++ /dev/null @@ -1,4 +0,0 @@ -extern int foo4(); -int foo2() { - return foo4(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c deleted file mode 100644 index d899aa9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "b10.h" -extern void foo(void); - -struct my_struct my_hooks = { - foo -}; - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h deleted file mode 100644 index fcb50d2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b10.h +++ /dev/null @@ -1,6 +0,0 @@ -struct my_struct -{ - void (*f)(void); -}; - -extern struct my_struct my_hooks; diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c deleted file mode 100644 index 59c5120..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b14.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int Y; -extern int X __attribute__((visibility("hidden"))); -void foo() { - printf ("%d\n", X); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c deleted file mode 100644 index 13c88d7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b15.c +++ /dev/null @@ -1,8 +0,0 @@ -extern void foo(); -void bar() { - foo(); -} - -void __attribute__((visibility("hidden"))) f2() -{} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c deleted file mode 100644 index 47d4cc3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b17.c +++ /dev/null @@ -1,4 +0,0 @@ -int b = 0; -int func_b() { return b; } - - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c deleted file mode 100644 index a20f6e3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b2.c +++ /dev/null @@ -1,9 +0,0 @@ -extern int foo4(void); - -int foo4(void) -{ - return 21; -} -int foo2() { - return foo4(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c deleted file mode 100644 index f6c0ed3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b20.c +++ /dev/null @@ -1 +0,0 @@ -void frob() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c deleted file mode 100644 index 31e7ee8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b3.c +++ /dev/null @@ -1,4 +0,0 @@ -extern int bar; -int foo2() { - return bar; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c deleted file mode 100644 index 9437ad0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b4.c +++ /dev/null @@ -1,13 +0,0 @@ -extern int foo4(void); - -int foo4(void) -{ - return 21; -} -static int myfoo() -{ - return foo4(); -} -int foo2() { - return myfoo(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c deleted file mode 100644 index a105df9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b5.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo2(void) -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c deleted file mode 100644 index d34f91a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/b7.c +++ /dev/null @@ -1,7 +0,0 @@ -extern int foo2(void); -extern int foo3(void); - -int foo3(void) -{ - return foo2(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c deleted file mode 100644 index 0089d64..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/c15.c +++ /dev/null @@ -1,9 +0,0 @@ -extern void foo(); -int main() { - foo(); - return 0; -} - -void __attribute__((visibility("hidden"))) f2() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c deleted file mode 100644 index ac424ca..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main.c +++ /dev/null @@ -1,9 +0,0 @@ -extern int foo2(); -extern int foo3(); -int main(){ - int i = foo3() + foo2(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c deleted file mode 100644 index ccad10d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main1.c +++ /dev/null @@ -1,13 +0,0 @@ -extern int foo2(); -extern int foo3(); -int foo4() -{ - return 21; -} -int main(){ - int i = foo3() + foo2(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c deleted file mode 100644 index dabcdcf..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main10.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "b10.h" - -int main() -{ - struct my_struct *mh = &my_hooks; - - mh->f(); - - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c deleted file mode 100644 index 82ef7ff..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main11.c +++ /dev/null @@ -1,7 +0,0 @@ - -extern void foo3(void); -int main() -{ - foo3(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c deleted file mode 100644 index 4de8725..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main12.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "a12.h" -int main() -{ - e[0] = ZERO; - foo(); - return e[0]; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc deleted file mode 100644 index 697d81b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main13.cc +++ /dev/null @@ -1,8 +0,0 @@ -#include "a13.h" - -int main() -{ - A a; - a.foo(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c deleted file mode 100644 index 67112aa..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main16.c +++ /dev/null @@ -1,8 +0,0 @@ - -int tent; -int global = 5; - -int foo() { return tent + global; } - -int main() { foo(); return 0; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c deleted file mode 100644 index e09c1c9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main19.c +++ /dev/null @@ -1,8 +0,0 @@ -extern int foo(); - -int main() -{ - foo(); - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c deleted file mode 100644 index ac424ca..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main2.c +++ /dev/null @@ -1,9 +0,0 @@ -extern int foo2(); -extern int foo3(); -int main(){ - int i = foo3() + foo2(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c deleted file mode 100644 index a5058fe..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main3.c +++ /dev/null @@ -1,13 +0,0 @@ -extern int foo1(); -extern int foo2(); -extern int bar; -int main(){ - int i; - bar = 14; - i = foo1() + foo2() + bar; - if (i == 42) - return 0; - else - return 1; - -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c deleted file mode 100644 index ac424ca..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main4.c +++ /dev/null @@ -1,9 +0,0 @@ -extern int foo2(); -extern int foo3(); -int main(){ - int i = foo3() + foo2(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c deleted file mode 100644 index 28d551d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main5.c +++ /dev/null @@ -1,16 +0,0 @@ - -extern int foo1(void); - -int foo3(void) -{ - return 42; -} - -int main() -{ - int i = foo1(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c deleted file mode 100644 index 3d00382..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main6.c +++ /dev/null @@ -1,10 +0,0 @@ -extern int foo1(); - -int main() -{ - int i = foo1(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c deleted file mode 100644 index 22427e3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main7.c +++ /dev/null @@ -1,10 +0,0 @@ -extern int foo1(void); - -int main(void) -{ - int i = foo1(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c deleted file mode 100644 index a9c924d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main8.c +++ /dev/null @@ -1,11 +0,0 @@ -extern int foo1(void); -extern void foo2(void); - -int main() -{ - int i = foo1(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c b/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c deleted file mode 100644 index b44bf6e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main9.c +++ /dev/null @@ -1,14 +0,0 @@ -extern int foo1(void); -extern void foo2(void); - -void foo4(void) -{ -} -int main() -{ - int i = foo1(); - if (i == 42) - return 0; - else - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile b/ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile deleted file mode 100644 index 126c10d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/loader_path/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# The point of this test is a sanity check that an indirect -# library loaded with @loader_path works -# -# ld64 should handle linking against dylibs that have @loader_path based dylib load commands -# - -run: all - -all: - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name @loader_path/libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} foo.c libbar.dylib -dynamiclib -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} main.c -o main libfoo.dylib - ${PASS_IFF_GOOD_MACHO} libfoo.dylib - -clean: - rm *.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c b/ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c deleted file mode 100644 index a307157..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/loader_path/bar.c +++ /dev/null @@ -1,6 +0,0 @@ - -int bar() -{ - return 1; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c b/ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c deleted file mode 100644 index 8c2179d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/loader_path/foo.c +++ /dev/null @@ -1,7 +0,0 @@ - -extern int bar(); - -int foo() -{ - return bar(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile deleted file mode 100644 index 8fc14f7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# This test case checks -non_global_symbols_no_strip_list and -non_global_symbols_strip_list -# with and without wildcards -# - - -run: all - -all: - ${CC} ${CCFLAGS} main.c foo.c -o main - ${FAIL_IF_BAD_MACHO} main - nm -j main > main.nm - # build stripping a.list - ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_strip_list,a.list -o main-a - ${FAIL_IF_BAD_MACHO} main-a - nm -j main-a > main-a.nm - diff main.nm main-a.nm | egrep '<|>' > a.diff - diff a.diff a.expect | ${FAIL_IF_STDIN} - # build but strip at .o file level a.list - ${CC} ${CCFLAGS} main.c -c -o main.o - ${CC} ${CCFLAGS} foo.c -c -o foo.o - ${LD} -r -arch ${ARCH} main.o foo.o -o all-a.o -non_global_symbols_strip_list a.list - ${CC} ${CCFLAGS} all-a.o -Wl,-non_global_symbols_strip_list,a.list -o main-a - ${FAIL_IF_BAD_MACHO} main-a - nm -j main-a > main-a.nm - diff main.nm main-a.nm | egrep '<|>' > a.diff - diff a.diff a.expect | ${FAIL_IF_STDIN} - # build stripping b.list - ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_strip_list,b.list -o main-b - ${FAIL_IF_BAD_MACHO} main-b - nm -j main-b > main-b.nm - diff main.nm main-b.nm | egrep '<|>' > b.diff - diff b.diff b.expect | ${FAIL_IF_STDIN} - # build but strip at .o file level b.list - ${CC} ${CCFLAGS} main.c -c -o main.o - ${CC} ${CCFLAGS} foo.c -c -o foo.o - ${LD} -r -arch ${ARCH} main.o foo.o -o all-b.o -non_global_symbols_strip_list b.list - ${CC} ${CCFLAGS} all-b.o -Wl,-non_global_symbols_strip_list,b.list -o main-b - ${FAIL_IF_BAD_MACHO} main-b - nm -j main-b > main-b.nm - diff main.nm main-b.nm | egrep '<|>' > b.diff - diff b.diff b.expect | ${FAIL_IF_STDIN} - # build stripping c.list - ${CC} ${CCFLAGS} main.c foo.c -Wl,-non_global_symbols_no_strip_list,c.list -o main-c - nm -m main-c | grep non-external | grep -v my | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main-c - - -clean: - rm -rf main main.nm main-a main-a.nm a.diff main-b main-b.nm b.diff main-c all-a.o all-b.o foo.o main.o diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect deleted file mode 100644 index 9ecbf53..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.expect +++ /dev/null @@ -1,2 +0,0 @@ -< _myglobal -< _xmyglobal2 diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list deleted file mode 100644 index 16c59a2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/a.list +++ /dev/null @@ -1,2 +0,0 @@ -_myglobal -_xmyglobal2 diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect deleted file mode 100644 index f1d15e4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.expect +++ /dev/null @@ -1,3 +0,0 @@ -< _myfunction -< _myglobal2 -< _xmyglobal2 diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list deleted file mode 100644 index 97170ac..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/b.list +++ /dev/null @@ -1,2 +0,0 @@ -*2 -_myf*on diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list deleted file mode 100644 index be715cd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/c.list +++ /dev/null @@ -1 +0,0 @@ -*my* diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c deleted file mode 100644 index 42e6cb7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/foo.c +++ /dev/null @@ -1,11 +0,0 @@ - - -int __attribute__((visibility("hidden"))) myglobal = 3; -int __attribute__((visibility("hidden"))) myglobal2 = 3; -int __attribute__((visibility("hidden"))) xmyglobal = 3; -int __attribute__((visibility("hidden"))) xmyglobal2 = 3; - -void __attribute__((visibility("hidden"))) myfunction(int x) { } - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c b/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c deleted file mode 100644 index 013bc55..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/local-symbol-partial-stripping/main.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -extern int myglobal; -extern void myfunction(int); - -int main() -{ - myfunction(myglobal); - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c b/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c deleted file mode 100644 index 8df913a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/foo.c +++ /dev/null @@ -1,6 +0,0 @@ - - -__attribute__((visibility("hidden"))) void foo() -{ - // do nothing -} diff --git a/ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile b/ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile deleted file mode 100644 index 03756ff..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/main-stripped/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that a dynamically referenced symbol is always exported -# - -run: all - -all: - ${CC} main.c -o main-${ARCH} -exported_symbols_list main.exp - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm -m main-${ARCH} | grep _magicSymbol | grep "referenced dynamically" | ${PASS_IFF_STDIN} - -clean: - rm main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c b/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c deleted file mode 100644 index 1e71f1b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.c +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -// set magic "dynamically referenced" bit on magicSymbol -int magicSymbol = 1; -asm(".desc _magicSymbol, 0x10"); - - -int main() -{ - return 0; -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp b/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp deleted file mode 100644 index 4eb9e89..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/main-stripped/main.exp +++ /dev/null @@ -1 +0,0 @@ -_main diff --git a/ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile b/ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile deleted file mode 100644 index 81c3f58..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/missing-option-args/Makefile +++ /dev/null @@ -1,98 +0,0 @@ -## -# Copyright (c) 2007 Apple, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Verify that missing arguments don't cause ld to crash -# This tests 64-bit arguments only -# - - -OUTPUT=2>/dev/null -LDCMD=${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} ${OUTPUT} - -run: all - -all: - ${FAIL_IF_SUCCESS} ${LD} -arch 2>/dev/null - ${LDCMD} -filelist - ${LDCMD} -o - ${LDCMD} -read_only_relocs - ${LDCMD} -sect_diff_relocs - ${LDCMD} -weak_reference_mismatches - ${LDCMD} -l - ${LDCMD} -weak-l - ${LDCMD} -weak-library - ${LDCMD} -L - ${LDCMD} -syslibroot - ${LDCMD} -framework - ${LDCMD} -framework name, - ${LDCMD} -weak_framework - ${LDCMD} -weak_framework name - ${LDCMD} -weak_framework name, - ${LDCMD} -F - ${LDCMD} -dylib_file - ${LDCMD} -dylib_file install_name - ${LDCMD} -sectcreate segname sectname - ${LDCMD} -sectorder - ${LDCMD} -sectorder segname sectname - ${LDCMD} -u - ${LDCMD} -e - ${LDCMD} -i - ${LDCMD} -idefinition: - ${LDCMD} -undefined - ${LDCMD} -U - ${LDCMD} -commons - ${LDCMD} -warn_commons - ${LDCMD} -exported_symbols_list - ${LDCMD} -unexported_symbols_list - ${LDCMD} -filelist - ${LDCMD} -filelist listfile, - ${LDCMD} -headerpad - ${LDCMD} -A - ${LDCMD} -dylib_install_name - ${LDCMD} -umbrella - ${LDCMD} -allowable_client - ${LDCMD} -client_name - ${LDCMD} -sub_umbrella - ${LDCMD} -sub_library - ${LDCMD} -init - ${LDCMD} -dylinker_install_name - ${LDCMD} -macosx_version_min - ${LDCMD} -final_output - ${LDCMD} -seg1addr - ${LDCMD} -pagezero_size - ${LDCMD} -dylib_compatibility_version - ${LDCMD} -stack_addr - ${LDCMD} -stack_size - ${LDCMD} -sectcreate - ${LDCMD} -sectcreate segname - ${LDCMD} -sectalign - ${LDCMD} -sectalign segname - ${LDCMD} -sectalign segname sectname - ${LDCMD} -sectorder segname - ${LDCMD} -dylib_current_version - ${PASS_IFF} true - -clean: diff --git a/ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt b/ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt deleted file mode 100644 index 8000102..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/missing-option-args/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Verify that missing arguments don't cause ld to crash diff --git a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile deleted file mode 100644 index 136bcf6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${ASMFLAGS} test.s -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_sort test.${ARCH}.o > test.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_sort test-r.${ARCH}.o > test-r.${ARCH}.o.dump - - ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt b/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt deleted file mode 100644 index 4e819e1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used -dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. -No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest deleted file mode 100644 index d0f7df8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/Makefile.newtest +++ /dev/null @@ -1,39 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to determine if -# common symbols are not allowed with MH_DYLIB output format with the -multi_module option -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o - ${PASS_IFF_ERROR} libtool -dynamic -o libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null - -clean: - rm -rf *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c deleted file mode 100644 index 8f92c9d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/a.c +++ /dev/null @@ -1,7 +0,0 @@ -extern int common_variable; - -int -main(int argc, char **argv) -{ - return common_variable; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt deleted file mode 100644 index a5a960d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to determine if common symbols are not allowed with MH_DYLIB output format with the -multi_module option diff --git a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c b/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c deleted file mode 100644 index f34267a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-dynamic-common/test.c +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int common_variable; diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile b/ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile deleted file mode 100644 index 5d52553..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-uuid/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the we set emit LC_UUID correctly -# - -run: all - -all: - -# Test main executable built with dwarf has uuid - ${CC} ${CCFLAGS} foo.c -o foo -gdwarf-2 - ${FAIL_IF_BAD_MACHO} foo - ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} - rm -f foo - -# Test main executable built with stabs has uuid - ${CC} ${CCFLAGS} foo.c -o foo -gfull -gstabs+ - ${FAIL_IF_BAD_MACHO} foo - ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} - -# Test main executable built with dwarf and -no_uuid does not have uuid - ${CC} ${CCFLAGS} foo.c -o foo -Wl,-no_uuid -gdwarf-2 - ${FAIL_IF_BAD_MACHO} foo - ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN} - -# Test ld -r of stabs file has no uuid - ${CC} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ - ${LD} -arch ${ARCH} foo.o -r -o foo2.o - ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN} - -# Test ld -r of two files one with uuid produces a uuid - ${CC} ${CCFLAGS} foo.c -c -o foo.o -gdwarf-2 - ${LD} -arch ${ARCH} foo.o -r -o foo2.o - ${CC} ${CCFLAGS} bar.c -c -gstabs+ -o bar.o - ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o - ${OTOOL} -hlv foobar.o | grep LC_UUID | ${PASS_IFF_STDIN} - -clean: - rm -rf foo foo.o foo2.o bar.o foobar.o foo.dSYM diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c b/ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c deleted file mode 100644 index cbefe0f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-uuid/bar.c +++ /dev/null @@ -1,4 +0,0 @@ -int bar (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt b/ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt deleted file mode 100644 index 269bbbd..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-uuid/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test the we set emit LC_UUID correctly diff --git a/ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c b/ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c deleted file mode 100644 index 57ed6ba..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/no-uuid/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int main (void) -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile deleted file mode 100644 index 3c14103..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that non-lazy-pointers are properly handled by -r -# - - -all: all-${ARCH} - -all-ppc: hasnl - -all-ppc64: hasnl - -all-i386: hasnl - -all-armv6: hasnl - -all-x86_64: all-true - -all-true: - ${PASS_IFF} true - - -hasnl: - ${CC} ${CCFLAGS} -c foo.c -o foo.o - ${CC} ${CCFLAGS} -c other.c -o other.o - ${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo - # make sure there are two indirect symbols: _foo and LOCAL - otool -Iv fooall.o | grep "2 entries" | ${FAIL_IF_EMPTY} - otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} - otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN} - # make sure re-parsed correctly - ${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${PASS_IFF} true - -clean: - rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c deleted file mode 100644 index 1fa325e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/foo.c +++ /dev/null @@ -1,12 +0,0 @@ - - - -extern int foo; - -int getfoo() { return foo; } - - -extern int other; - -int getother() { return other; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c b/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c deleted file mode 100644 index 6420437..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/non-lazy-r/other.c +++ /dev/null @@ -1,2 +0,0 @@ -int foo = 2; -int other = 3; diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp deleted file mode 100644 index 720c52d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.exp +++ /dev/null @@ -1 +0,0 @@ -.objc_class_name_Foo diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m b/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m deleted file mode 100644 index d39c8f7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/foo.m +++ /dev/null @@ -1,18 +0,0 @@ -#include - -@interface Foo : NSObject -@end - -@implementation Foo -@end - - - -@interface Bar : NSObject -@end - -@implementation Bar -@end - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile deleted file mode 100644 index 0abe7d5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -## -# Copyright (c) 2007-2008 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - - -# -# Validate that the linker catches illegal combinations of .o files -# compiled with different GC settings. -# - -test: - ${CC} ${CCFLAGS} foo.m -c -o foo.o - ${FAIL_IF_BAD_OBJ} foo.o - - ${CC} ${CCFLAGS} foo.m -c -o foo-gc.o -fobjc-gc - ${FAIL_IF_BAD_OBJ} foo-gc.o - - ${CC} ${CCFLAGS} foo.m -c -o foo-gc-only.o -fobjc-gc-only - ${FAIL_IF_BAD_OBJ} foo-gc-only.o - - ${CC} ${CCFLAGS} bar.m -c -o bar.o - ${FAIL_IF_BAD_OBJ} bar.o - - ${CC} ${CCFLAGS} bar.m -c -o bar-gc.o -fobjc-gc - ${FAIL_IF_BAD_OBJ} bar-gc.o - - ${CC} ${CCFLAGS} bar.m -c -o bar-gc-only.o -fobjc-gc-only - ${FAIL_IF_BAD_OBJ} bar-gc-only.o - - # check RR + RR -> RR - ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib - ${FAIL_IF_BAD_MACHO} libfoobar.dylib - - # check GC/RR + GC/RR -> GC/RR - ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib - ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY} - - # check GC + GC -> GC - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib - ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} - - # check RR + GC/RR -> RR - ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib - ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} - - # check GC + GC/RR -> GC - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib - ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} - - # check RR + GC -> error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log - - ${PASS_IFF} true - -clean: - rm -rf foo*.o bar*.o libfoobar.dylib fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m deleted file mode 100644 index 5c98709..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/bar.m +++ /dev/null @@ -1,11 +0,0 @@ - -@interface Bar { - int f; -} -- (void) doit; -@end - -@implementation Bar -- (void) doit { } -@end - diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt deleted file mode 100644 index 953da58..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Validate that the linker catches illegal combintations of .o files compiled with different GC settings diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m deleted file mode 100644 index e13367e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/foo.m +++ /dev/null @@ -1,12 +0,0 @@ - -@interface Foo { - int f; -} -- (void) doit; -@end - - -@implementation Foo -- (void) doit { } -@end - diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c b/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c deleted file mode 100644 index df4aeef..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-gc-checks/runtime.c +++ /dev/null @@ -1,2 +0,0 @@ -void _objc_empty_cache() {} -void _objc_empty_vtable() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m b/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m deleted file mode 100644 index a5c1ea8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/test.m +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include - -void test() -{ - // two class references - // two selector references - [NSObject superclass]; - [NSString description]; -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile b/ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile deleted file mode 100644 index a11f1d0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-references/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify an Objective-C object file -# is parsed to find the proper class references -# - -run: all - -all: - ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o - ${FAIL_IF_BAD_OBJ} test.${ARCH}.o - - ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_BAD_OBJ} test-r.${ARCH}.o - - nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSObject' | ${FAIL_IF_EMPTY} - nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSData' | ${FAIL_IF_EMPTY} - nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSArray' | ${FAIL_IF_EMPTY} - nm test-r.${ARCH}.o | grep -i 'objc_class_.*_NSString' | ${PASS_IFF_STDIN} - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt b/ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt deleted file mode 100644 index ce67b71..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-references/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to verify an Objective-C object file is parsed to find the proper class references diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-references/test.m b/ld64/FireOpal/unit-tests/test-cases/objc-references/test.m deleted file mode 100644 index d845227..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-references/test.m +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include - - -@interface Foo : NSObject -- (NSString*) foo; -@end - - -@implementation Foo -- (NSString*) foo -{ - return [NSString stringWithUTF8String:"hello"]; -} -@end - - -@interface Bar : NSData -- (NSArray*) bar; -@end - - -@implementation Bar -- (NSArray*) bar -{ - return [NSArray array]; -} -@end - diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m deleted file mode 100644 index a266128..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/main.m +++ /dev/null @@ -1,7 +0,0 @@ -#include - - -NSString* other() -{ - return [NSString stringWithUTF8String:"hello"]; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m b/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m deleted file mode 100644 index 77fd926..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/other.m +++ /dev/null @@ -1,10 +0,0 @@ - -#include - - -int main() -{ - [NSString stringWithUTF8String:"hello"]; - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx b/ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx deleted file mode 100644 index 3c99e35..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/operator-new/main.cxx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2006-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include // fprintf(), NULL -#include // exit(), EXIT_SUCCESS - -#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() - -#include -#include - - -// -// This test case verifies overriding operator new sets the MH_WEAK_DEFINES bit -// - -#if OP_NEW -void* operator new(size_t s) throw (std::bad_alloc) -{ - return malloc(s);; -} -#endif - -int main() -{ - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile deleted file mode 100644 index 23fe568..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Verify that -order_file can be used to order symbols with anonymous name spaces -# - -run: all - -all: - ${CXX} ${CXXFLAGS} main.cxx -DANCHOR=1 -o main -Wl,-order_file -Wl,main.order - ${FAIL_IF_BAD_MACHO} main - nm -n -g -j main | grep "_GLOBAL__N" > main.actual - ${PASS_IFF} diff main.actual main.expected - - -clean: - rm -rf main main.actual diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx deleted file mode 100644 index b0412f9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.cxx +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - - -#if ANCHOR - int anchor = 4; -#endif - -namespace { - struct myanonstruct { int a; }; -} - -// function defined in anonymous namespace -namespace { - void foo() { } -} - -// function that has an anonymous namespace parameter -void bar(myanonstruct* x) { } - - -// function in anonymous namespace that has an anonymous namespace parameter -namespace { - void baz(myanonstruct* x) { } -} - -// nested namespace -namespace wow { - namespace { - void inner() { } - } -} - - - - -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected deleted file mode 100644 index 75e104f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.expected +++ /dev/null @@ -1,4 +0,0 @@ -__Z3barPN17_GLOBAL__N_anchor12myanonstructE -__ZN3wow17_GLOBAL__N_anchor5innerEv -__ZN17_GLOBAL__N_anchor3bazEPNS_12myanonstructE -__ZN17_GLOBAL__N_anchor3fooEv diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order b/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order deleted file mode 100644 index 36dd786..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file-ans/main.order +++ /dev/null @@ -1,4 +0,0 @@ -__Z3barPN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C12myanonstructE -__ZN3wow95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C5innerEv -__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3bazEPNS_12myanonstructE -__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3fooEv diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/Makefile b/ld64/FireOpal/unit-tests/test-cases/order_file/Makefile deleted file mode 100644 index 21ae0ea..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check -order_file. -# The main1 test verifies that C functions can be re-ordered -# The main2 test verifies that a block of assembly is not moves en mas -# The main1 test verifies that an order file with spaces and comments works -# - -run: all - -all: - as -arch ${ARCH} -L extra.s -o extra.o - ${CC} ${CCFLAGS} main.c extra.o -o main1 -Wl,-order_file -Wl,main1.order - ${FAIL_IF_BAD_MACHO} main1 - nm -n -g -j main1 | grep "_main" > main1.nm - ${PASS_IFF} diff main1.nm main1.expected - - ${CC} ${CCFLAGS} main.c extra.o -o main2 -Wl,-order_file -Wl,main2.order - ${FAIL_IF_BAD_MACHO} main2 - nm -n -j main2 | egrep '^_[a-z]+[0-9]$$' > main2.nm - ${PASS_IFF} diff main2.nm main2.expected - - ${CC} -arch ${ARCH} -c main.c -o main.o - ${CC} ${CCFLAGS} main.o extra.o -o main3 -Wl,-order_file -Wl,main3.order - ${FAIL_IF_BAD_MACHO} main3 - nm -n -g -j main3 | grep "_main" > main3.nm - ${PASS_IFF} diff main3.nm main3.expected - - - - -clean: - rm -rf main1 *.nm main2 *.o warnings.log main3 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/extra.s b/ld64/FireOpal/unit-tests/test-cases/order_file/extra.s deleted file mode 100644 index 90166ce..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/extra.s +++ /dev/null @@ -1,24 +0,0 @@ - - - .text - - .globl _foo1 -_foo1: nop - - .globl _aaa2 -_aaa2: -_bbb2: -_ccc2: - nop - - .globl _bbb3 -_aaa3: -_bbb3: -_ccc3: - nop - - -_aaa4: - nop - - \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main.c b/ld64/FireOpal/unit-tests/test-cases/order_file/main.c deleted file mode 100644 index 5643b45..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - return 0; -} - -void main2() {} -void main3() {} -void main4() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected b/ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected deleted file mode 100644 index 04d128b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main1.expected +++ /dev/null @@ -1,4 +0,0 @@ -_main4 -_main3 -_main -_main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main1.order b/ld64/FireOpal/unit-tests/test-cases/order_file/main1.order deleted file mode 100644 index 06b34d5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main1.order +++ /dev/null @@ -1,4 +0,0 @@ -_main4 -_main3 - - diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected b/ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected deleted file mode 100644 index 8aca65c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main2.expected +++ /dev/null @@ -1,11 +0,0 @@ -_main3 -_foo1 -_aaa2 -_bbb2 -_ccc2 -_aaa3 -_bbb3 -_ccc3 -_aaa4 -_main4 -_main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main2.order b/ld64/FireOpal/unit-tests/test-cases/order_file/main2.order deleted file mode 100644 index 87f89e6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main2.order +++ /dev/null @@ -1,6 +0,0 @@ -_main3 -_aaa3 -_main4 - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected b/ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected deleted file mode 100644 index 04d128b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main3.expected +++ /dev/null @@ -1,4 +0,0 @@ -_main4 -_main3 -_main -_main2 diff --git a/ld64/FireOpal/unit-tests/test-cases/order_file/main3.order b/ld64/FireOpal/unit-tests/test-cases/order_file/main3.order deleted file mode 100644 index d135527..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/order_file/main3.order +++ /dev/null @@ -1,8 +0,0 @@ - -# spaces before and after main4 -main.o: _main4 -# -main.o: _main3# trailing comment -# - - diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c b/ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c deleted file mode 100644 index 251979e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/prebound-main/main.c +++ /dev/null @@ -1,3 +0,0 @@ - -int main() { return 0; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile deleted file mode 100644 index 07bce59..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - -# -# The point of this test is to build a prebound split-seg library -# - -run: all - -all: - ${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 diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table deleted file mode 100644 index b611ca8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/address_table +++ /dev/null @@ -1,4 +0,0 @@ -# comment -0x91000000 0xA1000000 /foo/bar/libbar.dylib -# - diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c b/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c deleted file mode 100644 index 46b7269..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/prebound-split-seg/bar.c +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -int x = 3; -int* xp = &x; - - -int bar() -{ - return *xp; -} - -void* pbar = &bar; - diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c deleted file mode 100644 index 601dc69..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/bar.c +++ /dev/null @@ -1,3 +0,0 @@ - -int __attribute__((visibility("hidden"))) foo = 0; - diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt deleted file mode 100644 index e6d11c0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to check that a non-lazy-pointer in foo.o to a private-extern symbol in bar.o will properly survive ld -r diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c deleted file mode 100644 index 6816d0b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/foo.c +++ /dev/null @@ -1,7 +0,0 @@ - - -extern int foo; - -int getfoo() { return foo; } - - diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c b/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c deleted file mode 100644 index 20dccc4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/hello.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int getfoo(); - -int main() -{ - return getfoo(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile deleted file mode 100644 index 64ec112..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/Makefile +++ /dev/null @@ -1,167 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test all the different ways that re-exports can be specified and implemented -# - - -run: all - -all: - -# -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 - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -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} - - -# -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 - 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 - ${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 - ${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 - 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 - ${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 - ${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 - ${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 - otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY} - otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN} - -# -reexport_library 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_library,libbar.dylib -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-l 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 -Wl,-reexport-lbar -L. -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} - -# -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 - 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. -Wl,-reexport_framework,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} - -# -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" -mmacosx-version-min=10.5 - ${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 - ${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 -mmacosx-version-min=10.4 - ${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 - ${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} - - -# -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 - ${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 - ${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 - - -clean: - - rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c deleted file mode 100644 index 9c18401..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/bar.c +++ /dev/null @@ -1,5 +0,0 @@ - -int bar (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c deleted file mode 100644 index af6a9f8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/baz.c +++ /dev/null @@ -1,5 +0,0 @@ - -int baz (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c deleted file mode 100644 index d0cdf47..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-cases/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile deleted file mode 100644 index 82228ee..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that the MH_NO_REEXPORTED_DYLIBS bit is set in dylibs with no re-exports -# - -run: all - -all: -# build base library - ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - -# build library the re-exports base library - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -sub_library libbar -# test that foo does not have MH_NO_REEXPORTED_DYLIBS bit - ${FAIL_IF_BAD_MACHO} libfoo.dylib - -# build libray that links with base but does not re-export it - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - otool -hv libfoo2.dylib | grep NO_REEXPORTED_DYLIBS | ${PASS_IFF_STDIN} - -clean: - rm -rf *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c deleted file mode 100644 index 34e5666..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/bar.c +++ /dev/null @@ -1,5 +0,0 @@ - -int bar(void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c deleted file mode 100644 index 714540a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-flag/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo(void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c deleted file mode 100644 index 9c18401..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/bar.c +++ /dev/null @@ -1,5 +0,0 @@ - -int bar (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c deleted file mode 100644 index d0cdf47..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile deleted file mode 100644 index 2560a86..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that @loader_path and @executable_path can be resolved finding indirect dylibs -# - - -run: all - -all: - mkdir -p hide - ${CC} ${CCFLAGS} -dynamiclib foo.c -install_name '@loader_path/libfoo.dylib' -o hide/libfoo.dylib - ${FAIL_IF_BAD_MACHO} hide/libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib -install_name '@executable_path/hide/libbar.dylib' - ${FAIL_IF_BAD_MACHO} hide/libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide - ${FAIL_IF_BAD_MACHO} hide/libwrap.dylib - ${CC} ${CCFLAGS} main.c -o main hide/libwrap.dylib - ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd`/main - ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd` - ${PASS_IFF} /usr/bin/true - - -clean: - - rm -rf hide libbar.dylib libfoo.dylib libwrap.dylib main libmain.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c deleted file mode 100644 index 9c18401..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/bar.c +++ /dev/null @@ -1,5 +0,0 @@ - -int bar (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c deleted file mode 100644 index d0cdf47..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo (void) -{ - return 1; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c deleted file mode 100644 index 367c6cb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/main.c +++ /dev/null @@ -1,11 +0,0 @@ -extern int foo(); -extern int bar(); -extern int wrap(); - -int main() -{ - foo(); - bar(); - wrap(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c b/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c deleted file mode 100644 index d3cdd85..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-relative-paths/wrap.c +++ /dev/null @@ -1,2 +0,0 @@ -int wrap() { return 0; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile deleted file mode 100644 index 02ed1df..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that ld can linke a dylib built with -mdynamic-no-pic -# - - -SHELL = bash # use bash shell so we can redirect just stderr - -NO_PIC = -STATIC = - -ifeq (${ARCH},i386) - NO_PIC = -mdynamic-no-pic - STATIC = -static -else - ifeq (${ARCH},ppc) - NO_PIC = -mdynamic-no-pic - STATIC = -mdynamic-no-pic - endif -endif - - - -all: - # build libfoo.dylib as regular dylib - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - # build libtest.dylib using -mdynamic-no-pic and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${NO_PIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w - # build libtest.dylib using -static and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${STATIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w - # build main using -static and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${STATIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -o foo -read_only_relocs suppress -Wl,-w - ${PASS_IFF_GOOD_MACHO} foo - -clean: - rm -rf test.o libfoo.dylib libtest.dylib foo diff --git a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c deleted file mode 100644 index bef1580..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/foo.c +++ /dev/null @@ -1,6 +0,0 @@ - -int b=0; - -void func() {} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c b/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c deleted file mode 100644 index f3484e2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/read-only-relocs/test.c +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int a=0; -extern int b; -extern void func(); - -int main() -{ - func(); - return a+b; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile deleted file mode 100644 index ba262aa..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to see that a dylib -# run through the rebase tool is the same as if -# the dylib was originally built at that address -# - -run: all - -all: - ${CC} ${CCFLAGS} -c foo.c -o foo.${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o - - ${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o - ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o - - ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation - ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib - - ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} libfoo-alt.${ARCH}.dylib - - rebase -arch ${ARCH} -low_address 0x12340000 libfoo.${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib - - ${PASS_IFF} diff libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib - -clean: - rm *.o *.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m deleted file mode 100644 index ff4f2ac..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/bar.m +++ /dev/null @@ -1,13 +0,0 @@ -#include - -@interface Bar : NSObject - --(void) blah; - -@end - -@implementation Bar - --(void) blah {} - -@end \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt deleted file mode 100644 index 013eb45..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to see that a dylib run through the rebase tool is the same as if the dylib was originally built at that address diff --git a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c b/ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c deleted file mode 100644 index 15d4ae6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/rebase-basic/foo.c +++ /dev/null @@ -1,14 +0,0 @@ - -int foo() { return 10; } - -void* foop = &foo; - -int glob = 5; - -int* globp = &glob; - - -int big[3000]; - - - \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile deleted file mode 100644 index c231ac4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${ASMFLAGS} relocs-asm.s -c -o relocs-asm.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content relocs-asm.${ARCH}.o > relocs-asm.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs relocs-asm.${ARCH}.o -o relocs-asm-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content relocs-asm-r.${ARCH}.o > relocs-asm-r.${ARCH}.o.dump - - ${PASS_IFF} diff relocs-asm.${ARCH}.o.dump relocs-asm-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt deleted file mode 100644 index 4e819e1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used -dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. -No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s deleted file mode 100644 index 06e10e8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#if __arm__ - .text - .align 2 - - .globl _test_loads -_test_loads: - @ PIC load of a - ldr r0, L6 -L0: - ldr r0, [pc, r0] - - @ PIC load of c - ldr r0, L6+4 -L1: - ldr r0, [pc, r0] - - @ sorta-absolute load of a - ldr r0, L6+8 - ldr r0, [r0, #0] - - @ sorta-absolute load of c - ldr r0, L6+12 - ldr r0, [r0, #0] - - @ sorta-absolute load of external - ldr r0, L6+16 - ldr r0, [r0, #0] - - @ PIC load of a + addend ?? - bx lr - -L6: - .long _a-(L0+8) - .long _c-(L1+8) - .long _a - .long _c - .long _ax - -_test_calls: - @ call internal - bl _test_branches - - @ call internal + addend - bl _test_branches+0x19000 - - @ call external - bl _external - - @ call external + addend - bl _external+0x19000 - - -_test_branches: - @ call internal - bne _test_calls - - @ call internal + addend - bne _test_calls+16 - - @ call external - bne _external - - @ call external + addend - bne _external+16 -#endif - -#if __ppc__ || __ppc64__ - - .text - .align 2 - - .globl _test_loads -_test_loads: - stmw r30,-8(r1) - stwu r1,-48(r1) -Lpicbase: - - ; PIC load of a - addis r2,r10,ha16(_a-Lpicbase) - lwz r2,lo16(_a-Lpicbase)(r2) - - ; PIC load of c - addis r2,r10,ha16(_c-Lpicbase) - lwz r2,lo16(_c-Lpicbase)(r2) - - ; absolute load of a - lis r2,ha16(_a) - lwz r2,lo16(_a)(r2) - - ; absolute load of c - lis r2,ha16(_c) - lwz r2,lo16(_c)(r2) - - ; absolute load of external - lis r2,ha16(_ax) - lwz r2,lo16(_ax)(r2) - - ; absolute lea of external - lis r2,hi16(_ax) - ori r2,r2,lo16(_ax) - - - ; PIC load of a + addend - addis r2,r10,ha16(_a+0x19000-Lpicbase) - lwz r2,lo16(_a+0x19000-Lpicbase)(r2) - - ; absolute load of a + addend - lis r2,ha16(_a+0x19000) - lwz r2,lo16(_a+0x19000)(r2) - - ; lea of a + addend - lis r2,ha16(_a+0x19000) - addi r2,r2,lo16(_a+0x19000) - - ; alt lea of a + addend - lis r2,hi16(_a+0x19000) - ori r2,r2,lo16(_a+0x19000) - - ; absolute load of external + addend - lis r2,ha16(_ax+0x19000) - lwz r2,lo16(_ax+0x19000)(r2) - - ; absolute lea of external + addend - lis r2,hi16(_ax+0x19000) - ori r2,r2,lo16(_ax+0x19000) - - - ; PIC load of a + addend - addis r2,r10,ha16(_a+0x09000-Lpicbase) - lwz r2,lo16(_a+0x09000-Lpicbase)(r2) - - ; absolute load of a + addend - lis r2,ha16(_a+0x09000) - lwz r2,lo16(_a+0x09000)(r2) - - ; lea of a + addend - lis r2,ha16(_a+0x09000) - addi r2,r2,lo16(_a+0x09000) - - ; alt lea of a + addend - lis r2,hi16(_a+0x09000) - ori r2,r2,lo16(_a+0x09000) - - ; absolute load of external + addend - lis r2,ha16(_ax+0x09000) - lwz r2,lo16(_ax+0x09000)(r2) - - ; absolute lea of external + addend - lis r2,hi16(_ax+0x09000) - ori r2,r2,lo16(_ax+0x09000) - - blr - - -_test_calls: - ; call internal - bl _test_branches - - ; call internal + addend - bl _test_branches+0x19000 - - ; call external - bl _external - - ; call external + addend - bl _external+0x19000 - - -_test_branches: - ; call internal - bne _test_calls - - ; call internal + addend - bne _test_calls+16 - - ; call external - bne _external - - ; call external + addend - bne _external+16 -#endif - - - -#if __i386__ - .text - .align 2 - - .globl _test_loads -_test_loads: - pushl %ebp -Lpicbase: - - # PIC load of a - movl _a-Lpicbase(%ebx), %eax - - # absolute load of a - movl _a, %eax - - # absolute load of external - movl _ax, %eax - - # absolute lea of external - leal _ax, %eax - - - # PIC load of a + addend - movl _a-Lpicbase+0x19000(%ebx), %eax - - # absolute load of a + addend - movl _a+0x19000(%ebx), %eax - - # absolute load of external + addend - movl _ax+0x19000(%ebx), %eax - - # absolute lea of external + addend - leal _ax+0x1900, %eax - - ret - - -_test_calls: - # call internal - call _test_branches - - # call internal + addend - call _test_branches+0x19000 - - # call external - call _external - - # call external + addend - call _external+0x19000 - - -_test_branches: - # call internal - jne _test_calls - - # call internal + addend - jne _test_calls+16 - - # call external - jne _external - - # call external + addend - jne _external+16 - -_pointer_diffs: - nop - call _get_ret_eax -1: movl _foo-1b(%eax),%esi - movl _foo+10-1b(%eax),%esi - movl _test_branches-1b(%eax),%esi - movl _test_branches+3-1b(%eax),%esi - -_word_relocs: - callw _pointer_diffs - -_byte_relocs: - mov $100, %ecx -c_1: - loop c_1 - mov $100, %ecx -c_2: - sub $(1), %ecx - jcxz c_2 - -#endif - - - -#if __x86_64__ - .text - .align 2 - - .globl _test_loads -_test_loads: - - # PIC load of a - movl _a(%rip), %eax - - # PIC load of a + addend - movl _a+0x1234(%rip), %eax - - # PIC lea - leaq _a(%rip), %rax - - # PIC lea through GOT - movq _a@GOTPCREL(%rip), %rax - - # PIC access of GOT - pushq _a@GOTPCREL(%rip) - - # PIC lea external through GOT - movq _ax@GOTPCREL(%rip), %rax - - # PIC external access of GOT - pushq _ax@GOTPCREL(%rip) - - # 1-byte store - movb $0x12, _a(%rip) - movb $0x12, _a+2(%rip) - movb $0x12, L0(%rip) - - # 4-byte store - movl $0x12345678, _a(%rip) - movl $0x12345678, _a+4(%rip) - movl $0x12345678, L0(%rip) - - # test local labels - lea L1(%rip), %rax - movl L0(%rip), %eax - - ret - - -_test_calls: - # call internal - call _test_branches - - # call internal + addend - call _test_branches+0x19000 - - # call external - call _external - - # call external + addend - call _external+0x19000 - - -_test_branches: - # call internal - jne _test_calls - - # call internal + addend - jne _test_calls+16 - - # call external - jne _external - - # call external + addend - jne _external+16 - -_byte_relocs: - mov $100, %ecx -c_1: - loop _byte_relocs - nop - -#endif - - - - # test that pointer-diff relocs are preserved - .text -_test_diffs: - .align 2 -Llocal2: - .long 0 - .long Llocal2-_test_branches - .long . - _test_branches - .long . - _test_branches + 8 - .long _test_branches - . - .long _test_branches - . + 8 - .long _test_branches - . - 8 -#if __ppc64__ - .quad Llocal2-_test_branches -#endif - -_foo: nop - - .align 2 -_distance_from_foo: - .long 0 - .long . - _foo - .long . - 8 - _foo - - -_distance_to_foo: - .long _foo - . - .long _foo - . + 4 - - -_distance_to_here: - .long _foo - _distance_to_here - .long _foo - _distance_to_here - 4 - .long _foo - _distance_to_here - 12 - .long 0 - - -#if __x86_64__ - .data -L0: .quad _test_branches -_prev: - .quad _test_branches+4 -L1: .quad _test_branches - _test_diffs - .quad _test_branches - _test_diffs + 4 - .long _test_branches - _test_diffs -# .long LCL0-. ### assembler bug: should SUB/UNSIGNED with content= LCL0-24, or single pc-rel SIGNED reloc with content = LCL0-.+4 - .quad L1 - .quad L0 - .quad _test_branches - . - .quad _test_branches - L1 - .quad L1 - _prev - -# the following generates: _foo cannot be undefined in a subtraction expression -# but it should be ok (it will be a linker error if _foo and _bar are not in same linkage unit) -# .quad _foo - _bar ### assembler bug - - .section __DATA,__data2 -LCL0: .long 2 - - -#endif - - - .data -_a: - .long 0 - -_b: -#if __ppc__ || __i386__ || __arm__ - .long _test_calls - .long _test_calls+16 - .long _external - .long _external+16 -#elif __ppc64__ || __x86_64__ - .quad _test_calls - .quad _test_calls+16 - .quad _external - .quad _external+16 -#endif - - # test that reloc sizes are the same -Llocal3: - .long 0 - -Llocal4: - .long 0 - - .long Llocal4-Llocal3 - -Lfiller: - .space 0x9000 -_c: - .long 0 - diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile deleted file mode 100644 index 7428127..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-c/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -## -# Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -ifeq (${ARCH},x86_64) - ADDR_SHIFT = 0x1FF000000 -else - ADDR_SHIFT = 0xF0000000 -endif - - -run: all - -all: - ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump - ${FAIL_IF_ERROR} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -seg1addr ${ADDR_SHIFT} -o test2-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test2-r.${ARCH}.o > test2-r.${ARCH}.o.dump - ${PASS_IFF} diff test.${ARCH}.o.dump test2-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c deleted file mode 100644 index b877760..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-c/test.c +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -static int foo; - -int __attribute__((visibility("hidden"))) foofoo; - -static int uninit_static; -static int init_static = 1; - int __attribute__((visibility("hidden"))) uninit_hidden; - int __attribute__((visibility("hidden"))) init_hidden = 1; - int uninit_global; - int init_global = 1; -extern int extern_global; -extern int __attribute__((visibility("hidden"))) extern_hidden; - -static int uninit_static_array[4]; -static int init_static_array[4] = {1,2,3,4}; - int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; - int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; - int uninit_global_array[4]; - int init_global_array[4] = {1,2,3,4}; -extern int extern_global_array[4]; - -int test1() { return uninit_static; } -int test2() { return init_static; } -int test3() { return uninit_hidden; } -int test4() { return init_hidden; } -int test5() { return uninit_global; } -int test6() { return init_global; } -int test7() { return extern_global; } -int test8() { return extern_hidden; } - -int test_array1() { return uninit_static_array[2]; } -int test_array2() { return init_static_array[2]; } -int test_array3() { return uninit_hidden_array[2]; } -int test_array4() { return init_hidden_array[2]; } -int test_array5() { return uninit_global_array[2]; } -int test_array6() { return init_global_array[2]; } -int test_array7() { return extern_global_array[2]; } - -static int foo2; -int test9() { return foo2; } - - -int* p_init_global = &init_global; -void* p_test1 = (void*)&test1; -unsigned char pad = 2; -unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... - -int func() __attribute__((visibility("hidden"))); -int func() { return foo; } - -int func2() { return func() + 1; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile deleted file mode 100644 index 767b210..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -## -# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# -# Currently for ppc64 the .o's alternate! in content -# - - -run: all - -all: - ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - #grep "plus" test.${ARCH}.o.dump | ${FAIL_IF_STDIN} - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump - #grep "plus" test-r.${ARCH}.o.dump | ${FAIL_IF_STDIN} - - ${LD} -arch ${ARCH} -r -keep_private_externs test-r.${ARCH}.o -o test-r-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r-r.${ARCH}.o > test-r-r.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test-r-r.${ARCH}.o -o test-r-r-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r-r-r.${ARCH}.o > test-r-r-r.${ARCH}.o.dump - - ${PASS_IFF} diff -c -w test.${ARCH}.o.dump test-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt deleted file mode 100644 index 2499674..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/comment.txt +++ /dev/null @@ -1,5 +0,0 @@ -The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used -dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. -No differences means this test passes - -Currently for ppc64 the .o's alternate! in content diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c deleted file mode 100644 index b877760..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-c2/test.c +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -static int foo; - -int __attribute__((visibility("hidden"))) foofoo; - -static int uninit_static; -static int init_static = 1; - int __attribute__((visibility("hidden"))) uninit_hidden; - int __attribute__((visibility("hidden"))) init_hidden = 1; - int uninit_global; - int init_global = 1; -extern int extern_global; -extern int __attribute__((visibility("hidden"))) extern_hidden; - -static int uninit_static_array[4]; -static int init_static_array[4] = {1,2,3,4}; - int __attribute__((visibility("hidden"))) uninit_hidden_array[4]; - int __attribute__((visibility("hidden"))) init_hidden_array[4] = {1,2,3,4}; - int uninit_global_array[4]; - int init_global_array[4] = {1,2,3,4}; -extern int extern_global_array[4]; - -int test1() { return uninit_static; } -int test2() { return init_static; } -int test3() { return uninit_hidden; } -int test4() { return init_hidden; } -int test5() { return uninit_global; } -int test6() { return init_global; } -int test7() { return extern_global; } -int test8() { return extern_hidden; } - -int test_array1() { return uninit_static_array[2]; } -int test_array2() { return init_static_array[2]; } -int test_array3() { return uninit_hidden_array[2]; } -int test_array4() { return init_hidden_array[2]; } -int test_array5() { return uninit_global_array[2]; } -int test_array6() { return init_global_array[2]; } -int test_array7() { return extern_global_array[2]; } - -static int foo2; -int test9() { return foo2; } - - -int* p_init_global = &init_global; -void* p_test1 = (void*)&test1; -unsigned char pad = 2; -unsigned char pad2 = 3; // this padding throws off alignment on compiler generated anonymous non-lazy pointers... - -int func() __attribute__((visibility("hidden"))); -int func() { return foo; } - -int func2() { return func() + 1; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile deleted file mode 100644 index a9ca5ef..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump - - ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c deleted file mode 100644 index 2d199d0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals/test.c +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -const char* foo = "foo"; -const char* const bar = "bar"; - -const char charArray1[] = "charArray1"; -static const char charArray2[] = "charArray2"; - - -const char* getString() { return "string"; } -const char* getString2() { return charArray2; } -const char* getString3() { return charArray1; } -const char* getString4() { return foo; } - - -float f1 = 3.0; -double d1 = 3.0; -long double ld1 = 3.0; - - - -float getSingle() { return 1.0; } -double getDouble() { return 2.0; } -long double getLongDouble() { return 3.0; } - - -// rdar://problem/4732996 -const char* stringFutz(int x) { - return "hello" + 0x1000 + x; -} - -const char* usesAddend = "teststr" + 0x2000; diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile deleted file mode 100644 index 23e4a82..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# -ifneq (${ARCH},x86_64) - PIC=-mdynamic-no-pic -endif - -run: all - -all: - ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump - - ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c deleted file mode 100644 index 2d199d0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals2/test.c +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -const char* foo = "foo"; -const char* const bar = "bar"; - -const char charArray1[] = "charArray1"; -static const char charArray2[] = "charArray2"; - - -const char* getString() { return "string"; } -const char* getString2() { return charArray2; } -const char* getString3() { return charArray1; } -const char* getString4() { return foo; } - - -float f1 = 3.0; -double d1 = 3.0; -long double ld1 = 3.0; - - - -float getSingle() { return 1.0; } -double getDouble() { return 2.0; } -long double getLongDouble() { return 3.0; } - - -// rdar://problem/4732996 -const char* stringFutz(int x) { - return "hello" + 0x1000 + x; -} - -const char* usesAddend = "teststr" + 0x2000; diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile deleted file mode 100644 index e0fa4ad..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${CCFLAGS} -Os -mdynamic-no-pic test.c -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump - - ${PASS_IFF} diff -C 6 test.${ARCH}.o.dump test-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt deleted file mode 100644 index 4e819e1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used -dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. -No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c b/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c deleted file mode 100644 index 31e87c2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-literals3/test.c +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -const char* foo = "foo"; -const char* const bar = "bar"; - -const char charArray1[] = "charArray1"; -static const char charArray2[] = "charArray2"; - - -const char* getString() { return "string"; } -const char* getString2() { return charArray2; } -const char* getString3() { return charArray1; } -const char* getString4() { return foo; } - - -float f1 = 3.0; -double d1 = 3.0; -long double ld1 = 3.0; - - - -float getSingle() { return 1.0; } -double getDouble() { return 2.0; } -long double getLongDouble() { return 3.0; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile deleted file mode 100644 index 0f8846d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify a .o file can round-trip -# through ld -r correctly. The ObjectDump utility is used -# dump a "canonical" textual representation of a .o file. -# The before and after .o files are then diff'ed. -# No differences means this test passes -# - -run: all - -all: - ${CC} ${CCFLAGS} test.m -c -o test.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump - - ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump - -clean: - rm -rf *.o *.dump diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt b/ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt deleted file mode 100644 index 4e819e1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -The point of this test is to verify a .o file can round-trip through ld -r correctly. The ObjectDump utility is used -dump a "canonical" textual representation of a .o file. The before and after .o files are then diff'ed. -No differences means this test passes diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order b/ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order deleted file mode 100644 index e05b042..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/segment-order/expected.order +++ /dev/null @@ -1,3 +0,0 @@ -_sym_kkk -_sym_jjj -_sym_lll diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/main.c b/ld64/FireOpal/unit-tests/test-cases/segment-order/main.c deleted file mode 100644 index df77448..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/segment-order/main.c +++ /dev/null @@ -1,4 +0,0 @@ - - -int main() { return 0; } - diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s b/ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s deleted file mode 100644 index d9f5f71..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/segment-order/segJJJ.s +++ /dev/null @@ -1,7 +0,0 @@ - - .section __JJJ,__jjj -_sym_jjj: .space 128 - - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s b/ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s deleted file mode 100644 index 70b1952..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/segment-order/segKKK.s +++ /dev/null @@ -1,7 +0,0 @@ - - .section __KKK,__kkk -_sym_kkk: .space 128 - - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s b/ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s deleted file mode 100644 index 045eea4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/segment-order/segLLL.s +++ /dev/null @@ -1,7 +0,0 @@ - - .section __LLL,__lll -_sym_lll: .space 128 - - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile b/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile deleted file mode 100644 index ee6a0e5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Sanity check that -slow_stubs for i386 leaves no __IMPORT segment -# - -run: all - - - -all: - ${CC} ${CCFLAGS} hello.c -o hello -Wl,-slow_stubs - size -l hello | grep __IMPORT | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib -Wl,-slow_stubs - size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} hello - -clean: - rm hello libhello.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c b/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c deleted file mode 100644 index fe3b0df..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/slow-x86-stubs/hello.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main() -{ - fprintf(stdout, "hello\n"); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s b/ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s deleted file mode 100644 index 0755508..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/special-labels/extra.s +++ /dev/null @@ -1,9 +0,0 @@ - - - .data - -_foo: .long 0 -lother: .long 0 -L123: .long 0 -_bar: .long 0 - diff --git a/ld64/FireOpal/unit-tests/test-cases/special-labels/main.c b/ld64/FireOpal/unit-tests/test-cases/special-labels/main.c deleted file mode 100644 index 5c73586..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/special-labels/main.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile deleted file mode 100644 index 6e11c59..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# removes the stabs associated with a copy of a coalesced -# function that was removed. -# Running nm through stabs-filter.pl produces connonical stabs -# that can be diffed against a checked in know good set of stabs -# - -run: all - -all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH} - nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count - echo " 1" > one - ${PASS_IFF} diff stabs-hello-foo-count one - -hello.o : hello.cxx - ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ - ${FAIL_IF_BAD_OBJ} $@ - -other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ - ${FAIL_IF_BAD_OBJ} $@ - -clean: - rm -rf stabs-hello-* *.o *.stabs stabs-hello-foo-count one diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt deleted file mode 100644 index f22b9a1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -The point of this test is a sanity check that ld removes the stabs associated with a copy of a coalesced -function that was removed. Running nm through stabs-filter.pl produces connonical stabs -that can be diffed against a checked in know good set of stabs diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h deleted file mode 100644 index 378308f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/header.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -inline int foo(int x) -{ - return x + 10; -} - -extern int bar(int x); diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx deleted file mode 100644 index 33bf273..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/hello.cxx +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -#include "header.h" - - -int main() -{ - foo(bar(3)); - fprintf(stdout, "hello\n"); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx b/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx deleted file mode 100644 index ee97d7d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-coalesce/other.cxx +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include "header.h" - -int uninit; -int init = 1; -static int suninit; -static int sinit=0; - -int bar(int x) -{ - static int bar_uninit; - static int bar_init=3; - bar_uninit = x; - return 20 + suninit + sinit + - bar_init + bar_uninit + foo(x); -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile b/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile deleted file mode 100644 index 5318933..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Test that file paths in a stab reference ends with a / -# if there is no terminating /, gdb does not recognize this as a file path -# The provided files coalesced1a.o coalesced1b.o are ppc64 linked -# rdar://problem/4565088 - -run: all - -all: - $(CXX) -gstabs+ main.c -o outfile - ${FAIL_IF_BAD_MACHO} outfile - nm -ap outfile | ${PASS_IFF} grep '.*\.*test-cases.*/$$' - -clean: - rm outfile* diff --git a/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c b/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c deleted file mode 100644 index 54dc4c5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stabs-directory-slash/main.c +++ /dev/null @@ -1,3 +0,0 @@ -main() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest deleted file mode 100644 index a9e452f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest +++ /dev/null @@ -1,77 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Test the ld commands -stack_addr, -stack_size -# Test using -stack_addr only - - -ifeq (,${findstring 64,$(ARCH)}) - STACK_ADDR = 0xC0000000 - STACK_SIZE = 0x04000000 - STACK_TOP = 0xbc000000 -else -#ifeq (${ARCH},x86_64) - STACK_ADDR = 0x0007fff5fc000000 - STACK_TOP = 0x00007fff57000000 - STACK_SIZE = 0x0000000005000000 -#else - #STACK_ADDR = 0x0007ffff00000000 - #STACK_TOP = 0x0007fffefb000000 - #STACK_SIZE = 0x0000000005000000 -#endif -endif - - -run: all - -all: -# info seems to not work, use warning: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c main.c -o main-${ARCH}.o - - - ${FAIL_IF_ERROR} ${LD} -arch ${ARCH} \ - -stack_addr ${STACK_ADDR} \ - -lcrt1.o -lSystem \ - main-${ARCH}.o -o main \ - 2>lderr.out - -# Can check warning if desired. -#ifeq (,${findstring 64,$(ARCH)}) -# grep "warning no -stack_size specified using the default size" lderr.out | ${FAIL_IF_EMPTY} -#else -# grep "failed: -stack_addr must be used with -stack_size" lderr.out | ${FAIL_IF_EMPTY} -#endif - - -# Check for __UNIXSTACK section in object, check that it has the correct value - ${FAIL_IF_ERROR} ${OTOOL} -l main>ldcmds.out - (echo '1,/^[ ]*segname __UNIXSTACK$$/-d'; echo '/^[ ]*segname /,$$d'; echo w; echo q) | ed ldcmds.out >/dev/null - grep __UNIXSTACK ldcmds.out | ${FAIL_IF_EMPTY} - grep " vmsize[ ]*${STACK_SIZE}" ldcmds.out | ${FAIL_IF_EMPTY} - grep " vmaddr[ ]*${STACK_TOP}" ldcmds.out | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf *.o *.err *.out main diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt deleted file mode 100644 index da74f89..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/comment.txt +++ /dev/null @@ -1,11 +0,0 @@ -Test the ld commands -stack_addr, -stack_size (3939852 and 4729162) - - --stack_addr value -Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. - - --stack_size value -Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . - - diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c b/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c deleted file mode 100644 index 5c73586..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_addr_no_size/main.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile deleted file mode 100644 index 670f014..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Test the ld option -stack_addr and -stack_size used together - -ifeq ($(ARCH),armv6) - STACK_ADDR = 0x2C000000 - STACK_SIZE = 0x05000000 - STACK_TOP = 0x27000000 -else -ifeq (,${findstring 64,$(ARCH)}) - STACK_ADDR = 0xCC000000 - STACK_SIZE = 0x05000000 - STACK_TOP = 0xc7000000 -else - STACK_ADDR = 0x110000000 - STACK_TOP = 0x000000010b000000 - STACK_SIZE = 0x0000000005000000 -endif -endif - -run: all - - - -all: - ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR} - # Check for __UNIXSTACK section in object, check that it has the correct value - 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 - -clean: - rm -rf main main.otool diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt deleted file mode 100644 index da74f89..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/comment.txt +++ /dev/null @@ -1,11 +0,0 @@ -Test the ld commands -stack_addr, -stack_size (3939852 and 4729162) - - --stack_addr value -Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. - - --stack_size value -Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . - - diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c b/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c deleted file mode 100644 index 5c73586..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_addr_size/main.c +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile deleted file mode 100644 index 6134c7e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -## -# Copyright (c) 2005-2008 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Test the ld option -stack_size adds a custom stack segment - -ifeq ($(ARCH),armv6) - STACK_ADDR = 0x30000000 - STACK_SIZE = 0x05000000 - STACK_TOP = 0x2b000000 -else -ifeq (,${findstring 64,$(ARCH)}) - STACK_ADDR = 0xC0000000 - STACK_SIZE = 0x05000000 - STACK_TOP = 0xbb000000 -else - STACK_ADDR = 0x0007fff5fc000000 - STACK_TOP = 0x00007fff57000000 - STACK_SIZE = 0x0000000005000000 -endif -endif - - -run: all - -all: - ${CC} ${CCFLAGS} main.c -o main -Wl,-stack_size,${STACK_SIZE} - # Check for __UNIXSTACK section in object, check that it has the correct value - 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 - -clean: - rm -rf main main.otool diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt deleted file mode 100644 index 5933975..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/comment.txt +++ /dev/null @@ -1,11 +0,0 @@ -Test the ld commands -stack_addr, -stack_size - - --stack_addr value -Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_size is specified and -stack_addr is not, a default stack address specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK. Note that the initial stack address will be either at the high address of the segment or the low address of the segment depending on which direction the stack grows for the architecture being linked. - - --stack_size value -Specifies the size of the stack segment value, where value is a hexadecimal number rounded to the segment alignment. The default segment alignment is the target pagesize (currently, 1000 hexadecimal for the PowerPC and for i386). If -stack_addr is specified and -stack_size is not, a default stack size specific for the architecture being linked will be used and its value printed as a warning message. This creates a segment named __UNIXSTACK . - - diff --git a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c b/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c deleted file mode 100644 index 4aaef3a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stack_size_no_addr/main.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -#if __x86_64__ -static char buffer[8000000000]; -#elif __arm__ -static char buffer[100000000]; -#else -static char buffer[2000000000]; -#endif - -int main() -{ - return buffer[0]; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile b/ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile deleted file mode 100644 index 2d71eff..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/static-executable/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that ld can link a static executable -# - -all: - ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker - ${PASS_IFF_GOOD_MACHO} test - -clean: - rm -rf test diff --git a/ld64/FireOpal/unit-tests/test-cases/static-executable/test.c b/ld64/FireOpal/unit-tests/test-cases/static-executable/test.c deleted file mode 100644 index 27fe88d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/static-executable/test.c +++ /dev/null @@ -1,11 +0,0 @@ - -int foo() -{ - return 0; -} - - -int entry() -{ - return foo(); -} diff --git a/ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest deleted file mode 100644 index 323200e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/static-strip/Makefile.newtest +++ /dev/null @@ -1,40 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a static executable (requires non-public archives) -# - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} test.c -static -o test-${ARCH} -L/usr/local/lib/system -lc_static -lm_static -Wl,-new_linker - ${FAIL_IF_BAD_MACHO} test-${ARCH} - ${FAIL_IF_ERROR} strip test-${ARCH} - ${PASS_IFF_GOOD_MACHO} test-${ARCH} - -clean: - rm -rf test-* diff --git a/ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt b/ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt deleted file mode 100644 index bc535a3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/static-strip/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a static executable (requires non-public archives) diff --git a/ld64/FireOpal/unit-tests/test-cases/static-strip/test.c b/ld64/FireOpal/unit-tests/test-cases/static-strip/test.c deleted file mode 100644 index ab472fb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/static-strip/test.c +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -int main() -{ - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile b/ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile deleted file mode 100644 index 778770e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip-test2/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Test strip: symbols referenced by indirect symbol table entries that can'tÊ -# be stripped in: -# __ZN9__gnu_cxx13new_allocatorIiE7destroyEPi -# __ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv -# __ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ -# __ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim -# __ZN9__gnu_cxx13new_allocatorIiED2Ev -# __ZNSt6vectorIiSaIiEEC1ERKS0_ -# __ZNSaIiEC1ERKS_ -# __ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim -# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ -# __ZNSaIiED2Ev -# __ZNSt12_Vector_baseIiSaIiEED2Ev -# __ZNSaIiEC1Ev -# __ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ -# __ZNSt6vectorIiSaIiEED1Ev -# __ZNSaIiED1Ev -# __ZSt8_DestroyIPiSaIiEEvT_S2_T0_ -# __ZN9__gnu_cxx13new_allocatorIiEC2Ev -# __ZNSaIiEC2ERKS_ -# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev - - -run: all - - -all: - $(CXX) main.cxx -arch ${ARCH} -o main - ${FAIL_IF_BAD_MACHO} main - ${FAIL_IF_ERROR} nm -j main >main-no-strip.nm - $(CXX) main.cxx -arch ${ARCH} -o main - ${FAIL_IF_BAD_MACHO} main - - # Make sure there are no symbols in the stripped file that aren't - # in the unstripped - nm -j main | comm -23 - main-no-strip.nm | ${FAIL_IF_STDIN} - - # Now make sure that all the __Z symbols exist - strip_cnt=`nm -j main | comm -12 - main-no-strip.nm | grep -c __Z`; \ - nostrip_cnt=`nm -j main|grep -c __Z`; \ - [ x"$$strip_cnt" = x"$$nostrip_cnt" ] - @echo PASS $$UNIT_TEST_NAME - -clean: - rm -rf *.o main-* main diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt deleted file mode 100644 index a99f78e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip-test2/comment.txt +++ /dev/null @@ -1,21 +0,0 @@ -Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) - -__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi -__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv -__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ -__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim -__ZN9__gnu_cxx13new_allocatorIiED2Ev -__ZNSt6vectorIiSaIiEEC1ERKS0_ -__ZNSaIiEC1ERKS_ -__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim -__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ -__ZNSaIiED2Ev -__ZNSt12_Vector_baseIiSaIiEED2Ev -__ZNSaIiEC1Ev -__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ -__ZNSt6vectorIiSaIiEED1Ev -__ZNSaIiED1Ev -__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ -__ZN9__gnu_cxx13new_allocatorIiEC2Ev -__ZNSaIiEC2ERKS_ -__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx b/ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx deleted file mode 100644 index dd65fef..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip-test2/main.cxx +++ /dev/null @@ -1,6 +0,0 @@ -#include -int main() -{ - std::vector stuff; - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest deleted file mode 100644 index c1ad4c4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip-test3/Makefile.newtest +++ /dev/null @@ -1,71 +0,0 @@ -## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# Test strip: symbols referenced by indirect symbol table entries that can'tÊ -# be stripped in: -# __ZN9__gnu_cxx13new_allocatorIiE7destroyEPi -# __ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv -# __ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ -# __ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim -# __ZN9__gnu_cxx13new_allocatorIiED2Ev -# __ZNSt6vectorIiSaIiEEC1ERKS0_ -# __ZNSaIiEC1ERKS_ -# __ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim -# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ -# __ZNSaIiED2Ev -# __ZNSt12_Vector_baseIiSaIiEED2Ev -# __ZNSaIiEC1Ev -# __ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ -# __ZNSt6vectorIiSaIiEED1Ev -# __ZNSaIiED1Ev -# __ZSt8_DestroyIPiSaIiEEvT_S2_T0_ -# __ZN9__gnu_cxx13new_allocatorIiEC2Ev -# __ZNSaIiEC2ERKS_ -# __ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev - - -run: all - - -all: - ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -o main - ${FAIL_IF_BAD_MACHO} main - ${FAIL_IF_ERROR} nm -j main >main-no-strip.nm - ${FAIL_IF_ERROR} strip main - ${FAIL_IF_BAD_MACHO} main - ${FAIL_IF_ERROR} $(CXX) main.cxx -arch ${ARCH} -s -o main - ${PASS_IFF_GOOD_MACHO} main - - # Make sure there are no symbols in the stripped file that aren't - # in the unstripped - nm -j main | comm -23 - main-no-strip.nm | ${FAIL_IF_STDIN} - - # Now make sure that all the __Z symbols exist - strip_cnt=`nm -j main | comm -12 - main-no-strip.nm | grep -c __Z`; \ - nostrip_cnt=`nm -j main|grep -c __Z`; \ - [ x"$$strip_cnt" = x"$$nostrip_cnt" ] - @echo PASS $$UNIT_TEST_NAME -clean: - rm -rf *.o main main-* *.nm *.out diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt deleted file mode 100644 index a99f78e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip-test3/comment.txt +++ /dev/null @@ -1,21 +0,0 @@ -Test strip: symbols referenced by indirect symbol table entries can't be stripped (4096290) - -__ZN9__gnu_cxx13new_allocatorIiE7destroyEPi -__ZNKSt12_Vector_baseIiSaIiEE13get_allocatorEv -__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_ -__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim -__ZN9__gnu_cxx13new_allocatorIiED2Ev -__ZNSt6vectorIiSaIiEEC1ERKS0_ -__ZNSaIiEC1ERKS_ -__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim -__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_ -__ZNSaIiED2Ev -__ZNSt12_Vector_baseIiSaIiEED2Ev -__ZNSaIiEC1Ev -__ZNSt12_Vector_baseIiSaIiEEC2ERKS0_ -__ZNSt6vectorIiSaIiEED1Ev -__ZNSaIiED1Ev -__ZSt8_DestroyIPiSaIiEEvT_S2_T0_ -__ZN9__gnu_cxx13new_allocatorIiEC2Ev -__ZNSaIiEC2ERKS_ -__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev diff --git a/ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx b/ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx deleted file mode 100644 index dd65fef..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip-test3/main.cxx +++ /dev/null @@ -1,6 +0,0 @@ -#include -int main() -{ - std::vector stuff; - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile b/ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile deleted file mode 100644 index f32267c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip_local/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# This test case checks merges two .o files. One uses -# a lazy and non-lazy pointer to access the second. -# The result then has local symbols stripped. So the -# end result is a .o file with a lazy and non-lazy pointer to -# anonymous stuff. -# -# - - -run: all - -all: - ${CC} ${CCFLAGS} hello.c -c - ${FAIL_IF_BAD_OBJ} hello.o - - ${CC} ${CCFLAGS} foo.c -c - ${FAIL_IF_BAD_OBJ} foo.o - - ${LD} -r hello.o foo.o -o hellofoo.o - ${FAIL_IF_BAD_OBJ} hellofoo.o - - strip -x hellofoo.o -o hellofoostripped.o - ${CC} ${CCFLAGS} hellofoostripped.o -o main - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf *.o main diff --git a/ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c b/ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c deleted file mode 100644 index defa5eb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip_local/foo.c +++ /dev/null @@ -1,8 +0,0 @@ - - -int __attribute__((visibility("hidden"))) data = 3; - -void __attribute__((visibility("hidden"))) func(int x) { } - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c b/ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c deleted file mode 100644 index 2deed42..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/strip_local/hello.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern int data; -extern void func(int); - -int main() -{ - func(data); -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile deleted file mode 100644 index c0647b3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -ifeq "${ARCH}" "i386" - POINTER_SEGMENT = __IMPORT - POINTER_SECTION = __pointers -else - POINTER_SEGMENT = __DATA - POINTER_SECTION = __nl_symbol_ptr -endif - - -# -# Test that using strip -R to selectively strip symbol names -# of of a .o file still works with ld. -# - -run: all - -all: - ${CC} ${CCFLAGS} a.c -c -o a.o - ${CC} ${CCFLAGS} b.c -c -o b.o - ${CC} ${CCFLAGS} c.c -c -o c.o - ${CC} ${CCFLAGS} func.c -c -o func.o - ${LD} -arch ${ARCH} -r a.o b.o c.o -o most.o - strip -x -R strip.list most.o -o most.stripped.o - ${CC} ${CCFLAGS} most.stripped.o func.o -dynamiclib -o dylib1 - ${LD} -arch ${ARCH} -r most.stripped.o func.o -o all.o - ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 - otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers - otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers - ${PASS_IFF} diff dylib1.pointers dylib2.pointers - -clean: - rm -rf *.o dylib1 dylib2 *.pointers diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c deleted file mode 100644 index 141b6d9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/a.c +++ /dev/null @@ -1,7 +0,0 @@ - -int aData = 0; - -void a() -{ - ++aData; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c deleted file mode 100644 index 9608402..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/b.c +++ /dev/null @@ -1,12 +0,0 @@ - -int bData = 0; - -void b() -{ - ++bData; -} - -void bb() -{ - ++bData; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c deleted file mode 100644 index db8276a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/c.c +++ /dev/null @@ -1,11 +0,0 @@ -extern void b(); -extern void bb(); - -extern void func(void*); - - -void c() -{ - func(&b); - func(&bb); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c deleted file mode 100644 index 5724811..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/func.c +++ /dev/null @@ -1 +0,0 @@ -void func(void* x) {} diff --git a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list b/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list deleted file mode 100644 index 77ac6e9..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list +++ /dev/null @@ -1,2 +0,0 @@ -_b -_bb diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c deleted file mode 100644 index 0122e53..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - -void foo() {} -void bar() {} -void baz() {} - diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c b/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c deleted file mode 100644 index 1803c6a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/main.c +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern void foo() __attribute__((weak_import)); -extern void bar() __attribute__((weak_import)); -extern void baz(); - -int main() -{ - if ( &foo != 0 ) { - foo(); - bar(); - } - baz(); - return 0; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile b/ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile deleted file mode 100644 index 9e6ecc0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stub-generation/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that ld generates correct stubs -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -dynamiclib -Wl,-interposable -o libtest.dylib - # only stub should be to _test - otool -Iv libtest.dylib | grep '1 entries' | ${FAIL_IF_EMPTY} - otool -Iv libtest.dylib | grep '_test' | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} libtest.dylib - - -clean: - rm libtest.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c b/ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c deleted file mode 100644 index 4573622..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/stub-generation/test.c +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -const char kMyStr[] = "hello"; - -int test() -{ - return 10; -} - - -const char* getstr() -{ - test(); - return kMyStr; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile deleted file mode 100644 index aacd78d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -## -# Copyright (c) 2008 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that -mdynamic-no-pic jump table in the middle of -# a function does not cause relocations. -# -# SPEC2000/eon built with -mdynamic-no-pic won't run -# - -run: all - -all: - # check jump table in a weak function - ${CC} ${CCFLAGS} main.c switch.s -o main - 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 - 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 - 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 - ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-pie -read_only_relocs suppress - otool -rv main | grep "External relocation" | ${FAIL_IF_STDIN} - otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm -f main diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp deleted file mode 100644 index b9e50b8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/interpose.exp +++ /dev/null @@ -1,2 +0,0 @@ -_foo -_bar diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s b/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s deleted file mode 100644 index 12b559f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/switch.s +++ /dev/null @@ -1,49 +0,0 @@ - - .section __TEXT,__textcoal_nt,coalesced,pure_instructions - - - -/* - Simulate a switch statement in a weak function compiled - to a jump table -*/ - .globl _foo - .weak_definition _foo -_foo: - nop - nop -#if __arm__ || __i386__ - .long L1 - .long L2 - .long L3 -#endif - nop -L1: nop -L2: nop -L3: nop - nop - - -/* - Simulate a switch statement in a regular function compiled - to a jump table -*/ - .text - .globl _bar -_bar: nop - nop - nop - nop -#if __arm__ || __i386__ - .long L5 - .long L6 - .long L7 -#endif - nop -L5: nop -L6: nop -L7: nop - nop - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile deleted file mode 100644 index fc85d7b..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/Makefile +++ /dev/null @@ -1,93 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test magic $ld$ symbols which tell ld to view exported symbols -# differently than dyld sees them. -# -# In this test case aaa and bbb both moved between libfoo and libar -# between 10.4 and 10.5. -# - - -run: all - -all: - # In this test case aaa and bbb both moved between libfoo and libar - # between 10.4 and 10.5. - ${CC} ${CCFLAGS} -dynamiclib bar.c bbb.c anotb.c -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c aaa.c bnota.c -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} main.c -o main-10.4 libfoo.dylib libbar.dylib -mmacosx-version-min=10.4 - nm -m main-10.4 | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} - nm -m main-10.4 | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -o main-10.4a libbar.dylib libfoo.dylib -mmacosx-version-min=10.4 - nm -m main-10.4a | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} - nm -m main-10.4a | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -o main-10.5 libfoo.dylib libbar.dylib -mmacosx-version-min=10.5 - nm -m main-10.5 | grep _aaa | grep libfoo | ${FAIL_IF_EMPTY} - nm -m main-10.5 | grep _bbb | grep libbar | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -o main-10.5a libbar.dylib libfoo.dylib -mmacosx-version-min=10.5 - nm -m main-10.5a | grep _aaa | grep libfoo | ${FAIL_IF_EMPTY} - nm -m main-10.5a | grep _bbb | grep libbar | ${FAIL_IF_EMPTY} - # In this test case aaa and bbb both moved between subframeworks of Foo and Bar - # between 10.4 and 10.5. - mkdir -p Frameworks/Foo.framework/Frameworks/subFoo.framework - ${CC} ${CCFLAGS} -dynamiclib foo.c aaa.c -o Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo \ - -install_name /System/Library/Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo \ - -umbrella Foo - ${CC} ${CCFLAGS} -dynamiclib bnota.c -o Frameworks/Foo.framework/Foo \ - -install_name /System/Library/Frameworks/Frameworks/Foo.framework/Foo \ - Frameworks/Foo.framework/Frameworks/subFoo.framework/subFoo - mkdir -p Frameworks/Bar.framework/Frameworks/subBar.framework - ${CC} ${CCFLAGS} -dynamiclib bar.c bbb.c -o Frameworks/Bar.framework/Frameworks/subBar.framework/subBar \ - -install_name /System/Library/Frameworks/Bar.framework/Frameworks/subBar.framework/subBar \ - -umbrella Bar - ${CC} ${CCFLAGS} -dynamiclib anotb.c -o Frameworks/Bar.framework/Bar \ - -install_name /System/Library/Frameworks/Frameworks/Bar.framework/Bar \ - Frameworks/Bar.framework/Frameworks/subBar.framework/subBar - ${CC} ${CCFLAGS} main.c -o main-10.4 -framework Foo -framework Bar -mmacosx-version-min=10.4 \ - -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks - nm -m main-10.4 | grep _aaa | grep " Bar" | ${FAIL_IF_EMPTY} - nm -m main-10.4 | grep _bbb | grep " Foo" | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -o main-10.4a -framework Bar -framework Foo -mmacosx-version-min=10.4 \ - -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks - nm -m main-10.4a | grep _aaa | grep " Bar" | ${FAIL_IF_EMPTY} - nm -m main-10.4a | grep _bbb | grep " Foo" | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -o main-10.5 -framework Foo -framework Bar -mmacosx-version-min=10.5 \ - -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks - nm -m main-10.5 | grep _aaa | grep " Foo" | ${FAIL_IF_EMPTY} - nm -m main-10.5 | grep _bbb | grep " Bar" | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -o main-10.5a -framework Bar -framework Foo -mmacosx-version-min=10.5 \ - -F./Frameworks -F./Frameworks/Bar.framework/Frameworks -F./Frameworks/Foo.framework/Frameworks - nm -m main-10.5a | grep _aaa | grep " Foo" | ${FAIL_IF_EMPTY} - nm -m main-10.5a | grep _bbb | grep " Bar" | ${FAIL_IF_EMPTY} - ${PASS_IFF} /usr/bin/true - - -clean: - - rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c deleted file mode 100644 index 71123eb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/aaa.c +++ /dev/null @@ -1,3 +0,0 @@ - -void aaa() {} - diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c deleted file mode 100644 index 60fcf64..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/anotb.c +++ /dev/null @@ -1,26 +0,0 @@ - - -#define SYMBOL_IS_HERE_IN_10_4(sym) \ - extern const char sym##_tmp __asm("$ld$add$os10.4$_" #sym ); const char sym##_tmp = 0; - -#define SYMBOL_IS_HERE_IN_10_5(sym) \ - extern const char sym##_tmp __asm("$ld$add$os10.5$_" #sym ); const char sym##_tmp = 0; - -#define SYMBOL_NOT_HERE_IN_10_4(sym) \ - extern const char sym##_tmp __asm("$ld$hide$os10.4$_" #sym ); const char sym##_tmp = 0; - -#define SYMBOL_NOT_HERE_IN_10_5(sym) \ - extern const char sym##_tmp __asm("$ld$hide$os10.5$_" #sym ); const char sym##_tmp = 0; - - -// 10.4 10.5 -// aaa libbar libfoo -// bbb libfoo libbar -// - -// bbb is new here in 10.5. It was elsewhere in 10.4 -SYMBOL_NOT_HERE_IN_10_4(bbb) - -// aaa was here in 10.4 and move elsewhere -SYMBOL_IS_HERE_IN_10_4(aaa) - diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c deleted file mode 100644 index b6e9cc0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bbb.c +++ /dev/null @@ -1 +0,0 @@ -void bbb() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c deleted file mode 100644 index d29b878..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bnota.c +++ /dev/null @@ -1,25 +0,0 @@ -#define SYMBOL_IS_HERE_IN_10_4(sym) \ - extern const char sym##_tmp __asm("$ld$add$os10.4$_" #sym ); const char sym##_tmp = 0; - -#define SYMBOL_IS_HERE_IN_10_5(sym) \ - extern const char sym##_tmp __asm("$ld$add$os10.5$_" #sym ); const char sym##_tmp = 0; - -#define SYMBOL_NOT_HERE_IN_10_4(sym) \ - extern const char sym##_tmp __asm("$ld$hide$os10.4$_" #sym ); const char sym##_tmp = 0; - -#define SYMBOL_NOT_HERE_IN_10_5(sym) \ - extern const char sym##_tmp __asm("$ld$hide$os10.5$_" #sym ); const char sym##_tmp = 0; - - -// 10.4 10.5 -// aaa libbar libfoo -// bbb libfoo libbar -// - - -// bbb was here in 10.4 and move elsewhere -SYMBOL_IS_HERE_IN_10_4(bbb) - -// aaa is new here in 10.5. It was elsewhere in 10.4 -SYMBOL_NOT_HERE_IN_10_4(aaa) - diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c b/ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c deleted file mode 100644 index 902e908..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/main.c +++ /dev/null @@ -1,17 +0,0 @@ - -extern void foo(); -extern void bar(); - -extern void aaa(); -extern void bbb(); - - -int main() -{ - foo(); - bar(); - aaa(); - bbb(); - - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c deleted file mode 100644 index a2254b5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/foo.c +++ /dev/null @@ -1,6 +0,0 @@ - -extern void bar(); - -void foo() { bar(); } - -int var = 9; diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c deleted file mode 100644 index e5046a0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/main.c +++ /dev/null @@ -1,8 +0,0 @@ - -int var; - -int main() -{ - var = 3; - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile deleted file mode 100644 index 0ed1fa4..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - -# -# Test how tentative definitions interact with dylibs -# - -run: all - -all: - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-reexport_library,libbar.dylib - # verify -warn_commons works - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-warn_commons 2> warnings.log - grep "using common symbol" warnings.log | ${FAIL_IF_EMPTY} - # verify -commons use_dylibs works - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,use_dylibs - nm -m main | grep _var | grep libfoo | ${FAIL_IF_EMPTY} - # verify -commons ignore_dylibs works - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,ignore_dylibs - nm -m main | grep _var | grep __DATA | ${FAIL_IF_EMPTY} - # verify -commons error works - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -Wl,-commons,error 2> warnings.log - # verify -commons use_dylibs works with indirect dylibs - ${CC} ${CCFLAGS} main.c -Dvar=bar libfoo.dylib -o main -Wl,-commons,use_dylibs - nm -m main | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm -rf main libfoo.dylib libbar.dylib warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c deleted file mode 100644 index c1bb919..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/foo.c +++ /dev/null @@ -1,2 +0,0 @@ -void foo() {} -int var = 9; diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c deleted file mode 100644 index e5046a0..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/main.c +++ /dev/null @@ -1,8 +0,0 @@ - -int var; - -int main() -{ - var = 3; - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile deleted file mode 100644 index 833d676..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# Verify that -r -d -exported_symbol_list uses proper relocations for hidden -# newly defined (no longer tentative) definitions. -# - -ifneq (${ARCH},x86_64) - BETTER_NOT_FIND = _tent -else - # x86_64 uses a different style of relocations, so external relocs are ok to have - BETTER_NOT_FIND = blahblah -endif - - - -run: all - -all: - ${CC} ${CCFLAGS} test.c -c -o test.o - ${FAIL_IF_BAD_OBJ} test.o - - ${LD} -arch ${ARCH} -d -r test.o -exported_symbol _tent1 -o test-r.o - otool -rv test-r.o | grep ${BETTER_NOT_FIND} | ${PASS_IFF_EMPTY} - - -clean: - rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c deleted file mode 100644 index c9cf479..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real-hidden/test.c +++ /dev/null @@ -1,11 +0,0 @@ - -// tentative definitions -int tent1; -int tent2; -int __attribute__((visibility("hidden"))) tent3; - -// initialized to point to tentative definitions -int* pa = &tent1; -int* pb = &tent2; -int* pc = &tent3; - diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile deleted file mode 100644 index 092aa2e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# The point of this test is to verify that -r -d -# will transform a tentative definition into a real one. -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o - ${FAIL_IF_BAD_OBJ} test.${ARCH}.o - - ${LD} -arch ${ARCH} -r test.${ARCH}.o -o test-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r.${ARCH}.o | grep tentative | ${FAIL_IF_EMPTY} - - ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r-d.${ARCH}.o | grep tentative | ${PASS_IFF_EMPTY} - -clean: - rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt deleted file mode 100644 index de80bea..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is to verify that -r -d will transform a tentative definition into a real one. diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c b/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c deleted file mode 100644 index 87360fc..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-to-real/test.c +++ /dev/null @@ -1,3 +0,0 @@ - -// a tentative definition -int a; diff --git a/ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile b/ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile deleted file mode 100644 index 65b3131..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/thumb-blx/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# Verify the linker parses call sites correctly. -# The tricky case is thumb, which uses a blx to call to -# the arm stubs. This test verifies that there is no -# +2 error by checking for "plus" and that when the file -# is regenerated through ld -r that the dumped output -# remains unchanged. -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -c -o test.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.o > test.o.dump - # verify no +2 errors - grep "plus" test.o.dump | ${FAIL_IF_STDIN} - # verify .o file can be regenerated to an equivalent state - ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test-r.o > test-r.o.dump - # verify final linked image has no +2 errors - ${CC} ${CCFLAGS} test.o -o test - otool -tV -p _main test | grep blx | grep -v _malloc | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} test-r.o -o test-r - otool -tV -p _main test-r | grep blx | grep -v _malloc | ${FAIL_IF_STDIN} - ${PASS_IFF} diff test.o.dump test-r.o.dump - -clean: - rm -rf test.o test-r.o test.o.dump test-r.o.dump test test-r diff --git a/ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c b/ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c deleted file mode 100644 index ce0359f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/thumb-blx/test.c +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include - -int main() -{ - malloc(1); - malloc(2); - malloc(3); - malloc(4); - return 0; -} - - diff --git a/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile deleted file mode 100644 index eebcc37..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Check that -U and -undefined dynamic_lookup work -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup - nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} - ${FAIL_IF_BAD_MACHO} main - - ${CC} ${CCFLAGS} main.c -o main -Wl,-U,_foo - nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} - ${FAIL_IF_BAD_MACHO} main - - ${CC} ${CCFLAGS} main.c -o main -flat_namespace -Wl,-U,_foo - nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm main diff --git a/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c b/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c deleted file mode 100644 index 5de972f..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/undefined-dynamic-lookup/main.c +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern void foo(); - -int main() -{ - foo(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile deleted file mode 100644 index 789a304..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that a late loaded hidden symbol from an archive does not conflict -# with a symbol previously found in a dylib. -# gcc 4.2: DejaGnu failures due to libgcc visibility issues with -m64 -mmacosx-version-min=10.4 (G5) -# - -SHELL = bash # use bash shell so we can redirect just stderr - - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} -c bar.c -o bar.o - libtool -static bar.o -o libbar.a - ${CC} ${CCFLAGS} main.c -o main libfoo.dylib libbar.a 2>warning.log - cat warning.log | ${PASS_IFF_EMPTY} - - -clean: - rm -f libfoo.dylib bar.o libbar.a main warning.log diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c deleted file mode 100644 index 276f502..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/bar.c +++ /dev/null @@ -1,11 +0,0 @@ - - -void __attribute__((weak,visibility("hidden"))) foo() -{ - -} - - -void bar() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c deleted file mode 100644 index 3d8616a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - - -void __attribute__((weak)) foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c deleted file mode 100644 index 8c9c95c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning-dylib-v-archive/main.c +++ /dev/null @@ -1,11 +0,0 @@ - -extern void foo(); -extern void bar(); - - -int main() -{ - foo(); - bar(); - return 0; -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile deleted file mode 100644 index 43bdcd8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that two weak symbols with different visibility causes a warning -# and a weak and strong with different visibilities do not cause a warning. -# Spurious link warnings for inline members of C++ template classes -# - -SHELL = bash # use bash shell so we can redirect just stderr - - -run: all - -all: - ${CC} ${CCFLAGS} -c foo_weak_hidden.c -o foo_weak_hidden.o - ${CC} ${CCFLAGS} -c foo_weak.c -o foo_weak.o - ${CC} ${CCFLAGS} -c foo.c -o foo.o - ${CC} ${CCFLAGS} -c foo_hidden.c -o foo_hidden.o - # weak default and weak hidden should warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_EMPTY} - # weak hidden and strong should not warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_STDIN} - # weak default and strong hidden should not warn - ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_STDIN} - # weak default and weak hidden but -w should not warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -w -o libfoo.dylib 2> warnings.log - cat warnings.log | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} libfoo.dylib - -clean: - rm libfoo.dylib foo_weak_hidden.o foo_weak.o foo.o foo_hidden.o warnings.log diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c deleted file mode 100644 index 1624757..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - - -void foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c deleted file mode 100644 index cac53ce..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_hidden.c +++ /dev/null @@ -1,5 +0,0 @@ - - -void __attribute__((visibility("hidden"))) foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c deleted file mode 100644 index 3d8616a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak.c +++ /dev/null @@ -1,5 +0,0 @@ - - -void __attribute__((weak)) foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c b/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c deleted file mode 100644 index 8d461e6..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/visibility-warning/foo_weak_hidden.c +++ /dev/null @@ -1,5 +0,0 @@ - - -void __attribute__((weak, visibility("hidden"))) foo() -{ -} diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile deleted file mode 100644 index 222a82d..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# libfoo.dylib has weak defintiion of _foo -# libbar.dylib has strong defintiion of _foo -# -# Tests that if you link against libfoo.dylib and libbar.dylib -# that the two-level-namespace ordinal is set to the non-weak definition -# -# ld should keep looking when it finds a weak definition in a dylib# -# - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} main.c -o main libfoo.dylib libbar.dylib - nm -m main | grep _aaa | grep libbar | ${FAIL_IF_EMPTY} - nm -m main | grep _bbb | grep libfoo | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm libfoo.dylib libbar.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c deleted file mode 100644 index ae61731..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/bar.c +++ /dev/null @@ -1,6 +0,0 @@ - -int aaa() -{ - return 1; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c deleted file mode 100644 index d371654..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/foo.c +++ /dev/null @@ -1,11 +0,0 @@ - -int __attribute__((weak)) aaa() -{ - return 0; -} - -int __attribute__((weak)) bbb() -{ - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c b/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c deleted file mode 100644 index 74726fb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak-def-ordinal/main.c +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -extern void aaa(); -extern void bbb(); - -int main() -{ - aaa(); - bbb(); - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile deleted file mode 100644 index 0a00a39..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded -# - - -run: all - -all: - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib - ${FAIL_IF_BAD_MACHO} libbar.dylib - - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 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 - otool -lv main | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c deleted file mode 100644 index 261a806..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.c +++ /dev/null @@ -1,9 +0,0 @@ - - -#include "bar.h" - -void bar1() {} -void bar2() {} -void bar3() {} -void bar4() {} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h deleted file mode 100644 index 7ea2ef3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/bar.h +++ /dev/null @@ -1,9 +0,0 @@ - - -extern void bar1(); -extern void bar2() __attribute__((weak_import)); -extern void bar3(); -extern void bar4() __attribute__((weak_import)); - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c deleted file mode 100644 index aa25da5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.c +++ /dev/null @@ -1,9 +0,0 @@ - - -#include "foo.h" - -void foo1() {} -void foo2() {} -void foo3() {} -void foo4() {} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h deleted file mode 100644 index 54b3e36..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/foo.h +++ /dev/null @@ -1,6 +0,0 @@ - - -extern void foo1(); -extern void foo2() __attribute__((weak_import)); -extern void foo3(); -extern void foo4() __attribute__((weak_import)); diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c deleted file mode 100644 index cb61aeb..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_dylib/main.c +++ /dev/null @@ -1,22 +0,0 @@ - -#include "foo.h" -#include "bar.h" - -void* p; - -int main (void) -{ - // non-lazy reference to foo2 - p = &foo2; - // lazy reference to foo4 - foo4(); - - // non-lazy reference to bar2 - p = &bar2; - // lazy reference to bar4 and bar1 - bar4(); - bar1(); - - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile deleted file mode 100644 index d1fa1f3..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import/Makefile +++ /dev/null @@ -1,62 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the weak_import attribute works -# - - -run: all - -all: - ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib - ${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib - - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib - nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null - nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null - nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null - nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null - otool -rv main-${ARCH} | grep _data6 > /dev/null - ${FAIL_IF_BAD_MACHO} main-${ARCH} - - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib - nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null - nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null - otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null - ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib - -clean: - rm -rf *.dylib main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c deleted file mode 100644 index 900b052..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.c +++ /dev/null @@ -1,17 +0,0 @@ - - -#include "foo.h" - -void func1() {} -void func2() {} -void func3() {} -void func4() {} - - -int data1 = 0; -int data2 = 0; // weak_import initialized -int data3; -int data4; // weak_import uninitialized -int data5 = 0; -int data6 = 0; // weak_import - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h deleted file mode 100644 index f455515..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import/foo.h +++ /dev/null @@ -1,16 +0,0 @@ - - -extern void func1(); -extern void func2() __attribute__((weak_import)); -extern void func3(); -extern void func4() __attribute__((weak_import)); - -extern int data1; -extern int data2 __attribute__((weak_import)); -extern int data3; -extern int data4 __attribute__((weak_import)); -extern int data5; -extern int data6 __attribute__((weak_import)); - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_import/main.c deleted file mode 100644 index 3266aed..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import/main.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include "foo.h" - - -int* pdata5 = &data5; -int* pdata6 = &data6; - - -int main (void) -{ - // make non-lazy reference to func3 and func4 - if ( &func3 == &func4 ) { - // make lazy reference to func3 and func4 - func1(); - func2(); - } - - return data1 + data2 + data3 + data4; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest b/ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest deleted file mode 100644 index 5e51b89..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import2/Makefile.newtest +++ /dev/null @@ -1,58 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the weak_import attribute works -# - - -run: all - -all: - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib - ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib - nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null - nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _func4 | grep weak >/dev/null - nm -m main-${ARCH} | grep _data1 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null - nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null - ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib - nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null - nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _func4 | grep weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data1 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null - nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null - ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib - -clean: - rm -rf *.dylib main-* *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt deleted file mode 100644 index 5be42d8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import2/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test the weak_import attribute works diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c deleted file mode 100644 index 900b052..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.c +++ /dev/null @@ -1,17 +0,0 @@ - - -#include "foo.h" - -void func1() {} -void func2() {} -void func3() {} -void func4() {} - - -int data1 = 0; -int data2 = 0; // weak_import initialized -int data3; -int data4; // weak_import uninitialized -int data5 = 0; -int data6 = 0; // weak_import - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h deleted file mode 100644 index f455515..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo.h +++ /dev/null @@ -1,16 +0,0 @@ - - -extern void func1(); -extern void func2() __attribute__((weak_import)); -extern void func3(); -extern void func4() __attribute__((weak_import)); - -extern int data1; -extern int data2 __attribute__((weak_import)); -extern int data3; -extern int data4 __attribute__((weak_import)); -extern int data5; -extern int data6 __attribute__((weak_import)); - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c b/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c deleted file mode 100644 index 4580a87..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import2/foo1.c +++ /dev/null @@ -1,10 +0,0 @@ - - -void func2() {} -void func4() {} - - -int data2 = 0; // foo.c also has weak_import initialized -int data4; // foo.c also has weak_import uninitialized -int data6 = 0; // foo.c also has weak_import - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c deleted file mode 100644 index 3266aed..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import2/main.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include "foo.h" - - -int* pdata5 = &data5; -int* pdata6 = &data6; - - -int main (void) -{ - // make non-lazy reference to func3 and func4 - if ( &func3 == &func4 ) { - // make lazy reference to func3 and func4 - func1(); - func2(); - } - - return data1 + data2 + data3 + data4; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile b/ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile deleted file mode 100644 index 98a2779..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import3/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the weak_import attribute works -# - - -run: all - -all: - ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o - - ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o - ${FAIL_IF_BAD_OBJ} foo1-${ARCH}.o - - ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib 2>/dev/null - -clean: - rm -rf *.o *.dylib main-* diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt deleted file mode 100644 index 5be42d8..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test the weak_import attribute works diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c deleted file mode 100644 index 900b052..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.c +++ /dev/null @@ -1,17 +0,0 @@ - - -#include "foo.h" - -void func1() {} -void func2() {} -void func3() {} -void func4() {} - - -int data1 = 0; -int data2 = 0; // weak_import initialized -int data3; -int data4; // weak_import uninitialized -int data5 = 0; -int data6 = 0; // weak_import - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h deleted file mode 100644 index f455515..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo.h +++ /dev/null @@ -1,16 +0,0 @@ - - -extern void func1(); -extern void func2() __attribute__((weak_import)); -extern void func3(); -extern void func4() __attribute__((weak_import)); - -extern int data1; -extern int data2 __attribute__((weak_import)); -extern int data3; -extern int data4 __attribute__((weak_import)); -extern int data5; -extern int data6 __attribute__((weak_import)); - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c b/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c deleted file mode 100644 index 392a5b7..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import3/foo1.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "foo.h" - -int data4; // foo.c also has weak_import uninitialized - diff --git a/ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c b/ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c deleted file mode 100644 index 3266aed..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/weak_import3/main.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include "foo.h" - - -int* pdata5 = &data5; -int* pdata6 = &data6; - - -int main (void) -{ - // make non-lazy reference to func3 and func4 - if ( &func3 == &func4 ) { - // make lazy reference to func3 and func4 - func1(); - func2(); - } - - return data1 + data2 + data3 + data4; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/Makefile b/ld64/FireOpal/unit-tests/test-cases/why_live/Makefile deleted file mode 100644 index 9c4811e..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/why_live/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -## -# Copyright (c) 2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - -# -# Text -why_live option with dead code stripping -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c foo.c bar.c -o main -dead_strip -Wl,-why_live,_bar 2>call_chains - grep _bar call_chains | ${FAIL_IF_EMPTY} - grep _foo call_chains | ${FAIL_IF_EMPTY} - grep _main call_chains | ${FAIL_IF_EMPTY} - grep _frob call_chains | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm main call_chains diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/bar.c b/ld64/FireOpal/unit-tests/test-cases/why_live/bar.c deleted file mode 100644 index e425999..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/why_live/bar.c +++ /dev/null @@ -1 +0,0 @@ -void bar() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/foo.c b/ld64/FireOpal/unit-tests/test-cases/why_live/foo.c deleted file mode 100644 index 9a2edf5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/why_live/foo.c +++ /dev/null @@ -1,12 +0,0 @@ - -extern void bar(); - -void foo() -{ - bar(); -} - -void frob() -{ - bar(); -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/why_live/main.c b/ld64/FireOpal/unit-tests/test-cases/why_live/main.c deleted file mode 100644 index a5a79d5..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/why_live/main.c +++ /dev/null @@ -1,7 +0,0 @@ -extern void foo(); - -int main() -{ - foo(); - return 0; -} \ No newline at end of file diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c b/ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c deleted file mode 100644 index cfdc08c..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill/test.c +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -// if we used one big array, the linker would page align it -// but we want to test a non-page align big chunk of zero-fill data -int bigarray1[256]; -int bigarray2[256]; -int bigarray3[256]; -int bigarray4[256]; -int bigarray5[256]; -int bigarray6[256]; -static int staticbigarray1[256]; -static int staticbigarray2[256]; -static int staticbigarray3[256]; -static int staticbigarray4[256]; -static int staticbigarray5[256]; -static int staticbigarray6[256]; - -int main() -{ - staticbigarray1[10] = 4; - staticbigarray2[10] = 4; - staticbigarray3[10] = 4; - staticbigarray4[10] = 4; - staticbigarray5[10] = 4; - staticbigarray6[10] = 4; - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile deleted file mode 100644 index b011e70..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: all - -all: - ${CC} ${CCFLAGS} test.c -o test - ${PASS_IFF_GOOD_MACHO} test - -clean: - rm -rf test diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c b/ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c deleted file mode 100644 index 219cdc2..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill2/test.c +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -// if we used one big array, the linker would page align it -// but we want to test a non-page align big chunk of zero-fill data - -#if __LP64__ - #define BOOST 100UL -#else - #define BOOST 1 -#endif - -int bigarray1[256]; -int bigarray2[2560]; -int bigarray3[25600]; -int bigarray4[256000]; -int bigarray5[2560000]; -int bigarray6[256000000*BOOST]; -static int staticbigarray1[256]; -static int staticbigarray2[2560]; -static int staticbigarray3[25600]; -static int staticbigarray4[256000]; -static int staticbigarray5[2560000]; -static int staticbigarray6[25600000*BOOST]; - -int main() -{ - staticbigarray1[10] = 4; - staticbigarray2[10] = 4; - staticbigarray3[10] = 4; - staticbigarray4[10] = 4; - staticbigarray5[10] = 4; - staticbigarray6[10] = 4; - return 0; -} - diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile deleted file mode 100644 index 6266019..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: test-run-${ARCH} - -# i386 catches the problem in the assembler phase -test-run-i386: - ${PASS_IFF} true - -test-run-ppc test-run-ppc64: test-${ARCH} - -test-run-x86_64 test-ppc64: - ${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: - ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null - -clean: - rm -rf test-* *.o *.s *.i diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c b/ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c deleted file mode 100644 index 64ac72a..0000000 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill3/test.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include - -// if we used one big array, the linker would page align it -// but we want to test a non-page align big chunk of zero-fill data -int bigarray1[256]; -int bigarray2[2560]; -int bigarray3[25600]; -int bigarray4[256000]; -int bigarray5[2560000]; -int bigarray7[16777216L+1]; -int bigarray8[2*16777216L+1]; -int bigarray9[4*16777216L+1]; -int bigarray10[8*16777216L+1]; -int bigarray11[16*16777216L+1]; -int bigarray99[2147483647U/SHRINK]; -static int staticbigarray1[256]; -static int staticbigarray2[2560]; -static int staticbigarray3[25600]; -static int staticbigarray4[256000]; -static int staticbigarray5[2560000]; -static int staticbigarray6[25600000]; -//static int staticbigarray99[2147483647U/SHRINK]; - -int main() -{ - bigarray5[10] = 4; - bigarray7[10] = 4; - bigarray8[10] = 4; - bigarray9[10] = 4; - bigarray10[10] = 4; - bigarray11[10] = 4; - bigarray99[10] = 4; - staticbigarray1[10] = 4; - staticbigarray2[10] = 4; - staticbigarray3[10] = 4; - staticbigarray4[10] = 4; - staticbigarray5[10] = 4; - staticbigarray6[10] = 4; - return 0; -} diff --git a/ld64/doc/man/man1/dyldinfo.1 b/ld64/doc/man/man1/dyldinfo.1 new file mode 100644 index 0000000..3dafedc --- /dev/null +++ b/ld64/doc/man/man1/dyldinfo.1 @@ -0,0 +1,47 @@ +.Dd November 20, 2008 +.Dt dyldinfo 1 +.Os Darwin +.Sh NAME +.Nm dyldinfo +.Nd "Displays information used by dyld in an executable" +.Sh SYNOPSIS +.Nm +.Op Fl arch Ar arch-name +.Op Fl rebase +.Op Fl bind +.Op Fl weak_bind +.Op Fl lazy_bind +.Op Fl export +.Op Fl opcodes +.Ar file(s) +.Sh DESCRIPTION +Executables built for Mac OS X 10.6 and later have a new format for the +information in the __LINKEDIT segment. The dyldinfo tool will display +that information. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl arch Ar arch +Only display the specified architecture. Other architectures in a universal image are ignored. +.It Fl rebase +Display the table of rebasing information. Rebasing is what dyld does when an image is +not loaded at its preferred address. Typically, this involves updating pointers in the __DATA +segment which are point within the image. +.It Fl bind +Display the table of binding information. These are the symbolic fix ups that dyld must +do when an image is loaded. +.It Fl weak_bind +Display the table of weak binding information. Typically, only C++ progams will have any +weak binding. These are symbols which dyld must unique accross all images. +.It Fl lazy_bind +Display the table of lazy binding information. These are symbols which dyld delays binding +until they are first used. Lazy binding is automatically used for all function calls to +functions in some external dylib. +.It Fl export +Display the table symbols which this image exports. +.It Fl opcodes +Display the low level opcodes used to encode all rebase and binding information. +.El +.Sh SEE ALSO +.Xr otool 1 +.Xr nm 1 diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index a8b0188..c910e04 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -1,4 +1,4 @@ -.Dd December 8, 2006 +.Dd December 15, 2008 .Dt ld 1 .Os Darwin .Sh NAME @@ -192,6 +192,9 @@ and before the default search path. Loads all members of static archive libraries. .It Fl ObjC Loads all members of static archive libraries that implement an Objective-C class or category. +.It Fl force_load Ar path_to_archive +Loads all members of the specified static archive library. Note: -all_load forces all members of all +archives to be loaded. This option allows you to target a specific archive. .El .Ss Options that control additional content .Bl -tag @@ -217,15 +220,10 @@ Enables dtrace static probes when producing a final linked image. The file .Ar file must be a DTrace script which declares the static probes. .El -.Ss Options that control optimizations +.Ss Options that control optimizations .Bl -tag .It Fl dead_strip Remove functions and data that are unreachable by the entry point or exported symbols. -.It Fl dead_strip_dylibs -Remove dylibs that are unreachable by the entry point or exported symbols. That is, -suppresses the generation of load command commands for dylibs which supplied no -symbols during the link. This option should not be used when linking against a dylib which -is required at runtime for some indirect reason such as the dylib has an important initializer. .It Fl order_file Ar file Alters the order in which functions and data are laid out. For each section in the output file, any symbol in that section that are specified in the order file @@ -238,6 +236,14 @@ This is useful for static functions/data that occur in multiple files. A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). This enables you to have one order file that works for multiple architectures. Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file. +.It Fl no_order_inits +When the -order_file option is not used, the linker lays out functions in object file order and +it moves all initializer routines to the start of the __text section and terminator routines +to the end. Use this option to disable the automatic rearrangement of initializers and terminators. +.It Fl no_order_data +By default the linker reorders global data in the __DATA segment so that all global variables that +dyld will need to adjust at launch time will early in the __DATA segment. This reduces the number +of dirty pages at launch time. This option disables that optimization. .It Fl macosx_version_min Ar version This is set to indicate the oldest Mac OS X version that that the output is to be used on. Specifying a later version enables the linker to assumes features of that OS in the output file. The format of @@ -260,13 +266,22 @@ If you link with -framework Cocoa and use a symbol from Foundation, the linker w command to load Foundation and encode the symbol as coming from Foundation. If you use this option, the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation. +.It Fl exported_symbols_order Ar file +When targeting Mac OS X 10.6 or later, the format of the exported symbol information can be optimized to +make lookups of popular symbols faster. This option is used to pass a file containing a list of +the symbols most frequently used by clients of the dynamic library being built. Not all exported symbols +need to be listed. .El -.Ss Options when creating a dynamic library (dylib) +.Ss Options when creating a dynamic library (dylib) .Bl -tag .It Fl install_name Ar name Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library will record that path as the way dyld should locate this library. If this option is not specified, then the -o path will be used. This option is also called -dylib_install_name for compatibility. +.It Fl mark_dead_strippable_dylib +Specifies that the dylib being built can be dead strip by any client. That is, the dylib has +no initialization side effects. So if a client links against the dylib, but never uses +any symbol from it, the linker can optimize away the use of the dylib. .It Fl compatibility_version Ar number Specifies the compatibility version number of the library. When a library is loaded by dyld, the compatibility version is checked and if the program's version is greater that the library's version, it is an error. @@ -463,6 +478,28 @@ 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 no_eh_labels +Normally in -r mode, the linker produces .eh labels on all FDEs in the __eh_frame section. +This option suppresses those labels. Those labels are not needed by the Mac OS X 10.6 +linker but are needed by earlier linker tools. +.It Fl warn_compact_unwind +When producing a final linked image, the linker processes the __eh_frame section and +produces an __unwind_info section. Most FDE entries in the __eh_frame can be represented +by a 32-bit value in the __unwind_info section. The option issues a warning for +any function whose FDE cannot be expressed in the compact unwind format. +.It Fl dead_strip_dylibs +Remove dylibs that are unreachable by the entry point or exported symbols. That is, +suppresses the generation of load command commands for dylibs which supplied no +symbols during the link. This option should not be used when linking against a dylib which +is required at runtime for some indirect reason such as the dylib has an important initializer. +.It Fl allow_sub_type_mismatches +Normally the linker consisders different cpu-subtype for ARM (e.g. armv4t and armv6) to be different +different architectures that cannot be mixed at build time. This option relaxes that requirement, +allowing you to mix object files compiled for different ARM subtypes. .It Fl no_uuid Do not generate an LC_UUID load command in the output file. .It Fl root_safe @@ -516,6 +553,9 @@ specified is a hexadecimal number that indicates the base address for the read-o .It Fl segaddr Ar name Ar address Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number that is a multiple of 4K page size. +.It Fl seg_page_size Ar name Ar size +Specifies the page size used by the specified segment. By default the page size is 4096 for all segments. +The linker will lay out segments such that size of a segment is always an even multiple of its page size. .It Fl dylib_file Ar install_name:file_name Specifies that a dynamic shared library is in a different location than its standard location. Use this option when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other diff --git a/ld64/doc/man/man1/unwinddump.1 b/ld64/doc/man/man1/unwinddump.1 new file mode 100644 index 0000000..3aefe93 --- /dev/null +++ b/ld64/doc/man/man1/unwinddump.1 @@ -0,0 +1,22 @@ +.Dd November 7, 2008 +.Dt unwinddump 1 +.Os Darwin +.Sh NAME +.Nm unwinddump +.Nd "Displays compact unwind information in an executable" +.Sh SYNOPSIS +.Nm +.Op Fl arch Ar arch-name +.Ar file(s) +.Sh DESCRIPTION +When a C++ (or x86_64 Objective-C) exception is thrown, the runtime must unwind +the stack looking for some function to catch the exception. Traditionally, +the unwind information is stored in the __TEXT/__eh_frame section of each executable +as Dwarf CFI (call frame information). Beginning in Mac OS X 10.6, the unwind +information is also encoded in the __TEXT/__unwind_info section using a two-level +lookup table of compact unwind encodings. +.Pp +The unwinddump tool displays the content of the __TEXT/__unwind_info section. +.Sh SEE ALSO +.Xr ld 1 +.Xr dwarfdump 1 diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 5f8cd7f..fd0213c 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ F96D536C094A275F008E9EE8 /* PBXTargetDependency */, F96904890A4333AC00B77D2A /* PBXTargetDependency */, F9EA73970974999B008B4F1D /* PBXTargetDependency */, + F9B693890EC4D28C00076912 /* PBXTargetDependency */, ); name = "unit-tests"; productName = "unit-tests"; @@ -30,6 +31,9 @@ dependencies = ( F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, F9B1A26B0A3A568400DA8FAB /* PBXTargetDependency */, + F9C12EEA0ED65765005BC69D /* PBXTargetDependency */, + F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */, + F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */, ); name = all; productName = all; @@ -39,14 +43,19 @@ /* Begin PBXBuildFile section */ F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; - F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; - F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; + F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; + F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */; }; + F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */; }; + F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; + F9B670120DDA17E800E6D0DA /* UnwindDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */; }; + F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B813810EC2653000F94C13 /* unwinddump.1 */; }; + F9BA51650ECE58C800D1D62E /* dyldinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */; }; F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; + F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */; }; F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; - F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9FCC3F10A54A75600CEB866 /* ld64.1 */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -90,6 +99,13 @@ remoteGlobalIDString = F971EED206D5ACF60041D381; remoteInfo = ObjectDump; }; + F9A3DE150ED76D9A00C590B9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9A3DDC90ED762B700C590B9; + remoteInfo = libprunetrie; + }; F9B1A2680A3A568200DA8FAB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9023C3006D5A227001BBF46 /* Project object */; @@ -104,6 +120,27 @@ remoteGlobalIDString = F9EC77ED0A2F85F6002A3E39; remoteInfo = rebase; }; + F9B693880EC4D28C00076912 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9B670010DDA176100E6D0DA; + remoteInfo = unwinddump; + }; + F9B8135C0EC2620E00F94C13 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9B670010DDA176100E6D0DA; + remoteInfo = unwinddump; + }; + F9C12EE90ED65765005BC69D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9BA51600ECE58BE00D1D62E; + remoteInfo = dyldinfo; + }; F9EA73960974999B008B4F1D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9023C3006D5A227001BBF46 /* Project object */; @@ -114,57 +151,99 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - F97F5025070D0B6300B9FCD7 /* CopyFiles */ = { + F97F5025070D0B6300B9FCD7 /* copy man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */, - F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */, + F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */, ); + name = "copy man page"; runOnlyForDeploymentPostprocessing = 1; }; - F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */ = { + F9A3DE140ED76D7700C590B9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "/usr/local/include/mach-o"; + dstSubfolderSpec = 0; + files = ( + F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + F9B1A25E0A3A44CB00DA8FAB /* install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - F9B1A2640A3A563E00DA8FAB /* rebase.1 in CopyFiles */, + F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */, + ); + name = "install man page"; + runOnlyForDeploymentPostprocessing = 1; + }; + F9B813870EC2659600F94C13 /* install man page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */, ); + name = "install man page"; + runOnlyForDeploymentPostprocessing = 1; + }; + F9C12EA50ED63E05005BC69D /* install man page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */, + ); + name = "install man page"; runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/LTOReader.hpp; sourceTree = ""; }; + 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/ld/LTOReader.hpp; sourceTree = ""; }; C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ExecutableFile.h; sourceTree = ""; }; - F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld.cpp; sourceTree = ""; }; - F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ObjectFile.h; sourceTree = ""; }; - F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/FileAbstraction.hpp; sourceTree = ""; }; - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/MachOFileAbstraction.hpp; sourceTree = ""; }; - F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/Architectures.hpp; sourceTree = ""; }; - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/MachOReaderDylib.hpp; sourceTree = ""; }; - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/MachOReaderRelocatable.hpp; sourceTree = ""; }; - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/MachOWriterExecutable.hpp; sourceTree = ""; }; + F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ld/ExecutableFile.h; sourceTree = ""; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; }; + F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ld/ObjectFile.h; sourceTree = ""; }; + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; }; + F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; }; + F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/ld/MachOReaderDylib.hpp; sourceTree = ""; }; + F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/ld/MachOReaderRelocatable.hpp; sourceTree = ""; }; + F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/ld/MachOWriterExecutable.hpp; sourceTree = ""; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; - F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/ObjectDump.cpp; sourceTree = ""; }; + F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = ""; }; F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; - F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/OpaqueSection.hpp; sourceTree = ""; }; - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ArchiveReader.hpp; sourceTree = SOURCE_ROOT; }; + F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/ld/OpaqueSection.hpp; sourceTree = ""; }; + F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ld/ArchiveReader.hpp; sourceTree = ""; }; + F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprunetrie.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PruneTrie.cpp; path = src/other/PruneTrie.cpp; sourceTree = ""; }; + F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prune_trie.h; path = src/other/prune_trie.h; sourceTree = ""; }; F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; - F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/Options.cpp; sourceTree = ""; }; - F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/Options.h; sourceTree = ""; }; + F9B670080DDA176100E6D0DA /* unwinddump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unwinddump; sourceTree = BUILT_PRODUCTS_DIR; }; + F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindDump.cpp; path = src/other/unwinddump.cpp; sourceTree = ""; }; + F9B813810EC2653000F94C13 /* unwinddump.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = unwinddump.1; path = doc/man/man1/unwinddump.1; sourceTree = ""; }; + F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOTrie.hpp; path = src/abstraction/MachOTrie.hpp; sourceTree = ""; }; + F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = src/other/dyldinfo.cpp; sourceTree = ""; }; + F9BA51610ECE58BE00D1D62E /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; }; + F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; }; F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; - F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/machochecker.cpp; sourceTree = ""; }; - F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/debugline.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/debugline.h; sourceTree = ""; }; + F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = ""; }; + F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/ld/debugline.c; sourceTree = ""; tabWidth = 8; usesTabs = 1; }; + F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; - F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/rebase.cpp; sourceTree = ""; }; - F9FCC3F10A54A75600CEB866 /* ld64.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld64.1; path = doc/man/man1/ld64.1; sourceTree = ""; }; + F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -182,6 +261,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F9B670040DDA176100E6D0DA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9BA515F0ECE58BE00D1D62E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9EA72C9097454A6008B4F1D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -203,10 +296,55 @@ isa = PBXGroup; children = ( C02A29DE0953B26E001FB8C1 /* ChangeLog */, - F933DC37092A82480083EAC8 /* Architectures.hpp */, + F9B813A80EC27B6300F94C13 /* abstraction */, + F9B813AD0EC27B8500F94C13 /* ld */, + F9B813B00EC27B9E00F94C13 /* other */, + F9B8137E0EC2651200F94C13 /* doc */, + F9023C3A06D5A23E001BBF46 /* Products */, + ); + sourceTree = ""; + }; + F9023C3A06D5A23E001BBF46 /* Products */ = { + isa = PBXGroup; + children = ( + F9023C3906D5A23E001BBF46 /* ld */, + F971EED306D5ACF60041D381 /* ObjectDump */, + F9EA72CB097454A6008B4F1D /* machocheck */, + F9EC77EE0A2F85F6002A3E39 /* rebase */, + F9B670080DDA176100E6D0DA /* unwinddump */, + F9BA51610ECE58BE00D1D62E /* dyldinfo */, + F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */, + ); + name = Products; + sourceTree = ""; + }; + F9B8137E0EC2651200F94C13 /* doc */ = { + isa = PBXGroup; + children = ( + F97F5028070D0BB200B9FCD7 /* ld.1 */, + F9B1A2580A3A448800DA8FAB /* rebase.1 */, + F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */, + F9B813810EC2653000F94C13 /* unwinddump.1 */, + ); + name = doc; + sourceTree = ""; + }; + F9B813A80EC27B6300F94C13 /* abstraction */ = { + isa = PBXGroup; + children = ( + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */, F933D9460929277C0083EAC8 /* FileAbstraction.hpp */, + F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */, + ); + name = abstraction; + sourceTree = ""; + }; + F9B813AD0EC27B8500F94C13 /* ld */ = { + isa = PBXGroup; + children = ( + F9023C3F06D5A254001BBF46 /* ld.cpp */, + F933DC37092A82480083EAC8 /* Architectures.hpp */, F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */, - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */, F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, @@ -214,30 +352,26 @@ F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, F9023C4106D5A254001BBF46 /* ObjectFile.h */, F98D26850AA779BD00416316 /* OpaqueSection.hpp */, - F9023C3F06D5A254001BBF46 /* ld.cpp */, F9C0D48A06DD1E1B001C7193 /* Options.cpp */, F9C0D48B06DD1E1B001C7193 /* Options.h */, - F9EA7583097882F3008B4F1D /* debugline.h */, F9EA7582097882F3008B4F1D /* debugline.c */, - F9EA72D4097454FF008B4F1D /* machochecker.cpp */, - F971EED706D5AD240041D381 /* ObjectDump.cpp */, - F9EC78050A2F8674002A3E39 /* rebase.cpp */, - F97F5028070D0BB200B9FCD7 /* ld.1 */, - F9FCC3F10A54A75600CEB866 /* ld64.1 */, - F9B1A2580A3A448800DA8FAB /* rebase.1 */, - F9023C3A06D5A23E001BBF46 /* Products */, + F9EA7583097882F3008B4F1D /* debugline.h */, ); + name = ld; sourceTree = ""; }; - F9023C3A06D5A23E001BBF46 /* Products */ = { + F9B813B00EC27B9E00F94C13 /* other */ = { isa = PBXGroup; children = ( - F9023C3906D5A23E001BBF46 /* ld */, - F971EED306D5ACF60041D381 /* ObjectDump */, - F9EA72CB097454A6008B4F1D /* machocheck */, - F9EC77EE0A2F85F6002A3E39 /* rebase */, + F9EA72D4097454FF008B4F1D /* machochecker.cpp */, + F971EED706D5AD240041D381 /* ObjectDump.cpp */, + F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */, + F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */, + F9EC78050A2F8674002A3E39 /* rebase.cpp */, + F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */, + F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */, ); - name = Products; + name = other; sourceTree = ""; }; /* End PBXGroup section */ @@ -250,8 +384,7 @@ 0B12F6A50CE39466008ABCAE /* build configure.h */, F9023C3606D5A23E001BBF46 /* Sources */, F9023C3706D5A23E001BBF46 /* Frameworks */, - F97F5025070D0B6300B9FCD7 /* CopyFiles */, - F9FCC3EF0A54A4ED00CEB866 /* Run Script */, + F97F5025070D0B6300B9FCD7 /* copy man page */, ); buildRules = ( F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, @@ -280,6 +413,56 @@ productReference = F971EED306D5ACF60041D381 /* ObjectDump */; productType = "com.apple.product-type.tool"; }; + F9A3DDC90ED762B700C590B9 /* libprunetrie */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */; + buildPhases = ( + F9A3DDC70ED762B700C590B9 /* Sources */, + F9A3DE140ED76D7700C590B9 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libprunetrie; + productName = libmachotrie; + productReference = F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */; + productType = "com.apple.product-type.library.static"; + }; + F9B670010DDA176100E6D0DA /* unwinddump */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */; + buildPhases = ( + F9B670020DDA176100E6D0DA /* Sources */, + F9B670040DDA176100E6D0DA /* Frameworks */, + F9B813870EC2659600F94C13 /* install man page */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = unwinddump; + productName = machocheck; + productReference = F9B670080DDA176100E6D0DA /* unwinddump */; + productType = "com.apple.product-type.tool"; + }; + F9BA51600ECE58BE00D1D62E /* dyldinfo */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */; + buildPhases = ( + F9BA515E0ECE58BE00D1D62E /* Sources */, + F9BA515F0ECE58BE00D1D62E /* Frameworks */, + F9C12EA50ED63E05005BC69D /* install man page */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyldinfo; + productName = dyldinfo; + productReference = F9BA51610ECE58BE00D1D62E /* dyldinfo */; + productType = "com.apple.product-type.tool"; + }; F9EA72CA097454A6008B4F1D /* machocheck */ = { isa = PBXNativeTarget; buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; @@ -302,7 +485,7 @@ buildPhases = ( F9EC77EB0A2F85F6002A3E39 /* Sources */, F9EC77EC0A2F85F6002A3E39 /* Frameworks */, - F9B1A25E0A3A44CB00DA8FAB /* CopyFiles */, + F9B1A25E0A3A44CB00DA8FAB /* install man page */, ); buildRules = ( ); @@ -329,8 +512,11 @@ F9B1A2670A3A567B00DA8FAB /* all */, F9023C3806D5A23E001BBF46 /* ld */, F9EC77ED0A2F85F6002A3E39 /* rebase */, + F9B670010DDA176100E6D0DA /* unwinddump */, F971EED206D5ACF60041D381 /* ObjectDump */, F9EA72CA097454A6008B4F1D /* machocheck */, + F9BA51600ECE58BE00D1D62E /* dyldinfo */, + F9A3DDC90ED762B700C590B9 /* libprunetrie */, F96D5368094A2754008E9EE8 /* unit-tests */, ); }; @@ -350,7 +536,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/bash; - shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\nelse\n\techo \"#undef LTO_SUPPORT\t\" > ${DERIVED_FILE_DIR}/configure.h\nfi\n"; + shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib\" > ${DERIVED_FILE_DIR}/linker_opts\nelse\n\techo \"#undef LTO_SUPPORT\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"\" > ${DERIVED_FILE_DIR}/linker_opts\nfi\n"; showEnvVarsInLog = 0; }; F96D5367094A2754008E9EE8 /* ShellScript */ = { @@ -367,21 +553,6 @@ shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; showEnvVarsInLog = 0; }; - F9FCC3EF0A54A4ED00CEB866 /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "cd ${DSTROOT}/usr/bin\nln -s ld ld64"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -404,6 +575,30 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F9A3DDC70ED762B700C590B9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9B670020DDA176100E6D0DA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9B670120DDA17E800E6D0DA /* UnwindDump.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9BA515E0ECE58BE00D1D62E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9BA51650ECE58C800D1D62E /* dyldinfo.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9EA72C8097454A6008B4F1D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -438,6 +633,11 @@ target = F971EED206D5ACF60041D381 /* ObjectDump */; targetProxy = F96D536B094A275F008E9EE8 /* PBXContainerItemProxy */; }; + F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9A3DDC90ED762B700C590B9 /* libprunetrie */; + targetProxy = F9A3DE150ED76D9A00C590B9 /* PBXContainerItemProxy */; + }; F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9023C3806D5A23E001BBF46 /* ld */; @@ -448,6 +648,21 @@ target = F9EC77ED0A2F85F6002A3E39 /* rebase */; targetProxy = F9B1A26A0A3A568400DA8FAB /* PBXContainerItemProxy */; }; + F9B693890EC4D28C00076912 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9B670010DDA176100E6D0DA /* unwinddump */; + targetProxy = F9B693880EC4D28C00076912 /* PBXContainerItemProxy */; + }; + F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9B670010DDA176100E6D0DA /* unwinddump */; + targetProxy = F9B8135C0EC2620E00F94C13 /* PBXContainerItemProxy */; + }; + F9C12EEA0ED65765005BC69D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9BA51600ECE58BE00D1D62E /* dyldinfo */; + targetProxy = F9C12EE90ED65765005BC69D /* PBXContainerItemProxy */; + }; F9EA73970974999B008B4F1D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9EA72CA097454A6008B4F1D /* machocheck */; @@ -496,9 +711,10 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_LDFLAGS = "@$(DERIVED_FILE_DIR)/linker_opts"; PREBINDING = NO; PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; @@ -510,7 +726,8 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; + ARCHS = x86_64; + COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; @@ -548,11 +765,16 @@ ); INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_LDFLAGS = ( + "@$(DERIVED_FILE_DIR)/linker_opts", + "-Wl,-exported_symbol,__mh_execute_header", + ); PREBINDING = NO; PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; - VALID_ARCHS = "i386 ppc"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "x86_64 i386 ppc"; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; }; @@ -566,7 +788,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/include"; + HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include"; INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; OTHER_REZFLAGS = ""; @@ -589,6 +811,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = s; + HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include"; INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; OTHER_REZFLAGS = ""; @@ -615,7 +838,6 @@ buildSettings = { GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/SDKs/Extra/usr/include"; }; name = Release; }; @@ -637,6 +859,37 @@ }; name = Release; }; + F9A3DDCB0ED762B800C590B9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = prunetrie; + }; + name = Debug; + }; + F9A3DDCC0ED762B800C590B9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = prunetrie; + }; + name = Release; + }; F9B1A26D0A3A568700DA8FAB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -659,6 +912,72 @@ }; name = Release; }; + F9B670060DDA176100E6D0DA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = unwinddump; + }; + name = Debug; + }; + F9B670070DDA176100E6D0DA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = unwinddump; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + }; + name = Release; + }; + F9BA51630ECE58BF00D1D62E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = dyldinfo; + }; + name = Debug; + }; + F9BA51640ECE58BF00D1D62E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = dyldinfo; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + ZERO_LINK = NO; + }; + name = Release; + }; F9EA72D0097454D5008B4F1D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -680,6 +999,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ""; INSTALL_PATH = "$(HOME)/bin"; PREBINDING = NO; PRODUCT_NAME = machocheck; @@ -704,15 +1024,19 @@ F9EC77F20A2F8616002A3E39 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; + COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; PREBINDING = NO; PRODUCT_NAME = rebase; - VALID_ARCHS = "i386 ppc"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "i386 ppc x86_64"; }; name = Release; }; @@ -755,6 +1079,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9A3DDCB0ED762B800C590B9 /* Debug */, + F9A3DDCC0ED762B800C590B9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -764,6 +1097,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9B670060DDA176100E6D0DA /* Debug */, + F9B670070DDA176100E6D0DA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9BA51630ECE58BF00D1D62E /* Debug */, + F9BA51640ECE58BF00D1D62E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ld64/src/Architectures.hpp b/ld64/src/Architectures.hpp deleted file mode 100644 index 2546bfe..0000000 --- a/ld64/src/Architectures.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __ARCHITECTURES__ -#define __ARCHITECTURES__ - -#include "FileAbstraction.hpp" - - -// -// Architectures -// -struct ppc -{ - typedef Pointer32 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; -}; - -struct ppc64 -{ - typedef Pointer64 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; -}; - -struct x86 -{ - typedef Pointer32 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff16, - kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; -}; - -struct x86_64 -{ - typedef Pointer64 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32, - kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, - kBranchPCRel32, kBranchPCRel32WeakImport, - kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, - kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; -}; - -struct arm -{ - typedef Pointer32 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kReadOnlyPointer, - kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; -}; - -#endif // __ARCHITECTURES__ - - diff --git a/ld64/src/FileAbstraction.hpp b/ld64/src/FileAbstraction.hpp deleted file mode 100644 index 1f7a629..0000000 --- a/ld64/src/FileAbstraction.hpp +++ /dev/null @@ -1,145 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef __FILE_ABSTRACTION__ -#define __FILE_ABSTRACTION__ - - -#include -#include -#include - -#ifdef __OPTIMIZE__ -#define INLINE __attribute__((always_inline)) -#else -#define INLINE -#endif - -// -// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants -// -// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 -// -// -// get16() read a 16-bit number from an E endian struct -// set16() write a 16-bit number to an E endian struct -// get32() read a 32-bit number from an E endian struct -// set32() write a 32-bit number to an E endian struct -// get64() read a 64-bit number from an E endian struct -// set64() write a 64-bit number to an E endian struct -// -// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) -// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) -// -// getBitsRaw() read a bit field from a struct with native endianness -// setBitsRaw() write a bit field from a struct with native endianness -// - -class BigEndian -{ -public: - static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } - static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } - - static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } - static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } - - static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } - static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } - - static uint32_t getBits(const uint32_t& from, - uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } - static void setBits(uint32_t& into, uint32_t value, - uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } - - static uint32_t getBitsRaw(const uint32_t& from, - uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< -class Pointer32 -{ -public: - typedef uint32_t uint_t; - typedef _E E; - - static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } - static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } -}; - - -template -class Pointer64 -{ -public: - typedef uint64_t uint_t; - typedef _E E; - - static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } - static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } -}; - - - - - -#endif // __FILE_ABSTRACTION__ - - diff --git a/ld64/src/MachOReaderRelocatable.hpp b/ld64/src/MachOReaderRelocatable.hpp deleted file mode 100644 index 73c58e2..0000000 --- a/ld64/src/MachOReaderRelocatable.hpp +++ /dev/null @@ -1,4583 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_MACH_O__ -#define __OBJECT_FILE_MACH_O__ - -#include -#include -#include -#include - -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ObjectFile.h" -#include "dwarf2.h" -#include "debugline.h" - - -// -// -// To implement architecture xxx, you must write template specializations for the following six methods: -// Reader::validFile() -// Reader::validSectionType() -// Reader::addRelocReference() -// Reference::getDescription() -// -// - - - -extern __attribute__((noreturn)) void throwf(const char* format, ...); -extern void warning(const char* format, ...); - -namespace mach_o { -namespace relocatable { - - - -class ReferenceSorter -{ -public: - bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right) - { - return ( left->getFixUpOffset() < right->getFixUpOffset() ); - } -}; - - -// forward reference -template class Reader; - -struct AtomAndOffset -{ - AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {} - AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} - ObjectFile::Atom* atom; - uint32_t offset; -}; - - -template -class Reference : public ObjectFile::Reference -{ -public: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - - Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget); - Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget); - Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset); - - virtual ~Reference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const; - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const; - virtual uint8_t getKind() const { return (uint8_t)fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return (fToTargetName != NULL) ? fToTargetName : fToTarget.atom->getName(); } - virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } - virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); } - virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } - virtual const char* getFromTargetName() const { return (fFromTargetName != NULL) ? fFromTargetName : fFromTarget.atom->getName(); } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } - virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } - virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } - virtual void setFromTargetName(const char* name) { fFromTargetName = name; } - virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } - virtual const char* getDescription() const; - virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } - - static bool fgForFinalLinkedImage; - -private: - pint_t fFixUpOffsetInSrc; - AtomAndOffset fToTarget; - AtomAndOffset fFromTarget; - const char* fToTargetName; - const char* fFromTargetName; - Kinds fKind; - -}; - -template bool Reference::fgForFinalLinkedImage = true; - -template -Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) - : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL), - fKind(kind) -{ - // make reference a by-name unless: - // - the reference type is only used with direct references - // - the target is translation unit scoped - // - the target kind is not regular (is weak or tentative) - if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) - && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && (toTarget.atom != at.atom) ) { - fToTargetName = toTarget.atom->getName(); - //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName()); - fToTarget.atom = NULL; - } - ((class BaseAtom*)at.atom)->addReference(this); - //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset); -} - -template -Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget) - : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget), - fToTargetName(NULL), fFromTargetName(NULL), fKind(kind) -{ - // make reference a by-name where needed - if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) - && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && (toTarget.atom != at.atom) ) { - fToTargetName = toTarget.atom->getName(); - fToTarget.atom = NULL; - } - ((class BaseAtom*)at.atom)->addReference(this); - //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind, - // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset); -} - -template -Reference::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset) - : fFixUpOffsetInSrc(at.offset), - fToTargetName(toName), fFromTargetName(NULL), fKind(kind) -{ - fToTarget.offset = toOffset; - ((class BaseAtom*)at.atom)->addReference(this); -} - -template -ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const -{ - if ( fgForFinalLinkedImage ) { - if ( (fKind == A::kDtraceProbe) || (fKind == A::kDtraceProbeSite) || (fKind == A::kDtraceIsEnabledSite) || (fKind == A::kDtraceTypeReference) ) - return ObjectFile::Reference::kDontBind; - } - if ( fToTarget.atom == NULL ) - return ObjectFile::Reference::kUnboundByName; - if ( fToTargetName == NULL ) - return ObjectFile::Reference::kBoundDirectly; - else - return ObjectFile::Reference::kBoundByName; -} - -template -ObjectFile::Reference::TargetBinding Reference::getFromTargetBinding() const -{ - if ( fFromTarget.atom == NULL ) { - if ( fFromTargetName == NULL ) - return ObjectFile::Reference::kDontBind; - else - return ObjectFile::Reference::kUnboundByName; - } - else { - if ( fFromTargetName == NULL ) - return ObjectFile::Reference::kBoundDirectly; - else - return ObjectFile::Reference::kBoundByName; - } -} - - - -template -class Segment : public ObjectFile::Segment -{ -public: - Segment(const macho_section* sect); - virtual const char* getName() const { return fSection->segname(); } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } -private: - const macho_section* fSection; - bool fWritable; - bool fExecutable; -}; - -template -Segment::Segment(const macho_section* sect) - : fSection(sect), fWritable(true), fExecutable(false) -{ - if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { - fWritable = false; - fExecutable = true; - } - else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { - fWritable = true; - fExecutable = true; - } -} - - -class DataSegment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const { return "__DATA"; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return true; } - virtual bool isContentExecutable() const { return false; } - - static DataSegment fgSingleton; -}; - -DataSegment DataSegment::fgSingleton; - -class LinkEditSegment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const { return "__LINKEDIT"; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } - - static LinkEditSegment fgSingleton; -}; - -LinkEditSegment LinkEditSegment::fgSingleton; - -class BaseAtom : public ObjectFile::Atom -{ -public: - BaseAtom() : fStabsStartIndex(0), fStabsCount(0) {} - - virtual void setSize(uint64_t size) = 0; - virtual void addReference(ObjectFile::Reference* ref) = 0; - virtual void sortReferences() = 0; - virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; - virtual uint64_t getObjectAddress() const = 0; - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual void setOrdinal(uint32_t value) { fOrdinal = value; } - virtual const void* getSectionRecord() const = 0; - virtual bool isAlias() const { return false; } - - uint32_t fStabsStartIndex; - uint32_t fStabsCount; - uint32_t fOrdinal; -}; - -class BaseAtomSorter -{ -public: - bool operator()(const class BaseAtom* left, const class BaseAtom* right) { - if ( left == right ) - return false; - uint64_t leftAddr = left->getObjectAddress(); - uint64_t rightAddr = right->getObjectAddress(); - if ( leftAddr < rightAddr ) { - return true; - } - else if ( leftAddr > rightAddr ) { - return false; - } - else { - // if they have same address, one might be the end of a section and the other the start of the next section - const void* leftSection = left->getSectionRecord(); - const void* rightSection = right->getSectionRecord(); - if ( leftSection != rightSection ) { - return ( leftSection < rightSection ); - } - // if they have same address and section, one might be an alias - bool leftAlias = left->isAlias(); - bool rightAlias = right->isAlias(); - if ( leftAlias && rightAlias ) { - // sort multiple aliases for same address first by scope - ObjectFile::Atom::Scope leftScope = left->getScope(); - ObjectFile::Atom::Scope rightScope = right->getScope(); - if ( leftScope != rightScope ) { - return ( leftScope < rightScope ); - } - // sort multiple aliases for same address then by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - } - else if ( leftAlias ) { - return true; - } - else if ( rightAlias ) { - return false; - } - else { - // they must be tentative defintions - switch ( left->getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - // sort tentative definitions by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - case ObjectFile::Atom::kAbsoluteSymbol: - // sort absolute symbols with same address by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - default: - // hack for rdar://problem/5102873 - if ( !left->isZeroFill() || !right->isZeroFill() ) - warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); - break; - } - } - } - return false; - } -}; - - -// -// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry -// pointing to it. A C function or global variable is represented by one of these atoms. -// -// -template -class SymbolAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) - ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } - virtual bool dontDeadStrip() const; - virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } - virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } - virtual uint64_t getSize() const { return fSize; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual Segment& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector* getLineInfo() const { return (std::vector*)&fLineInfo; } - virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size); - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } - virtual uint64_t getObjectAddress() const { return fAddress; } - virtual const void* getSectionRecord() const { return (const void*)fSection; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - typedef typename std::vector*> ReferenceVector; - typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser - typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser - friend class Reader; - - SymbolAtom(Reader&, const macho_nlist

*, const macho_section

*); - virtual ~SymbolAtom() {} - - Reader& fOwner; - const macho_nlist

* fSymbol; - pint_t fAddress; - pint_t fSize; - const macho_section

* fSection; - Segment* fSegment; - ReferenceVector fReferences; - std::vector fLineInfo; - ObjectFile::Atom::Scope fScope; - SymbolTableInclusion fSymbolTableInclusion; - ObjectFile::Alignment fAlignment; -}; - - -template -SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const macho_section

* section) - : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fAlignment(0) -{ - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( (type & N_TYPE) == N_SECT ) { - // real definition - fSegment = new Segment(fSection); - fAddress = fSymbol->n_value(); - pint_t sectionStartAddr = section->addr(); - pint_t sectionEndAddr = section->addr()+section->size(); - if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) { - throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX", - this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(), - (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) ); - } - } - else { - warning("unknown symbol type: %d", type); - } - - //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress); - // support for .o files built with old ld64 - if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) { - const char* name = this->getName(); - const int nameLen = strlen(name); - if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) { - // switch symbol to point at name that does not have trailing $stub - char correctName[nameLen]; - strncpy(correctName, name, nameLen-5); - correctName[nameLen-5] = '\0'; - const macho_nlist

* symbolsStart = fOwner.fSymbols; - const macho_nlist

* symbolsEnd = &symbolsStart[fOwner.fSymbolCount]; - for(const macho_nlist

* s = symbolsStart; s < symbolsEnd; ++s) { - if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) { - fSymbol = s; - break; - } - } - } - } - // support for labeled stubs - switch ( section->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - setSize(section->reserved2()); - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - setSize(sizeof(pint_t)); - break; - case S_4BYTE_LITERALS: - setSize(4); - break; - case S_8BYTE_LITERALS: - setSize(8); - break; - case S_16BYTE_LITERALS: - setSize(16); - break; - case S_CSTRING_LITERALS: - setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); - break; - case S_REGULAR: - case S_ZEROFILL: - case S_COALESCED: - // size calculate later after next atom is found - break; - } - - // compute alignment - fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); - - // compute whether this atom needs to be in symbol table - if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; - } - else if ( fOwner.fOptions.fForFinalLinkedImage - && ((section->flags() & SECTION_TYPE) == S_COALESCED) - && ((section->flags() & S_ATTR_NO_TOC) == S_ATTR_NO_TOC) - && ((section->flags() & S_ATTR_STRIP_STATIC_SYMS) == S_ATTR_STRIP_STATIC_SYMS) - && (strcmp(section->sectname(), "__eh_frame") == 0) ) { - // .eh symbols exist so the linker can associate them with functions - // removing them from final linked images is a big space savings rdar://problem/4180168 - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment - fAlignment = ObjectFile::Alignment(0); - } - else if ( fOwner.fOptions.fForFinalLinkedImage - && ((section->flags() & SECTION_TYPE) == S_REGULAR) - && (strncmp(section->sectname(), "__gcc_except_tab", 16) == 0) - && (strncmp(this->getName(), "GCC_except_table", 16) == 0) ) { - // GCC_except_table* symbols don't need to exist in final linked image - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - } - else if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) { - // labels beginning with a lowercase ell are automatically removed in final linked images - // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - } - else { - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - - // work around malformed icc generated .o files - // if section starts with a symbol and that symbol address does not match section alignment, then force it to - if ( (section->addr() == fAddress) && (fAlignment.modulus != 0) ) - fAlignment.modulus = 0; -} - -template -bool SymbolAtom::dontDeadStrip() const -{ - // the symbol can have a no-dead-strip bit - if ( (fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 ) - return true; - // or the section can have a no-dead-strip bit - return ( fSection->flags() & S_ATTR_NO_DEAD_STRIP ); -} - - -template -const char* SymbolAtom::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage && (strcmp(fSection->sectname(), "__textcoal_nt") == 0) ) - return "__text"; - - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -template -ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const -{ - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return ref->getTarget(); - } - return *((ObjectFile::Atom*)NULL); -} - - -class Beyond -{ -public: - Beyond(uint64_t offset) : fOffset(offset) {} - bool operator()(ObjectFile::Reference* ref) const { - return ( ref->getFixUpOffset() >= fOffset ); - } -private: - uint64_t fOffset; -}; - - -template -void SymbolAtom::setSize(uint64_t size) -{ - // when resizing, any references beyond the new size are tossed - if ( (fSize != 0) && (fReferences.size() > 0) ) - fReferences.erase(std::remove_if(fReferences.begin(), fReferences.end(), Beyond(size)), fReferences.end()); - // set new size - fSize = size; -} - -template -void SymbolAtom::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - -// -// A SymbolAliasAtom represents an alternate name for a SymbolAtom -// -// -template -class SymbolAliasAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fAliasOf.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); } - virtual bool dontDeadStrip() const { return fDontDeadStrip; } - virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); } - virtual bool isThumb() const { return fAliasOf.isThumb(); } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return fAliasOf.getSectionName(); } - virtual Segment& getSegment() const { return (Segment&)fAliasOf.getSegment(); } - virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return fAliasOf.getAlignment(); } - virtual void copyRawContent(uint8_t buffer[]) const {} - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { } - virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } - virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } - virtual bool isAlias() const { return true; } - -protected: - typedef typename A::P P; - typedef typename std::vector*> ReferenceVector; - typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser - typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser - friend class Reader; - - SymbolAliasAtom(const char* name, const macho_nlist

*, const BaseAtom& ); - virtual ~SymbolAliasAtom() {} - - const char* fName; - const BaseAtom& fAliasOf; - ObjectFile::Atom::Scope fScope; - bool fDontDeadStrip; - ReferenceVector fReferences; -}; - - -template -SymbolAliasAtom::SymbolAliasAtom(const char* name, const macho_nlist

* symbol, const BaseAtom& aliasOf) - : fName(name), fAliasOf(aliasOf) -{ - //fprintf(stderr, "SymbolAliasAtom(%p) %s\n", this, name); - if ( symbol != NULL ) { - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - fDontDeadStrip = ((symbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); - } - else { - // aliases defined on the command line are initially global scope - fScope = ObjectFile::Atom::scopeGlobal; - fDontDeadStrip = false; - } - // add follow-on reference to real atom - new Reference(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf)); -} - - -// -// A TentativeAtom represents a C "common" or "tentative" defintion of data. -// For instance, "int foo;" is neither a declaration or a definition and -// is represented by a TentativeAtom. -// -template -class TentativeAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } - virtual bool isZeroFill() const { return true; } - virtual bool isThumb() const { return false; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) - ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } - virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } - virtual uint64_t getSize() const { return fSymbol->n_value(); } - virtual std::vector& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } - virtual const void* getSectionRecord() const { return NULL; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader; - - TentativeAtom(Reader&, const macho_nlist

*); - virtual ~TentativeAtom() {} - - Reader& fOwner; - const macho_nlist

* fSymbol; - ObjectFile::Atom::Scope fScope; - static std::vector fgNoReferences; -}; - -template -std::vector TentativeAtom::fgNoReferences; - -template -TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) - : fOwner(owner), fSymbol(symbol) -{ - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { - // tentative definition - } - else { - warning("unknown symbol type: %d", type); - } - //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName()); -} - - -template -ObjectFile::Alignment TentativeAtom::getAlignment() const -{ - uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc()); - if ( alignment == 0 ) { - // common symbols align to their size - // that is, a 4-byte common aligns to 4-bytes - // if this size is not a power of two, - // then round up to the next power of two - uint64_t size = this->getSize(); - alignment = 63 - (uint8_t)__builtin_clzll(size); - if ( size != (1ULL << alignment) ) - ++alignment; - } - // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignment < 12 ) - return ObjectFile::Alignment(alignment); - else - return ObjectFile::Alignment(12); -} - -template -const char* TentativeAtom::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal ) - return "__common"; - else - return "._tentdef"; -} - - -template -void TentativeAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - -// -// An AnonymousAtom represents compiler generated data that has no name. -// For instance, a literal C-string or a 64-bit floating point constant -// is represented by an AnonymousAtom. -// -template -class AnonymousAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fSynthesizedName; } - virtual const char* getDisplayName() const; - virtual ObjectFile::Atom::Scope getScope() const; - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } - virtual bool dontDeadStrip() const { return fDontDeadStrip; } - virtual bool isZeroFill() const; - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return fSize; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual Segment& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { fSize = size; } - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); } - virtual uint64_t getObjectAddress() const { return fAddress; } - virtual const void* getSectionRecord() const { return (const void*)fSection; } - BaseAtom* redirectTo() { return fRedirect; } - bool isWeakImportStub() { return fWeakImportStub; } - void resolveName(); - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - typedef typename std::vector*> ReferenceVector; - typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser - typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser - friend class Reader; - - AnonymousAtom(Reader&, const macho_section

*, pint_t addr, pint_t size); - virtual ~AnonymousAtom() {} - static bool cstringsHaveLabels(); - - Reader& fOwner; - const char* fSynthesizedName; - const char* fDisplayName; - const macho_section

* fSection; - pint_t fAddress; - pint_t fSize; - Segment* fSegment; - ReferenceVector fReferences; - BaseAtom* fRedirect; - bool fDontDeadStrip; - bool fWeakImportStub; - ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; - ObjectFile::Atom::Scope fScope; - ObjectFile::Atom::DefinitionKind fKind; -}; - -template -AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, pint_t addr, pint_t size) - : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), - fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), - fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition) -{ - fSegment = new Segment(fSection); - fRedirect = this; - uint8_t type = fSection->flags() & SECTION_TYPE; - //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath()); - switch ( type ) { - case S_ZEROFILL: - { - asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr); - } - break; - case S_COALESCED: - case S_REGULAR: - if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { - // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only - fSynthesizedName = ".objc_class_name_PENDING"; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - if ( fOwner.fOptions.fForFinalLinkedImage ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - else - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAsAbsolute; - fScope = ObjectFile::Atom::scopeGlobal; - } - else if ( strcmp(fSection->sectname(), "__cstring") == 0 ) { - // handle .o files created by old ld64 -r that are missing cstring section type - const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - asprintf((char**)&fSynthesizedName, "cstring=%s", str); - } - else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) { - fSynthesizedName = "cfstring-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - fDontDeadStrip = false; - fKind = ObjectFile::Atom::kWeakDefinition; - } - break; - case S_CSTRING_LITERALS: - { - const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - asprintf((char**)&fSynthesizedName, "cstring=%s", str); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - break; - case S_4BYTE_LITERALS: - { - uint32_t value = E::get32(*(uint32_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - asprintf((char**)&fSynthesizedName, "4-byte-literal=0x%08X", value); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_8BYTE_LITERALS: - { - uint64_t value = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - asprintf((char**)&fSynthesizedName, "8-byte-literal=0x%016llX", value); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_16BYTE_LITERALS: - { - uint64_t value1 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - uint64_t value2 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 8 - section->addr())); - asprintf((char**)&fSynthesizedName, "16-byte-literal=0x%016llX,%016llX", value1, value2); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_LITERAL_POINTERS: - { - //uint32_t literalNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - //const char* str = (char*)(owner.fHeader) + section->offset() + literalNameAddr - section->addr(); - //asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", section->segname(), section->sectname(), str); - fSynthesizedName = "literal-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - } - break; - case S_MOD_INIT_FUNC_POINTERS: - asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); - break; - case S_MOD_TERM_FUNC_POINTERS: - asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); - break; - case S_SYMBOL_STUBS: - { - uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2(); - index += fSection->reserved1(); - uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); - const macho_nlist

* sym = &fOwner.fSymbols[symbolIndex]; - uint32_t strOffset = sym->n_strx(); - // want name to not have $stub suffix, this is what automatic stub generation expects - fSynthesizedName = &fOwner.fStrings[strOffset]; - // check for weak import - fWeakImportStub = fOwner.isWeakImportSymbol(sym); - // sometimes the compiler gets confused and generates a stub to a static function - // if so, we should redirect any call to the stub to be calls to the real static function atom - if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) { - BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); - if ( staticAtom != NULL ) - fRedirect = staticAtom; - } - fKind = ObjectFile::Atom::kWeakDefinition; - // might be a spurious stub for a static function, make stub static too - if ( (sym->n_type() & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else - fScope = ObjectFile::Atom::scopeLinkageUnit; - } - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - { - fDontDeadStrip = false; - fScope = ObjectFile::Atom::scopeLinkageUnit; - uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); - index += fSection->reserved1(); - uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); - if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { - // Silly codegen with non-lazy pointer to a local symbol - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fOwner.fHeader)+fileOffset))); - // All atoms not created yet, so we need to scan symbol table - const macho_nlist

* closestSym = NULL; - const macho_nlist

* end = &fOwner.fSymbols[fOwner.fSymbolCount]; - for (const macho_nlist

* sym = fOwner.fSymbols; sym < end; ++sym) { - if ( ((sym->n_type() & N_TYPE) == N_SECT) - && ((sym->n_type() & N_STAB) == 0) ) { - if ( sym->n_value() == nonLazyPtrValue ) { - const char* name = &fOwner.fStrings[sym->n_strx()]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - strcat(str, "$non_lazy_ptr"); - fSynthesizedName = str; - // add direct reference to target later, because its atom may not be constructed yet - fOwner.fLocalNonLazys.push_back(this); - fScope = ObjectFile::Atom::scopeTranslationUnit; - return; - } - else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) { - closestSym = sym; - } - } - } - // add direct reference to target later, because its atom may not be constructed yet - if ( closestSym != NULL ) { - const char* name = &fOwner.fStrings[closestSym->n_strx()]; - char* str; - asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value()); - fSynthesizedName = str; - } - else { - fSynthesizedName = "$interior$non_lazy_ptr"; - } - fScope = ObjectFile::Atom::scopeTranslationUnit; - fOwner.fLocalNonLazys.push_back(this); - return; - } - const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; - const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - if ( type == S_LAZY_SYMBOL_POINTERS ) - strcat(str, "$lazy_ptr"); - else - strcat(str, "$non_lazy_ptr"); - fSynthesizedName = str; - - // optimize __IMPORT segment out of i386 dyld or if -slow_stubs is used - if ( (fOwner.fOptions.fForDyld || fOwner.fOptions.fSlowx86Stubs) && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { - macho_section

* dummySection = new macho_section

(*fSection); - dummySection->set_segname("__DATA"); - dummySection->set_sectname("__nl_symbol_ptr"); - fSection = dummySection; - fSegment = new Segment(fSection); - } - - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) - fKind = ObjectFile::Atom::kWeakDefinition; - - if ( (targetSymbol->n_type() & N_EXT) == 0 ) { - // target is translation unit scoped, so add direct reference to target - //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); - new Reference(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value())); - } - else { - if ( fOwner.isWeakImportSymbol(targetSymbol) ) - new Reference(A::kPointerWeakImport, AtomAndOffset(this), name, 0); - else - new Reference(A::kPointer, AtomAndOffset(this), name, 0); - } - } - break; - default: - throwf("section type %d not supported with address=0x%08X", type, addr); - } - //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); -} - -// x86_64 uses L labels on cstrings to allow relocs with addends -template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } -template bool AnonymousAtom::cstringsHaveLabels() { return false; } - - -template -void AnonymousAtom::resolveName() -{ - if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) { - std::vector& references = this->getReferences(); - // references are not yet sorted, so scan the vector - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* superStr = (*rit)->getTargetName(); - if ( strncmp(superStr, "cstring=", 8) == 0 ) { - const char* superClassName; - asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]); - new Reference(A::kNoFixUp, AtomAndOffset(this), superClassName, 0); - } - break; - } - } - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* classStr = (*rit)->getTargetName(); - if ( strncmp(classStr, "cstring=", 8) == 0 ) { - asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]); - } - break; - } - } - } - else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) { - std::vector& references = this->getReferences(); - if ( references.size() < 1 ) - throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname()); - ObjectFile::Reference* ref = references[0]; - const char* str = ref->getTargetName(); - if ( strncmp(str, "cstring=", 8) == 0 ) { - asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]); - } - } - else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - // references are not yet sorted, so scan the vector - std::vector& references = this->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* superStr = (*rit)->getTargetName(); - if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) { - asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]); - } - else { - // compiled with -fwritable-strings or a non-ASCII string - ObjectFile::Atom& stringDataAtom = (*rit)->getTarget(); - uint8_t buffer[stringDataAtom.getSize()]; - stringDataAtom.copyRawContent(buffer); - fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable - fScope = ObjectFile::Atom::scopeTranslationUnit; - fSynthesizedName = "cfstring-not-coalesable"; - } - break; - } - } - } -} - - -template -const char* AnonymousAtom::getDisplayName() const -{ - if ( fSynthesizedName != NULL ) - return fSynthesizedName; - - if ( fDisplayName != NULL ) - return fDisplayName; - - if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); - } - else { - asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); - } - return fDisplayName; -} - - -template -ObjectFile::Atom::Scope AnonymousAtom::getScope() const -{ - return fScope; -} - - -template -bool AnonymousAtom::isZeroFill() const -{ - return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL ); -} - - -template -const char* AnonymousAtom::getSectionName() const -{ - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -template -ObjectFile::Alignment AnonymousAtom::getAlignment() const -{ - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - return ObjectFile::Alignment(2); - case S_8BYTE_LITERALS: - return ObjectFile::Alignment(3); - case S_16BYTE_LITERALS: - return ObjectFile::Alignment(4); - case S_NON_LAZY_SYMBOL_POINTERS: - return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t))); - case S_CSTRING_LITERALS: - if ( ! fOwner.fOptions.fForFinalLinkedImage ) - return ObjectFile::Alignment(fSection->align()); - default: - return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); - } -} - - -template -ObjectFile::Atom& AnonymousAtom::getFollowOnAtom() const -{ - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return ref->getTarget(); - } - return *((ObjectFile::Atom*)NULL); -} - -template -void AnonymousAtom::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - - -// -// An AbsoluteAtom represents an N_ABS symbol which can only be created in -// assembly language and usable by static executables such as the kernel/ -// -template -class AbsoluteAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; } - virtual bool dontDeadStrip() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return "._absolute"; } - virtual ObjectFile::Segment& getSegment() const { return LinkEditSegment::fgSingleton; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } - virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } - virtual const void* getSectionRecord() const { return NULL; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader; - - AbsoluteAtom(Reader&, const macho_nlist

*); - virtual ~AbsoluteAtom() {} - - Reader& fOwner; - const macho_nlist

* fSymbol; - ObjectFile::Atom::Scope fScope; - static std::vector fgNoReferences; -}; - -template -std::vector AbsoluteAtom::fgNoReferences; - -template -AbsoluteAtom::AbsoluteAtom(Reader& owner, const macho_nlist

* symbol) - : fOwner(owner), fSymbol(symbol) -{ - // store absolute adress in fSectionOffset - fSectionOffset = symbol->n_value(); - // compute scope - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName()); -} - - - -template -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent); - Reader(const uint8_t* fileContent, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return fModTime; } - virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; } - virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabs() { return &fStabs; } - virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; } - virtual uint32_t updateCpuConstraint(uint32_t current); - virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); } - virtual bool objcReplacementClasses(){ return fReplacementClasses; } - virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; } - - bool getTranslationUnitSource(const char** dir, const char** name) const; - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - //typedef typename std::vector*> AtomVector; - //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser - typedef typename A::ReferenceKinds Kinds; - friend class AnonymousAtom; - friend class TentativeAtom; - friend class AbsoluteAtom; - friend class SymbolAtom; - typedef std::map AddrToAtomMap; - - void addReferencesForSection(const macho_section

* sect); - bool addRelocReference(const macho_section

* sect, const macho_relocation_info

* reloc); - bool addRelocReference_powerpc(const macho_section

* sect, const macho_relocation_info

* reloc); - bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); - static bool isWeakImportSymbol(const macho_nlist

* sym); - static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); - static const char* assureFullPath(const char* path); - AtomAndOffset findAtomAndOffset(pint_t addr); - AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr); - Reference* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr); - Reference* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr); - Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr); - Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr); - Reference* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset); - Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); - Reference* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset); - void validSectionType(uint8_t type); - void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName); - void setCpuConstraint(uint32_t cpusubtype); - - BaseAtom* findAtomByName(const char*); - - const char* fPath; - time_t fModTime; - uint32_t fOrdinalBase; - const ObjectFile::ReaderOptions& fOptions; - const macho_header

* fHeader; - const char* fStrings; - const macho_nlist

* fSymbols; - uint32_t fSymbolCount; - const macho_segment_command

* fSegment; - const uint32_t* fIndirectTable; - std::vector fAtoms; - AddrToAtomMap fAddrToAtom; - AddrToAtomMap fAddrToAbsoluteAtom; - std::vector*> fLocalNonLazys; - std::vector*> fAtomsPendingAName; - std::set*> fSectionsWithAtomsPendingAName; - std::vector fDtraceProviderInfo; - ObjectFile::Reader::DebugInfoKind fDebugInfo; - bool fHasUUID; - const macho_section

* fDwarfDebugInfoSect; - const macho_section

* fDwarfDebugAbbrevSect; - const macho_section

* fDwarfDebugLineSect; - const char* fDwarfTranslationUnitDir; - const char* fDwarfTranslationUnitFile; - std::map fDwarfIndexToFile; - std::vector fStabs; - bool fAppleObjc; - bool fHasDTraceProbes; - bool fHaveIndirectSymbols; - bool fReplacementClasses; - bool fHasLongBranchStubs; - ObjectFile::Reader::ObjcConstraint fObjConstraint; - uint32_t fCpuConstraint; -}; - -template -Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header

*)fileContent), - fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), - fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), - fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), - fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), - fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny) -{ - // sanity check - if ( ! validFile(fileContent) ) - throw "not a valid mach-o object file"; - - Reference::fgForFinalLinkedImage = options.fForFinalLinkedImage; - - // write out path for -t or -whatsloaded option - if ( options.fLogObjectFiles || options.fLogAllFiles ) - printf("%s\n", path); - - // cache intersting pointers - const macho_header

* header = (const macho_header

*)fileContent; - this->setCpuConstraint(header->cpusubtype()); - const uint32_t cmd_count = header->ncmds(); - const macho_load_command

* const cmds = (macho_load_command

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

)); - const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; - uint32_t undefinedStartIndex = 0; - uint32_t undefinedEndIndex = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - fSymbolCount = symtab->nsyms(); - fSymbols = (const macho_nlist

*)((char*)header + symtab->symoff()); - fStrings = (char*)header + symtab->stroff(); - if ( undefinedEndIndex == 0 ) { - undefinedStartIndex = 0; - undefinedEndIndex = symtab->nsyms(); - } - } - break; - case LC_DYSYMTAB: - { - const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; - fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); - undefinedStartIndex = dsymtab->iundefsym(); - undefinedEndIndex = undefinedStartIndex + dsymtab->nundefsym(); - } - break; - case LC_UUID: - fHasUUID = true; - break; - - default: - if ( cmd->cmd() == macho_segment_command

::CMD ) { - fSegment = (macho_segment_command

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

*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - - // if there are no load commands, then this file has no content, so no atoms - if ( header->ncmds() < 1 ) - return; - - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[fSegment->nsects()]; - - // inital guess for number of atoms - fAtoms.reserve(fSymbolCount); - - // add all atoms that have entries in symbol table - const macho_section

* sections = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - for (int i=fSymbolCount-1; i >= 0 ; --i) { - // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the reaal name - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - uint8_t type = (sym.n_type() & N_TYPE); - if ( type == N_SECT ) { - const macho_section

* section = §ions[sym.n_sect()-1]; - pint_t sectionEndAddr = section->addr() + section->size(); - bool suppress = false; - // ignore atoms in debugger sections - if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { - if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) { - // ignore dtrace probe labels - fHasDTraceProbes = true; - } - else if ( fStrings[sym.n_strx()] == 'L' ) { - // ignore L labels, - } - else { - // ignore labels for atoms in other sections - switch ( section->flags() & SECTION_TYPE ) { - case S_REGULAR: - if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 ) - suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS - case S_ZEROFILL: - case S_COALESCED: - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - case S_CSTRING_LITERALS: - { - BaseAtom* newAtom; - typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value()); - if ( (pos != fAddrToAtom.end()) && (strcmp(pos->second->getSectionName(), section->sectname())==0) ) { - // another label to an existing address in the same section, make this an alias - newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *pos->second); - } - else { - // make SymbolAtom atom for this address - newAtom = new SymbolAtom(*this, &sym, section); - // don't add symbols at end of section to addr->atom map - if ( sym.n_value() != sectionEndAddr ) - fAddrToAtom[newAtom->getObjectAddress()] = newAtom; - } - if ( ! suppress ) - fAtoms.push_back(newAtom); - } - break; - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - // ignore symboled stubs produces by old ld64 - break; - default: - warning("symbol %s found in unsupported section in %s", - &fStrings[sym.n_strx()], this->getPath()); - } - } - } - } - else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { - fAtoms.push_back(new TentativeAtom(*this, &sym)); - } - else if ( type == N_ABS ) { - const char* symName = &fStrings[sym.n_strx()]; - if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) { - // ignore .objc_class_name_* symbols - fAppleObjc = true; - } - else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) { - // ignore empty *.eh symbols - } - else { - BaseAtom* abAtom = new AbsoluteAtom(*this, &sym); - fAtoms.push_back(abAtom); - fAddrToAbsoluteAtom[sym.n_value()] = abAtom; - } - } - else if ( type == N_INDR ) { - fHaveIndirectSymbols = true; - } - } - } - - // add all fixed size anonymous atoms from special sections - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - pint_t atomSize = 0; - uint8_t type (sect->flags() & SECTION_TYPE); - validSectionType(type); - bool suppress = false; - switch ( type ) { - case S_SYMBOL_STUBS: - suppress = true; - atomSize = sect->reserved2(); - break; - case S_LAZY_SYMBOL_POINTERS: - suppress = true; - atomSize = sizeof(pint_t); - break; - case S_NON_LAZY_SYMBOL_POINTERS: - case S_LITERAL_POINTERS: - case S_MOD_INIT_FUNC_POINTERS: - case S_MOD_TERM_FUNC_POINTERS: - atomSize = sizeof(pint_t); - break; - case S_INTERPOSING: - atomSize = sizeof(pint_t)*2; - break; - case S_4BYTE_LITERALS: - atomSize = 4; - break; - case S_8BYTE_LITERALS: - atomSize = 8; - break; - case S_16BYTE_LITERALS: - atomSize = 16; - break; - case S_REGULAR: - // special case ObjC classes to synthesize .objc_class_name_* symbols - if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) && fAppleObjc ) { - // gcc sometimes over aligns class structure - uint32_t align = 1 << sect->align(); - atomSize = ((12 * sizeof(pint_t)) + align-1) & (-align); - } - // get objc Garbage Collection info - else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) - || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset()); - if ( (sect->size() >= 8) && (contents[0] == 0) ) { - uint32_t flags = E::get32(contents[1]); - if ( (flags & 4) == 4 ) - fObjConstraint = ObjectFile::Reader::kObjcGC; - else if ( (flags & 2) == 2 ) - fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - else - fObjConstraint = ObjectFile::Reader::kObjcRetainRelease; - if ( (flags & 1) == 1 ) - fReplacementClasses = true; - // don't make atom for this section - atomSize = sect->size(); - suppress = true; - } - else { - warning("can't parse __OBJC/__image_info section in %s", fPath); - } - } - // special case constant NS/CFString literals and make an atom out of each one - else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { - atomSize = 4 * sizeof(pint_t); - } - break; - } - if ( atomSize != 0 ) { - for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { - pint_t atomAddr = sect->addr() + sectOffset; - // add if not already an atom at that address - if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) { - AnonymousAtom* newAtom = new AnonymousAtom(*this, sect, atomAddr, atomSize); - if ( !suppress ) - fAtoms.push_back(newAtom); - fAddrToAtom[atomAddr] = newAtom->redirectTo(); - } - } - } - } - - // add all c-string anonymous atoms - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { - uint32_t stringLen; - pint_t stringAddr; - BaseAtom* mostAlignedEmptyString = NULL; - uint32_t mostAlignedEmptyStringTrailingZeros = 0; - std::vector > emptyStrings; - for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { - stringAddr = sect->addr() + sectOffset; - stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; - // add if not already an atom at that address - if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) { - BaseAtom* newAtom = new AnonymousAtom(*this, sect, stringAddr, stringLen); - if ( stringLen == 1 ) { - // because of padding it may look like there are lots of empty strings, keep track of all - emptyStrings.push_back(std::make_pair(stringAddr, newAtom)); - // record empty string with greatest alignment requirement - uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr); - if ( (mostAlignedEmptyString == NULL) - || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) { - mostAlignedEmptyString = newAtom; - mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros; - } - } - else { - fAtoms.push_back(newAtom); - fAddrToAtom[stringAddr] = newAtom; - } - } - } - // map all uses of empty strings to the most aligned one - if ( mostAlignedEmptyString != NULL ) { - // make most aligned atom a real atom - fAtoms.push_back(mostAlignedEmptyString); - // map all other empty atoms to this one - for (typename std::vector >::iterator it=emptyStrings.begin(); it != emptyStrings.end(); it++) { - fAddrToAtom[it->first] = mostAlignedEmptyString; - } - } - } - } - - // sort all atoms so far by address and section - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - - //fprintf(stderr, "sorted atoms:\n"); - //for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) - // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName()); - - // create atoms to cover any non-debug ranges not handled above - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - pint_t sectionStartAddr = sect->addr(); - pint_t sectionEndAddr = sect->addr() + sect->size(); - const bool setFollowOnAtom = ! this->canScatterAtoms(); - if ( sect->size() != 0 ) { - // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change - if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { - fDebugInfo = kDebugInfoDwarf; - if ( strcmp(sect->sectname(), "__debug_info") == 0 ) - fDwarfDebugInfoSect = sect; - else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) - fDwarfDebugAbbrevSect = sect; - else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) - fDwarfDebugLineSect = sect; - } - else { - if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { - throw "object file contains old DWARF debug info - rebuild with newer compiler"; - } - uint8_t type (sect->flags() & SECTION_TYPE); - switch ( type ) { - case S_REGULAR: - case S_ZEROFILL: - case S_COALESCED: - // if there is not an atom already at the start of this section, add an anonymous one - pint_t previousAtomAddr = 0; - BaseAtom* previousAtom = NULL; - if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { - BaseAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr(), 0); - fAddrToAtom[sect->addr()] = newAtom; - fAtoms.push_back(newAtom); - previousAtomAddr = sectionStartAddr; - previousAtom = newAtom; - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - } - // calculate size of all atoms in this section and add follow-on references - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - pint_t atomAddr = atom->getObjectAddress(); - if ( atom->getSectionRecord() == sect ) { - //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName()); - if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) { - previousAtom->setSize(atomAddr - previousAtomAddr); - if ( setFollowOnAtom && (atom != previousAtom) ) - new Reference(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom)); - } - previousAtomAddr = atomAddr; - previousAtom = atom; - } - } - if ( previousAtom != NULL ) { - // set last atom in section - previousAtom->setSize(sectionEndAddr - previousAtomAddr); - } - break; - } - } - } - } - - // check for object file that defines no objc classes, but uses objc classes - // check for dtrace provider info - for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_UNDF ) { - const char* undefinedName = &fStrings[sym.n_strx()]; - if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) { - fAppleObjc = true; - } - else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) { - if ( strchr(undefinedName, '$') != NULL ) { - if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) { - // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* - // is extra provider info - fDtraceProviderInfo.push_back(undefinedName); - } - } - } - } - } - } - - // add relocation based references to sections that have atoms with pending names - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( fSectionsWithAtomsPendingAName.count(sect) != 0 ) - addReferencesForSection(sect); - } - - // update any anonymous atoms that need references built in order to name themselves - for (typename std::vector*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) { - (*it)->resolveName(); - } - - // add relocation based references to other sections - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( fSectionsWithAtomsPendingAName.count(sect) == 0 ) - addReferencesForSection(sect); - } - - // add objective-c references - if ( fAppleObjc ) { - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { - for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { - AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset); - ObjectFile::Reference* classRef = ao.atom->getReferences()[0]; - if ( classRef->getFixUpOffset() == 0 ) { - const char* classStr = classRef->getTargetName(); - if ( strncmp(classStr, "cstring=", 8) == 0 ) { - const char* className; - asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]); - new Reference(A::kNoFixUp, ao, className, 0); - } - } - } - } - } - } - - // add direct references to local non-lazy-pointers, can do this now that all atoms are constructed - for (typename std::vector*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) { - AnonymousAtom* localNonLazy = *it; - uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress; - pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset))); - makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); - } - - // add implicit direct reference from each C++ function to its eh info - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( ((sect->flags() & SECTION_TYPE) == S_COALESCED) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { - for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - // note: this algorithm depens on the map iterator returning entries in address order - if ( (it->first >= sect->addr()) && (it->first < sect->addr()+sect->size()) ) { - pint_t ehAtomAddress = it->first; - BaseAtom* ehAtom = it->second; - const char* ehName = ehAtom->getName(); - if ( (ehName != NULL) && (strcmp(&ehName[strlen(ehName)-3], ".eh") == 0) ) { - makeReferenceToEH(ehName, ehAtomAddress, sect); - // make EH symbol static so linker does not try to coalesce - if ( fOptions.fForFinalLinkedImage ) - ehAtom->setScope(ObjectFile::Atom::scopeTranslationUnit); - // if it has a reference to a LSDA, add a group reference - std::vector& ehrefs = ehAtom->getReferences(); - // all FDE's have at least 2 references (to CIE and to function) - if ( ehrefs.size() > 2 ) { - // a third reference means there is a LSDA - ObjectFile::Atom* lsdaAtom = NULL; - for (std::vector::iterator rit=ehrefs.begin(); rit != ehrefs.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getFixUpOffset() ) { - case 4: - case 8: - // these are CIE and function references - break; - default: - // this is LSDA reference - lsdaAtom = &ref->getTarget(); - } - } - if ( lsdaAtom != NULL ) { - new Reference(A::kGroupSubordinate, AtomAndOffset(ehAtom), AtomAndOffset(lsdaAtom)); - } - } - } - } - } - } - } - - // add command line aliases - for(std::vector::const_iterator it = fOptions.fAliases.begin(); it != fOptions.fAliases.end(); ++it) { - BaseAtom* target = this->findAtomByName(it->realName); - if ( (target != NULL) && target->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn ) - fAtoms.push_back(new SymbolAliasAtom(it->alias, NULL, *target)); - } - - // add dtrace probe locations - if ( fHasDTraceProbes ) { - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_SECT ) { - const char* symbolName = &fStrings[sym.n_strx()]; - if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) { - //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName); - makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0); - } - } - } - } - } - - // turn indirect symbols int SymbolAliasAtom - if ( fHaveIndirectSymbols ) { - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_INDR ) { - const char* aliasName = &fStrings[sym.n_strx()]; - const char* targetName = &fStrings[sym.n_value()]; - //fprintf(stderr, "found alias %s for %s\n", aliasName, targetName); - BaseAtom* target = this->findAtomByName(targetName); - // only currently support N_INDR based aliases to something in the same .o file - if ( target != NULL ) { - fAtoms.push_back(new SymbolAliasAtom(aliasName, &sym, *target)); - //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName); - } - } - } - } - } - - //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName()); - //} - - // add translation unit info from dwarf - uint64_t stmtList; - if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // compiler sometimes emits emtpty dwarf sections when there is no debug info, skip those - if ( (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { - if ( !read_comp_unit(&fDwarfTranslationUnitFile, &fDwarfTranslationUnitDir, &stmtList) ) { - // if can't parse dwarf, warn and give up - fDwarfTranslationUnitFile = NULL; - fDwarfTranslationUnitDir = NULL; - warning("can't parse dwarf compilation unit info in %s", this->getPath()); - fDebugInfo = kDebugInfoNone; - } - } - } - - // add line number info to atoms from dwarf - if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // file with just data will have no __debug_line info - if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0) - && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { - // validate stmt_list - if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) { - const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset(); - if ( debug_line != NULL ) { - struct line_reader_data* lines = line_open(&debug_line[stmtList], - fDwarfDebugLineSect->size() - stmtList, E::little_endian); - struct line_info result; - ObjectFile::Atom* curAtom = NULL; - uint32_t curAtomOffset = 0; - uint32_t curAtomAddress = 0; - uint32_t curAtomSize = 0; - while ( line_next (lines, &result, line_stop_pc) ) { - //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", - // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); - // work around weird debug line table compiler generates if no functions in __text section - if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) - continue; - // for performance, see if in next pc is in current atom - if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - // or pc at end of current atom - else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - else { - // do slow look up of atom by address - AtomAndOffset ao = this->findAtomAndOffset(result.pc); - curAtom = ao.atom; - if ( curAtom == NULL ) - break; // file has line info but no functions - if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { - // a one line function can be returned by line_next() as one entry with pc at end of blob - // look for alt atom starting at end of previous atom - uint32_t previousEnd = curAtomAddress+curAtomSize; - AtomAndOffset alt = this->findAtomAndOffset(previousEnd); - if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { - curAtom = alt.atom; - curAtomOffset = alt.offset; - curAtomAddress = previousEnd - alt.offset; - curAtomSize = curAtom->getSize(); - } - else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); - } - } - else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); - } - } - const char* filename; - std::map::iterator pos = fDwarfIndexToFile.find(result.file); - if ( pos == fDwarfIndexToFile.end() ) { - filename = line_file(lines, result.file); - fDwarfIndexToFile[result.file] = filename; - } - else { - filename = pos->second; - } - ObjectFile::LineInfo info; - info.atomOffset = curAtomOffset; - info.fileName = filename; - info.lineNumber = result.line; - //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", - // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); - ((BaseAtom*)curAtom)->addLineInfo(info); - if ( result.end_of_sequence ) { - curAtom = NULL; - } - } - line_free(lines); - } - else { - warning("could not parse dwarf line number info in %s", this->getPath()); - } - } - } - } - - // if no dwarf, try processing stabs debugging info - if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // scan symbol table for stabs entries - fStabs.reserve(fSymbolCount); // reduce re-allocations - BaseAtom* currentAtom = NULL; - pint_t currentAtomAddress = 0; - enum { start, inBeginEnd, inFun } state = start; - for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { - const macho_nlist

* sym = &fSymbols[symbolIndex]; - bool useStab = true; - uint8_t type = sym->n_type(); - const char* symString = (sym->n_strx() != 0) ? &fStrings[sym->n_strx()] : NULL; - if ( (type & N_STAB) != 0 ) { - fDebugInfo = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs); - Stab stab; - stab.atom = NULL; - stab.type = type; - stab.other = sym->n_sect(); - stab.desc = sym->n_desc(); - stab.value = sym->n_value(); - stab.string = NULL; - switch (state) { - case start: - switch (type) { - case N_BNSYM: - // beginning of function block - state = inBeginEnd; - // fall into case to lookup atom by addresss - case N_LCSYM: - case N_STSYM: - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", - (uint64_t)sym->n_value(), path); - } - break; - case N_SO: - case N_OSO: - case N_OPT: - case N_LSYM: - case N_RSYM: - case N_PSYM: - // not associated with an atom, just copy - stab.string = symString; - break; - case N_GSYM: - { - // n_value field is NOT atom address ;-( - // need to find atom by name match - const char* colon = strchr(symString, ':'); - if ( colon != NULL ) { - // build underscore leading name - int nameLen = colon - symString; - char symName[nameLen+2]; - strlcpy(&symName[1], symString, nameLen+1); - symName[0] = '_'; - symName[nameLen+1] = '\0'; - currentAtom = findAtomByName(symName); - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - } - else { - // might be a debug-note without trailing :G() - currentAtom = findAtomByName(symString); - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - } - if ( stab.atom == NULL ) { - warning("can't find atom for N_GSYM stabs %s in %s", symString, path); - useStab = false; - } - break; - } - case N_FUN: - // old style stabs without BNSYM - state = inFun; - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs FUN at %08llX in %s", - (uint64_t)currentAtomAddress, path); - } - break; - case N_SOL: - case N_SLINE: - stab.string = symString; - // old stabs - break; - case N_BINCL: - case N_EINCL: - case N_EXCL: - stab.string = symString; - // -gfull built .o file - break; - default: - warning("unknown stabs type 0x%X in %s", type, path); - } - break; - case inBeginEnd: - stab.atom = currentAtom; - switch (type) { - case N_ENSYM: - state = start; - currentAtom = NULL; - break; - case N_LCSYM: - case N_STSYM: - { - BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; - if ( nestedAtom != NULL ) { - stab.atom = nestedAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs 0x%X at %08llX in %s", - type, (uint64_t)sym->n_value(), path); - } - break; - } - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - // adjust value to be offset in atom - stab.value -= currentAtomAddress; - default: - stab.string = symString; - break; - } - break; - case inFun: - switch (type) { - case N_FUN: - if ( sym->n_sect() != 0 ) { - // found another start stab, must be really old stabs... - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs FUN at %08llX in %s", - (uint64_t)currentAtomAddress, path); - } - } - else { - // found ending stab, switch back to start state - stab.string = symString; - stab.atom = currentAtom; - state = start; - currentAtom = NULL; - } - break; - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - // adjust value to be offset in atom - stab.value -= currentAtomAddress; - stab.atom = currentAtom; - break; - case N_SO: - stab.string = symString; - state = start; - break; - default: - stab.atom = currentAtom; - stab.string = symString; - break; - } - break; - } - // add to list of stabs for this .o file - if ( useStab ) - fStabs.push_back(stab); - } - } - } - -#if 0 - // special case precompiled header .o file (which has no content) to have one empty atom - if ( fAtoms.size() == 0 ) { - int pathLen = strlen(path); - if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) { - ObjectFile::Atom* phony = new AnonymousAtom(*this, (uint32_t)0); - //phony->fSynthesizedName = ".gch.o"; - fAtoms.push_back(phony); - } - } -#endif - - // sort all atoms by address - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - - // set ordinal and sort references in each atom - uint32_t index = fOrdinalBase; - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - atom->setOrdinal(index++); - atom->sortReferences(); - } - -} - - -template <> -void Reader::setCpuConstraint(uint32_t cpusubtype) -{ - switch (cpusubtype) { - case CPU_SUBTYPE_POWERPC_ALL: - case CPU_SUBTYPE_POWERPC_750: - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - case CPU_SUBTYPE_POWERPC_970: - fCpuConstraint = cpusubtype; - break; - default: - warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); - fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL; - break; - } -} - -template <> -void Reader::setCpuConstraint(uint32_t cpusubtype) -{ - switch (cpusubtype) { - case CPU_SUBTYPE_ARM_ALL: - case CPU_SUBTYPE_ARM_V4T: - case CPU_SUBTYPE_ARM_V5TEJ: - case CPU_SUBTYPE_ARM_V6: - case CPU_SUBTYPE_ARM_XSCALE: - case CPU_SUBTYPE_ARM_V7: - fCpuConstraint = cpusubtype; - break; - default: - warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); - fCpuConstraint = CPU_SUBTYPE_ARM_ALL; - break; - } -} - -template -void Reader::setCpuConstraint(uint32_t cpusubtype) -{ - // no cpu sub types for this architecture -} - -template <> -uint32_t Reader::updateCpuConstraint(uint32_t previous) -{ - switch ( previous ) { - case CPU_SUBTYPE_POWERPC_ALL: - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_750: - if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 || - fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 || - fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_970: - // G5 can run everything - break; - default: - throw "Unhandled PPC cpu subtype!"; - break; - } - return previous; -} - - - -template <> -uint32_t Reader::updateCpuConstraint(uint32_t previous) -{ - switch (previous) { - case CPU_SUBTYPE_ARM_ALL: - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - // v6, v7, and xscale are more constrained than previous file (v5), so use it - if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V7) - || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V4T: - // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it - if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V6) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ) - || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V6: - // v6 can run everything except xscale and v7 - if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) - throw "can't mix xscale and v6 code"; - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_XSCALE: - // xscale can run everything except v6 and v7 - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 ) - throw "can't mix xscale and v6 code"; - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) - throw "can't mix xscale and v7 code"; - break; - case CPU_SUBTYPE_ARM_V7: - // v7 can run everything except xscale - if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) - throw "can't mix xscale and v7 code"; - break; - default: - throw "Unhandled ARM cpu subtype!"; - } - return previous; -} - -template -uint32_t Reader::updateCpuConstraint(uint32_t current) -{ - // no cpu sub types for this architecture - return current; -} - -template -void Reader::addDtraceExtraInfos(uint32_t probeAddr, const char* providerName) -{ - // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with - // a matching provider name, add a by-name kDtraceTypeReference at probe site - const char* dollar = strchr(providerName, '$'); - if ( dollar != NULL ) { - int providerNameLen = dollar-providerName+1; - for ( std::vector::iterator it = fDtraceProviderInfo.begin(); it != fDtraceProviderInfo.end(); ++it) { - const char* typeDollar = strchr(*it, '$'); - if ( typeDollar != NULL ) { - if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) { - makeByNameReference(A::kDtraceTypeReference, probeAddr, *it, 0); - } - } - } - } -} - - -template <> -void Reader::validSectionType(uint8_t type) -{ - switch ( type ) { - case S_SYMBOL_STUBS: - throw "symbol_stub sections not valid in x86_64 object files"; - case S_LAZY_SYMBOL_POINTERS: - throw "lazy pointer sections not valid in x86_64 object files"; - case S_NON_LAZY_SYMBOL_POINTERS: - throw "non lazy pointer sections not valid in x86_64 object files"; - } -} - -template -void Reader::validSectionType(uint8_t type) -{ -} - -template -bool Reader::getTranslationUnitSource(const char** dir, const char** name) const -{ - if ( fDebugInfo == kDebugInfoDwarf ) { - *dir = fDwarfTranslationUnitDir; - *name = fDwarfTranslationUnitFile; - return (fDwarfTranslationUnitFile != NULL); - } - return false; -} - -template -BaseAtom* Reader::findAtomByName(const char* name) -{ - // first search the more important atoms - for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - const char* atomName = it->second->getName(); - if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { - return it->second; - } - } - // try all atoms, because this might have been a tentative definition - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - const char* atomName = atom->getName(); - if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { - return atom; - } - } - return NULL; -} - -template -Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr)); -} - -template -Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr)); -} - -template -Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr)); -} - -template -Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr)); -} - -template -Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) -{ - return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); -} - -template -Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) -{ - // add a direct reference from function atom to its eh frame atom - const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); - int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function - pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8; - return makeReference(A::kGroupSubordinate, funcAddr, ehAtomAddress); -} - - -template <> -Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) -{ - // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references - // instead check scope of target - BaseAtom* target = findAtomByName(toName); - if ( (target != NULL) && (target->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) - return new Reference(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset)); - else - return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); -} - -template <> -Reference* Reader::makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset) -{ - // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references - // instead check scope of target - const char* symbolName = &fStrings[toSymbol->n_strx()]; - if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toSymbol->n_value(), toSymbol->n_value()+toOffset)); - else - return new Reference(kind, findAtomAndOffset(atAddr), symbolName, toOffset); -} - - -template <> -Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) -{ - // add a direct reference from function atom to its eh frame atom - // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target - uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + ehSect->reloff()); - const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { - pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); - return makeReference(x86_64::kGroupSubordinate, funcAddr, ehAtomAddress); - } - } - warning("can't find matching function for eh symbol %s", ehName); - return NULL; -} - - -template -AtomAndOffset Reader::findAtomAndOffset(pint_t addr) -{ - // STL has no built-in for "find largest key that is same or less than" - typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr); - // if no atoms up to this address return none found - if ( it == fAddrToAtom.begin() ) - return AtomAndOffset(NULL); - // otherwise upper_bound gets us next key, so we back up one - --it; - AtomAndOffset result; - result.atom = it->second; - result.offset = addr - it->first; - //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n", - // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize()); - return result; -} - -// "scattered" relocations enable you to offset into an atom past the end of it -// baseAddr is the address of the target atom, -// realAddr is the points into it -template -AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) -{ - typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr); - if ( it != fAddrToAtom.end() ) { - AtomAndOffset result; - result.atom = it->second; - result.offset = realAddr - it->first; - //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); - return result; - } - // getting here means we have a scattered relocation to an address without a label - // we should never get here... - // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section - return findAtomAndOffset(realAddr); -} - - -/* Skip over a LEB128 value (signed or unsigned). */ -static void -skip_leb128 (const uint8_t ** offset, const uint8_t * end) -{ - while (*offset != end && **offset >= 0x80) - (*offset)++; - if (*offset != end) - (*offset)++; -} - -/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow - or error. On overflow, skip past the rest of the uleb128. */ -static uint64_t -read_uleb128 (const uint8_t ** offset, const uint8_t * end) -{ - uint64_t result = 0; - int bit = 0; - - do { - uint64_t b; - - if (*offset == end) - return (uint64_t) -1; - - b = **offset & 0x7f; - - if (bit >= 64 || b << bit >> bit != b) - result = (uint64_t) -1; - else - result |= b << bit, bit += 7; - } while (*(*offset)++ >= 0x80); - return result; -} - - -/* Skip over a DWARF attribute of form FORM. */ -template -bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, - uint8_t addr_size, bool dwarf64) -{ - int64_t sz=0; - - switch (form) - { - case DW_FORM_addr: - sz = addr_size; - break; - - case DW_FORM_block2: - if (end - *offset < 2) - return false; - sz = 2 + A::P::E::get16(*(uint16_t*)offset); - break; - - case DW_FORM_block4: - if (end - *offset < 4) - return false; - sz = 2 + A::P::E::get32(*(uint32_t*)offset); - break; - - case DW_FORM_data2: - case DW_FORM_ref2: - sz = 2; - break; - - case DW_FORM_data4: - case DW_FORM_ref4: - sz = 4; - break; - - case DW_FORM_data8: - case DW_FORM_ref8: - sz = 8; - break; - - case DW_FORM_string: - while (*offset != end && **offset) - ++*offset; - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_ref1: - sz = 1; - break; - - case DW_FORM_block: - sz = read_uleb128 (offset, end); - break; - - case DW_FORM_block1: - if (*offset == end) - return false; - sz = 1 + **offset; - break; - - case DW_FORM_sdata: - case DW_FORM_udata: - case DW_FORM_ref_udata: - skip_leb128 (offset, end); - return true; - - case DW_FORM_strp: - case DW_FORM_ref_addr: - sz = dwarf64 ? 8 : 4; - break; - - default: - return false; - } - if (end - *offset < sz) - return false; - *offset += sz; - return true; -} - -// Look at the compilation unit DIE and determine -// its NAME, compilation directory (in COMP_DIR) and its -// line number information offset (in STMT_LIST). NAME and COMP_DIR -// may be NULL (especially COMP_DIR) if they are not in the .o file; -// STMT_LIST will be (uint64_t) -1. -// -// At present this assumes that there's only one compilation unit DIE. -// -template -bool Reader::read_comp_unit(const char ** name, const char ** comp_dir, - uint64_t *stmt_list) -{ - const uint8_t * debug_info; - const uint8_t * debug_abbrev; - const uint8_t * di; - const uint8_t * da; - const uint8_t * end; - const uint8_t * enda; - uint64_t sz; - uint16_t vers; - uint64_t abbrev_base; - uint64_t abbrev; - uint8_t address_size; - bool dwarf64; - - *name = NULL; - *comp_dir = NULL; - *stmt_list = (uint64_t) -1; - - if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) ) - return false; - - debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset(); - debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset(); - di = debug_info; - - if (fDwarfDebugInfoSect->size() < 12) - /* Too small to be a real debug_info section. */ - return false; - sz = A::P::E::get32(*(uint32_t*)di); - di += 4; - dwarf64 = sz == 0xffffffff; - if (dwarf64) - sz = A::P::E::get64(*(uint64_t*)di), di += 8; - else if (sz > 0xffffff00) - /* Unknown dwarf format. */ - return false; - - /* Verify claimed size. */ - if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) - return false; - - vers = A::P::E::get16(*(uint16_t*)di); - if (vers < 2 || vers > 3) - /* DWARF version wrong for this code. - Chances are we could continue anyway, but we don't know for sure. */ - return false; - di += 2; - - /* Find the debug_abbrev section. */ - abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); - di += dwarf64 ? 8 : 4; - - if (abbrev_base > fDwarfDebugAbbrevSect->size()) - return false; - da = debug_abbrev + abbrev_base; - enda = debug_abbrev + fDwarfDebugAbbrevSect->size(); - - address_size = *di++; - - /* Find the abbrev number we're looking for. */ - end = di + sz; - abbrev = read_uleb128 (&di, end); - if (abbrev == (uint64_t) -1) - return false; - - /* Skip through the debug_abbrev section looking for that abbrev. */ - for (;;) - { - uint64_t this_abbrev = read_uleb128 (&da, enda); - uint64_t attr; - - if (this_abbrev == abbrev) - /* This is almost always taken. */ - break; - skip_leb128 (&da, enda); /* Skip the tag. */ - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - do { - attr = read_uleb128 (&da, enda); - skip_leb128 (&da, enda); - } while (attr != 0 && attr != (uint64_t) -1); - if (attr != 0) - return false; - } - - /* Check that the abbrev is one for a DW_TAG_compile_unit. */ - if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) - return false; - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - /* Now, go through the DIE looking for DW_AT_name, - DW_AT_comp_dir, and DW_AT_stmt_list. */ - for (;;) - { - uint64_t attr = read_uleb128 (&da, enda); - uint64_t form = read_uleb128 (&da, enda); - - if (attr == (uint64_t) -1) - return false; - else if (attr == 0) - return true; - - if (form == DW_FORM_indirect) - form = read_uleb128 (&di, end); - - if (attr == DW_AT_name && form == DW_FORM_string) - *name = (const char *) di; - else if (attr == DW_AT_comp_dir && form == DW_FORM_string) - *comp_dir = (const char *) di; - /* Really we should support DW_FORM_strp here, too, but - there's usually no reason for the producer to use that form - for the DW_AT_name and DW_AT_comp_dir attributes. */ - else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) - *stmt_list = A::P::E::get32(*(uint32_t*)di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) - *stmt_list = A::P::E::get64(*(uint64_t*)di); - if (! skip_form (&di, end, form, address_size, dwarf64)) - return false; - } -} - -template -const char* Reader::assureFullPath(const char* path) -{ - if ( path[0] == '/' ) - return path; - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - char* result; - asprintf(&result, "%s/%s", cwdbuff, path); - if ( result != NULL ) - return result; - } - return path; -} - - -// -// -// To implement architecture xxx, you must write template specializations for the following six methods: -// Reader::validFile() -// Reader::addRelocReference() -// Reference::getDescription() -// -// - - -template <> -bool Reader::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; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::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; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::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_I386 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::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_X86_64 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::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; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template -bool Reader::isWeakImportSymbol(const macho_nlist

* sym) -{ - return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) ); -} - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - return addRelocReference_powerpc(sect, reloc); -} - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - return addRelocReference_powerpc(sect, reloc); -} - - -// -// ppc and ppc64 both use the same relocations, so process them in one common routine -// -template -bool Reader::addRelocReference_powerpc(const macho_section* sect, - const macho_relocation_info* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - int32_t displacement = 0; - uint32_t instruction = 0; - uint32_t offsetInTarget; - int16_t lowBits; - bool result = false; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - const macho_relocation_info

* nextReloc = &reloc[1]; - const char* targetName = NULL; - bool weakImport = false; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != PPC_RELOC_PAIR ) - instruction = BigEndian::get32(*fixUpPtr); - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - weakImport = this->isWeakImportSymbol(targetSymbol); - } - switch ( reloc->r_type() ) { - case PPC_RELOC_BR24: - { - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - } - else { - printf("bad instruction for BR24 reloc"); - } - if ( reloc->r_extern() ) { - offsetInTarget = srcAddr + displacement; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(A::kBranch24WeakImport, srcAddr, dstAddr); - else - makeReference(A::kBranch24, srcAddr, dstAddr); - } - } - break; - case PPC_RELOC_BR14: - { - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - if ( reloc->r_extern() ) { - offsetInTarget = srcAddr + displacement; - makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - makeReference(A::kBranch14, srcAddr, dstAddr); - } - } - break; - case PPC_RELOC_PAIR: - // skip, processed by a previous look ahead - break; - case PPC_RELOC_LO16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_LO16 missing following pair"); - break; - } - result = true; - lowBits = (instruction & 0xFFFF); - if ( reloc->r_extern() ) { - offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsLow16, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsLow16, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_LO14: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_LO14 missing following pair"); - break; - } - result = true; - lowBits = (instruction & 0xFFFC); - if ( reloc->r_extern() ) { - offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsLow14, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsLow14, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_HI16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_HI16 missing following pair"); - break; - } - result = true; - if ( reloc->r_extern() ) { - offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsHigh16, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsHigh16, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_HA16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_HA16 missing following pair"); - break; - } - result = true; - lowBits = (nextReloc->r_address() & 0x0000FFFF); - if ( reloc->r_extern() ) { - offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_VANILLA: - { - pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - if ( weakImport ) - makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue); - else - makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); - } - else { - makeReference(A::kPointer, srcAddr, pointerValue); - } - } - break; - case PPC_RELOC_JBSR: - // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_JBSR missing following pair"); - break; - } - fHasLongBranchStubs = true; - result = true; - makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - } - else { - fprintf(stderr, "bad instruction for BR24 reloc"); - } - if ( reloc->r_extern() ) { - fprintf(stderr, "PPC_RELOC_JBSR should not be using an external relocation"); - } - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - uint32_t betterDstAddr; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - const macho_relocation_info

* nextReloc = &reloc[1]; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - } - switch (sreloc->r_type()) { - case PPC_RELOC_VANILLA: - { - betterDstAddr = P::getP(*(pint_t*)fixUpPtr); - //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_BR14: - { - instruction = BigEndian::get32(*fixUpPtr); - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - betterDstAddr = srcAddr+displacement; - //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement); - makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_BR24: - { - instruction = BigEndian::get32(*fixUpPtr); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - betterDstAddr = srcAddr+displacement; - makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr); - } - } - break; - case PPC_RELOC_LO16_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO16_SECTDIFF missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFF); - displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_LO14_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO14_SECTDIFF missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFC); - displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_HA16_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_HA16_SECTDIFF missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0x0000FFFF); - displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_LO14: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO14 missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFC); - betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_LO16: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO16 missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFF); - betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_HA16: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_HA16 missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0xFFFF); - betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; - makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_HI16: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_HI16 missing following PAIR"); - break; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0xFFFF); - betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_SECTDIFF: - case PPC_RELOC_LOCAL_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_SECTDIFF missing following pair"); - break; - } - Kinds kind = A::kPointerDiff32;; - uint32_t contentAddr = 0; - switch ( sreloc->r_length() ) { - case 0: - throw "bad diff relocations r_length (0) for ppc architecture"; - case 1: - kind = A::kPointerDiff16; - contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr)); - break; - case 2: - kind = A::kPointerDiff32; - contentAddr = BigEndian::get32(*fixUpPtr); - break; - case 3: - kind = A::kPointerDiff64; - contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr)); - break; - } - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", - // dstAddr, nextRelocValue, contentAddr); - if ( (dstAddr - nextRelocValue) != contentAddr ) { - if ( toao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - contentAddr) - nextRelocValue; - } - //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", - // srcao.atom->getDisplayName(), srcao.offset, - // fromao.atom->getDisplayName(), fromao.offset, - // toao.atom->getDisplayName(), toao.offset); - new Reference(kind, srcao, fromao, toao); - } - break; - case PPC_RELOC_PAIR: - break; - case PPC_RELOC_HI16_SECTDIFF: - warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - bool result = false; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - switch ( reloc->r_type() ) { - case GENERIC_RELOC_VANILLA: - { - x86::ReferenceKinds kind = x86::kPointer; - uint32_t pointerValue = E::get32(*fixUpPtr); - if ( reloc->r_pcrel() ) { - switch( reloc->r_length() ) { - case 0: - kind = x86::kPCRel8; - pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t); - break; - case 1: - kind = x86::kPCRel16; - pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t); - break; - case 2: - kind = x86::kPCRel32; - pointerValue += srcAddr + sizeof(uint32_t); - break; - case 3: - throw "bad pc-rel vanilla relocation length"; - } - } - else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { - kind = x86::kAbsolute32; - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - } - else { - kind = x86::kPointer; - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - } - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - if ( this->isWeakImportSymbol(targetSymbol) ) { - if ( reloc->r_pcrel() ) - kind = x86::kPCRel32WeakImport; - else - kind = x86::kPointerWeakImport; - } - const char* targetName = &fStrings[targetSymbol->n_strx()]; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else - makeByNameReference(kind, srcAddr, targetName, pointerValue); - } - else { - // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol - ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom; - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(kind, srcAddr, pointerValue); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(kind, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - } - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - const macho_relocation_info

* nextReloc = &reloc[1]; - pint_t betterDstAddr; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - } - } - switch (sreloc->r_type()) { - case GENERIC_RELOC_VANILLA: - betterDstAddr = LittleEndian::get32(*fixUpPtr); - //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - if ( sreloc->r_pcrel() ) { - betterDstAddr += srcAddr + 4; - makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); - } - else { - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); - else - makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); - } - break; - case GENERIC_RELOC_SECTDIFF: - case GENERIC_RELOC_LOCAL_SECTDIFF: - { - if ( !nextRelocIsPair ) { - warning("GENERIC_RELOC_SECTDIFF missing following pair"); - break; - } - x86::ReferenceKinds kind = x86::kPointerDiff; - uint32_t contentAddr = 0; - switch ( sreloc->r_length() ) { - case 0: - case 3: - throw "bad length for GENERIC_RELOC_SECTDIFF"; - case 1: - kind = x86::kPointerDiff16; - contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)); - break; - case 2: - kind = x86::kPointerDiff; - contentAddr = LittleEndian::get32(*fixUpPtr); - break; - } - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", - // dstAddr, nextRelocValue, contentAddr); - if ( (dstAddr - nextRelocValue) != contentAddr ) { - if ( toao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - contentAddr) - nextRelocValue; - } - //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", - // srcao.atom->getDisplayName(), srcao.offset, - // fromao.atom->getDisplayName(), fromao.offset, - // toao.atom->getDisplayName(), toao.offset); - new Reference(kind, srcao, fromao, toao); - } - break; - case GENERIC_RELOC_PAIR: - // do nothing, already used via a look ahead - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - uint64_t srcAddr; - uint64_t dstAddr = 0; - uint64_t addend; - uint32_t* fixUpPtr; - x86_64::ReferenceKinds kind = x86_64::kNoFixUp; - bool result = false; - const macho_nlist

* targetSymbol = NULL; - const char* targetName = NULL; - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - //fprintf(stderr, "addReloc type=%d\n", reloc->r_type()); - if ( reloc->r_extern() ) { - targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - } - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - if ( reloc->r_pcrel() ) - throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; - if ( reloc->r_length() != 3 ) - throw "length < 3 and X86_64_RELOC_UNSIGNED not supported"; - dstAddr = E::get64(*((uint64_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kPointer, srcAddr, dstAddr); - // verify that dstAddr is in the section being targeted - int sectNum = reloc->r_symbolnum(); - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

* const targetSection = §ionsStart[sectNum-1]; - if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { - throwf("local relocation for address 0x%08llX in section %s does not target section %s", - srcAddr, sect->sectname(), targetSection->sectname()); - } - } - break; - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( reloc->r_extern() ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - kind = x86_64::kPCRel32; - // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created - if ( addend == (uint64_t)(-1) ) { - addend = 0; - kind = x86_64::kPCRel32_1; - } - else if ( addend == (uint64_t)(-2) ) { - addend = 0; - kind = x86_64::kPCRel32_2; - } - else if ( addend == (uint64_t)(-4) ) { - addend = 0; - kind = x86_64::kPCRel32_4; - } - break; - // end support for old .o files before X86_64_RELOC_SIGNED_1 was created - case X86_64_RELOC_SIGNED_1: - kind = x86_64::kPCRel32_1; - addend += 1; - break; - case X86_64_RELOC_SIGNED_2: - kind = x86_64::kPCRel32_2; - addend += 2; - break; - case X86_64_RELOC_SIGNED_4: - kind = x86_64::kPCRel32_4; - addend += 4; - break; - } - makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend); - } - else { - uint64_t ripRelativeOffset = addend; - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - dstAddr = srcAddr + 4 + ripRelativeOffset; - kind = x86_64::kPCRel32; - break; - case X86_64_RELOC_SIGNED_1: - dstAddr = srcAddr + 5 + ripRelativeOffset; - kind = x86_64::kPCRel32_1; - break; - case X86_64_RELOC_SIGNED_2: - dstAddr = srcAddr + 6 + ripRelativeOffset; - kind = x86_64::kPCRel32_2; - break; - case X86_64_RELOC_SIGNED_4: - dstAddr = srcAddr + 8 + ripRelativeOffset; - kind = x86_64::kPCRel32_4; - break; - } - makeReference(kind, srcAddr, dstAddr); - // verify that dstAddr is in the section being targeted - int sectNum = reloc->r_symbolnum(); - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

* const targetSection = §ionsStart[sectNum-1]; - if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { - throwf("local relocation for address 0x%08llX in section %s does not target section %s", - srcAddr, sect->sectname(), targetSection->sectname()); - } - } - break; - case X86_64_RELOC_BRANCH: - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_BRANCH not supported"; - if ( reloc->r_length() == 2 ) { - dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( reloc->r_extern() ) { - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr); - else - makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr); - } - } - else if ( reloc->r_length() == 0 ) { - dstAddr = *((int8_t*)fixUpPtr); - if ( reloc->r_extern() ) { - makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr); - } - } - else { - throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());; - } - break; - case X86_64_RELOC_GOT: - if ( ! reloc->r_extern() ) - throw "not extern and X86_64_RELOC_GOT not supported"; - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_GOT not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_GOT not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend); - else - makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend); - break; - case X86_64_RELOC_GOT_LOAD: - if ( ! reloc->r_extern() ) - throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend); - else - makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend); - break; - case X86_64_RELOC_SUBTRACTOR: - { - if ( reloc->r_pcrel() ) - throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; - if ( reloc->r_length() < 2 ) - throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; - if ( !reloc->r_extern() ) - throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; - const macho_relocation_info* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) - throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; - result = true; - if ( nextReloc->r_pcrel() ) - throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; - if ( nextReloc->r_length() != reloc->r_length() ) - throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; - Reference* ref; - bool negativeAddend; - if ( reloc->r_length() == 2 ) { - kind = x86_64::kPointerDiff32; - dstAddr = E::get32(*fixUpPtr); // addend is in content - negativeAddend = ((dstAddr & 0x80000000) != 0); - } - else { - kind = x86_64::kPointerDiff; - dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content - negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); - } - ObjectFile::Atom* inAtom = this->findAtomAndOffset(srcAddr).atom; - // create reference with "to" target - if ( nextReloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0); - // if "to" is in this atom, change by-name to a direct reference - if ( strcmp(targetName, inAtom->getName()) == 0 ) - ref->setTarget(*inAtom, 0); - } - else { - ref = makeReference(kind, srcAddr, dstAddr); - } - // add in "from" target - if ( reloc->r_extern() ) { - const macho_nlist

* targetFromSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()]; - if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) { - // from target is translation unit scoped, so use a direct reference - ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom)); - } - else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) { - // if "from" is in this atom, change by-name to a direct reference - ref->setFromTarget(*inAtom); - } - else { - // some non-static other atom - ref->setFromTargetName(fromTargetName); - } - } - // addend goes in from side iff negative - if ( negativeAddend ) - ref->setFromTargetOffset(-dstAddr); - else - ref->setToTargetOffset(dstAddr); - break; - } - default: - warning("unknown relocation type %d", reloc->r_type()); - } - return result; -} - - -/// Reader::addRelocReference - -/// turns arm relocation entries into references. Returns true if the next -/// relocation should be skipped, false otherwise. -template <> -bool Reader::addRelocReference(const macho_section* sect, - const macho_relocation_info* reloc) -{ - uint32_t * fixUpPtr; - int32_t displacement; - uint32_t instruction = 0; - bool result = false; - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t pointerValue; - - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - // non-scattered relocation - const char* targetName = NULL; - bool weakImport = false; - - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != ARM_RELOC_PAIR ) - instruction = LittleEndian::get32(*fixUpPtr); - - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - weakImport = this->isWeakImportSymbol(targetSymbol); - } - - switch ( reloc->r_type() ) { - case ARM_RELOC_BR24: - // Sign-extend displacement - displacement = (instruction & 0x00FFFFFF) << 2; - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - // The pc added will be +8 from the pc - displacement += 8; - // If this is BLX add H << 1 - if ((instruction & 0xFE000000) == 0xFA000000) - displacement += ((instruction & 0x01000000) >> 23); - - if ( reloc->r_extern() ) { - uint32_t offsetInTarget = srcAddr + displacement; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - // check for dtrace probes and weak_import stubs - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(arm::kBranch24, srcAddr, dstAddr); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - break; - - case ARM_THUMB_RELOC_BR22: - // First instruction has upper 11 bits of the displacement. - displacement = (instruction & 0x7FF) << 12; - if ( (displacement & 0x400000) != 0 ) - displacement |= 0xFF800000; - // Second instruction has lower eleven bits of the displacement. - displacement += ((instruction >> 16) & 0x7FF) << 1; - // The pc added will be +4 from the pc - displacement += 4; - // If the instruction was blx, force the low 2 bits to be clear - dstAddr = srcAddr + displacement; - if ((instruction & 0xF8000000) == 0xE8000000) - dstAddr &= 0xFFFFFFFC; - - if ( reloc->r_extern() ) { - uint32_t offsetInTarget = dstAddr; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget); - } - else { - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - // check for dtrace probes and weak_import stubs - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(arm::kThumbBranch22, srcAddr, dstAddr); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - break; - - case ARM_RELOC_VANILLA: - if ( reloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_VANILLA"; - - pointerValue = instruction; - if ( reloc->r_extern() ) { - if ( weakImport ) - makeByNameReference(arm::kPointerWeakImport, srcAddr, targetName, pointerValue); - else if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeByNameReference(arm::kReadOnlyPointer, srcAddr, targetName, pointerValue); - else - makeByNameReference(arm::kPointer, srcAddr, targetName, pointerValue); - } - else { - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReference(arm::kReadOnlyPointer, srcAddr, pointerValue); - else - makeReference(arm::kPointer, srcAddr, pointerValue); - } - break; - - default: - warning("unexpected relocation type %u", reloc->r_type()); - break; - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - uint32_t betterDstAddr; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - instruction = LittleEndian::get32(*fixUpPtr); - - // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF} - // relocation types, and it is an error to see one otherwise. - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - - switch (sreloc->r_type()) { - case ARM_RELOC_VANILLA: - if ( sreloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_VANILLA"; - - betterDstAddr = LittleEndian::get32(*fixUpPtr); - //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReferenceWithToBase(arm::kReadOnlyPointer, srcAddr, betterDstAddr, dstAddr); - else - makeReferenceWithToBase(arm::kPointer, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_RELOC_BR24: - // Sign-extend displacement - displacement = (instruction & 0x00FFFFFF) << 2; - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - // The pc added will be +8 from the pc - displacement += 8; - // If this is BLX add H << 1 - if ((instruction & 0xFE000000) == 0xFA000000) - displacement += ((instruction & 0x01000000) >> 23); - betterDstAddr = srcAddr+displacement; - makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_THUMB_RELOC_BR22: - // First instruction has upper 11 bits of the displacement. - displacement = (instruction & 0x7FF) << 12; - if ( (displacement & 0x400000) != 0 ) - displacement |= 0xFF800000; - // Second instruction has lower eleven bits of the displacement. - displacement += ((instruction >> 16) & 0x7FF) << 1; - // The pc added will be +4 from the pc - displacement += 4; - betterDstAddr = srcAddr+displacement; - // If the instruction was blx, force the low 2 bits to be clear - if ((instruction & 0xF8000000) == 0xE8000000) - betterDstAddr &= 0xFFFFFFFC; - makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_RELOC_SECTDIFF: - case ARM_RELOC_LOCAL_SECTDIFF: - if ( !nextRelocIsPair ) { - warning("ARM_RELOC_SECTDIFF missing following pair"); - break; - } - if ( sreloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_SECTDIFF"; - { - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - pointerValue = LittleEndian::get32(*fixUpPtr); - if ( (dstAddr - nextRelocValue) != pointerValue ) { - if ( toao.atom == srcao.atom ) - toao.offset += (pointerValue + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (pointerValue + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - pointerValue) - nextRelocValue; - } - new Reference(arm::kPointerDiff, srcao, fromao, toao); - } - break; - - default: - warning("unexpected srelocation type %u", sreloc->r_type()); - break; - } - } - return result; -} - -template -void Reader::addReferencesForSection(const macho_section

* sect) -{ - // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change - if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) { - switch ( sect->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - // we ignore compiler generated stubs, so ignore those relocs too - break; - default: - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); - const uint32_t relocCount = sect->nreloc(); - //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); - for (uint32_t r = 0; r < relocCount; ++r) { - try { - if ( addRelocReference(sect, &relocs[r]) ) - ++r; // skip next - } - catch (const char* msg) { - throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); - } - } - } - } -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case x86::kNoFixUp: - sprintf(temp, "reference to "); - break; - case x86::kFollowOn: - sprintf(temp, "followed by "); - break; - case x86::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case x86::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case x86::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case x86::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86::kPCRel32WeakImport: - sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc); - break; - case x86::kPCRel32: - sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86::kPCRel16: - sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc); - break; - case x86::kPCRel8: - sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc); - break; - case x86::kAbsolute32: - sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); - break; - case x86::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case x86::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case x86::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case x86::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case ppc::kNoFixUp: - sprintf(temp, "reference to "); - break; - case ppc::kFollowOn: - sprintf(temp, "followed by "); - break; - case ppc::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case ppc::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case ppc::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case ppc::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc::kPointerDiff32: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc::kPointerDiff64: - throw "unsupported refrence kind"; - break; - case ppc::kBranch24WeakImport: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case ppc::kBranch24: - case ppc::kBranch14: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case ppc::kPICBaseLow16: - sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kPICBaseLow14: - sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kPICBaseHigh16: - sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kAbsLow16: - sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsLow14: - sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsHigh16: - sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsHigh16AddLow: - sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case ppc::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case ppc::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case ppc::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case ppc64::kNoFixUp: - sprintf(temp, "reference to "); - break; - case ppc64::kFollowOn: - sprintf(temp, "followed by "); - break; - case ppc64::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case ppc64::kPointerWeakImport: - sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); - break; - case ppc64::kPointer: - sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); - break; - case ppc64::kPointerDiff64: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kPointerDiff32: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kBranch24WeakImport: - sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case ppc64::kBranch24: - case ppc64::kBranch14: - sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case ppc64::kPICBaseLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kPICBaseLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kPICBaseHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kAbsLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsHigh16AddLow: - sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceProbe: - sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceProbeSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceTypeReference: - sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); - - return temp; -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case x86_64::kNoFixUp: - sprintf(temp, "reference to "); - break; - case x86_64::kFollowOn: - sprintf(temp, "followed by "); - break; - case x86_64::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case x86_64::kPointerWeakImport: - sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointer: - sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointerDiff32: - case x86_64::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; - sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, size, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86_64::kPCRel32: - sprintf(temp, "offset 0x%04llX, rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_1: - sprintf(temp, "offset 0x%04llX, rel32-1 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_2: - sprintf(temp, "offset 0x%04llX, rel32-2 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_4: - sprintf(temp, "offset 0x%04llX, rel32-4 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel32: - sprintf(temp, "offset 0x%04llX, branch rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel32WeakImport: - sprintf(temp, "offset 0x%04llX, branch rel32 reference to weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOT: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTWeakImport: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTLoad: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTLoadWeakImport: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel8: - sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceProbe: - sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceProbeSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceTypeReference: - sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); - - return temp; -} - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case arm::kNoFixUp: - sprintf(temp, "reference to "); - break; - case arm::kFollowOn: - sprintf(temp, "followed by "); - break; - case arm::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case arm::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case arm::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case arm::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case arm::kReadOnlyPointer: - sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc); - break; - case arm::kBranch24: - case arm::kThumbBranch22: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case arm::kBranch24WeakImport: - case arm::kThumbBranch22WeakImport: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case arm::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case arm::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case arm::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case arm::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - -}; // namespace relocatable -}; // namespace mach_o - -#endif // __OBJECT_FILE_MACH_O__ diff --git a/ld64/src/MachOWriterExecutable.hpp b/ld64/src/MachOWriterExecutable.hpp deleted file mode 100644 index 8667ae4..0000000 --- a/ld64/src/MachOWriterExecutable.hpp +++ /dev/null @@ -1,8579 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __EXECUTABLE_MACH_O__ -#define __EXECUTABLE_MACH_O__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ObjectFile.h" -#include "ExecutableFile.h" -#include "Options.h" - -#include "MachOFileAbstraction.hpp" - - -// -// -// To implement architecture xxx, you must write template specializations for the following methods: -// MachHeaderAtom::setHeaderInfo() -// ThreadsLoadCommandsAtom::getSize() -// ThreadsLoadCommandsAtom::copyRawContent() -// Writer::addObjectRelocs() -// Writer::fixUpReferenceRelocatable() -// Writer::fixUpReferenceFinal() -// Writer::stubableReference() -// Writer::weakImportReferenceKind() -// Writer::GOTReferenceKind() -// - - -namespace mach_o { -namespace executable { - -// forward references -template class WriterAtom; -template class PageZeroAtom; -template class CustomStackAtom; -template class MachHeaderAtom; -template class SegmentLoadCommandsAtom; -template class EncryptionLoadCommandsAtom; -template class SymbolTableLoadCommandsAtom; -template class ThreadsLoadCommandsAtom; -template class DylibIDLoadCommandsAtom; -template class RoutinesLoadCommandsAtom; -template class DyldLoadCommandsAtom; -template class UUIDLoadCommandAtom; -template class LinkEditAtom; -template class SectionRelocationsLinkEditAtom; -template class LocalRelocationsLinkEditAtom; -template class ExternalRelocationsLinkEditAtom; -template class SymbolTableLinkEditAtom; -template class SegmentSplitInfoLoadCommandsAtom; -template class SegmentSplitInfoContentAtom; -template class IndirectTableLinkEditAtom; -template class ModuleInfoLinkEditAtom; -template class StringsLinkEditAtom; -template class LoadCommandsPaddingAtom; -template class StubAtom; -template class StubHelperAtom; -template class LazyPointerAtom; -template class NonLazyPointerAtom; -template class DylibLoadCommandsAtom; - - -// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes -class SectionInfo : public ObjectFile::Section { -public: - SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), - fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false), - fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false), - fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false), - fHasTextLocalRelocs(false), fHasTextExternalRelocs(false) - { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } - void setIndex(unsigned int index) { fIndex=index; } - std::vector fAtoms; - char fSegmentName[20]; - char fSectionName[20]; - uint64_t fFileOffset; - uint64_t fSize; - uint32_t fRelocCount; - uint32_t fRelocOffset; - uint32_t fIndirectSymbolOffset; - uint8_t fAlignment; - bool fAllLazyPointers; - bool fAllLazyDylibPointers; - bool fAllNonLazyPointers; - bool fAllStubs; - bool fAllSelfModifyingStubs; - bool fAllZeroFill; - bool fVirtualSection; - bool fHasTextLocalRelocs; - bool fHasTextExternalRelocs; -}; - -// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes -class SegmentInfo -{ -public: - SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), - fBaseAddress(0), fSize(0), fFixedAddress(false), - fIndependentAddress(false) { fName[0] = '\0'; } - std::vector fSections; - char fName[20]; - uint32_t fInitProtection; - uint32_t fMaxProtection; - uint64_t fFileOffset; - uint64_t fFileSize; - uint64_t fBaseAddress; - uint64_t fSize; - bool fFixedAddress; - bool fIndependentAddress; -}; - -template -class Writer : public ExecutableFile::Writer -{ -public: - Writer(const char* path, Options& options, std::vector& dynamicLibraries); - virtual ~Writer(); - - virtual const char* getPath() { return fFilePath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms() { return fWriterSynthesizedAtoms; } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabs() { return NULL; } - - virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses); - virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); - virtual uint64_t write(std::vector& atoms, - std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool createUUID, bool canScatter, - ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, bool overridesDylibWeakDefines); - -private: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - - enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; - - void assignFileOffsets(); - void synthesizeStubs(); - void insertDummyStubs(); - void partitionIntoSections(); - bool addBranchIslands(); - bool addPPCBranchIslands(); - bool isBranch24Reference(uint8_t kind); - void adjustLoadCommandsAndPadding(); - void createDynamicLinkerCommand(); - void createDylibCommands(); - void buildLinkEdit(); - const char* getArchString(); - void writeMap(); - uint64_t writeAtoms(); - void writeNoOps(int fd, uint32_t from, uint32_t to); - void copyNoOps(uint8_t* from, uint8_t* to); - bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to); - void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref); - void collectExportedAndImportedAndLocalAtoms(); - void setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count); - void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); - void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); - void buildSymbolTable(); - const char* symbolTableName(const ObjectFile::Atom* atom); - void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); - void setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); - void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); - void copyNlistRange(const std::vector >& entries, uint32_t startIndex); - uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); - uint8_t ordinalForLibrary(ObjectFile::Reader* file); - bool shouldExport(const ObjectFile::Atom& atom) const; - void buildFixups(); - void adjustLinkEditSections(); - void buildObjectFileFixups(); - void buildExecutableFixups(); - bool preboundLazyPointerType(uint8_t* type); - uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; - void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; - void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; - void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, - uint8_t buffer[], bool finalLinkedImage) const; - uint32_t symbolIndex(ObjectFile::Atom& atom); - bool makesExternalRelocatableReference(ObjectFile::Atom& target) const; - uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); - uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref); - uint8_t getRelocPointerSize(); - uint64_t maxAddress(); - bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref); - bool GOTReferenceKind(uint8_t kind); - bool optimizableGOTReferenceKind(uint8_t kind); - bool weakImportReferenceKind(uint8_t kind); - unsigned int collectStabs(); - uint64_t valueForStab(const ObjectFile::Reader::Stab& stab); - uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab); - uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab); - void addStabs(uint32_t startIndex); - RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const; - bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&); - bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); - bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); - bool mightNeedPadSegment(); - void scanForAbsoluteReferences(); - bool needsModuleTable(); - void optimizeDylibReferences(); - bool indirectSymbolIsLocal(const ObjectFile::Reference* ref) const; - - struct DirectLibrary { - class ObjectFile::Reader* fLibrary; - bool fWeak; - bool fReExport; - }; - - friend class WriterAtom; - friend class PageZeroAtom; - friend class CustomStackAtom; - friend class MachHeaderAtom; - friend class SegmentLoadCommandsAtom; - friend class EncryptionLoadCommandsAtom; - friend class SymbolTableLoadCommandsAtom; - friend class ThreadsLoadCommandsAtom; - friend class DylibIDLoadCommandsAtom; - friend class RoutinesLoadCommandsAtom; - friend class DyldLoadCommandsAtom; - friend class UUIDLoadCommandAtom; - friend class LinkEditAtom; - friend class SectionRelocationsLinkEditAtom; - friend class LocalRelocationsLinkEditAtom; - friend class ExternalRelocationsLinkEditAtom; - friend class SymbolTableLinkEditAtom; - friend class SegmentSplitInfoLoadCommandsAtom; - friend class SegmentSplitInfoContentAtom; -// friend class IndirectTableLinkEditAtom; - friend class ModuleInfoLinkEditAtom; - friend class StringsLinkEditAtom; - friend class LoadCommandsPaddingAtom; - friend class StubAtom; - friend class StubHelperAtom; - friend class LazyPointerAtom; - friend class NonLazyPointerAtom; - friend class DylibLoadCommandsAtom; - - const char* fFilePath; - Options& fOptions; - std::vector* fAllAtoms; - std::vector* fStabs; - class SectionInfo* fLoadCommandsSection; - class SegmentInfo* fLoadCommandsSegment; - class EncryptionLoadCommandsAtom* fEncryptionLoadCommand; - class SegmentLoadCommandsAtom* fSegmentCommands; - class SymbolTableLoadCommandsAtom* fSymbolTableCommands; - class LoadCommandsPaddingAtom* fHeaderPadding; - class UUIDLoadCommandAtom* fUUIDAtom; - std::vector fWriterSynthesizedAtoms; - std::vector fSegmentInfos; - class SegmentInfo* fPadSegmentInfo; - class ObjectFile::Atom* fEntryPoint; - class ObjectFile::Atom* fDyldHelper; - class ObjectFile::Atom* fDyldLazyDylibHelper; - std::map*> fLibraryToLoadCommand; - std::map fLibraryToOrdinal; - std::map fLibraryAliases; - std::vector fExportedAtoms; - std::vector fImportedAtoms; - std::vector fLocalSymbolAtoms; - std::vector > fLocalExtraLabels; - std::vector > fGlobalExtraLabels; - class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; - class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; - class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; - class SymbolTableLinkEditAtom* fSymbolTableAtom; - class SegmentSplitInfoContentAtom* fSplitCodeToDataContentAtom; - class IndirectTableLinkEditAtom* fIndirectTableAtom; - class ModuleInfoLinkEditAtom* fModuleInfoAtom; - class StringsLinkEditAtom* fStringsAtom; - class PageZeroAtom* fPageZeroAtom; - macho_nlist

* fSymbolTable; - std::vector > fSectionRelocs; - std::vector > fInternalRelocs; - std::vector > fExternalRelocs; - std::map fStubsMap; - std::map fGOTMap; - std::vector*> fAllSynthesizedStubs; - std::vector fAllSynthesizedStubHelpers; - std::vector*> fAllSynthesizedLazyPointers; - std::vector*> fAllSynthesizedLazyDylibPointers; - std::vector*> fAllSynthesizedNonLazyPointers; - uint32_t fSymbolTableCount; - uint32_t fSymbolTableStabsCount; - uint32_t fSymbolTableStabsStartIndex; - uint32_t fSymbolTableLocalCount; - uint32_t fSymbolTableLocalStartIndex; - uint32_t fSymbolTableExportCount; - uint32_t fSymbolTableExportStartIndex; - uint32_t fSymbolTableImportCount; - uint32_t fSymbolTableImportStartIndex; - uint32_t fLargestAtomSize; - bool fEmitVirtualSections; - bool fHasWeakExports; - bool fReferencesWeakImports; - bool fCanScatter; - bool fWritableSegmentPastFirst4GB; - bool fNoReExportedDylibs; - bool fBiggerThanTwoGigs; - bool fSlideable; - std::map fWeakImportMap; - std::set fDylibReadersWithNonWeakImports; - std::set fDylibReadersWithWeakImports; - SegmentInfo* fFirstWritableSegment; - ObjectFile::Reader::CpuConstraint fCpuConstraint; - uint32_t fAnonNameIndex; -}; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - - static Segment fgTextSegment; - static Segment fgPageZeroSegment; - static Segment fgLinkEditSegment; - static Segment fgStackSegment; - static Segment fgImportSegment; - static Segment fgROImportSegment; - static Segment fgDataSegment; - static Segment fgObjCSegment; - - -private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; -}; - -Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true); -Segment Segment::fgTextSegment("__TEXT", true, false, true, false); -Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false); -Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true); -Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); -Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false); -Segment Segment::fgDataSegment("__DATA", true, true, false, false); -Segment Segment::fgObjCSegment("__OBJC", true, true, false, false); - - -template -class WriterAtom : public ObjectFile::Atom -{ -public: - enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; - WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { } - - virtual ObjectFile::Reader* getFile() const { return &fWriter; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return NULL; } - virtual const char* getDisplayName() const { return this->getName(); } - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return true; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual std::vector& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return true; } - virtual ObjectFile::Segment& getSegment() const { return fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return 0; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); } - virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } - virtual void setScope(Scope) { } - - -protected: - virtual ~WriterAtom() {} - typedef typename A::P P; - typedef typename A::P::E E; - - static std::vector fgEmptyReferenceList; - - Writer& fWriter; - Segment& fSegment; -}; - -template std::vector WriterAtom::fgEmptyReferenceList; - - -template -class PageZeroAtom : public WriterAtom -{ -public: - PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment), - fSize(fWriter.fOptions.zeroPageSize()) {} - virtual const char* getDisplayName() const { return "page zero content"; } - virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fSize; } - virtual const char* getSectionName() const { return "._zeropage"; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - void setSize(uint64_t size) { fSize = size; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint64_t fSize; -}; - - -template -class DsoHandleAtom : public WriterAtom -{ -public: - DsoHandleAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} - virtual const char* getName() const { return "___dso_handle"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual uint64_t getSize() const { return 0; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - virtual const char* getSectionName() const { return "._mach_header"; } - virtual void copyRawContent(uint8_t buffer[]) const {} -}; - - -template -class MachHeaderAtom : public WriterAtom -{ -public: - MachHeaderAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} - virtual const char* getName() const; - virtual const char* getDisplayName() const; - virtual ObjectFile::Atom::Scope getScope() const; - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; - virtual uint64_t getSize() const { return sizeof(macho_header); } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - virtual const char* getSectionName() const { return "._mach_header"; } - virtual uint32_t getOrdinal() const { return 1; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - void setHeaderInfo(macho_header& header) const; -}; - -template -class CustomStackAtom : public WriterAtom -{ -public: - CustomStackAtom(Writer& writer); - virtual const char* getDisplayName() const { return "custom stack content"; } - virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } - virtual const char* getSectionName() const { return "._stack"; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - static bool stackGrowsDown(); -}; - -template -class LoadCommandAtom : public WriterAtom -{ -protected: - LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment), fOrdinal(fgCurrentOrdinal++) {} - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } - virtual const char* getSectionName() const { return "._load_commands"; } - virtual uint32_t getOrdinal() const { return fOrdinal; } - static uint64_t alignedSize(uint64_t size); -protected: - uint32_t fOrdinal; - static uint32_t fgCurrentOrdinal; -}; - -template uint32_t LoadCommandAtom::fgCurrentOrdinal = 0; - -template -class SegmentLoadCommandsAtom : public LoadCommandAtom -{ -public: - SegmentLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0) - { writer.fSegmentCommands = this; } - virtual const char* getDisplayName() const { return "segment load commands"; } - virtual uint64_t getSize() const { return fSize; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void computeSize(); - void setup(); - unsigned int commandCount() { return fCommandCount; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - unsigned int fCommandCount; - uint32_t fSize; -}; - - -template -class SymbolTableLoadCommandsAtom : public LoadCommandAtom -{ -public: - SymbolTableLoadCommandsAtom(Writer&); - virtual const char* getDisplayName() const { return "symbol table load commands"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - unsigned int commandCount(); - void needDynamicTable(); -private: - using WriterAtom::fWriter; - typedef typename A::P P; - bool fNeedsDynamicSymbolTable; - macho_symtab_command fSymbolTable; - macho_dysymtab_command fDynamicSymbolTable; -}; - -template -class ThreadsLoadCommandsAtom : public LoadCommandAtom -{ -public: - ThreadsLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment) {} - virtual const char* getDisplayName() const { return "thread load commands"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint8_t* fBuffer; - uint32_t fBufferSize; -}; - -template -class DyldLoadCommandsAtom : public LoadCommandAtom -{ -public: - DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} - virtual const char* getDisplayName() const { return "dyld load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom -{ -public: - SegmentSplitInfoLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} - virtual const char* getDisplayName() const { return "segment split info load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class AllowableClientLoadCommandsAtom : public LoadCommandAtom -{ -public: - AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : - LoadCommandAtom(writer, Segment::fgTextSegment), clientString(client) {} - virtual const char* getDisplayName() const { return "allowable_client load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* clientString; -}; - -template -class DylibLoadCommandsAtom : public LoadCommandAtom -{ -public: - DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) - : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info), - fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; } - virtual const char* getDisplayName() const { return "dylib load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void optimizeAway() { fOptimizedAway = true; } - bool linkedWeak() { return fInfo.options.fWeakImport; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - ExecutableFile::DyLibUsed fInfo; - bool fOptimizedAway; -}; - -template -class DylibIDLoadCommandsAtom : public LoadCommandAtom -{ -public: - DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} - virtual const char* getDisplayName() const { return "dylib ID load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class RoutinesLoadCommandsAtom : public LoadCommandAtom -{ -public: - RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} - virtual const char* getDisplayName() const { return "routines load command"; } - virtual uint64_t getSize() const { return sizeof(macho_routines_command); } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom -{ -public: - SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) - : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} - virtual const char* getDisplayName() const { return "sub-umbrella load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - typedef typename A::P P; - const char* fName; -}; - -template -class SubLibraryLoadCommandsAtom : public LoadCommandAtom -{ -public: - SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) - : LoadCommandAtom(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} - virtual const char* getDisplayName() const { return "sub-library load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fNameStart; - int fNameLength; -}; - -template -class UmbrellaLoadCommandsAtom : public LoadCommandAtom -{ -public: - UmbrellaLoadCommandsAtom(Writer& writer, const char* name) - : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} - virtual const char* getDisplayName() const { return "umbrella load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fName; -}; - -template -class UUIDLoadCommandAtom : public LoadCommandAtom -{ -public: - UUIDLoadCommandAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) {} - virtual const char* getDisplayName() const { return "uuid load command"; } - virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void generate(); - void setContent(const uint8_t uuid[16]); - const uint8_t* getUUID() { return fUUID; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uuid_t fUUID; - bool fEmit; -}; - - -template -class RPathLoadCommandsAtom : public LoadCommandAtom -{ -public: - RPathLoadCommandsAtom(Writer& writer, const char* path) - : LoadCommandAtom(writer, Segment::fgTextSegment), fPath(path) {} - virtual const char* getDisplayName() const { return "rpath load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fPath; -}; - -template -class EncryptionLoadCommandsAtom : public LoadCommandAtom -{ -public: - EncryptionLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fStartOffset(0), - fEndOffset(0) {} - virtual const char* getDisplayName() const { return "encryption info load command"; } - virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command); } - virtual void copyRawContent(uint8_t buffer[]) const; - void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; } - void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint32_t fStartOffset; - uint32_t fEndOffset; -}; - -template -class LoadCommandsPaddingAtom : public WriterAtom -{ -public: - LoadCommandsPaddingAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment), fSize(0) {} - virtual const char* getDisplayName() const { return "header padding"; } - virtual uint64_t getSize() const { return fSize; } - virtual const char* getSectionName() const { return "._load_cmds_pad"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void setSize(uint64_t newSize); -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint64_t fSize; -}; - -template -class LinkEditAtom : public WriterAtom -{ -public: - LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment), fOrdinal(fgCurrentOrdinal++) {} - uint64_t getFileOffset() const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } - virtual uint32_t getOrdinal() const { return fOrdinal; } -private: - uint32_t fOrdinal; - static uint32_t fgCurrentOrdinal; -private: - typedef typename A::P P; -}; - -template uint32_t LinkEditAtom::fgCurrentOrdinal = 0; - -template -class SectionRelocationsLinkEditAtom : public LinkEditAtom -{ -public: - SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "section relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._section_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class LocalRelocationsLinkEditAtom : public LinkEditAtom -{ -public: - LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "local relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._local_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class SymbolTableLinkEditAtom : public LinkEditAtom -{ -public: - SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "symbol table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._symbol_table"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class ExternalRelocationsLinkEditAtom : public LinkEditAtom -{ -public: - ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "external relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._extern_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -struct IndirectEntry { - uint32_t indirectIndex; - uint32_t symbolIndex; -}; - - -template -class SegmentSplitInfoContentAtom : public LinkEditAtom -{ -public: - SegmentSplitInfoContentAtom(Writer& writer) : LinkEditAtom(writer), fCantEncode(false) { } - virtual const char* getDisplayName() const { return "split segment info"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._split_info"; } - virtual void copyRawContent(uint8_t buffer[]) const; - bool canEncode() { return !fCantEncode; } - void setCantEncode() { fCantEncode = true; } - void add32bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind1Locations.push_back(AtomAndOffset(atom, offset)); } - void add64bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind2Locations.push_back(AtomAndOffset(atom, offset)); } - void addPPCHi16Location(const ObjectFile::Atom* atom, uint32_t offset) { fKind3Locations.push_back(AtomAndOffset(atom, offset)); } - void add32bitImportLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind4Locations.push_back(AtomAndOffset(atom, offset)); } - void encode(); - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - struct AtomAndOffset { - AtomAndOffset(const ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} - const ObjectFile::Atom* atom; - uint32_t offset; - }; - void uleb128EncodeAddresses(const std::vector& locations); - - std::vector fKind1Locations; - std::vector fKind2Locations; - std::vector fKind3Locations; - std::vector fKind4Locations; - std::vector fEncodedData; - bool fCantEncode; -}; - -template -class IndirectTableLinkEditAtom : public LinkEditAtom -{ -public: - IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "indirect symbol table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._indirect_syms"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - std::vector fTable; - -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class ModuleInfoLinkEditAtom : public LinkEditAtom -{ -public: - ModuleInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer), fModuleNameOffset(0) { } - virtual const char* getDisplayName() const { return "module table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._module_info"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); } - uint32_t getTableOfContentsFileOffset() const; - uint32_t getModuleTableFileOffset() const; - uint32_t getReferencesFileOffset() const; - uint32_t getReferencesCount() const; - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint32_t fModuleNameOffset; -}; - - -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - -template -class StringsLinkEditAtom : public LinkEditAtom -{ -public: - StringsLinkEditAtom(Writer& writer); - virtual const char* getDisplayName() const { return "string pool"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._string_pool"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - int32_t add(const char* name); - int32_t addUnique(const char* name); - int32_t emptyString() { return 1; } - const char* stringForIndex(int32_t) const; - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - enum { kBufferSize = 0x01000000 }; - typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; - - std::vector fFullBuffers; - char* fCurrentBuffer; - uint32_t fCurrentBufferUsed; - StringToOffset fUniqueStrings; -}; - - - -template -class UndefinedSymbolProxyAtom : public WriterAtom -{ -public: - UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, Segment::fgLinkEditSegment), fName(name) {} - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual uint64_t getSize() const { return 0; } - virtual const char* getSectionName() const { return "._imports"; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fName; -}; - -template -class BranchIslandAtom : public WriterAtom -{ -public: - BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "__text"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - const char* fName; - ObjectFile::Atom& fTarget; - uint32_t fTargetOffset; -}; - -template -class StubAtom : public WriterAtom -{ -public: - StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const; - virtual ObjectFile::Alignment getAlignment() const; - virtual const char* getSectionName() const { return "__symbol_stub1"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fTarget; } -private: - static const char* stubName(const char* importName); - bool pic() const { return fWriter.fSlideable; } - using WriterAtom::fWriter; - const char* fName; - ObjectFile::Atom& fTarget; - std::vector fReferences; - bool fForLazyDylib; -}; - -template -class StubHelperAtom : public WriterAtom -{ -public: - StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fTarget; } -private: - static const char* stubName(const char* importName); - using WriterAtom::fWriter; - const char* fName; - ObjectFile::Atom& fTarget; - std::vector fReferences; -}; - -template -class LazyPointerAtom : public WriterAtom -{ -public: - LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, - StubAtom& stub, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const { return fForLazyDylib ? "__ld_symbol_ptr" : "__la_symbol_ptr"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fExternalTarget; } -private: - using WriterAtom::fWriter; - static const char* lazyPointerName(const char* importName); - const char* fName; - ObjectFile::Atom& fTarget; - ObjectFile::Atom& fExternalTarget; - std::vector fReferences; - bool fForLazyDylib; -}; - - -template -class NonLazyPointerAtom : public WriterAtom -{ -public: - NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const { return "__nl_symbol_ptr"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fTarget; } -private: - using WriterAtom::fWriter; - static const char* nonlazyPointerName(const char* importName); - const char* fName; - ObjectFile::Atom& fTarget; - std::vector fReferences; -}; - - -template -class ObjCInfoAtom : public WriterAtom -{ -public: - ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses); - virtual const char* getName() const { return "objc$info"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const { return 8; } - virtual const char* getSectionName() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - Segment& getInfoSegment() const; - uint32_t fContent[2]; -}; - - -template -class WriterReference : public ObjectFile::Reference -{ -public: - typedef typename A::ReferenceKinds Kinds; - - WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, - uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) - : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), - fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} - - virtual ~WriterReference() {} - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return (uint8_t)fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return fTarget->getName(); } - virtual ObjectFile::Atom& getTarget() const { return *fTarget; } - virtual uint64_t getTargetOffset() const { return fTargetOffset; } - virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } - virtual const char* getFromTargetName() const { return fFromTarget->getName(); } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; } - virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } - virtual void setFromTargetName(const char* name) { } - virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; } - virtual const char* getDescription() const { return "writer reference"; } - virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } - -private: - Kinds fKind; - uint32_t fFixUpOffsetInSrc; - ObjectFile::Atom* fTarget; - uint32_t fTargetOffset; - ObjectFile::Atom* fFromTarget; - uint32_t fFromTargetOffset; -}; - - - -template <> -StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, - ObjectFile::Atom& lazyPointer, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubHelpers.push_back(this); - - fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldLazyDylibHelper)); - } - else { - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); - } -} - -template <> -uint64_t StubHelperAtom::getSize() const -{ - return 12; -} - -template <> -void StubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 - buffer[1] = 0x8D; - buffer[2] = 0x1D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0xE9; // jmp dyld_stub_binding_helper - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; -} - - -template -const char* StubHelperAtom::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stubHelper", name); - return buf; -} - - -// specialize lazy pointer for x86_64 to initially pointer to stub helper -template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - StubHelperAtom* helper = new StubHelperAtom(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); -} - -// specialize lazy pointer for x86 to initially pointer to second half of stub -template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - // helper part of stub is 14 or 6 bytes into stub - fReferences.push_back(new WriterReference(0, x86::kPointer, &stub, writer.fSlideable ? 14 : 6)); -} - -template -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); -} - - - -template -const char* LazyPointerAtom::lazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$lazy_pointer", name); - return buf; -} - -template -void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - -template -NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); - - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); -} - -template -const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$non_lazy_pointer", name); - return buf; -} - -template -void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - - -template <> -bool StubAtom::pic() const -{ - // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB. - // Usually that only happens if page zero is very large - return ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ); -} - - -template <> -bool StubAtom::pic() const -{ - return fWriter.fSlideable; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - writer.fAllSynthesizedStubs.push_back(this); - LazyPointerAtom* lp; - if ( fWriter.fOptions.prebind() ) { - // for prebound ppc, lazy pointer starts out pointing to target symbol's address - // if target is a weak definition within this linkage unit or zero if in some dylib - lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - } - else { - // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); - } - else { - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); - } - } - if ( pic() ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, this, 8)); - fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, this, 8)); - } - else { - fReferences.push_back(new WriterReference(0, ppc::kAbsHigh16AddLow, lp)); - fReferences.push_back(new WriterReference(4, ppc::kAbsLow16, lp)); - } -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); - } - else { - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); - } - if ( pic() ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, this, 8)); - fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, this, 8)); - } - else { - fReferences.push_back(new WriterReference(0, ppc64::kAbsHigh16AddLow, lp)); - fReferences.push_back(new WriterReference(4, ppc64::kAbsLow14, lp)); - } -} - -// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, (writer.fOptions.slowx86Stubs() || forLazyDylib) ? Segment::fgTextSegment : - ( writer.fOptions.readOnlyx86Stubs() ? Segment::fgROImportSegment : Segment::fgImportSegment)), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - if ( writer.fOptions.slowx86Stubs() || forLazyDylib ) { - fName = stubName(target.getName()); - writer.fAllSynthesizedStubs.push_back(this); - LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - ObjectFile::Atom* helper; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - helper = writer.fDyldLazyDylibHelper; - } - else { - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldHelper; - } - if ( pic() ) { - // picbase is 5 bytes into atom - fReferences.push_back(new WriterReference(8, x86::kPointerDiff, lp, 0, this, 5)); - fReferences.push_back(new WriterReference(16, x86::kPCRel32, helper)); - } - else { - fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); - fReferences.push_back(new WriterReference(7, x86::kAbsolute32, lp)); - fReferences.push_back(new WriterReference(12, x86::kPCRel32, helper)); - } - } - else { - if ( &target == NULL ) - fName = "cache-line-crossing-stub"; - else { - fName = stubName(target.getName()); - writer.fAllSynthesizedStubs.push_back(this); - } - } -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, lp)); -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp; - if ( fWriter.fOptions.prebind() && !forLazyDylib ) { - // for prebound arm, lazy pointer starts out pointing to target symbol's address - // if target is a weak definition within this linkage unit or zero if in some dylib - lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - } - else { - // for non-prebound arm, lazy pointer starts out pointing to dyld_stub_binding_helper glue code - ObjectFile::Atom* helper; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - helper = writer.fDyldLazyDylibHelper; - } - else { - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldHelper; - } - lp = new LazyPointerAtom(writer, *helper, *this, forLazyDylib); - } - if ( pic() ) - fReferences.push_back(new WriterReference(12, arm::kPointerDiff, lp, 0, this, 12)); - else - fReferences.push_back(new WriterReference(8, arm::kPointer, lp)); -} - -template -const char* StubAtom::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stub", name); - return buf; -} - -template <> -uint64_t StubAtom::getSize() const -{ - return ( pic() ? 32 : 16 ); -} - -template <> -uint64_t StubAtom::getSize() const -{ - return ( pic() ? 32 : 16 ); -} - - -template <> -uint64_t StubAtom::getSize() const -{ - return ( pic() ? 16 : 12 ); -} - -template <> -uint64_t StubAtom::getSize() const -{ - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { - if ( pic() ) - return 20; - else - return 16; - } - return 5; -} - -template <> -uint64_t StubAtom::getSize() const -{ - return 6; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) - return 2; - else - return 0; // special case x86 fast stubs to be byte aligned -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( pic() ) { - OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - else { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) - OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( pic() ) { - OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - else { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) - OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { - if ( pic() ) { - buffer[0] = 0xE8; // call picbase - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x58; // pop eax - buffer[6] = 0x8D; // lea foo$lazy_pointer-picbase(eax),eax - buffer[7] = 0x80; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; - buffer[12] = 0xFF; // jmp *(eax) - buffer[13] = 0x20; - buffer[14] = 0x50; // push eax - buffer[15] = 0xE9; // jump dyld_stub_binding_helper - buffer[16] = 0x00; - buffer[17] = 0x00; - buffer[18] = 0x00; - buffer[19] = 0x00; - } - else { - buffer[0] = 0xFF; // jmp *foo$lazy_pointer - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x68; // pushl $foo$lazy_pointer - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0xE9; // jump dyld_stub_binding_helper - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - } - } - else { - if ( fWriter.fOptions.prebind() ) { - uint32_t address = this->getAddress(); - int32_t rel32 = 0 - (address+5); - buffer[0] = 0xE9; - buffer[1] = rel32 & 0xFF; - buffer[2] = (rel32 >> 8) & 0xFF; - buffer[3] = (rel32 >> 16) & 0xFF; - buffer[4] = (rel32 >> 24) & 0xFF; - } - else { - buffer[0] = 0xF4; - buffer[1] = 0xF4; - buffer[2] = 0xF4; - buffer[3] = 0xF4; - buffer[4] = 0xF4; - } - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( pic() ) { - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 - OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) - } - else { - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] - OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr - } -} - -// x86_64 stubs are 7 bytes and need no alignment -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 0; -} - -template <> -const char* StubAtom::getSectionName() const -{ - return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); -} - -template <> -const char* StubAtom::getSectionName() const -{ - return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); -} - -template <> -const char* StubAtom::getSectionName() const -{ - return ( pic() ? "__picsymbolstub4" : "__symbol_stub4"); -} - -template <> -const char* StubAtom::getSectionName() const -{ - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { - if ( pic() ) - return "__picsymbol_stub"; - else - return "__symbol_stub"; - } - return "__jump_table"; -} - - - - -struct AtomByNameSorter -{ - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) - { - return (strcmp(left->getName(), right->getName()) < 0); - } -}; - -template -struct ExternalRelocSorter -{ - bool operator()(const macho_relocation_info

& left, const macho_relocation_info

& right) - { - // sort first by symbol number - if ( left.r_symbolnum() != right.r_symbolnum() ) - return (left.r_symbolnum() < right.r_symbolnum()); - // then sort all uses of the same symbol by address - return (left.r_address() < right.r_address()); - } -}; - - -template -Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) - : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), - fAllAtoms(NULL), fStabs(NULL), fLoadCommandsSection(NULL), - fLoadCommandsSegment(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), - fSymbolTableCommands(NULL), fHeaderPadding(NULL), - fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), fDyldHelper(NULL), fDyldLazyDylibHelper(NULL), - fSectionRelocationsAtom(NULL), fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), - fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL), - fStringsAtom(NULL), fPageZeroAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), fSymbolTableStabsCount(0), - fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), - fLargestAtomSize(1), - fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), - fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), - fBiggerThanTwoGigs(false), fSlideable(false), - fFirstWritableSegment(NULL), fAnonNameIndex(1000) -{ - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - if ( fOptions.zeroPageSize() != 0 ) - fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom(*this)); - if ( fOptions.outputKind() == Options::kDynamicExecutable ) - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - if ( fOptions.outputKind() == Options::kDynamicExecutable ) - fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); - if ( fOptions.hasCustomStack() ) - fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - // fall through - case Options::kObjectFile: - fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - if ( fOptions.outputKind() == Options::kDynamicLibrary ) { - fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); - if ( fOptions.initFunctionName() != NULL ) - fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); - } - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - if ( fOptions.sharedRegionEligible() ) - fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - if ( fOptions.sharedRegionEligible() ) { - fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom(*this)); - } - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - if ( this->needsModuleTable() ) - fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - case Options::kDyld: - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - } - - // add extra commmands - bool hasReExports = false; - uint32_t ordinal = 1; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - if ( fOptions.makeEncryptable() ) { - fEncryptionLoadCommand = new EncryptionLoadCommandsAtom(*this); - fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand); - } - // fall through - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - { - // add dylib load command atoms for all dynamic libraries - const unsigned int libCount = dynamicLibraries.size(); - for (unsigned int i=0; i < libCount; ++i) { - ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; - //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() ); - - if ( dylibInfo.options.fReExport ) { - hasReExports = true; - } - else { - const char* parentUmbrella = dylibInfo.reader->parentUmbrella(); - if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) { - const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/'); - if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) ) - hasReExports = true; - } - } - - if ( dylibInfo.options.fBundleLoader ) { - fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL; - } - else { - // see if a DylibLoadCommandsAtom has already been created for this install path - bool newDylib = true; - const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); - for (unsigned int seenLib=0; seenLib < i; ++seenLib) { - ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; - if ( !seenDylibInfo.options.fBundleLoader ) { - const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); - if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { - fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; - fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader]; - fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader; - newDylib = false; - break; - } - } - } - - if ( newDylib ) { - // assign new ordinal and check for other paired load commands - fLibraryToOrdinal[dylibInfo.reader] = ordinal++; - DylibLoadCommandsAtom* dyliblc = new DylibLoadCommandsAtom(*this, dylibInfo); - fLibraryToLoadCommand[dylibInfo.reader] = dyliblc; - fWriterSynthesizedAtoms.push_back(dyliblc); - if ( dylibInfo.options.fReExport - && (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) - && (fOptions.outputKind() == Options::kDynamicLibrary) ) { - // see if child has sub-framework that is this - bool isSubFramework = false; - const char* childInUmbrella = dylibInfo.reader->parentUmbrella(); - if ( childInUmbrella != NULL ) { - const char* myLeaf = strrchr(fOptions.installPath(), '/'); - if ( myLeaf != NULL ) { - if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) - isSubFramework = true; - } - } - // LC_SUB_FRAMEWORK is in child, so do nothing in parent - if ( ! isSubFramework ) { - // this dylib also needs a sub_x load command - bool isFrameworkReExport = false; - const char* lastSlash = strrchr(dylibInstallPath, '/'); - if ( lastSlash != NULL ) { - char frameworkName[strlen(lastSlash)+20]; - sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); - isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); - } - if ( isFrameworkReExport ) { - // needs a LC_SUB_UMBRELLA command - fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1])); - } - else { - // needs a LC_SUB_LIBRARY command - const char* nameStart = &lastSlash[1]; - if ( lastSlash == NULL ) - nameStart = dylibInstallPath; - int len = strlen(nameStart); - const char* dot = strchr(nameStart, '.'); - if ( dot != NULL ) - len = dot - nameStart; - fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len)); - } - } - } - } - } - } - // add umbrella command if needed - if ( fOptions.umbrellaName() != NULL ) { - fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); - } - // add allowable client commands if used - std::vector& allowableClients = fOptions.allowableClients(); - for (std::vector::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it) - fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom(*this, *it)); - } - break; - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kDyld: - break; - } - fNoReExportedDylibs = !hasReExports; - - // add any rpath load commands - for(std::vector::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) { - fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom(*this, *it)); - } - - // set up fSlideable - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kStaticExecutable: - fSlideable = false; - break; - case Options::kDynamicExecutable: - fSlideable = fOptions.positionIndependentExecutable(); - break; - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - fSlideable = true; - break; - } - - //fprintf(stderr, "ordinals table:\n"); - //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); - //} -} - -template -Writer::~Writer() -{ - if ( fFilePath != NULL ) - free((void*)fFilePath); - if ( fSymbolTable != NULL ) - delete [] fSymbolTable; -} - - -// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments -template <>bool Writer::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); } -template bool Writer::mightNeedPadSegment() { return false; } - - -template -ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) -{ - if ( fOptions.outputKind() == Options::kObjectFile ) { - // when doing -r -exported_symbols_list, don't creat proxy for a symbol - // that is supposed to be exported. We want an error instead - // ld does not report error when -r is used and exported symbols are not defined. - if ( fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) - return NULL; - else - return new UndefinedSymbolProxyAtom(*this, name); - } - else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) ) - return new UndefinedSymbolProxyAtom(*this, name); - else - return NULL; -} - -template -uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) -{ - // flat namespace images use zero for all ordinals - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) - return 0; - - // is an UndefinedSymbolProxyAtom - if ( lib == this ) - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) - return DYNAMIC_LOOKUP_ORDINAL; - - std::map::iterator pos = fLibraryToOrdinal.find(lib); - if ( pos != fLibraryToOrdinal.end() ) - return pos->second; - - throw "can't find ordinal for imported symbol"; -} - -template -ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) -{ - return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses)); -} - - -template -uint64_t Writer::write(std::vector& atoms, - std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, bool overridesDylibWeakDefines) -{ - fAllAtoms = &atoms; - fStabs = &stabs; - fEntryPoint = entryPointAtom; - fDyldHelper = dyldHelperAtom; - fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; - fCanScatter = canScatter; - fCpuConstraint = cpuConstraint; - fBiggerThanTwoGigs = biggerThanTwoGigs; - fHasWeakExports = overridesDylibWeakDefines; // dyld needs to search this image as if it had weak exports - - try { - // Set for create UUID - if (createUUID) - fUUIDAtom->generate(); - - // remove uneeded dylib load commands - optimizeDylibReferences(); - - // check for mdynamic-no-pic codegen - scanForAbsoluteReferences(); - - // create inter-library stubs - synthesizeStubs(); - - // create SegmentInfo and SectionInfo objects and assign all atoms to a section - partitionIntoSections(); - - // segment load command can now be sized and padding can be set - adjustLoadCommandsAndPadding(); - - // assign each section a file offset - assignFileOffsets(); - - // if need to add branch islands, reassign file offsets - if ( addBranchIslands() ) - assignFileOffsets(); - - // build symbol table and relocations - buildLinkEdit(); - - // write map file if requested - writeMap(); - - // write everything - return writeAtoms(); - } catch (...) { - // clean up if any errors - (void)unlink(fFilePath); - throw; - } -} - -template -void Writer::buildLinkEdit() -{ - this->collectExportedAndImportedAndLocalAtoms(); - this->buildSymbolTable(); - this->buildFixups(); - this->adjustLinkEditSections(); -} - - - -template -uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) -{ - return atom->getAddress(); -// SectionInfo* info = (SectionInfo*)atom->getSection(); -// return info->getBaseAddress() + atom->getSectionOffset(); -} - - -template <> -const char* Writer::symbolTableName(const ObjectFile::Atom* atom) -{ - static unsigned int counter = 0; - const char* name = atom->getName(); - if ( strncmp(name, "cstring=", 8) == 0 ) - asprintf((char**)&name, "LC%u", counter++); - return name; -} - -template -const char* Writer::symbolTableName(const ObjectFile::Atom* atom) -{ - return atom->getName(); -} - -template -void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) -{ - // set n_strx - entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom))); - - // set n_type - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { - entry->set_n_type(N_EXT | N_ABS); - } - else { - entry->set_n_type(N_EXT | N_SECT); - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { - if ( fOptions.keepPrivateExterns() ) - entry->set_n_type(N_EXT | N_SECT | N_PEXT); - } - } - - // set n_sect (section number of implementation ) - uint8_t sectionIndex = atom->getSection()->getIndex(); - entry->set_n_sect(sectionIndex); - - // the __mh_execute_header is magic and must be an absolute symbol - if ( (sectionIndex==0) - && (fOptions.outputKind() == Options::kDynamicExecutable) - && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) - entry->set_n_type(N_EXT | N_ABS); - - // set n_desc - uint16_t desc = 0; - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) - desc |= REFERENCED_DYNAMICALLY; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - desc |= N_WEAK_DEF; - fHasWeakExports = true; - } - entry->set_n_desc(desc); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - entry->set_n_value(atom->getSectionOffset()); - else - entry->set_n_value(this->getAtomLoadAddress(atom)); -} - -template -void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) -{ - // set n_strx - entry->set_n_strx(this->fStringsAtom->add(atom->getName())); - - // set n_type - if ( (fOptions.outputKind() == Options::kObjectFile) - && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) - entry->set_n_type(N_UNDF | N_EXT | N_PEXT); - else if ( fOptions.prebind() ) - entry->set_n_type(N_PBUD | N_EXT); - else - entry->set_n_type(N_UNDF | N_EXT); - - // set n_sect - entry->set_n_sect(0); - - uint16_t desc = 0; - if ( fOptions.outputKind() != Options::kObjectFile ) { - // set n_desc ( high byte is library ordinal, low byte is reference type ) - std::map::iterator pos = fStubsMap.find(atom); - if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) - desc = REFERENCE_FLAG_UNDEFINED_LAZY; - else - desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY; - try { - uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); - //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); - SET_LIBRARY_ORDINAL(desc, ordinal); - } - catch (const char* msg) { - throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) { - uint8_t align = atom->getAlignment().powerOf2; - // always record custom alignment of common symbols to match what compiler does - SET_COMM_ALIGN(desc, align); - } - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) - desc |= REFERENCED_DYNAMICALLY; - if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - desc |= N_REF_TO_WEAK; - fReferencesWeakImports = true; - } - // set weak_import attribute - if ( fWeakImportMap[atom] ) - desc |= N_WEAK_REF; - entry->set_n_desc(desc); - - // set n_value, zero for import proxy and size for tentative definition - entry->set_n_value(atom->getSize()); -} - - -template -void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) -{ - // set n_strx - const char* symbolName = this->symbolTableName(atom); - char anonName[32]; - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { - sprintf(anonName, "l%u", fAnonNameIndex++); - symbolName = anonName; - } - entry->set_n_strx(this->fStringsAtom->add(symbolName)); - - // set n_type - uint8_t type = N_SECT; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - type = N_ABS; - if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) - type |= N_PEXT; - entry->set_n_type(type); - - // set n_sect (section number of implementation ) - uint8_t sectIndex = atom->getSection()->getIndex(); - if ( sectIndex == 0 ) { - // see synthesized lable for mach_header needs special section number... - if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) - sectIndex = 1; - } - entry->set_n_sect(sectIndex); - - // set n_desc - uint16_t desc = 0; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - desc |= N_WEAK_DEF; - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - entry->set_n_desc(desc); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - entry->set_n_value(atom->getSectionOffset()); - else - entry->set_n_value(this->getAtomLoadAddress(atom)); -} - - -template -void Writer::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) -{ - macho_nlist

entry; - - // set n_strx - entry.set_n_strx(fStringsAtom->add(name)); - - // set n_type - entry.set_n_type(N_SECT); - - // set n_sect (section number of implementation ) - entry.set_n_sect(atom.getSection()->getIndex()); - - // set n_desc - entry.set_n_desc(0); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); - - // add - fLocalExtraLabels.push_back(entry); -} - - - -template -void Writer::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) -{ - macho_nlist

entry; - - // set n_strx - entry.set_n_strx(fStringsAtom->add(name)); - - // set n_type - entry.set_n_type(N_SECT|N_EXT); - - // set n_sect (section number of implementation ) - entry.set_n_sect(atom.getSection()->getIndex()); - - // set n_desc - entry.set_n_desc(0); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); - - // add - fGlobalExtraLabels.push_back(entry); -} - -template -void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) -{ - macho_nlist

* entry = &fSymbolTable[startIndex]; - for (uint32_t i=0; i < count; ++i, ++entry) { - ObjectFile::Atom* atom = atoms[i]; - if ( &atoms == &fExportedAtoms ) { - this->setExportNlist(atom, entry); - } - else if ( &atoms == &fImportedAtoms ) { - this->setImportNlist(atom, entry); - } - else { - this->setLocalNlist(atom, entry); - } - } -} - -template -void Writer::copyNlistRange(const std::vector >& entries, uint32_t startIndex) -{ - for ( typename std::vector >::const_iterator it = entries.begin(); it != entries.end(); ++it) - fSymbolTable[startIndex++] = *it; -} - - -template -struct NListNameSorter -{ - NListNameSorter(StringsLinkEditAtom* pool) : fStringPool(pool) {} - - bool operator()(const macho_nlist& left, const macho_nlist& right) - { - return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0); - } -private: - StringsLinkEditAtom* fStringPool; -}; - - -template -void Writer::buildSymbolTable() -{ - fSymbolTableStabsStartIndex = 0; - fSymbolTableStabsCount = fStabs->size(); - fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; - fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size(); - fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; - fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size(); - fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; - fSymbolTableImportCount = fImportedAtoms.size(); - - // allocate symbol table - fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; - fSymbolTable = new macho_nlist

[fSymbolTableCount]; - - // fill in symbol table and string pool (do stabs last so strings are at end of pool) - setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size()); - if ( fLocalExtraLabels.size() != 0 ) - copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size()); - setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size()); - if ( fGlobalExtraLabels.size() != 0 ) { - copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size()); - // re-sort combined range - std::sort( &fSymbolTable[fSymbolTableExportStartIndex], - &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount], - NListNameSorter(fStringsAtom) ); - } - setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); - addStabs(fSymbolTableStabsStartIndex); - - // set up module table - if ( fModuleInfoAtom != NULL ) - fModuleInfoAtom->setName(); -} - - - -template -bool Writer::shouldExport(const ObjectFile::Atom& atom) const -{ - switch ( atom.getSymbolTableInclusion() ) { - case ObjectFile::Atom::kSymbolTableNotIn: - return false; - case ObjectFile::Atom::kSymbolTableInAndNeverStrip: - return true; - case ObjectFile::Atom::kSymbolTableInAsAbsolute: - case ObjectFile::Atom::kSymbolTableIn: - switch ( atom.getScope() ) { - case ObjectFile::Atom::scopeGlobal: - return true; - case ObjectFile::Atom::scopeLinkageUnit: - return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); - default: - return false; - } - break; - } - return false; -} - -template -void Writer::collectExportedAndImportedAndLocalAtoms() -{ - const int atomCount = fAllAtoms->size(); - // guess at sizes of each bucket to minimize re-allocations - fImportedAtoms.reserve(100); - fExportedAtoms.reserve(atomCount/2); - fLocalSymbolAtoms.reserve(atomCount); - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - // only named atoms go in symbol table - if ( atom->getName() != NULL ) { - // put atom into correct bucket: imports, exports, locals - //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - fImportedAtoms.push_back(atom); - break; - case ObjectFile::Atom::kTentativeDefinition: - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { - fImportedAtoms.push_back(atom); - break; - } - // else fall into - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - if ( this->shouldExport(*atom) ) - fExportedAtoms.push_back(atom); - else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) - && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) - fLocalSymbolAtoms.push_back(atom); - break; - } - } - // when geneating a .o file, dtrace static probes become local labels - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); - } - } - } - // when linking kernel, old style dtrace static probes become global labels - else if ( fOptions.readerOptions().fForStatic ) { - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); - } - } - } - } - - // sort exported atoms by name - std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter()); - // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable) - std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter()); -} - - -template -uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) -{ - switch ( stab.type ) { - case N_FUN: - if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { - // end of function N_FUN has size - return stab.atom->getSize(); - } - else { - // start of function N_FUN has address - return getAtomLoadAddress(stab.atom); - } - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - if ( stab.atom == NULL ) - // some weird assembly files have slines not associated with a function - return stab.value; - else - // all these stab types need their value changed from an offset in the atom to an address - return getAtomLoadAddress(stab.atom) + stab.value; - case N_STSYM: - case N_LCSYM: - case N_BNSYM: - // all these need address of atom - return getAtomLoadAddress(stab.atom);; - case N_ENSYM: - return stab.atom->getSize(); - case N_SO: - if ( stab.atom == NULL ) { - return 0; - } - else { - if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { - // end of translation unit N_SO has address of end of last atom - return getAtomLoadAddress(stab.atom) + stab.atom->getSize(); - } - else { - // start of translation unit N_SO has address of end of first atom - return getAtomLoadAddress(stab.atom); - } - } - break; - default: - return stab.value; - } -} - -template -uint32_t Writer::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) -{ - switch (stab.type) { - case N_SO: - if ( (stab.string == NULL) || stab.string[0] == '\0' ) { - return this->fStringsAtom->emptyString(); - break; - } - // fall into uniquing case - case N_SOL: - case N_BINCL: - case N_EXCL: - return this->fStringsAtom->addUnique(stab.string); - break; - default: - if ( stab.string == NULL ) - return 0; - else if ( stab.string[0] == '\0' ) - return this->fStringsAtom->emptyString(); - else - return this->fStringsAtom->add(stab.string); - } - return 0; -} - -template -uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) -{ - // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN - if ( stab.type == N_FUN ) - return stab.other; - else if ( stab.atom != NULL ) - return stab.atom->getSection()->getIndex(); - else - return stab.other; -} - -template -void Writer::addStabs(uint32_t startIndex) -{ - macho_nlist

* entry = &fSymbolTable[startIndex]; - for(std::vector::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) { - const ObjectFile::Reader::Stab& stab = *it; - entry->set_n_type(stab.type); - entry->set_n_sect(sectionIndexForStab(stab)); - entry->set_n_desc(stab.desc); - entry->set_n_value(valueForStab(stab)); - entry->set_n_strx(stringOffsetForStab(stab)); - } -} - - - -template -uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) -{ - // search imports - int i = 0; - for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableImportStartIndex; - ++i; - } - - // search locals - i = 0; - for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableLocalStartIndex; - ++i; - } - - // search exports - i = 0; - for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableExportStartIndex; - ++i; - } - - throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); -} - - -template <> -bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const -{ - switch ( target.getSymbolTableInclusion() ) { - case ObjectFile::Atom::kSymbolTableNotIn: - return false; - case ObjectFile::Atom::kSymbolTableInAsAbsolute: - case ObjectFile::Atom::kSymbolTableIn: - case ObjectFile::Atom::kSymbolTableInAndNeverStrip: - return true; - }; - return false; -} - -template -bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kTentativeDefinition: - if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal ) - return false; - else - return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit); - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - return shouldExport(target); - } - return false; -} - -template -void Writer::buildFixups() -{ - if ( fOptions.outputKind() == Options::kObjectFile ) { - this->buildObjectFileFixups(); - } - else { - if ( fOptions.keepRelocations() ) - this->buildObjectFileFixups(); - this->buildExecutableFixups(); - } -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool external = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind(); - - switch ( kind ) { - case x86_64::kNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - return 0; - - case x86_64::kPointer: - case x86_64::kPointerWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(3); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointerDiff32: - case x86_64::kPointerDiff: - { - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); - uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex(); - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - reloc2.set_r_address(address); - reloc2.set_r_symbolnum(fromSymbolIndex); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); - reloc2.set_r_extern(fromExternal); - reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); - fSectionRelocs.push_back(reloc1); - fSectionRelocs.push_back(reloc2); - return 2; - } - - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kDtraceProbeSite: - case x86_64::kDtraceIsEnabledSite: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_BRANCH); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_1: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_1); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_2: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_2); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_4: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_4); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kBranchPCRel8: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(0); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_BRANCH); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_GOT); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // generates no relocs - return 0; - } - return 0; -} - - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); - - if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) - warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", - target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); - - - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - return 0; - - case x86::kPointer: - case x86::kPointerWeakImport: - case x86::kAbsolute32: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case x86::kPointerDiff16: - case x86::kPointerDiff: - { - //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n", - // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(), - // ref->getFromTarget().getAddress(), ref->getFromTargetOffset()); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); - sreloc2->set_r_type(GENERIC_RELOC_PAIR); - sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case x86::kPCRel32WeakImport: - case x86::kPCRel32: - case x86::kPCRel16: - case x86::kPCRel8: - case x86::kDtraceProbeSite: - case x86::kDtraceIsEnabledSite: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case x86::kDtraceTypeReference: - case x86::kDtraceProbe: - // generates no relocs - return 0; - - } - return 0; -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind(); - - if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) - warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", - target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); - - - switch ( kind ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - return 0; - - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kPointerDiff: - { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(ARM_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(ARM_RELOC_PAIR); - sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case arm::kBranch24WeakImport: - case arm::kBranch24: - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_RELOC_BR24); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_THUMB_RELOC_BR22); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kDtraceTypeReference: - case arm::kDtraceProbe: - // generates no relocs - return 0; - - } - return 0; -} - -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } - -template <> -uint8_t Writer::getRelocPointerSize() -{ - return 2; -} - -template <> -uint8_t Writer::getRelocPointerSize() -{ - return 3; -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - return addObjectRelocs_powerpc(atom, ref); -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - return addObjectRelocs_powerpc(atom, ref); -} - -// -// addObjectRelocs and addObjectRelocs are almost exactly the same, so -// they use a common addObjectRelocs_powerpc() method. -// -template -uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind(); - - switch ( kind ) { - case A::kNoFixUp: - case A::kFollowOn: - case A::kGroupSubordinate: - return 0; - - case A::kPointer: - case A::kPointerWeakImport: - if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) { - // use scattered reloc is target offset is outside target - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(getRelocPointerSize()); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(getRelocPointerSize()); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kPointerDiff16: - case A::kPointerDiff32: - case A::kPointerDiff64: - { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(PPC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(sreloc1->r_length()); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kBranch24WeakImport: - case A::kBranch24: - case A::kDtraceProbeSite: - case A::kDtraceIsEnabledSite: - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_type(PPC_RELOC_BR24); - reloc1.set_r_extern(isExtern); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kBranch14: - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_type(PPC_RELOC_BR14); - reloc1.set_r_extern(isExtern); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kPICBaseLow16: - case A::kPICBaseLow14: - { - pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kPICBaseHigh16: - { - pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsLow14: - case A::kAbsLow16: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() >> 16); - else - reloc2.set_r_address(toAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsHigh16: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(PPC_RELOC_HI16); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HI16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); - else - reloc2.set_r_address(toAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsHigh16AddLow: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - uint32_t overflow = 0; - if ( (toAddr & 0x00008000) != 0 ) - overflow = 0x10000; - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(PPC_RELOC_HA16); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); - else - reloc2.set_r_address(toAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kDtraceTypeReference: - case A::kDtraceProbe: - // generates no relocs - return 0; - } - return 0; -} - - - -// -// There are cases when an entry in the indirect symbol table is the magic value -// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens -// the content of the corresponding part of the __nl_symbol_pointer section -// must also change. -// -template -bool Writer::indirectSymbolIsLocal(const ObjectFile::Reference* ref) const -{ - // use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend - return ( !this->shouldExport(ref->getTarget()) || (ref->getTargetOffset() != 0) ); -} - - -template -void Writer::buildObjectFileFixups() -{ - uint32_t relocIndex = 0; - std::vector& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName); - std::vector& sectionAtoms = curSection->fAtoms; - if ( ! curSection->fAllZeroFill ) { - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers - || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); - curSection->fRelocOffset = relocIndex; - const int atomCount = sectionAtoms.size(); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; - //fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName()); - std::vector& refs = atom->getReferences(); - const int refCount = refs.size(); - for (int l=0; l < refCount; ++l) { - ObjectFile::Reference* ref = refs[l]; - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers - || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) { - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / atom->getSize(); - uint32_t undefinedSymbolIndex; - if ( curSection->fAllStubs ) { - ObjectFile::Atom& stubTarget =ref->getTarget(); - ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget(); - undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); - //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); - } - else if ( curSection->fAllNonLazyPointers) { - // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend - if ( this->indirectSymbolIsLocal(ref) ) - undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - else - undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); - } - else { - // should never get here, fAllLazyPointers not used in generated .o files - undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - } - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize()); - fIndirectTableAtom->fTable.push_back(entry); - if ( curSection->fAllLazyPointers ) { - ObjectFile::Atom& target = ref->getTarget(); - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - if ( &fromTarget == NULL ) { - warning("lazy pointer %s missing initial binding", atom->getDisplayName()); - } - else { - bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition)) - && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ); - macho_relocation_info

reloc1; - reloc1.set_r_address(atom->getSectionOffset()); - reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - fSectionRelocs.push_back(reloc1); - ++relocIndex; - } - } - else if ( curSection->fAllStubs ) { - relocIndex += this->addObjectRelocs(atom, ref); - } - } - else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) { - relocIndex += this->addObjectRelocs(atom, ref); - } - } - } - curSection->fRelocCount = relocIndex - curSection->fRelocOffset; - } - } - } - - // reverse the relocs - std::reverse(fSectionRelocs.begin(), fSectionRelocs.end()); - - // now reverse section reloc offsets - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; - } - } - -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) - return true; - } - return false; -} - - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) - return true; - } - return false; -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return fSlideable; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // illegal until we support TEXT relocs - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - // absolute symbbols only allowed in static executables - return ( fOptions.outputKind() != Options::kStaticExecutable); - } - } - return false; -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - return false; -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - if ( ref.getKind() == arm::kReadOnlyPointer ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return fSlideable; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // illegal until we support TEXT relocs - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - // absolute symbbols only allowed in static executables - return ( fOptions.outputKind() != Options::kStaticExecutable); - } - } - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - macho_relocation_info

reloc; - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(sectInfo->getIndex()); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(false); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(reloc); - atomSection->fHasTextLocalRelocs = true; - return true; - } - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); - reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc1.set_r_symbolnum(sectInfo->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - reloc2.set_r_address(targetAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fInternalRelocs.push_back(reloc1); - fInternalRelocs.push_back(reloc2); - atomSection->fHasTextLocalRelocs = true; - return true; - } - break; - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) { - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); - reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc1.set_r_symbolnum(sectInfo->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); - reloc2.set_r_address(targetAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fInternalRelocs.push_back(reloc1); - fInternalRelocs.push_back(reloc2); - atomSection->fHasTextLocalRelocs = true; - return true; - } - } - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == arm::kReadOnlyPointer ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - macho_relocation_info

reloc; - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(sectInfo->getIndex()); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(false); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(reloc); - atomSection->fHasTextLocalRelocs = true; - return true; - } - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - // text relocs not supported (usually never needed because of RIP addressing) - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - // text relocs not supported - return false; -} - -template <> -bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - macho_relocation_info

reloc; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // a reference to the absolute address of something in another linkage unit can be - // encoded as an external text reloc in a dylib or bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(true); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - -template -bool Writer::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - return false; -} - - - - -template -typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - // in main executables, the only way regular symbols are indirected is if -interposable is used - if ( fOptions.outputKind() == Options::kDynamicExecutable ) { - if ( this->shouldExport(target) && fOptions.interposable(target.getName()) ) - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - } - // for flat-namespace or interposable two-level-namespace - // all references to exported symbols get indirected - else if ( this->shouldExport(target) && - ((fOptions.nameSpace() == Options::kFlatNameSpace) - || (fOptions.nameSpace() == Options::kForceFlatNameSpace) - || fOptions.interposable(target.getName())) - && (target.getName() != NULL) - && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - case ObjectFile::Atom::kWeakDefinition: - // all calls to global weak definitions get indirected - if ( this->shouldExport(target) ) - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - return kRelocExternal; - case ObjectFile::Atom::kAbsoluteSymbol: - return kRelocNone; - } - return kRelocNone; -} - -template -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for 32-bit architectures, the r_address field in relocs - // for final linked images is the offset from the first segment - uint64_t result = address - fSegmentInfos[0]->fBaseAddress; - // or the offset from the first writable segment if built split-seg - if ( fOptions.splitSeg() ) - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0x7FFFFFFF ) { - throwf("image too large: address can't fit in 31-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - -template <> -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for x86_64, the r_address field in relocs for final linked images - // is the offset from the start address of the first writable segment - uint64_t result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - -template <> -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address. - // the 10.5 dyld, iterprets the r_address as: - // 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise - // 2) an offset from the base address of the first writable segment - // For dyld, r_address is always the offset from the base address - uint64_t result; - bool badFor10_4 = false; - if ( fWritableSegmentPastFirst4GB ) { - if ( fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5 ) - badFor10_4 = true; - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - result = address - fSegmentInfos[0]->fBaseAddress; - if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) && (result > 0x7FFFFFFF) ) - badFor10_4 = true; - } - if ( badFor10_4 ) { - throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - - -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; } - -template -void Writer::buildExecutableFixups() -{ - fIndirectTableAtom->fTable.reserve(50); // minimize reallocations - std::vector& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "starting section %s\n", curSection->fSectionName); - std::vector& sectionAtoms = curSection->fAtoms; - if ( ! curSection->fAllZeroFill ) { - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers - || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); - const int atomCount = sectionAtoms.size(); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; - std::vector& refs = atom->getReferences(); - const int refCount = refs.size(); - //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); - for (int l=0; l < refCount; ++l) { - ObjectFile::Reference* ref = refs[l]; - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol - if ( atom->getSize() != sizeof(pint_t) ) { - warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath()); - } - ObjectFile::Atom* pointerTarget = &(ref->getTarget()); - if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - pointerTarget = ((LazyPointerAtom*)atom)->getTarget(); - } - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / sizeof(pint_t); - uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) - undefinedSymbolIndex = this->symbolIndex(*pointerTarget); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X), pointerTarget=%s\n", - // indirectTableIndex, undefinedSymbolIndex, pointerTarget->getDisplayName()); - fIndirectTableAtom->fTable.push_back(entry); - if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - uint8_t preboundLazyType; - if ( fOptions.prebind() && (fDyldHelper != NULL) - && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) { - // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid - macho_scattered_relocation_info

pblaReloc; - pblaReloc.set_r_scattered(true); - pblaReloc.set_r_pcrel(false); - pblaReloc.set_r_length(); - pblaReloc.set_r_type(preboundLazyType); - pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - pblaReloc.set_r_value(fDyldHelper->getAddress()); - fInternalRelocs.push_back(*((macho_relocation_info

*)&pblaReloc)); - } - else if ( fSlideable ) { - // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides - macho_relocation_info

dyldHelperReloc; - uint32_t sectionNum = 1; - if ( fDyldHelper != NULL ) - sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex(); - //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); - dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - dyldHelperReloc.set_r_symbolnum(sectionNum); - dyldHelperReloc.set_r_pcrel(false); - dyldHelperReloc.set_r_length(); - dyldHelperReloc.set_r_extern(false); - dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(dyldHelperReloc); - } - } - } - else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) { - if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { - throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { - case kRelocNone: - // no reloc needed - break; - case kRelocInternal: - { - macho_relocation_info

internalReloc; - SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); - uint32_t sectionNum = sectInfo->getIndex(); - // special case _mh_dylib_header and friends which are not in any real section - if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) - sectionNum = 1; - internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); - internalReloc.set_r_symbolnum(sectionNum); - internalReloc.set_r_pcrel(false); - internalReloc.set_r_length(); - internalReloc.set_r_extern(false); - internalReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(internalReloc); - } - break; - case kRelocExternal: - { - macho_relocation_info

externalReloc; - externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); - externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); - externalReloc.set_r_pcrel(false); - externalReloc.set_r_length(); - externalReloc.set_r_extern(true); - externalReloc.set_r_type(GENERIC_RELOC_VANILLA); - fExternalRelocs.push_back(externalReloc); - } - break; - } - } - else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { - if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { - if ( fOptions.warnAboutTextRelocs() ) - warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); - if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fInternalRelocs - } - else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fExternalRelocs - } - else { - throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. " - "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath()); - } - } - } - if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { - ObjectFile::Atom* stubTarget = ((StubAtom*)atom)->getTarget(); - uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS; - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / atom->getSize(); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize()); - fIndirectTableAtom->fTable.push_back(entry); - } - } - } - } - } - if ( fSplitCodeToDataContentAtom != NULL ) - fSplitCodeToDataContentAtom->encode(); -} - - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (ppc::ReferenceKinds)ref->getKind() ) { - case ppc::kPICBaseHigh16: - fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); - break; - case ppc::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc::kPointerDiff64: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc::kNoFixUp: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - // ignore - break; - default: - warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (ppc64::ReferenceKinds)ref->getKind() ) { - case ppc64::kPICBaseHigh16: - fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); - break; - case ppc64::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc64::kPointerDiff64: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc64::kNoFixUp: - case ppc64::kGroupSubordinate: - case ppc64::kPointer: - case ppc64::kPointerWeakImport: - case ppc64::kPICBaseLow16: - case ppc64::kPICBaseLow14: - // ignore - break; - default: - warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (x86::ReferenceKinds)ref->getKind() ) { - case x86::kPointerDiff: - if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 ) - fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); - else - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86::kNoFixUp: - case x86::kGroupSubordinate: - case x86::kPointer: - case x86::kPointerWeakImport: - // ignore - break; - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment) - || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) { - fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); - break; - } - // fall into warning case - default: - warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (x86_64::ReferenceKinds)ref->getKind() ) { - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86_64::kPointerDiff: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86_64::kNoFixUp: - case x86_64::kGroupSubordinate: - case x86_64::kPointer: - // ignore - break; - default: - warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (arm::ReferenceKinds)ref->getKind() ) { - case arm::kPointerDiff: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case arm::kNoFixUp: - case arm::kGroupSubordinate: - case arm::kPointer: - case arm::kPointerWeakImport: - case arm::kReadOnlyPointer: - // ignore - break; - default: - warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template -bool Writer::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to) -{ - switch ( to.getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // segments with same permissions slide together - return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable()) - || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) ); - } - throw "ld64 internal error"; -} - - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint32_t ppcNop; - OSWriteBigInt32(&ppcNop, 0, 0x60000000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &ppcNop, 4, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint32_t ppcNop; - OSWriteBigInt32(&ppcNop, 0, 0x60000000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &ppcNop, 4, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint8_t x86Nop = 0x90; - for (uint32_t p=from; p < to; ++p) - ::pwrite(fd, &x86Nop, 1, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint8_t x86Nop = 0x90; - for (uint32_t p=from; p < to; ++p) - ::pwrite(fd, &x86Nop, 1, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - // FIXME: need thumb nop? - uint32_t armNop; - OSWriteLittleInt32(&armNop, 0, 0xe1a00000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &armNop, 4, p); -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; ++p) - *p = 0x90; -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; ++p) - *p = 0x90; -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - // fixme: need thumb nop? - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); -} - -static const char* stringName(const char* str) -{ - if ( strncmp(str, "cstring=", 8) == 0) { - static char buffer[1024]; - char* t = buffer; - *t++ = '\"'; - for(const char*s = &str[8]; *s != '\0'; ++s) { - switch(*s) { - case '\n': - *t++ = '\\'; - *t++ = 'n'; - break; - case '\t': - *t++ = '\\'; - *t++ = 't'; - break; - default: - *t++ = *s; - break; - } - if ( t > &buffer[1020] ) { - *t++= '\"'; - *t++= '.'; - *t++= '.'; - *t++= '.'; - *t++= '\0'; - return buffer; - } - } - *t++= '\"'; - *t++= '\0'; - return buffer; - } - else { - return str; - } -} - - -template <> const char* Writer::getArchString() { return "ppc"; } -template <> const char* Writer::getArchString() { return "ppc64"; } -template <> const char* Writer::getArchString() { return "i386"; } -template <> const char* Writer::getArchString() { return "x86_64"; } -template <> const char* Writer::getArchString() { return "arm"; } - -template -void Writer::writeMap() -{ - if ( fOptions.generatedMapPath() != NULL ) { - FILE* mapFile = fopen(fOptions.generatedMapPath(), "w"); - if ( mapFile != NULL ) { - // write output path - fprintf(mapFile, "# Path: %s\n", fFilePath); - // write output architecure - fprintf(mapFile, "# Arch: %s\n", getArchString()); - // write UUID - if ( fUUIDAtom != NULL ) { - const uint8_t* uuid = fUUIDAtom->getUUID(); - fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n", - uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); - } - // write table of object files - std::map readerToOrdinal; - std::map ordinalToReader; - std::map readerToFileOrdinal; - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - std::vector& sectionAtoms = (*secit)->fAtoms; - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Reader* reader = (*ait)->getFile(); - uint32_t readerOrdinal = (*ait)->getOrdinal(); - std::map::iterator pos = readerToOrdinal.find(reader); - if ( pos == readerToOrdinal.end() ) { - readerToOrdinal[reader] = readerOrdinal; - ordinalToReader[readerOrdinal] = reader; - } - } - } - } - } - fprintf(mapFile, "# Object files:\n"); - fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); - uint32_t fileIndex = 0; - readerToFileOrdinal[this] = fileIndex++; - for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first != 0 ) { - fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->getPath()); - readerToFileOrdinal[it->second] = fileIndex++; - } - } - // write table of sections - fprintf(mapFile, "# Sections:\n"); - fprintf(mapFile, "# Address\tSize \tSegment\tSection\n"); - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - SectionInfo* sect = *secit; - fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->getBaseAddress(), sect->fSize, - (*segit)->fName, sect->fSectionName); - } - } - } - // write table of symbols - fprintf(mapFile, "# Symbols:\n"); - fprintf(mapFile, "# Address\tSize \tFile Name\n"); - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - std::vector& sectionAtoms = (*secit)->fAtoms; - bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0); - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(), - readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName()); - } - } - } - } - fclose(mapFile); - } - else { - warning("could not write map file: %s\n", fOptions.generatedMapPath()); - } - } -} - -static const char* sCleanupFile = NULL; -static void cleanup(int sig) -{ - ::signal(sig, SIG_DFL); - if ( sCleanupFile != NULL ) { - ::unlink(sCleanupFile); - } - if ( sig == SIGINT ) - ::exit(1); -} - - -template -uint64_t Writer::writeAtoms() -{ - // for UNIX conformance, error if file exists and is not writable - if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) ) - throwf("can't write output file: %s", fFilePath); - - int permissions = 0777; - if ( fOptions.outputKind() == Options::kObjectFile ) - permissions = 0666; - // Calling unlink first assures the file is gone so that open creates it with correct permissions - // It also handles the case where fFilePath file is not writable but its directory is - // And it means we don't have to truncate the file when done writing (in case new is smaller than old) - (void)unlink(fFilePath); - - // try to allocate buffer for entire output file content - int fd = -1; - SectionInfo* lastSection = fSegmentInfos.back()->fSections.back(); - uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096); - uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1); - uint8_t* atomBuffer = NULL; - bool streaming = false; - if ( wholeBuffer == NULL ) { - fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); - atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; - streaming = true; - // install signal handlers to delete output file if program is killed - sCleanupFile = fFilePath; - ::signal(SIGINT, cleanup); - ::signal(SIGBUS, cleanup); - ::signal(SIGSEGV, cleanup); - } - uint32_t size = 0; - uint32_t end = 0; - try { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - bool isTextSeg = (strcmp(curSegment->fName, "__TEXT") == 0); - std::vector& sectionInfos = curSegment->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - SectionInfo* curSection = *secit; - std::vector& sectionAtoms = curSection->fAtoms; - //printf("writing with max atom size 0x%X\n", fLargestAtomSize); - //fprintf(stderr, "writing %lu atoms for section %s\n", sectionAtoms.size(), curSection->fSectionName); - if ( ! curSection->fAllZeroFill ) { - end = curSection->fFileOffset; - bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0); - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) - && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) - && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) { - uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset(); - if ( fileOffset != end ) { - if ( needsNops ) { - // fill gaps with no-ops - if ( streaming ) - writeNoOps(fd, end, fileOffset); - else - copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]); - } - else if ( streaming ) { - // zero fill gaps - if ( (fileOffset-end) == 4 ) { - uint32_t zero = 0; - ::pwrite(fd, &zero, 4, end); - } - else { - uint8_t zero = 0x00; - for (uint32_t p=end; p < fileOffset; ++p) - ::pwrite(fd, &zero, 1, p); - } - } - } - uint64_t atomSize = atom->getSize(); - if ( streaming ) { - if ( atomSize > fLargestAtomSize ) - throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX", - atom->getDisplayName(), atomSize, fLargestAtomSize); - } - else { - if ( fileOffset > fileBufferSize ) - throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX", - atom->getDisplayName(), fileOffset, fileBufferSize); - } - uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset]; - end = fileOffset+atomSize; - // copy raw bytes - atom->copyRawContent(buffer); - // apply any fix-ups - try { - std::vector& references = atom->getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( fOptions.outputKind() == Options::kObjectFile ) { - // doing ld -r - // skip fix-ups for undefined targets - if ( &(ref->getTarget()) != NULL ) - this->fixUpReferenceRelocatable(ref, atom, buffer); - } - else { - // producing final linked image - this->fixUpReferenceFinal(ref, atom, buffer); - } - } - } - catch (const char* msg) { - throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); - } - //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n", - // fileOffset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); - if ( streaming ) { - // write out - ::pwrite(fd, buffer, atomSize, fileOffset); - } - else { - if ( (fileOffset + atomSize) > size ) - size = fileOffset + atomSize; - } - } - } - } - } - } - - // update content based UUID - if ( fOptions.getUUIDMode() == Options::kUUIDContent ) { - uint8_t digest[CC_MD5_DIGEST_LENGTH]; - if ( streaming ) { - // if output file file did not fit in memory, re-read file to generate md5 hash - uint32_t kMD5BufferSize = 16*1024; - uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize); - if ( md5Buffer != NULL ) { - CC_MD5_CTX md5State; - CC_MD5_Init(&md5State); - ::lseek(fd, 0, SEEK_SET); - ssize_t len; - while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 ) - CC_MD5_Update(&md5State, md5Buffer, len); - CC_MD5_Final(digest, &md5State); - ::free(md5Buffer); - } - else { - // if malloc fails, fall back to random uuid - ::uuid_generate_random(digest); - } - fUUIDAtom->setContent(digest); - uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); - fUUIDAtom->copyRawContent(atomBuffer); - ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset); - } - else { - // if output file fit in memory, just genrate an md5 hash in memory - #if 1 - // temp hack for building on Tiger - CC_MD5_CTX md5State; - CC_MD5_Init(&md5State); - CC_MD5_Update(&md5State, wholeBuffer, size); - CC_MD5_Final(digest, &md5State); - #else - CC_MD5(wholeBuffer, size, digest); - #endif - fUUIDAtom->setContent(digest); - uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); - fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]); - } - } - } - catch (...) { - if ( sCleanupFile != NULL ) - ::unlink(sCleanupFile); - throw; - } - - // finish up - if ( streaming ) { - delete [] atomBuffer; - close(fd); - // restore default signal handlers - sCleanupFile = NULL; - ::signal(SIGINT, SIG_DFL); - ::signal(SIGBUS, SIG_DFL); - ::signal(SIGSEGV, SIG_DFL); - } - else { - // write whole output file in one chunk - fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); - ::pwrite(fd, wholeBuffer, size, 0); - close(fd); - delete [] wholeBuffer; - } - - return end; -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - int64_t displacement; - int64_t baseAddr; - uint32_t instruction; - uint32_t newInstruction; - uint64_t targetAddr = 0; - uint32_t firstDisp; - uint32_t nextDisp; - uint32_t opcode; - bool relocateableExternal = false; - bool is_bl; - bool is_blx; - bool targetIsThumb; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - } - - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - switch ( (arm::ReferenceKinds)(ref->getKind()) ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - // do nothing - break; - case arm::kPointerWeakImport: - case arm::kPointer: - // If this is the lazy pointers section, then set all lazy pointers to - // point to the dyld stub binding helper. - if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers - || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound lazy pointer to another dylib ==> pointer contains zero - LittleEndian::set32(*fixUp, 0); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - // prebound lazy pointer to withing this dylib ==> pointer contains address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - break; - } - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - else { - // pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - break; - case arm::kPointerDiff: - LittleEndian::set32(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case arm::kReadOnlyPointer: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); - break; - } - break; - case arm::kBranch24WeakImport: - case arm::kBranch24: - displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - // The pc added will be +8 from the pc - displacement -= 8; - // fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); - // max positive displacement is 0x007FFFFF << 2 - // max negative displacement is 0xFF800000 << 2 - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - instruction = LittleEndian::get32(*fixUp); - // Make sure we are calling arm with bl, thumb with blx - is_bl = ((instruction & 0xFF000000) == 0xEB000000); - is_blx = ((instruction & 0xFE000000) == 0xFA000000); - if ( is_bl && ref->getTarget().isThumb() ) { - uint32_t opcode = 0xFA000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; - newInstruction = opcode | h_bit | disp; - } - else if ( is_blx && !ref->getTarget().isThumb() ) { - uint32_t opcode = 0xEB000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - newInstruction = opcode | disp; - } - else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { - throwf("don't know how to convert instruction %x referencing %s to thumb", - instruction, ref->getTarget().getDisplayName()); - } - else { - newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); - } - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xF8000000) == 0xF8000000); - is_blx = ((instruction & 0xF8000000) == 0xE8000000); - targetIsThumb = ref->getTarget().isThumb(); - - // The pc added will be +4 from the pc - baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; - // If the target is not thumb, we will be generating a blx instruction - // Since blx cannot have the low bit set, set bit[1] of the target to - // bit[1] of the base address, so that the difference is a multiple of - // 4 bytes. - if ( !targetIsThumb ) { - targetAddr &= -3ULL; - targetAddr |= (baseAddr & 2LL); - } - displacement = targetAddr - baseAddr; - - // max positive displacement is 0x003FFFFE - // max negative displacement is 0xFFC00000 - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the first - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the next - // 11 bits of the displacement, as well as differentiating bl and blx. - { - firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; - nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; - if ( is_bl && !targetIsThumb ) { - opcode = 0xE800F000; - } - else if ( is_blx && targetIsThumb ) { - opcode = 0xF800F000; - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - else { - opcode = instruction & 0xF800F800; - } - newInstruction = opcode | (nextDisp << 16) | firstDisp; - LittleEndian::set32(*fixUp, newInstruction); - } - break; - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - if ( inAtom->isThumb() ) { - // change 32-bit blx call site to two thumb NOPs - LittleEndian::set32(*fixUp, 0x46C046C0); - } - else { - // change call site to a NOP - LittleEndian::set32(*fixUp, 0xE1A00000); - } - break; - case arm::kDtraceTypeReference: - case arm::kDtraceProbe: - // nothing to fix up - break; - default: - throw "boom shaka laka"; - } -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - int64_t displacement; - uint32_t instruction; - uint32_t newInstruction; - uint64_t targetAddr = 0; - int64_t baseAddr; - uint32_t firstDisp; - uint32_t nextDisp; - uint32_t opcode; - bool relocateableExternal = false; - bool is_bl; - bool is_blx; - bool targetIsThumb; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); - } - - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - switch ( (arm::ReferenceKinds)(ref->getKind()) ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - // do nothing - break; - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - { - if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content - if ( this->indirectSymbolIsLocal(ref) ) - LittleEndian::set32(*fixUp, targetAddr); - else - LittleEndian::set32(*fixUp, 0); - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - } - else { - // internal relocation - if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { - // pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - else { - // pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - } - break; - case arm::kPointerDiff: - LittleEndian::set32(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - case arm::kBranch24WeakImport: - case arm::kBranch24: - displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - // The pc added will be +8 from the pc - displacement -= 8; - // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - // max positive displacement is 0x007FFFFF << 2 - // max negative displacement is 0xFF800000 << 2 - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - instruction = LittleEndian::get32(*fixUp); - // Make sure we are calling arm with bl, thumb with blx - is_bl = ((instruction & 0xFF000000) == 0xEB000000); - is_blx = ((instruction & 0xFE000000) == 0xFA000000); - if ( is_bl && ref->getTarget().isThumb() ) { - uint32_t opcode = 0xFA000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; - newInstruction = opcode | h_bit | disp; - } - else if ( is_blx && !ref->getTarget().isThumb() ) { - uint32_t opcode = 0xEB000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - newInstruction = opcode | disp; - } - else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { - throwf("don't know how to convert instruction %x referencing %s to thumb", - instruction, ref->getTarget().getDisplayName()); - } - else { - newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); - } - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xF8000000) == 0xF8000000); - is_blx = ((instruction & 0xF8000000) == 0xE8000000); - targetIsThumb = ref->getTarget().isThumb(); - - // The pc added will be +4 from the pc - baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; - // If the target is not thumb, we will be generating a blx instruction - // Since blx cannot have the low bit set, set bit[1] of the target to - // bit[1] of the base address, so that the difference is a multiple of - // 4 bytes. - if (!targetIsThumb) { - targetAddr &= -3ULL; - targetAddr |= (baseAddr & 2LL); - } - displacement = targetAddr - baseAddr; - - //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - // max positive displacement is 0x003FFFFE - // max negative displacement is 0xFFC00000 - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the first - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the next - // 11 bits of the displacement, as well as differentiating bl and blx. - firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; - nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; - if ( is_bl && !targetIsThumb ) { - opcode = 0xE800F000; - } - else if ( is_blx && targetIsThumb ) { - opcode = 0xF800F000; - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - else { - opcode = instruction & 0xF800F800; - } - newInstruction = opcode | (nextDisp << 16) | firstDisp; - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kDtraceProbe: - case arm::kDtraceTypeReference: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - uint8_t* dtraceProbeSite; - const int64_t kTwoGigLimit = 0x7FFFFFFF; - const int64_t kSixtyFourKiloLimit = 0x7FFF; - const int64_t kOneTwentyEightLimit = 0x7F; - int64_t displacement; - x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - // do nothing - break; - case x86::kPointerWeakImport: - case x86::kPointer: - { - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - else { - // pointer contains target address - //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86::kPointerDiff: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - LittleEndian::set32(*fixUp, (uint32_t)displacement); - break; - case x86::kPointerDiff16: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) - throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - break; - case x86::kDtraceProbeSite: - // change call site to a NOP - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x90; // 1-byte nop - dtraceProbeSite[0] = 0x0F; // 4-byte nop - dtraceProbeSite[1] = 0x1F; - dtraceProbeSite[2] = 0x40; - dtraceProbeSite[3] = 0x00; - break; - case x86::kDtraceIsEnabledSite: - // change call site to a clear eax - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x33; // xorl eax,eax - dtraceProbeSite[0] = 0xC0; - dtraceProbeSite[1] = 0x90; // 1-byte nop - dtraceProbeSite[2] = 0x90; // 1-byte nop - dtraceProbeSite[3] = 0x90; // 1-byte nop - break; - case x86::kPCRel32WeakImport: - case x86::kPCRel32: - case x86::kPCRel16: - case x86::kPCRel8: - displacement = 0; - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throw "codegen problem, can't use rel32 to external symbol"; - case ObjectFile::Atom::kTentativeDefinition: - displacement = 0; - break; - case ObjectFile::Atom::kAbsoluteSymbol: - displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - } - if ( kind == x86::kPCRel8 ) { - if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel8 out of range in %s", inAtom->getDisplayName()); - } - *(int8_t*)fixUp = (int8_t)displacement; - } - else if ( kind == x86::kPCRel16 ) { - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel16 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - } - else { - if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel32 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set32(*fixUp, (int32_t)displacement); - } - break; - case x86::kAbsolute32: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); - break; - } - break; - case x86::kDtraceTypeReference: - case x86::kDtraceProbe: - // nothing to fix up - break; - } -} - - - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t kTwoGigLimit = 0x7FFFFFFF; - const int64_t kSixtyFourKiloLimit = 0x7FFF; - const int64_t kOneTwentyEightLimit = 0x7F; - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - bool isExtern = this->makesExternalRelocatableReference(ref->getTarget()); - int64_t displacement; - x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - // do nothing - break; - case x86::kPointer: - case x86::kPointerWeakImport: - case x86::kAbsolute32: - { - if ( isExtern ) { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - else if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolIsLocal(ref) ) - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - else - LittleEndian::set32(*fixUp, 0); - } - else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { - // internal relocation => pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // internal relocation to tentative ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - break; - case x86::kPointerDiff: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - LittleEndian::set32(*fixUp, (uint32_t)displacement); - break; - case x86::kPointerDiff16: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) - throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - break; - case x86::kPCRel8: - case x86::kPCRel16: - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - case x86::kDtraceProbeSite: - case x86::kDtraceIsEnabledSite: - { - if ( isExtern ) - displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - else - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - if ( kind == x86::kPCRel8 ) { - displacement += 3; - if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName()); - } - int8_t byte = (int8_t)displacement; - *((int8_t*)fixUp) = byte; - } - else if ( kind == x86::kPCRel16 ) { - displacement += 2; - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel16 out of range in %s", inAtom->getDisplayName()); - } - int16_t word = (int16_t)displacement; - LittleEndian::set16(*((uint16_t*)fixUp), word); - } - else { - if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { - //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement, - // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - throwf("rel32 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set32(*fixUp, (int32_t)displacement); - } - } - break; - case x86::kDtraceProbe: - case x86::kDtraceTypeReference: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t twoGigLimit = 0x7FFFFFFF; - uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; - uint8_t* dtraceProbeSite; - int64_t displacement = 0; - switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { - case x86_64::kNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - // do nothing - break; - case x86_64::kPointerWeakImport: - case x86_64::kPointer: - { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); - } - else { - // internal relocation - // pointer contains target address - //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86_64::kPointerDiff32: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) - throw "32-bit pointer difference out of range"; - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); - break; - case x86_64::kPointerDiff: - LittleEndian::set64(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - // if GOT entry was optimized away, change movq instruction to a leaq - if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) { - //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName()); - uint8_t* opcodes = (uint8_t*)fixUp; - if ( opcodes[-2] != 0x8B ) - throw "GOT load reloc does not point to a movq instruction"; - opcodes[-2] = 0x8D; - } - // fall into general rel32 case - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel8: - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - displacement = (ref->getTarget().getSectionOffset() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throw "codegen problem, can't use rel32 to external symbol"; - break; - } - switch ( ref->getKind() ) { - case x86_64::kPCRel32_1: - displacement -= 1; - break; - case x86_64::kPCRel32_2: - displacement -= 2; - break; - case x86_64::kPCRel32_4: - displacement -= 4; - break; - case x86_64::kBranchPCRel8: - displacement += 3; - break; - } - if ( ref->getKind() == x86_64::kBranchPCRel8 ) { - if ( (displacement > 127) || (displacement < (-128)) ) { - fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n", - inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); - throw "rel8 out of range"; - } - *((int8_t*)fixUp) = (int8_t)displacement; - } - else { - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - fprintf(stderr, "call out of range from %s (%llX) in %s to %s (%llX) in %s\n", - inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); - throw "rel32 out of range"; - } - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - } - break; - case x86_64::kDtraceProbeSite: - // change call site to a NOP - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x90; // 1-byte nop - dtraceProbeSite[0] = 0x0F; // 4-byte nop - dtraceProbeSite[1] = 0x1F; - dtraceProbeSite[2] = 0x40; - dtraceProbeSite[3] = 0x00; - break; - case x86_64::kDtraceIsEnabledSite: - // change call site to a clear eax - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x48; // xorq eax,eax - dtraceProbeSite[0] = 0x33; - dtraceProbeSite[1] = 0xC0; - dtraceProbeSite[2] = 0x90; // 1-byte nop - dtraceProbeSite[3] = 0x90; // 1-byte nop - break; - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t twoGigLimit = 0x7FFFFFFF; - bool external = this->makesExternalRelocatableReference(ref->getTarget()); - uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; - int64_t displacement = 0; - int32_t temp32; - switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { - case x86_64::kNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - // do nothing - break; - case x86_64::kPointer: - case x86_64::kPointerWeakImport: - { - if ( external ) { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); - } - else { - // internal relocation ==> pointer contains target address - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86_64::kPointerDiff32: - // addend in content - LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset() - ref->getFromTargetOffset() ); - break; - case x86_64::kPointerDiff: - // addend in content - LittleEndian::set64(*fixUp, ref->getTargetOffset() - ref->getFromTargetOffset() ); - break; - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kDtraceProbeSite: - case x86_64::kDtraceIsEnabledSite: - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had - temp32 = ref->getTargetOffset(); - if ( external ) { - // extern relocation contains addend - displacement = temp32; - } - else { - // internal relocations contain delta to target address - displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - } - switch ( ref->getKind() ) { - case x86_64::kPCRel32_1: - displacement -= 1; - break; - case x86_64::kPCRel32_2: - displacement -= 2; - break; - case x86_64::kPCRel32_4: - displacement -= 4; - break; - } - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "rel32 out of range"; - } - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - break; - case x86_64::kBranchPCRel8: - // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had - temp32 = ref->getTargetOffset(); - if ( external ) { - // extern relocation contains addend - displacement = temp32; - } - else { - // internal relocations contain delta to target address - displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 1); - } - if ( (displacement > 127) || (displacement < (-128)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "rel8 out of range"; - } - *((int8_t*)fixUp) = (int8_t)displacement; - break; - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoadWeakImport: - // contains addend (usually zero) - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); - break; - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, true); -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, true); -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, false); -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, false); -} - -// -// ppc and ppc64 are mostly the same, so they share a template specialzation -// -template -void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const -{ - uint32_t instruction; - uint32_t newInstruction; - int64_t displacement; - uint64_t targetAddr = 0; - uint64_t picBaseAddr; - uint16_t instructionLowHalf; - uint16_t instructionHighHalf; - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()]; - bool relocateableExternal = false; - const int64_t picbase_twoGigLimit = 0x80000000; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - if ( finalLinkedImage ) - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - else - relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); - } - - switch ( (typename A::ReferenceKinds)(ref->getKind()) ) { - case A::kNoFixUp: - case A::kFollowOn: - case A::kGroupSubordinate: - // do nothing - break; - case A::kPointerWeakImport: - case A::kPointer: - { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers - || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound lazy pointer to another dylib ==> pointer contains zero - P::setP(*fixUpPointer, 0); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - // prebound lazy pointer to withing this dylib ==> pointer contains address - P::setP(*fixUpPointer, targetAddr); - break; - } - } - else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolIsLocal(ref) ) - P::setP(*fixUpPointer, targetAddr); - else - P::setP(*fixUpPointer, 0); - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - P::setP(*fixUpPointer, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else { - // external relocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - } - } - else { - // internal relocation - if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { - // pointer contains target address - //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr); - P::setP(*fixUpPointer, targetAddr); - } - else { - // pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - } - } - } - break; - case A::kPointerDiff64: - P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kPointerDiff32: - P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kPointerDiff16: - P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kDtraceProbeSite: - if ( finalLinkedImage ) { - // change call site to a NOP - BigEndian::set32(*fixUp, 0x60000000); - } - else { - // set bl instuction to branch to address zero in .o file - int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kDtraceIsEnabledSite: - if ( finalLinkedImage ) { - // change call site to a li r3,0 - BigEndian::set32(*fixUp, 0x38600000); - } - else { - // set bl instuction to branch to address zero in .o file - int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kBranch24WeakImport: - case A::kBranch24: - { - //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); - int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - const int64_t bl_eightMegLimit = 0x00FFFFFF; - if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s", - displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath()); - } - } - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kBranch14: - { - int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; - if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - - //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC); - //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kPICBaseLow16: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - instructionLowHalf = (displacement & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kPICBaseLow14: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - if ( (displacement & 0x3) != 0 ) - throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement); - instructionLowHalf = (displacement & 0xFFFC); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kPICBaseHigh16: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - instructionLowHalf = displacement >> 16; - if ( (displacement & 0x00008000) != 0 ) - ++instructionLowHalf; - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsLow16: - if ( relocateableExternal && !finalLinkedImage ) - targetAddr -= ref->getTarget().getAddress(); - instructionLowHalf = (targetAddr & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsLow14: - if ( relocateableExternal && !finalLinkedImage ) - targetAddr -= ref->getTarget().getAddress(); - if ( (targetAddr & 0x3) != 0 ) - throw "bad address for absolute lo14 instruction fix-up"; - instructionLowHalf = (targetAddr & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsHigh16: - if ( relocateableExternal ) { - if ( finalLinkedImage ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // use target address - break; - case ObjectFile::Atom::kAbsoluteSymbol: - targetAddr = ref->getTarget().getSectionOffset(); - break; - } - } - else { - targetAddr -= ref->getTarget().getAddress(); - } - } - instructionHighHalf = (targetAddr >> 16); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsHigh16AddLow: - if ( relocateableExternal ) { - if ( finalLinkedImage ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // use target address - break; - case ObjectFile::Atom::kAbsoluteSymbol: - targetAddr = ref->getTarget().getSectionOffset(); - break; - } - } - else { - targetAddr -= ref->getTarget().getAddress(); - } - } - if ( targetAddr & 0x00008000 ) - targetAddr += 0x00010000; - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16); - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kDtraceTypeReference: - case A::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (ppc::ReferenceKinds)kind ) { - case ppc::kNoFixUp: - case ppc::kFollowOn: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPointerDiff16: - case ppc::kPointerDiff32: - case ppc::kPointerDiff64: - case ppc::kDtraceProbe: - case ppc::kDtraceProbeSite: - case ppc::kDtraceIsEnabledSite: - case ppc::kDtraceTypeReference: - // these are never used to call external functions - return false; - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - case ppc::kBranch14: - // these are used to call external functions - return true; - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - case ppc::kPICBaseHigh16: - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - // these are only used to call external functions - // in -mlong-branch stubs - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // if the .o file this atom came from has long-branch stubs, - // then assume these instructions in a stub. - // Otherwise, these are a direct reference to something (maybe a runtime text reloc) - return ( inAtom->getFile()->hasLongBranchStubs() ); - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - break; - } - return false; -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (arm::ReferenceKinds)kind ) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - return true; - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - case arm::kPointerDiff: - case arm::kDtraceProbe: - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - case arm::kDtraceTypeReference: - return false; - } - return false; -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (ppc64::ReferenceKinds)kind ) { - case ppc::kNoFixUp: - case ppc::kFollowOn: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPointerDiff16: - case ppc::kPointerDiff32: - case ppc::kPointerDiff64: - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - case ppc::kPICBaseHigh16: - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - case ppc::kDtraceProbe: - case ppc::kDtraceProbeSite: - case ppc::kDtraceIsEnabledSite: - case ppc::kDtraceTypeReference: - // these are never used to call external functions - return false; - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - case ppc::kBranch14: - // these are used to call external functions - return true; - } - return false; -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport); -} - - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPointerWeakImport: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport || - kind == arm::kPointerWeakImport); -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity -template bool Writer::needsModuleTable() {return fOptions.needsModuleTable(); } -template <> bool Writer::needsModuleTable() { return false; } -template <> bool Writer::needsModuleTable() { return false; } - - -template -void Writer::optimizeDylibReferences() -{ - //fprintf(stderr, "original ordinals table:\n"); - //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); - //} - // find unused dylibs that can be removed - std::map ordinalToReader; - std::map readerAliases; - for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - ObjectFile::Reader* reader = it->first; - std::map::iterator aliasPos = fLibraryAliases.find(reader); - if ( aliasPos != fLibraryAliases.end() ) { - // already noticed that this reader has same install name as another reader - readerAliases[reader] = aliasPos->second; - } - else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || fOptions.deadStripDylibs()) ) { - // this reader can be optimized away - it->second = 0xFFFFFFFF; - typename std::map* >::iterator pos = fLibraryToLoadCommand.find(reader); - if ( pos != fLibraryToLoadCommand.end() ) - pos->second->optimizeAway(); - } - else { - // mark this reader as using it ordinal - std::map::iterator pos = ordinalToReader.find(it->second); - if ( pos == ordinalToReader.end() ) - ordinalToReader[it->second] = reader; - else - readerAliases[reader] = pos->second; - } - } - // renumber ordinals (depends on iterator walking in ordinal order) - // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals - uint32_t newOrdinal = 0; - for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first <= fLibraryToOrdinal.size() ) { - if ( ! it->second->isLazyLoadedDylib() ) - fLibraryToOrdinal[it->second] = ++newOrdinal; - } - } - for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first <= fLibraryToOrdinal.size() ) { - if ( it->second->isLazyLoadedDylib() ) { - fLibraryToOrdinal[it->second] = ++newOrdinal; - } - } - } - - // linker does not error when dylib ordinal exceeds 250 - if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) ) - throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal); - - // add aliases (e.g. -lm points to libSystem.dylib) - for (std::map::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) { - fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second]; - } - - //fprintf(stderr, "new ordinals table:\n"); - //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); - //} -} - - -template <> -void Writer::scanForAbsoluteReferences() -{ - // arm codegen never has absolute references. FIXME: Is this correct? -} - -template <> -void Writer::scanForAbsoluteReferences() -{ - // x86_64 codegen never has absolute references -} - -template <> -void Writer::scanForAbsoluteReferences() -{ - // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used - if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case x86::kAbsolute32: - throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); - return; - } - } - } - } -} - -template <> -void Writer::scanForAbsoluteReferences() -{ - // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used - if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); - return; - } - } - } - } -} - - -// for ppc64 look for any -mdynamic-no-pic codegen -template <> -void Writer::scanForAbsoluteReferences() -{ - // only do this for main executable - if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case ppc64::kAbsLow16: - case ppc64::kAbsLow14: - case ppc64::kAbsHigh16: - case ppc64::kAbsHigh16AddLow: - //fprintf(stderr, "found -mdynamic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); - // shrink page-zero and add pad segment to compensate - fPadSegmentInfo = new SegmentInfo(); - strcpy(fPadSegmentInfo->fName, "__4GBFILL"); - fPageZeroAtom->setSize(0x1000); - return; - } - } - } - } -} - - -template -void Writer::insertDummyStubs() -{ - // only needed for x86 -} - -template <> -void Writer::insertDummyStubs() -{ - // any 5-byte stubs that cross a 32-byte cache line may update incorrectly - std::vector*> betterStubs; - for (std::vector*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) { - switch (betterStubs.size() % 64 ) { - case 12:// stub would occupy 0x3C->0x41 - case 25:// stub would occupy 0x7D->0x82 - case 38:// stub would occupy 0xBE->0xC3 - case 51:// stub would occupy 0xFF->0x04 - betterStubs.push_back(new StubAtom(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub - break; - } - betterStubs.push_back(*it); - } - // replace - fAllSynthesizedStubs.clear(); - fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end()); -} - -template -void Writer::synthesizeStubs() -{ - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - // these output kinds never have stubs - return; - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDynamicExecutable: - // try to synthesize stubs for these - break; - } - - // walk every atom and reference - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - ObjectFile::Atom& target = ref->getTarget(); - // build map of which symbols need weak importing - if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - bool weakImport = this->weakImportReferenceKind(ref->getKind()); - // Obj-C Symbols in Leopard Can't Be Weak Linked - // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols - // in dylibs that are weakly linked. - if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) { - typename std::map* >::iterator pos; - pos = fLibraryToLoadCommand.find(target.getFile()); - if ( pos != fLibraryToLoadCommand.end() ) { - if ( pos->second->linkedWeak() ) - weakImport = true; - } - } - std::map::iterator pos = fWeakImportMap.find(&target); - if ( pos == fWeakImportMap.end() ) { - // target not in fWeakImportMap, so add - fWeakImportMap[&target] = weakImport; - } - else { - // target in fWeakImportMap, check for weakness mismatch - if ( pos->second != weakImport ) { - // found mismatch - switch ( fOptions.weakReferenceMismatchTreatment() ) { - case Options::kWeakReferenceMismatchError: - throwf("mismatching weak references for symbol: %s", target.getName()); - case Options::kWeakReferenceMismatchWeak: - pos->second = true; - break; - case Options::kWeakReferenceMismatchNonWeak: - pos->second = false; - break; - } - } - } - // update if we use a weak_import or a strong import from this dylib - if ( fWeakImportMap[&target] ) - fDylibReadersWithWeakImports.insert(target.getFile()); - else - fDylibReadersWithNonWeakImports.insert(target.getFile()); - } - // create stubs as needed - if ( this->stubableReference(atom, ref) - && (ref->getTargetOffset() == 0) - && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { - ObjectFile::Atom* stub = NULL; - std::map::iterator pos = fStubsMap.find(&target); - if ( pos == fStubsMap.end() ) { - bool forLazyDylib = false; - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kTentativeDefinition: - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - if ( target.getFile()->isLazyLoadedDylib() ) - forLazyDylib = true; - break; - } - stub = new StubAtom(*this, target, forLazyDylib); - fStubsMap[&target] = stub; - } - else { - stub = pos->second; - } - // alter reference to use stub instead - ref->setTarget(*stub, 0); - } - else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) { - throwf("illegal reference to %s in lazy loaded dylib from %s in %s", - target.getDisplayName(), atom->getDisplayName(), - atom->getFile()->getPath()); - } - // create GOT slots (non-lazy pointers) as needed - else if ( this->GOTReferenceKind(ref->getKind()) ) { - // - bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); - bool useGOT; - if ( fBiggerThanTwoGigs ) { - // in big images use GOT for all zero fill atoms - // this is just a heuristic and may need to be re-examined - useGOT = mustUseGOT || ref->getTarget().isZeroFill(); - } - else { - // < 2GB image so remove all GOT entries that we can - useGOT = mustUseGOT; - } - // if this GOT usage cannot be optimized away then make a GOT enry - if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) - useGOT = true; - if ( useGOT ) { - ObjectFile::Atom* nlp = NULL; - std::map::iterator pos = fGOTMap.find(&target); - if ( pos == fGOTMap.end() ) { - nlp = new NonLazyPointerAtom(*this, target); - fGOTMap[&target] = nlp; - } - else { - nlp = pos->second; - } - // alter reference to use non lazy pointer instead - ref->setTarget(*nlp, ref->getTargetOffset()); - } - } - } - } - } - - // sort stubs - std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); - - // add dummy fast stubs (x86 only) - if ( !fOptions.slowx86Stubs() ) - this->insertDummyStubs(); - - // sort lazy pointers - std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); - std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter()); - - - // add stubs to fAllAtoms - if ( fAllSynthesizedStubs.size() != 0 ) { - std::vector textStubs; - std::vector importStubs; - for (typename std::vector*>::iterator sit=fAllSynthesizedStubs.begin(); sit != fAllSynthesizedStubs.end(); ++sit) { - ObjectFile::Atom* stubAtom = *sit; - if ( strcmp(stubAtom->getSegment().getName(), "__TEXT") == 0 ) - textStubs.push_back(stubAtom); - else - importStubs.push_back(stubAtom); - } - // any helper stubs go right after regular stubs - if ( fAllSynthesizedStubHelpers.size() != 0 ) - textStubs.insert(textStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); - // insert text stubs right after __text section - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) { - // found end of __text section, insert stubs here - fAllAtoms->insert(it, textStubs.begin(), textStubs.end()); - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( importStubs.size() != 0 ) { - // insert __IMPORTS stubs right before __LINKEDIT - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - // for i386 where stubs are not in __TEXT segment - if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0)) - || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) { - // insert stubs at end of __IMPORT segment, or before __LINKEDIT - fAllAtoms->insert(it, importStubs.begin(), importStubs.end()); - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - } - } - - - // add lazy dylib pointers to fAllAtoms - if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { - // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - throw "can't insert lazy pointers, __dyld section not found"; - } - } - - // add lazy pointers to fAllAtoms - if ( fAllSynthesizedLazyPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { - // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - throw "can't insert lazy pointers, __dyld section not found"; - } - } - - // add non-lazy pointers to fAllAtoms - if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) - && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0) - || ((strcmp(prevAtom->getSectionName(), "__data") == 0) && - ((fOptions.outputKind() == Options::kDyld) || (fOptions.outputKind() == Options::kStaticExecutable))) ) ) { - // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - throw "can't insert non-lazy pointers, __dyld section not found"; - } - } - - // build LC_SEGMENT_SPLIT_INFO content now that all atoms exist - if ( fSplitCodeToDataContentAtom != NULL ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) { - this->addCrossSegmentRef(atom, ref); - } - break; - } - } - } - } - -} - - -template -void Writer::partitionIntoSections() -{ - const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); - - // for every atom, set its sectionInfo object and section offset - // build up fSegmentInfos along the way - ObjectFile::Section* curSection = NULL; - SectionInfo* currentSectionInfo = NULL; - SegmentInfo* currentSegmentInfo = NULL; - SectionInfo* cstringSectionInfo = NULL; - unsigned int sectionIndex = 1; - fSegmentInfos.reserve(8); - for (unsigned int i=0; i < fAllAtoms->size(); ++i) { - ObjectFile::Atom* atom = (*fAllAtoms)[i]; - if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) { - if ( oneSegmentCommand ) { - if ( currentSegmentInfo == NULL ) { - currentSegmentInfo = new SegmentInfo(); - currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - this->fSegmentInfos.push_back(currentSegmentInfo); - } - currentSectionInfo = new SectionInfo(); - strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); - strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; - currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.'); - if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) - currentSectionInfo->setIndex(sectionIndex++); - currentSegmentInfo->fSections.push_back(currentSectionInfo); - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) ) - cstringSectionInfo = currentSectionInfo; - } - else { - if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { - currentSegmentInfo = new SegmentInfo(); - strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); - uint32_t initprot = 0; - if ( atom->getSegment().isContentReadable() ) - initprot |= VM_PROT_READ; - if ( atom->getSegment().isContentWritable() ) - initprot |= VM_PROT_WRITE; - if ( atom->getSegment().isContentExecutable() ) - initprot |= VM_PROT_EXECUTE; - if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) ) - initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker - currentSegmentInfo->fInitProtection = initprot; - if ( initprot == 0 ) - currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 - else if ( fOptions.architecture() == CPU_TYPE_ARM ) - currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init - else - currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - std::vector& customSegProtections = fOptions.customSegmentProtections(); - for(std::vector::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) { - if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) { - currentSegmentInfo->fInitProtection = it->init; - currentSegmentInfo->fMaxProtection = it->max; - } - } - currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); - currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); - if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) ) - currentSegmentInfo->fIndependentAddress = true; - this->fSegmentInfos.push_back(currentSegmentInfo); - } - currentSectionInfo = new SectionInfo(); - currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large - strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); - strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; - // check for -sectalign override - std::vector& alignmentOverrides = fOptions.sectionAlignments(); - for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { - if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) - currentSectionInfo->fAlignment = it->alignment; - } - currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); - if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) - currentSectionInfo->setIndex(sectionIndex++); - currentSegmentInfo->fSections.push_back(currentSectionInfo); - } - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { - fLoadCommandsSection = currentSectionInfo; - fLoadCommandsSegment = currentSegmentInfo; - } - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) - currentSectionInfo->fAllLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) ) - currentSectionInfo->fAllLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__ld_symbol_ptr") == 0) ) - currentSectionInfo->fAllLazyDylibPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) - currentSectionInfo->fAllNonLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) - currentSectionInfo->fAllNonLazyPointers = true; - if ( (fOptions.outputKind() == Options::kDyld) && (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) - currentSectionInfo->fAllNonLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub4") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub4") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { - currentSectionInfo->fAllSelfModifyingStubs = true; - currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary - } - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) ) - currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned - curSection = atom->getSection(); - if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers - || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) { - fSymbolTableCommands->needDynamicTable(); - } - } - // any non-zero fill atoms make whole section marked not-zero-fill - if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) - currentSectionInfo->fAllZeroFill = false; - // change section object to be Writer's SectionInfo object - atom->setSection(currentSectionInfo); - // section alignment is that of a contained atom with the greatest alignment - uint8_t atomAlign = atom->getAlignment().powerOf2; - if ( currentSectionInfo->fAlignment < atomAlign ) - currentSectionInfo->fAlignment = atomAlign; - // calculate section offset for this atom - uint64_t offset = currentSectionInfo->fSize; - uint64_t alignment = 1 << atomAlign; - uint64_t currentModulus = (offset % alignment); - uint64_t requiredModulus = atom->getAlignment().modulus; - if ( currentModulus != requiredModulus ) { - if ( requiredModulus > currentModulus ) - offset += requiredModulus-currentModulus; - else - offset += requiredModulus+alignment-currentModulus; - } - atom->setSectionOffset(offset); - uint64_t curAtomSize = atom->getSize(); - currentSectionInfo->fSize = offset + curAtomSize; - // add atom to section vector - currentSectionInfo->fAtoms.push_back(atom); - // update largest size - if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) - fLargestAtomSize = curAtomSize; - } - if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) { - // when merging cstring sections in .o files, all strings need to use the max alignment - uint64_t offset = 0; - uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment; - for (std::vector::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) { - offset = (offset + (cstringAlignment-1)) & (-cstringAlignment); - ObjectFile::Atom* atom = *it; - atom->setSectionOffset(offset); - offset += atom->getSize(); - } - cstringSectionInfo->fSize = offset; - } -} - - -struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; -class TargetAndOffsetComparor -{ -public: - bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const - { - if ( left.atom != right.atom ) - return ( left.atom < right.atom ); - return ( left.offset < right.offset ); - } -}; - -template <> -bool Writer::addBranchIslands() -{ - return this->addPPCBranchIslands(); -} - -template <> -bool Writer::addBranchIslands() -{ - return this->addPPCBranchIslands(); -} - -template <> -bool Writer::addBranchIslands() -{ - // x86 branches can reach entire 4G address space, so no need for branch islands - return false; -} - -template <> -bool Writer::addBranchIslands() -{ - // x86 branches can reach entire 4G size of largest image - return false; -} - -template <> -bool Writer::addBranchIslands() -{ - // arm branch islands not (yet) supported - // you can instead compile with -mlong-call - return false; -} - -template <> -bool Writer::isBranch24Reference(uint8_t kind) -{ - switch (kind) { - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - return true; - } - return false; -} - -template <> -bool Writer::isBranch24Reference(uint8_t kind) -{ - switch (kind) { - case ppc64::kBranch24: - case ppc64::kBranch24WeakImport: - return true; - } - return false; -} - -// -// PowerPC can do PC relative branches as far as +/-16MB. -// If a branch target is >16MB then we insert one or more -// "branch islands" between the branch and its target that -// allows island hoping to the target. -// -// Branch Island Algorithm -// -// If the __TEXT segment < 16MB, then no branch islands needed -// Otherwise, every 15MB into the __TEXT segment is region is -// added which can contain branch islands. Every out of range -// bl instruction is checked. If it crosses a region, an island -// is added to that region with the same target and the bl is -// adjusted to target the island instead. -// -// In theory, if too many islands are added to one region, it -// could grow the __TEXT enough that other previously in-range -// bl branches could be pushed out of range. We reduce the -// probability this could happen by placing the ranges every -// 15MB which means the region would have to be 1MB (256K islands) -// before any branches could be pushed out of range. -// -template -bool Writer::addPPCBranchIslands() -{ - bool log = false; - bool result = false; - // Can only possibly need branch islands if __TEXT segment > 16M - if ( fLoadCommandsSegment->fSize > 16000000 ) { - if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); - const uint32_t kBetweenRegions = 15*1024*1024; // place regions of islands every 15MB in __text section - SectionInfo* textSection = NULL; - for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { - if ( strcmp((*it)->fSectionName, "__text") == 0 ) { - textSection = *it; - if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize); - break; - } - } - const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions; - typedef std::map AtomToIsland; - AtomToIsland regionsMap[kIslandRegionsCount]; - std::vector regionsIslands[kIslandRegionsCount]; - unsigned int islandCount = 0; - if ( log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); - - // create islands for branch references that are out of range - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( this->isBranch24Reference(ref->getKind()) ) { - ObjectFile::Atom& target = ref->getTarget(); - int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); - int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); - int64_t displacement = dstAddr - srcAddr; - TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; - const int64_t kFifteenMegLimit = kBetweenRegions; - if ( displacement > kFifteenMegLimit ) { - // create forward branch chain - ObjectFile::Atom* nextTarget = ⌖ - uint64_t nextTargetOffset = ref->getTargetOffset(); - for (int i=kIslandRegionsCount-1; i >=0 ; --i) { - AtomToIsland* region = ®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress(); - if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { - AtomToIsland::iterator pos = region->find(finalTargetAndOffset); - if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset); - island->setSection(textSection); - (*region)[finalTargetAndOffset] = island; - if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); - regionsIslands[i].push_back(island); - ++islandCount; - nextTarget = island; - nextTargetOffset = 0; - } - else { - nextTarget = pos->second; - nextTargetOffset = 0; - } - } - } - if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName()); - ref->setTarget(*nextTarget, nextTargetOffset); - } - else if ( displacement < (-kFifteenMegLimit) ) { - // create back branching chain - ObjectFile::Atom* prevTarget = ⌖ - uint64_t prevTargetOffset = ref->getTargetOffset(); - for (int i=0; i < kIslandRegionsCount ; ++i) { - AtomToIsland* region = ®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { - AtomToIsland::iterator pos = region->find(finalTargetAndOffset); - if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset); - island->setSection(textSection); - (*region)[finalTargetAndOffset] = island; - if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); - regionsIslands[i].push_back(island); - ++islandCount; - prevTarget = island; - prevTargetOffset = 0; - } - else { - prevTarget = pos->second; - prevTargetOffset = 0; - } - } - } - if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); - ref->setTarget(*prevTarget, prevTargetOffset); - } - } - } - } - - // insert islands into __text section and adjust section offsets - if ( islandCount > 0 ) { - if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); - std::vector newAtomList; - newAtomList.reserve(textSection->fAtoms.size()+islandCount); - uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress(); - uint64_t textSectionAlignment = (1 << textSection->fAlignment); - int regionIndex = 0; - uint64_t atomSlide = 0; - uint64_t sectionOffset = 0; - for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getAddress() > islandRegionAddr ) { - uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide; - sectionOffset = islandStartOffset; - std::vector* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - sectionOffset += islandAtom->getSize(); - } - ++regionIndex; - islandRegionAddr += kBetweenRegions; - uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment; - atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment); - } - newAtomList.push_back(atom); - if ( atomSlide != 0 ) - atom->setSectionOffset(atom->getSectionOffset()+atomSlide); - } - sectionOffset = textSection->fSize+atomSlide; - // put any remaining islands at end of __text section - if ( regionIndex < kIslandRegionsCount ) { - std::vector* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - sectionOffset += islandAtom->getSize(); - } - } - - textSection->fAtoms = newAtomList; - textSection->fSize = sectionOffset; - result = true; - } - - } - return result; -} - - -template -void Writer::adjustLoadCommandsAndPadding() -{ - fSegmentCommands->computeSize(); - - // recompute load command section offsets - uint64_t offset = 0; - std::vector& loadCommandAtoms = fLoadCommandsSection->fAtoms; - const unsigned int atomCount = loadCommandAtoms.size(); - for (unsigned int i=0; i < atomCount; ++i) { - ObjectFile::Atom* atom = loadCommandAtoms[i]; - uint64_t alignment = 1 << atom->getAlignment().powerOf2; - offset = ( (offset+alignment-1) & (-alignment) ); - atom->setSectionOffset(offset); - uint32_t atomSize = atom->getSize(); - if ( atomSize > fLargestAtomSize ) - fLargestAtomSize = atomSize; - offset += atomSize; - fLoadCommandsSection->fSize = offset; - } - - std::vector& sectionInfos = fLoadCommandsSegment->fSections; - const int sectionCount = sectionInfos.size(); - uint32_t totalSizeOfHeaderAndLoadCommands = 0; - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - totalSizeOfHeaderAndLoadCommands += curSection->fSize; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) - break; - } - uint64_t paddingSize = 0; - if ( fOptions.outputKind() == Options::kDyld ) { - // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address - paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); - } - else if ( fOptions.outputKind() == Options::kObjectFile ) { - // mach-o .o files need no padding between load commands and first section - paddingSize = 0; - } - else if ( fOptions.makeEncryptable() ) { - // want load commands to end on a page boundary, so __text starts on page boundary - paddingSize = 4096 - ((totalSizeOfHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); - fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfHeaderAndLoadCommands+paddingSize); - } - else { - // work backwards from end of segment and lay out sections so that extra room goes to padding atom - uint64_t addr = 0; - for(int j=sectionCount-1; j >=0; --j) { - SectionInfo* curSection = sectionInfos[j]; - addr -= curSection->fSize; - addr = addr & (0 - (1 << curSection->fAlignment)); - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { - addr -= totalSizeOfHeaderAndLoadCommands; - paddingSize = addr % 4096; - break; - } - } - - // if command line requires more padding than this - uint32_t minPad = fOptions.minimumHeaderPad(); - if ( fOptions.maxMminimumHeaderPad() ) { - // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes - uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN; - if ( fOptions.outputKind() == Options::kDynamicLibrary ) - altMin += MAXPATHLEN; - if ( altMin > minPad ) - minPad = altMin; - } - if ( paddingSize < minPad ) { - int extraPages = (minPad - paddingSize + 4095)/4096; - paddingSize += extraPages * 4096; - } - } - - // adjust atom size and update section size - fHeaderPadding->setSize(paddingSize); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) - curSection->fSize = paddingSize; - } -} - -// assign file offsets and logical address to all segments -template -void Writer::assignFileOffsets() -{ - bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile); - bool haveFixedSegments = false; - uint64_t fileOffset = 0; - uint64_t nextContiguousAddress = fOptions.baseAddress(); - uint64_t nextReadOnlyAddress = fOptions.baseAddress(); - uint64_t nextWritableAddress = fOptions.baseWritableAddress(); - - // process segments with fixed addresses (-segaddr) - for (std::vector::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, it->name) == 0 ) { - curSegment->fBaseAddress = it->address; - curSegment->fFixedAddress = true; - break; - } - } - } - - // Run through the segments and each segment's sections to assign addresses - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - - if ( fOptions.splitSeg() ) { - if ( curSegment->fInitProtection & VM_PROT_WRITE ) - nextContiguousAddress = nextWritableAddress; - else - nextContiguousAddress = nextReadOnlyAddress; - } - - fileOffset = (fileOffset+4095) & (-4096); - curSegment->fFileOffset = fileOffset; - - // Set the segment base address - if ( curSegment->fFixedAddress ) - haveFixedSegments = true; - else - curSegment->fBaseAddress = nextContiguousAddress; - - // We've set the segment address, now run through each section. - uint64_t address = curSegment->fBaseAddress; - SectionInfo* firstZeroFillSection = NULL; - SectionInfo* prevSection = NULL; - - std::vector& sectionInfos = curSegment->fSections; - - for (std::vector::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) { - SectionInfo* curSection = *it; - - // adjust section address based on alignment - uint64_t alignment = 1 << curSection->fAlignment; - address = ( (address+alignment-1) & (-alignment) ); - - // adjust file offset to match address - if ( prevSection != NULL ) { - if ( finalLinkedImage || !prevSection->fVirtualSection ) - fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; - else - fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); - } - - // update section info - curSection->fFileOffset = fileOffset; - curSection->setBaseAddress(address); - //fprintf(stderr, "%s %s %llX\n", curSegment->fName, curSection->fSectionName, address); - - // keep track of trailing zero fill sections - if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) - firstZeroFillSection = curSection; - if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage ) - throwf("zero-fill section %s not at end of segment", curSection->fSectionName); - - // update running pointers - if ( finalLinkedImage || !curSection->fVirtualSection ) - address += curSection->fSize; - fileOffset += curSection->fSize; - - // sanity check size of 32-bit binaries - if ( address > maxAddress() ) - throwf("section %s exceeds 4GB limit", curSection->fSectionName); - - // update segment info - curSegment->fFileSize = fileOffset - curSegment->fFileOffset; - curSegment->fSize = curSegment->fFileSize; - prevSection = curSection; - } - - if ( fOptions.outputKind() == Options::kObjectFile ) { - // don't page align .o files - } - else { - // optimize trailing zero-fill sections to not occupy disk space - if ( firstZeroFillSection != NULL ) { - curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset; - fileOffset = firstZeroFillSection->fFileOffset; - } - // page align segment size - curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); - curSegment->fSize = (curSegment->fSize+4095) & (-4096); - if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { - nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); - if ( curSegment->fInitProtection & VM_PROT_WRITE ) - nextWritableAddress = nextContiguousAddress; - else - nextReadOnlyAddress = nextContiguousAddress; - } - } - } - - // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) - if ( haveFixedSegments ) { - int segCount = fSegmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* segment1 = fSegmentInfos[i]; - - for(int j=0; j < segCount; ++j) { - if ( i != j ) { - SegmentInfo* segment2 = fSegmentInfos[j]; - - if ( segment1->fBaseAddress < segment2->fBaseAddress ) { - if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { - if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) { - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - } - } - } - } - - // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) { - if ( fFirstWritableSegment == NULL ) - fFirstWritableSegment = curSegment; - if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL ) - fWritableSegmentPastFirst4GB = true; - } - } - - // record size of encrypted part of __TEXT segment - if ( fOptions.makeEncryptable() ) { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, "__TEXT") == 0 ) { - fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize); - break; - } - } - } - -} - -template -void Writer::adjustLinkEditSections() -{ - // link edit content is always in last segment - SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; - unsigned int firstLinkEditSectionIndex = 0; - while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) - ++firstLinkEditSectionIndex; - - const unsigned int linkEditSectionCount = lastSeg->fSections.size(); - uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; - uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); - if ( fPadSegmentInfo != NULL ) { - // insert __4GBFILL segment into segments vector before LINKEDIT - for(std::vector::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) { - if ( *it == lastSeg ) { - fSegmentInfos.insert(it, fPadSegmentInfo); - break; - } - } - // adjust __4GBFILL segment to span from end of last segment to zeroPageSize - fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address; - fPadSegmentInfo->fBaseAddress = address; - // adjust LINKEDIT to start at zeroPageSize - address = fOptions.zeroPageSize(); - lastSeg->fBaseAddress = fOptions.zeroPageSize(); - } - for (unsigned int i=firstLinkEditSectionIndex; i < linkEditSectionCount; ++i) { - std::vector& atoms = lastSeg->fSections[i]->fAtoms; - // adjust section address based on alignment - uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment; - uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address; - address += pad; - fileOffset += pad; // adjust file offset to match address - lastSeg->fSections[i]->setBaseAddress(address); - if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 ) - lastSeg->fSections[i]->setBaseAddress(0); - lastSeg->fSections[i]->fFileOffset = fileOffset; - uint64_t sectionOffset = 0; - for (unsigned int j=0; j < atoms.size(); ++j) { - ObjectFile::Atom* atom = atoms[j]; - uint64_t alignment = 1 << atom->getAlignment().powerOf2; - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - atom->setSectionOffset(sectionOffset); - uint64_t size = atom->getSize(); - sectionOffset += size; - if ( size > fLargestAtomSize ) - fLargestAtomSize = size; - } - //fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset); - lastSeg->fSections[i]->fSize = sectionOffset; - fileOffset += sectionOffset; - address += sectionOffset; - } - if ( fOptions.outputKind() == Options::kObjectFile ) { - //lastSeg->fBaseAddress = 0; - //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> - //lastSeg->fFileOffset = 0; - //lastSeg->fFileSize = - } - else { - lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; - lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); - } -} - - -template -ObjectFile::Atom::Scope MachHeaderAtom::getScope() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - return ObjectFile::Atom::scopeGlobal; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - case Options::kObjectFile: - return ObjectFile::Atom::scopeLinkageUnit; - } - throw "unknown header type"; -} - -template -ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusion() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - return ObjectFile::Atom::kSymbolTableInAndNeverStrip; - case Options::kStaticExecutable: - return ObjectFile::Atom::kSymbolTableInAsAbsolute; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - return ObjectFile::Atom::kSymbolTableIn; - case Options::kObjectFile: - return ObjectFile::Atom::kSymbolTableNotIn; - } - throw "unknown header type"; -} - -template -const char* MachHeaderAtom::getName() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - return "__mh_execute_header"; - case Options::kDynamicLibrary: - return "__mh_dylib_header"; - case Options::kDynamicBundle: - return "__mh_bundle_header"; - case Options::kObjectFile: - return NULL; - case Options::kDyld: - return "__mh_dylinker_header"; - } - throw "unknown header type"; -} - -template -const char* MachHeaderAtom::getDisplayName() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - return this->getName(); - case Options::kObjectFile: - return "mach header"; - } - throw "unknown header type"; -} - -template -void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const -{ - // get file type - uint32_t fileType = 0; - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - fileType = MH_EXECUTE; - break; - case Options::kDynamicLibrary: - fileType = MH_DYLIB; - break; - case Options::kDynamicBundle: - fileType = MH_BUNDLE; - break; - case Options::kObjectFile: - fileType = MH_OBJECT; - break; - case Options::kDyld: - fileType = MH_DYLINKER; - break; - } - - // get flags - uint32_t flags = 0; - if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { - if ( fWriter.fCanScatter ) - flags = MH_SUBSECTIONS_VIA_SYMBOLS; - } - else { - if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { - flags |= MH_NOUNDEFS; - } - else { - flags = MH_DYLDLINK; - if ( fWriter.fOptions.bindAtLoad() ) - flags |= MH_BINDATLOAD; - switch ( fWriter.fOptions.nameSpace() ) { - case Options::kTwoLevelNameSpace: - flags |= MH_TWOLEVEL | MH_NOUNDEFS; - break; - case Options::kFlatNameSpace: - break; - case Options::kForceFlatNameSpace: - flags |= MH_FORCE_FLAT; - break; - } - if ( fWriter.fHasWeakExports ) - flags |= MH_WEAK_DEFINES; - if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) - flags |= MH_BINDS_TO_WEAK; - if ( fWriter.fOptions.prebind() ) - flags |= MH_PREBOUND; - if ( fWriter.fOptions.splitSeg() ) - flags |= MH_SPLIT_SEGS; - if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs ) - flags |= MH_NO_REEXPORTED_DYLIBS; - if ( fWriter.fOptions.positionIndependentExecutable() ) - flags |= MH_PIE; - } - if ( fWriter.fOptions.hasExecutableStack() ) - flags |= MH_ALLOW_STACK_EXECUTION; - if ( fWriter.fOptions.readerOptions().fRootSafe ) - flags |= MH_ROOT_SAFE; - if ( fWriter.fOptions.readerOptions().fSetuidSafe ) - flags |= MH_SETUID_SAFE; - } - - // get commands info - uint32_t commandsSize = 0; - uint32_t commandsCount = 0; - - std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; - for (std::vector::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - commandsSize += atom->getSize(); - // segment and symbol table atoms can contain more than one load command - if ( atom == fWriter.fSegmentCommands ) - commandsCount += fWriter.fSegmentCommands->commandCount(); - else if ( atom == fWriter.fSymbolTableCommands ) - commandsCount += fWriter.fSymbolTableCommands->commandCount(); - else if ( atom->getSize() != 0 ) - ++commandsCount; - } - - // fill out mach_header - macho_header* mh = (macho_header*)buffer; - setHeaderInfo(*mh); - mh->set_filetype(fileType); - mh->set_ncmds(commandsCount); - mh->set_sizeofcmds(commandsSize); - mh->set_flags(flags); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_POWERPC); - header.set_cpusubtype(fWriter.fCpuConstraint); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC_64); - header.set_cputype(CPU_TYPE_POWERPC64); - if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000); - else - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); - header.set_reserved(0); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_I386); - header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC_64); - header.set_cputype(CPU_TYPE_X86_64); - if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000); - else - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); - header.set_reserved(0); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_ARM); - header.set_cpusubtype(fWriter.fCpuConstraint); -} - -template -CustomStackAtom::CustomStackAtom(Writer& writer) - : WriterAtom(writer, Segment::fgStackSegment) -{ - if ( stackGrowsDown() ) - Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); - else - Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); -} - - -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } - -template -void SegmentLoadCommandsAtom::computeSize() -{ - uint64_t size = 0; - std::vector& segmentInfos = fWriter.fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - size += sizeof(macho_segment_command

); - std::vector& sectionInfos = segmentInfos[i]->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) - size += sizeof(macho_section

); - } - } - fSize = size; - fCommandCount = segCount; - if ( fWriter.fPadSegmentInfo != NULL ) { - ++fCommandCount; - fSize += sizeof(macho_segment_command

); - } -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template -void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); - bzero(buffer, size); - uint8_t* p = buffer; - typename std::vector& segmentInfos = fWriter.fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* segInfo = segmentInfos[i]; - const int sectionCount = segInfo->fSections.size(); - macho_segment_command

* cmd = (macho_segment_command

*)p; - cmd->set_cmd(macho_segment_command

::CMD); - cmd->set_segname(segInfo->fName); - cmd->set_vmaddr(segInfo->fBaseAddress); - cmd->set_vmsize(segInfo->fSize); - cmd->set_fileoff(segInfo->fFileOffset); - cmd->set_filesize(segInfo->fFileSize); - cmd->set_maxprot(segInfo->fMaxProtection); - cmd->set_initprot(segInfo->fInitProtection); - // add sections array - macho_section

* const sections = (macho_section

*)&p[sizeof(macho_segment_command

)]; - unsigned int sectionsEmitted = 0; - for (int j=0; j < sectionCount; ++j) { - SectionInfo* sectInfo = segInfo->fSections[j]; - if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { - macho_section

* sect = §ions[sectionsEmitted++]; - if ( oneSegment ) { - // .o file segment does not cover load commands, so recalc at first real section - if ( sectionsEmitted == 1 ) { - cmd->set_vmaddr(sectInfo->getBaseAddress()); - cmd->set_fileoff(sectInfo->fFileOffset); - } - cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); - cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); - } - sect->set_sectname(sectInfo->fSectionName); - sect->set_segname(sectInfo->fSegmentName); - sect->set_addr(sectInfo->getBaseAddress()); - sect->set_size(sectInfo->fSize); - sect->set_offset(sectInfo->fFileOffset); - sect->set_align(sectInfo->fAlignment); - if ( sectInfo->fRelocCount != 0 ) { - sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info

) + fWriter.fSectionRelocationsAtom->getFileOffset()); - sect->set_nreloc(sectInfo->fRelocCount); - } - if ( sectInfo->fAllZeroFill ) { - sect->set_flags(S_ZEROFILL); - sect->set_offset(0); - } - else if ( sectInfo->fAllLazyPointers ) { - sect->set_flags(S_LAZY_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllLazyDylibPointers ) { - sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllNonLazyPointers ) { - sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllStubs ) { - sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); - } - else if ( sectInfo->fAllSelfModifyingStubs ) { - sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); - } - else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_MOD_INIT_FUNC_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_MOD_TERM_FUNC_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); - } - else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_COALESCED); - } - else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_COALESCED); - } - else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_INTERPOSING); - } - else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_CSTRING_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_4BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_8BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_16BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_DTRACE_DOF); - } - else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_DTRACE_DOF); - } - else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - if ( sectInfo->fHasTextExternalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC); - } - } - } - p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; - cmd->set_cmdsize(sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)); - cmd->set_nsects(sectionsEmitted); - } -} - - -template -SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment) -{ - bzero(&fSymbolTable, sizeof(macho_symtab_command

)); - bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command

)); - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - fNeedsDynamicSymbolTable = true; - break; - case Options::kObjectFile: - case Options::kStaticExecutable: - fNeedsDynamicSymbolTable = false; - break; - } - writer.fSymbolTableCommands = this; -} - - - -template -void SymbolTableLoadCommandsAtom::needDynamicTable() -{ - fNeedsDynamicSymbolTable = true; -} - - -template -uint64_t SymbolTableLoadCommandsAtom::getSize() const -{ - if ( fNeedsDynamicSymbolTable ) - return this->alignedSize(sizeof(macho_symtab_command

) + sizeof(macho_dysymtab_command

)); - else - return this->alignedSize(sizeof(macho_symtab_command

)); -} - -template -void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - // build LC_DYSYMTAB command - macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)buffer; - bzero(symbolTableCmd, sizeof(macho_symtab_command

)); - symbolTableCmd->set_cmd(LC_SYMTAB); - symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); - symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); - symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset()); - symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset()); - symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); - - // build LC_DYSYMTAB command - if ( fNeedsDynamicSymbolTable ) { - macho_dysymtab_command

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)&buffer[sizeof(macho_symtab_command

)]; - bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command

)); - dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); - dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); - dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); - dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); - dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); - dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount); - dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex); - dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount); - if ( fWriter.fModuleInfoAtom != NULL ) { - dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); - dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); - dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); - dynamicSymbolTableCmd->set_nmodtab(1); - dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); - dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); - } - dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size()); - if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { - dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); - dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); - } - } -} - - -template -unsigned int SymbolTableLoadCommandsAtom::commandCount() -{ - return fNeedsDynamicSymbolTable ? 2 : 1; -} - -template -uint64_t DyldLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_dylinker_command

) + strlen("/usr/lib/dyld") + 1); -} - -template -void DyldLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_dylinker_command

* cmd = (macho_dylinker_command

*)buffer; - if ( fWriter.fOptions.outputKind() == Options::kDyld ) - cmd->set_cmd(LC_ID_DYLINKER); - else - cmd->set_cmd(LC_LOAD_DYLINKER); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - strcpy((char*)&buffer[sizeof(macho_dylinker_command

)], "/usr/lib/dyld"); -} - -template -uint64_t AllowableClientLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_client_command

) + strlen(this->clientString) + 1); -} - -template -void AllowableClientLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - - bzero(buffer, size); - macho_sub_client_command

* cmd = (macho_sub_client_command

*)buffer; - cmd->set_cmd(LC_SUB_CLIENT); - cmd->set_cmdsize(size); - cmd->set_client_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_client_command

)], this->clientString); - -} - -template -uint64_t DylibLoadCommandsAtom::getSize() const -{ - if ( fOptimizedAway ) { - return 0; - } - else { - const char* path = fInfo.reader->getInstallPath(); - return this->alignedSize(sizeof(macho_dylib_command

) + strlen(path) + 1); - } -} - -template -void DylibLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( fOptimizedAway ) - return; - uint64_t size = this->getSize(); - bzero(buffer, size); - const char* path = fInfo.reader->getInstallPath(); - macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) - && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); - if ( fInfo.options.fLazyLoad ) - cmd->set_cmd(LC_LAZY_LOAD_DYLIB); - else if ( fInfo.options.fWeakImport || autoWeakLoadDylib ) - cmd->set_cmd(LC_LOAD_WEAK_DYLIB); - else if ( fInfo.options.fReExport && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - cmd->set_cmd(LC_REEXPORT_DYLIB); - else - cmd->set_cmd(LC_LOAD_DYLIB); - cmd->set_cmdsize(this->getSize()); - cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses - cmd->set_current_version(fInfo.reader->getCurrentVersion()); - cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); - cmd->set_name_offset(); - strcpy((char*)&buffer[sizeof(macho_dylib_command

)], path); -} - - - -template -uint64_t DylibIDLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_dylib_command

) + strlen(fWriter.fOptions.installPath()) + 1); -} - -template -void DylibIDLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; - cmd->set_cmd(LC_ID_DYLIB); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses - cmd->set_current_version(fWriter.fOptions.currentVersion()); - cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); - strcpy((char*)&buffer[sizeof(macho_dylib_command

)], fWriter.fOptions.installPath()); -} - - -template -void RoutinesLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - if (fWriter.fEntryPoint->isThumb()) - initAddr |= 1ULL; - bzero(buffer, sizeof(macho_routines_command

)); - macho_routines_command

* cmd = (macho_routines_command

*)buffer; - cmd->set_cmd(macho_routines_command

::CMD); - cmd->set_cmdsize(this->getSize()); - cmd->set_init_address(initAddr); -} - - -template -uint64_t SubUmbrellaLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(fName) + 1); -} - -template -void SubUmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_umbrella_command

* cmd = (macho_sub_umbrella_command

*)buffer; - cmd->set_cmd(LC_SUB_UMBRELLA); - cmd->set_cmdsize(this->getSize()); - cmd->set_sub_umbrella_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command

)], fName); -} - -template -void UUIDLoadCommandAtom::generate() -{ - switch ( fWriter.fOptions.getUUIDMode() ) { - case Options::kUUIDNone: - fEmit = false; - break; - case Options::kUUIDRandom: - ::uuid_generate_random(fUUID); - fEmit = true; - break; - case Options::kUUIDContent: - bzero(fUUID, 16); - fEmit = true; - break; - } -} - -template -void UUIDLoadCommandAtom::setContent(const uint8_t uuid[16]) -{ - memcpy(fUUID, uuid, 16); -} - -template -void UUIDLoadCommandAtom::copyRawContent(uint8_t buffer[]) const -{ - if (fEmit) { - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_uuid_command

* cmd = (macho_uuid_command

*)buffer; - cmd->set_cmd(LC_UUID); - cmd->set_cmdsize(this->getSize()); - cmd->set_uuid((uint8_t*)fUUID); - } -} - - -template -uint64_t SubLibraryLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_library_command

) + fNameLength + 1); -} - -template -void SubLibraryLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_library_command

* cmd = (macho_sub_library_command

*)buffer; - cmd->set_cmd(LC_SUB_LIBRARY); - cmd->set_cmdsize(this->getSize()); - cmd->set_sub_library_offset(); - strncpy((char*)&buffer[sizeof(macho_sub_library_command

)], fNameStart, fNameLength); - buffer[sizeof(macho_sub_library_command

)+fNameLength] = '\0'; -} - -template -uint64_t UmbrellaLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_framework_command

) + strlen(fName) + 1); -} - -template -void UmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_framework_command

* cmd = (macho_sub_framework_command

*)buffer; - cmd->set_cmd(LC_SUB_FRAMEWORK); - cmd->set_cmdsize(this->getSize()); - cmd->set_umbrella_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_framework_command

)], fName); -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); -} - -// We should be picking it up from a header -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); // PPC_THREAD_STATE - cmd->set_count(40); // PPC_THREAD_STATE_COUNT; - cmd->set_thread_register(0, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 -} - - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(5); // PPC_THREAD_STATE64 - cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; - cmd->set_thread_register(0, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); // i386_THREAD_STATE - cmd->set_count(16); // i386_THREAD_STATE_COUNT; - cmd->set_thread_register(10, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(x86_THREAD_STATE64); - cmd->set_count(x86_THREAD_STATE64_COUNT); - cmd->set_thread_register(16, start); // rip - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); - cmd->set_count(17); - cmd->set_thread_register(15, start); // pc - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp? -} - -template -uint64_t RPathLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_rpath_command

) + strlen(fPath) + 1); -} - -template -void RPathLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_rpath_command

* cmd = (macho_rpath_command

*)buffer; - cmd->set_cmd(LC_RPATH); - cmd->set_cmdsize(this->getSize()); - cmd->set_path_offset(); - strcpy((char*)&buffer[sizeof(macho_rpath_command

)], fPath); -} - - - -template -void EncryptionLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)buffer; - cmd->set_cmd(LC_ENCRYPTION_INFO); - cmd->set_cmdsize(this->getSize()); - cmd->set_cryptoff(fStartOffset); - cmd->set_cryptsize(fEndOffset-fStartOffset); - cmd->set_cryptid(0); -} - - - -template -void LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, fSize); -} - -template -void LoadCommandsPaddingAtom::setSize(uint64_t newSize) -{ - fSize = newSize; - // this resizing by-passes the way fLargestAtomSize is set, so re-check here - if ( fWriter.fLargestAtomSize < newSize ) - fWriter.fLargestAtomSize = newSize; -} - -template -uint64_t LinkEditAtom::getFileOffset() const -{ - return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); -} - - -template -uint64_t SectionRelocationsLinkEditAtom::getSize() const -{ - return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info

); -} - -template -void SectionRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize()); -} - - -template -uint64_t LocalRelocationsLinkEditAtom::getSize() const -{ - return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info

); -} - -template -void LocalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize()); -} - - - -template -uint64_t SymbolTableLinkEditAtom::getSize() const -{ - return fWriter.fSymbolTableCount * sizeof(macho_nlist

); -} - -template -void SymbolTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fWriter.fSymbolTable, this->getSize()); -} - -template -uint64_t ExternalRelocationsLinkEditAtom::getSize() const -{ - return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info

); -} - -template -void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter

()); - memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); -} - - - -template -uint64_t IndirectTableLinkEditAtom::getSize() const -{ - return fTable.size() * sizeof(uint32_t); -} - -template -void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - const uint32_t indirectTableSize = fTable.size(); - uint32_t* indirectTable = (uint32_t*)buffer; - for(std::vector::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { - if ( it->indirectIndex < indirectTableSize ) { - A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); - } - else { - throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); - } - } -} - - - -template -uint64_t ModuleInfoLinkEditAtom::getSize() const -{ - return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

) - + sizeof(macho_dylib_module

) - + this->getReferencesCount()*sizeof(uint32_t); -} - -template -uint32_t ModuleInfoLinkEditAtom::getTableOfContentsFileOffset() const -{ - return this->getFileOffset(); -} - -template -uint32_t ModuleInfoLinkEditAtom::getModuleTableFileOffset() const -{ - return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

); -} - -template -uint32_t ModuleInfoLinkEditAtom::getReferencesFileOffset() const -{ - return this->getModuleTableFileOffset() + sizeof(macho_dylib_module

); -} - -template -uint32_t ModuleInfoLinkEditAtom::getReferencesCount() const -{ - return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount; -} - -template -void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - // create toc. The symbols are already sorted, they are all in the smae module - macho_dylib_table_of_contents

* p = (macho_dylib_table_of_contents

*)buffer; - for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) { - p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i); - p->set_module_index(0); - } - // create module table (one entry) - uint16_t numInits = 0; - uint16_t numTerms = 0; - std::vector& segmentInfos = fWriter.fSegmentInfos; - for (std::vector::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) { - if ( strcmp((*segit)->fName, "__DATA") == 0 ) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { - if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 ) - numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t); - else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 ) - numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t); - } - } - } - macho_dylib_module

* module = (macho_dylib_module

*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

)]; - module->set_module_name(fModuleNameOffset); - module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); - module->set_nextdefsym(fWriter.fSymbolTableExportCount); - module->set_irefsym(0); - module->set_nrefsym(this->getReferencesCount()); - module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); - module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount); - module->set_iextrel(0); - module->set_nextrel(fWriter.fExternalRelocs.size()); - module->set_iinit_iterm(0,0); - module->set_ninit_nterm(numInits,numTerms); - module->set_objc_module_info_addr(0); // Not used by ld_classic, and not used by objc runtime for many years - module->set_objc_module_info_size(0); // Not used by ld_classic, and not used by objc runtime for many years - // create reference table - macho_dylib_reference

* ref = (macho_dylib_reference

*)((uint8_t*)module + sizeof(macho_dylib_module

)); - for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) { - ref->set_isym(fWriter.fSymbolTableExportStartIndex+i); - ref->set_flags(REFERENCE_FLAG_DEFINED); - } - for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) { - ref->set_isym(fWriter.fSymbolTableImportStartIndex+i); - std::map::iterator pos = fWriter.fStubsMap.find(fWriter.fImportedAtoms[i]); - if ( pos != fWriter.fStubsMap.end() ) - ref->set_flags(REFERENCE_FLAG_UNDEFINED_LAZY); - else - ref->set_flags(REFERENCE_FLAG_UNDEFINED_NON_LAZY); - } -} - - - -template -StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) - : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) -{ - fCurrentBuffer = new char[kBufferSize]; - // burn first byte of string pool (so zero is never a valid string offset) - fCurrentBuffer[fCurrentBufferUsed++] = ' '; - // make offset 1 always point to an empty string - fCurrentBuffer[fCurrentBufferUsed++] = '\0'; -} - -template -uint64_t StringsLinkEditAtom::getSize() const -{ - // align size - return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t)); -} - -template -void StringsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t offset = 0; - for (unsigned int i=0; i < fFullBuffers.size(); ++i) { - memcpy(&buffer[offset], fFullBuffers[i], kBufferSize); - offset += kBufferSize; - } - memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed); - // zero fill end to align - offset += fCurrentBufferUsed; - while ( (offset % sizeof(typename A::P::uint_t)) != 0 ) - buffer[offset++] = 0; -} - -template -int32_t StringsLinkEditAtom::add(const char* name) -{ - int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; - int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1; - if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) { - fCurrentBufferUsed += lenNeeded; - } - else { - int copied = kBufferSize-fCurrentBufferUsed-1; - // change trailing '\0' that strlcpy added to real char - fCurrentBuffer[kBufferSize-1] = name[copied]; - // alloc next buffer - fFullBuffers.push_back(fCurrentBuffer); - fCurrentBuffer = new char[kBufferSize]; - fCurrentBufferUsed = 0; - // append rest of string - this->add(&name[copied+1]); - } - return offset; -} - - -template -int32_t StringsLinkEditAtom::addUnique(const char* name) -{ - StringToOffset::iterator pos = fUniqueStrings.find(name); - if ( pos != fUniqueStrings.end() ) { - return pos->second; - } - else { - int32_t offset = this->add(name); - fUniqueStrings[name] = offset; - return offset; - } -} - - -template -const char* StringsLinkEditAtom::stringForIndex(int32_t index) const -{ - int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size(); - int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed; - // check for out of bounds - if ( index > maxIndex ) - return ""; - // check for index in fCurrentBuffer - if ( index > currentBufferStartIndex ) - return &fCurrentBuffer[index-currentBufferStartIndex]; - // otherwise index is in a full buffer - uint32_t fullBufferIndex = index/kBufferSize; - return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; -} - - - -template -BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) - : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset) -{ - char* buf = new char[strlen(name)+32]; - if ( targetOffset == 0 ) { - if ( islandRegion == 0 ) - sprintf(buf, "%s$island", name); - else - sprintf(buf, "%s$island_%d", name, islandRegion); - } - else { - sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); - } - fName = buf; -} - - -template <> -void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const -{ - int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); -} - -template <> -void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const -{ - int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); -} - -template <> -uint64_t BranchIslandAtom::getSize() const -{ - return 4; -} - -template <> -uint64_t BranchIslandAtom::getSize() const -{ - return 4; -} - - - -template -uint64_t SegmentSplitInfoLoadCommandsAtom::getSize() const -{ - if ( fWriter.fSplitCodeToDataContentAtom->canEncode() ) - return this->alignedSize(sizeof(macho_linkedit_data_command

)); - else - return 0; // a zero size causes the load command to be suppressed -} - -template -void SegmentSplitInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)buffer; - cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); - cmd->set_cmdsize(size); - cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset()); - cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize()); -} - - -template -uint64_t SegmentSplitInfoContentAtom::getSize() const -{ - return fEncodedData.size(); -} - -template -void SegmentSplitInfoContentAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fEncodedData[0], fEncodedData.size()); -} - - -template -void SegmentSplitInfoContentAtom::uleb128EncodeAddresses(const std::vector::AtomAndOffset>& locations) -{ - pint_t addr = fWriter.fOptions.baseAddress(); - for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { - pint_t nextAddr = it->atom->getAddress() + it->offset; - //fprintf(stderr, "\t0x%0llX\n", (uint64_t)nextAddr); - uint64_t delta = nextAddr - addr; - if ( delta == 0 ) - throw "double split seg info for same address"; - // uleb128 encode - uint8_t byte; - do { - byte = delta & 0x7F; - delta &= ~0x7F; - if ( delta != 0 ) - byte |= 0x80; - fEncodedData.push_back(byte); - delta = delta >> 7; - } - while( byte >= 0x80 ); - addr = nextAddr; - } -} - -template -void SegmentSplitInfoContentAtom::encode() -{ - if ( ! fCantEncode ) { - fEncodedData.reserve(8192); - - if ( fKind1Locations.size() != 0 ) { - fEncodedData.push_back(1); - //fprintf(stderr, "type 1:\n"); - this->uleb128EncodeAddresses(fKind1Locations); - fEncodedData.push_back(0); - } - - if ( fKind2Locations.size() != 0 ) { - fEncodedData.push_back(2); - //fprintf(stderr, "type 2:\n"); - this->uleb128EncodeAddresses(fKind2Locations); - fEncodedData.push_back(0); - } - - if ( fKind3Locations.size() != 0 ) { - fEncodedData.push_back(3); - //fprintf(stderr, "type 3:\n"); - this->uleb128EncodeAddresses(fKind3Locations); - fEncodedData.push_back(0); - } - - if ( fKind4Locations.size() != 0 ) { - fEncodedData.push_back(4); - //fprintf(stderr, "type 4:\n"); - this->uleb128EncodeAddresses(fKind4Locations); - fEncodedData.push_back(0); - } - - // always add zero byte to mark end - fEncodedData.push_back(0); - - // add zeros to end to align size - while ( (fEncodedData.size() % sizeof(pint_t)) != 0 ) - fEncodedData.push_back(0); - } -} - - -template -ObjCInfoAtom::ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, bool objcReplacementClasses) - : WriterAtom(writer, getInfoSegment()) -{ - fContent[0] = 0; - uint32_t value = 0; - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - if ( objcReplacementClasses ) - value = 1; - switch ( objcConstraint ) { - case ObjectFile::Reader::kObjcNone: - case ObjectFile::Reader::kObjcRetainRelease: - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - value |= 2; - break; - case ObjectFile::Reader::kObjcGC: - value |= 6; - break; - } - A::P::E::set32(fContent[1], value); -} - -template -void ObjCInfoAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fContent[0], 8); -} - - -// objc info section is in a different segment and section for 32 vs 64 bit runtimes -template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } - -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } - - -}; // namespace executable -}; // namespace mach_o - - -#endif // __EXECUTABLE_MACH_O__ diff --git a/ld64/src/OpaqueSection.hpp b/ld64/src/OpaqueSection.hpp deleted file mode 100644 index cddd45c..0000000 --- a/ld64/src/OpaqueSection.hpp +++ /dev/null @@ -1,199 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OPAQUE_SECTION__ -#define __OPAQUE_SECTION__ - - -#include - -#include "ObjectFile.h" - -namespace opaque_section { - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); } -private: - const char* fName; -}; - - -class Reader : public ObjectFile::Reader -{ -public: - Reader(const char* segmentName, const char* sectionName, const char* path, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL); - virtual ~Reader(); - - void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, - uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0); - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms() { return fAtoms; } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabs() { return NULL; } - -private: - const char* fPath; - std::vector fAtoms; -}; - -class Reference : public ObjectFile::Reference -{ -public: - Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset, - const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0) - : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind), - fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {} - virtual ~Reference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffset; } - virtual const char* getTargetName() const { return fTarget->getName(); } - virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } - virtual uint64_t getTargetOffset() const { return fTargetOffset; } - virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); } - virtual const char* getFromTargetName() const { return fFromTarget->getName(); } - virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } - virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; } - virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } - virtual const char* getDescription() const { return "opaque section reference"; } - -private: - uint64_t fFixUpOffset; - const ObjectFile::Atom* fTarget; - uint64_t fTargetOffset; - uint8_t fKind; - const ObjectFile::Atom* fFromTarget; - uint64_t fFromTargetOffset; -}; - - -class Atom : public ObjectFile::Atom { -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const; - virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return true; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return fFileLength; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return fSectionName; } - virtual Segment& getSegment() const { return fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); } - virtual void copyRawContent(uint8_t buffer[]) const; - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - - Atom(Reader& owner, Segment& segment, const char* sectionName, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName); - virtual ~Atom() {} - - Reader& fOwner; - Segment& fSegment; - const char* fName; - const char* fSectionName; - const uint8_t* fFileContent; - uint32_t fOrdinal; - uint64_t fFileLength; - std::vector fReferences; -}; - - - -Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName) - : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength) -{ - if ( symbolName != NULL ) - fName = strdup(symbolName); - else - asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName); -} - - -Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], - uint64_t fileLength, uint32_t ordinal, const char* symbolName) - : fPath(path) -{ - fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName)); -} - -Reader::~Reader() -{ -} - -void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, - uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget) -{ - fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget)); -} - - -const char* Atom::getDisplayName() const -{ - static char name[64]; - sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName); - return name; -} - - -void Atom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fFileContent, fFileLength); -} - - - -}; - - - -#endif // __OPAQUE_SECTION__ - - - diff --git a/ld64/src/Options.h b/ld64/src/Options.h deleted file mode 100644 index 4bbcffe..0000000 --- a/ld64/src/Options.h +++ /dev/null @@ -1,368 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OPTIONS__ -#define __OPTIONS__ - - -#include -#include - -#include -#include - -#include "ObjectFile.h" - -extern void throwf (const char* format, ...) __attribute__ ((noreturn)); -extern void warning(const char* format, ...); - -class DynamicLibraryOptions -{ -public: - DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false) {} - - bool fWeakImport; - bool fReExport; - bool fBundleLoader; - bool fLazyLoad; -}; - -// -// The public interface to the Options class is the abstract representation of what work the linker -// should do. -// -// This abstraction layer will make it easier to support a future where the linker is a shared library -// invoked directly from Xcode. The target settings in Xcode would be used to directly construct an Options -// object (without building a command line which is then parsed). -// -// -class Options -{ -public: - Options(int argc, const char* argv[]); - ~Options(); - - enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld }; - enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace }; - // Standard treatment for many options. - enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid }; - enum UndefinedTreatment { kUndefinedError, kUndefinedWarning, kUndefinedSuppress, kUndefinedDynamicLookup }; - enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, - kWeakReferenceMismatchNonWeak }; - enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; - enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; - enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; - enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; - - struct FileInfo { - const char* path; - uint64_t fileLen; - time_t modTime; - DynamicLibraryOptions options; - }; - - struct ExtraSection { - const char* segmentName; - const char* sectionName; - const char* path; - const uint8_t* data; - uint64_t dataLen; - }; - - struct SectionAlignment { - const char* segmentName; - const char* sectionName; - uint8_t alignment; - }; - - struct OrderedSymbol { - const char* symbolName; - const char* objectFileName; - }; - - struct SegmentStart { - const char* name; - uint64_t address; - }; - - struct SegmentProtect { - const char* name; - uint32_t max; - uint32_t init; - }; - - struct DylibOverride { - const char* installName; - const char* useInstead; - }; - - - const ObjectFile::ReaderOptions& readerOptions(); - const char* getOutputFilePath(); - std::vector& getInputFiles(); - - cpu_type_t architecture() { return fArchitecture; } - bool preferSubArchitecture() { return fHasPreferredSubType; } - cpu_subtype_t subArchitecture() { return fSubArchitecture; } - OutputKind outputKind(); - bool prebind(); - bool bindAtLoad(); - bool fullyLoadArchives(); - NameSpace nameSpace(); - const char* installPath(); // only for kDynamicLibrary - uint32_t currentVersion(); // only for kDynamicLibrary - uint32_t compatibilityVersion(); // only for kDynamicLibrary - const char* entryName(); // only for kDynamicExecutable or kStaticExecutable - const char* executablePath(); - uint64_t baseAddress(); - bool keepPrivateExterns(); // only for kObjectFile - bool needsModuleTable(); // only for kDynamicLibrary - bool interposable(const char* name); - bool hasExportRestrictList(); - bool hasExportMaskList(); - bool hasWildCardExportRestrictList(); - bool allGlobalsAreDeadStripRoots(); - bool shouldExport(const char*); - bool ignoreOtherArchInputFiles(); - bool forceCpuSubtypeAll(); - bool traceDylibs(); - bool traceArchives(); - DeadStripMode deadStrip(); - UndefinedTreatment undefinedTreatment(); - ObjectFile::ReaderOptions::VersionMin macosxVersionMin(); - bool messagesPrefixedWithArchitecture(); - Treatment picTreatment(); - WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); - const char* umbrellaName(); - std::vector& allowableClients(); - const char* clientName(); - const char* initFunctionName(); // only for kDynamicLibrary - const char* dotOutputFile(); - uint64_t zeroPageSize(); - bool hasCustomStack(); - uint64_t customStackSize(); - uint64_t customStackAddr(); - bool hasExecutableStack(); - std::vector& initialUndefines(); - bool printWhyLive(const char* name); - uint32_t minimumHeaderPad(); - bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; } - std::vector& extraSections(); - std::vector& sectionAlignments(); - CommonsMode commonsMode(); - bool warnCommons(); - bool keepRelocations(); - FileInfo findFile(const char* path); - UUIDMode getUUIDMode() { return fUUIDMode; } - bool warnStabs(); - bool pauseAtEnd() { return fPause; } - bool printStatistics() { return fStatistics; } - bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } - void gotoClassicLinker(int argc, const char* argv[]); - bool sharedRegionEligible() { return fSharedRegionEligible; } - bool printOrderFileStatistics() { return fPrintOrderFileStatistics; } - const char* dTraceScriptName() { return fDtraceScriptName; } - bool dTrace() { return (fDtraceScriptName != NULL); } - std::vector& orderedSymbols() { return fOrderedSymbols; } - bool splitSeg() { return fSplitSegs; } - uint64_t baseWritableAddress() { return fBaseWritableAddress; } - std::vector& customSegmentAddresses() { return fCustomSegmentAddresses; } - std::vector& customSegmentProtections() { return fCustomSegmentProtections; } - bool saveTempFiles() { return fSaveTempFiles; } - const std::vector& rpaths() { return fRPaths; } - bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; } - bool slowx86Stubs() { return fReaderOptions.fSlowx86Stubs; } - std::vector& dylibOverrides() { return fDylibOverrides; } - const char* generatedMapPath() { return fMapPath; } - bool positionIndependentExecutable() { return fPositionIndependentExecutable; } - Options::FileInfo findFileUsingPaths(const char* path); - bool deadStripDylibs() { return fDeadStripDylibs; } - bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } - bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); } - LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; } - bool keepLocalSymbol(const char* symbolName); - bool allowTextRelocs() { return fAllowTextRelocs; } - bool warnAboutTextRelocs() { return fWarnTextRelocs; } - bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; } - bool verbose() { return fVerbose; } - bool makeEncryptable() { return fEncryptable; } - std::vector& llvmOptions() { return fLLVMOptions; } - -private: - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - - typedef __gnu_cxx::hash_set, CStringEquals> NameSet; - enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; - enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; - enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome }; - - class SetWithWildcards { - public: - void insert(const char*); - bool contains(const char*); - bool hasWildCards() { return !fWildCard.empty(); } - NameSet::iterator regularBegin() { return fRegular.begin(); } - NameSet::iterator regularEnd() { return fRegular.end(); } - private: - static bool hasWildCards(const char*); - bool wildCardMatch(const char* pattern, const char* candidate); - bool inCharRange(const char*& range, unsigned char c); - - NameSet fRegular; - std::vector fWildCard; - }; - - - void parse(int argc, const char* argv[]); - void checkIllegalOptionCombinations(); - void buildSearchPaths(int argc, const char* argv[]); - void parseArch(const char* architecture); - FileInfo findLibrary(const char* rootName, bool dylibsOnly=false); - FileInfo findFramework(const char* frameworkName); - FileInfo findFramework(const char* rootName, const char* suffix); - bool checkForFile(const char* format, const char* dir, const char* rootName, - FileInfo& result); - uint32_t parseVersionNumber(const char*); - void parseSectionOrderFile(const char* segment, const char* section, const char* path); - void parseOrderFile(const char* path, bool cstring); - void addSection(const char* segment, const char* section, const char* path); - void addSubLibrary(const char* name); - void loadFileList(const char* fileOfPaths); - uint64_t parseAddress(const char* addr); - void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set); - void parseAliasFile(const char* fileOfAliases); - void parsePreCommandLineEnvironmentSettings(); - void parsePostCommandLineEnvironmentSettings(); - void setUndefinedTreatment(const char* treatment); - void setMacOSXVersionMin(const char* version); - void setIPhoneVersionMin(const char* version); - void setWeakReferenceMismatchTreatment(const char* treatment); - void addDylibOverride(const char* paths); - void addSectionAlignment(const char* segment, const char* section, const char* alignment); - CommonsMode parseCommonsTreatment(const char* mode); - Treatment parseTreatment(const char* treatment); - void reconfigureDefaults(); - void checkForClassic(int argc, const char* argv[]); - void parseSegAddrTable(const char* segAddrPath, const char* installPath); - void addLibrary(const FileInfo& info); - void warnObsolete(const char* arg); - uint32_t parseProtection(const char* prot); - - - ObjectFile::ReaderOptions fReaderOptions; - const char* fOutputFile; - std::vector fInputFiles; - cpu_type_t fArchitecture; - cpu_subtype_t fSubArchitecture; - OutputKind fOutputKind; - bool fHasPreferredSubType; - bool fPrebind; - bool fBindAtLoad; - bool fKeepPrivateExterns; - bool fNeedsModuleTable; - bool fIgnoreOtherArchFiles; - bool fForceSubtypeAll; - InterposeMode fInterposeMode; - DeadStripMode fDeadStrip; - NameSpace fNameSpace; - uint32_t fDylibCompatVersion; - uint32_t fDylibCurrentVersion; - const char* fDylibInstallName; - const char* fFinalName; - const char* fEntryName; - uint64_t fBaseAddress; - uint64_t fBaseWritableAddress; - bool fSplitSegs; - SetWithWildcards fExportSymbols; - SetWithWildcards fDontExportSymbols; - SetWithWildcards fInterposeList; - ExportMode fExportMode; - LibrarySearchMode fLibrarySearchMode; - UndefinedTreatment fUndefinedTreatment; - bool fMessagesPrefixedWithArchitecture; - WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment; - std::vector fSubUmbellas; - std::vector fSubLibraries; - std::vector fAllowableClients; - std::vector fRPaths; - const char* fClientName; - const char* fUmbrellaName; - const char* fInitFunctionName; - const char* fDotOutputFile; - const char* fExecutablePath; - const char* fBundleLoader; - const char* fDtraceScriptName; - const char* fSegAddrTablePath; - const char* fMapPath; - uint64_t fZeroPageSize; - uint64_t fStackSize; - uint64_t fStackAddr; - bool fExecutableStack; - uint32_t fMinimumHeaderPad; - CommonsMode fCommonsMode; - UUIDMode fUUIDMode; - SetWithWildcards fLocalSymbolsIncluded; - SetWithWildcards fLocalSymbolsExcluded; - LocalSymbolHandling fLocalSymbolHandling; - bool fWarnCommons; - bool fVerbose; - bool fKeepRelocations; - bool fWarnStabs; - bool fTraceDylibSearching; - bool fPause; - bool fStatistics; - bool fPrintOptions; - bool fSharedRegionEligible; - bool fPrintOrderFileStatistics; - bool fReadOnlyx86Stubs; - bool fPositionIndependentExecutable; - bool fMaxMinimumHeaderPad; - bool fDeadStripDylibs; - bool fAllowTextRelocs; - bool fWarnTextRelocs; - bool fUsingLazyDylibLinking; - bool fEncryptable; - std::vector fInitialUndefines; - NameSet fAllowedUndefined; - NameSet fWhyLive; - std::vector fExtraSections; - std::vector fSectionAlignments; - std::vector fOrderedSymbols; - std::vector fCustomSegmentAddresses; - std::vector fCustomSegmentProtections; - std::vector fDylibOverrides; - std::vector fLLVMOptions; - std::vector fLibrarySearchPaths; - std::vector fFrameworkSearchPaths; - std::vector fSDKPaths; - bool fSaveTempFiles; -}; - - - -#endif // __OPTIONS__ diff --git a/ld64/src/SectCreate.h b/ld64/src/SectCreate.h deleted file mode 100644 index d5c7f4e..0000000 --- a/ld64/src/SectCreate.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#ifndef __SECTCREATE__ -#define __SECTCREATE__ - - -#include "ObjectFile.h" - -namespace SectCreate { - - extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); - -}; - - -#endif - - - - diff --git a/ld64/FireOpal/src/FileAbstraction.hpp b/ld64/src/abstraction/FileAbstraction.hpp similarity index 100% rename from ld64/FireOpal/src/FileAbstraction.hpp rename to ld64/src/abstraction/FileAbstraction.hpp diff --git a/ld64/src/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp similarity index 76% rename from ld64/src/MachOFileAbstraction.hpp rename to ld64/src/abstraction/MachOFileAbstraction.hpp index 83a8ee1..330a8cb 100644 --- a/ld64/src/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -32,18 +32,14 @@ #include #include #include +#include +#include #include #include "FileAbstraction.hpp" -#include "Architectures.hpp" + // stuff that will eventually go away once newer cctools headers are widespread -#ifndef LC_LAZY_LOAD_DYLIB - #define LC_LAZY_LOAD_DYLIB 0x20 -#endif -#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS - #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 -#endif #ifndef CPU_SUBTYPE_ARM_V5TEJ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) #endif @@ -53,32 +49,88 @@ #ifndef CPU_SUBTYPE_ARM_V7 #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) #endif +#ifndef ARM_THUMB_32BIT_BRANCH + #define ARM_THUMB_32BIT_BRANCH 7 +#endif #ifndef N_ARM_THUMB_DEF #define N_ARM_THUMB_DEF 0x0008 #endif -enum reloc_type_arm -{ - ARM_RELOC_VANILLA, /* generic relocation as discribed above */ - ARM_RELOC_PAIR, /* the second relocation entry of a pair */ - ARM_RELOC_SECTDIFF, /* a PAIR follows with subtract symbol value */ - ARM_RELOC_LOCAL_SECTDIFF, /* like ARM_RELOC_SECTDIFF, but the symbol - referenced was local. */ - ARM_RELOC_PB_LA_PTR,/* prebound lazy pointer */ - ARM_RELOC_BR24, /* 24 bit branch displacement (to a word address) */ - ARM_THUMB_RELOC_BR22, /* 22 bit branch displacement (to a half-word - address) */ -}; - -#ifndef LC_ENCRYPTION_INFO - #define LC_ENCRYPTION_INFO 0x21 - struct encryption_info_command { - uint32_t cmd; - uint32_t cmdsize; - uint32_t cryptoff; /* file offset of encrypted range */ - uint32_t cryptsize; /* file size of encrypted range */ - uint32_t cryptid; /* which enryption system, 0 means not-encrypted yet */ - }; +#ifndef MH_DEAD_STRIPPABLE_DYLIB + #define MH_DEAD_STRIPPABLE_DYLIB 0x400000 +#endif +#ifndef MH_KEXT_BUNDLE + #define MH_KEXT_BUNDLE 11 #endif +#ifndef LC_DYLD_INFO + #define LC_DYLD_INFO 0x22 /* compressed dyld information */ + #define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */ + + struct dyld_info_command { + uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */ + uint32_t cmdsize; /* sizeof(struct dyld_info_command) */ + uint32_t rebase_off; /* file offset to rebase info */ + uint32_t rebase_size; /* size of rebase info */ + uint32_t bind_off; /* file offset to binding info */ + uint32_t bind_size; /* size of binding info */ + uint32_t weak_bind_off; /* file offset to weak binding info */ + uint32_t weak_bind_size; /* size of weak binding info */ + uint32_t lazy_bind_off; /* file offset to lazy binding info */ + uint32_t lazy_bind_size; /* size of lazy binding infs */ + uint32_t export_off; /* file offset to lazy binding info */ + uint32_t export_size; /* size of lazy binding infs */ + }; + + #define REBASE_TYPE_POINTER 1 + #define REBASE_TYPE_TEXT_ABSOLUTE32 2 + #define REBASE_TYPE_TEXT_PCREL32 3 + + #define REBASE_OPCODE_MASK 0xF0 + #define REBASE_IMMEDIATE_MASK 0x0F + #define REBASE_OPCODE_DONE 0x00 + #define REBASE_OPCODE_SET_TYPE_IMM 0x10 + #define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20 + #define REBASE_OPCODE_ADD_ADDR_ULEB 0x30 + #define REBASE_OPCODE_ADD_ADDR_IMM_SCALED 0x40 + #define REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50 + #define REBASE_OPCODE_DO_REBASE_ULEB_TIMES 0x60 + #define REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 0x70 + #define REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 0x80 + + #define BIND_TYPE_POINTER 1 + #define BIND_TYPE_TEXT_ABSOLUTE32 2 + #define BIND_TYPE_TEXT_PCREL32 3 + + #define BIND_SPECIAL_DYLIB_SELF 0 + #define BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE -1 + #define BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2 + + #define BIND_SYMBOL_FLAGS_WEAK_IMPORT 0x1 + #define BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 0x8 + + #define BIND_OPCODE_MASK 0xF0 + #define BIND_IMMEDIATE_MASK 0x0F + #define BIND_OPCODE_DONE 0x00 + #define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10 + #define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20 + #define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30 + #define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40 + #define BIND_OPCODE_SET_TYPE_IMM 0x50 + #define BIND_OPCODE_SET_ADDEND_SLEB 0x60 + #define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70 + #define BIND_OPCODE_ADD_ADDR_ULEB 0x80 + #define BIND_OPCODE_DO_BIND 0x90 + #define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xA0 + #define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xB0 + #define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xC0 + + #define EXPORT_SYMBOL_FLAGS_KIND_MASK 0x03 + #define EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00 + #define EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 0x01 + #define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04 + #define EXPORT_SYMBOL_FLAGS_INDIRECT_DEFINITION 0x08 + #define EXPORT_SYMBOL_FLAGS_HAS_SPECIALIZATIONS 0x10 + +#endif // @@ -919,6 +971,195 @@ class macho_encryption_info_command { }; +// +// start of __unwind_info section +// +template +class macho_unwind_info_section_header { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t commonEncodingsArraySectionOffset() const INLINE { return E::get32(fields.commonEncodingsArraySectionOffset); } + void set_commonEncodingsArraySectionOffset(uint32_t value) INLINE { E::set32(fields.commonEncodingsArraySectionOffset, value); } + + uint32_t commonEncodingsArrayCount() const INLINE { return E::get32(fields.commonEncodingsArrayCount); } + void set_commonEncodingsArrayCount(uint32_t value) INLINE { E::set32(fields.commonEncodingsArrayCount, value); } + + uint32_t personalityArraySectionOffset() const INLINE { return E::get32(fields.personalityArraySectionOffset); } + void set_personalityArraySectionOffset(uint32_t value) INLINE { E::set32(fields.personalityArraySectionOffset, value); } + + uint32_t personalityArrayCount() const INLINE { return E::get32(fields.personalityArrayCount); } + void set_personalityArrayCount(uint32_t value) INLINE { E::set32(fields.personalityArrayCount, value); } + + uint32_t indexSectionOffset() const INLINE { return E::get32(fields.indexSectionOffset); } + void set_indexSectionOffset(uint32_t value) INLINE { E::set32(fields.indexSectionOffset, value); } + + uint32_t indexCount() const INLINE { return E::get32(fields.indexCount); } + void set_indexCount(uint32_t value) INLINE { E::set32(fields.indexCount, value); } + + typedef typename P::E E; +private: + unwind_info_section_header fields; +}; + + + +// +// uwind first level index entry +// +template +class macho_unwind_info_section_header_index_entry { +public: + uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } + void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } + + uint32_t secondLevelPagesSectionOffset() const INLINE { return E::get32(fields.secondLevelPagesSectionOffset); } + void set_secondLevelPagesSectionOffset(uint32_t value) INLINE { E::set32(fields.secondLevelPagesSectionOffset, value); } + + uint32_t lsdaIndexArraySectionOffset() const INLINE { return E::get32(fields.lsdaIndexArraySectionOffset); } + void set_lsdaIndexArraySectionOffset(uint32_t value) INLINE { E::set32(fields.lsdaIndexArraySectionOffset, value); } + + typedef typename P::E E; +private: + unwind_info_section_header_index_entry fields; +}; + + +// +// LSDA table entry +// +template +class macho_unwind_info_section_header_lsda_index_entry { +public: + uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } + void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } + + uint32_t lsdaOffset() const INLINE { return E::get32(fields.lsdaOffset); } + void set_lsdaOffset(uint32_t value) INLINE { E::set32(fields.lsdaOffset, value); } + + typedef typename P::E E; +private: + unwind_info_section_header_lsda_index_entry fields; +}; + + +// +// regular second level entry +// +template +class macho_unwind_info_regular_second_level_entry { +public: + uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } + void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } + + uint32_t encoding() const INLINE { return E::get32(fields.encoding); } + void set_encoding(uint32_t value) INLINE { E::set32(fields.encoding, value); } + + typedef typename P::E E; +private: + unwind_info_regular_second_level_entry fields; +}; + + +// +// start of second level regular page +// +template +class macho_unwind_info_regular_second_level_page_header { +public: + uint32_t kind() const INLINE { return E::get32(fields.kind); } + void set_kind(uint32_t value) INLINE { E::set32(fields.kind, value); } + + uint16_t entryPageOffset() const INLINE { return E::get16(fields.entryPageOffset); } + void set_entryPageOffset(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryPageOffset, value); } + + uint16_t entryCount() const INLINE { return E::get16(fields.entryCount); } + void set_entryCount(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryCount, value); } + + typedef typename P::E E; +private: + unwind_info_regular_second_level_page_header fields; +}; + + +// +// start of second level compressed page +// +template +class macho_unwind_info_compressed_second_level_page_header { +public: + uint32_t kind() const INLINE { return E::get32(fields.kind); } + void set_kind(uint32_t value) INLINE { E::set32(fields.kind, value); } + + uint16_t entryPageOffset() const INLINE { return E::get16(fields.entryPageOffset); } + void set_entryPageOffset(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryPageOffset, value); } + + uint16_t entryCount() const INLINE { return E::get16(fields.entryCount); } + void set_entryCount(uint16_t value) INLINE { E::set16((uint16_t&)fields.entryCount, value); } + + uint16_t encodingsPageOffset() const INLINE { return E::get16(fields.encodingsPageOffset); } + void set_encodingsPageOffset(uint16_t value) INLINE { E::set16((uint16_t&)fields.encodingsPageOffset, value); } + + uint16_t encodingsCount() const INLINE { return E::get16(fields.encodingsCount); } + void set_encodingsCount(uint16_t value) INLINE { E::set16((uint16_t&)fields.encodingsCount, value); } + + typedef typename P::E E; +private: + unwind_info_compressed_second_level_page_header fields; +}; + + +// +// compressed dyld info load command +// +template +class macho_dyld_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } + void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } + + uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } + void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } + + uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } + void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } + + uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } + void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } + + uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } + void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } + + uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } + void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } + + uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } + void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } + + uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } + void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } + + uint32_t export_off() const INLINE { return E::get32(fields.export_off); } + void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } + + uint32_t export_size() const INLINE { return E::get32(fields.export_size); } + void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } + + + typedef typename P::E E; +private: + dyld_info_command fields; +}; + + + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp new file mode 100644 index 0000000..43284c4 --- /dev/null +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -0,0 +1,320 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_TRIE__ +#define __MACH_O_TRIE__ + +#include + +#include "MachOFileAbstraction.hpp" + +namespace mach_o { +namespace trie { + +struct Edge +{ + Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { } + ~Edge() { } + const char* fSubString; + struct Node* fChild; + +}; + +struct Node +{ + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), + fHaveExportInfo(false), fTrieOffset(0) {} + ~Node() { } + const char* fCummulativeString; + std::vector fChildren; + uint64_t fAddress; + uint32_t fFlags; + bool fOrdered; + bool fHaveExportInfo; + uint32_t fTrieOffset; + + void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { + const char* partialStr = &fullStr[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + int subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addSymbol(fullStr, address, flags); + return; + } + else { + for (int i=subStringLen-1; i > 0; --i) { + if ( strncmp(e.fSubString, partialStr, i) == 0 ) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + char* bNodeCummStr = strdup(e.fChild->fCummulativeString); + bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; + //node* aNode = this; + Node* bNode = new Node(bNodeCummStr); + Node* cNode = e.fChild; + char* abEdgeStr = strdup(e.fSubString); + abEdgeStr[i] = '\0'; + char* bcEdgeStr = strdup(&e.fSubString[i]); + Edge& abEdge = e; + abEdge.fSubString = abEdgeStr; + abEdge.fChild = bNode; + Edge bcEdge(bcEdgeStr, cNode); + bNode->fChildren.push_back(bcEdge); + bNode->addSymbol(fullStr, address, flags); + return; + } + } + } + } + // no commonality with any existing child, make a new edge that is this whole string + Node* newNode = new Node(strdup(fullStr)); + Edge newEdge(strdup(partialStr), newNode); + fChildren.push_back(newEdge); + newNode->fAddress = address; + newNode->fFlags = flags; + newNode->fHaveExportInfo = true; + } + + void addOrderedNodes(const char* name, std::vector& orderedNodes) { + if ( !fOrdered ) { + orderedNodes.push_back(this); + //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); + fOrdered = true; + } + const char* partialStr = &name[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + int subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addOrderedNodes(name, orderedNodes); + return; + } + } + } + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr) + // byte for child node count + // each child: zero terminated substring, uleb128 node offset + bool updateOffset(uint32_t& offset) { + uint32_t nodeSize = 1; // byte for length of export info + if ( fHaveExportInfo ) + nodeSize += uleb128_size(fFlags) + uleb128_size(fAddress); + + // add children + ++nodeSize; // byte for count of chidren + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset); + } + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fHaveExportInfo ) { + // nodes with export info: size, flags, address + out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress)); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + } + else { + // no export info + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + append_string(e.fSubString, out); + append_uleb128(e.fChild->fTrieOffset, out); + } + } + +private: + static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + static void append_string(const char* str, std::vector& out) { + for (const char* s = str; *s != '\0'; ++s) + out.push_back(*s); + out.push_back('\0'); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + +}; + +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + + +struct Entry +{ + const char* name; + uint64_t address; + uint64_t flags; +}; + + +inline void makeTrie(const std::vector& input, std::vector& output) +{ + Node start(strdup("")); + + // make nodes for all exported symbols + for (std::vector::const_iterator it = input.begin(); it != input.end(); ++it) { + start.addSymbol(it->name, it->address, it->flags); + } + + // create vector of nodes + std::vector orderedNodes; + orderedNodes.reserve(input.size()*2); + for (std::vector::const_iterator it = input.begin(); it != input.end(); ++it) { + start.addOrderedNodes(it->name, orderedNodes); + } + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + if ( (*it)->updateOffset(offset) ) + more = true; + } + } while ( more ); + + // create trie stream + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + (*it)->appendToStream(output); + } +} + +struct EntryWithOffset +{ + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } +}; + + + +static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, std::vector& output) +{ + if ( p >= end ) + throw "malformed trie, node past end"; + const uint8_t terminalSize = *p++; + const uint8_t* children = p + terminalSize; + if ( terminalSize != 0 ) { + EntryWithOffset e; + e.nodeOffset = p-start; + e.entry.name = strdup(cummulativeString); + e.entry.flags = read_uleb128(p, end); + e.entry.address = read_uleb128(p, end); + output.push_back(e); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = read_uleb128(s, end); + processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); + } +} + + +inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) +{ + // empty tree has no entries + if ( start == end ) + return; + char cummulativeString[4000]; + std::vector entries; + processExportNode(start, start, end, cummulativeString, 0, entries); + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) + output.push_back(it->entry); +} + + + + +}; // namespace trie +}; // namespace mach_o + + +#endif // __MACH_O_TRIE__ + + diff --git a/ld64/src/debugline.h b/ld64/src/debugline.h deleted file mode 100644 index 51d585e..0000000 --- a/ld64/src/debugline.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Information about a line. - DIRECTORY is to be ignored if FILENAME is absolute. - PC will be relative to the file the debug_line section is in. */ -struct line_info -{ - uint64_t file; - int64_t line; - uint64_t col; - uint64_t pc; - int end_of_sequence; -}; - -/* Opaque status structure for the line readers. */ -struct line_reader_data; - -/* Create a line_reader_data, given address and size of the debug_line section. - SIZE may be (size_t)-1 if unknown, although this suppresses checking - for an incorrectly large size in the debug_line section. - LITTLE_ENDIAN is set if the debug_line section is for a little-endian - machine. - Returns NULL on error. */ -struct line_reader_data * line_open (const uint8_t * debug_line, - size_t debug_line_size, - int little_endian); - -/* The STOP parameter to line_next is one of line_stop_{file,line,col}, - perhaps ORed with line_stop_pc; or line_stop_atend, or line_stop_always. */ -enum line_stop_constants { - line_stop_atend = 0, /* Stop only at the end of a sequence. */ - line_stop_file = 1, /* Stop if DIRECTORY or FILENAME change. */ - line_stop_line = 2, /* Stop if LINE, DIRECTORY, or FILENAME change. */ - line_stop_col = 3, /* Stop if COL, LINE, DIRECTORY, or FILENAME change. */ - line_stop_pos_mask = 3, - line_stop_pc = 4, /* Stop if PC changes. */ - line_stop_always = 8 /* Stop always. */ -}; - -/* Either return FALSE on an error, in which case the line_reader_data - may be invalid and should be passed immediately to line_free; or - fill RESULT with the first 'interesting' line, as determined by STOP. - The last line data in a sequence is always considered 'interesting'. */ -int line_next (struct line_reader_data * lnd, - struct line_info * result, - enum line_stop_constants stop); - -/* Find the region (START->pc through END->pc) in the debug_line - information which contains PC. This routine starts searching at - the current position (which is returned as END), and will go all - the way around the debug_line information. It will return false if - an error occurs or if there is no matching region; these may be - distinguished by looking at START->end_of_sequence, which will be - false on error and true if there was no matching region. - You could write this routine using line_next, but this version - will be slightly more efficient, and of course more convenient. */ - -int line_find_addr (struct line_reader_data * lnd, - struct line_info * start, - struct line_info * end, - uint64_t pc); - -/* Return TRUE if there is more line data to be fetched. - If line_next has not been called or it has been called but did not - set END_OF_SEQUENCE, you can assume there is more line data, - but it's safe to call this routine anyway. */ -int line_at_eof (struct line_reader_data * lnd); - -/* Return the pathname of the file in S, or NULL on error. - The result will have been allocated with malloc. */ -char * line_file (struct line_reader_data *lnd, uint64_t file); - -/* Reset the line_reader_data: go back to the beginning. */ -void line_reset (struct line_reader_data * lnd); - -/* Free a line_reader_data structure. */ -void line_free (struct line_reader_data * lnd); - -#ifdef __cplusplus -} -#endif - diff --git a/ld64/src/dwarf2.h b/ld64/src/dwarf2.h deleted file mode 100644 index 530b465..0000000 --- a/ld64/src/dwarf2.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -/* These constants were taken from version 3 of the DWARF standard, - which is Copyright (c) 2005 Free Standards Group, and - Copyright (c) 1992, 1993 UNIX International, Inc. */ - -/* This is not a complete list. */ -enum { - DW_TAG_compile_unit = 17, - DW_TAG_partial_unit = 60 -}; - -/* This is not a complete list. */ -enum { - DW_AT_sibling = 1, - DW_AT_name = 3, - DW_AT_stmt_list = 16, - DW_AT_comp_dir = 27 -}; - -enum { - DW_FORM_addr = 1, - DW_FORM_block2 = 3, - DW_FORM_block4, - DW_FORM_data2, - DW_FORM_data4, - DW_FORM_data8, - DW_FORM_string, - DW_FORM_block, - DW_FORM_block1, - DW_FORM_data1, - DW_FORM_flag, - DW_FORM_sdata, - DW_FORM_strp, - DW_FORM_udata, - DW_FORM_ref_addr, - DW_FORM_ref1, - DW_FORM_ref2, - DW_FORM_ref4, - DW_FORM_ref8, - DW_FORM_ref_udata, - DW_FORM_indirect /* 22 */ -}; - -enum { - DW_LNS_extended_op = 0, - DW_LNS_copy, - DW_LNS_advance_pc, - DW_LNS_advance_line, - DW_LNS_set_file, - DW_LNS_set_column, - DW_LNS_negate_stmt, - DW_LNS_set_basic_block, - DW_LNS_const_add_pc, - DW_LNS_fixed_advance_pc, - DW_LNS_set_prologue_end, - DW_LNS_set_epilogue_begin, - DW_LNS_set_isa -}; - -enum { - DW_LNE_end_sequence = 1, - DW_LNE_set_address, - DW_LNE_define_file -}; diff --git a/ld64/FireOpal/src/Architectures.hpp b/ld64/src/ld/Architectures.hpp similarity index 80% rename from ld64/FireOpal/src/Architectures.hpp rename to ld64/src/ld/Architectures.hpp index 2546bfe..ceb448a 100644 --- a/ld64/FireOpal/src/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -35,7 +35,8 @@ struct ppc { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, + kPointerDiff16, kPointerDiff32, kPointerDiff=kPointerDiff32, kPointerDiff64, kBranch24, kBranch24WeakImport, kBranch14, kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, @@ -46,7 +47,8 @@ struct ppc64 { typedef Pointer64 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, + kPointerDiff16, kPointerDiff32, kPointerDiff64, kPointerDiff=kPointerDiff64, kBranch24, kBranch24WeakImport, kBranch14, kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, @@ -57,8 +59,9 @@ struct x86 { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff16, - kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kPointerDiff16, + kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, + kImageOffset32, kPointerDiff24, kSectionOffset24, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; @@ -66,11 +69,12 @@ struct x86_64 { typedef Pointer64 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32, + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointer32, kPointerWeakImport, kPointerDiff, kPointerDiff32, kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, kBranchPCRel32, kBranchPCRel32WeakImport, kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, - kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, + kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, kGOTNoFixUp, + kImageOffset32, kPointerDiff24, kSectionOffset24, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; @@ -78,7 +82,7 @@ struct arm { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kReadOnlyPointer, + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kReadOnlyPointer, kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; diff --git a/ld64/src/ArchiveReader.hpp b/ld64/src/ld/ArchiveReader.hpp similarity index 90% rename from ld64/src/ArchiveReader.hpp rename to ld64/src/ld/ArchiveReader.hpp index a195090..b8f1a5c 100644 --- a/ld64/src/ArchiveReader.hpp +++ b/ld64/src/ld/ArchiveReader.hpp @@ -55,6 +55,7 @@ class Reader : public ObjectFile::Reader static bool validFile(const uint8_t* fileContent, uint64_t fileLength); Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, + const LibraryOptions& archiveOptions, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); virtual ~Reader() {} @@ -64,9 +65,11 @@ class Reader : public ObjectFile::Reader virtual std::vector& getAtoms(); virtual std::vector* getJustInTimeAtomsFor(const char* name); virtual std::vector* getStabs() { return NULL; } - virtual void optimize(std::vector&, std::vector&, + virtual bool optimize(const std::vector&, std::vector&, std::vector&, const std::set&, + std::vector& newDeadAtoms, uint32_t, ObjectFile::Reader* writer, + ObjectFile::Atom* entryPointAtom, const std::vector& llvmOptions, bool allGlobalsAReDeadStripRoots, int okind, bool verbose, bool saveTemps, const char* outputFilePath, @@ -121,6 +124,7 @@ class Reader : public ObjectFile::Reader std::set fInstantiatedEntries; std::set fPossibleEntries; NameToEntryMap fHashTable; + bool fForceLoad; static std::vector fgEmptyList; }; @@ -258,9 +262,10 @@ bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength) template Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, + const LibraryOptions& archiveOptions, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL), - fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL) + fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL), fForceLoad(archiveOptions.fForceLoad) { fPath = strdup(path); fFileContent = fileContent; @@ -273,7 +278,7 @@ Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* if ( options.fLogAllFiles ) printf("%s\n", path); - if ( !options.fFullyLoadArchives ) { + if ( !options.fFullyLoadArchives && !fForceLoad ) { const Entry* const firstMember = (Entry*)&fFileContent[8]; if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { const uint8_t* contents = firstMember->getContent(); @@ -313,7 +318,7 @@ ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture()); } #endif - throw "not a valid archive member"; + throwf("archive member '%s' with length %d is not mach-o or bitcode", memberName, member->getContentSize()); } catch (const char* msg) { throwf("in %s, %s", memberPath, msg); @@ -324,7 +329,7 @@ ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) template std::vector& Reader::getAtoms() { - if ( fOptions.fFullyLoadArchives ) { + if ( fOptions.fFullyLoadArchives || fForceLoad ) { // build vector of all atoms from all .o files in this archive const Entry* const start = (Entry*)&fFileContent[8]; const Entry* const end = (Entry*)&fFileContent[fFileLength]; @@ -332,8 +337,12 @@ std::vector& Reader::getAtoms() const char* memberName = p->getName(); if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) continue; - if ( fOptions.fWhyLoad ) - printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); + if ( fOptions.fWhyLoad ) { + if ( fForceLoad ) + printf("-force_load forced load of %s(%s)\n", this->getPath(), memberName); + else + printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); + } ObjectFile::Reader* r = this->makeObjectReaderForMember(p); std::vector& atoms = r->getAtoms(); fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); @@ -367,18 +376,22 @@ std::vector& Reader::getAtoms() } template -void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, +bool Reader::optimize(const std::vector& allAtoms, std::vector& newAtoms, std::vector& additionalUndefines, const std::set& deadAtoms, - uint32_t nextOrdinal, ObjectFile::Reader* writer, + std::vector& newDeadAtoms, + uint32_t nextOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, const std::vector& llvmOptions, bool allGlobalsAReDeadStripRoots, int okind, bool verbose, bool saveTemps, const char* outputFilePath, bool pie, bool allowTextRelocs) { + bool result = false; for(std::vector::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) { - (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, nextOrdinal, writer, llvmOptions, - allGlobalsAReDeadStripRoots, okind, verbose, saveTemps, outputFilePath, pie, allowTextRelocs); + result |= (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, newDeadAtoms, nextOrdinal, + writer, entryPointAtom, llvmOptions, allGlobalsAReDeadStripRoots, okind, + verbose, saveTemps, outputFilePath, pie, allowTextRelocs); } + return result; } @@ -420,7 +433,7 @@ void Reader::dumpTableOfContents() template std::vector* Reader::getJustInTimeAtomsFor(const char* name) { - if ( fOptions.fFullyLoadArchives ) { + if ( fOptions.fFullyLoadArchives || fForceLoad ) { return NULL; } else { @@ -430,7 +443,7 @@ std::vector* Reader::getJustInTimeAtomsFor(const cha if ( result != NULL ) { const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) + if ( fOptions.fWhyLoad ) printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); // only return these atoms once fInstantiatedEntries.insert(member); diff --git a/ld64/src/ExecutableFile.h b/ld64/src/ld/ExecutableFile.h similarity index 88% rename from ld64/src/ExecutableFile.h rename to ld64/src/ld/ExecutableFile.h index b0b760d..675d3da 100644 --- a/ld64/src/ExecutableFile.h +++ b/ld64/src/ld/ExecutableFile.h @@ -37,7 +37,7 @@ namespace ExecutableFile { struct DyLibUsed { ObjectFile::Reader* reader; - DynamicLibraryOptions options; + LibraryOptions options; }; class Writer : public ObjectFile::Reader @@ -54,12 +54,14 @@ namespace ExecutableFile { virtual uint64_t write(std::vector& atoms, std::vector& stabs, class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldClassicHelperAtom, + class ObjectFile::Atom* dyldCompressedHelperAtom, class ObjectFile::Atom* dyldLazyDylibHelperAtom, bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, bool biggerThanTwoGigs, - bool overridesDylibWeakDefines) = 0; + std::set& atomsThatOverrideWeak, + bool hasExternalWeakDefinitions) = 0; protected: Writer(std::vector&) {}; diff --git a/ld64/src/LTOReader.hpp b/ld64/src/ld/LTOReader.hpp similarity index 93% rename from ld64/src/LTOReader.hpp rename to ld64/src/ld/LTOReader.hpp index 2736f43..1b8f2c3 100644 --- a/ld64/src/LTOReader.hpp +++ b/ld64/src/ld/LTOReader.hpp @@ -142,7 +142,7 @@ class Atom : public ObjectFile::Atom { return fRealAtom->getTranslationUnitSource(dir, name); } const char * getName () const { return fName; } const char * getDisplayName() const { return this->getName(); } - Scope getScope() const { return fScope; } + Scope getScope() const { return (fRealAtom ? fRealAtom->getScope() : fScope); } DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } SymbolTableInclusion getSymbolTableInclusion() const { return fRealAtom->getSymbolTableInclusion(); } @@ -279,10 +279,11 @@ class Reader : public ObjectFile::Reader virtual time_t getModificationTime() { return fModTime; } virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; } virtual std::vector* getStabs() { return NULL; } - virtual void optimize(std::vector& allAtoms, std::vector& newAtoms, + virtual bool optimize(const std::vector& allAtoms, std::vector& newAtoms, std::vector& additionalUndefines, const std::set&, + std::vector& newDeadAtoms, uint32_t nextInputOrdinal, - ObjectFile::Reader* writer, + ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, const std::vector& llvmOptions, bool allGlobalsAReDeadStripRoots, int outputKind, bool verbose, bool saveTemps, const char* outputFilePath, @@ -338,7 +339,13 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path for (uint32_t i=0; i < count; ++i) { const char* name = ::lto_module_get_symbol_name(fModule, i); lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i); - + + // LTO doesn't like dtrace symbols + // ignore dtrace static probes for now + // later when codegen is done and a mach-o file is produces the probes will be processed + if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) + continue; + ObjectFile::Atom::DefinitionKind kind; switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { case LTO_SYMBOL_DEFINITION_REGULAR: @@ -351,6 +358,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path kind = ObjectFile::Atom::kWeakDefinition; break; case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: kind = ObjectFile::Atom::kExternalDefinition; break; default: @@ -399,7 +407,7 @@ const char* Reader::tripletPrefixForArch(cpu_type_t arch) case CPU_TYPE_X86_64: return "x86_64-"; case CPU_TYPE_ARM: - return "arm-"; + return "arm"; } return ""; } @@ -409,9 +417,10 @@ bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); } -void Reader::optimize(std::vector& allAtoms, std::vector& newAtoms, +bool Reader::optimize(const std::vector& allAtoms, std::vector& newAtoms, std::vector& additionalUndefines, const std::set& deadAtoms, - uint32_t nextInputOrdinal, ObjectFile::Reader* writer, + std::vector& newlyDeadAtoms, + uint32_t nextInputOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, const std::vector& llvmOptions, bool allGlobalsAReDeadStripRoots, int okind, bool verbose, bool saveTemps, const char* outputFilePath, @@ -420,7 +429,7 @@ void Reader::optimize(std::vector& allAtoms, std::vector& allAtoms, std::vector& allAtoms, std::vector::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { ObjectFile::Atom* atom = *it; // only look at references come from an atom that is not an llvm atom if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) { @@ -473,6 +479,12 @@ void Reader::optimize(std::vector& allAtoms, std::vectorgetFile())) != 0 ) + nonLLVMRefs.insert(entryPointAtom->getName()); + } + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead @@ -493,12 +505,12 @@ void Reader::optimize(std::vector& allAtoms, std::vectorfirst; Atom* atom = it->second; // Include llvm Symbol in export list if it meets one of following two conditions - // 1 - globals need preserving and atom scope is global (and not linkage unit). + // 1 - atom scope is global (and not linkage unit). // 2 - included in nonLLVMRefs set. // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( globalsNeedPreserving && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) ::lto_codegen_add_must_preserve_symbol(generator, name); - else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) ::lto_codegen_add_must_preserve_symbol(generator, name); } @@ -523,6 +535,7 @@ void Reader::optimize(std::vector& allAtoms, std::vector& allAtoms, std::vector& allAtoms, std::vector deletedAtoms; for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - deletedAtoms.insert(&((*it)->fInternalAtom)); + newlyDeadAtoms.push_back(&((*it)->fInternalAtom)); } // Remove Atoms from ld if code generator optimized them away for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { // check if setRealAtom() called on this Atom if ( li->second->getRealAtom() == NULL ) - deletedAtoms.insert(li->second); + newlyDeadAtoms.push_back(li->second); } - allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), RemovableAtoms(deletedAtoms)), allAtoms.end()); + + return true; } @@ -672,6 +686,7 @@ ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32 }; // namespace lto +extern void printLTOVersion(Options &opts); void printLTOVersion(Options &opts) { const char* vers = lto_get_version(); diff --git a/ld64/src/MachOReaderDylib.hpp b/ld64/src/ld/MachOReaderDylib.hpp similarity index 86% rename from ld64/src/MachOReaderDylib.hpp rename to ld64/src/ld/MachOReaderDylib.hpp index eb8e844..8aea6ef 100644 --- a/ld64/src/MachOReaderDylib.hpp +++ b/ld64/src/ld/MachOReaderDylib.hpp @@ -37,6 +37,7 @@ #include #include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" #include "ObjectFile.h" // @@ -223,7 +224,7 @@ class Reader : public ObjectFile::Reader public: static bool validFile(const uint8_t* fileContent, bool executableOrDylib); Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const DynamicLibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, + const LibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); virtual ~Reader() {} @@ -246,6 +247,7 @@ class Reader : public ObjectFile::Reader virtual const char* parentUmbrella() { return fParentUmbrella; } virtual std::vector* getAllowableClients(); virtual bool hasWeakExternals() { return fHasWeakExports; } + virtual bool deadStrippable() { return fDeadStrippable; } virtual bool isLazyLoadedDylib() { return fLazyLoaded; } virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } @@ -273,7 +275,13 @@ class Reader : public ObjectFile::Reader struct PathAndFlag { const char* path; bool reExport; }; bool isPublicLocation(const char* path); - void addSymbol(const char* name, bool weak, uint32_t ordinal); + void addSymbol(const char* name, bool weak); + void addDyldFastStub(); + void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent); + void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, const char* strings, + const uint8_t* fileContent); const char* fPath; const char* fParentUmbrella; @@ -288,6 +296,7 @@ class Reader : public ObjectFile::Reader NameSet fIgnoreExports; bool fNoRexports; bool fHasWeakExports; + bool fDeadStrippable; const bool fLinkingFlat; const bool fLinkingMainExecutable; bool fExplictReExportFound; @@ -298,7 +307,7 @@ class Reader : public ObjectFile::Reader bool fLazyLoaded; ObjectFile::Reader::ObjcConstraint fObjcContraint; std::vector fReExportedChildren; - const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin; + const ObjectFile::ReaderOptions::MacVersionMin fDeploymentVersionMin; std::vector fFlatImports; static bool fgLogHashtable; @@ -313,15 +322,15 @@ bool Reader::fgLogHashtable = false; template Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const DynamicLibraryOptions& dylibOptions, + const LibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), - fDylibCompatibilityVersion(0), fLinkingFlat(options.fFlatNamespace), + fDylibCompatibilityVersion(0), fReExportedOrdinal(ordinalBase), fLinkingFlat(options.fFlatNamespace), fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), fObjcContraint(ObjectFile::Reader::kObjcNone), - fDeploymentVersionMin(options.fVersionMin) + fDeploymentVersionMin(options.fMacVersionMin) { // sanity check if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) @@ -355,6 +364,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p // optimize the case where we know there is no reason to look at indirect dylibs fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); + fDeadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; // pass 1 builds list of all dependent libraries @@ -381,6 +391,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p // pass 2 determines re-export info const macho_dysymtab_command

* dynamicInfo = NULL; + const macho_dyld_info_command

* dyldInfo = NULL; const macho_nlist

* symbolTable = NULL; const char* strings = NULL; cmd = cmds; @@ -396,6 +407,10 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p case LC_DYSYMTAB: dynamicInfo = (macho_dysymtab_command

*)cmd; break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command

*)cmd; + break; case LC_ID_DYLIB: { macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; @@ -504,44 +519,90 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p for (const macho_nlist

* sym=start; sym < end; ++sym) { importNames.push_back(&strings[sym->n_strx()]); } - fFlatImports.push_back(new ImportAtom(*this, ordinalBase++, importNames)); + fFlatImports.push_back(new ImportAtom(*this, fReExportedOrdinal++, importNames)); } - + // build hash table + if ( dyldInfo != NULL ) + buildExportHashTableFromExportInfo(dyldInfo, fileContent); + else + buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); + + // special case libSystem + if ( (fDylibInstallPath != NULL) && (strcmp(fDylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) + addDyldFastStub(); + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + + +template +void Reader::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, const char* strings, + const uint8_t* fileContent) +{ if ( dynamicInfo->tocoff() == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), path); + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->getPath()); const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count - uint32_t index = ordinalBase; - for (const macho_nlist

* sym=start; sym < end; ++sym, ++index) { - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, index); + for (const macho_nlist

* sym=start; sym < end; ++sym) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0); } - fReExportedOrdinal = index; } else { int32_t count = dynamicInfo->ntoc(); fAtoms.resize(count); // set initial bucket count - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, path); - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)((char*)header + dynamicInfo->tocoff()); + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->getPath()); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); for (int32_t i = 0; i < count; ++i) { const uint32_t index = E::get32(toc[i].symbol_index); const macho_nlist

* sym = &symbolTable[index]; - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, ordinalBase+i); + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0); } - fReExportedOrdinal = ordinalBase + count; } +} - - // unmap file - munmap((caddr_t)fileContent, fileLength); + +template +void Reader::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent) +{ + if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->getPath()); + if ( dyldInfo->export_size() > 0 ) { + const uint8_t* start = fileContent + dyldInfo->export_off(); + const uint8_t* end = &start[dyldInfo->export_size()]; + std::vector list; + parseTrie(start, end, list); + for (std::vector::iterator it=list.begin(); it != list.end(); ++it) + this->addSymbol(it->name, it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); + } } +template <> +void Reader::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false); +} + +template <> +void Reader::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false); +} + +template +void Reader::addDyldFastStub() +{ + // do nothing +} template -void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) +void Reader::addSymbol(const char* name, bool weakDef) { + //fprintf(stderr, "addSymbol() %s\n", name); // symbols that start with $ld$ are meta-data to the static linker // need way for ld and dyld to see different exported symbols in a dylib if ( strncmp(name, "$ld$", 4) == 0 ) { @@ -549,7 +610,7 @@ void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) const char* symAction = &name[4]; const char* symCond = strchr(symAction, '$'); if ( symCond != NULL ) { - ObjectFile::ReaderOptions::VersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinUnset; + ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { switch ( symCond[6] - '0' ) { case 0: @@ -582,7 +643,7 @@ void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) return; } else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weak, ordinal); + this->addSymbol(symName, weakDef); return; } else { @@ -607,8 +668,8 @@ void Reader::addSymbol(const char* name, bool weak, uint32_t ordinal) if ( fIgnoreExports.count(name) == 0 ) { AtomAndWeak bucket; bucket.atom = NULL; - bucket.weak = weak; - bucket.ordinal = ordinal; + bucket.weak = weakDef; + bucket.ordinal = fReExportedOrdinal++; if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); fAtoms[strdup(name)] = bucket; } @@ -625,8 +686,11 @@ std::vector& Reader::getAtoms() template std::vector* Reader::getJustInTimeAtomsFor(const char* name) { + // if supposed to ignore this export, then pretend I don't have it + if ( fIgnoreExports.count(name) != 0 ) + return NULL; + std::vector* atoms = NULL; - NameToAtomMapIterator pos = fAtoms.find(name); if ( pos != fAtoms.end() ) { if ( pos->second.atom == NULL ) { @@ -641,23 +705,20 @@ std::vector* Reader::getJustInTimeAtomsFor(const cha } else { if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); - // if not supposed to ignore this export, see if I have it - if ( fIgnoreExports.count(name) == 0 ) { - // look in children that I re-export - for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { - //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); - std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); - if ( childAtoms != NULL ) { - // make a new atom that says this reader is the owner - bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); - // return a vector of one atom - ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); - fProvidedAtom = true; - atoms = new std::vector; - atoms->push_back(newAtom); - delete childAtoms; - return atoms; - } + // look in children that I re-export + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); + std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); + if ( childAtoms != NULL ) { + // make a new atom that says this reader is the owner + bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); + // return a vector of one atom + ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); + fProvidedAtom = true; + atoms = new std::vector; + atoms->push_back(newAtom); + delete childAtoms; + return atoms; } } } @@ -719,13 +780,18 @@ void Reader::processIndirectLibraries(DylibHander* handler) //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); ((Reader*)child)->setImplicitlyLinked(); } - else + else if ( child->explicitlyLinked() || child->implicitlyLinked() ) { + //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } + else { fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } } else { // add all child's symbols to me fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + //fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); } } else if ( !fExplictReExportFound ) { diff --git a/ld64/FireOpal/src/MachOReaderRelocatable.hpp b/ld64/src/ld/MachOReaderRelocatable.hpp similarity index 74% rename from ld64/FireOpal/src/MachOReaderRelocatable.hpp rename to ld64/src/ld/MachOReaderRelocatable.hpp index 73c58e2..e576313 100644 --- a/ld64/FireOpal/src/MachOReaderRelocatable.hpp +++ b/ld64/src/ld/MachOReaderRelocatable.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,6 +40,9 @@ #include "dwarf2.h" #include "debugline.h" +#include +#include +#include // // @@ -102,18 +105,21 @@ class Reference : public ObjectFile::Reference virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const; virtual uint8_t getKind() const { return (uint8_t)fKind; } virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return (fToTargetName != NULL) ? fToTargetName : fToTarget.atom->getName(); } + virtual const char* getTargetName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getName() : fToTargetName; } virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); } virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } - virtual const char* getFromTargetName() const { return (fFromTargetName != NULL) ? fFromTargetName : fFromTarget.atom->getName(); } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } + virtual const char* getFromTargetName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getName() : fFromTargetName; } + virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } virtual void setFromTargetName(const char* name) { fFromTargetName = name; } virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } virtual const char* getDescription() const; virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } + virtual bool isBranch() const; + virtual const char* getTargetDisplayName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName; } + virtual const char* getFromTargetDisplayName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getDisplayName() : fFromTargetName; } static bool fgForFinalLinkedImage; @@ -127,6 +133,7 @@ class Reference : public ObjectFile::Reference }; + template bool Reference::fgForFinalLinkedImage = true; template @@ -270,23 +277,86 @@ LinkEditSegment LinkEditSegment::fgSingleton; class BaseAtom : public ObjectFile::Atom { public: - BaseAtom() : fStabsStartIndex(0), fStabsCount(0) {} + BaseAtom() : fStabsStartIndex(0), fStabsCount(0), fHasCompactUnwindInfo(false) {} - virtual void setSize(uint64_t size) = 0; + virtual void setSize(uint64_t size) = 0; virtual void addReference(ObjectFile::Reference* ref) = 0; virtual void sortReferences() = 0; virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; + virtual const ObjectFile::ReaderOptions& getOptions() const = 0; virtual uint64_t getObjectAddress() const = 0; virtual uint32_t getOrdinal() const { return fOrdinal; } virtual void setOrdinal(uint32_t value) { fOrdinal = value; } virtual const void* getSectionRecord() const = 0; virtual bool isAlias() const { return false; } + virtual uint8_t getLSDAReferenceKind() const { return 0; } + virtual uint8_t getPersonalityReferenceKind() const { return 0; } + virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress) { return 0; } + virtual ObjectFile::UnwindInfo::iterator beginUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[0] : NULL; } + virtual ObjectFile::UnwindInfo::iterator endUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[1] : NULL; } + virtual ObjectFile::Reference* getLSDA(); + virtual ObjectFile::Reference* getFDE(); + virtual Atom* getPersonalityPointer(); + virtual void setCompactUnwindEncoding(uint64_t ehAtomAddress); uint32_t fStabsStartIndex; uint32_t fStabsCount; uint32_t fOrdinal; + ObjectFile::UnwindInfo fSingleUnwindInfo[1]; + bool fHasCompactUnwindInfo; }; + +ObjectFile::Reference* BaseAtom::getLSDA() +{ + const uint8_t groupKind = this->getLSDAReferenceKind(); + const std::vector& refs = this->getReferences(); + for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kLSDAType) ) { + return ref; + } + } + return NULL; +} + +ObjectFile::Reference* BaseAtom::getFDE() +{ + const uint8_t groupKind = this->getLSDAReferenceKind(); + const std::vector& refs = this->getReferences(); + for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kCFIType) ) { + return ref; + } + } + return NULL; +} + +ObjectFile::Atom* BaseAtom::getPersonalityPointer() +{ + const uint8_t personalityKind = this->getPersonalityReferenceKind(); + const std::vector& refs = this->getReferences(); + for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( ref->getKind() == personalityKind ) { + if ( strcmp(ref->getTarget().getSectionName(), "__nl_symbol_ptr") == 0 ) + return &ref->getTarget(); + if ( strcmp(ref->getTarget().getSectionName(), "__pointers") == 0 ) + return &ref->getTarget(); + } + } + return NULL; +} + + +void BaseAtom::setCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + fSingleUnwindInfo[0].unwindInfo = this->getCompactUnwindEncoding(ehAtomAddress); + fHasCompactUnwindInfo = true; +} + + class BaseAtomSorter { public: @@ -366,6 +436,7 @@ class SymbolAtom : public BaseAtom virtual ObjectFile::Atom::Scope getScope() const { return fScope; } virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } + virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } virtual bool dontDeadStrip() const; virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } @@ -384,8 +455,12 @@ class SymbolAtom : public BaseAtom virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } + virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return fAddress; } virtual const void* getSectionRecord() const { return (const void*)fSection; } + virtual uint8_t getLSDAReferenceKind() const; + virtual uint8_t getPersonalityReferenceKind() const; + virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); protected: typedef typename A::P P; @@ -410,14 +485,17 @@ class SymbolAtom : public BaseAtom std::vector fLineInfo; ObjectFile::Atom::Scope fScope; SymbolTableInclusion fSymbolTableInclusion; + ObjectFile::Atom::ContentType fType; ObjectFile::Alignment fAlignment; }; template SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const macho_section

* section) - : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fAlignment(0) + : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fType(ObjectFile::Atom::kUnclassifiedType), fAlignment(0) { + fSingleUnwindInfo[0].startOffset = 0; + fSingleUnwindInfo[0].unwindInfo = 0; uint8_t type = symbol->n_type(); if ( (type & N_EXT) == 0 ) fScope = ObjectFile::Atom::scopeTranslationUnit; @@ -481,6 +559,7 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const break; case S_CSTRING_LITERALS: setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); + fType = ObjectFile::Atom::kCStringType; break; case S_REGULAR: case S_ZEROFILL: @@ -496,24 +575,6 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; } - else if ( fOwner.fOptions.fForFinalLinkedImage - && ((section->flags() & SECTION_TYPE) == S_COALESCED) - && ((section->flags() & S_ATTR_NO_TOC) == S_ATTR_NO_TOC) - && ((section->flags() & S_ATTR_STRIP_STATIC_SYMS) == S_ATTR_STRIP_STATIC_SYMS) - && (strcmp(section->sectname(), "__eh_frame") == 0) ) { - // .eh symbols exist so the linker can associate them with functions - // removing them from final linked images is a big space savings rdar://problem/4180168 - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment - fAlignment = ObjectFile::Alignment(0); - } - else if ( fOwner.fOptions.fForFinalLinkedImage - && ((section->flags() & SECTION_TYPE) == S_REGULAR) - && (strncmp(section->sectname(), "__gcc_except_tab", 16) == 0) - && (strncmp(this->getName(), "GCC_except_table", 16) == 0) ) { - // GCC_except_table* symbols don't need to exist in final linked image - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - } else if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) { // labels beginning with a lowercase ell are automatically removed in final linked images // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable) @@ -543,8 +604,24 @@ bool SymbolAtom::dontDeadStrip() const template const char* SymbolAtom::getSectionName() const { - if ( fOwner.fOptions.fForFinalLinkedImage && (strcmp(fSection->sectname(), "__textcoal_nt") == 0) ) - return "__text"; + if ( fOwner.fOptions.fForFinalLinkedImage ) { + if ( strcmp(fSection->sectname(), "__textcoal_nt") == 0 ) + return "__text"; + else if ( strcmp(fSection->sectname(), "__const_coal") == 0 ) + return "__const"; + else if ( strcmp(fSection->sectname(), "__datacoal_nt") == 0 ) + return "__data"; + else if ( fOwner.fOptions.fAutoOrderInitializers && (strcmp(fSection->sectname(), "__StaticInit") == 0) ) + return "__text"; + else { + switch ( fSection->flags() & SECTION_TYPE ) { + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_16BYTE_LITERALS: + return "__const"; + } + } + } if ( strlen(fSection->sectname()) > 15 ) { static char temp[18]; @@ -601,6 +678,9 @@ void SymbolAtom::copyRawContent(uint8_t buffer[]) const } } + + + // // A SymbolAliasAtom represents an alternate name for a SymbolAtom // @@ -634,6 +714,7 @@ class SymbolAliasAtom : public BaseAtom virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } virtual void addLineInfo(const ObjectFile::LineInfo& info) { } + virtual const ObjectFile::ReaderOptions& getOptions() const { return fAliasOf.getOptions(); } virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } virtual bool isAlias() const { return true; } @@ -716,6 +797,7 @@ class TentativeAtom : public BaseAtom virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } virtual void sortReferences() { } virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } virtual const void* getSectionRecord() const { return NULL; } @@ -812,6 +894,7 @@ class AnonymousAtom : public BaseAtom virtual const char* getDisplayName() const; virtual ObjectFile::Atom::Scope getScope() const; virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; } + virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } virtual bool dontDeadStrip() const { return fDontDeadStrip; } virtual bool isZeroFill() const; @@ -829,12 +912,16 @@ class AnonymousAtom : public BaseAtom virtual void setSize(uint64_t size) { fSize = size; } virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info); + virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return fAddress; } virtual const void* getSectionRecord() const { return (const void*)fSection; } BaseAtom* redirectTo() { return fRedirect; } bool isWeakImportStub() { return fWeakImportStub; } void resolveName(); + virtual uint8_t getLSDAReferenceKind() const; + virtual uint8_t getPersonalityReferenceKind() const; + virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); protected: typedef typename A::P P; @@ -864,13 +951,15 @@ class AnonymousAtom : public BaseAtom ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; ObjectFile::Atom::Scope fScope; ObjectFile::Atom::DefinitionKind fKind; + ObjectFile::Atom::ContentType fType; }; template AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, pint_t addr, pint_t size) : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), - fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition) + fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition), + fType(ObjectFile::Atom::kUnclassifiedType) { fSegment = new Segment(fSection); fRedirect = this; @@ -884,7 +973,28 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio break; case S_COALESCED: case S_REGULAR: - if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { + if ( section == owner.fehFrameSection ) { + if ( fSize == 1 ) { + // is CIE + fSize = 0; + fDontDeadStrip = false; + if ( fOwner.fOptions.fForFinalLinkedImage ) + fSynthesizedName = "CIE"; + else + fSynthesizedName = "EH_frame1"; + } + else { + // is FDE + fSynthesizedName = ".eh_PENDING"; + fDontDeadStrip = false; + owner.fAtomsPendingAName.push_back(this); + } + fType = ObjectFile::Atom::kCFIType; + // FDEs and CIEs don't need to be in symbol table of final linked images + if ( !fOwner.fOptions.fNoEHLabels ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } + else if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only fSynthesizedName = ".objc_class_name_PENDING"; owner.fAtomsPendingAName.push_back(this); @@ -908,13 +1018,29 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio fDontDeadStrip = false; fKind = ObjectFile::Atom::kWeakDefinition; } + else if ( (fSection->flags() & S_ATTR_SOME_INSTRUCTIONS) != 0 ) { + fDontDeadStrip = false; + asprintf((char**)&fSynthesizedName, "anon-func-0x%X", addr); + } + else if ( strncmp(fSection->sectname(), "__gcc_except_tab",16) == 0 ) { + fType = ObjectFile::Atom::kLSDAType; + fDontDeadStrip = false; + fSynthesizedName = ".lsda_PENDING"; + owner.fAtomsPendingAName.push_back(this); + if ( !fOwner.fOptions.fNoEHLabels ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; + } break; case S_CSTRING_LITERALS: { const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - asprintf((char**)&fSynthesizedName, "cstring=%s", str); + if ( (strcmp(fSection->sectname(), "__cstring") == 0) && (strcmp(section->segname(), "__TEXT") == 0) ) + asprintf((char**)&fSynthesizedName, "cstring=%s", str); + else + asprintf((char**)&fSynthesizedName, "cstring%s%s=%s", fSection->segname(), fSection->sectname(), str); fScope = ObjectFile::Atom::scopeLinkageUnit; fKind = ObjectFile::Atom::kWeakDefinition; + fType = ObjectFile::Atom::kCStringType; fDontDeadStrip = false; if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; @@ -996,6 +1122,16 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: { + // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when + // generating the new compressed LINKEDIT format + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) && fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { + macho_section

* dummySection = new macho_section

(*fSection); + dummySection->set_segname("__DATA"); + dummySection->set_sectname("__nl_symbol_ptr"); + fSection = dummySection; + fSegment = new Segment(fSection); + } + fDontDeadStrip = false; fScope = ObjectFile::Atom::scopeLinkageUnit; uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); @@ -1051,15 +1187,6 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio strcat(str, "$non_lazy_ptr"); fSynthesizedName = str; - // optimize __IMPORT segment out of i386 dyld or if -slow_stubs is used - if ( (fOwner.fOptions.fForDyld || fOwner.fOptions.fSlowx86Stubs) && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { - macho_section

* dummySection = new macho_section

(*fSection); - dummySection->set_segname("__DATA"); - dummySection->set_sectname("__nl_symbol_ptr"); - fSection = dummySection; - fSegment = new Segment(fSection); - } - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) fKind = ObjectFile::Atom::kWeakDefinition; @@ -1086,6 +1213,13 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } template bool AnonymousAtom::cstringsHaveLabels() { return false; } +template +void AnonymousAtom::addLineInfo(const ObjectFile::LineInfo& info) +{ + // don't warn if line table has entries for stubs + if ( (fSection->flags() & SECTION_TYPE) != S_SYMBOL_STUBS ) + warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); +} template void AnonymousAtom::resolveName() @@ -1096,7 +1230,7 @@ void AnonymousAtom::resolveName() for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { const char* superStr = (*rit)->getTargetName(); - if ( strncmp(superStr, "cstring=", 8) == 0 ) { + if ( strncmp(superStr, "cstring", 7) == 0 ) { const char* superClassName; asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]); new Reference(A::kNoFixUp, AtomAndOffset(this), superClassName, 0); @@ -1107,7 +1241,7 @@ void AnonymousAtom::resolveName() for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { const char* classStr = (*rit)->getTargetName(); - if ( strncmp(classStr, "cstring=", 8) == 0 ) { + if ( strncmp(classStr, "cstring", 7) == 0 ) { asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]); } break; @@ -1120,7 +1254,7 @@ void AnonymousAtom::resolveName() throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname()); ObjectFile::Reference* ref = references[0]; const char* str = ref->getTargetName(); - if ( strncmp(str, "cstring=", 8) == 0 ) { + if ( strncmp(str, "cstring", 7) == 0 ) { asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]); } } @@ -1135,17 +1269,28 @@ void AnonymousAtom::resolveName() } else { // compiled with -fwritable-strings or a non-ASCII string - ObjectFile::Atom& stringDataAtom = (*rit)->getTarget(); - uint8_t buffer[stringDataAtom.getSize()]; - stringDataAtom.copyRawContent(buffer); fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable fScope = ObjectFile::Atom::scopeTranslationUnit; fSynthesizedName = "cfstring-not-coalesable"; + if ( (*rit)->getTargetOffset() != 0 ) + warning("-fwritable-strings not compatible with literal CF/NSString in %s", fOwner.getPath()); } break; } } } + else if ( fSection == fOwner.fehFrameSection ) { + // give name to FDE + ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromFDEAddress(fAddress); + if ( funcAtom != NULL ) + asprintf((char**)&fSynthesizedName, "%s.eh", funcAtom->getDisplayName()); + } + else if ( fOwner.fLSDAAtoms.count(this) != 0) { + // give name to LSDA + ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromLSDAAddress(fAddress); + if ( funcAtom != NULL ) + asprintf((char**)&fSynthesizedName, "%s.lsda", funcAtom->getDisplayName()); + } } @@ -1186,6 +1331,15 @@ bool AnonymousAtom::isZeroFill() const template const char* AnonymousAtom::getSectionName() const { + if ( fOwner.fOptions.fForFinalLinkedImage ) { + switch ( fSection->flags() & SECTION_TYPE ) { + case S_4BYTE_LITERALS: + case S_8BYTE_LITERALS: + case S_16BYTE_LITERALS: + return "__const"; + } + } + if ( strlen(fSection->sectname()) > 15 ) { static char temp[18]; strncpy(temp, fSection->sectname(), 16); @@ -1198,6 +1352,10 @@ const char* AnonymousAtom::getSectionName() const template ObjectFile::Alignment AnonymousAtom::getAlignment() const { + // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment + if ( fType == ObjectFile::Atom::kCFIType ) + return ObjectFile::Alignment(0); + switch ( fSection->flags() & SECTION_TYPE ) { case S_4BYTE_LITERALS: return ObjectFile::Alignment(2); @@ -1239,7 +1397,6 @@ void AnonymousAtom::copyRawContent(uint8_t buffer[]) const } } - // // An AbsoluteAtom represents an N_ABS symbol which can only be created in // assembly language and usable by static executables such as the kernel/ @@ -1273,6 +1430,7 @@ class AbsoluteAtom : public BaseAtom virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } virtual void sortReferences() { } virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } virtual const void* getSectionRecord() const { return NULL; } @@ -1315,11 +1473,165 @@ AbsoluteAtom::AbsoluteAtom(Reader& owner, const macho_nlist

* symbol) + +/// +/// ObjectFileAddressSpace is used as a template parameter to UnwindCursor for parsing +/// dwarf CFI information in an object file. +/// +template +class ObjectFileAddressSpace +{ +public: + ObjectFileAddressSpace(Reader& reader); + + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::uint_t sint_t; + + uint8_t get8(pint_t addr); + uint16_t get16(pint_t addr); + uint32_t get32(pint_t addr); + uint64_t get64(pint_t addr); + pint_t getP(pint_t addr); + uint64_t getULEB128(pint_t& addr, pint_t end); + int64_t getSLEB128(pint_t& addr, pint_t end); + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); +private: + const void* mappedAddress(pint_t addr, pint_t* relocTarget=NULL); + pint_t relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount); + void buildRelocatedMap(const macho_section

* sect, std::map& map); + + Reader& fReader; + const uint8_t* fMappingStart; + const macho_section

* fSectionsStart; + const macho_section

* fSectionsEnd; + std::map fEHFrameOffsetToTargetMap; +}; + + +template +ObjectFileAddressSpace::ObjectFileAddressSpace(Reader& reader) + : fReader(reader), fMappingStart(NULL), fSectionsStart(NULL), fSectionsEnd(NULL) +{ +} + + + +template +const void* ObjectFileAddressSpace::mappedAddress(pint_t addr, pint_t* relocTarget) +{ + if ( fMappingStart == NULL ) { + // delay initialization until now when fReader.fSegment is set up + fMappingStart = (uint8_t*)fReader.fHeader; + fSectionsStart = (macho_section

*)((char*)fReader.fSegment + sizeof(macho_segment_command

)); + fSectionsEnd = &fSectionsStart[fReader.fSegment->nsects()]; + // find __eh_frame section and build map of relocations for performance + buildRelocatedMap(fReader.fehFrameSection, fEHFrameOffsetToTargetMap); + } + // special case lookups in __eh_frame section to be fast + const macho_section

* ehSect = fReader.fehFrameSection; + if ( (ehSect->addr() <= addr) && (addr < (ehSect->addr()+ehSect->size())) ) { + pint_t offsetOfAddrInSection = addr - ehSect->addr(); + if ( relocTarget != NULL ) { + std::map::iterator pos = fEHFrameOffsetToTargetMap.find(offsetOfAddrInSection); + if ( pos != fEHFrameOffsetToTargetMap.end() ) + *relocTarget = pos->second; + else + *relocTarget = 0; + } + return fMappingStart + ehSect->offset() + offsetOfAddrInSection; + } + else { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + pint_t offsetOfAddrInSection = addr - sect->addr(); + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); + const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); + // return pointer to symbol name which this non-lazy-pointer will point to + if ( relocTarget != NULL ) + *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; + } + else { + if ( relocTarget != NULL ) + *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); + } + return fMappingStart + sect->offset() + offsetOfAddrInSection; + } + } + throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); + } +} + + + + +template +uint8_t ObjectFileAddressSpace::get8(pint_t logicalAddr) +{ + return *((uint8_t*)mappedAddress(logicalAddr)); +} + +template +uint16_t ObjectFileAddressSpace::get16(pint_t logicalAddr) +{ + return P::E::get16(*((uint16_t*)mappedAddress(logicalAddr))); +} + +template +uint32_t ObjectFileAddressSpace::get32(pint_t logicalAddr) +{ + pint_t relocTarget; + return P::E::get32(*((uint32_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; +} + +template +uint64_t ObjectFileAddressSpace::get64(pint_t logicalAddr) +{ + pint_t relocTarget; + return P::E::get64(*((uint64_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; +} + +template +typename A::P::uint_t ObjectFileAddressSpace::getP(pint_t logicalAddr) +{ + pint_t relocTarget; + return P::getP(*((pint_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; +} + +template +uint64_t ObjectFileAddressSpace::getULEB128(pint_t& logicalAddr, pint_t end) +{ + uintptr_t size = (end - logicalAddr); + libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); + libunwind::LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = libunwind::LocalAddressSpace::getULEB128(laddr, laddr+size); + logicalAddr += (laddr-sladdr); + return result; +} + +template +int64_t ObjectFileAddressSpace::getSLEB128(pint_t& logicalAddr, pint_t end) +{ + uintptr_t size = (end - logicalAddr); + libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); + libunwind::LocalAddressSpace::pint_t sladdr = laddr; + int64_t result = libunwind::LocalAddressSpace::getSLEB128(laddr, laddr+size); + logicalAddr += (laddr-sladdr); + return result; +} + + + + + + template class Reader : public ObjectFile::Reader { public: - static bool validFile(const uint8_t* fileContent); + static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); virtual ~Reader() {} @@ -1345,6 +1657,10 @@ class Reader : public ObjectFile::Reader //typedef typename std::vector*> AtomVector; //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser typedef typename A::ReferenceKinds Kinds; + typedef typename libunwind::CFI_Parser >::FDE_Atom_Info FDE_Atom_Info; + typedef typename libunwind::CFI_Parser >::CIE_Atom_Info CIE_Atom_Info; + typedef class ObjectFileAddressSpace OAS; + friend class ObjectFileAddressSpace; friend class AnonymousAtom; friend class TentativeAtom; friend class AbsoluteAtom; @@ -1354,6 +1670,7 @@ class Reader : public ObjectFile::Reader void addReferencesForSection(const macho_section

* sect); bool addRelocReference(const macho_section

* sect, const macho_relocation_info

* reloc); bool addRelocReference_powerpc(const macho_section

* sect, const macho_relocation_info

* reloc); + const char* getDwarfString(uint64_t form, const uint8_t* p); bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); static bool isWeakImportSymbol(const macho_nlist

* sym); static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); @@ -1365,11 +1682,18 @@ class Reader : public ObjectFile::Reader Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr); Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr); Reference* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset); - Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); + BaseAtom* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); Reference* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset); void validSectionType(uint8_t type); void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName); void setCpuConstraint(uint32_t cpusubtype); + const macho_section

* getSectionForAddress(pint_t); + ObjectFile::Atom* getFunctionAtomFromFDEAddress(pint_t); + ObjectFile::Atom* getFunctionAtomFromLSDAAddress(pint_t); + void addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target); + void addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding); + bool isSectDiffReloc(uint8_t r_type); + BaseAtom* findAtomByName(const char*); @@ -1392,13 +1716,18 @@ class Reader : public ObjectFile::Reader std::vector fDtraceProviderInfo; ObjectFile::Reader::DebugInfoKind fDebugInfo; bool fHasUUID; + const macho_section

* fehFrameSection; + std::set fLSDAAtoms; const macho_section

* fDwarfDebugInfoSect; const macho_section

* fDwarfDebugAbbrevSect; const macho_section

* fDwarfDebugLineSect; + const macho_section

* fDwarfDebugStringSect; const char* fDwarfTranslationUnitDir; const char* fDwarfTranslationUnitFile; std::map fDwarfIndexToFile; std::vector fStabs; + std::vector fFDEInfos; + std::vector fCIEInfos; bool fAppleObjc; bool fHasDTraceProbes; bool fHaveIndirectSymbols; @@ -1406,19 +1735,24 @@ class Reader : public ObjectFile::Reader bool fHasLongBranchStubs; ObjectFile::Reader::ObjcConstraint fObjConstraint; uint32_t fCpuConstraint; + const macho_section

* fSectionsStart; + const macho_section

* fSectionsEnd; + OAS fObjectAddressSpace; }; template Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header

*)fileContent), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), - fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), + fDebugInfo(kDebugInfoNone), fHasUUID(false), fehFrameSection(NULL), + fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), - fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny) + fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny), + fSectionsStart(NULL), fSectionsEnd(NULL), fObjectAddressSpace(*this) { // sanity check - if ( ! validFile(fileContent) ) + if ( ! validFile(fileContent, false, 0) ) throw "not a valid mach-o object file"; Reference::fgForFinalLinkedImage = options.fForFinalLinkedImage; @@ -1477,24 +1811,63 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, if ( header->ncmds() < 1 ) return; - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

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

*)((char*)fSegment + sizeof(macho_segment_command

)); + fSectionsEnd = &fSectionsStart[fSegment->nsects()]; // inital guess for number of atoms fAtoms.reserve(fSymbolCount); + // if there is an __eh_frame section, decode it into chunks to get atoms in that + // section as well as division points for functions in __text + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { + if ( (strcmp(sect->sectname(), "__eh_frame") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { + fehFrameSection = sect; + const char* msg = libunwind::CFI_Parser >::getCFIs(fObjectAddressSpace, sect->addr(), + sect->size(), fFDEInfos, fCIEInfos); + if ( msg != NULL ) { + throwf("malformed __eh_frame section: %s", msg); + } + else { + //fprintf(stderr, "%lu CIEs, %lu FDEs\n", fCIEInfos.size(), fFDEInfos.size()); + // add anonymous atoms for each CIE + for (typename std::vector::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { + AnonymousAtom* cieAtom = new AnonymousAtom(*this, sect, it->cieAddress, 1); + fAtoms.push_back(cieAtom); + fAddrToAtom[it->cieAddress] = cieAtom; + } + // add anonymous atoms for each FDE and LSDA + for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { + //fprintf(stderr, "fdeAddress=0x%08llX, lsdaAddr=0x%08llX, funcAddr=0x%08llX\n", (uint64_t)it->fdeAddress, (uint64_t)it->lsda.address, (uint64_t)it->function.address); + AnonymousAtom* fdeAtom = new AnonymousAtom(*this, sect, it->fdeAddress, 0); + fAtoms.push_back(fdeAtom); + fAddrToAtom[it->fdeAddress] = fdeAtom; + if ( it->lsda.address != 0 ) { + AnonymousAtom* lsdaAtom = new AnonymousAtom(*this, getSectionForAddress(it->lsda.address), it->lsda.address, 0); + fAtoms.push_back(lsdaAtom); + fAddrToAtom[it->lsda.address] = lsdaAtom; + fLSDAAtoms.insert(lsdaAtom); + } + } + } + } + } + + // add all atoms that have entries in symbol table - const macho_section

* sections = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); + BaseAtom* sectionEndAtoms[fSegment->nsects()]; + for (unsigned int i=0; i < fSegment->nsects(); ++i) + sectionEndAtoms[i] = NULL; for (int i=fSymbolCount-1; i >= 0 ; --i) { - // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the reaal name + // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the real name const macho_nlist

& sym = fSymbols[i]; if ( (sym.n_type() & N_STAB) == 0 ) { uint8_t type = (sym.n_type() & N_TYPE); if ( type == N_SECT ) { - const macho_section

* section = §ions[sym.n_sect()-1]; - pint_t sectionEndAddr = section->addr() + section->size(); + const macho_section

* section = &fSectionsStart[sym.n_sect()-1]; + const pint_t sectionStartAddr = section->addr(); + const pint_t sectionEndAddr = sectionStartAddr + section->size(); bool suppress = false; - // ignore atoms in debugger sections + // ignore atoms in debugger sections if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) { // ignore dtrace probe labels @@ -1503,6 +1876,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, else if ( fStrings[sym.n_strx()] == 'L' ) { // ignore L labels, } + else if ( section == fehFrameSection ) { + // ignore labels in __eh_frame section + } else { // ignore labels for atoms in other sections switch ( section->flags() & SECTION_TYPE ) { @@ -1518,16 +1894,39 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, { BaseAtom* newAtom; typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value()); - if ( (pos != fAddrToAtom.end()) && (strcmp(pos->second->getSectionName(), section->sectname())==0) ) { - // another label to an existing address in the same section, make this an alias - newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *pos->second); + if ( (pos != fAddrToAtom.end()) && (pos->second->getSectionRecord() == section) ) { + if ( fLSDAAtoms.count(pos->second) != 0 ) { + // already have LSDA atom from for this address, ignore compiler's label + suppress = true; + break; + } + else { + // another label to an existing address in the same section, make this an alias + newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *pos->second); + } } else { - // make SymbolAtom atom for this address - newAtom = new SymbolAtom(*this, &sym, section); - // don't add symbols at end of section to addr->atom map - if ( sym.n_value() != sectionEndAddr ) + if ( sym.n_value() == sectionEndAddr ) { + // Symbol address is at end of section. This can interfere + // with a symbol at the start of the next section, so don't + // add to fAddrToAtom. But do track in sectionEndAtoms so we + // still make aliases if there are duplicates. + if ( sectionEndAtoms[sym.n_sect()-1] == NULL ) { + newAtom = new SymbolAtom(*this, &sym, section); + sectionEndAtoms[sym.n_sect()-1] = newAtom; + // if this is a zero length section, so add to fAddrToAtom + if ( sym.n_value() == sectionStartAddr ) + fAddrToAtom[newAtom->getObjectAddress()] = newAtom; + } + else { + newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *sectionEndAtoms[sym.n_sect()-1]); + } + } + else { + // make SymbolAtom atom for this address + newAtom = new SymbolAtom(*this, &sym, section); fAddrToAtom[newAtom->getObjectAddress()] = newAtom; + } } if ( ! suppress ) fAtoms.push_back(newAtom); @@ -1569,8 +1968,29 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } + // add anonymous atoms for any functions (as determined by dwarf unwind) have no symbol names + if ( fehFrameSection != NULL ) { + for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { + // add if not already an atom at that address + if ( fAddrToAtom.find(it->function.address) == fAddrToAtom.end() ) { + AnonymousAtom* funcAtom = new AnonymousAtom(*this, getSectionForAddress(it->function.address), it->function.address, 0); + fAtoms.push_back(funcAtom); + fAddrToAtom[it->function.address] = funcAtom; + // even though we've made a new atom, be conservative and make sure they lay out together + if ( canScatterAtoms() ) { + AtomAndOffset prev = findAtomAndOffset(it->function.address-1); + if ( prev.atom != NULL ) { + if ( ((BaseAtom*)(prev.atom))->getSectionRecord() == funcAtom->getSectionRecord() ) + new Reference(A::kFollowOn, prev, AtomAndOffset(funcAtom)); + } + } + } + } + } + + // add all fixed size anonymous atoms from special sections - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { pint_t atomSize = 0; uint8_t type (sect->flags() & SECTION_TYPE); validSectionType(type); @@ -1659,7 +2079,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } // add all c-string anonymous atoms - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { uint32_t stringLen; pint_t stringAddr; @@ -1709,10 +2129,11 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName()); // create atoms to cover any non-debug ranges not handled above - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { pint_t sectionStartAddr = sect->addr(); pint_t sectionEndAddr = sect->addr() + sect->size(); - const bool setFollowOnAtom = ! this->canScatterAtoms(); + // don't set follow-on atoms in __eh_frame section + const bool setFollowOnAtom = !canScatterAtoms() && (sect != fehFrameSection); if ( sect->size() != 0 ) { // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { @@ -1723,6 +2144,8 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, fDwarfDebugAbbrevSect = sect; else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) fDwarfDebugLineSect = sect; + else if ( strcmp(sect->sectname(), "__debug_str") == 0 ) + fDwarfDebugStringSect = sect; } else { if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { @@ -1793,7 +2216,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } // add relocation based references to sections that have atoms with pending names - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { if ( fSectionsWithAtomsPendingAName.count(sect) != 0 ) addReferencesForSection(sect); } @@ -1804,14 +2227,14 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } // add relocation based references to other sections - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { if ( fSectionsWithAtomsPendingAName.count(sect) == 0 ) addReferencesForSection(sect); } // add objective-c references if ( fAppleObjc ) { - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset); @@ -1837,44 +2260,62 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); } - // add implicit direct reference from each C++ function to its eh info - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( ((sect->flags() & SECTION_TYPE) == S_COALESCED) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { - for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - // note: this algorithm depens on the map iterator returning entries in address order - if ( (it->first >= sect->addr()) && (it->first < sect->addr()+sect->size()) ) { - pint_t ehAtomAddress = it->first; - BaseAtom* ehAtom = it->second; - const char* ehName = ehAtom->getName(); - if ( (ehName != NULL) && (strcmp(&ehName[strlen(ehName)-3], ".eh") == 0) ) { - makeReferenceToEH(ehName, ehAtomAddress, sect); - // make EH symbol static so linker does not try to coalesce - if ( fOptions.fForFinalLinkedImage ) - ehAtom->setScope(ObjectFile::Atom::scopeTranslationUnit); - // if it has a reference to a LSDA, add a group reference - std::vector& ehrefs = ehAtom->getReferences(); - // all FDE's have at least 2 references (to CIE and to function) - if ( ehrefs.size() > 2 ) { - // a third reference means there is a LSDA - ObjectFile::Atom* lsdaAtom = NULL; - for (std::vector::iterator rit=ehrefs.begin(); rit != ehrefs.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getFixUpOffset() ) { - case 4: - case 8: - // these are CIE and function references - break; - default: - // this is LSDA reference - lsdaAtom = &ref->getTarget(); - } - } - if ( lsdaAtom != NULL ) { - new Reference(A::kGroupSubordinate, AtomAndOffset(ehAtom), AtomAndOffset(lsdaAtom)); - } - } - } - } + + // add personality references to CIEs + for (typename std::vector::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { + if ( it->personality.offsetInFDE != 0 ) + addCiePersonalityReference(fAddrToAtom[it->cieAddress], it->personality.offsetInFDE, it->personality.encodingOfAddress); + } + + // add all references for FDEs, including implicit group references + for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { + AtomAndOffset funcAO = this->findAtomAndOffset(it->function.address); + if ( funcAO.offset != 0 ) + warning("FDE does not point to start of function %s\n", funcAO.atom->getDisplayName()); + AtomAndOffset fdeAO = this->findAtomAndOffset(it->fdeAddress); + if ( fdeAO.offset != 0 ) + warning("FDE does start its own atom %s\n", funcAO.atom->getDisplayName()); + AtomAndOffset cieAO = this->findAtomAndOffset(it->cie.address); + if ( cieAO.offset != 0 ) + warning("CIE does start its own atom %s\n", cieAO.atom->getDisplayName()); + AtomAndOffset lsdaAO; + if ( it->lsda.address != 0 ) { + lsdaAO = this->findAtomAndOffset(it->lsda.address); + if ( lsdaAO.offset != 0 ) + warning("LSDA does start its own atom %s\n", lsdaAO.atom->getDisplayName()); + } + + // add reference from FDE to CIE + AtomAndOffset cieInfdeAO = AtomAndOffset(fdeAO.atom, it->cie.offsetInFDE); + new Reference(A::kPointerDiff32, cieInfdeAO, cieAO, cieInfdeAO); + + // add reference from FDE to function + addFdeReference(it->function.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->function.offsetInFDE), funcAO); + + // add reference from FDE to LSDA + if ( it->lsda.address != 0 ) { + addFdeReference(it->lsda.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->lsda.offsetInFDE), lsdaAO); + } + + // FDE is in group lead by function atom + new Reference(A::kGroupSubordinate, funcAO, fdeAO); + + // LSDA is in group lead by function atom + if ( it->lsda.address != 0 ) { + new Reference(A::kGroupSubordinate, funcAO, lsdaAO); + // add back reference from LSDA to owning function + new Reference(A::kNoFixUp, lsdaAO, funcAO); + } + + // compute compact encoding for this FDE + if ( fOptions.fAddCompactUnwindEncoding ) { + ((BaseAtom*)(funcAO.atom))->setCompactUnwindEncoding(it->fdeAddress); + // add reference from function atom to personality function + // the only reference a CIE can have is the reference to the personality function + std::vector& cieRefs = cieAO.atom->getReferences(); + if ( cieRefs.size() == 1 ) { + new Reference((typename A::ReferenceKinds)((BaseAtom*)(funcAO.atom))->getPersonalityReferenceKind(), + funcAO, cieRefs[0]->getTargetName(), 0); } } } @@ -1902,7 +2343,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } - // turn indirect symbols int SymbolAliasAtom + // turn indirect symbols into SymbolAliasAtom if ( fHaveIndirectSymbols ) { for (uint32_t i=0; i < fSymbolCount; ++i) { const macho_nlist

& sym = fSymbols[i]; @@ -1957,36 +2398,43 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, uint32_t curAtomOffset = 0; uint32_t curAtomAddress = 0; uint32_t curAtomSize = 0; - while ( line_next (lines, &result, line_stop_pc) ) { - //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", - // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); - // work around weird debug line table compiler generates if no functions in __text section - if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) - continue; - // for performance, see if in next pc is in current atom - if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - // or pc at end of current atom - else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - else { - // do slow look up of atom by address - AtomAndOffset ao = this->findAtomAndOffset(result.pc); - curAtom = ao.atom; - if ( curAtom == NULL ) - break; // file has line info but no functions - if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { - // a one line function can be returned by line_next() as one entry with pc at end of blob - // look for alt atom starting at end of previous atom - uint32_t previousEnd = curAtomAddress+curAtomSize; - AtomAndOffset alt = this->findAtomAndOffset(previousEnd); - if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { - curAtom = alt.atom; - curAtomOffset = alt.offset; - curAtomAddress = previousEnd - alt.offset; - curAtomSize = curAtom->getSize(); + if ( lines != NULL ) { + while ( line_next (lines, &result, line_stop_pc) ) { + //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", + // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); + // work around weird debug line table compiler generates if no functions in __text section + if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) + continue; + // for performance, see if in next pc is in current atom + if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or pc at end of current atom + else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + else { + // do slow look up of atom by address + AtomAndOffset ao = this->findAtomAndOffset(result.pc); + curAtom = ao.atom; + if ( curAtom == NULL ) + break; // file has line info but no functions + if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { + // a one line function can be returned by line_next() as one entry with pc at end of blob + // look for alt atom starting at end of previous atom + uint32_t previousEnd = curAtomAddress+curAtomSize; + AtomAndOffset alt = this->findAtomAndOffset(previousEnd); + if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { + curAtom = alt.atom; + curAtomOffset = alt.offset; + curAtomAddress = previousEnd - alt.offset; + curAtomSize = curAtom->getSize(); + } + else { + curAtomOffset = ao.offset; + curAtomAddress = result.pc - ao.offset; + curAtomSize = curAtom->getSize(); + } } else { curAtomOffset = ao.offset; @@ -1994,33 +2442,28 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, curAtomSize = curAtom->getSize(); } } + const char* filename; + std::map::iterator pos = fDwarfIndexToFile.find(result.file); + if ( pos == fDwarfIndexToFile.end() ) { + filename = line_file(lines, result.file); + fDwarfIndexToFile[result.file] = filename; + } else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); + filename = pos->second; + } + ObjectFile::LineInfo info; + info.atomOffset = curAtomOffset; + info.fileName = filename; + info.lineNumber = result.line; + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", + // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); + ((BaseAtom*)curAtom)->addLineInfo(info); + if ( result.end_of_sequence ) { + curAtom = NULL; } } - const char* filename; - std::map::iterator pos = fDwarfIndexToFile.find(result.file); - if ( pos == fDwarfIndexToFile.end() ) { - filename = line_file(lines, result.file); - fDwarfIndexToFile[result.file] = filename; - } - else { - filename = pos->second; - } - ObjectFile::LineInfo info; - info.atomOffset = curAtomOffset; - info.fileName = filename; - info.lineNumber = result.line; - //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", - // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); - ((BaseAtom*)curAtom)->addLineInfo(info); - if ( result.end_of_sequence ) { - curAtom = NULL; - } - } line_free(lines); + } } else { warning("could not parse dwarf line number info in %s", this->getPath()); @@ -2106,7 +2549,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } if ( stab.atom == NULL ) { - warning("can't find atom for N_GSYM stabs %s in %s", symString, path); + // ld_classic added bogus GSYM stabs for old style dtrace probes + if ( (strncmp(symString, "__dtrace_probe$", 15) != 0) ) + warning("can't find atom for N_GSYM stabs %s in %s", symString, path); useStab = false; } break; @@ -2230,22 +2675,536 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, fAtoms.push_back(phony); } } -#endif +#endif + + // sort all atoms by address + std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); + + // set ordinal and sort references in each atom + uint32_t index = fOrdinalBase; + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + atom->setOrdinal(index++); + atom->sortReferences(); + } + +} + +template +const macho_section* Reader::getSectionForAddress(pint_t addr) +{ + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) + return sect; + } + throwf("section not found for address 0x%08llX", (uint64_t)addr); +} + +template +ObjectFile::Atom* Reader::getFunctionAtomFromFDEAddress(pint_t addr) +{ + for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { + if ( it->fdeAddress == addr ) { + return findAtomAndOffset(it->function.address).atom; + } + } + // CIEs won't be in fFDEInfos + return NULL; +} + +template +ObjectFile::Atom* Reader::getFunctionAtomFromLSDAAddress(pint_t addr) +{ + for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { + if ( it->lsda.address == addr ) { + return findAtomAndOffset(it->function.address).atom; + } + } + return NULL; +} + + +template <> +void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) +{ + // mach-o x86_64 is different, the content of a section with a relocation is the addend + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fReader.fHeader) + sect->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + std::map::iterator pos; + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + pos = map.find(reloc->r_address()); + if ( pos != map.end() ) + pos->second += fReader.fSymbols[reloc->r_symbolnum()].n_value(); + else + map[reloc->r_address()] = fReader.fSymbols[reloc->r_symbolnum()].n_value(); + break; + case X86_64_RELOC_SUBTRACTOR: + map[reloc->r_address()] = -fReader.fSymbols[reloc->r_symbolnum()].n_value(); + break; + case X86_64_RELOC_GOT: + // there is no good address to return here. + // GOT slots are synthsized by the linker + // this is used for the reference to the personality function in CIEs + map[reloc->r_address()] = 0; + break; + default: + fprintf(stderr, "ObjectFileAddressSpace::buildRelocatedMap() unexpected relocation at r_address=0x%08X\n", reloc->r_address()); + break; + } + } +} + +template +void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) +{ + // in all architectures except x86_64, the section contents are already fixed up to point + // to content in the same object file. +} + +template <> +uint64_t ObjectFileAddressSpace::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) +{ + // mach-o x86_64 is different, the content of a section with a relocation is the addend + uint64_t result = 0; + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fReader.fHeader) + relocsOffset); + const macho_relocation_info

* relocsEnd = &relocs[relocsCount]; + for (const macho_relocation_info

* reloc = relocs; reloc < relocsEnd; ++reloc) { + //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X), r_address=0x%08X\n", sectOffset, reloc->r_address()); + if ( reloc->r_address() == sectOffset ) { + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + result += fReader.fSymbols[reloc->r_symbolnum()].n_value(); + break; + case X86_64_RELOC_SUBTRACTOR: + result -= fReader.fSymbols[reloc->r_symbolnum()].n_value(); + break; + case X86_64_RELOC_GOT: + // there is no good address to return here. + // GOT slots are synthsized by the linker + // this is used for the reference to the personality function in CIEs + result = 0; + break; + default: + fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => type=%d, value=0x%08X\n", sectOffset, reloc->r_type(), reloc->r_symbolnum()); + break; + } + } + } + //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => 0x%0llX\n", sectOffset, result); + return result; +} + +template +typename A::P::uint_t ObjectFileAddressSpace::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) +{ + // in all architectures except x86_64, the section contents are already fixed up to point + // to content in the same object file. + return 0; +} + + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by gcc compiler +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + +template <> +void Reader::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding) +{ + if ( encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4) ) + throw "unexpected personality encoding in CIE"; + + // walk relocs looking for reloc in this CIE + uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr(); + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + fehFrameSection->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( reloc->r_address() == sectOffset ) { + switch ( reloc->r_type() ) { + case X86_64_RELOC_GOT: + if ( !reloc->r_extern() ) + throw "GOT reloc not extern for personality function"; + new Reference(x86_64::kPCRel32GOT, AtomAndOffset(cieAtom, offsetInCIE), &fStrings[fSymbols[reloc->r_symbolnum()].n_strx()], 4); + return; + default: + throw "expected GOT reloc for personality function"; + } + } + } + throw "personality function not found for CIE"; +} + +template <> +bool Reader::isSectDiffReloc(uint8_t r_type) +{ + switch ( r_type ) { + case PPC_RELOC_LOCAL_SECTDIFF: + case PPC_RELOC_SECTDIFF: + return true; + } + return false; +} + +template <> +bool Reader::isSectDiffReloc(uint8_t r_type) +{ + switch ( r_type ) { + case PPC_RELOC_LOCAL_SECTDIFF: + case PPC_RELOC_SECTDIFF: + return true; + } + return false; +} + +template <> +bool Reader::isSectDiffReloc(uint8_t r_type) +{ + switch ( r_type ) { + case GENERIC_RELOC_LOCAL_SECTDIFF: + case GENERIC_RELOC_SECTDIFF: + return true; + } + return false; +} + +template <> +bool Reader::isSectDiffReloc(uint8_t r_type) +{ + switch ( r_type ) { + case ARM_RELOC_LOCAL_SECTDIFF: + case ARM_RELOC_SECTDIFF: + return true; + } + return false; +} + +template +void Reader::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding) +{ + if ( (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4)) && (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel)) ) + throw "unexpected personality encoding in CIE"; + + // walk relocs looking for personality reloc in this CIE + uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr(); + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + fehFrameSection->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + // ignore + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_address() == sectOffset ) { + if ( isSectDiffReloc(sreloc->r_type()) ) { + // r_value is address of non-lazy-pointer to personality function + new Reference(A::kPointerDiff32, AtomAndOffset(cieAtom, offsetInCIE), AtomAndOffset(cieAtom, offsetInCIE), + findAtomAndOffset(sreloc->r_value())); + return; + } + } + } + } + throw "can't find relocation for personality in CIE"; +} - // sort all atoms by address - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); +template +void Reader::addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target) +{ + if ( (encoding & 0xF0) != DW_EH_PE_pcrel ) + throw "unsupported encoding in FDE"; + Kinds kind = A::kNoFixUp; + switch ( encoding & 0xF ) { + case DW_EH_PE_ptr: + kind = A::kPointerDiff; + break; + case DW_EH_PE_sdata4: + kind = A::kPointerDiff32; + break; + default: + throw "unsupported encoding in FDE"; + } + new Reference(kind, inFDE, inFDE, target); +} - // set ordinal and sort references in each atom - uint32_t index = fOrdinalBase; - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - atom->setOrdinal(index++); - atom->sortReferences(); +template +typename A::P::uint_t ObjectFileAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + pint_t p = addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + throwf("ObjectFileAddressSpace::getEncodedP() encoding 0x%08X not supported", encoding); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + throw "DW_EH_PE_textrel pointer encoding not supported"; + break; + case DW_EH_PE_datarel: + throw "DW_EH_PE_datarel pointer encoding not supported"; + break; + case DW_EH_PE_funcrel: + throw "DW_EH_PE_funcrel pointer encoding not supported"; + break; + case DW_EH_PE_aligned: + throw "DW_EH_PE_aligned pointer encoding not supported"; + break; + default: + throwf("ObjectFileAddressSpace::getEncodedP() encoding 0x%08X not supported", encoding); + break; } + if ( encoding & DW_EH_PE_indirect ) + result = getP(result); + + return result; +} + +template <> +uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + pint_t lsda; + pint_t personality; + char warningBuffer[1024]; + uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { + //if ( fOwner.fOptions.fForDyld ) + // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); + //else + if ( fOwner.fOptions.fWarnCompactUnwind ) + warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); + } + return result; +} + +template <> +uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + pint_t lsda; + pint_t personality; + char warningBuffer[1024]; + uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { + //if ( fOwner.fOptions.fForDyld ) + // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); + //else + if ( fOwner.fOptions.fWarnCompactUnwind ) + warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); + } + return result; +} + +template <> +uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + // compact encoding not supported for ppc + return 0; +} + +template <> +uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + // compact encoding not supported for ppc64 + return 0; +} + +template <> +uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + // compact encoding not supported for arm + return 0; +} + + +template +uint8_t SymbolAtom::getLSDAReferenceKind() const +{ + return A::kGroupSubordinate; +} + +template <> +uint8_t SymbolAtom::getPersonalityReferenceKind() const +{ + return x86_64::kGOTNoFixUp; +} + +template <> +uint8_t SymbolAtom::getPersonalityReferenceKind() const +{ + return x86::kNoFixUp; +} + +template +uint8_t SymbolAtom::getPersonalityReferenceKind() const +{ + // only used with architectures that support compact unwinding + return 0; +} + + +template <> +uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + pint_t lsda; + pint_t personality; + char warningBuffer[1024]; + uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { + //if ( fOwner.fOptions.fForDyld ) + // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); + //else + if ( fOwner.fOptions.fWarnCompactUnwind ) + warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); + } + return result; +} + +template <> +uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + pint_t lsda; + pint_t personality; + char warningBuffer[1024]; + uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { + //if ( fOwner.fOptions.fForDyld ) + // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); + //else + if ( fOwner.fOptions.fWarnCompactUnwind ) + warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); + } + return result; +} + +template <> +uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + // compact encoding not supported for ppc + return 0; +} + +template <> +uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + // compact encoding not supported for ppc64 + return 0; +} + +template <> +uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) +{ + // compact encoding not supported for arm + return 0; +} + + +template +uint8_t AnonymousAtom::getLSDAReferenceKind() const +{ + return A::kGroupSubordinate; +} + +template <> +uint8_t AnonymousAtom::getPersonalityReferenceKind() const +{ + return x86_64::kGOTNoFixUp; +} + +template <> +uint8_t AnonymousAtom::getPersonalityReferenceKind() const +{ + return x86::kNoFixUp; +} + +template +uint8_t AnonymousAtom::getPersonalityReferenceKind() const +{ + // only used with architectures that support compact unwinding + return 0; } + + + + + template <> void Reader::setCpuConstraint(uint32_t cpusubtype) { @@ -2474,13 +3433,16 @@ Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const ch } template -Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +BaseAtom* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) { - // add a direct reference from function atom to its eh frame atom + // add a group subordinate reference from function atom to its eh frame atom const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8; - return makeReference(A::kGroupSubordinate, funcAddr, ehAtomAddress); + ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom; + ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom; + new Reference(A::kGroupSubordinate, funcAtom, ehAtom); + return (BaseAtom*)funcAtom; } @@ -2510,9 +3472,9 @@ Reference* Reader::makeReferenceToSymbol(Kinds kind, pint_t atAd template <> -Reference* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) +BaseAtom* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) { - // add a direct reference from function atom to its eh frame atom + // add a group subordinate reference from function atom to its eh frame atom // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + ehSect->reloff()); @@ -2520,7 +3482,10 @@ Reference* Reader::makeReferenceToEH(const char* ehName, pint_t for (const macho_relocation_info

* reloc = relocs; reloc < relocsEnd; ++reloc) { if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); - return makeReference(x86_64::kGroupSubordinate, funcAddr, ehAtomAddress); + ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom; + ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom; + new Reference(x86_64::kGroupSubordinate, funcAtom, ehAtom); + return (BaseAtom*)funcAtom; } } warning("can't find matching function for eh symbol %s", ehName); @@ -2557,13 +3522,16 @@ AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) AtomAndOffset result; result.atom = it->second; result.offset = realAddr - it->first; + if ( result.atom->isThumb() ) + result.offset &= -2; //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); return result; } // getting here means we have a scattered relocation to an address without a label - // we should never get here... - // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section - return findAtomAndOffset(realAddr); + // so, find the atom that contains the baseAddr, and offset from that to the readAddr + AtomAndOffset result = findAtomAndOffset(baseAddr); + result.offset += (realAddr-baseAddr); + return result; } @@ -2669,7 +3637,7 @@ bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t case DW_FORM_strp: case DW_FORM_ref_addr: - sz = dwarf64 ? 8 : 4; + sz = 4; break; default: @@ -2681,6 +3649,25 @@ bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t return true; } +template +const char* Reader::getDwarfString(uint64_t form, const uint8_t* p) +{ + if ( form == DW_FORM_string ) + return (const char*)p; + else if ( form == DW_FORM_strp ) { + uint32_t offset = E::get32(*((uint32_t*)p)); + const char* dwarfStrings = (char*)(fHeader) + fDwarfDebugStringSect->offset(); + if ( offset > fDwarfDebugStringSect->size() ) { + warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->getPath()); + return NULL; + } + return &dwarfStrings[offset]; + } + warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->getPath()); + return NULL; +} + + // Look at the compilation unit DIE and determine // its NAME, compilation directory (in COMP_DIR) and its // line number information offset (in STMT_LIST). NAME and COMP_DIR @@ -2801,13 +3788,10 @@ bool Reader::read_comp_unit(const char ** name, const char ** comp_dir, if (form == DW_FORM_indirect) form = read_uleb128 (&di, end); - if (attr == DW_AT_name && form == DW_FORM_string) - *name = (const char *) di; - else if (attr == DW_AT_comp_dir && form == DW_FORM_string) - *comp_dir = (const char *) di; - /* Really we should support DW_FORM_strp here, too, but - there's usually no reason for the producer to use that form - for the DW_AT_name and DW_AT_comp_dir attributes. */ + if (attr == DW_AT_name) + *name = getDwarfString(form, di); + else if (attr == DW_AT_comp_dir) + *comp_dir = getDwarfString(form, di); else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) *stmt_list = A::P::E::get32(*(uint32_t*)di); else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) @@ -2844,7 +3828,7 @@ const char* Reader::assureFullPath(const char* path) template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC ) @@ -2857,7 +3841,7 @@ bool Reader::validFile(const uint8_t* fileContent) } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC_64 ) @@ -2870,7 +3854,7 @@ bool Reader::validFile(const uint8_t* fileContent) } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC ) @@ -2883,7 +3867,7 @@ bool Reader::validFile(const uint8_t* fileContent) } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC_64 ) @@ -2896,7 +3880,7 @@ bool Reader::validFile(const uint8_t* fileContent) } template <> -bool Reader::validFile(const uint8_t* fileContent) +bool Reader::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) { const macho_header

* header = (const macho_header

*)fileContent; if ( header->magic() != MH_MAGIC ) @@ -2905,6 +3889,8 @@ bool Reader::validFile(const uint8_t* fileContent) return false; if ( header->filetype() != MH_OBJECT ) return false; + if ( subtypeMustMatch && ((cpu_subtype_t)header->cpusubtype() != subtype) ) + return false; return true; } @@ -3023,8 +4009,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LO16: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_LO16 missing following pair"); - break; + throw "PPC_RELOC_LO16 missing following pair"; } result = true; lowBits = (instruction & 0xFFFF); @@ -3051,8 +4036,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LO14: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_LO14 missing following pair"); - break; + throw "PPC_RELOC_LO14 missing following pair"; } result = true; lowBits = (instruction & 0xFFFC); @@ -3079,8 +4063,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_HI16: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_HI16 missing following pair"); - break; + throw "PPC_RELOC_HI16 missing following pair"; } result = true; if ( reloc->r_extern() ) { @@ -3106,8 +4089,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_HA16: { if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_HA16 missing following pair"); - break; + throw "PPC_RELOC_HA16 missing following pair"; } result = true; lowBits = (nextReloc->r_address() & 0x0000FFFF); @@ -3148,11 +4130,15 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_JBSR: // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - warning("PPC_RELOC_JBSR missing following pair"); - break; + throw "PPC_RELOC_JBSR missing following pair"; } + if ( !fHasLongBranchStubs ) + warning("object file compiled with -mlong-branch which is no longer needed. To remove this warning, recompile without -mlong-branch: %s", fPath); fHasLongBranchStubs = true; result = true; + if ( reloc->r_extern() ) { + throw "PPC_RELOC_JBSR should not be using an external relocation"; + } makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); if ( (instruction & 0x4C000000) == 0x48000000 ) { displacement = (instruction & 0x03FFFFFC); @@ -3162,9 +4148,6 @@ bool Reader::addRelocReference_powerpc(const macho_section* se else { fprintf(stderr, "bad instruction for BR24 reloc"); } - if ( reloc->r_extern() ) { - fprintf(stderr, "PPC_RELOC_JBSR should not be using an external relocation"); - } break; default: warning("unknown relocation type %d", reloc->r_type()); @@ -3232,8 +4215,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LO16_SECTDIFF: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO16_SECTDIFF missing following PAIR"); - break; + throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (instruction & 0xFFFF); @@ -3244,8 +4226,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LO14_SECTDIFF: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO14_SECTDIFF missing following PAIR"); - break; + throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (instruction & 0xFFFC); @@ -3256,8 +4237,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_HA16_SECTDIFF: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_HA16_SECTDIFF missing following PAIR"); - break; + throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (nextRelocAddress & 0x0000FFFF); @@ -3268,8 +4248,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LO14: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO14 missing following PAIR"); - break; + throw "PPC_RELOC_LO14 missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (instruction & 0xFFFC); @@ -3280,8 +4259,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LO16: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_LO16 missing following PAIR"); - break; + throw "PPC_RELOC_LO16 missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (instruction & 0xFFFF); @@ -3292,8 +4270,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_HA16: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_HA16 missing following PAIR"); - break; + throw "PPC_RELOC_HA16 missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (nextRelocAddress & 0xFFFF); @@ -3304,8 +4281,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_HI16: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_HI16 missing following PAIR"); - break; + throw "PPC_RELOC_HI16 missing following pair"; } instruction = BigEndian::get32(*fixUpPtr); lowBits = (nextRelocAddress & 0xFFFF); @@ -3317,8 +4293,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se case PPC_RELOC_LOCAL_SECTDIFF: { if ( ! nextRelocIsPair ) { - warning("PPC_RELOC_SECTDIFF missing following pair"); - break; + throw "PPC_RELOC_SECTDIFF missing following pair"; } Kinds kind = A::kPointerDiff32;; uint32_t contentAddr = 0; @@ -3499,10 +4474,27 @@ bool Reader::addRelocReference(const macho_section* sect, const mac //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) if ( sreloc->r_pcrel() ) { - betterDstAddr += srcAddr + 4; - makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); + switch ( sreloc->r_length() ) { + case 2: + betterDstAddr += srcAddr + 4; + makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); + break; + case 1: + betterDstAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)) + srcAddr + 2; + makeReferenceWithToBase(x86::kPCRel16, srcAddr, betterDstAddr, dstAddr); + break; + case 0: + betterDstAddr = *((uint8_t*)fixUpPtr) + srcAddr + 1; + makeReferenceWithToBase(x86::kPCRel8, srcAddr, betterDstAddr, dstAddr); + break; + case 3: + throwf("unsupported r_length=3 for scattered pc-rel vanilla reloc"); + break; + } } else { + if ( sreloc->r_length() != 2 ) + throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length()); if ( strcmp(sect->segname(), "__TEXT") == 0 ) makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); else @@ -3513,8 +4505,7 @@ bool Reader::addRelocReference(const macho_section* sect, const mac case GENERIC_RELOC_LOCAL_SECTDIFF: { if ( !nextRelocIsPair ) { - warning("GENERIC_RELOC_SECTDIFF missing following pair"); - break; + throw "GENERIC_RELOC_SECTDIFF missing following pair"; } x86::ReferenceKinds kind = x86::kPointerDiff; uint32_t contentAddr = 0; @@ -3575,7 +4566,7 @@ bool Reader::addRelocReference(const macho_section* sect, con const char* targetName = NULL; srcAddr = sect->addr() + reloc->r_address(); fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - //fprintf(stderr, "addReloc type=%d\n", reloc->r_type()); + //fprintf(stderr, "addReloc type=%d, len=%d, address=0x%X\n", reloc->r_type(), reloc->r_length(), reloc->r_address()); if ( reloc->r_extern() ) { targetSymbol = &fSymbols[reloc->r_symbolnum()]; targetName = &fStrings[targetSymbol->n_strx()]; @@ -3584,14 +4575,26 @@ bool Reader::addRelocReference(const macho_section* sect, con case X86_64_RELOC_UNSIGNED: if ( reloc->r_pcrel() ) throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; - if ( reloc->r_length() != 3 ) - throw "length < 3 and X86_64_RELOC_UNSIGNED not supported"; + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "length < 2 and X86_64_RELOC_UNSIGNED not supported"; + case 2: + kind = x86_64::kPointer32; + break; + case 3: + if ( reloc->r_extern() && isWeakImportSymbol(targetSymbol) ) + kind = x86_64::kPointerWeakImport; + else + kind = x86_64::kPointer; + break; + } dstAddr = E::get64(*((uint64_t*)fixUpPtr)); if ( reloc->r_extern() ) { - makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr); + makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr); } else { - makeReference(x86_64::kPointer, srcAddr, dstAddr); + makeReference(kind, srcAddr, dstAddr); // verify that dstAddr is in the section being targeted int sectNum = reloc->r_symbolnum(); const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); @@ -3766,7 +4769,8 @@ bool Reader::addRelocReference(const macho_section* sect, con dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); } - ObjectFile::Atom* inAtom = this->findAtomAndOffset(srcAddr).atom; + AtomAndOffset inAtomAndOffset = this->findAtomAndOffset(srcAddr); + ObjectFile::Atom* inAtom = inAtomAndOffset.atom; // create reference with "to" target if ( nextReloc->r_extern() ) { const macho_nlist

* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; @@ -3796,6 +4800,9 @@ bool Reader::addRelocReference(const macho_section* sect, con ref->setFromTargetName(fromTargetName); } } + else { + throw "X86_64_RELOC_SUBTRACTOR not supported with r_extern=0"; + } // addend goes in from side iff negative if ( negativeAddend ) ref->setFromTargetOffset(-dstAddr); @@ -3824,6 +4831,7 @@ bool Reader::addRelocReference(const macho_section* sect, uint32_t srcAddr; uint32_t dstAddr; uint32_t pointerValue; + arm::ReferenceKinds kind = arm::kNoFixUp; if ( (reloc->r_address() & R_SCATTERED) == 0 ) { // non-scattered relocation @@ -3898,12 +4906,21 @@ bool Reader::addRelocReference(const macho_section* sect, break; case ARM_THUMB_RELOC_BR22: - // First instruction has upper 11 bits of the displacement. - displacement = (instruction & 0x7FF) << 12; - if ( (displacement & 0x400000) != 0 ) - displacement |= 0xFF800000; - // Second instruction has lower eleven bits of the displacement. - displacement += ((instruction >> 16) & 0x7FF) << 1; + // thumb2 added two more bits to displacement, complicating the displacement decoding + { + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + if ( s ) + sdis |= 0xFE000000; + displacement = sdis; + } // The pc added will be +4 from the pc displacement += 4; // If the instruction was blx, force the low 2 bits to be clear @@ -3959,22 +4976,27 @@ bool Reader::addRelocReference(const macho_section* sect, throw "bad length for ARM_RELOC_VANILLA"; pointerValue = instruction; + kind = arm::kPointer; + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + kind = arm::kReadOnlyPointer; + if ( weakImport ) + kind = arm::kPointerWeakImport; if ( reloc->r_extern() ) { - if ( weakImport ) - makeByNameReference(arm::kPointerWeakImport, srcAddr, targetName, pointerValue); - else if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeByNameReference(arm::kReadOnlyPointer, srcAddr, targetName, pointerValue); - else - makeByNameReference(arm::kPointer, srcAddr, targetName, pointerValue); + makeByNameReference(kind, srcAddr, targetName, pointerValue); } else { - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReference(arm::kReadOnlyPointer, srcAddr, pointerValue); - else - makeReference(arm::kPointer, srcAddr, pointerValue); + AtomAndOffset at = findAtomAndOffset(srcAddr); + AtomAndOffset to = findAtomAndOffset(pointerValue); + if ( to.atom->isThumb() ) + to.offset &= -2; + new Reference(kind, at, to); } break; + case ARM_THUMB_32BIT_BRANCH: + // work around for + break; + default: warning("unexpected relocation type %u", reloc->r_type()); break; @@ -4006,13 +5028,13 @@ bool Reader::addRelocReference(const macho_section* sect, if ( sreloc->r_length() != 2 ) throw "bad length for ARM_RELOC_VANILLA"; - betterDstAddr = LittleEndian::get32(*fixUpPtr); //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); + betterDstAddr = LittleEndian::get32(*fixUpPtr); + kind = arm::kPointer; + if ( strcmp(sect->segname(), "__TEXT") == 0 ) + kind = arm::kReadOnlyPointer; // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReferenceWithToBase(arm::kReadOnlyPointer, srcAddr, betterDstAddr, dstAddr); - else - makeReferenceWithToBase(arm::kPointer, srcAddr, betterDstAddr, dstAddr); + makeReferenceWithToBase(kind, srcAddr, betterDstAddr, dstAddr); break; case ARM_RELOC_BR24: @@ -4030,12 +5052,21 @@ bool Reader::addRelocReference(const macho_section* sect, break; case ARM_THUMB_RELOC_BR22: - // First instruction has upper 11 bits of the displacement. - displacement = (instruction & 0x7FF) << 12; - if ( (displacement & 0x400000) != 0 ) - displacement |= 0xFF800000; - // Second instruction has lower eleven bits of the displacement. - displacement += ((instruction >> 16) & 0x7FF) << 1; + // thumb2 added two more bits to displacement, complicating the displacement decoding + { + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + if ( s ) + sdis |= 0xFE000000; + displacement = sdis; + } // The pc added will be +4 from the pc displacement += 4; betterDstAddr = srcAddr+displacement; @@ -4048,8 +5079,7 @@ bool Reader::addRelocReference(const macho_section* sect, case ARM_RELOC_SECTDIFF: case ARM_RELOC_LOCAL_SECTDIFF: if ( !nextRelocIsPair ) { - warning("ARM_RELOC_SECTDIFF missing following pair"); - break; + throw "ARM_RELOC_SECTDIFF missing following pair"; } if ( sreloc->r_length() != 2 ) throw "bad length for ARM_RELOC_SECTDIFF"; @@ -4090,6 +5120,9 @@ void Reader::addReferencesForSection(const macho_section

* sect) // we ignore compiler generated stubs, so ignore those relocs too break; default: + // ignore all relocations in __eh_frame section + if ( sect == fehFrameSection ) + return; const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); const uint32_t relocCount = sect->nreloc(); //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); @@ -4133,8 +5166,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } break; @@ -4144,8 +5177,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } break; @@ -4164,6 +5197,18 @@ const char* Reference::getDescription() const case x86::kAbsolute32: sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); break; + case x86::kImageOffset32: + sprintf(temp, "offset 0x%04X, 32-bit offset of ", fFixUpOffsetInSrc); + break; + case x86::kPointerDiff24: + sprintf(temp, "offset 0x%04X, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", + fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset, + this->getFromTargetDisplayName(), fFromTarget.offset ); + return temp; + break; + case x86::kSectionOffset24: + sprintf(temp, "offset 0x%04X, 24-bit section offset of ", fFixUpOffsetInSrc); + break; case x86::kDtraceProbe: sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); break; @@ -4222,8 +5267,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } case ppc::kPointerDiff32: @@ -4232,8 +5277,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } case ppc::kPointerDiff64: @@ -4324,8 +5369,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } case ppc64::kPointerDiff32: @@ -4334,8 +5379,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } case ppc64::kPointerDiff16: @@ -4344,8 +5389,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } case ppc64::kBranch24WeakImport: @@ -4428,6 +5473,9 @@ const char* Reference::getDescription() const case x86_64::kPointer: sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); break; + case x86_64::kPointer32: + sprintf(temp, "offset 0x%04llX, 32-bit pointer to ", fFixUpOffsetInSrc); + break; case x86_64::kPointerDiff32: case x86_64::kPointerDiff: { @@ -4436,8 +5484,8 @@ const char* Reference::getDescription() const const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, size, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, size, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } break; @@ -4471,9 +5519,23 @@ const char* Reference::getDescription() const case x86_64::kPCRel32GOTLoadWeakImport: sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); break; + case x86_64::kGOTNoFixUp: + sprintf(temp, "reference to GOT entry for "); + break; case x86_64::kBranchPCRel8: sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc); break; + case x86_64::kPointerDiff24: + sprintf(temp, "offset 0x%04llX, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", + fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset, + this->getFromTargetDisplayName(), fFromTarget.offset ); + return temp; + case x86_64::kImageOffset32: + sprintf(temp, "offset 0x%04llX, 32bit offset of ", fFixUpOffsetInSrc); + break; + case x86_64::kSectionOffset24: + sprintf(temp, "offset 0x%04llX, 24-bit section offset of ", fFixUpOffsetInSrc); + break; case x86_64::kDtraceProbe: sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); break; @@ -4505,6 +5567,7 @@ const char* Reference::getDescription() const return temp; } + template <> const char* Reference::getDescription() const { @@ -4531,8 +5594,8 @@ const char* Reference::getDescription() const const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetName(), fromQuotes, fFromTarget.offset ); + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } case arm::kReadOnlyPointer: @@ -4577,6 +5640,71 @@ const char* Reference::getDescription() const return temp; } + +template <> +bool Reference::isBranch() const +{ + switch ( fKind ) { + case x86::kPCRel32: + case x86::kPCRel32WeakImport: + return true; + default: + return false; + } +} + +template <> +bool Reference::isBranch() const +{ + switch ( fKind ) { + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + return true; + default: + return false; + } +} + +template <> +bool Reference::isBranch() const +{ + switch ( fKind ) { + case ppc::kBranch24: + case ppc::kBranch24WeakImport: + return true; + default: + return false; + } +} + +template <> +bool Reference::isBranch() const +{ + switch ( fKind ) { + case ppc64::kBranch24: + case ppc64::kBranch24WeakImport: + return true; + default: + return false; + } +} + +template <> +bool Reference::isBranch() const +{ + switch ( fKind ) { + case arm::kBranch24: + case arm::kBranch24WeakImport: + case arm::kThumbBranch22: + case arm::kThumbBranch22WeakImport: + return true; + default: + return false; + } +} + + + }; // namespace relocatable }; // namespace mach_o diff --git a/ld64/FireOpal/src/MachOWriterExecutable.hpp b/ld64/src/ld/MachOWriterExecutable.hpp similarity index 67% rename from ld64/FireOpal/src/MachOWriterExecutable.hpp rename to ld64/src/ld/MachOWriterExecutable.hpp index 8667ae4..b019af2 100644 --- a/ld64/FireOpal/src/MachOWriterExecutable.hpp +++ b/ld64/src/ld/MachOWriterExecutable.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -45,6 +45,7 @@ #include "Options.h" #include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" // @@ -73,6 +74,7 @@ template class MachHeaderAtom; template class SegmentLoadCommandsAtom; template class EncryptionLoadCommandsAtom; template class SymbolTableLoadCommandsAtom; +template class DyldInfoLoadCommandsAtom; template class ThreadsLoadCommandsAtom; template class DylibIDLoadCommandsAtom; template class RoutinesLoadCommandsAtom; @@ -80,6 +82,11 @@ template class DyldLoadCommandsAtom; template class UUIDLoadCommandAtom; template class LinkEditAtom; template class SectionRelocationsLinkEditAtom; +template class CompressedRebaseInfoLinkEditAtom; +template class CompressedBindingInfoLinkEditAtom; +template class CompressedWeakBindingInfoLinkEditAtom; +template class CompressedLazyBindingInfoLinkEditAtom; +template class CompressedExportInfoLinkEditAtom; template class LocalRelocationsLinkEditAtom; template class ExternalRelocationsLinkEditAtom; template class SymbolTableLinkEditAtom; @@ -89,8 +96,14 @@ template class IndirectTableLinkEditAtom; template class ModuleInfoLinkEditAtom; template class StringsLinkEditAtom; template class LoadCommandsPaddingAtom; +template class UnwindInfoAtom; template class StubAtom; template class StubHelperAtom; +template class ClassicStubHelperAtom; +template class HybridStubHelperAtom; +template class HybridStubHelperHelperAtom; +template class FastStubHelperAtom; +template class FastStubHelperHelperAtom; template class LazyPointerAtom; template class NonLazyPointerAtom; template class DylibLoadCommandsAtom; @@ -102,7 +115,8 @@ class SectionInfo : public ObjectFile::Section { SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false), fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false), - fAllSelfModifyingStubs(false), fAllZeroFill(false), fVirtualSection(false), + fAllSelfModifyingStubs(false), fAllStubHelpers(false), + fAllZeroFill(false), fVirtualSection(false), fHasTextLocalRelocs(false), fHasTextExternalRelocs(false) { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } void setIndex(unsigned int index) { fIndex=index; } @@ -120,6 +134,7 @@ class SectionInfo : public ObjectFile::Section { bool fAllNonLazyPointers; bool fAllStubs; bool fAllSelfModifyingStubs; + bool fAllStubHelpers; bool fAllZeroFill; bool fVirtualSection; bool fHasTextLocalRelocs; @@ -130,9 +145,9 @@ class SectionInfo : public ObjectFile::Section { class SegmentInfo { public: - SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), - fBaseAddress(0), fSize(0), fFixedAddress(false), - fIndependentAddress(false) { fName[0] = '\0'; } + SegmentInfo(uint64_t pageSize) : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), + fBaseAddress(0), fSize(0), fPageSize(pageSize), fFixedAddress(false), + fIndependentAddress(false), fHasLoadCommand(true) { fName[0] = '\0'; } std::vector fSections; char fName[20]; uint32_t fInitProtection; @@ -141,10 +156,119 @@ class SegmentInfo uint64_t fFileSize; uint64_t fBaseAddress; uint64_t fSize; + uint64_t fPageSize; bool fFixedAddress; bool fIndependentAddress; + bool fHasLoadCommand; }; + +struct RebaseInfo { + RebaseInfo(uint8_t t, uint64_t addr) : fType(t), fAddress(addr) {} + uint8_t fType; + uint64_t fAddress; + // for sorting + int operator<(const RebaseInfo& rhs) const { + // sort by type, then address + if ( this->fType != rhs.fType ) + return (this->fType < rhs.fType ); + return (this->fAddress < rhs.fAddress ); + } +}; + +struct BindingInfo { + BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t addend) + : fType(t), fFlags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), fLibraryOrdinal(ord), + fSymbolName(sym), fAddress(addr), fAddend(addend) {} + BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t addend) + : fType(t), fFlags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ), fLibraryOrdinal(0), + fSymbolName(sym), fAddress(addr), fAddend(addend) {} + uint8_t fType; + uint8_t fFlags; + int fLibraryOrdinal; + const char* fSymbolName; + uint64_t fAddress; + int64_t fAddend; + + // for sorting + int operator<(const BindingInfo& rhs) const { + // sort by library, symbol, type, then address + if ( this->fLibraryOrdinal != rhs.fLibraryOrdinal ) + return (this->fLibraryOrdinal < rhs.fLibraryOrdinal ); + if ( this->fSymbolName != rhs.fSymbolName ) + return ( strcmp(this->fSymbolName, rhs.fSymbolName) < 0 ); + if ( this->fType != rhs.fType ) + return (this->fType < rhs.fType ); + return (this->fAddress < rhs.fAddress ); + } +}; + + +class ByteStream { +private: + std::vector fData; +public: + std::vector& bytes() { return fData; } + unsigned long size() const { return fData.size(); } + void reserve(unsigned long l) { fData.reserve(l); } + const uint8_t* start() const { return &fData[0]; } + + void append_uleb128(uint64_t value) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + fData.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + void append_sleb128(int64_t value) { + bool isNeg = ( value < 0 ); + uint8_t byte; + bool more; + do { + byte = value & 0x7F; + value = value >> 7; + if ( isNeg ) + more = ( (value != -1) || ((byte & 0x40) == 0) ); + else + more = ( (value != 0) || ((byte & 0x40) != 0) ); + if ( more ) + byte |= 0x80; + fData.push_back(byte); + } + while( more ); + } + + void append_string(const char* str) { + for (const char* s = str; *s != '\0'; ++s) + fData.push_back(*s); + fData.push_back('\0'); + } + + void append_byte(uint8_t byte) { + fData.push_back(byte); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + void pad_to_size(unsigned int alignment) { + while ( (fData.size() % alignment) != 0 ) + fData.push_back(0); + } +}; + + template class Writer : public ExecutableFile::Writer { @@ -165,11 +289,14 @@ class Writer : public ExecutableFile::Writer virtual uint64_t write(std::vector& atoms, std::vector& stabs, class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* dyldClassicHelperAtom, + class ObjectFile::Atom* dyldCompressedHelperAtom, class ObjectFile::Atom* dyldLazyDylibHelperAtom, bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, bool overridesDylibWeakDefines); + bool biggerThanTwoGigs, + std::set& atomsThatOverrideWeak, + bool hasExternalWeakDefinitions); private: typedef typename A::P P; @@ -179,6 +306,9 @@ class Writer : public ExecutableFile::Writer void assignFileOffsets(); void synthesizeStubs(); + void synthesizeKextGOT(); + void createSplitSegContent(); + void synthesizeUnwindInfoTable(); void insertDummyStubs(); void partitionIntoSections(); bool addBranchIslands(); @@ -200,6 +330,7 @@ class Writer : public ExecutableFile::Writer void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); void buildSymbolTable(); + bool stringsNeedLabelsInObjects(); const char* symbolTableName(const ObjectFile::Atom* atom); void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); void setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); @@ -207,6 +338,8 @@ class Writer : public ExecutableFile::Writer void copyNlistRange(const std::vector >& entries, uint32_t startIndex); uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); uint8_t ordinalForLibrary(ObjectFile::Reader* file); + bool targetRequiresWeakBinding(const ObjectFile::Atom& target); + int compressedOrdinalForImortedAtom(ObjectFile::Atom* target); bool shouldExport(const ObjectFile::Atom& atom) const; void buildFixups(); void adjustLinkEditSections(); @@ -241,7 +374,7 @@ class Writer : public ExecutableFile::Writer void scanForAbsoluteReferences(); bool needsModuleTable(); void optimizeDylibReferences(); - bool indirectSymbolIsLocal(const ObjectFile::Reference* ref) const; + bool indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const; struct DirectLibrary { class ObjectFile::Reader* fLibrary; @@ -256,6 +389,7 @@ class Writer : public ExecutableFile::Writer friend class SegmentLoadCommandsAtom; friend class EncryptionLoadCommandsAtom; friend class SymbolTableLoadCommandsAtom; + friend class DyldInfoLoadCommandsAtom; friend class ThreadsLoadCommandsAtom; friend class DylibIDLoadCommandsAtom; friend class RoutinesLoadCommandsAtom; @@ -263,17 +397,28 @@ class Writer : public ExecutableFile::Writer friend class UUIDLoadCommandAtom; friend class LinkEditAtom; friend class SectionRelocationsLinkEditAtom; + friend class CompressedRebaseInfoLinkEditAtom; + friend class CompressedBindingInfoLinkEditAtom; + friend class CompressedWeakBindingInfoLinkEditAtom; + friend class CompressedLazyBindingInfoLinkEditAtom; + friend class CompressedExportInfoLinkEditAtom; friend class LocalRelocationsLinkEditAtom; friend class ExternalRelocationsLinkEditAtom; friend class SymbolTableLinkEditAtom; friend class SegmentSplitInfoLoadCommandsAtom; friend class SegmentSplitInfoContentAtom; -// friend class IndirectTableLinkEditAtom; + friend class IndirectTableLinkEditAtom; friend class ModuleInfoLinkEditAtom; friend class StringsLinkEditAtom; friend class LoadCommandsPaddingAtom; + friend class UnwindInfoAtom; friend class StubAtom; friend class StubHelperAtom; + friend class ClassicStubHelperAtom; + friend class HybridStubHelperAtom; + friend class FastStubHelperAtom; + friend class FastStubHelperHelperAtom; + friend class HybridStubHelperHelperAtom; friend class LazyPointerAtom; friend class NonLazyPointerAtom; friend class DylibLoadCommandsAtom; @@ -282,28 +427,39 @@ class Writer : public ExecutableFile::Writer Options& fOptions; std::vector* fAllAtoms; std::vector* fStabs; + std::set* fRegularDefAtomsThatOverrideADylibsWeakDef; class SectionInfo* fLoadCommandsSection; class SegmentInfo* fLoadCommandsSegment; + class MachHeaderAtom* fMachHeaderAtom; class EncryptionLoadCommandsAtom* fEncryptionLoadCommand; class SegmentLoadCommandsAtom* fSegmentCommands; class SymbolTableLoadCommandsAtom* fSymbolTableCommands; class LoadCommandsPaddingAtom* fHeaderPadding; + class UnwindInfoAtom* fUnwindInfoAtom; class UUIDLoadCommandAtom* fUUIDAtom; std::vector fWriterSynthesizedAtoms; std::vector fSegmentInfos; class SegmentInfo* fPadSegmentInfo; class ObjectFile::Atom* fEntryPoint; - class ObjectFile::Atom* fDyldHelper; + class ObjectFile::Atom* fDyldClassicHelperAtom; + class ObjectFile::Atom* fDyldCompressedHelperAtom; class ObjectFile::Atom* fDyldLazyDylibHelper; std::map*> fLibraryToLoadCommand; std::map fLibraryToOrdinal; std::map fLibraryAliases; + std::set fForcedWeakImportReaders; std::vector fExportedAtoms; std::vector fImportedAtoms; std::vector fLocalSymbolAtoms; std::vector > fLocalExtraLabels; std::vector > fGlobalExtraLabels; - class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; + std::map fAtomToSymbolIndex; + class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; + class CompressedRebaseInfoLinkEditAtom* fCompressedRebaseInfoAtom; + class CompressedBindingInfoLinkEditAtom* fCompressedBindingInfoAtom; + class CompressedWeakBindingInfoLinkEditAtom* fCompressedWeakBindingInfoAtom; + class CompressedLazyBindingInfoLinkEditAtom* fCompressedLazyBindingInfoAtom; + class CompressedExportInfoLinkEditAtom* fCompressedExportInfoAtom; class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; class SymbolTableLinkEditAtom* fSymbolTableAtom; @@ -312,10 +468,14 @@ class Writer : public ExecutableFile::Writer class ModuleInfoLinkEditAtom* fModuleInfoAtom; class StringsLinkEditAtom* fStringsAtom; class PageZeroAtom* fPageZeroAtom; + class NonLazyPointerAtom* fFastStubGOTAtom; macho_nlist

* fSymbolTable; std::vector > fSectionRelocs; std::vector > fInternalRelocs; std::vector > fExternalRelocs; + std::vector fRebaseInfo; + std::vector fBindingInfo; + std::vector fWeakBindingInfo; std::map fStubsMap; std::map fGOTMap; std::vector*> fAllSynthesizedStubs; @@ -369,6 +529,7 @@ class Segment : public ObjectFile::Segment static Segment fgROImportSegment; static Segment fgDataSegment; static Segment fgObjCSegment; + static Segment fgHeaderSegment; private: @@ -387,6 +548,7 @@ Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false); Segment Segment::fgDataSegment("__DATA", true, true, false, false); Segment Segment::fgObjCSegment("__OBJC", true, true, false, false); +Segment Segment::fgHeaderSegment("__HEADER", true, false, true, false); template @@ -422,6 +584,9 @@ class WriterAtom : public ObjectFile::Atom typedef typename A::P P; typedef typename A::P::E E; + static Segment& headerSegment(Writer& writer) { return (writer.fOptions.outputKind()==Options::kPreload) + ? Segment::fgHeaderSegment : Segment::fgTextSegment; } + static std::vector fgEmptyReferenceList; Writer& fWriter; @@ -469,7 +634,7 @@ template class MachHeaderAtom : public WriterAtom { public: - MachHeaderAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} + MachHeaderAtom(Writer& writer) : WriterAtom(writer, headerSegment(writer)) {} virtual const char* getName() const; virtual const char* getDisplayName() const; virtual ObjectFile::Atom::Scope getScope() const; @@ -505,7 +670,7 @@ template class LoadCommandAtom : public WriterAtom { protected: - LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment), fOrdinal(fgCurrentOrdinal++) {} + LoadCommandAtom(Writer& writer) : WriterAtom(writer, headerSegment(writer)), fOrdinal(fgCurrentOrdinal++) {} virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } virtual const char* getSectionName() const { return "._load_commands"; } virtual uint32_t getOrdinal() const { return fOrdinal; } @@ -522,7 +687,7 @@ class SegmentLoadCommandsAtom : public LoadCommandAtom { public: SegmentLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fCommandCount(0), fSize(0) + : LoadCommandAtom(writer), fCommandCount(0), fSize(0) { writer.fSegmentCommands = this; } virtual const char* getDisplayName() const { return "segment load commands"; } virtual uint64_t getSize() const { return fSize; } @@ -562,7 +727,7 @@ class ThreadsLoadCommandsAtom : public LoadCommandAtom { public: ThreadsLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment) {} + : LoadCommandAtom(writer) {} virtual const char* getDisplayName() const { return "thread load commands"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -577,7 +742,7 @@ template class DyldLoadCommandsAtom : public LoadCommandAtom { public: - DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} virtual const char* getDisplayName() const { return "dyld load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -590,7 +755,7 @@ template class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom { public: - SegmentSplitInfoLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + SegmentSplitInfoLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} virtual const char* getDisplayName() const { return "segment split info load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -604,7 +769,7 @@ class AllowableClientLoadCommandsAtom : public LoadCommandAtom { public: AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : - LoadCommandAtom(writer, Segment::fgTextSegment), clientString(client) {} + LoadCommandAtom(writer), clientString(client) {} virtual const char* getDisplayName() const { return "allowable_client load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -619,7 +784,7 @@ class DylibLoadCommandsAtom : public LoadCommandAtom { public: DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) - : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info), + : LoadCommandAtom(writer), fInfo(info), fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; } virtual const char* getDisplayName() const { return "dylib load command"; } virtual uint64_t getSize() const; @@ -637,7 +802,7 @@ template class DylibIDLoadCommandsAtom : public LoadCommandAtom { public: - DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} virtual const char* getDisplayName() const { return "dylib ID load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -650,7 +815,7 @@ template class RoutinesLoadCommandsAtom : public LoadCommandAtom { public: - RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer, Segment::fgTextSegment) {} + RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} virtual const char* getDisplayName() const { return "routines load command"; } virtual uint64_t getSize() const { return sizeof(macho_routines_command); } virtual void copyRawContent(uint8_t buffer[]) const; @@ -664,7 +829,7 @@ class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom { public: SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) - : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + : LoadCommandAtom(writer), fName(name) {} virtual const char* getDisplayName() const { return "sub-umbrella load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -678,7 +843,7 @@ class SubLibraryLoadCommandsAtom : public LoadCommandAtom { public: SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) - : LoadCommandAtom(writer, Segment::fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {} + : LoadCommandAtom(writer), fNameStart(nameStart), fNameLength(nameLen) {} virtual const char* getDisplayName() const { return "sub-library load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -694,7 +859,7 @@ class UmbrellaLoadCommandsAtom : public LoadCommandAtom { public: UmbrellaLoadCommandsAtom(Writer& writer, const char* name) - : LoadCommandAtom(writer, Segment::fgTextSegment), fName(name) {} + : LoadCommandAtom(writer), fName(name) {} virtual const char* getDisplayName() const { return "umbrella load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -709,7 +874,7 @@ class UUIDLoadCommandAtom : public LoadCommandAtom { public: UUIDLoadCommandAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) {} + : LoadCommandAtom(writer), fEmit(false) {} virtual const char* getDisplayName() const { return "uuid load command"; } virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } virtual void copyRawContent(uint8_t buffer[]) const; @@ -729,7 +894,7 @@ class RPathLoadCommandsAtom : public LoadCommandAtom { public: RPathLoadCommandsAtom(Writer& writer, const char* path) - : LoadCommandAtom(writer, Segment::fgTextSegment), fPath(path) {} + : LoadCommandAtom(writer), fPath(path) {} virtual const char* getDisplayName() const { return "rpath load command"; } virtual uint64_t getSize() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -744,7 +909,7 @@ class EncryptionLoadCommandsAtom : public LoadCommandAtom { public: EncryptionLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fStartOffset(0), + : LoadCommandAtom(writer), fStartOffset(0), fEndOffset(0) {} virtual const char* getDisplayName() const { return "encryption info load command"; } virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command); } @@ -758,12 +923,27 @@ class EncryptionLoadCommandsAtom : public LoadCommandAtom uint32_t fEndOffset; }; +template +class DyldInfoLoadCommandsAtom : public LoadCommandAtom +{ +public: + DyldInfoLoadCommandsAtom(Writer& writer) + : LoadCommandAtom(writer) {} + virtual const char* getDisplayName() const { return "dyld info load command"; } + virtual uint64_t getSize() const { return sizeof(macho_dyld_info_command); } + virtual void copyRawContent(uint8_t buffer[]) const; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + + template class LoadCommandsPaddingAtom : public WriterAtom { public: LoadCommandsPaddingAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment), fSize(0) {} + : WriterAtom(writer, headerSegment(writer)), fSize(0) {} virtual const char* getDisplayName() const { return "header padding"; } virtual uint64_t getSize() const { return fSize; } virtual const char* getSectionName() const { return "._load_cmds_pad"; } @@ -776,6 +956,65 @@ class LoadCommandsPaddingAtom : public WriterAtom uint64_t fSize; }; +template +class UnwindInfoAtom : public WriterAtom +{ +public: + UnwindInfoAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment), + fHeaderSize(0), fPagesSize(0), fAlignment(4) {} + virtual const char* getName() const { return "unwind info"; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual uint64_t getSize() const { return fHeaderSize+fPagesSize; } + virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } + virtual const char* getSectionName() const { return "__unwind_info"; } + virtual uint32_t getOrdinal() const { return 1; } + virtual std::vector& getReferences() const { return (std::vector&)fReferences; } + virtual void copyRawContent(uint8_t buffer[]) const; + + void addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, + ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsda, + ObjectFile::Atom* personalityPointer); + void generate(); + +private: + using WriterAtom::fWriter; + typedef typename A::P P; + struct Info { ObjectFile::Atom* func; ObjectFile::Atom* fde; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; + struct LSDAEntry { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; }; + struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fde; }; + struct CompressedFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fromFunc; }; + struct CompressedEncodingFixUp { uint8_t* contentPointer; ObjectFile::Atom* fde; }; + + bool encodingMeansUseDwarf(compact_unwind_encoding_t encoding); + void compressDuplicates(std::vector& uniqueInfos); + void findCommonEncoding(const std::vector& uniqueInfos, std::map& commonEncodings); + void makeLsdaIndex(const std::vector& uniqueInfos, std::map& lsdaIndexOffsetMap); + unsigned int makeRegularSecondLevelPage(const std::vector& uniqueInfos, uint32_t pageSize, unsigned int endIndex, + uint8_t*& pageEnd); + unsigned int makeCompressedSecondLevelPage(const std::vector& uniqueInfos, + const std::map commonEncodings, + uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd); + void makePersonalityIndex(std::vector& uniqueInfos); + + + uint32_t fHeaderSize; + uint32_t fPagesSize; + uint8_t* fHeaderContent; + uint8_t* fPagesContent; + uint8_t* fPagesContentForDelete; + ObjectFile::Alignment fAlignment; + std::vector fInfos; + std::map fPersonalityIndexMap; + std::vector fLSDAIndex; + std::vector fRegFixUps; + std::vector fCompressedFixUps; + std::vector fCompressedEncodingFixUps; + std::vector fReferences; +}; + + + template class LinkEditAtom : public WriterAtom { @@ -807,6 +1046,235 @@ class SectionRelocationsLinkEditAtom : public LinkEditAtom typedef typename A::P P; }; +template +class CompressedInfoLinkEditAtom : public LinkEditAtom +{ +public: + CompressedInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } + virtual uint64_t getSize() const { return fEncodedData.size(); } + virtual void copyRawContent(uint8_t buffer[]) const { memcpy(buffer, fEncodedData.start(), fEncodedData.size()); } +protected: + typedef typename A::P::uint_t pint_t; + ByteStream fEncodedData; +private: + using WriterAtom::fWriter; + typedef typename A::P P; +}; + + + +template +class CompressedRebaseInfoLinkEditAtom : public CompressedInfoLinkEditAtom +{ +public: + CompressedRebaseInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "compressed rebase info"; } + virtual const char* getSectionName() const { return "._rebase info"; } + void encode(); +private: + using CompressedInfoLinkEditAtom::fEncodedData; + using CompressedInfoLinkEditAtom::fWriter; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; +}; + +template +class CompressedBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom +{ +public: + CompressedBindingInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "compressed binding info"; } + virtual const char* getSectionName() const { return "._binding info"; } + void encode(); +private: + using CompressedInfoLinkEditAtom::fWriter; + using CompressedInfoLinkEditAtom::fEncodedData; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; +}; + +template +class CompressedWeakBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom +{ +public: + CompressedWeakBindingInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "compressed weak binding info"; } + virtual const char* getSectionName() const { return "._wkbinding info"; } + void encode(); +private: + using CompressedInfoLinkEditAtom::fWriter; + using CompressedInfoLinkEditAtom::fEncodedData; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; +}; + +template +class CompressedLazyBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom +{ +public: + CompressedLazyBindingInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } + virtual const char* getDisplayName() const { return "compressed lazy binding info"; } + virtual const char* getSectionName() const { return "._lzbinding info"; } + void encode(); +private: + std::vector fStarts; + + using CompressedInfoLinkEditAtom::fWriter; + using CompressedInfoLinkEditAtom::fEncodedData; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; +}; + + +template +class CompressedExportInfoLinkEditAtom : public CompressedInfoLinkEditAtom +{ +public: + CompressedExportInfoLinkEditAtom(Writer& writer) + : CompressedInfoLinkEditAtom(writer), fStartNode(strdup("")) { } + virtual const char* getDisplayName() const { return "compressed export info"; } + virtual const char* getSectionName() const { return "._export info"; } + void encode(); +private: + using WriterAtom::fWriter; + using CompressedInfoLinkEditAtom::fEncodedData; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + struct node; + + struct edge + { + edge(const char* s, struct node* n) : fSubString(s), fChild(n) { } + ~edge() { } + const char* fSubString; + struct node* fChild; + + }; + + struct node + { + node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), + fHaveExportInfo(false), fTrieOffset(0) {} + ~node() { } + const char* fCummulativeString; + std::vector fChildren; + uint64_t fAddress; + uint32_t fFlags; + bool fOrdered; + bool fHaveExportInfo; + uint32_t fTrieOffset; + + void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { + const char* partialStr = &fullStr[strlen(fCummulativeString)]; + for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + edge& e = *it; + int subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addSymbol(fullStr, address, flags); + return; + } + else { + for (int i=subStringLen-1; i > 0; --i) { + if ( strncmp(e.fSubString, partialStr, i) == 0 ) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + char* bNodeCummStr = strdup(e.fChild->fCummulativeString); + bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; + //node* aNode = this; + node* bNode = new node(bNodeCummStr); + node* cNode = e.fChild; + char* abEdgeStr = strdup(e.fSubString); + abEdgeStr[i] = '\0'; + char* bcEdgeStr = strdup(&e.fSubString[i]); + edge& abEdge = e; + abEdge.fSubString = abEdgeStr; + abEdge.fChild = bNode; + edge bcEdge(bcEdgeStr, cNode); + bNode->fChildren.push_back(bcEdge); + bNode->addSymbol(fullStr, address, flags); + return; + } + } + } + } + // no commonality with any existing child, make a new edge that is this whole string + node* newNode = new node(strdup(fullStr)); + edge newEdge(strdup(partialStr), newNode); + fChildren.push_back(newEdge); + newNode->fAddress = address; + newNode->fFlags = flags; + newNode->fHaveExportInfo = true; + } + + void addOrderedNodes(const char* name, std::vector& orderedNodes) { + if ( !fOrdered ) { + orderedNodes.push_back(this); + //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); + fOrdered = true; + } + const char* partialStr = &name[strlen(fCummulativeString)]; + for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + edge& e = *it; + int subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addOrderedNodes(name, orderedNodes); + return; + } + } + } + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr) + // byte for child node count + // each child: zero terminated substring, uleb128 node offset + bool updateOffset(uint32_t& offset) { + uint32_t nodeSize = 1; // byte for length of export info + if ( fHaveExportInfo ) + nodeSize += ByteStream::uleb128_size(fFlags) + ByteStream::uleb128_size(fAddress); + + // add children + ++nodeSize; // byte for count of chidren + for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + edge& e = *it; + nodeSize += strlen(e.fSubString) + 1 + ByteStream::uleb128_size(e.fChild->fTrieOffset); + } + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(ByteStream& out) { + if ( fHaveExportInfo ) { + // nodes with export info: size, flags, address + out.append_byte(out.uleb128_size(fFlags) + out.uleb128_size(fAddress)); + out.append_uleb128(fFlags); + out.append_uleb128(fAddress); + } + else { + // no export info + out.append_byte(0); + } + // write number of children + out.append_byte(fChildren.size()); + // write each child + for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + edge& e = *it; + out.append_string(e.fSubString); + out.append_uleb128(e.fChild->fTrieOffset); + } + } + + }; + + + struct node fStartNode; +}; + template class LocalRelocationsLinkEditAtom : public LinkEditAtom { @@ -927,6 +1395,7 @@ class ModuleInfoLinkEditAtom : public LinkEditAtom private: using WriterAtom::fWriter; typedef typename A::P P; + typedef typename A::P::uint_t pint_t; uint32_t fModuleNameOffset; }; @@ -1023,25 +1492,107 @@ class StubAtom : public WriterAtom bool fForLazyDylib; }; + +template +class FastStubHelperHelperAtom : public WriterAtom +{ +public: + FastStubHelperHelperAtom(Writer& writer); + virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__stub_helper"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } +protected: + using WriterAtom::fWriter; + std::vector fReferences; +}; + +template +class HybridStubHelperHelperAtom : public WriterAtom +{ +public: + HybridStubHelperHelperAtom(Writer& writer); + virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual uint64_t getSize() const; + virtual const char* getSectionName() const { return "__stub_helper"; } + virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } +protected: + using WriterAtom::fWriter; + std::vector fReferences; +}; + template class StubHelperAtom : public WriterAtom { public: - StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer, bool forLazyDylib); + StubHelperAtom(Writer& writer, ObjectFile::Atom& target, + LazyPointerAtom& lazyPointer, bool forLazyDylib) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), + fTarget(target), fLazyPointerAtom(lazyPointer) { + writer.fAllSynthesizedStubHelpers.push_back(this); + } + virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const; virtual const char* getSectionName() const { return "__stub_helper"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; ObjectFile::Atom* getTarget() { return &fTarget; } -private: + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } +protected: static const char* stubName(const char* importName); using WriterAtom::fWriter; const char* fName; ObjectFile::Atom& fTarget; + LazyPointerAtom& fLazyPointerAtom; std::vector fReferences; }; + +template +class ClassicStubHelperAtom : public StubHelperAtom +{ +public: + ClassicStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib); + + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; +}; + + +template +class HybridStubHelperAtom : public StubHelperAtom +{ +public: + HybridStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib); + + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; + static class HybridStubHelperHelperAtom* fgHelperHelperAtom; +}; +template class HybridStubHelperHelperAtom* HybridStubHelperAtom::fgHelperHelperAtom = NULL; + +template +class FastStubHelperAtom : public StubHelperAtom +{ +public: + FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib); + virtual uint64_t getSize() const; + virtual void copyRawContent(uint8_t buffer[]) const; + static FastStubHelperHelperAtom* fgHelperHelperAtom; +}; +template FastStubHelperHelperAtom* FastStubHelperAtom::fgHelperHelperAtom = NULL; + + template class LazyPointerAtom : public WriterAtom @@ -1056,6 +1607,8 @@ class LazyPointerAtom : public WriterAtom virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; ObjectFile::Atom* getTarget() { return &fExternalTarget; } + void setLazyBindingInfoOffset(uint32_t off) { fLazyBindingOffset = off; } + uint32_t getLazyBindingInfoOffset() { return fLazyBindingOffset; } private: using WriterAtom::fWriter; static const char* lazyPointerName(const char* importName); @@ -1064,6 +1617,7 @@ class LazyPointerAtom : public WriterAtom ObjectFile::Atom& fExternalTarget; std::vector fReferences; bool fForLazyDylib; + uint32_t fLazyBindingOffset; }; @@ -1072,18 +1626,20 @@ class NonLazyPointerAtom : public WriterAtom { public: NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target); + NonLazyPointerAtom(Writer& writer, const char* targetName); + NonLazyPointerAtom(Writer& writer); virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const { return "__nl_symbol_ptr"; } + virtual const char* getSectionName() const { return (fWriter.fOptions.outputKind() == Options::kKextBundle) ? "__got" : "__nl_symbol_ptr"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fTarget; } + ObjectFile::Atom* getTarget() { return fTarget; } private: using WriterAtom::fWriter; static const char* nonlazyPointerName(const char* importName); const char* fName; - ObjectFile::Atom& fTarget; + ObjectFile::Atom* fTarget; std::vector fReferences; }; @@ -1113,16 +1669,19 @@ class WriterReference : public ObjectFile::Reference WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) - : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), + : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), fTargetName(target->getName()), fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} + WriterReference(uint32_t offset, Kinds kind, const char* targetName) + : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(NULL), fTargetName(targetName), + fTargetOffset(0), fFromTarget(NULL), fFromTargetOffset(0) {} virtual ~WriterReference() {} - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } + virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kUnboundByName; } virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; } virtual uint8_t getKind() const { return (uint8_t)fKind; } virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return fTarget->getName(); } + virtual const char* getTargetName() const { return fTargetName; } virtual ObjectFile::Atom& getTarget() const { return *fTarget; } virtual uint64_t getTargetOffset() const { return fTargetOffset; } virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } @@ -1138,41 +1697,48 @@ class WriterReference : public ObjectFile::Reference Kinds fKind; uint32_t fFixUpOffsetInSrc; ObjectFile::Atom* fTarget; + const char* fTargetName; uint32_t fTargetOffset; ObjectFile::Atom* fFromTarget; uint32_t fFromTargetOffset; }; +template +const char* StubHelperAtom::stubName(const char* name) +{ + char* buf; + asprintf(&buf, "%s$stubHelper", name); + return buf; +} template <> -StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, - ObjectFile::Atom& lazyPointer, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +ClassicStubHelperAtom::ClassicStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) { - writer.fAllSynthesizedStubHelpers.push_back(this); - - fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &fLazyPointerAtom)); if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) + if ( fWriter.fDyldLazyDylibHelper == NULL ) throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldLazyDylibHelper)); + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, fWriter.fDyldLazyDylibHelper)); } else { - if ( writer.fDyldHelper == NULL ) + if ( fWriter.fDyldClassicHelperAtom == NULL ) throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, fWriter.fDyldClassicHelperAtom)); } } + template <> -uint64_t StubHelperAtom::getSize() const +uint64_t ClassicStubHelperAtom::getSize() const { return 12; } template <> -void StubHelperAtom::copyRawContent(uint8_t buffer[]) const +void ClassicStubHelperAtom::copyRawContent(uint8_t buffer[]) const { buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 buffer[1] = 0x8D; @@ -1189,49 +1755,479 @@ void StubHelperAtom::copyRawContent(uint8_t buffer[]) const } -template -const char* StubHelperAtom::stubName(const char* name) +template <> +FastStubHelperHelperAtom::FastStubHelperHelperAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment) { - char* buf; - asprintf(&buf, "%s$stubHelper", name); - return buf; + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, new NonLazyPointerAtom(writer))); + fReferences.push_back(new WriterReference(11, x86_64::kPCRel32, writer.fFastStubGOTAtom)); } - -// specialize lazy pointer for x86_64 to initially pointer to stub helper template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +uint64_t FastStubHelperHelperAtom::getSize() const { - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - StubHelperAtom* helper = new StubHelperAtom(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); + return 16; } -// specialize lazy pointer for x86 to initially pointer to second half of stub template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const { - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); + buffer[0] = 0x4C; // leaq dyld_mageLoaderCache(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x41; // pushq %r11 + buffer[8] = 0x53; + buffer[9] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[10] = 0x25; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x90; // nop +} - // helper part of stub is 14 or 6 bytes into stub - fReferences.push_back(new WriterReference(0, x86::kPointer, &stub, writer.fSlideable ? 14 : 6)); + +template <> +HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment) +{ + if ( writer.fDyldClassicHelperAtom == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32_1, writer.fFastStubGOTAtom)); + fReferences.push_back(new WriterReference(13, x86_64::kPCRel32, new NonLazyPointerAtom(writer))); + fReferences.push_back(new WriterReference(21, x86_64::kPCRel32, writer.fFastStubGOTAtom)); + fReferences.push_back(new WriterReference(30, x86_64::kPCRel32, writer.fDyldClassicHelperAtom)); } -template -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +template <> +uint64_t HybridStubHelperHelperAtom::getSize() const +{ + return 34; +} + +template <> +void HybridStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x48; // cmpl $0x00,_fast_lazy_bind + buffer[1] = 0x83; + buffer[2] = 0x3D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x74; // je 16 + buffer[9] = 0x0F; + buffer[10] = 0x4C; // leaq imageCache(%rip),%r11 + buffer[11] = 0x8D; + buffer[12] = 0x1D; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x00; + buffer[16] = 0x00; + buffer[17] = 0x41; // pushq %r11 + buffer[18] = 0x53; + buffer[19] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[20] = 0x25; + buffer[21] = 0x00; + buffer[22] = 0x00; + buffer[23] = 0x00; + buffer[24] = 0x00; + buffer[25] = 0x48; // addq $8,%rsp + buffer[26] = 0x83; + buffer[27] = 0xC4; + buffer[28] = 0x08; + buffer[29] = 0xE9; // jmp dyld_stub_binding_helper + buffer[30] = 0x00; + buffer[31] = 0x00; + buffer[32] = 0x00; + buffer[33] = 0x00; +} + + +template <> +HybridStubHelperAtom::HybridStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) +{ + if ( fgHelperHelperAtom == NULL ) { + fgHelperHelperAtom = new HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(fWriter); + fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); + } + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, &fLazyPointerAtom)); + fReferences.push_back(new WriterReference(13, x86_64::kPCRel32, fgHelperHelperAtom)); +} + +template <> +uint64_t HybridStubHelperAtom::getSize() const +{ + return 18; +} + +template <> +void HybridStubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x68; // pushq $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 + buffer[6] = 0x8D; + buffer[7] = 0x1D; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0xE9; // jmp helper-helper + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x00; + buffer[16] = 0x00; + buffer[17] = 0x90; // nop + + // the lazy binding info is created later than this helper atom, so there + // is no Reference to update. Instead we blast the offset here. + uint32_t offset; + LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); + memcpy(&buffer[1], &offset, 4); +} + +template <> +FastStubHelperAtom::FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) +{ + if ( fgHelperHelperAtom == NULL ) { + fgHelperHelperAtom = new FastStubHelperHelperAtom::FastStubHelperHelperAtom(fWriter); + fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); + } + fReferences.push_back(new WriterReference(6, x86_64::kPCRel32, fgHelperHelperAtom)); +} + +template <> +uint64_t FastStubHelperAtom::getSize() const +{ + return 10; +} + +template <> +void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x68; // pushq $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + + // the lazy binding info is created later than this helper atom, so there + // is no Reference to update. Instead we blast the offset here. + uint32_t offset; + LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); + memcpy(&buffer[1], &offset, 4); +} + +template <> +FastStubHelperHelperAtom::FastStubHelperHelperAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment) +{ + fReferences.push_back(new WriterReference(1, x86::kAbsolute32, new NonLazyPointerAtom(writer))); + fReferences.push_back(new WriterReference(7, x86::kAbsolute32, writer.fFastStubGOTAtom)); +} + +template <> +uint64_t FastStubHelperHelperAtom::getSize() const +{ + return 12; +} + +template <> +void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x68; // pushl $dyld_ImageLoaderCache + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[6] = 0x25; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x90; // nop +} + + +template <> +HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment) +{ + if ( writer.fDyldClassicHelperAtom == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + fReferences.push_back(new WriterReference(2, x86::kAbsolute32, writer.fFastStubGOTAtom)); + fReferences.push_back(new WriterReference(18, x86::kPCRel32, writer.fDyldClassicHelperAtom)); + fReferences.push_back(new WriterReference(26, x86::kAbsolute32, new NonLazyPointerAtom(writer))); + fReferences.push_back(new WriterReference(32, x86::kAbsolute32, writer.fFastStubGOTAtom)); +} + +template <> +uint64_t HybridStubHelperHelperAtom::getSize() const +{ + return 36; +} + + +template <> +void HybridStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x83; // cmpl $0x00,_fast_lazy_bind + buffer[1] = 0x3D; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x75; // jne 22 + buffer[8] = 0x0D; + buffer[9] = 0x89; // %eax,4(%esp) + buffer[10] = 0x44; + buffer[11] = 0x24; + buffer[12] = 0x04; + buffer[13] = 0x58; // popl %eax + buffer[14] = 0x87; // xchgl (%esp),%eax + buffer[15] = 0x04; + buffer[16] = 0x24; + buffer[17] = 0xE9; // jmpl dyld_stub_binding_helper + buffer[18] = 0x00; + buffer[19] = 0x00; + buffer[20] = 0x00; + buffer[21] = 0x00; + buffer[22] = 0x83; // addl $0x04,%esp + buffer[23] = 0xC4; + buffer[24] = 0x04; + buffer[25] = 0x68; // pushl imageloadercahce + buffer[26] = 0x00; + buffer[27] = 0x00; + buffer[28] = 0x00; + buffer[29] = 0x00; + buffer[30] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[31] = 0x25; + buffer[32] = 0x00; + buffer[33] = 0x00; + buffer[34] = 0x00; + buffer[35] = 0x00; +} + + +template <> +ClassicStubHelperAtom::ClassicStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) +{ + fReferences.push_back(new WriterReference(1, x86::kAbsolute32, &fLazyPointerAtom)); + if ( forLazyDylib ) { + if ( fWriter.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + fReferences.push_back(new WriterReference(6, x86::kPCRel32, fWriter.fDyldLazyDylibHelper)); + } + else { + if ( fWriter.fDyldClassicHelperAtom == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + fReferences.push_back(new WriterReference(6, x86::kPCRel32, fWriter.fDyldClassicHelperAtom)); + } +} + +template <> +uint64_t ClassicStubHelperAtom::getSize() const +{ + return 10; +} + +template <> +void ClassicStubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x68; // pushl $foo$lazy_ptr + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; +} + +template <> +HybridStubHelperAtom::HybridStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) +{ + if ( fgHelperHelperAtom == NULL ) { + fgHelperHelperAtom = new HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(fWriter); + fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); + } + fReferences.push_back(new WriterReference(6, x86::kAbsolute32, &fLazyPointerAtom)); + fReferences.push_back(new WriterReference(11, x86::kPCRel32, fgHelperHelperAtom)); +} + + +template <> +uint64_t HybridStubHelperAtom::getSize() const +{ + return 16; +} + +template <> +void HybridStubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x68; // pushl $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x68; // pushl $foo$lazy_ptr + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0xE9; // jmp dyld_hybrid_stub_binding_helper + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x90; // nop + + // the lazy binding info is created later than this helper atom, so there + // is no Reference to update. Instead we blast the offset here. + uint32_t offset; + LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); + memcpy(&buffer[1], &offset, 4); +} + + +template <> +FastStubHelperAtom::FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) +{ + if ( fgHelperHelperAtom == NULL ) { + fgHelperHelperAtom = new FastStubHelperHelperAtom::FastStubHelperHelperAtom(fWriter); + fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); + } + fReferences.push_back(new WriterReference(6, x86::kPCRel32, fgHelperHelperAtom)); +} + + +template <> +uint64_t FastStubHelperAtom::getSize() const +{ + return 10; +} + +template <> +void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + buffer[0] = 0x68; // pushl $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + + // the lazy binding info is created later than this helper atom, so there + // is no Reference to update. Instead we blast the offset here. + uint32_t offset; + LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); + memcpy(&buffer[1], &offset, 4); +} + + + +// specialize lazy pointer for x86_64 to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fLazyBindingOffset(0) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + ObjectFile::Atom* helper; + if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) { + if ( writer.fOptions.makeClassicDyldInfo() ) + // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper + if ( writer.targetRequiresWeakBinding(target) ) + helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); + else + helper = new HybridStubHelperAtom(writer, target, *this, forLazyDylib); + else { + if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + helper = ⌖ + else + helper = new FastStubHelperAtom(writer, target, *this, forLazyDylib); + } + } + else { + helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); + } + fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); +} + + +// specialize lazy pointer for x86 to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + ObjectFile::Atom* helper; + if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) { + if ( writer.fOptions.makeClassicDyldInfo() ) { + // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper + if ( writer.targetRequiresWeakBinding(target) ) + helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); + else + helper = new HybridStubHelperAtom(writer, target, *this, forLazyDylib); + } + else { + if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + helper = ⌖ + else + helper = new FastStubHelperAtom(writer, target, *this, forLazyDylib); + } + } + else { + helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); + } + fReferences.push_back(new WriterReference(0, x86::kPointer, helper)); +} + +template +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) { if ( forLazyDylib ) writer.fAllSynthesizedLazyDylibPointers.push_back(this); @@ -1260,13 +2256,27 @@ void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const template NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) + : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(&target) { writer.fAllSynthesizedNonLazyPointers.push_back(this); - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); } +template +NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer) + : WriterAtom(writer, Segment::fgDataSegment), fName("none"), fTarget(NULL) +{ + writer.fAllSynthesizedNonLazyPointers.push_back(this); +} + +template +NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, const char* targetName) + : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(targetName)), fTarget(NULL) +{ + writer.fAllSynthesizedNonLazyPointers.push_back(this); + fReferences.push_back(new WriterReference(0, A::kPointer, targetName)); +} + template const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) { @@ -1336,9 +2346,9 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forL lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); } else { - if ( writer.fDyldHelper == NULL ) + if ( writer.fDyldClassicHelperAtom == NULL ) throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); + lp = new LazyPointerAtom(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); } } if ( pic() ) { @@ -1366,9 +2376,9 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); } else { - if ( writer.fDyldHelper == NULL ) + if ( writer.fDyldClassicHelperAtom == NULL ) throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldHelper, *this, forLazyDylib); + lp = new LazyPointerAtom(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); } if ( pic() ) { // picbase is 8 bytes into atom @@ -1381,38 +2391,16 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool } } -// specialize to put x86 fast stub in __IMPORT segment with no lazy pointer template <> StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, (writer.fOptions.slowx86Stubs() || forLazyDylib) ? Segment::fgTextSegment : - ( writer.fOptions.readOnlyx86Stubs() ? Segment::fgROImportSegment : Segment::fgImportSegment)), - fTarget(target), fForLazyDylib(forLazyDylib) + : WriterAtom(writer, (writer.fOptions.makeCompressedDyldInfo()|| forLazyDylib) ? Segment::fgTextSegment : Segment::fgImportSegment), + fName(NULL), fTarget(target), fForLazyDylib(forLazyDylib) { - if ( writer.fOptions.slowx86Stubs() || forLazyDylib ) { + if ( writer.fOptions.makeCompressedDyldInfo() || forLazyDylib ) { fName = stubName(target.getName()); - writer.fAllSynthesizedStubs.push_back(this); LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - ObjectFile::Atom* helper; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - helper = writer.fDyldLazyDylibHelper; - } - else { - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldHelper; - } - if ( pic() ) { - // picbase is 5 bytes into atom - fReferences.push_back(new WriterReference(8, x86::kPointerDiff, lp, 0, this, 5)); - fReferences.push_back(new WriterReference(16, x86::kPCRel32, helper)); - } - else { - fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); - fReferences.push_back(new WriterReference(7, x86::kAbsolute32, lp)); - fReferences.push_back(new WriterReference(12, x86::kPCRel32, helper)); - } + fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); + writer.fAllSynthesizedStubs.push_back(this); } else { if ( &target == NULL ) @@ -1424,6 +2412,7 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forL } } + template <> StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) @@ -1455,9 +2444,9 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forL helper = writer.fDyldLazyDylibHelper; } else { - if ( writer.fDyldHelper == NULL ) + if ( writer.fDyldClassicHelperAtom == NULL ) throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldHelper; + helper = writer.fDyldClassicHelperAtom; } lp = new LazyPointerAtom(writer, *helper, *this, forLazyDylib); } @@ -1497,13 +2486,10 @@ uint64_t StubAtom::getSize() const template <> uint64_t StubAtom::getSize() const { - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { - if ( pic() ) - return 20; - else - return 16; - } - return 5; + if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) + return 6; + else + return 5; } template <> @@ -1515,10 +2501,10 @@ uint64_t StubAtom::getSize() const template <> ObjectFile::Alignment StubAtom::getAlignment() const { - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) - return 2; + if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) + return 1; else - return 0; // special case x86 fast stubs to be byte aligned + return 0; // special case x86 self-modifying stubs to be byte aligned } template <> @@ -1566,47 +2552,13 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const template <> void StubAtom::copyRawContent(uint8_t buffer[]) const { - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { - if ( pic() ) { - buffer[0] = 0xE8; // call picbase - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x58; // pop eax - buffer[6] = 0x8D; // lea foo$lazy_pointer-picbase(eax),eax - buffer[7] = 0x80; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; - buffer[12] = 0xFF; // jmp *(eax) - buffer[13] = 0x20; - buffer[14] = 0x50; // push eax - buffer[15] = 0xE9; // jump dyld_stub_binding_helper - buffer[16] = 0x00; - buffer[17] = 0x00; - buffer[18] = 0x00; - buffer[19] = 0x00; - } - else { - buffer[0] = 0xFF; // jmp *foo$lazy_pointer - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x68; // pushl $foo$lazy_pointer - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0xE9; // jump dyld_stub_binding_helper - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - } + if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; } else { if ( fWriter.fOptions.prebind() ) { @@ -1655,11 +2607,11 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const } } -// x86_64 stubs are 7 bytes and need no alignment +// x86_64 stubs are 6 bytes template <> ObjectFile::Alignment StubAtom::getAlignment() const { - return 0; + return 1; } template <> @@ -1683,13 +2635,10 @@ const char* StubAtom::getSectionName() const template <> const char* StubAtom::getSectionName() const { - if ( fWriter.fOptions.slowx86Stubs() || fForLazyDylib ) { - if ( pic() ) - return "__picsymbol_stub"; - else - return "__symbol_stub"; - } - return "__jump_table"; + if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) + return "__symbol_stub"; + else + return "__jump_table"; } @@ -1720,14 +2669,17 @@ struct ExternalRelocSorter template Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), - fAllAtoms(NULL), fStabs(NULL), fLoadCommandsSection(NULL), - fLoadCommandsSegment(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), - fSymbolTableCommands(NULL), fHeaderPadding(NULL), - fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), fDyldHelper(NULL), fDyldLazyDylibHelper(NULL), - fSectionRelocationsAtom(NULL), fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), + fAllAtoms(NULL), fStabs(NULL), fRegularDefAtomsThatOverrideADylibsWeakDef(NULL), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), fMachHeaderAtom(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), + fSymbolTableCommands(NULL), fHeaderPadding(NULL), fUnwindInfoAtom(NULL), + fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), + fDyldClassicHelperAtom(NULL), fDyldCompressedHelperAtom(NULL), fDyldLazyDylibHelper(NULL), + fSectionRelocationsAtom(NULL), fCompressedRebaseInfoAtom(NULL), fCompressedBindingInfoAtom(NULL), + fCompressedWeakBindingInfoAtom(NULL), fCompressedLazyBindingInfoAtom(NULL), fCompressedExportInfoAtom(NULL), + fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL), - fStringsAtom(NULL), fPageZeroAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), fSymbolTableStabsCount(0), - fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), + fStringsAtom(NULL), fPageZeroAtom(NULL), fFastStubGOTAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), + fSymbolTableStabsCount(0), fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), fLargestAtomSize(1), fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), @@ -1741,8 +2693,10 @@ Writer::Writer(const char* path, Options& options, std::vector(*this)); if ( fOptions.outputKind() == Options::kDynamicExecutable ) fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + if ( fOptions.makeCompressedDyldInfo() ) + fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); if ( fOptions.outputKind() == Options::kDynamicExecutable ) fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); @@ -1751,19 +2705,43 @@ Writer::Writer(const char* path, Options& options, std::vector(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + if ( fOptions.needsUnwindInfoSection() ) + fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + if ( fOptions.makeCompressedDyldInfo() ) { + fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom(*this)); + } + if ( fOptions.makeClassicDyldInfo() ) + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + if ( fOptions.makeClassicDyldInfo() ) + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; + case Options::kPreload: + fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); break; case Options::kDynamicLibrary: case Options::kDynamicBundle: fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - // fall through - case Options::kObjectFile: - fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + case Options::kKextBundle: + fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); if ( fOptions.outputKind() == Options::kDynamicLibrary ) { fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); @@ -1771,31 +2749,60 @@ Writer::Writer(const char* path, Options& options, std::vector(*this)); } fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + if ( fOptions.makeCompressedDyldInfo() ) + fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); if ( fOptions.sharedRegionEligible() ) fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + if ( fOptions.needsUnwindInfoSection() ) + fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + if ( fOptions.makeCompressedDyldInfo() ) { + fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom(*this)); + } + if ( fOptions.makeClassicDyldInfo() ) + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); if ( fOptions.sharedRegionEligible() ) { fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom(*this)); } fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + if ( fOptions.makeClassicDyldInfo() ) + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + if ( fOptions.outputKind() != Options::kKextBundle ) + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); if ( this->needsModuleTable() ) fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); break; + case Options::kObjectFile: + fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); + fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); + fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); + fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); + break; case Options::kDyld: fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this)); + fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + if ( fOptions.needsUnwindInfoSection() ) + fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); @@ -1835,6 +2842,10 @@ Writer::Writer(const char* path, Options& options, std::vector::Writer(const char* path, Options& options, std::vector::Writer(const char* path, Options& options, std::vector bool Writer::mightNeedPadSegment() { return false; } template ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) { - if ( fOptions.outputKind() == Options::kObjectFile ) { - // when doing -r -exported_symbols_list, don't creat proxy for a symbol + if ( fOptions.outputKind() == Options::kKextBundle ) { + return new UndefinedSymbolProxyAtom(*this, name); + } + else if ( fOptions.outputKind() == Options::kObjectFile ) { + // when doing -r -exported_symbols_list, don't create proxy for a symbol // that is supposed to be exported. We want an error instead // ld does not report error when -r is used and exported symbols are not defined. - if ( fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) + if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) return NULL; else return new UndefinedSymbolProxyAtom(*this, name); @@ -2001,6 +3019,59 @@ uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) throw "can't find ordinal for imported symbol"; } +template +bool Writer::targetRequiresWeakBinding(const ObjectFile::Atom& target) +{ + switch ( target.getDefinitionKind() ) { + case ObjectFile::Atom::kExternalWeakDefinition: + case ObjectFile::Atom::kWeakDefinition: + return true; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kTentativeDefinition: + break; + } + return false; +} + +template +int Writer::compressedOrdinalForImortedAtom(ObjectFile::Atom* target) +{ + // flat namespace images use zero for all ordinals + if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + // is an UndefinedSymbolProxyAtom + ObjectFile::Reader* lib = target->getFile(); + if ( lib == this ) + if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + std::map::iterator pos; + switch ( target->getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + pos = fLibraryToOrdinal.find(lib); + if ( pos != fLibraryToOrdinal.end() ) { + if ( pos->second == EXECUTABLE_ORDINAL ) + return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; + else + return pos->second; + } + break; + case ObjectFile::Atom::kWeakDefinition: + throw "compressedOrdinalForImortedAtom() should not have been called on a weak definition"; + case ObjectFile::Atom::kAbsoluteSymbol: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kTentativeDefinition: + return BIND_SPECIAL_DYLIB_SELF; + } + + throw "can't find ordinal for imported symbol"; +} + + template ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) { @@ -2011,20 +3082,27 @@ ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint template uint64_t Writer::write(std::vector& atoms, std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, class ObjectFile::Atom* dyldHelperAtom, + class ObjectFile::Atom* entryPointAtom, + class ObjectFile::Atom* dyldClassicHelperAtom, + class ObjectFile::Atom* dyldCompressedHelperAtom, class ObjectFile::Atom* dyldLazyDylibHelperAtom, bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, bool overridesDylibWeakDefines) + bool biggerThanTwoGigs, + std::set& atomsThatOverrideWeak, + bool hasExternalWeakDefinitions) { fAllAtoms = &atoms; fStabs = &stabs; fEntryPoint = entryPointAtom; - fDyldHelper = dyldHelperAtom; + fDyldClassicHelperAtom = dyldClassicHelperAtom; + fDyldCompressedHelperAtom = dyldCompressedHelperAtom; fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; fCanScatter = canScatter; fCpuConstraint = cpuConstraint; fBiggerThanTwoGigs = biggerThanTwoGigs; - fHasWeakExports = overridesDylibWeakDefines; // dyld needs to search this image as if it had weak exports + fHasWeakExports = hasExternalWeakDefinitions; // dyld needs to search this image as if it had weak exports + fRegularDefAtomsThatOverrideADylibsWeakDef = &atomsThatOverrideWeak; + try { // Set for create UUID @@ -2040,6 +3118,9 @@ uint64_t Writer::write(std::vector& atoms, // create inter-library stubs synthesizeStubs(); + // create table of unwind info + synthesizeUnwindInfoTable(); + // create SegmentInfo and SectionInfo objects and assign all atoms to a section partitionIntoSections(); @@ -2052,6 +3133,17 @@ uint64_t Writer::write(std::vector& atoms, // if need to add branch islands, reassign file offsets if ( addBranchIslands() ) assignFileOffsets(); + + // now that addresses are assigned, create unwind info + if ( fUnwindInfoAtom != NULL ) { + fUnwindInfoAtom->generate(); + // re-layout + adjustLoadCommandsAndPadding(); + assignFileOffsets(); + } + + // make spit-seg info now that all atoms exist + createSplitSegContent(); // build symbol table and relocations buildLinkEdit(); @@ -2087,20 +3179,30 @@ uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) // return info->getBaseAddress() + atom->getSectionOffset(); } - template <> -const char* Writer::symbolTableName(const ObjectFile::Atom* atom) +bool Writer::stringsNeedLabelsInObjects() { - static unsigned int counter = 0; - const char* name = atom->getName(); - if ( strncmp(name, "cstring=", 8) == 0 ) - asprintf((char**)&name, "LC%u", counter++); - return name; + return true; +} + +template +bool Writer::stringsNeedLabelsInObjects() +{ + return false; } template const char* Writer::symbolTableName(const ObjectFile::Atom* atom) { + static unsigned int counter = 0; + const char* name; + if ( stringsNeedLabelsInObjects() + && (atom->getContentType() == ObjectFile::Atom::kCStringType) + && (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) + asprintf((char**)&name, "LC%u", counter++); + else + name = atom->getName(); + return name; return atom->getName(); } @@ -2138,6 +3240,8 @@ void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent desc |= N_ARM_THUMB_DEF; if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) desc |= REFERENCED_DYNAMICALLY; + if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { desc |= N_WEAK_DEF; fHasWeakExports = true; @@ -2158,14 +3262,19 @@ void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent entry->set_n_strx(this->fStringsAtom->add(atom->getName())); // set n_type - if ( (fOptions.outputKind() == Options::kObjectFile) - && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) - entry->set_n_type(N_UNDF | N_EXT | N_PEXT); - else if ( fOptions.prebind() ) - entry->set_n_type(N_PBUD | N_EXT); - else - entry->set_n_type(N_UNDF | N_EXT); + if ( fOptions.outputKind() == Options::kObjectFile ) { + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) + && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) + entry->set_n_type(N_UNDF | N_EXT | N_PEXT); + else + entry->set_n_type(N_UNDF | N_EXT); + } + else { + if ( fOptions.prebind() ) + entry->set_n_type(N_PBUD | N_EXT); + else + entry->set_n_type(N_UNDF | N_EXT); + } // set n_sect entry->set_n_sect(0); @@ -2201,7 +3310,7 @@ void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent fReferencesWeakImports = true; } // set weak_import attribute - if ( fWeakImportMap[atom] ) + if ( fWeakImportMap[atom] ) desc |= N_WEAK_REF; entry->set_n_desc(desc); @@ -2217,8 +3326,14 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr const char* symbolName = this->symbolTableName(atom); char anonName[32]; if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { - sprintf(anonName, "l%u", fAnonNameIndex++); - symbolName = anonName; + if ( stringsNeedLabelsInObjects() && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { + // don't use 'l' labels for x86_64 strings + // x86_64 obj-c runtime confused when static lib is stripped + } + else { + sprintf(anonName, "l%u", fAnonNameIndex++); + symbolName = anonName; + } } entry->set_n_strx(this->fStringsAtom->add(symbolName)); @@ -2241,6 +3356,8 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr // set n_desc uint16_t desc = 0; + if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) desc |= N_WEAK_DEF; if ( atom->isThumb() ) @@ -2379,6 +3496,27 @@ void Writer::buildSymbolTable() // set up module table if ( fModuleInfoAtom != NULL ) fModuleInfoAtom->setName(); + + // create atom to symbol index map + // imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + fAtomToSymbolIndex[*it] = i + fSymbolTableImportStartIndex; + ++i; + } + // locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + fAtomToSymbolIndex[*it] = i + fSymbolTableLocalStartIndex; + ++i; + } + // exports + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + fAtomToSymbolIndex[*it] = i + fSymbolTableExportStartIndex; + ++i; + } + } @@ -2431,8 +3569,17 @@ void Writer::collectExportedAndImportedAndLocalAtoms() break; } // else fall into - case ObjectFile::Atom::kRegularDefinition: case ObjectFile::Atom::kWeakDefinition: + if ( stringsNeedLabelsInObjects() + && (fOptions.outputKind() == Options::kObjectFile) + && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn) + && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) + && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { + fLocalSymbolAtoms.push_back(atom); + break; + } + // else fall into + case ObjectFile::Atom::kRegularDefinition: case ObjectFile::Atom::kAbsoluteSymbol: if ( this->shouldExport(*atom) ) fExportedAtoms.push_back(atom); @@ -2579,30 +3726,9 @@ void Writer::addStabs(uint32_t startIndex) template uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) { - // search imports - int i = 0; - for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableImportStartIndex; - ++i; - } - - // search locals - i = 0; - for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableLocalStartIndex; - ++i; - } - - // search exports - i = 0; - for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableExportStartIndex; - ++i; - } - + std::map::iterator pos = fAtomToSymbolIndex.find(&atom); + if ( pos != fAtomToSymbolIndex.end() ) + return pos->second; throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); } @@ -2667,6 +3793,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref switch ( kind ) { case x86_64::kNoFixUp: + case x86_64::kGOTNoFixUp: case x86_64::kFollowOn: case x86_64::kGroupSubordinate: return 0; @@ -2682,6 +3809,16 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref fSectionRelocs.push_back(reloc1); return 1; + case x86_64::kPointer32: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolIndex); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + fSectionRelocs.push_back(reloc1); + return 1; + case x86_64::kPointerDiff32: case x86_64::kPointerDiff: { @@ -2790,6 +3927,15 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref fSectionRelocs.push_back(reloc1); return 1; + case x86_64::kPointerDiff24: + throw "internal linker error, kPointerDiff24 can't be encoded into object files"; + + case x86_64::kImageOffset32: + throw "internal linker error, kImageOffset32 can't be encoded into object files"; + + case x86_64::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; + case x86_64::kDtraceTypeReference: case x86_64::kDtraceProbe: // generates no relocs @@ -2865,12 +4011,16 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); sreloc1->set_r_address(address); sreloc1->set_r_value(target.getAddress()); + sreloc2->set_r_scattered(true); sreloc2->set_r_pcrel(false); sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); sreloc2->set_r_type(GENERIC_RELOC_PAIR); sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + if ( &ref->getFromTarget() == atom ) + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + else + sreloc2->set_r_value(ref->getFromTarget().getAddress()); fSectionRelocs.push_back(reloc2); fSectionRelocs.push_back(reloc1); return 2; @@ -2902,6 +4052,15 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere fSectionRelocs.push_back(reloc1); return 1; + case x86::kPointerDiff24: + throw "internal linker error, kPointerDiff24 can't be encoded into object files"; + + case x86::kImageOffset32: + throw "internal linker error, kImageOffset32 can't be encoded into object files"; + + case x86::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; + case x86::kDtraceTypeReference: case x86::kDtraceProbe: // generates no relocs @@ -2977,7 +4136,10 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere sreloc2->set_r_length(2); sreloc2->set_r_type(ARM_RELOC_PAIR); sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + if ( &ref->getFromTarget() == atom ) + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + else + sreloc2->set_r_value(ref->getFromTarget().getAddress()); fSectionRelocs.push_back(reloc2); fSectionRelocs.push_back(reloc1); return 2; @@ -3010,7 +4172,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere case arm::kThumbBranch22WeakImport: case arm::kThumbBranch22: if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero + // use scattered reloc if target offset is non-zero sreloc1->set_r_scattered(true); sreloc1->set_r_pcrel(true); sreloc1->set_r_length(2); @@ -3364,10 +4526,19 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: // must also change. // template -bool Writer::indirectSymbolIsLocal(const ObjectFile::Reference* ref) const +bool Writer::indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const { - // use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend - return ( !this->shouldExport(ref->getTarget()) || (ref->getTargetOffset() != 0) ); + // cannot use INDIRECT_SYMBOL_LOCAL to tentative definitions in object files + // because tentative defs don't have addresses + if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) + return false; + + // must use INDIRECT_SYMBOL_LOCAL if there is an addend + if ( ref->getTargetOffset() != 0 ) + return true; + + // don't use INDIRECT_SYMBOL_LOCAL for external symbols + return ! this->shouldExport(ref->getTarget()); } @@ -3393,7 +4564,7 @@ void Writer::buildObjectFileFixups() const int atomCount = sectionAtoms.size(); for (int k=0; k < atomCount; ++k) { ObjectFile::Atom* atom = sectionAtoms[k]; - //fprintf(stderr, "buildObjectFileFixups(): atom %s\n", atom->getDisplayName()); + //fprintf(stderr, "buildObjectFileFixups(): atom %s has %lu references\n", atom->getDisplayName(), atom->getReferences().size()); std::vector& refs = atom->getReferences(); const int refCount = refs.size(); for (int l=0; l < refCount; ++l) { @@ -3411,7 +4582,7 @@ void Writer::buildObjectFileFixups() } else if ( curSection->fAllNonLazyPointers) { // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend - if ( this->indirectSymbolIsLocal(ref) ) + if ( this->indirectSymbolInRelocatableIsLocal(ref) ) undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; else undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); @@ -3475,6 +4646,37 @@ void Writer::buildObjectFileFixups() } + +template <> +uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const +{ + uint64_t result; + if ( fOptions.outputKind() == Options::kKextBundle ) { + // for x86_64 kext bundles, the r_address field in relocs + // is the offset from the start address of the first segment + result = address - fSegmentInfos[0]->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + throwf("kext bundle too large: address can't fit in 31-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + } + else { + // for x86_64, the r_address field in relocs for final linked images + // is the offset from the start address of the first writable segment + result = address - fFirstWritableSegment->fBaseAddress; + if ( result > 0xFFFFFFFF ) { + if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) + throwf("text relocs not supported for x86_64 in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + else + throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", + atom->getDisplayName(), atom->getFile()->getPath()); + } + } + return result; +} + + template <> bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { @@ -3529,6 +4731,28 @@ bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& re template <> bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { + if ( fOptions.outputKind() == Options::kKextBundle ) { + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + // true means we need a TEXT relocs + switch ( ref.getKind() ) { + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + return true; + } + break; + } + } return false; } @@ -3575,6 +4799,9 @@ bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, cons reloc.set_r_type(GENERIC_RELOC_VANILLA); fInternalRelocs.push_back(reloc); atomSection->fHasTextLocalRelocs = true; + if ( fOptions.makeCompressedDyldInfo() ) { + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset())); + } return true; } return false; @@ -3732,6 +4959,67 @@ bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, c return false; } +template <> +bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) +{ + if ( fOptions.outputKind() == Options::kKextBundle ) { + macho_relocation_info

reloc; + switch ( ref.getTarget().getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + switch ( ref.getKind() ) { + case x86_64::kBranchPCRel32: + case x86_64::kBranchPCRel32WeakImport: + // a branch to something in another linkage unit is + // encoded as an external text reloc in a kext bundle + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); + reloc.set_r_pcrel(true); + reloc.set_r_length(2); + reloc.set_r_extern(true); + reloc.set_r_type(X86_64_RELOC_BRANCH); + fExternalRelocs.push_back(reloc); + atomSection->fHasTextExternalRelocs = true; + return true; + case x86_64::kPCRel32GOTLoad: + case x86_64::kPCRel32GOTLoadWeakImport: + // a load of the GOT entry for a symbol in another linkage unit is + // encoded as an external text reloc in a kext bundle + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); + reloc.set_r_pcrel(true); + reloc.set_r_length(2); + reloc.set_r_extern(true); + reloc.set_r_type(X86_64_RELOC_GOT_LOAD); + fExternalRelocs.push_back(reloc); + atomSection->fHasTextExternalRelocs = true; + return true; + case x86_64::kPCRel32GOT: + case x86_64::kPCRel32GOTWeakImport: + // a use of the GOT entry for a symbol in another linkage unit is + // encoded as an external text reloc in a kext bundle + reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); + reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); + reloc.set_r_pcrel(true); + reloc.set_r_length(2); + reloc.set_r_extern(true); + reloc.set_r_type(X86_64_RELOC_GOT); + fExternalRelocs.push_back(reloc); + atomSection->fHasTextExternalRelocs = true; + return true; + } + break; + } + } + return false; +} + + template bool Writer::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) { @@ -3792,6 +5080,10 @@ uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const Objec // for 32-bit architectures, the r_address field in relocs // for final linked images is the offset from the first segment uint64_t result = address - fSegmentInfos[0]->fBaseAddress; + if ( fOptions.outputKind() == Options::kPreload ) { + // kPreload uses a virtual __HEADER segment to cover the load commands + result = address - fSegmentInfos[1]->fBaseAddress; + } // or the offset from the first writable segment if built split-seg if ( fOptions.splitSeg() ) result = address - fFirstWritableSegment->fBaseAddress; @@ -3802,19 +5094,6 @@ uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const Objec return result; } -template <> -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for x86_64, the r_address field in relocs for final linked images - // is the offset from the start address of the first writable segment - uint64_t result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - template <> uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const { @@ -3856,7 +5135,8 @@ template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type template void Writer::buildExecutableFixups() { - fIndirectTableAtom->fTable.reserve(50); // minimize reallocations + if ( fIndirectTableAtom != NULL ) + fIndirectTableAtom->fTable.reserve(50); // minimize reallocations std::vector& segmentInfos = fSegmentInfos; const int segCount = segmentInfos.size(); for(int i=0; i < segCount; ++i) { @@ -3869,17 +5149,31 @@ void Writer::buildExecutableFixups() std::vector& sectionAtoms = curSection->fAtoms; if ( ! curSection->fAllZeroFill ) { if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers - || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) { + if ( fIndirectTableAtom != NULL ) + curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); + } const int atomCount = sectionAtoms.size(); for (int k=0; k < atomCount; ++k) { ObjectFile::Atom* atom = sectionAtoms[k]; std::vector& refs = atom->getReferences(); const int refCount = refs.size(); //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); + if ( curSection->fAllNonLazyPointers && (refCount == 0) ) { + // handle imageloadercache GOT slot + uint32_t offsetInSection = atom->getSectionOffset(); + uint32_t indexInSection = offsetInSection / sizeof(pint_t); + uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; + // use INDIRECT_SYMBOL_ABS so 10.5 dyld will leave value as zero + IndirectEntry entry = { indirectTableIndex, INDIRECT_SYMBOL_ABS }; + //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n", + // indirectTableIndex, INDIRECT_SYMBOL_LOCAL, curSection->fSectionName); + fIndirectTableAtom->fTable.push_back(entry); + } for (int l=0; l < refCount; ++l) { ObjectFile::Reference* ref = refs[l]; - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { + if ( (fOptions.outputKind() != Options::kKextBundle) && + (curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers) ) { // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol if ( atom->getSize() != sizeof(pint_t) ) { warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath()); @@ -3891,16 +5185,18 @@ void Writer::buildExecutableFixups() uint32_t offsetInSection = atom->getSectionOffset(); uint32_t indexInSection = offsetInSection / sizeof(pint_t); uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) + if (atom == fFastStubGOTAtom) + undefinedSymbolIndex = INDIRECT_SYMBOL_ABS; + else if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) undefinedSymbolIndex = this->symbolIndex(*pointerTarget); uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X), pointerTarget=%s\n", - // indirectTableIndex, undefinedSymbolIndex, pointerTarget->getDisplayName()); + //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n", + // indirectTableIndex, undefinedSymbolIndex, curSection->fSectionName); fIndirectTableAtom->fTable.push_back(entry); if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { uint8_t preboundLazyType; - if ( fOptions.prebind() && (fDyldHelper != NULL) + if ( fOptions.prebind() && (fDyldClassicHelperAtom != NULL) && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) { // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid macho_scattered_relocation_info

pblaReloc; @@ -3909,15 +5205,15 @@ void Writer::buildExecutableFixups() pblaReloc.set_r_length(); pblaReloc.set_r_type(preboundLazyType); pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - pblaReloc.set_r_value(fDyldHelper->getAddress()); + pblaReloc.set_r_value(fDyldClassicHelperAtom->getAddress()); fInternalRelocs.push_back(*((macho_relocation_info

*)&pblaReloc)); } else if ( fSlideable ) { // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides macho_relocation_info

dyldHelperReloc; uint32_t sectionNum = 1; - if ( fDyldHelper != NULL ) - sectionNum = ((SectionInfo*)(fDyldHelper->getSection()))->getIndex(); + if ( fDyldClassicHelperAtom != NULL ) + sectionNum = ((SectionInfo*)(fDyldClassicHelperAtom->getSection()))->getIndex(); //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); dyldHelperReloc.set_r_symbolnum(sectionNum); @@ -3926,13 +5222,82 @@ void Writer::buildExecutableFixups() dyldHelperReloc.set_r_extern(false); dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA); fInternalRelocs.push_back(dyldHelperReloc); + if ( fOptions.makeCompressedDyldInfo() ) { + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); + } + } + if ( fOptions.makeCompressedDyldInfo() ) { + uint8_t type = BIND_TYPE_POINTER; + uint64_t addresss = atom->getAddress() + ref->getFixUpOffset(); + if ( pointerTarget->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { + // This is a referece to a weak def in some dylib (e.g. operator new) + // need to bind into to directly bind this + // later weak binding info may override + int ordinal = compressedOrdinalForImortedAtom(pointerTarget); + fBindingInfo.push_back(BindingInfo(type, ordinal, pointerTarget->getName(), false, addresss, 0)); + } + if ( targetRequiresWeakBinding(*pointerTarget) ) { + // note: lazy pointers to weak symbols are not bound lazily + fWeakBindingInfo.push_back(BindingInfo(type, pointerTarget->getName(), false, addresss, 0)); + } + } + } + if ( curSection->fAllNonLazyPointers && fOptions.makeCompressedDyldInfo() ) { + if ( pointerTarget != NULL ) { + switch ( this->relocationNeededInFinalLinkedImage(*pointerTarget) ) { + case kRelocNone: + // no rebase or binding info needed + break; + case kRelocInternal: + // a non-lazy pointer that has been optimized to LOCAL needs rebasing info + // but not the magic fFastStubGOTAtom atom + if (atom != fFastStubGOTAtom) + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); + break; + case kRelocExternal: + { + uint8_t type = BIND_TYPE_POINTER; + uint64_t addresss = atom->getAddress(); + if ( targetRequiresWeakBinding(ref->getTarget()) ) { + fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, 0)); + // if this is a non-lazy pointer to a weak definition within this linkage unit + // the pointer needs to initially point within linkage unit and have + // rebase command to slide it. + if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + // unless if this is a hybrid format, in which case the non-lazy pointer + // is zero on disk. So use a bind instead of a rebase to set initial value + if ( fOptions.makeClassicDyldInfo() ) + fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, 0)); + else + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); + } + // if this is a non-lazy pointer to a weak definition in a dylib, + // the pointer needs to initially bind to the dylib + else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { + int ordinal = compressedOrdinalForImortedAtom(pointerTarget); + fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, pointerTarget->getName(), false, addresss, 0)); + } + } + else { + int ordinal = compressedOrdinalForImortedAtom(pointerTarget); + bool weak_import = fWeakImportMap[pointerTarget]; + fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, 0)); + } + } + } } } } else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) { if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { - throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", + if ( fOptions.allowTextRelocs() ) { + if ( fOptions.warnAboutTextRelocs() ) + warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); + } + else { + throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); + } } switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { case kRelocNone: @@ -3953,6 +5318,9 @@ void Writer::buildExecutableFixups() internalReloc.set_r_extern(false); internalReloc.set_r_type(GENERIC_RELOC_VANILLA); fInternalRelocs.push_back(internalReloc); + if ( fOptions.makeCompressedDyldInfo() ) { + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, atom->getAddress() + ref->getFixUpOffset())); + } } break; case kRelocExternal: @@ -3965,12 +5333,47 @@ void Writer::buildExecutableFixups() externalReloc.set_r_extern(true); externalReloc.set_r_type(GENERIC_RELOC_VANILLA); fExternalRelocs.push_back(externalReloc); + if ( fOptions.makeCompressedDyldInfo() ) { + int64_t addend = ref->getTargetOffset(); + uint64_t addresss = atom->getAddress() + ref->getFixUpOffset(); + if ( !fOptions.makeClassicDyldInfo() ) { + if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + // pointers to internal weak defs need a rebase + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, addresss)); + } + } + uint8_t type = BIND_TYPE_POINTER; + if ( targetRequiresWeakBinding(ref->getTarget()) ) { + fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, addend)); + if ( fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // hybrid linkedit puts addend in data, so we need bind phase to reset pointer to local definifion + fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, addend)); + } + // if this is a pointer to a weak definition in a dylib, + // the pointer needs to initially bind to the dylib + else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { + int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); + fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, ref->getTarget().getName(), false, addresss, addend)); + } + } + else { + int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); + bool weak_import = fWeakImportMap[&(ref->getTarget())]; + fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, addend)); + } + } } break; } } else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { - if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { + // new x86 stubs always require text relocs + if ( curSection->fAllStubs || curSection->fAllStubHelpers ) { + if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { + // relocs added to fInternalRelocs + } + } + else if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { if ( fOptions.warnAboutTextRelocs() ) warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { @@ -4005,6 +5408,16 @@ void Writer::buildExecutableFixups() } if ( fSplitCodeToDataContentAtom != NULL ) fSplitCodeToDataContentAtom->encode(); + if ( fCompressedRebaseInfoAtom != NULL ) + fCompressedRebaseInfoAtom->encode(); + if ( fCompressedBindingInfoAtom != NULL ) + fCompressedBindingInfoAtom->encode(); + if ( fCompressedWeakBindingInfoAtom != NULL ) + fCompressedWeakBindingInfoAtom->encode(); + if ( fCompressedLazyBindingInfoAtom != NULL ) + fCompressedLazyBindingInfoAtom->encode(); + if ( fCompressedExportInfoAtom != NULL ) + fCompressedExportInfoAtom->encode(); } @@ -4067,6 +5480,7 @@ void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectF { switch ( (x86::ReferenceKinds)ref->getKind() ) { case x86::kPointerDiff: + case x86::kImageOffset32: if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 ) fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); else @@ -4087,8 +5501,13 @@ void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectF } // fall into warning case default: - warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); - fSplitCodeToDataContentAtom->setCantEncode(); + if ( fOptions.makeCompressedDyldInfo() && (ref->getKind() == x86::kAbsolute32) ) { + // will be encoded in rebase info + } + else { + warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); + fSplitCodeToDataContentAtom->setCantEncode(); + } } } @@ -4105,6 +5524,7 @@ void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const Obje case x86_64::kPCRel32GOT: case x86_64::kPCRel32GOTWeakImport: case x86_64::kPointerDiff32: + case x86_64::kImageOffset32: fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); break; case x86_64::kPointerDiff: @@ -4113,6 +5533,7 @@ void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const Obje case x86_64::kNoFixUp: case x86_64::kGroupSubordinate: case x86_64::kPointer: + case x86_64::kGOTNoFixUp: // ignore break; default: @@ -4422,16 +5843,14 @@ uint64_t Writer::writeAtoms() try { for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { SegmentInfo* curSegment = *segit; - bool isTextSeg = (strcmp(curSegment->fName, "__TEXT") == 0); std::vector& sectionInfos = curSegment->fSections; for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { SectionInfo* curSection = *secit; std::vector& sectionAtoms = curSection->fAtoms; //printf("writing with max atom size 0x%X\n", fLargestAtomSize); - //fprintf(stderr, "writing %lu atoms for section %s\n", sectionAtoms.size(), curSection->fSectionName); + //fprintf(stderr, "writing %lu atoms for section %p %s at file offset 0x%08llX\n", sectionAtoms.size(), curSection, curSection->fSectionName, curSection->fFileOffset); if ( ! curSection->fAllZeroFill ) { - end = curSection->fFileOffset; - bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0); + bool needsNops = ((strcmp(curSection->fSegmentName, "__TEXT") == 0) && (strncmp(curSection->fSectionName, "__text", 6) == 0)); for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { ObjectFile::Atom* atom = *ait; if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) @@ -4439,6 +5858,7 @@ uint64_t Writer::writeAtoms() && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) { uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset(); if ( fileOffset != end ) { + //fprintf(stderr, "writing %d pad bytes, needsNops=%d\n", fileOffset-end, needsNops); if ( needsNops ) { // fill gaps with no-ops if ( streaming ) @@ -4494,8 +5914,8 @@ uint64_t Writer::writeAtoms() catch (const char* msg) { throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); } - //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %s from %s\n", - // fileOffset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); + //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %p %s from %s\n", + // fileOffset, end, atom->getAddress(), atom->getSize(), atom, atom->getDisplayName(), atom->getFile()->getPath()); if ( streaming ) { // write out ::pwrite(fd, buffer, atomSize, fileOffset); @@ -4592,7 +6012,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob uint64_t targetAddr = 0; uint32_t firstDisp; uint32_t nextDisp; - uint32_t opcode; + uint32_t opcode = 0; bool relocateableExternal = false; bool is_bl; bool is_blx; @@ -4670,12 +6090,14 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); break; case arm::kReadOnlyPointer: + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) + targetAddr |= 1; switch ( ref->getTarget().getDefinitionKind() ) { case ObjectFile::Atom::kRegularDefinition: case ObjectFile::Atom::kWeakDefinition: case ObjectFile::Atom::kTentativeDefinition: // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + LittleEndian::set32(*fixUp, targetAddr); break; case ObjectFile::Atom::kExternalDefinition: case ObjectFile::Atom::kExternalWeakDefinition: @@ -4684,7 +6106,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; case ObjectFile::Atom::kAbsoluteSymbol: // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); + LittleEndian::set32(*fixUp, targetAddr); break; } break; @@ -4728,8 +6150,8 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob case arm::kThumbBranch22WeakImport: case arm::kThumbBranch22: instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xF8000000) == 0xF8000000); - is_blx = ((instruction & 0xF8000000) == 0xE8000000); + is_bl = ((instruction & 0xD000F800) == 0xD000F000); + is_blx = ((instruction & 0xD000F800) == 0xC000F000); targetIsThumb = ref->getTarget().isThumb(); // The pc added will be +4 from the pc @@ -4747,16 +6169,62 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob // max positive displacement is 0x003FFFFE // max negative displacement is 0xFFC00000 if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", + // armv7 supports a larger displacement + if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { + throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + else { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + if ( is_bl ) { + if ( targetIsThumb ) + opcode = 0xD000F000; // keep bl + else + opcode = 0xC000F000; // change to blx + } + else if ( is_blx ) { + if ( targetIsThumb ) + opcode = 0xD000F000; // change to bl + else + opcode = 0xC000F000; // keep blx + } + else if ( !is_bl && !is_blx && !targetIsThumb ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, ref->getTarget().getDisplayName()); + } + nextDisp = (j1 << 13) | (j2 << 11) | imm11; + firstDisp = (s << 10) | imm10; + newInstruction = opcode | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + LittleEndian::set32(*fixUp, newInstruction); + } + } + else { + throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the first - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the next - // 11 bits of the displacement, as well as differentiating bl and blx. - { + else { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; if ( is_bl && !targetIsThumb ) { @@ -4777,7 +6245,6 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob } break; case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: if ( inAtom->isThumb() ) { // change 32-bit blx call site to two thumb NOPs LittleEndian::set32(*fixUp, 0x46C046C0); @@ -4787,6 +6254,16 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob LittleEndian::set32(*fixUp, 0xE1A00000); } break; + case arm::kDtraceIsEnabledSite: + if ( inAtom->isThumb() ) { + // change 32-bit blx call site to 'nop', 'eor r0, r0' + LittleEndian::set32(*fixUp, 0x46C04040); + } + else { + // change call site to 'eor r0, r0, r0' + LittleEndian::set32(*fixUp, 0xE0200000); + } + break; case arm::kDtraceTypeReference: case arm::kDtraceProbe: // nothing to fix up @@ -4806,7 +6283,7 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co int64_t baseAddr; uint32_t firstDisp; uint32_t nextDisp; - uint32_t opcode; + uint32_t opcode = 0; bool relocateableExternal = false; bool is_bl; bool is_blx; @@ -4830,7 +6307,7 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co { if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content - if ( this->indirectSymbolIsLocal(ref) ) + if ( this->indirectSymbolInRelocatableIsLocal(ref) ) LittleEndian::set32(*fixUp, targetAddr); else LittleEndian::set32(*fixUp, 0); @@ -4944,10 +6421,54 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target displacement -= ref->getTarget().getAddress(); } - else { - // max positive displacement is 0x003FFFFE - // max negative displacement is 0xFFC00000 - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + // armv7 supports a larger displacement + if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { + throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s", + displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + } + else { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + if ( is_bl ) { + if ( targetIsThumb ) + opcode = 0xD000F000; // keep bl + else + opcode = 0xC000F000; // change to blx + } + else if ( is_blx ) { + if ( targetIsThumb ) + opcode = 0xD000F000; // change to bl + else + opcode = 0xC000F000; // keep blx + } + else if ( !is_bl && !is_blx && !targetIsThumb ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, ref->getTarget().getDisplayName()); + } + nextDisp = (j1 << 13) | (j2 << 11) | imm11; + firstDisp = (s << 10) | imm10; + newInstruction = opcode | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + LittleEndian::set32(*fixUp, newInstruction); + break; + } + } + else { throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); @@ -4989,9 +6510,11 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; uint8_t* dtraceProbeSite; const int64_t kTwoGigLimit = 0x7FFFFFFF; + const int64_t kSixteenMegLimit = 0x00FFFFFF; const int64_t kSixtyFourKiloLimit = 0x7FFF; const int64_t kOneTwentyEightLimit = 0x7F; int64_t displacement; + uint32_t temp; x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); switch ( kind ) { case x86::kNoFixUp: @@ -5020,7 +6543,12 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; } } - else { + else if ( !fOptions.makeClassicDyldInfo() + && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // when using only compressed dyld info, pointer is initially set to point directly to weak definition + LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + else { // external relocation ==> pointer contains addend LittleEndian::set32(*fixUp, ref->getTargetOffset()); } @@ -5042,6 +6570,24 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); break; + case x86::kPointerDiff24: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) + throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); + temp = LittleEndian::get32(*fixUp); + temp &= 0xFF000000; + temp |= (displacement & 0x00FFFFFF); + LittleEndian::set32(*fixUp, temp); + break; + case x86::kSectionOffset24: + displacement = ref->getTarget().getSectionOffset(); + if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) + throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); + temp = LittleEndian::get32(*fixUp); + temp &= 0xFF000000; + temp |= (displacement & 0x00FFFFFF); + LittleEndian::set32(*fixUp, temp); + break; case x86::kDtraceProbeSite: // change call site to a NOP dtraceProbeSite = (uint8_t*)fixUp; @@ -5081,6 +6627,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; } if ( kind == x86::kPCRel8 ) { + displacement += 3; if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); throwf("rel8 out of range in %s", inAtom->getDisplayName()); @@ -5088,6 +6635,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob *(int8_t*)fixUp = (int8_t)displacement; } else if ( kind == x86::kPCRel16 ) { + displacement += 2; if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); throwf("rel16 out of range in %s", inAtom->getDisplayName()); @@ -5121,6 +6669,11 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; } break; + case x86::kImageOffset32: + // offset of target atom from mach_header + displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress(); + LittleEndian::set32(*fixUp, (int32_t)displacement); + break; case x86::kDtraceTypeReference: case x86::kDtraceProbe: // nothing to fix up @@ -5150,17 +6703,17 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co case x86::kPointerWeakImport: case x86::kAbsolute32: { - if ( isExtern ) { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - else if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { + if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolIsLocal(ref) ) + if ( this->indirectSymbolInRelocatableIsLocal(ref) ) LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); else LittleEndian::set32(*fixUp, 0); } + else if ( isExtern ) { + // external relocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { // internal relocation => pointer contains target address LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); @@ -5220,6 +6773,12 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co } } break; + case x86::kPointerDiff24: + throw "internal linker error, kPointerDiff24 can't be encoded into object files"; + case x86::kImageOffset32: + throw "internal linker error, kImageOffset32 can't be encoded into object files"; + case x86::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; case x86::kDtraceProbe: case x86::kDtraceTypeReference: // nothing to fix up @@ -5230,42 +6789,105 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co template <> void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const { - const int64_t twoGigLimit = 0x7FFFFFFF; + const int64_t twoGigLimit = 0x7FFFFFFF; + const int64_t kSixteenMegLimit = 0x00FFFFFF; uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; uint8_t* dtraceProbeSite; int64_t displacement = 0; + uint32_t temp; switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { case x86_64::kNoFixUp: + case x86_64::kGOTNoFixUp: case x86_64::kFollowOn: case x86_64::kGroupSubordinate: // do nothing break; case x86_64::kPointerWeakImport: case x86_64::kPointer: + { + if ( &ref->getTarget() != NULL ) { + //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); + if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal) { + if ( !fOptions.makeClassicDyldInfo() + && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // when using only compressed dyld info, pointer is initially set to point directly to weak definition + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + else { + // external relocation ==> pointer contains addend + LittleEndian::set64(*fixUp, ref->getTargetOffset()); + } + } + else { + // internal relocation + // pointer contains target address + //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); + LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + } + break; + case x86_64::kPointer32: { //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); + // external relocation + throwf("32-bit pointer to dylib or weak symbol %s not supported for x86_64",ref->getTarget().getDisplayName()); } else { // internal relocation // pointer contains target address //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); + displacement = ref->getTarget().getAddress() + ref->getTargetOffset(); + switch ( fOptions.outputKind() ) { + case Options::kObjectFile: + case Options::kPreload: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kKextBundle: + throwf("32-bit pointer to symbol %s not supported for x86_64",ref->getTarget().getDisplayName()); + case Options::kDynamicExecutable: + // allow x86_64 main executables to use 32-bit pointers if program loads in load 2GB + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) + throw "32-bit pointer out of range"; + break; + case Options::kStaticExecutable: + // allow x86_64 mach_kernel to truncate pointers + break; + } + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); } } break; case x86_64::kPointerDiff32: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) - throw "32-bit pointer difference out of range"; - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); - break; + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) + throw "32-bit pointer difference out of range"; + LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); + break; case x86_64::kPointerDiff: - LittleEndian::set64(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; + LittleEndian::set64(*fixUp, + (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + break; + case x86_64::kPointerDiff24: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) + throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); + temp = LittleEndian::get32(*((uint32_t*)fixUp)); + temp &= 0xFF000000; + temp |= (displacement & 0x00FFFFFF); + LittleEndian::set32(*((uint32_t*)fixUp), temp); + break; + case x86_64::kSectionOffset24: + displacement = ref->getTarget().getSectionOffset(); + if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) + throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); + temp = LittleEndian::get32(*((uint32_t*)fixUp)); + temp &= 0xFF000000; + temp |= (displacement & 0x00FFFFFF); + LittleEndian::set32(*((uint32_t*)fixUp), temp); + break; case x86_64::kPCRel32GOTLoad: case x86_64::kPCRel32GOTLoadWeakImport: // if GOT entry was optimized away, change movq instruction to a leaq @@ -5297,7 +6919,10 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const break; case ObjectFile::Atom::kExternalDefinition: case ObjectFile::Atom::kExternalWeakDefinition: - throw "codegen problem, can't use rel32 to external symbol"; + if ( fOptions.outputKind() == Options::kKextBundle ) + displacement = 0; + else + throwf("codegen problem, can't use rel32 to external symbol %s", ref->getTarget().getDisplayName()); break; } switch ( ref->getKind() ) { @@ -5331,6 +6956,11 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); } break; + case x86_64::kImageOffset32: + // offset of target atom from mach_header + displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress(); + LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); + break; case x86_64::kDtraceProbeSite: // change call site to a NOP dtraceProbeSite = (uint8_t*)fixUp; @@ -5366,6 +6996,7 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, int32_t temp32; switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { case x86_64::kNoFixUp: + case x86_64::kGOTNoFixUp: case x86_64::kFollowOn: case x86_64::kGroupSubordinate: // do nothing @@ -5383,13 +7014,33 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, } } break; + case x86_64::kPointer32: + { + if ( external ) { + // external relocation ==> pointer contains addend + LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset()); + } + else { + // internal relocation ==> pointer contains target address + LittleEndian::set32(*((uint32_t*)fixUp), ref->getTarget().getAddress() + ref->getTargetOffset()); + } + } + break; case x86_64::kPointerDiff32: - // addend in content - LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset() - ref->getFromTargetOffset() ); + displacement = ref->getTargetOffset() - ref->getFromTargetOffset(); + if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + displacement += ref->getTarget().getAddress(); + if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + displacement -= ref->getFromTarget().getAddress(); + LittleEndian::set32(*((uint32_t*)fixUp), displacement); break; case x86_64::kPointerDiff: - // addend in content - LittleEndian::set64(*fixUp, ref->getTargetOffset() - ref->getFromTargetOffset() ); + displacement = ref->getTargetOffset() - ref->getFromTargetOffset(); + if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + displacement += ref->getTarget().getAddress(); + if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + displacement -= ref->getFromTarget().getAddress(); + LittleEndian::set64(*fixUp, displacement); break; case x86_64::kBranchPCRel32: case x86_64::kBranchPCRel32WeakImport: @@ -5450,6 +7101,12 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, // contains addend (usually zero) LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); break; + case x86_64::kPointerDiff24: + throw "internal linker error, kPointerDiff24 can't be encoded into object files"; + case x86_64::kImageOffset32: + throw "internal linker error, kImageOffset32 can't be encoded into object files"; + case x86_64::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; case x86_64::kDtraceTypeReference: case x86_64::kDtraceProbe: // nothing to fix up @@ -5536,7 +7193,7 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O } else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolIsLocal(ref) ) + if ( this->indirectSymbolInRelocatableIsLocal(ref) ) P::setP(*fixUpPointer, targetAddr); else P::setP(*fixUpPointer, 0); @@ -5966,6 +7623,7 @@ bool Writer::GOTReferenceKind(uint8_t kind) case x86_64::kPCRel32GOTWeakImport: case x86_64::kPCRel32GOTLoad: case x86_64::kPCRel32GOTLoadWeakImport: + case x86_64::kGOTNoFixUp: return true; } return false; @@ -6035,7 +7693,7 @@ void Writer::optimizeDylibReferences() // already noticed that this reader has same install name as another reader readerAliases[reader] = aliasPos->second; } - else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || fOptions.deadStripDylibs()) ) { + else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || reader->deadStrippable() || fOptions.deadStripDylibs()) ) { // this reader can be optimized away it->second = 0xFFFFFFFF; typename std::map* >::iterator pos = fLibraryToLoadCommand.find(reader); @@ -6158,7 +7816,7 @@ void Writer::scanForAbsoluteReferences() case ppc64::kAbsHigh16AddLow: //fprintf(stderr, "found -mdynamic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); // shrink page-zero and add pad segment to compensate - fPadSegmentInfo = new SegmentInfo(); + fPadSegmentInfo = new SegmentInfo(4096); strcpy(fPadSegmentInfo->fName, "__4GBFILL"); fPageZeroAtom->setSize(0x1000); return; @@ -6168,7 +7826,7 @@ void Writer::scanForAbsoluteReferences() } } - + template void Writer::insertDummyStubs() { @@ -6196,13 +7854,94 @@ void Writer::insertDummyStubs() fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end()); } + +template +void Writer::synthesizeKextGOT() +{ + // walk every atom and reference + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + switch ( ref->getTargetBinding()) { + case ObjectFile::Reference::kUnboundByName: + case ObjectFile::Reference::kDontBind: + break; + case ObjectFile::Reference::kBoundByName: + case ObjectFile::Reference::kBoundDirectly: + ObjectFile::Atom& target = ref->getTarget(); + // create GOT slots (non-lazy pointers) as needed + if ( this->GOTReferenceKind(ref->getKind()) ) { + bool useGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); + // if this GOT usage cannot be optimized away then make a GOT enry + if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) + useGOT = true; + if ( useGOT ) { + ObjectFile::Atom* nlp = NULL; + std::map::iterator pos = fGOTMap.find(&target); + if ( pos == fGOTMap.end() ) { + nlp = new NonLazyPointerAtom(*this, target); + fGOTMap[&target] = nlp; + } + else { + nlp = pos->second; + } + // alter reference to use non lazy pointer instead + ref->setTarget(*nlp, ref->getTargetOffset()); + } + } + // build map of which symbols need weak importing + if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + if ( this->weakImportReferenceKind(ref->getKind()) ) { + fWeakImportMap[&target] = true; + } + } + break; + } + } + } + + // add non-lazy pointers to fAllAtoms + if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { + ObjectFile::Section* curSection = NULL; + ObjectFile::Atom* prevAtom = NULL; + bool inserted = false; + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__data") == 0) ) { + // found end of __data section, insert lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + if ( !inserted ) { + throw "can't insert non-lazy pointers, __data section not found"; + } + } + +} + + template void Writer::synthesizeStubs() { switch ( fOptions.outputKind() ) { case Options::kObjectFile: + case Options::kPreload: // these output kinds never have stubs return; + case Options::kKextBundle: + // new kext need a synthesized GOT only + synthesizeKextGOT(); + return; case Options::kStaticExecutable: case Options::kDyld: case Options::kDynamicLibrary: @@ -6240,6 +7979,12 @@ void Writer::synthesizeStubs() weakImport = true; } } + // -weak_library no longer forces uses to be weak_import + if ( fForcedWeakImportReaders.count(target.getFile()) != 0 ) { + fWeakImportMap[&target] = true; + weakImport = true; + } + std::map::iterator pos = fWeakImportMap.find(&target); if ( pos == fWeakImportMap.end() ) { // target not in fWeakImportMap, so add @@ -6287,6 +8032,12 @@ void Writer::synthesizeStubs() forLazyDylib = true; break; } + // just-in-time, create GOT slot to dyld_stub_binder + if ( fOptions.makeCompressedDyldInfo() && (fFastStubGOTAtom == NULL) ) { + if ( fDyldCompressedHelperAtom == NULL ) + throw "missing symbol dyld_stub_binder"; + fFastStubGOTAtom = new NonLazyPointerAtom(*this, *fDyldCompressedHelperAtom); + } stub = new StubAtom(*this, target, forLazyDylib); fStubsMap[&target] = stub; } @@ -6318,7 +8069,7 @@ void Writer::synthesizeStubs() // if this GOT usage cannot be optimized away then make a GOT enry if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) useGOT = true; - if ( useGOT ) { + if ( useGOT ) { ObjectFile::Atom* nlp = NULL; std::map::iterator pos = fGOTMap.find(&target); if ( pos == fGOTMap.end() ) { @@ -6338,9 +8089,10 @@ void Writer::synthesizeStubs() // sort stubs std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); + std::sort(fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end(), AtomByNameSorter()); - // add dummy fast stubs (x86 only) - if ( !fOptions.slowx86Stubs() ) + // add dummy self-modifying stubs (x86 only) + if ( ! fOptions.makeCompressedDyldInfo() ) this->insertDummyStubs(); // sort lazy pointers @@ -6399,18 +8151,19 @@ void Writer::synthesizeStubs() } - // add lazy dylib pointers to fAllAtoms - if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) { + // add non-lazy pointers to fAllAtoms + if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { ObjectFile::Section* curSection = NULL; ObjectFile::Atom* prevAtom = NULL; bool inserted = false; + // first try to insert at end of __nl_symbol_ptr for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { ObjectFile::Atom* atom = *it; ObjectFile::Section* nextSection = atom->getSection(); if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { - // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) { + // found end of __nl_symbol_ptr section, insert non-lazy pointers at end of it + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); inserted = true; break; } @@ -6419,12 +8172,49 @@ void Writer::synthesizeStubs() prevAtom = atom; } if ( !inserted ) { - throw "can't insert lazy pointers, __dyld section not found"; + // next try to insert after __dyld section + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( strcmp(atom->getSegment().getName(), "__DATA") == 0 ) { + const char* prevSectionName = (prevAtom != NULL) ? prevAtom->getSectionName() : ""; + if ( (strcmp(prevSectionName, "__dyld") != 0) + && (strcmp(prevSectionName, "__program_vars") != 0) + && (strcmp(prevSectionName, "__mod_init_func") != 0) ) { + // found end of __dyld section, insert non-lazy pointers here + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + inserted = true; + break; + } + } + } + prevAtom = atom; + } + if ( !inserted ) { + // might not be any __DATA sections, insert after end of __TEXT + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + ObjectFile::Section* nextSection = atom->getSection(); + if ( nextSection != curSection ) { + if ( (prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__TEXT") == 0) && (strcmp(atom->getSegment().getName(), "__TEXT") != 0)) { + // found end of __TEXT segment, insert non-lazy pointers at end of it + fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + inserted = true; + break; + } + curSection = nextSection; + } + prevAtom = atom; + } + } + if ( !inserted ) + throw "can't insert non-lazy pointers, __dyld section not found"; } } - // add lazy pointers to fAllAtoms - if ( fAllSynthesizedLazyPointers.size() != 0 ) { + // add lazy dylib pointers to fAllAtoms + if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) { ObjectFile::Section* curSection = NULL; ObjectFile::Atom* prevAtom = NULL; bool inserted = false; @@ -6432,9 +8222,12 @@ void Writer::synthesizeStubs() ObjectFile::Atom* atom = *it; ObjectFile::Section* nextSection = atom->getSection(); if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__dyld") == 0) ) { + if ( (prevAtom != NULL) && + ( (strcmp(prevAtom->getSectionName(), "__dyld") == 0) + || (strcmp(prevAtom->getSectionName(), "__program_vars") == 0) + || (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) ) { // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); + fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); inserted = true; break; } @@ -6446,9 +8239,9 @@ void Writer::synthesizeStubs() throw "can't insert lazy pointers, __dyld section not found"; } } - - // add non-lazy pointers to fAllAtoms - if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { + + // add lazy pointers to fAllAtoms + if ( fAllSynthesizedLazyPointers.size() != 0 ) { ObjectFile::Section* curSection = NULL; ObjectFile::Atom* prevAtom = NULL; bool inserted = false; @@ -6456,12 +8249,12 @@ void Writer::synthesizeStubs() ObjectFile::Atom* atom = *it; ObjectFile::Section* nextSection = atom->getSection(); if ( nextSection != curSection ) { - if ( (prevAtom != NULL) - && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0) - || ((strcmp(prevAtom->getSectionName(), "__data") == 0) && - ((fOptions.outputKind() == Options::kDyld) || (fOptions.outputKind() == Options::kStaticExecutable))) ) ) { + if ( (prevAtom != NULL) && + ( (strcmp(prevAtom->getSectionName(), "__dyld") == 0) + || (strcmp(prevAtom->getSectionName(), "__program_vars") == 0) + || (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) ) { // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); + fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); inserted = true; break; } @@ -6470,11 +8263,17 @@ void Writer::synthesizeStubs() prevAtom = atom; } if ( !inserted ) { - throw "can't insert non-lazy pointers, __dyld section not found"; + throw "can't insert lazy pointers, __dyld section not found"; } } - // build LC_SEGMENT_SPLIT_INFO content now that all atoms exist + +} + +template +void Writer::createSplitSegContent() +{ + // build LC_SEGMENT_SPLIT_INFO once all atoms exist if ( fSplitCodeToDataContentAtom != NULL ) { for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { ObjectFile::Atom* atom = *it; @@ -6494,11 +8293,37 @@ void Writer::synthesizeStubs() } } } + // bad codegen may cause LC_SEGMENT_SPLIT_INFO to be removed + adjustLoadCommandsAndPadding(); } } +template +void Writer::synthesizeUnwindInfoTable() +{ + if ( fUnwindInfoAtom != NULL ) { + // walk every atom and gets its unwind info + for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + ObjectFile::Atom* atom = *it; + if ( atom->beginUnwind() == atom->endUnwind() ) { + // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info + if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) + fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL, NULL); + } + else { + // atom has unwind + for ( ObjectFile::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { + fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getFDE(), atom->getLSDA(), atom->getPersonalityPointer()); + } + } + } + } +} + + + template void Writer::partitionIntoSections() { @@ -6506,7 +8331,7 @@ void Writer::partitionIntoSections() // for every atom, set its sectionInfo object and section offset // build up fSegmentInfos along the way - ObjectFile::Section* curSection = NULL; + ObjectFile::Section* curSection = (ObjectFile::Section*)(-1); SectionInfo* currentSectionInfo = NULL; SegmentInfo* currentSegmentInfo = NULL; SectionInfo* cstringSectionInfo = NULL; @@ -6514,10 +8339,13 @@ void Writer::partitionIntoSections() fSegmentInfos.reserve(8); for (unsigned int i=0; i < fAllAtoms->size(); ++i) { ObjectFile::Atom* atom = (*fAllAtoms)[i]; - if ( (atom->getSection() != curSection) || ((curSection==NULL) && (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0)) ) { + if ( ((atom->getSection() != curSection) || (curSection==NULL)) + && ((currentSectionInfo == NULL) + || (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0) + || (strcmp(atom->getSegment().getName(),currentSectionInfo->fSegmentName) != 0)) ) { if ( oneSegmentCommand ) { if ( currentSegmentInfo == NULL ) { - currentSegmentInfo = new SegmentInfo(); + currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment()); currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; this->fSegmentInfos.push_back(currentSegmentInfo); @@ -6536,7 +8364,7 @@ void Writer::partitionIntoSections() } else { if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { - currentSegmentInfo = new SegmentInfo(); + currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment()); strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); uint32_t initprot = 0; if ( atom->getSegment().isContentReadable() ) @@ -6565,6 +8393,10 @@ void Writer::partitionIntoSections() currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) ) currentSegmentInfo->fIndependentAddress = true; + if ( (fOptions.outputKind() == Options::kPreload) && (strcmp(currentSegmentInfo->fName, "__LINKEDIT")==0) ) + currentSegmentInfo->fHasLoadCommand = false; + if ( strcmp(currentSegmentInfo->fName, "__HEADER")==0 ) + currentSegmentInfo->fHasLoadCommand = false; this->fSegmentInfos.push_back(currentSegmentInfo); } currentSectionInfo = new SectionInfo(); @@ -6584,7 +8416,8 @@ void Writer::partitionIntoSections() currentSectionInfo->setIndex(sectionIndex++); currentSegmentInfo->fSections.push_back(currentSectionInfo); } - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) { + //fprintf(stderr, "new section %s for atom %s\n", atom->getSectionName(), atom->getDisplayName()); + if ( strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0 ) { fLoadCommandsSection = currentSectionInfo; fLoadCommandsSegment = currentSegmentInfo; } @@ -6616,13 +8449,15 @@ void Writer::partitionIntoSections() currentSectionInfo->fAllSelfModifyingStubs = true; currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary } + if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__stub_helper") == 0) ) + currentSectionInfo->fAllStubHelpers = true; if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) ) currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned curSection = atom->getSection(); if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) { fSymbolTableCommands->needDynamicTable(); - } + } } // any non-zero fill atoms make whole section marked not-zero-fill if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) @@ -6649,6 +8484,8 @@ void Writer::partitionIntoSections() currentSectionInfo->fSize = offset + curAtomSize; // add atom to section vector currentSectionInfo->fAtoms.push_back(atom); + //fprintf(stderr, " adding atom %p %s size=0x%0llX to section %p %s from %s\n", atom, atom->getDisplayName(), atom->getSize(), + // currentSectionInfo, currentSectionInfo->fSectionName, atom->getFile()->getPath()); // update largest size if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) fLargestAtomSize = curAtomSize; @@ -6745,7 +8582,7 @@ bool Writer::isBranch24Reference(uint8_t kind) // Branch Island Algorithm // // If the __TEXT segment < 16MB, then no branch islands needed -// Otherwise, every 15MB into the __TEXT segment is region is +// Otherwise, every 14MB into the __TEXT segment a region is // added which can contain branch islands. Every out of range // bl instruction is checked. If it crosses a region, an island // is added to that region with the same target and the bl is @@ -6766,7 +8603,7 @@ bool Writer::addPPCBranchIslands() // Can only possibly need branch islands if __TEXT segment > 16M if ( fLoadCommandsSegment->fSize > 16000000 ) { if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); - const uint32_t kBetweenRegions = 15*1024*1024; // place regions of islands every 15MB in __text section + const uint32_t kBetweenRegions = 14*1024*1024; // place regions of islands every 14MB in __text section SectionInfo* textSection = NULL; for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { if ( strcmp((*it)->fSectionName, "__text") == 0 ) { @@ -6867,7 +8704,7 @@ bool Writer::addPPCBranchIslands() uint64_t sectionOffset = 0; for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { ObjectFile::Atom* atom = *it; - if ( atom->getAddress() > islandRegionAddr ) { + if ( (atom->getAddress()+atom->getSize()) > islandRegionAddr ) { uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide; sectionOffset = islandStartOffset; std::vector* regionIslands = ®ionsIslands[regionIndex]; @@ -6935,39 +8772,44 @@ void Writer::adjustLoadCommandsAndPadding() std::vector& sectionInfos = fLoadCommandsSegment->fSections; const int sectionCount = sectionInfos.size(); - uint32_t totalSizeOfHeaderAndLoadCommands = 0; + uint32_t totalSizeOfTEXTLessHeaderAndLoadCommands = 0; for(int j=0; j < sectionCount; ++j) { SectionInfo* curSection = sectionInfos[j]; - totalSizeOfHeaderAndLoadCommands += curSection->fSize; if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) break; + totalSizeOfTEXTLessHeaderAndLoadCommands += curSection->fSize; } uint64_t paddingSize = 0; if ( fOptions.outputKind() == Options::kDyld ) { // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address - paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); + paddingSize = 4096 - (totalSizeOfTEXTLessHeaderAndLoadCommands % 4096); } else if ( fOptions.outputKind() == Options::kObjectFile ) { // mach-o .o files need no padding between load commands and first section + // but leave enough room that the object file could be signed + paddingSize = 32; + } + else if ( fOptions.outputKind() == Options::kPreload ) { + // mach-o MH_PRELOAD files need no padding between load commands and first section paddingSize = 0; } else if ( fOptions.makeEncryptable() ) { // want load commands to end on a page boundary, so __text starts on page boundary - paddingSize = 4096 - ((totalSizeOfHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); - fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfHeaderAndLoadCommands+paddingSize); + paddingSize = 4096 - ((totalSizeOfTEXTLessHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); + fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfTEXTLessHeaderAndLoadCommands+paddingSize); } else { // work backwards from end of segment and lay out sections so that extra room goes to padding atom uint64_t addr = 0; for(int j=sectionCount-1; j >=0; --j) { SectionInfo* curSection = sectionInfos[j]; - addr -= curSection->fSize; - addr = addr & (0 - (1 << curSection->fAlignment)); if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { - addr -= totalSizeOfHeaderAndLoadCommands; - paddingSize = addr % 4096; + addr -= (fLoadCommandsSection->fSize+fMachHeaderAtom->getSize()); + paddingSize = addr % fOptions.segmentAlignment(); break; } + addr -= curSection->fSize; + addr = addr & (0 - (1 << curSection->fAlignment)); } // if command line requires more padding than this @@ -6981,8 +8823,8 @@ void Writer::adjustLoadCommandsAndPadding() minPad = altMin; } if ( paddingSize < minPad ) { - int extraPages = (minPad - paddingSize + 4095)/4096; - paddingSize += extraPages * 4096; + int extraPages = (minPad - paddingSize + fOptions.segmentAlignment() - 1)/fOptions.segmentAlignment(); + paddingSize += extraPages * fOptions.segmentAlignment(); } } @@ -6995,11 +8837,17 @@ void Writer::adjustLoadCommandsAndPadding() } } +static uint64_t segmentAlign(uint64_t addr, uint64_t alignment) +{ + return ((addr+alignment-1) & (-alignment)); +} + // assign file offsets and logical address to all segments template void Writer::assignFileOffsets() { - bool finalLinkedImage = (fOptions.outputKind() != Options::kObjectFile); + const bool virtualSectionOccupyAddressSpace = ((fOptions.outputKind() != Options::kObjectFile) + && (fOptions.outputKind() != Options::kPreload)); bool haveFixedSegments = false; uint64_t fileOffset = 0; uint64_t nextContiguousAddress = fOptions.baseAddress(); @@ -7018,6 +8866,17 @@ void Writer::assignFileOffsets() } } + // process segments with fixed addresses (-seg_page_size) + for (std::vector::iterator it = fOptions.customSegmentSizes().begin(); it != fOptions.customSegmentSizes().end(); ++it) { + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + SegmentInfo* curSegment = *segit; + if ( strcmp(curSegment->fName, it->name) == 0 ) { + curSegment->fPageSize = it->size; + break; + } + } + } + // Run through the segments and each segment's sections to assign addresses for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { SegmentInfo* curSegment = *segit; @@ -7029,14 +8888,21 @@ void Writer::assignFileOffsets() nextContiguousAddress = nextReadOnlyAddress; } - fileOffset = (fileOffset+4095) & (-4096); + if ( fOptions.outputKind() == Options::kPreload ) { + if ( strcmp(curSegment->fName, "__HEADER") == 0 ) + nextContiguousAddress = 0; + else if ( strcmp(curSegment->fName, "__TEXT") == 0 ) + nextContiguousAddress = fOptions.baseAddress(); + } + + fileOffset = segmentAlign(fileOffset, curSegment->fPageSize); curSegment->fFileOffset = fileOffset; // Set the segment base address if ( curSegment->fFixedAddress ) haveFixedSegments = true; else - curSegment->fBaseAddress = nextContiguousAddress; + curSegment->fBaseAddress = segmentAlign(nextContiguousAddress, curSegment->fPageSize); // We've set the segment address, now run through each section. uint64_t address = curSegment->fBaseAddress; @@ -7050,11 +8916,25 @@ void Writer::assignFileOffsets() // adjust section address based on alignment uint64_t alignment = 1 << curSection->fAlignment; - address = ( (address+alignment-1) & (-alignment) ); + if ( curSection->fAtoms.size() == 1 ) { + // if there is only one atom in section, use modulus for even better layout + ObjectFile::Alignment atomAlign = curSection->fAtoms[0]->getAlignment(); + uint64_t atomAlignP2 = (1 << atomAlign.powerOf2); + uint64_t currentModulus = (address % atomAlignP2); + if ( currentModulus != atomAlign.modulus ) { + if ( atomAlign.modulus > currentModulus ) + address += atomAlign.modulus-currentModulus; + else + address += atomAlign.modulus+atomAlignP2-currentModulus; + } + } + else { + address = ( (address+alignment-1) & (-alignment) ); + } // adjust file offset to match address if ( prevSection != NULL ) { - if ( finalLinkedImage || !prevSection->fVirtualSection ) + if ( virtualSectionOccupyAddressSpace || !prevSection->fVirtualSection ) fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; else fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); @@ -7063,16 +8943,16 @@ void Writer::assignFileOffsets() // update section info curSection->fFileOffset = fileOffset; curSection->setBaseAddress(address); - //fprintf(stderr, "%s %s %llX\n", curSegment->fName, curSection->fSectionName, address); + //fprintf(stderr, "%s %s addr=0x%llX, fileoffset=0x%llX, size=0x%llX\n", curSegment->fName, curSection->fSectionName, address, fileOffset, curSection->fSize); // keep track of trailing zero fill sections if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) firstZeroFillSection = curSection; - if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && finalLinkedImage ) + if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && (fOptions.outputKind() != Options::kObjectFile) ) throwf("zero-fill section %s not at end of segment", curSection->fSectionName); // update running pointers - if ( finalLinkedImage || !curSection->fVirtualSection ) + if ( virtualSectionOccupyAddressSpace || !curSection->fVirtualSection ) address += curSection->fSize; fileOffset += curSection->fSize; @@ -7096,16 +8976,18 @@ void Writer::assignFileOffsets() fileOffset = firstZeroFillSection->fFileOffset; } // page align segment size - curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); - curSegment->fSize = (curSegment->fSize+4095) & (-4096); + curSegment->fFileSize = segmentAlign(curSegment->fFileSize, curSegment->fPageSize); + curSegment->fSize = segmentAlign(curSegment->fSize, curSegment->fPageSize); if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { - nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); + nextContiguousAddress = segmentAlign(curSegment->fBaseAddress+curSegment->fSize, curSegment->fPageSize); + fileOffset = segmentAlign(fileOffset, curSegment->fPageSize); if ( curSegment->fInitProtection & VM_PROT_WRITE ) nextWritableAddress = nextContiguousAddress; else nextReadOnlyAddress = nextContiguousAddress; } } + //fprintf(stderr, "end of seg %s, fileoffset=0x%llX, nextContiguousAddress=0x%llX\n", curSegment->fName, fileOffset, nextContiguousAddress); } // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) @@ -7239,6 +9121,8 @@ ObjectFile::Atom::Scope MachHeaderAtom::getScope() const case Options::kDynamicBundle: case Options::kDyld: case Options::kObjectFile: + case Options::kPreload: + case Options::kKextBundle: return ObjectFile::Atom::scopeLinkageUnit; } throw "unknown header type"; @@ -7257,6 +9141,8 @@ ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusio case Options::kDyld: return ObjectFile::Atom::kSymbolTableIn; case Options::kObjectFile: + case Options::kPreload: + case Options::kKextBundle: return ObjectFile::Atom::kSymbolTableNotIn; } throw "unknown header type"; @@ -7274,6 +9160,8 @@ const char* MachHeaderAtom::getName() const case Options::kDynamicBundle: return "__mh_bundle_header"; case Options::kObjectFile: + case Options::kPreload: + case Options::kKextBundle: return NULL; case Options::kDyld: return "__mh_dylinker_header"; @@ -7292,6 +9180,8 @@ const char* MachHeaderAtom::getDisplayName() const case Options::kDyld: return this->getName(); case Options::kObjectFile: + case Options::kPreload: + case Options::kKextBundle: return "mach header"; } throw "unknown header type"; @@ -7319,6 +9209,12 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const case Options::kDyld: fileType = MH_DYLINKER; break; + case Options::kPreload: + fileType = MH_PRELOAD; + break; + case Options::kKextBundle: + fileType = MH_KEXT_BUNDLE; + break; } // get flags @@ -7331,6 +9227,11 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { flags |= MH_NOUNDEFS; } + else if ( fWriter.fOptions.outputKind() == Options::kPreload ) { + flags |= MH_NOUNDEFS; + if ( fWriter.fOptions.positionIndependentExecutable() ) + flags |= MH_PIE; + } else { flags = MH_DYLDLINK; if ( fWriter.fOptions.bindAtLoad() ) @@ -7345,7 +9246,17 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const flags |= MH_FORCE_FLAT; break; } - if ( fWriter.fHasWeakExports ) + bool hasWeakDefines = fWriter.fHasWeakExports; + if ( fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->size() != 0 ) { + for(std::set::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin(); + it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) { + if ( fWriter.shouldExport(**it) ) { + hasWeakDefines = true; + break; + } + } + } + if ( hasWeakDefines ) flags |= MH_WEAK_DEFINES; if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) flags |= MH_BINDS_TO_WEAK; @@ -7357,6 +9268,8 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const flags |= MH_NO_REEXPORTED_DYLIBS; if ( fWriter.fOptions.positionIndependentExecutable() ) flags |= MH_PIE; + if ( fWriter.fOptions.markAutoDeadStripDylib() ) + flags |= MH_DEAD_STRIPPABLE_DYLIB; } if ( fWriter.fOptions.hasExecutableStack() ) flags |= MH_ALLOW_STACK_EXECUTION; @@ -7462,14 +9375,18 @@ void SegmentLoadCommandsAtom::computeSize() { uint64_t size = 0; std::vector& segmentInfos = fWriter.fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - size += sizeof(macho_segment_command

); - std::vector& sectionInfos = segmentInfos[i]->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) - size += sizeof(macho_section

); + int segCount = 0; + for(std::vector::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) { + SegmentInfo* seg = *it; + if ( seg->fHasLoadCommand ) { + ++segCount; + size += sizeof(macho_segment_command

); + std::vector& sectionInfos = seg->fSections; + const int sectionCount = sectionInfos.size(); + for(int j=0; j < sectionCount; ++j) { + if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) + size += sizeof(macho_section

); + } } } fSize = size; @@ -7518,17 +9435,18 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const bzero(buffer, size); uint8_t* p = buffer; typename std::vector& segmentInfos = fWriter.fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* segInfo = segmentInfos[i]; + for(std::vector::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) { + SegmentInfo* segInfo = *it; + if ( ! segInfo->fHasLoadCommand ) + continue; const int sectionCount = segInfo->fSections.size(); macho_segment_command

* cmd = (macho_segment_command

*)p; cmd->set_cmd(macho_segment_command

::CMD); cmd->set_segname(segInfo->fName); cmd->set_vmaddr(segInfo->fBaseAddress); - cmd->set_vmsize(segInfo->fSize); + cmd->set_vmsize(oneSegment ? 0 : segInfo->fSize); cmd->set_fileoff(segInfo->fFileOffset); - cmd->set_filesize(segInfo->fFileSize); + cmd->set_filesize(oneSegment ? 0 : segInfo->fFileSize); cmd->set_maxprot(segInfo->fMaxProtection); cmd->set_initprot(segInfo->fInitProtection); // add sections array @@ -7577,21 +9495,31 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); sect->set_reserved1(sectInfo->fIndirectSymbolOffset); sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); + if ( sectInfo->fHasTextLocalRelocs ) + sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); } else if ( sectInfo->fAllSelfModifyingStubs ) { sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); sect->set_reserved1(sectInfo->fIndirectSymbolOffset); sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); } + else if ( sectInfo->fAllStubHelpers ) { + sect->set_flags(S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + if ( sectInfo->fHasTextLocalRelocs ) + sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); + } + else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCStringType ) { + sect->set_flags(S_CSTRING_LITERALS); + } + else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCFIType ) { + sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); + } else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { sect->set_flags(S_MOD_INIT_FUNC_POINTERS); } else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { sect->set_flags(S_MOD_TERM_FUNC_POINTERS); } - else if ( (strcmp(sectInfo->fSectionName, "__eh_frame") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); - } else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { sect->set_flags(S_COALESCED); } @@ -7601,9 +9529,6 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { sect->set_flags(S_INTERPOSING); } - else if ( (strcmp(sectInfo->fSectionName, "__cstring") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_CSTRING_LITERALS); - } else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { sect->set_flags(S_4BYTE_LITERALS); } @@ -7616,6 +9541,9 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { sect->set_flags(S_LITERAL_POINTERS); } + else if ( (strcmp(sectInfo->fSectionName, "__objc_selrefs") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { + sect->set_flags(S_LITERAL_POINTERS); + } else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { sect->set_flags(S_LITERAL_POINTERS); } @@ -7632,6 +9560,7 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( sectInfo->fHasTextExternalRelocs ) sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC); } + //fprintf(stderr, "section %s flags=0x%08X\n", sectInfo->fSectionName, sect->flags()); } } p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; @@ -7643,7 +9572,7 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const template SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment) + : LoadCommandAtom(writer), fNeedsDynamicSymbolTable(false) { bzero(&fSymbolTable, sizeof(macho_symtab_command

)); bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command

)); @@ -7652,11 +9581,14 @@ SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: + case Options::kKextBundle: fNeedsDynamicSymbolTable = true; break; case Options::kObjectFile: case Options::kStaticExecutable: fNeedsDynamicSymbolTable = false; + case Options::kPreload: + fNeedsDynamicSymbolTable = fWriter.fOptions.positionIndependentExecutable(); break; } writer.fSymbolTableCommands = this; @@ -7683,14 +9615,14 @@ uint64_t SymbolTableLoadCommandsAtom::getSize() const template void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { - // build LC_DYSYMTAB command + // build LC_SYMTAB command macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)buffer; bzero(symbolTableCmd, sizeof(macho_symtab_command

)); symbolTableCmd->set_cmd(LC_SYMTAB); symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); - symbolTableCmd->set_symoff(fWriter.fSymbolTableAtom->getFileOffset()); - symbolTableCmd->set_stroff(fWriter.fStringsAtom->getFileOffset()); + symbolTableCmd->set_symoff(fWriter.fSymbolTableCount == 0 ? 0 : fWriter.fSymbolTableAtom->getFileOffset()); + symbolTableCmd->set_stroff(fWriter.fStringsAtom->getSize() == 0 ? 0 : fWriter.fStringsAtom->getFileOffset()); symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); // build LC_DYSYMTAB command @@ -7713,13 +9645,17 @@ void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); } - dynamicSymbolTableCmd->set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nindirectsyms(fWriter.fIndirectTableAtom->fTable.size()); + dynamicSymbolTableCmd->set_indirectsymoff((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nindirectsyms((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->fTable.size()); if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { - dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); - dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); + if ( fWriter.fExternalRelocationsAtom != 0 ) { + dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); + } + if ( fWriter.fLocalRelocationsAtom != 0 ) { + dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); + dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); + } } } } @@ -7840,8 +9776,8 @@ template void RoutinesLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - if (fWriter.fEntryPoint->isThumb()) - initAddr |= 1ULL; + if (fWriter.fEntryPoint->isThumb()) + initAddr |= 1ULL; bzero(buffer, sizeof(macho_routines_command

)); macho_routines_command

* cmd = (macho_routines_command

*)buffer; cmd->set_cmd(macho_routines_command

::CMD); @@ -8044,6 +9980,8 @@ void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { uint64_t size = this->getSize(); uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); + if ( fWriter.fEntryPoint->isThumb() ) + start |= 1ULL; bzero(buffer, size); macho_thread_command* cmd = (macho_thread_command*)buffer; cmd->set_cmd(LC_UNIXTHREAD); @@ -8106,9 +10044,479 @@ void LoadCommandsPaddingAtom::setSize(uint64_t newSize) } template -uint64_t LinkEditAtom::getFileOffset() const -{ - return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); +void UnwindInfoAtom::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, + ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsdaRef, + ObjectFile::Atom* personalityPointer) +{ + Info info; + info.func = func; + if ( fdeRef != NULL ) + info.fde = &fdeRef->getTarget(); + else + info.fde = NULL; + if ( lsdaRef != NULL ) { + info.lsda = &lsdaRef->getTarget(); + info.lsdaOffset = lsdaRef->getTargetOffset(); + } + else { + info.lsda = NULL; + info.lsdaOffset = 0; + } + info.personalityPointer = personalityPointer; + info.encoding = encoding; + fInfos.push_back(info); + //fprintf(stderr, "addUnwindInfo() encoding=0x%08X, lsda=%p, lsdaOffset=%d, person=%p, func=%s\n", + // encoding, info.lsda, info.lsdaOffset, personalityPointer, func->getDisplayName()); +} + +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) +{ + return ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); +} + +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) +{ + return ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); +} + +template +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) +{ + return false; +} + + +template +void UnwindInfoAtom::compressDuplicates(std::vector& uniqueInfos) +{ + // build new list removing entries where next function has same encoding + uniqueInfos.reserve(fInfos.size()); + Info last; + last.func = NULL; + last.lsda = NULL; + last.lsdaOffset = 0; + last.personalityPointer = NULL; + last.encoding = 0xFFFFFFFF; + for(typename std::vector::iterator it=fInfos.begin(); it != fInfos.end(); ++it) { + Info& newInfo = *it; + bool newNeedsDwarf = encodingMeansUseDwarf(newInfo.encoding); + // remove infos which have same encoding and personalityPointer as last one + if ( newNeedsDwarf || (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) + || (newInfo.lsda != NULL) || (last.lsda != NULL) ) { + uniqueInfos.push_back(newInfo); + } + last = newInfo; + } + //fprintf(stderr, "compressDuplicates() fInfos.size()=%lu, uniqueInfos.size()=%lu\n", fInfos.size(), uniqueInfos.size()); +} + +template +void UnwindInfoAtom::findCommonEncoding(const std::vector& uniqueInfos, std::map& commonEncodings) +{ + // scan infos to get frequency counts for each encoding + std::map encodingsUsed; + unsigned int mostCommonEncodingUsageCount = 0; + for(typename std::vector::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { + // never put dwarf into common table + if ( encodingMeansUseDwarf(it->encoding) ) + continue; + std::map::iterator pos = encodingsUsed.find(it->encoding); + if ( pos == encodingsUsed.end() ) { + encodingsUsed[it->encoding] = 1; + } + else { + encodingsUsed[it->encoding] += 1; + if ( mostCommonEncodingUsageCount < encodingsUsed[it->encoding] ) + mostCommonEncodingUsageCount = encodingsUsed[it->encoding]; + } + } + // put the most common encodings into the common table, but at most 127 of them + for(unsigned int usages=mostCommonEncodingUsageCount; usages > 1; --usages) { + for (std::map::iterator euit=encodingsUsed.begin(); euit != encodingsUsed.end(); ++euit) { + if ( euit->second == usages ) { + unsigned int size = commonEncodings.size(); + if ( size < 127 ) { + commonEncodings[euit->first] = size; + } + } + } + } +} + +template +void UnwindInfoAtom::makeLsdaIndex(const std::vector& uniqueInfos, std::map& lsdaIndexOffsetMap) +{ + for(typename std::vector::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { + lsdaIndexOffsetMap[it->func] = fLSDAIndex.size() * sizeof(macho_unwind_info_section_header_lsda_index_entry

); + if ( it->lsda != NULL ) { + LSDAEntry entry; + entry.func = it->func; + entry.lsda = it->lsda; + entry.lsdaOffset = it->lsdaOffset; + fLSDAIndex.push_back(entry); + } + } +} + +template +void UnwindInfoAtom::makePersonalityIndex(std::vector& uniqueInfos) +{ + for(typename std::vector::iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { + if ( it->personalityPointer != NULL ) { + std::map::iterator pos = fPersonalityIndexMap.find(it->personalityPointer); + if ( pos == fPersonalityIndexMap.end() ) { + const uint32_t nextIndex = fPersonalityIndexMap.size() + 1; + fPersonalityIndexMap[it->personalityPointer] = nextIndex; + } + uint32_t personalityIndex = fPersonalityIndexMap[it->personalityPointer]; + it->encoding |= (personalityIndex << (__builtin_ctz(UNWIND_PERSONALITY_MASK)) ); + } + } +} + +template +unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vector& uniqueInfos, uint32_t pageSize, + unsigned int endIndex, uint8_t*& pageEnd) +{ + const unsigned int maxEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + const unsigned int entriesToAdd = ((endIndex > maxEntriesPerPage) ? maxEntriesPerPage : endIndex); + uint8_t* pageStart = pageEnd + - entriesToAdd*sizeof(unwind_info_regular_second_level_entry) + - sizeof(unwind_info_regular_second_level_page_header); + macho_unwind_info_regular_second_level_page_header

* page = (macho_unwind_info_regular_second_level_page_header

*)pageStart; + page->set_kind(UNWIND_SECOND_LEVEL_REGULAR); + page->set_entryPageOffset(sizeof(macho_unwind_info_regular_second_level_page_header

)); + page->set_entryCount(entriesToAdd); + macho_unwind_info_regular_second_level_entry

* entryTable = (macho_unwind_info_regular_second_level_entry

*)(pageStart + page->entryPageOffset()); + for (unsigned int i=0; i < entriesToAdd; ++i) { + const Info& info = uniqueInfos[endIndex-entriesToAdd+i]; + entryTable[i].set_functionOffset(0); + entryTable[i].set_encoding(info.encoding); + RegFixUp fixup; + fixup.contentPointer = (uint8_t*)(&entryTable[i]); + fixup.func = info.func; + fixup.fde = ( encodingMeansUseDwarf(info.encoding) ? info.fde : NULL ); + fRegFixUps.push_back(fixup); + } + //fprintf(stderr, "regular page with %u entries\n", entriesToAdd); + pageEnd = pageStart; + return endIndex - entriesToAdd; +} + + +template +unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector& uniqueInfos, + const std::map commonEncodings, + uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) +{ + const bool log = false; + if (log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); + // first pass calculates how many compressed entries we could fit in this sized page + // keep adding entries to page until: + // 1) encoding table plus entry table plus header exceed page size + // 2) the file offset delta from the first to last function > 24 bits + // 3) custom encoding index reachs 255 + // 4) run out of uniqueInfos to encode + std::map pageSpecificEncodings; + uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t); + std::vector encodingIndexes; + int index = endIndex-1; + int entryCount = 0; + uint64_t lastEntryAddress = uniqueInfos[index].func->getAddress(); + bool canDo = true; + while ( canDo && (index >= 0) ) { + const Info& info = uniqueInfos[index--]; + // compute encoding index + unsigned int encodingIndex; + std::map::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) { + encodingIndex = pos->second; + } + else { + // no commmon entry, so add one on this page + uint32_t encoding = info.encoding; + if ( encodingMeansUseDwarf(encoding) ) { + // make unique pseudo encoding so this dwarf will gets is own encoding entry slot + encoding += (index+1); + } + std::map::iterator ppos = pageSpecificEncodings.find(encoding); + if ( ppos != pageSpecificEncodings.end() ) { + encodingIndex = pos->second; + } + else { + encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); + if ( encodingIndex <= 255 ) { + pageSpecificEncodings[encoding] = encodingIndex; + } + else { + canDo = false; // case 3) + if (log) fprintf(stderr, "end of compressed page with %u entries, %lu custom encodings because too many custom encodings\n", + entryCount, pageSpecificEncodings.size()); + } + } + } + if ( canDo ) + encodingIndexes.push_back(encodingIndex); + // compute function offset + uint32_t funcOffsetWithInPage = lastEntryAddress - info.func->getAddress(); + if ( funcOffsetWithInPage > 0x00FFFF00 ) { + // don't use 0x00FFFFFF because addresses may vary after atoms are laid out again + canDo = false; // case 2) + if (log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount); + } + else { + ++entryCount; + } + // check room for entry + if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { + canDo = false; // case 1) + --entryCount; + if (log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); + } + //if (log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); + } + + // check for cases where it would be better to use a regular (non-compressed) page + const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + + pageSpecificEncodings.size()*sizeof(uint32_t) + + entryCount*sizeof(uint32_t); + if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { + const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + if ( entryCount < regularEntriesPerPage ) { + return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); + } + } + + // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 + uint32_t pad = 0; + if ( compressPageUsed == (pageSize-4) ) + pad = 4; + + // second pass fills in page + uint8_t* pageStart = pageEnd - compressPageUsed - pad; + macho_unwind_info_compressed_second_level_page_header

* page = (macho_unwind_info_compressed_second_level_page_header

*)pageStart; + page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); + page->set_entryPageOffset(sizeof(macho_unwind_info_compressed_second_level_page_header

)); + page->set_entryCount(entryCount); + page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); + page->set_encodingsCount(pageSpecificEncodings.size()); + uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; + // fill in entry table + uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; + ObjectFile::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; + for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { + const Info& info = uniqueInfos[i]; + uint8_t encodingIndex; + if ( encodingMeansUseDwarf(info.encoding) ) { + // dwarf entries are always in page specific encodings + encodingIndex = pageSpecificEncodings[info.encoding+i]; + } + else { + std::map::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) + encodingIndex = pos->second; + else + encodingIndex = pageSpecificEncodings[info.encoding]; + } + uint32_t entryIndex = i - endIndex + entryCount; + A::P::E::set32(entiresArray[entryIndex], encodingIndex << 24); + CompressedFixUp funcStartFixUp; + funcStartFixUp.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); + funcStartFixUp.func = info.func; + funcStartFixUp.fromFunc = firstFunc; + fCompressedFixUps.push_back(funcStartFixUp); + if ( encodingMeansUseDwarf(info.encoding) ) { + CompressedEncodingFixUp dwarfStartFixup; + dwarfStartFixup.contentPointer = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]); + dwarfStartFixup.fde = info.fde; + fCompressedEncodingFixUps.push_back(dwarfStartFixup); + } + } + // fill in encodings table + for(std::map::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { + A::P::E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); + } + + if (log) fprintf(stderr, "compressed page with %u entries, %lu custom encodings\n", entryCount, pageSpecificEncodings.size()); + + // update pageEnd; + pageEnd = pageStart; + return endIndex-entryCount; // endIndex for next page +} + +template <> void UnwindInfoAtom::generate() { } +template <> void UnwindInfoAtom::generate() { } +template <> void UnwindInfoAtom::generate() { } + + +template +void UnwindInfoAtom::generate() +{ + // only generate table if there are functions with unwind info + if ( fInfos.size() > 0 ) { + // find offset of end of __unwind_info section + SectionInfo* unwindSectionInfo = (SectionInfo*)this->getSection(); + + // build new list that has proper offsetInImage and remove entries where next function has same encoding + std::vector uniqueInfos; + this->compressDuplicates(uniqueInfos); + + // build personality index, update encodings with personality index + this->makePersonalityIndex(uniqueInfos); + if ( fPersonalityIndexMap.size() > 3 ) + throw "too many personality routines for compact unwind to encode"; + + // put the most common encodings into the common table, but at most 127 of them + std::map commonEncodings; + this->findCommonEncoding(uniqueInfos, commonEncodings); + + // build lsda index + std::map lsdaIndexOffsetMap; + this->makeLsdaIndex(uniqueInfos, lsdaIndexOffsetMap); + + // calculate worst case size for all unwind info pages when allocating buffer + const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + const unsigned int pageCount = ((uniqueInfos.size() - 1)/entriesPerRegularPage) + 1; + fPagesContentForDelete = (uint8_t*)calloc(pageCount,4096); + fPagesSize = 0; + if ( fPagesContentForDelete == NULL ) + throw "could not allocate space for compact unwind info"; + ObjectFile::Atom* secondLevelFirstFuncs[pageCount*3]; + uint8_t* secondLevelPagesStarts[pageCount*3]; + + // make last second level page smaller so that all other second level pages can be page aligned + uint32_t maxLastPageSize = unwindSectionInfo->fFileOffset % 4096; + uint32_t tailPad = 0; + if ( maxLastPageSize < 128 ) { + tailPad = maxLastPageSize; + maxLastPageSize = 4096; + } + + // fill in pages in reverse order + unsigned int endIndex = uniqueInfos.size(); + unsigned int secondLevelPageCount = 0; + uint8_t* pageEnd = &fPagesContentForDelete[pageCount*4096]; + uint32_t pageSize = maxLastPageSize; + while ( endIndex > 0 ) { + endIndex = makeCompressedSecondLevelPage(uniqueInfos, commonEncodings, pageSize, endIndex, pageEnd); + secondLevelPagesStarts[secondLevelPageCount] = pageEnd; + secondLevelFirstFuncs[secondLevelPageCount] = uniqueInfos[endIndex].func; + ++secondLevelPageCount; + pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + } + fPagesContent = pageEnd; + fPagesSize = &fPagesContentForDelete[pageCount*4096] - pageEnd; + + // calculate section layout + const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header

); + const uint32_t commonEncodingsArrayCount = commonEncodings.size(); + const uint32_t commonEncodingsArraySize = commonEncodingsArrayCount * sizeof(compact_unwind_encoding_t); + const uint32_t personalityArraySectionOffset = commonEncodingsArraySectionOffset + commonEncodingsArraySize; + const uint32_t personalityArrayCount = fPersonalityIndexMap.size(); + const uint32_t personalityArraySize = personalityArrayCount * sizeof(uint32_t); + const uint32_t indexSectionOffset = personalityArraySectionOffset + personalityArraySize; + const uint32_t indexCount = secondLevelPageCount+1; + const uint32_t indexSize = indexCount * sizeof(macho_unwind_info_section_header_index_entry

); + const uint32_t lsdaIndexArraySectionOffset = indexSectionOffset + indexSize; + const uint32_t lsdaIndexArrayCount = fLSDAIndex.size(); + const uint32_t lsdaIndexArraySize = lsdaIndexArrayCount * sizeof(macho_unwind_info_section_header_lsda_index_entry

); + const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; + + + // allocate and fill in section header + fHeaderSize = headerEndSectionOffset; + fHeaderContent = new uint8_t[fHeaderSize]; + bzero(fHeaderContent, fHeaderSize); + macho_unwind_info_section_header

* sectionHeader = (macho_unwind_info_section_header

*)fHeaderContent; + sectionHeader->set_version(UNWIND_SECTION_VERSION); + sectionHeader->set_commonEncodingsArraySectionOffset(commonEncodingsArraySectionOffset); + sectionHeader->set_commonEncodingsArrayCount(commonEncodingsArrayCount); + sectionHeader->set_personalityArraySectionOffset(personalityArraySectionOffset); + sectionHeader->set_personalityArrayCount(personalityArrayCount); + sectionHeader->set_indexSectionOffset(indexSectionOffset); + sectionHeader->set_indexCount(indexCount); + + // copy common encodings + uint32_t* commonEncodingsTable = (uint32_t*)&fHeaderContent[commonEncodingsArraySectionOffset]; + for (std::map::iterator it=commonEncodings.begin(); it != commonEncodings.end(); ++it) + A::P::E::set32(commonEncodingsTable[it->second], it->first); + + // make references for personality entries + uint32_t* personalityArray = (uint32_t*)&fHeaderContent[sectionHeader->personalityArraySectionOffset()]; + for (std::map::iterator it=fPersonalityIndexMap.begin(); it != fPersonalityIndexMap.end(); ++it) { + uint32_t offset = (uint8_t*)&personalityArray[it->second-1] - fHeaderContent; + fReferences.push_back(new WriterReference(offset, A::kImageOffset32, it->first)); + } + + // build first level index and references + macho_unwind_info_section_header_index_entry

* indexTable = (macho_unwind_info_section_header_index_entry

*)&fHeaderContent[indexSectionOffset]; + for (unsigned int i=0; i < secondLevelPageCount; ++i) { + unsigned int reverseIndex = secondLevelPageCount - 1 - i; + indexTable[i].set_functionOffset(0); + indexTable[i].set_secondLevelPagesSectionOffset(secondLevelPagesStarts[reverseIndex]-fPagesContent+headerEndSectionOffset); + indexTable[i].set_lsdaIndexArraySectionOffset(lsdaIndexOffsetMap[secondLevelFirstFuncs[reverseIndex]]+lsdaIndexArraySectionOffset); + uint32_t refOffset = (uint8_t*)&indexTable[i] - fHeaderContent; + fReferences.push_back(new WriterReference(refOffset, A::kImageOffset32, secondLevelFirstFuncs[reverseIndex])); + } + indexTable[secondLevelPageCount].set_functionOffset(0); + indexTable[secondLevelPageCount].set_secondLevelPagesSectionOffset(0); + indexTable[secondLevelPageCount].set_lsdaIndexArraySectionOffset(lsdaIndexArraySectionOffset+lsdaIndexArraySize); + fReferences.push_back(new WriterReference((uint8_t*)&indexTable[secondLevelPageCount] - fHeaderContent, A::kImageOffset32, + fInfos.back().func, fInfos.back().func->getSize()+1)); + + // build lsda references + uint32_t lsdaEntrySectionOffset = lsdaIndexArraySectionOffset; + for (typename std::vector::iterator it = fLSDAIndex.begin(); it != fLSDAIndex.end(); ++it) { + fReferences.push_back(new WriterReference(lsdaEntrySectionOffset, A::kImageOffset32, it->func)); + fReferences.push_back(new WriterReference(lsdaEntrySectionOffset+4, A::kImageOffset32, it->lsda, it->lsdaOffset)); + lsdaEntrySectionOffset += sizeof(unwind_info_section_header_lsda_index_entry); + } + + // make references for regular second level entries + for (typename std::vector::iterator it = fRegFixUps.begin(); it != fRegFixUps.end(); ++it) { + uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; + fReferences.push_back(new WriterReference(offset, A::kImageOffset32, it->func)); + if ( it->fde != NULL ) + fReferences.push_back(new WriterReference(offset+4, A::kSectionOffset24, it->fde)); + } + // make references for compressed second level entries + for (typename std::vector::iterator it = fCompressedFixUps.begin(); it != fCompressedFixUps.end(); ++it) { + uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; + fReferences.push_back(new WriterReference(offset, A::kPointerDiff24, it->func, 0, it->fromFunc, 0)); + } + for (typename std::vector::iterator it = fCompressedEncodingFixUps.begin(); it != fCompressedEncodingFixUps.end(); ++it) { + uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; + fReferences.push_back(new WriterReference(offset, A::kSectionOffset24, it->fde)); + } + + // update section record with new size + unwindSectionInfo->fSize = this->getSize(); + + // alter alignment so this section lays out so second level tables are page aligned + if ( secondLevelPageCount > 2 ) + fAlignment = ObjectFile::Alignment(12, (unwindSectionInfo->fFileOffset - this->getSize()) % 4096); + } + +} + + + + +template +void UnwindInfoAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, fHeaderContent, fHeaderSize); + memcpy(&buffer[fHeaderSize], fPagesContent, fPagesSize); +} + + + +template +uint64_t LinkEditAtom::getFileOffset() const +{ + return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); } @@ -8180,12 +10588,10 @@ void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const const uint32_t indirectTableSize = fTable.size(); uint32_t* indirectTable = (uint32_t*)buffer; for(std::vector::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { - if ( it->indirectIndex < indirectTableSize ) { + if ( it->indirectIndex < indirectTableSize ) A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); - } - else { + else throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); - } } } @@ -8235,12 +10641,14 @@ void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const p->set_module_index(0); } // create module table (one entry) + pint_t objcModuleSectionStart = 0; + pint_t objcModuleSectionSize = 0; uint16_t numInits = 0; uint16_t numTerms = 0; std::vector& segmentInfos = fWriter.fSegmentInfos; for (std::vector::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; if ( strcmp((*segit)->fName, "__DATA") == 0 ) { - std::vector& sectionInfos = (*segit)->fSections; for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 ) numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t); @@ -8248,6 +10656,15 @@ void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t); } } + else if ( strcmp((*segit)->fName, "__OBJC") == 0 ) { + for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { + SectionInfo* sectInfo = (*sectit); + if ( strcmp(sectInfo->fSectionName, "__module_info") == 0 ) { + objcModuleSectionStart = sectInfo->getBaseAddress(); + objcModuleSectionSize = sectInfo->fSize; + } + } + } } macho_dylib_module

* module = (macho_dylib_module

*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

)]; module->set_module_name(fModuleNameOffset); @@ -8261,8 +10678,8 @@ void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const module->set_nextrel(fWriter.fExternalRelocs.size()); module->set_iinit_iterm(0,0); module->set_ninit_nterm(numInits,numTerms); - module->set_objc_module_info_addr(0); // Not used by ld_classic, and not used by objc runtime for many years - module->set_objc_module_info_size(0); // Not used by ld_classic, and not used by objc runtime for many years + module->set_objc_module_info_addr(objcModuleSectionStart); + module->set_objc_module_info_size(objcModuleSectionSize); // create reference table macho_dylib_reference

* ref = (macho_dylib_reference

*)((uint8_t*)module + sizeof(macho_dylib_module

)); for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) { @@ -8431,12 +10848,14 @@ template void SegmentSplitInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { uint64_t size = this->getSize(); - bzero(buffer, size); - macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)buffer; - cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); - cmd->set_cmdsize(size); - cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset()); - cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize()); + if ( size > 0 ) { + bzero(buffer, size); + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)buffer; + cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); + cmd->set_cmdsize(size); + cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset()); + cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize()); + } } @@ -8572,6 +10991,783 @@ template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segme template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } + + +template +void DyldInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + // build LC_DYLD_INFO command + macho_dyld_info_command

* cmd = (macho_dyld_info_command

*)buffer; + bzero(cmd, sizeof(macho_dyld_info_command

)); + + cmd->set_cmd( fWriter.fOptions.makeClassicDyldInfo() ? LC_DYLD_INFO : LC_DYLD_INFO_ONLY); + cmd->set_cmdsize(sizeof(macho_dyld_info_command

)); + if ( (fWriter.fCompressedRebaseInfoAtom != NULL) && (fWriter.fCompressedRebaseInfoAtom->getSize() != 0) ) { + cmd->set_rebase_off(fWriter.fCompressedRebaseInfoAtom->getFileOffset()); + cmd->set_rebase_size(fWriter.fCompressedRebaseInfoAtom->getSize()); + } + if ( (fWriter.fCompressedBindingInfoAtom != NULL) && (fWriter.fCompressedBindingInfoAtom->getSize() != 0) ) { + cmd->set_bind_off(fWriter.fCompressedBindingInfoAtom->getFileOffset()); + cmd->set_bind_size(fWriter.fCompressedBindingInfoAtom->getSize()); + } + if ( (fWriter.fCompressedWeakBindingInfoAtom != NULL) && (fWriter.fCompressedWeakBindingInfoAtom->getSize() != 0) ) { + cmd->set_weak_bind_off(fWriter.fCompressedWeakBindingInfoAtom->getFileOffset()); + cmd->set_weak_bind_size(fWriter.fCompressedWeakBindingInfoAtom->getSize()); + } + if ( (fWriter.fCompressedLazyBindingInfoAtom != NULL) && (fWriter.fCompressedLazyBindingInfoAtom->getSize() != 0) ) { + cmd->set_lazy_bind_off(fWriter.fCompressedLazyBindingInfoAtom->getFileOffset()); + cmd->set_lazy_bind_size(fWriter.fCompressedLazyBindingInfoAtom->getSize()); + } + if ( (fWriter.fCompressedExportInfoAtom != NULL) && (fWriter.fCompressedExportInfoAtom->getSize() != 0) ) { + cmd->set_export_off(fWriter.fCompressedExportInfoAtom->getFileOffset()); + cmd->set_export_size(fWriter.fCompressedExportInfoAtom->getSize()); + } +} + + +struct rebase_tmp +{ + rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; +}; + + +template +void CompressedRebaseInfoLinkEditAtom::encode() +{ + // sort rebase info by type, then address + const std::vector& segments = fWriter.fSegmentInfos; + std::vector& info = fWriter.fRebaseInfo; + std::sort(info.begin(), info.end()); + + // convert to temp encoding that can be more easily optimized + std::vector mid; + const SegmentInfo* currentSegment = NULL; + unsigned int segIndex = 0; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { + if ( type != it->fType ) { + mid.push_back(rebase_tmp(REBASE_OPCODE_SET_TYPE_IMM, it->fType)); + type = it->fType; + } + if ( address != it->fAddress ) { + if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) + || ((currentSegment->fBaseAddress+currentSegment->fSize) <= it->fAddress) ) { + segIndex = 0; + for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { + if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { + currentSegment = *segit; + break; + } + ++segIndex; + } + mid.push_back(rebase_tmp(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); + } + else { + mid.push_back(rebase_tmp(REBASE_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); + } + address = it->fAddress; + } + mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); + address += sizeof(pint_t); + } + mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); + + // optimize phase 1, compress packed runs of pointers + rebase_tmp* dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) { + *dst = *src++; + while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) { + dst->operand1 += src->operand1; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 2, combine rebase/add pairs + dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) + && (src->operand1 == 1) + && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) { + dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with + // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) + && (src[2].operand1 == delta) ) { + // found at least three in a row, this is worth compressing + dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 4, use immediate encodings + for (rebase_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) { + p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES; + } + } + + // convert to compressed encoding + const static bool log = false; + fEncodedData.reserve(info.size()*2); + bool done = false; + for (std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case REBASE_OPCODE_DONE: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DONE()\n"); + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(REBASE_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + fEncodedData.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + fEncodedData.append_uleb128(it->operand2); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_IMM_TIMES(%lld)\n", it->operand1); + fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%lld)\n", it->operand1); + fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + fEncodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + fEncodedData.append_uleb128(it->operand1); + fEncodedData.append_uleb128(it->operand2); + break; + } + } + + + // align to pointer size + fEncodedData.pad_to_size(sizeof(pint_t)); + + if (log) fprintf(stderr, "total rebase info size = %ld\n", fEncodedData.size()); +} + + +struct binding_tmp +{ + binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) + : opcode(op), operand1(p1), operand2(p2), name(s) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + const char* name; +}; + + + +template +void CompressedBindingInfoLinkEditAtom::encode() +{ + // sort by library, symbol, type, then address + const std::vector& segments = fWriter.fSegmentInfos; + std::vector& info = fWriter.fBindingInfo; + std::sort(info.begin(), info.end()); + + // convert to temp encoding that can be more easily optimized + std::vector mid; + const SegmentInfo* currentSegment = NULL; + unsigned int segIndex = 0; + int ordinal = 0x80000000; + const char* symbolName = NULL; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + int64_t addend = 0; + for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { + if ( ordinal != it->fLibraryOrdinal ) { + if ( it->fLibraryOrdinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->fLibraryOrdinal)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->fLibraryOrdinal)); + } + ordinal = it->fLibraryOrdinal; + } + if ( symbolName != it->fSymbolName ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName)); + symbolName = it->fSymbolName; + } + if ( type != it->fType ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType)); + type = it->fType; + } + if ( address != it->fAddress ) { + if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) + || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) + || (it->fAddress < address) ) { + segIndex = 0; + for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { + if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { + currentSegment = *segit; + break; + } + ++segIndex; + } + mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); + } + address = it->fAddress; + } + if ( addend != it->fAddend ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend)); + addend = it->fAddend; + } + mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); + address += sizeof(pint_t); + } + mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); + + + // optimize phase 1, combine bind/add pairs + binding_tmp* dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { + p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // convert to compressed encoding + const static bool log = false; + fEncodedData.reserve(info.size()*2); + bool done = false; + for (std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); + fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); + fEncodedData.append_string(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + fEncodedData.append_sleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + fEncodedData.append_uleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + fEncodedData.append_uleb128(it->operand1); + fEncodedData.append_uleb128(it->operand2); + break; + } + } + + // align to pointer size + fEncodedData.pad_to_size(sizeof(pint_t)); + + if (log) fprintf(stderr, "total binding info size = %ld\n", fEncodedData.size()); + +} + + + +struct WeakBindingSorter +{ + bool operator()(const BindingInfo& left, const BindingInfo& right) + { + // sort by symbol, type, address + if ( left.fSymbolName != right.fSymbolName ) + return ( strcmp(left.fSymbolName, right.fSymbolName) < 0 ); + if ( left.fType != right.fType ) + return (left.fType < right.fType); + return (left.fAddress < right.fAddress); + } +}; + + + +template +void CompressedWeakBindingInfoLinkEditAtom::encode() +{ + // add regular atoms that override a dylib's weak definitions + for(std::set::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin(); + it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) { + if ( fWriter.shouldExport(**it) ) + fWriter.fWeakBindingInfo.push_back(BindingInfo(0, (*it)->getName(), true, 0, 0)); + } + + // add all exported weak definitions + for(std::vector::iterator it = fWriter.fAllAtoms->begin(); it != fWriter.fAllAtoms->end(); ++it) { + ObjectFile::Atom* atom = *it; + if ( (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) && fWriter.shouldExport(*atom) ) { + fWriter.fWeakBindingInfo.push_back(BindingInfo(0, atom->getName(), false, 0, 0)); + } + } + + // sort by symbol, type, address + const std::vector& segments = fWriter.fSegmentInfos; + std::vector& info = fWriter.fWeakBindingInfo; + if ( info.size() == 0 ) + return; + std::sort(info.begin(), info.end(), WeakBindingSorter()); + + // convert to temp encoding that can be more easily optimized + std::vector mid; + mid.reserve(info.size()); + const SegmentInfo* currentSegment = NULL; + unsigned int segIndex = 0; + const char* symbolName = NULL; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + int64_t addend = 0; + for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { + if ( symbolName != it->fSymbolName ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName)); + symbolName = it->fSymbolName; + } + if ( it->fType != 0 ) { + if ( type != it->fType ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType)); + type = it->fType; + } + if ( address != it->fAddress ) { + // non weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND + if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) + || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) ) { + segIndex = 0; + for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { + if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { + currentSegment = *segit; + break; + } + ++segIndex; + } + mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); + } + address = it->fAddress; + } + if ( addend != it->fAddend ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend)); + addend = it->fAddend; + } + mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); + address += sizeof(pint_t); + } + } + mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); + + + // optimize phase 1, combine bind/add pairs + binding_tmp* dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + } + dst->opcode = BIND_OPCODE_DONE; + + + // convert to compressed encoding + const static bool log = false; + fEncodedData.reserve(info.size()*2); + bool done = false; + for (std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); + fEncodedData.append_byte(BIND_OPCODE_DONE); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); + fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); + fEncodedData.append_string(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + fEncodedData.append_sleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + fEncodedData.append_uleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + fEncodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + fEncodedData.append_uleb128(it->operand1); + fEncodedData.append_uleb128(it->operand2); + break; + } + } + + // align to pointer size + fEncodedData.pad_to_size(sizeof(pint_t)); + + if (log) fprintf(stderr, "total weak binding info size = %ld\n", fEncodedData.size()); + +} + +template +void CompressedLazyBindingInfoLinkEditAtom::encode() +{ + // stream all lazy bindings and record start offsets + const SegmentInfo* currentSegment = NULL; + uint8_t segIndex = 0; + const std::vector& segments = fWriter.fSegmentInfos; + std::vector*>& allLazys = fWriter.fAllSynthesizedLazyPointers; + for (typename std::vector*>::iterator it = allLazys.begin(); it != allLazys.end(); ++it) { + LazyPointerAtom* lazyPointerAtom = *it; + ObjectFile::Atom* lazyPointerTargetAtom = lazyPointerAtom->getTarget(); + + // skip lazy pointers that are bound non-lazily because they are coalesced + if ( ! fWriter.targetRequiresWeakBinding(*lazyPointerTargetAtom) ) { + // record start offset for use by stub helper + lazyPointerAtom->setLazyBindingInfoOffset(fEncodedData.size()); + + // write address to bind + pint_t address = lazyPointerAtom->getAddress(); + if ( (currentSegment == NULL) || (address < currentSegment->fBaseAddress) + || ((currentSegment->fBaseAddress+currentSegment->fSize) <= address) ) { + segIndex = 0; + for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { + if ( ((*segit)->fBaseAddress <= address) && (address < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { + currentSegment = *segit; + break; + } + ++segIndex; + } + } + fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); + fEncodedData.append_uleb128(lazyPointerAtom->getAddress() - currentSegment->fBaseAddress); + + // write ordinal + int ordinal = fWriter.compressedOrdinalForImortedAtom(lazyPointerTargetAtom); + if ( ordinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (ordinal & BIND_IMMEDIATE_MASK) ); + } + else if ( ordinal <= 15 ) { + // small ordinals are encoded in opcode + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | ordinal); + } + else { + fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + fEncodedData.append_uleb128(ordinal); + } + // write symbol name + bool weak_import = fWriter.fWeakImportMap[lazyPointerTargetAtom]; + if ( weak_import ) + fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | BIND_SYMBOL_FLAGS_WEAK_IMPORT); + else + fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); + fEncodedData.append_string(lazyPointerTargetAtom->getName()); + // write do bind + fEncodedData.append_byte(BIND_OPCODE_DO_BIND); + fEncodedData.append_byte(BIND_OPCODE_DONE); + } + } + // align to pointer size + fEncodedData.pad_to_size(sizeof(pint_t)); + + //fprintf(stderr, "lazy binding info size = %ld, for %ld entries\n", fEncodedData.size(), allLazys.size()); +} + +struct TrieEntriesSorter +{ + TrieEntriesSorter(Options& o) : fOptions(o) {} + + bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) + { + unsigned int leftOrder; + unsigned int rightOrder; + fOptions.exportedSymbolOrder(left.name, &leftOrder); + fOptions.exportedSymbolOrder(right.name, &rightOrder); + if ( leftOrder != rightOrder ) + return (leftOrder < rightOrder); + else + return (left.address < right.address); + } +private: + Options& fOptions; +}; + + +template +void CompressedExportInfoLinkEditAtom::encode() +{ + // make vector of mach_o::trie::Entry for all exported symbols + std::vector& exports = fWriter.fExportedAtoms; + uint64_t imageBaseAddress = fWriter.fMachHeaderAtom->getAddress(); + std::vector entries; + entries.reserve(exports.size()); + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + ObjectFile::Atom* atom = *it; + uint64_t flags = 0; + if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + uint64_t address = atom->getAddress() - imageBaseAddress; + if ( atom->isThumb() ) + address |= 1; + mach_o::trie::Entry entry; + entry.name = atom->getName(); + entry.flags = flags; + entry.address = address; + entries.push_back(entry); + } + + // sort vector by -exported_symbols_order, and any others by address + std::sort(entries.begin(), entries.end(), TrieEntriesSorter(fWriter.fOptions)); + + // create trie + mach_o::trie::makeTrie(entries, fEncodedData.bytes()); + + // align to pointer size + fEncodedData.pad_to_size(sizeof(pint_t)); +} + + + + + }; // namespace executable }; // namespace mach_o diff --git a/ld64/src/ObjectFile.h b/ld64/src/ld/ObjectFile.h similarity index 86% rename from ld64/src/ObjectFile.h rename to ld64/src/ld/ObjectFile.h index 1f42994..8b9a2d3 100644 --- a/ld64/src/ObjectFile.h +++ b/ld64/src/ld/ObjectFile.h @@ -62,14 +62,21 @@ class ReaderOptions { public: ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), - fLinkingMainExecutable(false), fSlowx86Stubs(false), - fForFinalLinkedImage(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), + fLinkingMainExecutable(false), + fForFinalLinkedImage(false), fNoEHLabels(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), - fImplicitlyLinkPublicDylibs(true), fLogObjectFiles(false), fLogAllFiles(false), + fImplicitlyLinkPublicDylibs(true), + fAddCompactUnwindEncoding(true), + fWarnCompactUnwind(false), + fRemoveDwarfUnwindIfCompactExists(false), + fMakeCompressedDyldInfo(false), + fAutoOrderInitializers(true), + fLogObjectFiles(false), fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), - fTraceOutputFile(NULL), fVersionMin(kMinUnset) {} + fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; - enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; + enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; + enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0 }; struct AliasPair { const char* realName; @@ -80,8 +87,8 @@ class ReaderOptions bool fLoadAllObjcObjectsFromArchives; bool fFlatNamespace; bool fLinkingMainExecutable; - bool fSlowx86Stubs; bool fForFinalLinkedImage; + bool fNoEHLabels; bool fForStatic; bool fForDyld; bool fMakeTentativeDefinitionsReal; @@ -90,13 +97,19 @@ class ReaderOptions bool fSetuidSafe; DebugInfoStripping fDebugInfoStripping; bool fImplicitlyLinkPublicDylibs; + bool fAddCompactUnwindEncoding; + bool fWarnCompactUnwind; + bool fRemoveDwarfUnwindIfCompactExists; + bool fMakeCompressedDyldInfo; + bool fAutoOrderInitializers; bool fLogObjectFiles; bool fLogAllFiles; bool fTraceDylibs; bool fTraceIndirectDylibs; bool fTraceArchives; const char* fTraceOutputFile; - VersionMin fVersionMin; + MacVersionMin fMacVersionMin; + IPhoneVersionMin fIPhoneVersionMin; std::vector fAliases; }; @@ -139,13 +152,15 @@ class Reader // For relocatable object files only virtual bool canScatterAtoms() { return true; } - virtual void optimize(std::vector&, std::vector&, + virtual bool optimize(const std::vector&, std::vector&, std::vector&, const std::set&, + std::vector&, uint32_t, ObjectFile::Reader* writer, + ObjectFile::Atom* entryPointAtom, const std::vector& llvmOptions, bool allGlobalsAReDeadStripRoots, int okind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) { } + bool pie, bool allowTextRelocs) { return false; } virtual bool hasLongBranchStubs() { return false; } // For Dynamic Libraries only @@ -161,6 +176,7 @@ class Reader virtual const char* parentUmbrella() { return NULL; } virtual std::vector* getAllowableClients() { return NULL; } virtual bool hasWeakExternals() { return false; } + virtual bool deadStrippable() { return false; } virtual bool isLazyLoadedDylib() { return false; } protected: @@ -211,6 +227,16 @@ struct Alignment uint16_t modulus; }; +struct UnwindInfo +{ + uint32_t startOffset; + uint32_t unwindInfo; + + typedef UnwindInfo* iterator; + +}; + + // // An atom is the fundamental unit of linking. A C function or global variable is an atom. // An atom has content and some attributes. The content of a function atom is the instructions @@ -254,6 +280,7 @@ class Atom public: enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; + enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType }; enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; virtual Reader* getFile() const = 0; @@ -262,6 +289,7 @@ class Atom virtual const char* getDisplayName() const = 0; virtual Scope getScope() const = 0; virtual DefinitionKind getDefinitionKind() const = 0; + virtual ContentType getContentType() const { return kUnclassifiedType; } virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; virtual bool dontDeadStrip() const = 0; virtual bool isZeroFill() const = 0; @@ -277,7 +305,11 @@ class Atom virtual Alignment getAlignment() const = 0; virtual void copyRawContent(uint8_t buffer[]) const = 0; virtual void setScope(Scope) = 0; - + virtual UnwindInfo::iterator beginUnwind() { return NULL; } + virtual UnwindInfo::iterator endUnwind() { return NULL; } + virtual Reference* getLSDA() { return NULL; } + virtual Reference* getFDE() { return NULL; } + virtual Atom* getPersonalityPointer() { return NULL; } uint64_t getSectionOffset() const { return fSectionOffset; } uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } @@ -336,6 +368,7 @@ class Reference virtual void setTarget(Atom&, uint64_t offset) = 0; virtual void setFromTarget(Atom&) = 0; virtual const char* getDescription() const = 0; + virtual bool isBranch() const { return false; } protected: Reference() {} diff --git a/ld64/FireOpal/src/OpaqueSection.hpp b/ld64/src/ld/OpaqueSection.hpp similarity index 100% rename from ld64/FireOpal/src/OpaqueSection.hpp rename to ld64/src/ld/OpaqueSection.hpp diff --git a/ld64/src/Options.cpp b/ld64/src/ld/Options.cpp similarity index 83% rename from ld64/src/Options.cpp rename to ld64/src/ld/Options.cpp index eb244ed..03969cd 100644 --- a/ld64/src/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,10 @@ extern void printLTOVersion(Options &opts); +// magic to place command line in crash reports +extern "C" char* __crashreporter_info__; +static char crashreporterBuffer[1000]; +char* __crashreporter_info__ = crashreporterBuffer; static bool sEmitWarnings = true; static const char* sWarningsSideFilePath = NULL; @@ -50,11 +55,11 @@ void warning(const char* format, ...) sWarningsSideFile = fopen(sWarningsSideFilePath, "a"); } va_start(list, format); - fprintf(stderr, "ld warning: "); + fprintf(stderr, "ld: warning: "); vfprintf(stderr, format, list); fprintf(stderr, "\n"); if ( sWarningsSideFile != NULL ) { - fprintf(sWarningsSideFile, "ld warning: "); + fprintf(sWarningsSideFile, "ld: warning: "); vfprintf(sWarningsSideFile, format, list); fprintf(sWarningsSideFile, "\n"); fflush(sWarningsSideFile); @@ -78,7 +83,7 @@ void throwf(const char* format, ...) Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), - fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), + fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace), fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0), fBaseWritableAddress(0), fSplitSegs(false), @@ -88,14 +93,16 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(32), + fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), + fMinimumHeaderPad(32), fSegmentAlignment(4096), fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), fSharedRegionEligible(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), - fUsingLazyDylibLinking(false), fEncryptable(true), fSaveTempFiles(false) + fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), + fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), fSaveTempFiles(false) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -228,11 +235,6 @@ Options::UndefinedTreatment Options::undefinedTreatment() return fUndefinedTreatment; } -ObjectFile::ReaderOptions::VersionMin Options::macosxVersionMin() -{ - return fReaderOptions.fVersionMin; -} - Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() { return fWeakReferenceMismatchTreatment; @@ -326,12 +328,14 @@ bool Options::allGlobalsAreDeadStripRoots() switch ( fOutputKind ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: + case Options::kPreload: // by default unused globals in a main executable are stripped return false; case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: + case Options::kKextBundle: return true; } return false; @@ -382,6 +386,96 @@ Options::DeadStripMode Options::deadStrip() return fDeadStrip; } +bool Options::hasExportedSymbolOrder() +{ + return (fExportSymbolsOrder.size() > 0); +} + +bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) +{ + NameToOrder::iterator pos = fExportSymbolsOrder.find(sym); + if ( pos != fExportSymbolsOrder.end() ) { + *order = pos->second; + return true; + } + else { + *order = 0xFFFFFFFF; + return false; + } +} + +void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping) +{ + // read in whole file + int fd = ::open(fileOfExports, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open -exported_symbols_order file: %s", fileOfExports); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) + throwf("can't process -exported_symbols_order file: %s", fileOfExports); + + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) + throwf("can't read -exported_symbols_order file: %s", fileOfExports); + + ::close(fd); + + // parse into symbols and add to hash_set + unsigned int count = 0; + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( (*s == '\n') || (*s == '\r') ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + orderMapping[symbolStart] = ++count; + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( (*s == '\n') || (*s == '\r') ) + state = lineStart; + break; + } + } + if ( state == inSymbol ) { + warning("missing line-end at end of file \"%s\"", fileOfExports); + int len = end-symbolStart+1; + char* temp = new char[len]; + strlcpy(temp, symbolStart, len); + + // remove any trailing spaces + char* last = &temp[len-2]; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + orderMapping[temp] = ++count; + } + + // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table +} + + bool Options::shouldExport(const char* symbolName) { switch (fExportMode) { @@ -803,14 +897,18 @@ void Options::loadFileList(const char* fileOfPaths) const char* comma = strrchr(fileOfPaths, ','); const char* prefix = NULL; if ( comma != NULL ) { - prefix = comma+1; - int realFileOfPathsLen = comma-fileOfPaths; - char realFileOfPaths[realFileOfPathsLen+1]; - strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); - realFileOfPaths[realFileOfPathsLen] = '\0'; - file = fopen(realFileOfPaths, "r"); - if ( file == NULL ) - throwf("-filelist file not found: %s\n", realFileOfPaths); + // -filelist fails with comma in path + file = fopen(fileOfPaths, "r"); + if ( file == NULL ) { + prefix = comma+1; + int realFileOfPathsLen = comma-fileOfPaths; + char realFileOfPaths[realFileOfPathsLen+1]; + strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); + realFileOfPaths[realFileOfPathsLen] = '\0'; + file = fopen(realFileOfPaths, "r"); + if ( file == NULL ) + throwf("-filelist file not found: %s\n", realFileOfPaths); + } } else { file = fopen(fileOfPaths, "r"); @@ -1131,25 +1229,25 @@ void Options::setMacOSXVersionMin(const char* version) switch ( num ) { case 0: case 1: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_1; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_1; break; case 2: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_2; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_2; break; case 3: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_3; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_3; break; case 4: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; break; case 5: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_5; break; case 6: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; break; default: - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_6; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; break; } } @@ -1163,21 +1261,54 @@ void Options::setIPhoneVersionMin(const char* version) if ( version == NULL ) throw "-iphoneos_version_min argument missing"; - if ( ((strncmp(version, "1.", 2) == 0) || (strncmp(version, "2.", 2) == 0)) && isdigit(version[2]) ) { + if ( strncmp(version, "1.", 2) == 0 ) { + warning("pre-2.0 iPhone OS version not supported"); + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + } + else if ( strncmp(version, "2.", 2) == 0 ) { int num = version[2] - '0'; switch ( num ) { + case 0: + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + break; + case 1: + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1; + break; case 2: - // TODO: store deployment version + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; + break; + default: + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; + break; + } + } + else if ( strncmp(version, "3.", 2) == 0 ) { + int num = version[2] - '0'; + switch ( num ) { + case 0: + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; break; default: + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; break; } } else { - warning("unknown option to -iphoneos_version_min, not 1.x or 2.x"); + warning("unknown option to -iphoneos_version_min, not 2.x or 3.x"); } } +bool Options::minOS(ObjectFile::ReaderOptions::MacVersionMin requiredMacMin, ObjectFile::ReaderOptions::IPhoneVersionMin requirediPhoneOSMin) +{ + if ( fReaderOptions.fMacVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { + return ( fReaderOptions.fMacVersionMin >= requiredMacMin ); + } + else { + return ( fReaderOptions.fIPhoneVersionMin >= requirediPhoneOSMin); + } +} + + void Options::setWeakReferenceMismatchTreatment(const char* treatment) { if ( treatment == NULL ) @@ -1361,6 +1492,9 @@ static const char* cstringSymbolName(const char* orderFileString) void Options::parseOrderFile(const char* path, bool cstring) { + // order files override auto-ordering + fReaderOptions.fAutoOrderInitializers = false; + // read in whole file int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) @@ -1608,9 +1742,10 @@ void Options::parse(int argc, const char* argv[]) // default } else if ( strcmp(arg, "-static") == 0 ) { - if ( fOutputKind != kObjectFile ) - fOutputKind = kStaticExecutable; fReaderOptions.fForStatic = true; + if ( fOutputKind != kObjectFile ) { + fOutputKind = kStaticExecutable; + } } else if ( strcmp(arg, "-dylib") == 0 ) { fOutputKind = kDynamicLibrary; @@ -1625,9 +1760,15 @@ void Options::parse(int argc, const char* argv[]) if ( fOutputKind != kStaticExecutable ) fOutputKind = kDynamicExecutable; } + else if ( strcmp(arg, "-preload") == 0 ) { + fOutputKind = kPreload; + } else if ( strcmp(arg, "-r") == 0 ) { fOutputKind = kObjectFile; } + else if ( strcmp(arg, "-kext") == 0 ) { + fOutputKind = kKextBundle; + } else if ( strcmp(arg, "-o") == 0 ) { fOutputFile = argv[++i]; } @@ -1675,6 +1816,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-ObjC") == 0 ) { fReaderOptions.fLoadAllObjcObjectsFromArchives = true; } + // Similar to -all_load, but for the following archive only. + else if ( strcmp(arg, "-force_load") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fForceLoad = true; + addLibrary(info); + } // Library versioning. else if ( (strcmp(arg, "-dylib_compatibility_version") == 0) || (strcmp(arg, "-compatibility_version") == 0)) { @@ -1724,9 +1871,9 @@ void Options::parse(int argc, const char* argv[]) if ( address == NULL ) throwf("%s missing

", arg); fBaseAddress = parseAddress(address); - uint64_t temp = (fBaseAddress+4095) & (-4096); // page align + uint64_t temp = ((fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment)); if ( fBaseAddress != temp ) { - warning("-seg1addr not page aligned, rounding up"); + warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment); fBaseAddress = temp; } } @@ -1925,8 +2072,16 @@ void Options::parse(int argc, const char* argv[]) } // Aligns all segments to the power of 2 boundary specified. else if ( strcmp(arg, "-segalign") == 0 ) { - warnObsolete(arg); - ++i; + const char* size = argv[++i]; + if ( size == NULL ) + throw "-segalign missing "; + fSegmentAlignment = parseAddress(size); + uint8_t alignment = (uint8_t)__builtin_ctz(fSegmentAlignment); + uint32_t p2aligned = (1 << alignment); + if ( p2aligned != fSegmentAlignment ) { + warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned); + fSegmentAlignment = p2aligned; + } } // Puts a specified segment at a particular address that must // be a multiple of the segment alignment. @@ -2029,7 +2184,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-macosx_version_min") == 0 ) { setMacOSXVersionMin(argv[++i]); } - else if ( (strcmp(arg, "-aspen_version_min") == 0) || (strcmp(arg, "-iphone_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { + else if ( strcmp(arg, "-iphoneos_version_min") == 0 ) { setIPhoneVersionMin(argv[++i]); } else if ( strcmp(arg, "-multiply_defined") == 0 ) { @@ -2117,7 +2272,7 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { - // FIX FIX + fErrorOnOtherArchFiles = true; } else if ( strcmp(arg, "-M") == 0 ) { // FIX FIX @@ -2274,7 +2429,7 @@ void Options::parse(int argc, const char* argv[]) fReadOnlyx86Stubs = true; } else if ( strcmp(arg, "-slow_stubs") == 0 ) { - fReaderOptions.fSlowx86Stubs = true; + warnObsolete(arg); } else if ( strcmp(arg, "-map") == 0 ) { fMapPath = argv[++i]; @@ -2311,12 +2466,50 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_encryption") == 0 ) { fEncryptable = false; } + else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { + fReaderOptions.fAddCompactUnwindEncoding = false; + } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = argv[++i]; if ( opts == NULL ) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); } + else if ( strcmp(arg, "-no_order_inits") == 0 ) { + fReaderOptions.fAutoOrderInitializers = false; + } + else if ( strcmp(arg, "-no_order_data") == 0 ) { + fOrderData = false; + } + else if ( strcmp(arg, "-seg_page_size") == 0 ) { + SegmentSize seg; + seg.name = argv[++i]; + if ( (seg.name == NULL) || (argv[i+1] == NULL) ) + throw "-seg_page_size missing segName Adddress"; + seg.size = parseAddress(argv[++i]); + uint64_t temp = seg.size & (-4096); // page align + if ( (seg.size != temp) ) + warning("-seg_page_size %s not 4K aligned, rounding down", seg.name); + fCustomSegmentSizes.push_back(seg); + } + else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) { + fMarkDeadStrippableDylib = true; + } + else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { + loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); + } + else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { + fMakeCompressedDyldInfo = false; + } + else if ( strcmp(arg, "-no_eh_labels") == 0 ) { + fReaderOptions.fNoEHLabels = true; + } + else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { + fReaderOptions.fWarnCompactUnwind = true; + } + else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { + fAllowCpuSubtypeMismatches = true; + } else { throwf("unknown option: %s", arg); } @@ -2388,14 +2581,15 @@ void Options::buildSearchPaths(int argc, const char* argv[]) sEmitWarnings = false; } } + int standardLibraryPathsStartIndex = libraryPaths.size(); + int standardFrameworkPathsStartIndex = frameworkPaths.size(); if ( addStandardLibraryDirectories ) { libraryPaths.push_back("/usr/lib"); libraryPaths.push_back("/usr/local/lib"); frameworkPaths.push_back("/Library/Frameworks/"); frameworkPaths.push_back("/System/Library/Frameworks/"); - // remove /Network from default search path - //frameworkPaths.push_back("/Network/Library/Frameworks/"); + // remove /Network/Library/Frameworks from default search path } // Support for configure based hacks @@ -2408,7 +2602,8 @@ void Options::buildSearchPaths(int argc, const char* argv[]) // now merge sdk and library paths to make real search paths fLibrarySearchPaths.reserve(libraryPaths.size()*(fSDKPaths.size()+1)); - for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); it++) { + int libIndex = 0; + for (std::vector::iterator it = libraryPaths.begin(); it != libraryPaths.end(); ++it, ++libIndex) { const char* libDir = *it; bool sdkOverride = false; if ( libDir[0] == '/' ) { @@ -2419,7 +2614,6 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } const int libDirLen = strlen(libDir); for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - // ??? Should be using string here. const char* sdkDir = *sdkit; const int sdkDirLen = strlen(sdkDir); char newPath[libDirLen + sdkDirLen+4]; @@ -2434,13 +2628,21 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } } } - if ( !sdkOverride ) - fLibrarySearchPaths.push_back(libDir); + if ( !sdkOverride ) { + if ( (libIndex >= standardLibraryPathsStartIndex) && (fSDKPaths.size() == 1) ) { + // -syslibroot should skip standard search paths not in the SDK + // if one SDK is specified and a standard library path is not in the SDK, don't use it + } + else { + fLibrarySearchPaths.push_back(libDir); + } + } } // now merge sdk and framework paths to make real search paths fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); - for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); it++) { + int frameIndex = 0; + for (std::vector::iterator it = frameworkPaths.begin(); it != frameworkPaths.end(); ++it, ++frameIndex) { const char* frameworkDir = *it; bool sdkOverride = false; if ( frameworkDir[0] == '/' ) { @@ -2451,7 +2653,6 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } const int frameworkDirLen = strlen(frameworkDir); for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - // ??? Should be using string here const char* sdkDir = *sdkit; const int sdkDirLen = strlen(sdkDir); char newPath[frameworkDirLen + sdkDirLen+4]; @@ -2466,8 +2667,15 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } } } - if ( !sdkOverride ) - fFrameworkSearchPaths.push_back(frameworkDir); + if ( !sdkOverride ) { + if ( (frameIndex >= standardFrameworkPathsStartIndex) && (fSDKPaths.size() == 1) ) { + // -syslibroot should skip standard search paths not in the SDK + // if one SDK is specified and a standard library path is not in the SDK, don't use it + } + else { + fFrameworkSearchPaths.push_back(frameworkDir); + } + } } if ( fVerbose ) { @@ -2515,7 +2723,16 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_NO_ENCRYPT") != NULL) fEncryptable = false; - + + if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL) + fAllowCpuSubtypeMismatches = true; + + // for now disable compressed linkedit functionality + if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) { + fMakeCompressedDyldInfo = false; + fMakeClassicDyldInfo = true; + } + sWarningsSideFilePath = getenv("LD_WARN_FILE"); } @@ -2546,9 +2763,11 @@ void Options::parsePostCommandLineEnvironmentSettings() case Options::kDynamicBundle: fDeadStrip = kDeadStripOn; break; + case Options::kPreload: case Options::kObjectFile: case Options::kDyld: case Options::kStaticExecutable: + case Options::kKextBundle: break; } } @@ -2569,65 +2788,101 @@ void Options::reconfigureDefaults() case Options::kDyld: fReaderOptions.fForDyld = true; fReaderOptions.fForFinalLinkedImage = true; + fReaderOptions.fNoEHLabels = true; break; case Options::kDynamicLibrary: case Options::kDynamicBundle: + case Options::kKextBundle: fReaderOptions.fForFinalLinkedImage = true; + fReaderOptions.fNoEHLabels = true; break; case Options::kDynamicExecutable: case Options::kStaticExecutable: + case Options::kPreload: fReaderOptions.fLinkingMainExecutable = true; fReaderOptions.fForFinalLinkedImage = true; + fReaderOptions.fNoEHLabels = true; break; } // set default min OS version - if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) { - // if -macosx_version_min not used, try environment variable - const char* envVers = getenv("MACOSX_DEPLOYMENT_TARGET"); - if ( envVers != NULL ) - setMacOSXVersionMin(envVers); - // if -macosx_version_min and environment variable not used assume current OS version - if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::kMinUnset ) - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; // FIX FIX, this really should be a check of the OS version the linker is running on + if ( (fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::kMinMacVersionUnset) + && (fReaderOptions.fIPhoneVersionMin == ObjectFile::ReaderOptions::kMinIPhoneVersionUnset) ) { + // if neither -macosx_version_min nor -iphoneos_version_min used, try environment variables + const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + if ( macVers != NULL ) + setMacOSXVersionMin(macVers); + else if ( iPhoneVers != NULL ) + setIPhoneVersionMin(iPhoneVers); + else { + // if still nothing, set default based on architecture + switch ( fArchitecture ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + case CPU_TYPE_POWERPC: + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; // FIX FIX, this really should be a check of the OS version the linker is running o + break; + case CPU_TYPE_ARM: + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + break; + } + } } + // adjust min based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { //warning("-macosx_version_min should be 10.4 or later for i386"); - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; } break; case CPU_TYPE_POWERPC64: - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { //warning("-macosx_version_min should be 10.4 or later for ppc64"); - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; } break; case CPU_TYPE_X86_64: - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); - fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; } break; } + + // adjust kext type based on architecture + if ( fOutputKind == kKextBundle ) { + switch ( fArchitecture ) { + case CPU_TYPE_X86_64: + // x86_64 uses new MH_KEXT_BUNDLE type + fMakeClassicDyldInfo = true; + fMakeCompressedDyldInfo = false; + fAllowTextRelocs = true; + fUndefinedTreatment = kUndefinedDynamicLookup; + break; + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + case CPU_TYPE_ARM: + // use .o files + fOutputKind = kObjectFile; + break; + } + } - // disable implicit dylibs when targetting 10.3 + // disable implicit dylibs when targeting 10.3 // add option to disable implicit load commands for indirectly used public dylibs - if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_3 ) + if ( !minOS(ObjectFile::ReaderOptions::k10_4, ObjectFile::ReaderOptions::k2_0) ) fReaderOptions.fImplicitlyLinkPublicDylibs = false; - // determine if info for shared region should be added - if ( fOutputKind == Options::kDynamicLibrary ) { - if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) - if ( fArchitecture != CPU_TYPE_ARM ) - fSharedRegionEligible = true; - } + // allow build system to force linker to ignore -prebind + if ( getenv("LD_FORCE_NO_PREBIND") != NULL ) + fPrebind = false; - // allow build system to force linker to ignore seg_addr_table + // allow build system to force linker to ignore -seg_addr_table if ( getenv("LD_FORCE_NO_SEG_ADDR_TABLE") != NULL ) fSegAddrTablePath = NULL; @@ -2636,8 +2891,11 @@ void Options::reconfigureDefaults() parseSegAddrTable(fSegAddrTablePath, this->installPath()); // HACK to support seg_addr_table entries that are physical paths instead of install paths if ( fBaseAddress == 0 ) { - if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) + if ( strcmp(this->installPath(), "/usr/lib/libstdc++.6.dylib") == 0 ) { parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.4.dylib"); + if ( fBaseAddress == 0 ) + parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libstdc++.6.0.9.dylib"); + } else if ( strcmp(this->installPath(), "/usr/lib/libz.1.dylib") == 0 ) parseSegAddrTable(fSegAddrTablePath, "/usr/lib/libz.1.2.3.dylib"); @@ -2676,17 +2934,21 @@ void Options::reconfigureDefaults() } } + // -r implies no prebinding for all architectures + if ( fOutputKind == Options::kObjectFile ) + fPrebind = false; + // disable prebinding depending on arch and min OS version if ( fPrebind ) { switch ( fArchitecture ) { case CPU_TYPE_POWERPC: case CPU_TYPE_I386: - if ( fReaderOptions.fVersionMin == ObjectFile::ReaderOptions::k10_4 ) { + if ( fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::k10_4 ) { // in 10.4 only split seg dylibs are prebound if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) fPrebind = false; } - else if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { + else if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { // in 10.5 nothing is prebound fPrebind = false; } @@ -2701,6 +2963,8 @@ void Options::reconfigureDefaults() case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: // disable prebinding for everything else fPrebind = false; break; @@ -2721,6 +2985,8 @@ void Options::reconfigureDefaults() case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: // disable prebinding for everything else fPrebind = false; break; @@ -2733,29 +2999,132 @@ void Options::reconfigureDefaults() if ( fSplitSegs && !fPrebind ) fSplitSegs = false; + // determine if info for shared region should be added + if ( fOutputKind == Options::kDynamicLibrary ) { + if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_0) ) + if ( !fPrebind ) + if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) + || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) + fSharedRegionEligible = true; + } + // figure out if module table is needed for compatibility with old ld/dyld if ( fOutputKind == Options::kDynamicLibrary ) { switch ( fArchitecture ) { case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table - if ( fReaderOptions.fVersionMin <= ObjectFile::ReaderOptions::k10_5 ) + if ( fReaderOptions.fMacVersionMin <= ObjectFile::ReaderOptions::k10_5 ) fNeedsModuleTable = true; break; case CPU_TYPE_ARM: - fNeedsModuleTable = true; // redo_prebinding requires a module table + if ( fPrebind ) + fNeedsModuleTable = true; // redo_prebinding requires a module table break; } } // -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; - + fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + + // choose how to process unwind info + switch ( fArchitecture ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + fReaderOptions.fAddCompactUnwindEncoding = false; + break; + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDynamicExecutable: + //if ( fReaderOptions.fAddCompactUnwindEncoding && (fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_6) ) + // fReaderOptions.fRemoveDwarfUnwindIfCompactExists = true; + break; + } + break; + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + case CPU_TYPE_ARM: + fReaderOptions.fAddCompactUnwindEncoding = false; + fReaderOptions.fRemoveDwarfUnwindIfCompactExists = false; + break; + case 0: + // if -arch is missing, assume we don't want compact unwind info + fReaderOptions.fAddCompactUnwindEncoding = false; + break; + } + // only ARM main executables can be encrypted if ( fOutputKind != Options::kDynamicExecutable ) fEncryptable = false; if ( fArchitecture != CPU_TYPE_ARM ) fEncryptable = false; + + // don't move inits in dyld because dyld wants certain + // entries point at stable locations at the start of __text + if ( fOutputKind == Options::kDyld ) + fReaderOptions.fAutoOrderInitializers = false; + + + // disable __data ordering for some output kinds + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kDyld: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + fOrderData = false; + break; + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + } + + // only use compressed LINKEDIT for x86_64 and i386 + if ( fMakeCompressedDyldInfo ) { + switch (fArchitecture) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_6 ) + fMakeClassicDyldInfo = false; + else if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_5 ) + fMakeCompressedDyldInfo = false; + break; + case CPU_TYPE_POWERPC: + case CPU_TYPE_ARM: + case CPU_TYPE_POWERPC64: + default: + fMakeCompressedDyldInfo = false; + } + } + + // only use compressed LINKEDIT for final linked images + if ( fMakeCompressedDyldInfo ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kPreload: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kKextBundle: + fMakeCompressedDyldInfo = false; + break; + } + } + fReaderOptions.fMakeCompressedDyldInfo = fMakeCompressedDyldInfo; + + // only ARM enforces that cpu-sub-types must match + if ( fArchitecture != CPU_TYPE_ARM ) + fAllowCpuSubtypeMismatches = true; } void Options::checkIllegalOptionCombinations() @@ -2876,6 +3245,8 @@ void Options::checkIllegalOptionCombinations() case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: throw "-stack_size option can only be used when linking a main executable"; } } @@ -2891,6 +3262,8 @@ void Options::checkIllegalOptionCombinations() case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: throw "-allow_stack_execute option can only be used when linking a main executable"; } } @@ -2905,6 +3278,8 @@ void Options::checkIllegalOptionCombinations() case Options::kDynamicLibrary: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: throw "-client_name can only be used with -bundle"; } } @@ -3000,7 +3375,7 @@ void Options::checkIllegalOptionCombinations() break; case CPU_TYPE_POWERPC64: // first 4GB for ppc64 on 10.5 - if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) fZeroPageSize = 0x100000000ULL; else fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page @@ -3024,6 +3399,8 @@ void Options::checkIllegalOptionCombinations() case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: if ( fZeroPageSize != 0 ) throw "-pagezero_size option can only be used when linking a main executable"; } @@ -3035,7 +3412,7 @@ void Options::checkIllegalOptionCombinations() // can't use -rpath unless targeting 10.5 or later if ( fRPaths.size() > 0 ) { - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) + if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; switch ( fOutputKind ) { case Options::kDynamicExecutable: @@ -3045,19 +3422,49 @@ void Options::checkIllegalOptionCombinations() case Options::kStaticExecutable: case Options::kObjectFile: case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: throw "-rpath can only be used when creating a dynamic final linked image"; } } // check -pie is only used when building a dynamic main executable for 10.5 if ( fPositionIndependentExecutable ) { - if ( fOutputKind != Options::kDynamicExecutable ) - throw "-pie can only be used when linking a main executable"; - if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_5 ) + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kPreload: + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + warning("-pie being ignored. It is only used when linking a main executable"); + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kKextBundle: + throw "-pie can only be used when linking a main executable"; + } + if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) throw "-pie can only be used when targeting Mac OS X 10.5 or later"; } -} - + + // check -read_only_relocs is not used with x86_64 + if ( fAllowTextRelocs ) { + if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind != kKextBundle) ) { + warning("-read_only_relocs cannot be used with x86_64"); + fAllowTextRelocs = false; + } + } + + // check -mark_auto_dead_strip is only used with dylibs + if ( fMarkDeadStrippableDylib ) { + if ( fOutputKind != Options::kDynamicLibrary ) { + warning("-mark_auto_dead_strip can only be used when creating a dylib"); + fMarkDeadStrippableDylib = false; + } + } + +} void Options::checkForClassic(int argc, const char* argv[]) @@ -3066,9 +3473,16 @@ void Options::checkForClassic(int argc, const char* argv[]) bool archFound = false; bool staticFound = false; bool dtraceFound = false; + bool kextFound = false; bool rFound = false; bool creatingMachKernel = false; bool newLinker = false; + + // build command line buffer in case ld crashes + for(int i=1; i < argc; ++i) { + strlcat(crashreporterBuffer, argv[i], 1000); + strlcat(crashreporterBuffer, " ", 1000); + } for(int i=0; i < argc; ++i) { const char* arg = argv[i]; @@ -3080,6 +3494,9 @@ void Options::checkForClassic(int argc, const char* argv[]) else if ( strcmp(arg, "-static") == 0 ) { staticFound = true; } + else if ( strcmp(arg, "-kext") == 0 ) { + kextFound = true; + } else if ( strcmp(arg, "-dtrace") == 0 ) { dtraceFound = true; } @@ -3113,20 +3530,27 @@ void Options::checkForClassic(int argc, const char* argv[]) case CPU_TYPE_I386: case CPU_TYPE_ARM: // if ( staticFound && (rFound || !creatingMachKernel) ) { - if ( staticFound && !newLinker ) { + 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 -aspen_version_min, so change + // ld_classic does not support -iphoneos_version_min, so change for(int j=0; j < argc; ++j) { - if ( (strcmp(argv[j], "-aspen_version_min") == 0) - || (strcmp(argv[j], "-iphone_version_min") == 0) - || (strcmp(argv[j], "-iphoneos_version_min") == 0) ) { + if ( strcmp(argv[j], "-iphoneos_version_min") == 0) { argv[j] = "-macosx_version_min"; if ( j < argc-1 ) argv[j+1] = "10.5"; break; } } + // ld classic does not understand -kext (change to -static -r) + if ( kextFound ) { + for(int j=0; j < argc; ++j) { + if ( strcmp(argv[j], "-kext") == 0) + argv[j] = "-r"; + else if ( strcmp(argv[j], "-dynamic") == 0) + argv[j] = "-static"; + } + } this->gotoClassicLinker(argc, argv); } } @@ -3144,6 +3568,16 @@ void Options::checkForClassic(int argc, const char* argv[]) void Options::gotoClassicLinker(int argc, const char* argv[]) { argv[0] = "ld_classic"; + char path[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "ld_classic"); + execvp(path, (char**)argv); + } + } + // in case of error in above, try searching for ld_classic via PATH execvp(argv[0], (char**)argv); fprintf(stderr, "can't exec ld_classic\n"); exit(1); diff --git a/ld64/FireOpal/src/Options.h b/ld64/src/ld/Options.h similarity index 86% rename from ld64/FireOpal/src/Options.h rename to ld64/src/ld/Options.h index 4bbcffe..9b8890e 100644 --- a/ld64/FireOpal/src/Options.h +++ b/ld64/src/ld/Options.h @@ -31,23 +31,28 @@ #include #include +#include #include "ObjectFile.h" extern void throwf (const char* format, ...) __attribute__ ((noreturn)); extern void warning(const char* format, ...); -class DynamicLibraryOptions +class LibraryOptions { public: - DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false) {} - + LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false), fForceLoad(false) {} + // for dynamic libraries bool fWeakImport; bool fReExport; bool fBundleLoader; bool fLazyLoad; + // for static libraries + bool fForceLoad; }; + + // // The public interface to the Options class is the abstract representation of what work the linker // should do. @@ -63,7 +68,7 @@ class Options Options(int argc, const char* argv[]); ~Options(); - enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld }; + enum OutputKind { kDynamicExecutable, kStaticExecutable, kDynamicLibrary, kDynamicBundle, kObjectFile, kDyld, kPreload, kKextBundle }; enum NameSpace { kTwoLevelNameSpace, kFlatNameSpace, kForceFlatNameSpace }; // Standard treatment for many options. enum Treatment { kError, kWarning, kSuppress, kNULL, kInvalid }; @@ -79,7 +84,7 @@ class Options const char* path; uint64_t fileLen; time_t modTime; - DynamicLibraryOptions options; + LibraryOptions options; }; struct ExtraSection { @@ -106,6 +111,11 @@ class Options uint64_t address; }; + struct SegmentSize { + const char* name; + uint64_t size; + }; + struct SegmentProtect { const char* name; uint32_t max; @@ -125,6 +135,7 @@ class Options cpu_type_t architecture() { return fArchitecture; } bool preferSubArchitecture() { return fHasPreferredSubType; } cpu_subtype_t subArchitecture() { return fSubArchitecture; } + bool allowSubArchitectureMismatches() { return fAllowCpuSubtypeMismatches; } OutputKind outputKind(); bool prebind(); bool bindAtLoad(); @@ -139,8 +150,8 @@ class Options bool keepPrivateExterns(); // only for kObjectFile bool needsModuleTable(); // only for kDynamicLibrary bool interposable(const char* name); - bool hasExportRestrictList(); - bool hasExportMaskList(); + bool hasExportRestrictList(); // -exported_symbol or -unexported_symbol + bool hasExportMaskList(); // just -exported_symbol bool hasWildCardExportRestrictList(); bool allGlobalsAreDeadStripRoots(); bool shouldExport(const char*); @@ -150,7 +161,9 @@ class Options bool traceArchives(); DeadStripMode deadStrip(); UndefinedTreatment undefinedTreatment(); - ObjectFile::ReaderOptions::VersionMin macosxVersionMin(); + ObjectFile::ReaderOptions::MacVersionMin macosxVersionMin() { return fReaderOptions.fMacVersionMin; } + ObjectFile::ReaderOptions::IPhoneVersionMin iphoneOSVersionMin() { return fReaderOptions.fIPhoneVersionMin; } + bool minOS(ObjectFile::ReaderOptions::MacVersionMin mac, ObjectFile::ReaderOptions::IPhoneVersionMin iPhoneOS); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); @@ -167,6 +180,7 @@ class Options std::vector& initialUndefines(); bool printWhyLive(const char* name); uint32_t minimumHeaderPad(); + uint64_t segmentAlignment() { return fSegmentAlignment; } bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; } std::vector& extraSections(); std::vector& sectionAlignments(); @@ -188,11 +202,11 @@ class Options bool splitSeg() { return fSplitSegs; } uint64_t baseWritableAddress() { return fBaseWritableAddress; } std::vector& customSegmentAddresses() { return fCustomSegmentAddresses; } + std::vector& customSegmentSizes() { return fCustomSegmentSizes; } std::vector& customSegmentProtections() { return fCustomSegmentProtections; } bool saveTempFiles() { return fSaveTempFiles; } const std::vector& rpaths() { return fRPaths; } bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; } - bool slowx86Stubs() { return fReaderOptions.fSlowx86Stubs; } std::vector& dylibOverrides() { return fDylibOverrides; } const char* generatedMapPath() { return fMapPath; } bool positionIndependentExecutable() { return fPositionIndependentExecutable; } @@ -207,15 +221,24 @@ class Options bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; } bool verbose() { return fVerbose; } bool makeEncryptable() { return fEncryptable; } + bool needsUnwindInfoSection() { return fReaderOptions.fAddCompactUnwindEncoding; } std::vector& llvmOptions() { return fLLVMOptions; } - + bool makeClassicDyldInfo() { return fMakeClassicDyldInfo; } + bool makeCompressedDyldInfo() { return fMakeCompressedDyldInfo; } + bool hasExportedSymbolOrder(); + bool exportedSymbolOrder(const char* sym, unsigned int* order); + bool orderData() { return fOrderData; } + bool errorOnOtherArchFiles() { return fErrorOnOtherArchFiles; } + bool markAutoDeadStripDylib() { return fMarkDeadStrippableDylib; } + bool removeEHLabels() { return fReaderOptions.fNoEHLabels; } + private: class CStringEquals { public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - + typedef __gnu_cxx::hash_map, CStringEquals> NameToOrder; typedef __gnu_cxx::hash_set, CStringEquals> NameSet; enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; @@ -272,6 +295,7 @@ class Options void addLibrary(const FileInfo& info); void warnObsolete(const char* arg); uint32_t parseProtection(const char* prot); + void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping); ObjectFile::ReaderOptions fReaderOptions; @@ -286,6 +310,7 @@ class Options bool fKeepPrivateExterns; bool fNeedsModuleTable; bool fIgnoreOtherArchFiles; + bool fErrorOnOtherArchFiles; bool fForceSubtypeAll; InterposeMode fInterposeMode; DeadStripMode fDeadStrip; @@ -301,6 +326,7 @@ class Options SetWithWildcards fExportSymbols; SetWithWildcards fDontExportSymbols; SetWithWildcards fInterposeList; + NameToOrder fExportSymbolsOrder; ExportMode fExportMode; LibrarySearchMode fLibrarySearchMode; UndefinedTreatment fUndefinedTreatment; @@ -324,6 +350,7 @@ class Options uint64_t fStackAddr; bool fExecutableStack; uint32_t fMinimumHeaderPad; + uint64_t fSegmentAlignment; CommonsMode fCommonsMode; UUIDMode fUUIDMode; SetWithWildcards fLocalSymbolsIncluded; @@ -347,6 +374,12 @@ class Options bool fWarnTextRelocs; bool fUsingLazyDylibLinking; bool fEncryptable; + bool fOrderData; + bool fMarkDeadStrippableDylib; + bool fMakeClassicDyldInfo; + bool fMakeCompressedDyldInfo; + bool fNoEHLabels; + bool fAllowCpuSubtypeMismatches; std::vector fInitialUndefines; NameSet fAllowedUndefined; NameSet fWhyLive; @@ -354,6 +387,7 @@ class Options std::vector fSectionAlignments; std::vector fOrderedSymbols; std::vector fCustomSegmentAddresses; + std::vector fCustomSegmentSizes; std::vector fCustomSegmentProtections; std::vector fDylibOverrides; std::vector fLLVMOptions; diff --git a/ld64/FireOpal/src/SectCreate.h b/ld64/src/ld/SectCreate.h similarity index 100% rename from ld64/FireOpal/src/SectCreate.h rename to ld64/src/ld/SectCreate.h diff --git a/ld64/src/debugline.c b/ld64/src/ld/debugline.c similarity index 99% rename from ld64/src/debugline.c rename to ld64/src/ld/debugline.c index ff0e1d9..971e616 100644 --- a/ld64/src/debugline.c +++ b/ld64/src/ld/debugline.c @@ -206,7 +206,8 @@ line_open (const uint8_t * debug_line, size_t debug_line_size, lnd = malloc (sizeof (struct line_reader_data)); if (! lnd) - goto error; + return NULL; + bzero(lnd, sizeof(struct line_reader_data)); lnd->little_endian = little_endian; lnd->cpos = debug_line; diff --git a/ld64/FireOpal/src/debugline.h b/ld64/src/ld/debugline.h similarity index 100% rename from ld64/FireOpal/src/debugline.h rename to ld64/src/ld/debugline.h diff --git a/ld64/FireOpal/src/dwarf2.h b/ld64/src/ld/dwarf2.h similarity index 97% rename from ld64/FireOpal/src/dwarf2.h rename to ld64/src/ld/dwarf2.h index 530b465..7a7b4f2 100644 --- a/ld64/FireOpal/src/dwarf2.h +++ b/ld64/src/ld/dwarf2.h @@ -24,6 +24,9 @@ which is Copyright (c) 2005 Free Standards Group, and Copyright (c) 1992, 1993 UNIX International, Inc. */ +#ifndef __DWARF2_H__ +#define __DWARF2_H__ + /* This is not a complete list. */ enum { DW_TAG_compile_unit = 17, @@ -83,3 +86,5 @@ enum { DW_LNE_set_address, DW_LNE_define_file }; + +#endif diff --git a/ld64/src/ld.cpp b/ld64/src/ld/ld.cpp similarity index 80% rename from ld64/src/ld.cpp rename to ld64/src/ld/ld.cpp index acd18c8..ded2a19 100644 --- a/ld64/src/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -1,5 +1,5 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -68,7 +68,6 @@ extern "C" double log2 ( double ); #include "LTOReader.hpp" #endif - #include "OpaqueSection.hpp" @@ -87,7 +86,7 @@ class CStringEquals class Section : public ObjectFile::Section { public: - static Section* find(const char* sectionName, const char* segmentName, bool zeroFill); + static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool createIfNeeded=true); static void assignIndexes(); const char* getName() { return fSectionName; } private: @@ -102,8 +101,8 @@ class Section : public ObjectFile::Section typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; //typedef std::map NameToSection; - const char* fSectionName; - const char* fSegmentName; + char fSectionName[18]; + char fSegmentName[18]; bool fZeroFill; static NameToSection fgMapping; @@ -116,13 +115,71 @@ std::vector Section::fgSections; Section::NameToOrdinal Section::fgSegmentDiscoverOrder; Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) - : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill) + : fZeroFill(zeroFill) { - this->fIndex = fgSections.size(); + strlcpy(fSectionName, sectionName, sizeof(fSectionName)); + strlcpy(fSegmentName, segmentName, sizeof(fSegmentName)); + + this->fIndex = fgSections.size() + 20; // room for 20 standard sections + // special placement of some sections + if ( strcmp(segmentName, "__TEXT") == 0 ) { + // sort unwind info to end of segment + if ( strcmp(sectionName, "__eh_frame") == 0 ) + this->fIndex = INT_MAX; + else if ( strcmp(sectionName, "__unwind_info") == 0 ) + this->fIndex = INT_MAX-1; + else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) + this->fIndex = INT_MAX-2; + } + else if ( strcmp(segmentName, "__DATA") == 0 ) { + // sort sections dyld will touch to start of segment + if ( strcmp(sectionName, "__dyld") == 0 ) + this->fIndex = 1; + else if ( strcmp(sectionName, "__program_vars") == 0 ) + this->fIndex = 1; + else if ( strcmp(sectionName, "__mod_init_func") == 0 ) + this->fIndex = 2; + else if ( strcmp(sectionName, "__nl_symbol_ptr") == 0 ) + this->fIndex = 3; + else if ( strcmp(sectionName, "__la_symbol_ptr") == 0 ) + this->fIndex = 4; + else if ( strcmp(sectionName, "__const") == 0 ) + this->fIndex = 5; + else if ( strcmp(sectionName, "__cfstring") == 0 ) + this->fIndex = 6; + else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) + this->fIndex = 7; + else if ( strcmp(sectionName, "__objc_data") == 0 ) + this->fIndex = 8; + else if ( strcmp(sectionName, "__objc_msgrefs") == 0 ) + this->fIndex = 9; + else if ( strcmp(sectionName, "__objc_protorefs") == 0 ) + this->fIndex = 10; + else if ( strcmp(sectionName, "__objc_selrefs") == 0 ) + this->fIndex = 11; + else if ( strcmp(sectionName, "__objc_classrefs") == 0 ) + this->fIndex = 12; + else if ( strcmp(sectionName, "__objc_superrefs") == 0 ) + this->fIndex = 13; + else if ( strcmp(sectionName, "__objc_const") == 0 ) + this->fIndex = 14; + else if ( strcmp(sectionName, "__objc_classlist") == 0 ) + this->fIndex = 15; + else if ( strcmp(sectionName, "__objc_nlclslist") == 0 ) + this->fIndex = 16; + else if ( strcmp(sectionName, "__objc_catlist") == 0 ) + this->fIndex = 17; + else if ( strcmp(sectionName, "__objc_protolist") == 0 ) + this->fIndex = 18; + else if ( strcmp(sectionName, "__objc_imageinfo") == 0 ) + this->fIndex = 19; + + } + //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); } -Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill) +Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill, bool createIfNeeded) { NameToSection::iterator pos = fgMapping.find(sectionName); if ( pos != fgMapping.end() ) { @@ -135,14 +192,17 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze } } + if ( !createIfNeeded ) + return NULL; + // does not exist, so make a new one Section* sect = new Section(sectionName, segmentName, zeroFill); fgMapping[sectionName] = sect; fgSections.push_back(sect); if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { - // special case __textcoal_nt to be right after __text - find("__textcoal_nt", "__TEXT", false); + // special case __StaticInit to be right after __text + find("__StaticInit", "__TEXT", false); } // remember segment discovery order @@ -154,6 +214,8 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze int Section::Sorter::segmentOrdinal(const char* segName) { + if ( strcmp(segName, "__HEADER") == 0 ) + return 1; if ( strcmp(segName, "__PAGEZERO") == 0 ) return 1; if ( strcmp(segName, "__TEXT") == 0 ) @@ -234,13 +296,14 @@ class Linker : public ObjectFile::Reader::DylibHander { struct WhyLiveBackChain { WhyLiveBackChain* previous; - const char* name; + ObjectFile::Atom* referer; }; ObjectFile::Reader* createReader(const Options::FileInfo&); void addAtom(ObjectFile::Atom& atom); void addAtoms(std::vector& atoms); void buildAtomList(); + void adjustScope(); void processDylibs(); void markDead(ObjectFile::Atom* atom); void updateConstraints(ObjectFile::Reader* reader); @@ -249,10 +312,10 @@ class Linker : public ObjectFile::Reader::DylibHander { void checkObjC(); void loadUndefines(); void checkUndefines(); - void addWeakAtomOverrides(); void resolveReferences(); void deadStripResolve(); void addLiveRoot(const char* name); + void moveToFrontOfSection(ObjectFile::Atom* atom); ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); void logArchive(ObjectFile::Reader* reader); void sortSections(); @@ -264,7 +327,8 @@ class Linker : public ObjectFile::Reader::DylibHander { void collectDebugInfo(); void writeOutput(); ObjectFile::Atom* entryPoint(bool orInit); - ObjectFile::Atom* dyldHelper(); + ObjectFile::Atom* dyldClassicHelper(); + ObjectFile::Atom* dyldCompressedHelper(); ObjectFile::Atom* dyldLazyLibraryHelper(); const char* assureFullPath(const char* path); void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); @@ -275,13 +339,12 @@ class Linker : public ObjectFile::Reader::DylibHander { char* commatize(uint64_t in, char* out); void getVMInfo(vm_statistics_data_t& info); cpu_type_t inferArchitecture(); - void addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName); void checkDylibClientRestrictions(ObjectFile::Reader* reader); void logDylib(ObjectFile::Reader* reader, bool indirect); void resolve(ObjectFile::Reference* reference); void resolveFrom(ObjectFile::Reference* reference); - std::vector* addJustInTimeAtoms(const char* name, bool dylibsOnly=false); + std::vector* addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy); void addJustInTimeAtomsAndMarkLive(const char* name); ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); @@ -301,10 +364,11 @@ class Linker : public ObjectFile::Reader::DylibHander { bool add(ObjectFile::Atom& atom); ObjectFile::Atom* find(const char* name); unsigned int getRequireCount() { return fRequireCount; } - void getNeededNames(bool andWeakDefintions, std::vector& undefines); + void getUndefinesNames(std::vector& undefines); + void getTentativesNames(std::vector& tents); bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } - void setHasExternalWeakDefinitions() { fHasExternalWeakDefinitions = true; } + void setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; } Mapper::iterator begin() { return fTable.begin(); } Mapper::iterator end() { return fTable.end(); } @@ -319,10 +383,14 @@ class Linker : public ObjectFile::Reader::DylibHander { class AtomSorter { public: - AtomSorter(std::map* map) : fOverriddenOrdinalMap(map) {} + AtomSorter(std::map* map, std::set& inits, + std::set& terms) : + fOverriddenOrdinalMap(map), fInitializerSet(inits), fTerminatorSet(terms) {} bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); private: std::map* fOverriddenOrdinalMap; + std::set& fInitializerSet; + std::set& fTerminatorSet; }; typedef std::map SectionOrder; @@ -353,7 +421,7 @@ class Linker : public ObjectFile::Reader::DylibHander { std::vector fInputFiles; ExecutableFile::Writer* fOutputFile; InstallNameToReader fDylibMap; - std::map fDylibOptionsMap; + std::map fDylibOptionsMap; std::set fDylibsProcessed; ObjectFile::Reader* fBundleLoaderReader; std::vector fReadersThatHaveSuppliedAtoms; @@ -363,12 +431,13 @@ class Linker : public ObjectFile::Reader::DylibHander { std::set fDeadAtoms; std::set fLiveAtoms; std::set fLiveRootAtoms; + std::set fInitializerAtoms; + std::set fTerminatorAtoms; + std::set fRegularDefAtomsThatOverrideADylibsWeakDef; std::vector fStabs; std::vector fAtomsWithUnresolvedReferences; - std::vector fDtraceProbes; - std::vector fDtraceProbeSites; - std::vector fDtraceIsEnabledSites; - std::map fDtraceAtomToTypes; + std::set fAtomsOverriddenByLateLoads; + bool fInitialLoadsDone; bool fCreateUUID; bool fCanScatter; SectionOrder fSectionOrder; @@ -404,7 +473,7 @@ class Linker : public ObjectFile::Reader::DylibHander { Linker::Linker(int argc, const char* argv[]) : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), - fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), + fInitialLoadsDone(false), fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), @@ -562,47 +631,108 @@ void Linker::loadAndResolve() void Linker::optimize() { // give each reader a chance to do any optimizations + bool didSomething = false; std::vector newAtoms; std::vector additionalUndefines; + std::vector newlyDeadAtoms; for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, fNextInputOrdinal, fOutputFile, - fOptions.llvmOptions(), + didSomething |= (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, newlyDeadAtoms, fNextInputOrdinal, + fOutputFile, entryPoint(true), fOptions.llvmOptions(), fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(), fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(), fOptions.allowTextRelocs()); } - // add all newly created atoms to fAllAtoms and update symbol table - this->addAtoms(newAtoms); + // only do next steps if some optimization was actually done + if ( didSomething ) { + // add all newly created atoms to fAllAtoms and update symbol table + this->addAtoms(newAtoms); + + // add dead atoms to dead list and remove from fAllAtoms + for(std::vector::iterator itr = newlyDeadAtoms.begin(); itr != newlyDeadAtoms.end(); ++itr) + markDead(*itr); + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could - // not have their section set until now. - for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - if ( atom->getSection() == NULL ) - atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); - } + // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could + // not have their section set until now. + for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { + ObjectFile::Atom *atom = *itr; + if ( atom->getSection() == NULL ) + atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); + } - // resolve new undefines - for(std::vector::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { - const char *targetName = *riter; - //fprintf(stderr, "LTO additional undefine: %s\n", targetName); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL) { - // mark that this symbol is needed - fGlobalSymbolTable.require(targetName); - // try to find it in some library - this->addJustInTimeAtoms(targetName); + // resolve new undefines + for(std::vector::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { + const char *targetName = *riter; + //fprintf(stderr, "LTO additional undefine: %s\n", targetName); + ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); + if ( target == NULL) { + // mark that this symbol is needed + fGlobalSymbolTable.require(targetName); + // try to find it in some library + this->addJustInTimeAtoms(targetName, true, true, true); + } + } + + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + // LTO may optimize away some atoms, so dead stripping must be redone + fLiveAtoms.clear(); + this->deadStripResolve(); + } + else { + // LTO may require new library symbols to be loaded, so redo + this->checkUndefines(); + this->resolveReferences(); } } +} - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - fLiveAtoms.clear(); - this->deadStripResolve(); + +void Linker::adjustScope() +{ + // if -exported_symbols_list is used, demoted to hidden, symbols that are not in it + if ( fOptions.hasExportRestrictList() ) { + // The use of an -export file means the previous computation of fHasExternalWeakDefinitions could change + fGlobalSymbolTable.setHasExternalWeakDefinitions(false); + for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { + ObjectFile::Atom *atom = *itr; + ObjectFile::Atom::Scope scope = atom->getScope(); + const char* name = atom->getName(); + if ( name != NULL ) { + if ( scope == ObjectFile::Atom::scopeGlobal ) { + // check for globals that are downgraded to hidden + if ( !fOptions.shouldExport(name) ) { + atom->setScope(ObjectFile::Atom::scopeLinkageUnit); + //fprintf(stderr, "demote %s to hidden\n", name); + } + else if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + // we do have an exported weak symbol, turn WEAK_DEFINES back on + fGlobalSymbolTable.setHasExternalWeakDefinitions(true); + } + } + else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { + // check for hiddens that were requested to be exported + if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { + warning("cannot export hidden symbol %s from %s", name, atom->getFile()->getPath()); + } + } + } + } + } + + // linking is done, so demote hidden symbols to static + if ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ) { + // ld -r -keep_private_externs does not move hidden symbols to static } else { - this->checkUndefines(); - this->resolveReferences(); + for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { + ObjectFile::Atom *atom = *itr; + // hidden common symbols cannot be demoted to static + if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (atom->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { + atom->setScope(ObjectFile::Atom::scopeTranslationUnit); + //fprintf(stderr, "demote %s to static\n", atom->getDisplayName()); + } + } } } @@ -611,6 +741,7 @@ void Linker::link() this->buildAtomList(); this->loadAndResolve(); this->optimize(); + this->adjustScope(); this->checkObjC(); this->processDTrace(); this->tweakLayout(); @@ -718,8 +849,6 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) fGlobalSymbolTable.require(reference->getTargetName()); if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) fGlobalSymbolTable.require(reference->getFromTargetName()); - if ( reference->getTargetBinding() == ObjectFile::Reference::kDontBind ) - addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); } // update total size info (except for __ZEROPAGE atom) if ( atom.getSegment().isContentReadable() ) { @@ -737,34 +866,8 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) ObjectFile::Atom::Scope scope = atom.getScope(); const char* name = atom.getName(); if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { - // update scope based on export list - if ( fOptions.hasExportRestrictList() ) { - if ( scope == ObjectFile::Atom::scopeGlobal ) { - // check for globals that are downgraded to hidden - bool doExport = fOptions.shouldExport(name); - if ( !doExport ) { - atom.setScope(ObjectFile::Atom::scopeLinkageUnit); - } - } - else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { - // check for hiddens that were requested to be exported - if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { - warning("cannot export hidden symbol %s from %s", name, atom.getFile()->getPath()); - } - } - } // add to symbol table - if ( fOptions.outputKind() == Options::kObjectFile ) { - // in ld -r mode don't add .eh symbols to symbol table - // instead kGroupSubordinate references will keep them paired - // with their functions. - const char* sectionName = atom.getSectionName(); - if ( (sectionName != NULL) && (strcmp(sectionName, "__eh_frame") != 0) ) - fGlobalSymbolTable.add(atom); - } - else { - fGlobalSymbolTable.add(atom); - } + fGlobalSymbolTable.add(atom); } // record section orders so output file can have same order @@ -775,7 +878,14 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) void Linker::markDead(ObjectFile::Atom* atom) { + //fprintf(stderr, "markDead(%p) %s from %s\n", atom, atom->getDisplayName(), atom->getFile()->getPath()); fDeadAtoms.insert(atom); + + // -dead_strip inhibits weak coalescing in no_dead_strip section + if ( fLiveRootAtoms.count(atom) != 0 ) { + fLiveRootAtoms.erase(atom); + } + // // The kGroupSubordinate reference kind is used to model group comdat. // The "signature" atom in the group has a kGroupSubordinate reference to @@ -787,6 +897,7 @@ void Linker::markDead(ObjectFile::Atom* atom) ObjectFile::Reference* ref = *rit; if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX ObjectFile::Atom* targetAtom = &(ref->getTarget()); + //fprintf(stderr, " markDead(%p) subordinate %s\n", targetAtom, targetAtom->getDisplayName()); if ( targetAtom == NULL ) { warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName()); } @@ -896,6 +1007,10 @@ void Linker::buildAtomList() this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); fNextInputOrdinal += it->dataLen; } + + // done with all .o files on command line + // everything loaded from now on is a just-in-time atom + fInitialLoadsDone = true; } static const char* pathLeafName(const char* path) @@ -907,6 +1022,7 @@ static const char* pathLeafName(const char* path) return &shortPath[1]; } + void Linker::loadUndefines() { // keep looping until no more undefines were added in last loop @@ -914,19 +1030,30 @@ void Linker::loadUndefines() while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { undefineCount = fGlobalSymbolTable.getRequireCount(); std::vector undefineNames; - fGlobalSymbolTable.getNeededNames(false, undefineNames); + fGlobalSymbolTable.getUndefinesNames(undefineNames); for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - const char* name = *it; - ObjectFile::Atom* possibleAtom = fGlobalSymbolTable.find(name); - if ( (possibleAtom == NULL) - || ((possibleAtom->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition) - && (fOptions.outputKind() != Options::kObjectFile) - && (possibleAtom->getScope() == ObjectFile::Atom::scopeGlobal)) ) { - std::vector* atoms = this->addJustInTimeAtoms(name); + // load for previous undefine may also have loaded this undefine, so check again + if ( fGlobalSymbolTable.find(*it) == NULL ) { + std::vector* atoms = this->addJustInTimeAtoms(*it, true, true, true); if ( atoms != NULL ) delete atoms; } } + // need to search archives for overrides of common symbols + if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { + bool searchDylibs = (fOptions.commonsMode() == Options::kCommonsOverriddenByDylibs); + std::vector tentativeDefinitionNames; + fGlobalSymbolTable.getTentativesNames(tentativeDefinitionNames); + for(std::vector::iterator it = tentativeDefinitionNames.begin(); it != tentativeDefinitionNames.end(); ++it) { + // load for previous tentative may also have overridden this tentative, so check again + ObjectFile::Atom* tent = fGlobalSymbolTable.find(*it); + if ( (tent != NULL) && (tent->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { + std::vector* atoms = this->addJustInTimeAtoms(*it, searchDylibs, true, false); + if ( atoms != NULL ) + delete atoms; + } + } + } } } @@ -973,7 +1100,7 @@ void Linker::checkUndefines() break; } std::vector unresolvableUndefines; - fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); + fGlobalSymbolTable.getUndefinesNames(unresolvableUndefines); // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names // ignore unresolved references to Objc class names that are listed in -exported_symbols_list @@ -1030,33 +1157,29 @@ void Linker::checkUndefines() if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition) && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { // look for dylibs that export same name as used by global tentative definition - addJustInTimeAtoms(atom->getName(), true); + addJustInTimeAtoms(atom->getName(), true, false, false); } } } - // if we have no weak symbols, see if we override some weak symbol in some dylib - if ( !fGlobalSymbolTable.hasExternalWeakDefinitions() ) { - bool done = false; - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); !done && (it != fGlobalSymbolTable.end()); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - const char* name = atom->getName(); - //fprintf(stderr, "looking for dylibs with a weak %s\n", name); - // look for dylibs with weak exports of the same name - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - ObjectFile::Reader* reader = it->second; - if ( reader->hasWeakExternals() ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - // if this is a weak definition in a dylib - if ( (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fGlobalSymbolTable.setHasExternalWeakDefinitions(); - done = true; - break; - } + + // record any overrides of weak symbols any linked dylib + for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { + ObjectFile::Atom* atom = it->second; + if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) + && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { + const char* name = atom->getName(); + //fprintf(stderr, "looking for dylibs with a weak %s\n", name); + // look for dylibs with weak exports of the same name + for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { + ObjectFile::Reader* reader = it->second; + if ( reader->hasWeakExternals() ) { + std::vector* dylibAtoms = reader->getJustInTimeAtomsFor(name); + if ( dylibAtoms != NULL ) { + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + // if this is a weak definition in a dylib + if ( (dylibAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + fRegularDefAtomsThatOverrideADylibsWeakDef.insert(atom); } } } @@ -1068,8 +1191,9 @@ void Linker::checkUndefines() -std::vector* Linker::addJustInTimeAtoms(const char* name, bool dylibsOnly) +std::vector* Linker::addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy) { + //fprintf(stderr, "addJustInTimeAtoms(%s, searchDylibs=%d, searchArchives=%d)\n", name, searchDylibs, searchArchives ); // when creating final linked image, writer gets first chance if ( fOptions.outputKind() != Options::kObjectFile ) { std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); @@ -1088,7 +1212,7 @@ std::vector* Linker::addJustInTimeAtoms(const char* nam // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); bool isDylibReader = (reader->getInstallPath() != NULL); - if ( !dylibsOnly || isDylibReader ) { + if ( isDylibReader ? searchDylibs : searchArchives ) { std::vector* atoms = reader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); @@ -1149,9 +1273,11 @@ std::vector* Linker::addJustInTimeAtoms(const char* nam // 1) ld -r is being used to create a .o file // 2) -undefined dynamic_lookup is being used // 3) -U _foo is being used + // 4) x86_64 kext bundle is being created if ( (fOptions.outputKind() == Options::kObjectFile) - || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && !dylibsOnly) - || (fOptions.someAllowedUndefines() && !dylibsOnly) ) { + || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && okToMakeProxy) + || (fOptions.someAllowedUndefines() && okToMakeProxy) + || (fOptions.outputKind() == Options::kKextBundle) ) { ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); if ( atom != NULL ) { this->addAtom(*atom); @@ -1168,7 +1294,7 @@ void Linker::resolve(ObjectFile::Reference* reference) const char* targetName = reference->getTargetName(); ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); if ( target == NULL ) { - fprintf(stderr, "Undefined symbol: %s\n", targetName); + throwf("unexpected undefined symbol: %s", targetName); } reference->setTarget(*target, reference->getTargetOffset()); } @@ -1179,7 +1305,7 @@ void Linker::resolveFrom(ObjectFile::Reference* reference) const char* fromTargetName = reference->getFromTargetName(); ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); if ( fromTarget == NULL ) { - fprintf(stderr, "Undefined symbol: %s\n", fromTargetName); + throwf("unexpected undefined symbol: %s", fromTargetName); } reference->setFromTarget(*fromTarget); } @@ -1237,7 +1363,8 @@ class NotLive void Linker::addJustInTimeAtomsAndMarkLive(const char* name) { - std::vector* atoms = this->addJustInTimeAtoms(name); + //fprintf(stderr, "addJustInTimeAtomsAndMarkLive(%s)\n", name); + std::vector* atoms = this->addJustInTimeAtoms(name, true, true, true); if ( atoms != NULL ) { if ( fOptions.allGlobalsAreDeadStripRoots() ) { for (std::vector::iterator it=atoms->begin(); it != atoms->end(); it++) { @@ -1245,7 +1372,7 @@ void Linker::addJustInTimeAtomsAndMarkLive(const char* name) if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { WhyLiveBackChain rootChain; rootChain.previous = NULL; - rootChain.name = atom->getDisplayName(); + rootChain.referer = atom; this->markLive(*atom, &rootChain); } } @@ -1256,14 +1383,15 @@ void Linker::addJustInTimeAtomsAndMarkLive(const char* name) void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) { + //fprintf(stderr, "markLive(%p)\n", &atom); if ( fLiveAtoms.count(&atom) == 0 ) { - // if -whylive cares about this symbol, then dump chain - if ( (previous->name != NULL) && fOptions.printWhyLive(previous->name) ) { + // if -why_live cares about this symbol, then dump chain + if ( (previous->referer != NULL) && fOptions.printWhyLive(previous->referer->getDisplayName()) ) { int depth = 0; for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { for(int i=depth; i > 0; --i) fprintf(stderr, " "); - fprintf(stderr, "%s\n", p->name); + fprintf(stderr, "%p %s from %s\n", p->referer, p->referer->getDisplayName(), p->referer->getFile()->getPath()); } } // set up next chain @@ -1285,7 +1413,7 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p // look in global symbol table const char* targetName = reference->getTargetName(); ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { + if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { // load archives or dylibs this->addJustInTimeAtomsAndMarkLive(targetName); } @@ -1303,12 +1431,10 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p switch ( reference->getTargetBinding() ) { case ObjectFile::Reference::kBoundDirectly: case ObjectFile::Reference::kBoundByName: - thisChain.name = reference->getTargetName(); + thisChain.referer = &reference->getTarget(); markLive(reference->getTarget(), &thisChain); break; case ObjectFile::Reference::kDontBind: - addDtraceProbe(atom, reference->getFixUpOffset(), reference->getTargetName()); - break; case ObjectFile::Reference::kUnboundByName: // do nothing break; @@ -1318,7 +1444,7 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p // look in global symbol table const char* targetName = reference->getFromTargetName(); ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { + if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { // load archives or dylibs this->addJustInTimeAtomsAndMarkLive(targetName); } @@ -1335,7 +1461,7 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p switch ( reference->getFromTargetBinding() ) { case ObjectFile::Reference::kBoundDirectly: case ObjectFile::Reference::kBoundByName: - thisChain.name = reference->getFromTargetName(); + thisChain.referer = &reference->getFromTarget(); markLive(reference->getFromTarget(), &thisChain); break; case ObjectFile::Reference::kUnboundByName: @@ -1359,6 +1485,32 @@ void Linker::addLiveRoot(const char* name) fLiveRootAtoms.insert(target); } +void Linker::moveToFrontOfSection(ObjectFile::Atom* atom) +{ + // check if already moved to front + if ( fInitializerAtoms.find(atom) == fInitializerAtoms.end() ) { + // don't re-order initializers from .o files without MH_SUBSECTIONS_VIA_SYMBOLS + // since that could make all atoms in the file look like initializers + if ( atom->getFile()->canScatterAtoms() ) { + //fprintf(stdout, "marking as initializer: %s\n", atom->getDisplayName()); + fInitializerAtoms.insert(atom); + // mark all functions that this function references + std::vector& references = atom->getReferences(); + for (std::vector::const_iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Atom* childAtom = &((*rit)->getTarget()); + if ( childAtom != NULL ) { + if ( (*rit)->isBranch() ) { + this->moveToFrontOfSection(childAtom); + } + else if ( (childAtom->getName() != NULL) && (strncmp(childAtom->getName(), "___tcf_", 7) == 0) ) { + //fprintf(stdout, "marking as terminator: %s\n", childAtom->getDisplayName()); + fTerminatorAtoms.insert(childAtom); + } + } + } + } + } +} void Linker::deadStripResolve() { @@ -1367,8 +1519,11 @@ void Linker::deadStripResolve() if ( entryPoint != NULL ) fLiveRootAtoms.insert(entryPoint); - // add dyld_stub_binding_helper() to live roots - ObjectFile::Atom* dyldHelper = this->dyldHelper(); + // add dyld_stub_binding_helper/dyld_stub_binder to live roots + ObjectFile::Atom* dyldHelper = this->dyldClassicHelper(); + if ( dyldHelper != NULL ) + fLiveRootAtoms.insert(dyldHelper); + dyldHelper = this->dyldCompressedHelper(); if ( dyldHelper != NULL ) fLiveRootAtoms.insert(dyldHelper); @@ -1409,7 +1564,7 @@ void Linker::deadStripResolve() for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { WhyLiveBackChain rootChain; rootChain.previous = NULL; - rootChain.name = (*it)->getDisplayName(); + rootChain.referer = *it; markLive(**it, &rootChain); } @@ -1446,6 +1601,46 @@ void Linker::deadStripResolve() } } + // It is possible that some weak symbols were overridden by lazily load objects from archives + // and we have some atoms that still refer to the overridden ones. + // In that case we need to go back and rebind + if ( fAtomsOverriddenByLateLoads.size() > 0 ) { + for (std::set::iterator it=fLiveAtoms.begin(); it != fLiveAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { + ObjectFile::Reference* reference = *rit; + ObjectFile::Atom* toTarget = &reference->getTarget(); + if ( fAtomsOverriddenByLateLoads.count(toTarget) ) { + //fprintf(stderr, "change reference in %p from %p to %p\n", atom, toTarget, fGlobalSymbolTable.find(toTarget->getName())); + reference->setTarget(*fGlobalSymbolTable.find(toTarget->getName()), reference->getTargetOffset()); + } + ObjectFile::Atom* fromTarget = &reference->getFromTarget(); + if ( (fromTarget != NULL) && fAtomsOverriddenByLateLoads.count(fromTarget) ) { + //fprintf(stderr, "change from reference in %p from %p to %p\n", atom, fromTarget, fGlobalSymbolTable.find(fromTarget->getName())); + reference->setTarget(*fGlobalSymbolTable.find(fromTarget->getName()), reference->getFromTargetOffset()); + } + } + } + + // make sure overriders are live if the atom they overrid was live + for (std::set::iterator it=fAtomsOverriddenByLateLoads.begin(); it != fAtomsOverriddenByLateLoads.end(); ++it) { + ObjectFile::Atom* overriderAtom = *it; + if ( fLiveAtoms.count(overriderAtom) ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.referer = *it; + markLive(*fGlobalSymbolTable.find(overriderAtom->getName()), &rootChain); + } + } + + // remove overridden atoms from fLiveAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fAtomsOverriddenByLateLoads)), fAllAtoms.end()); + fAtomsOverriddenByLateLoads.clear(); + // remove dead atoms from fLiveAtoms + fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); + } + // now remove all non-live atoms from fAllAtoms fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); } @@ -1486,36 +1681,6 @@ void Linker::checkObjC() } } -void Linker::addDtraceProbe(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* probeName) -{ - if ( probeName != NULL ) { - if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) - fDtraceProbeSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) - fDtraceIsEnabledSites.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) - fDtraceAtomToTypes[&atom].insert(probeName); - else if ( fOptions.dTrace() && (strncmp(probeName, "__dtrace_probe$", 15) == 0) ) - fDtraceProbes.push_back(DTraceProbeInfo(&atom, offsetInAtom, probeName)); - } -} - -static uint8_t pointerKind(cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - return ppc::kPointer; - case CPU_TYPE_POWERPC64: - return ppc64::kPointer; - case CPU_TYPE_I386: - return x86::kPointer; - case CPU_TYPE_X86_64: - return x86_64::kPointer; - case CPU_TYPE_ARM: - return arm::kPointer; - } - throw "uknown architecture"; -} static uint8_t pcRelKind(cpu_type_t arch) { @@ -1540,180 +1705,167 @@ typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], un void Linker::processDTrace() { - // handle dtrace 2.0 static probes - if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) { - // partition probes by provider name - // The symbol names looks like: - // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] - // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] - ProviderToProbes providerToProbes; - std::vector emptyList; - for(std::vector::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[16]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - for(std::vector::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[20]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); + // only make __dof section in final linked images + if ( fOptions.outputKind() == Options::kObjectFile ) + return; + + // scan all atoms looking for dtrace probes + std::vector probeSites; + std::vector isEnabledSites; + std::map atomToDtraceTypes; + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { + ObjectFile::Atom* atom = *it; + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { + ObjectFile::Reference* ref = *rit; + if ( ref->getTargetBinding() == ObjectFile::Reference::kDontBind ) { + const char* probeName = ref->getTargetName(); + if ( probeName != NULL ) { + uint32_t offsetInAtom = ref->getFixUpOffset(); + if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) + probeSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) + isEnabledSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName)); + else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) + atomToDtraceTypes[atom].insert(probeName); } } } - - // create a DOF section for each provider - int dofIndex=1; - CStringSet sectionNamesUsed; - for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { - const char* providerName = pit->first; - const std::vector& probes = pit->second; - - // open library and find dtrace_create_dof() - void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); - if ( handle == NULL ) - throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); - createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); - if ( pCreateDOF == NULL ) - throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); - // build list of typedefs/stability infos for this provider - CStringSet types; - for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { - std::map::iterator pos = fDtraceAtomToTypes.find(it->atom); - if ( pos != fDtraceAtomToTypes.end() ) { - for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { - const char* providerStart = strchr(*sit, '$')+1; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char aProviderName[providerEnd-providerStart+1]; - strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); - if ( strcmp(aProviderName, providerName) == 0 ) - types.insert(*sit); - } - } - } - } - int typeCount = types.size(); - const char* typeNames[typeCount]; - //fprintf(stderr, "types for %s:\n", providerName); - uint32_t index = 0; - for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { - typeNames[index] = *it; - //fprintf(stderr, "\t%s\n", *it); - ++index; - } - - // build list of probe/isenabled sites - const uint32_t probeCount = probes.size(); - const char* probeNames[probeCount]; - const char* funtionNames[probeCount]; - uint64_t offsetsInDOF[probeCount]; - index = 0; - for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { - probeNames[index] = it->probeName; - funtionNames[index] = it->atom->getName(); - offsetsInDOF[index] = 0; - ++index; - } - //fprintf(stderr, "calling libtrace to create DOF\n"); - //for(uint32_t i=0; i < probeCount; ++i) - // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); - // call dtrace library to create DOF section - size_t dofSectionSize; - uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); - if ( p != NULL ) { - char sectionName[18]; - strcpy(sectionName, "__dof_"); - strlcpy(§ionName[6], providerName, 10); - // create unique section name so each DOF is in its own section - if ( sectionNamesUsed.count(sectionName) != 0 ) { - sectionName[15] = '0'; - sectionName[16] = '\0'; - while ( sectionNamesUsed.count(sectionName) != 0 ) - ++sectionName[15]; - } - sectionNamesUsed.insert(sectionName); - char symbolName[strlen(providerName)+64]; - sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); - opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, - "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); - fNextInputOrdinal += dofSectionSize; - // add references - for (uint32_t i=0; i < probeCount; ++i) { - uint64_t offset = offsetsInDOF[i]; - //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); - if ( offset > dofSectionSize ) - throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); - reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); - } - this->addAtoms(reader->getAtoms()); - } - else { - throw "error creating dtrace DOF section"; + } + + // if no probes, we're done + if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) ) + return; + + // partition probes by provider name + // The symbol names looks like: + // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] + // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] + ProviderToProbes providerToProbes; + std::vector emptyList; + for(std::vector::iterator it = probeSites.begin(); it != probeSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { + const char* providerStart = &it->probeName[16]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + } + for(std::vector::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { + const char* providerStart = &it->probeName[20]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); } } } - // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files - else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) { - const uint32_t probeCount = fDtraceProbes.size(); - const char* labels[probeCount]; - const char* funtionNames[probeCount]; - uint64_t offsetsInDOF[probeCount]; - - // open libray and find dtrace_ld64_create_dof() + + // create a DOF section for each provider + int dofIndex=1; + CStringSet sectionNamesUsed; + for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { + const char* providerName = pit->first; + const std::vector& probes = pit->second; + + // open library and find dtrace_create_dof() void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); if ( handle == NULL ) - throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror()); - oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof"); + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); + createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); if ( pCreateDOF == NULL ) - throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror()); - - // build argument list + throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); + // build list of typedefs/stability infos for this provider + CStringSet types; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + std::map::iterator pos = atomToDtraceTypes.find(it->atom); + if ( pos != atomToDtraceTypes.end() ) { + for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { + const char* providerStart = strchr(*sit, '$')+1; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char aProviderName[providerEnd-providerStart+1]; + strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); + if ( strcmp(aProviderName, providerName) == 0 ) + types.insert(*sit); + } + } + } + } + int typeCount = types.size(); + const char* typeNames[typeCount]; + //fprintf(stderr, "types for %s:\n", providerName); uint32_t index = 0; - for(std::vector::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) { - labels[index] = it->probeName; + for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { + typeNames[index] = *it; + //fprintf(stderr, "\t%s\n", *it); + ++index; + } + + // build list of probe/isenabled sites + const uint32_t probeCount = probes.size(); + const char* probeNames[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + index = 0; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + probeNames[index] = it->probeName; funtionNames[index] = it->atom->getName(); offsetsInDOF[index] = 0; ++index; } - size_t dofSectionSize; + //fprintf(stderr, "calling libtrace to create DOF\n"); + //for(uint32_t i=0; i < probeCount; ++i) + // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); // call dtrace library to create DOF section - uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize); + size_t dofSectionSize; + uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); if ( p != NULL ) { - opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal); + char sectionName[18]; + strcpy(sectionName, "__dof_"); + strlcpy(§ionName[6], providerName, 10); + // create unique section name so each DOF is in its own section + if ( sectionNamesUsed.count(sectionName) != 0 ) { + sectionName[15] = '0'; + sectionName[16] = '\0'; + while ( sectionNamesUsed.count(sectionName) != 0 ) + ++sectionName[15]; + } + sectionNamesUsed.insert(sectionName); + char symbolName[strlen(providerName)+64]; + sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); + opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, + "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); fNextInputOrdinal += dofSectionSize; // add references for (uint32_t i=0; i < probeCount; ++i) { uint64_t offset = offsetsInDOF[i]; + //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); if ( offset > dofSectionSize ) - throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX", i, offset, dofSectionSize); - reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset); + throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); } this->addAtoms(reader->getAtoms()); } else { - throw "error created dtrace DOF section"; + throw "error creating dtrace DOF section"; } } } @@ -2041,11 +2193,11 @@ void Linker::sortAtoms() } // sort atoms - std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap)); + std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap, fInitializerAtoms, fTerminatorAtoms)); //fprintf(stderr, "Sorted atoms:\n"); //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName()); + // fprintf(stderr, "\t%p, %u %s\t%s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath()); //} } @@ -2068,6 +2220,81 @@ void Linker::tweakLayout() atom->setSection(hugeZeroFills); } } + + // move all initializers to start of __text section + if ( fOptions.readerOptions().fAutoOrderInitializers ) { + // move -init function to front of __text + if ( fOptions.initFunctionName() != NULL ) { + ObjectFile::Atom* initAtom = fGlobalSymbolTable.find(fOptions.initFunctionName()); + if ( initAtom == NULL ) + throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); + moveToFrontOfSection(initAtom); + } + + // move all functions pointed to by __mod_init_func section to front of __text + Section* initSection = Section::find("__mod_init_func", "__DATA", false, false); + if ( initSection != NULL ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { + if ( (*it)->getSection() == initSection ) { + std::vector& references = (*it)->getReferences(); + if ( references.size() == 1 ) + moveToFrontOfSection(&(references[0]->getTarget())); + } + } + } + } + + // move atoms with relocations to start of __DATA,__data section + // linker should order __DATA segment to reduce dyld dirtied pages + if ( fOptions.orderData() ) { + bool slideable = false; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + case Options::kKextBundle: + slideable = false; + break; + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + slideable = true; + break; + } + const bool hasPreferredLoadAddress = (fOptions.baseAddress() != 0); + Section* dataSection = Section::find("__data", "__DATA", false, false); + if ( dataSection != NULL ) { + for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { + ObjectFile::Atom* dataAtom = *it; + if ( dataAtom->getSection() == dataSection ) { + std::vector& references = dataAtom->getReferences(); + if ( references.size() > 0 ) { + if ( slideable && !hasPreferredLoadAddress ) { + // in a slidable image dyld will need to rebase and bind so any references will need runtime fixups + // if image has preferred base address, assume it will load there and not rebase + moveToFrontOfSection(dataAtom); + } + else { + // in a non-slideable image, dyld will only do binding, so only references to + // symbols in another dylib will need runtime fixups + //fprintf(stderr, "reference from atom %s\n", dataAtom->getDisplayName()); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* reference = *rit; + //fprintf(stderr, "\t%d %s\n", reference->getTarget().getDefinitionKind(), reference->getTarget().getDisplayName()); + if ( (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) + || (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { + moveToFrontOfSection(dataAtom); + break; + } + } + } + } + } + } + } + } + } @@ -2158,6 +2385,7 @@ ObjectFile::Atom* Linker::entryPoint(bool orInit) case Options::kDynamicExecutable: case Options::kStaticExecutable: case Options::kDyld: + case Options::kPreload: entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); if ( entryPoint == NULL ) { throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); @@ -2173,15 +2401,34 @@ ObjectFile::Atom* Linker::entryPoint(bool orInit) break; case Options::kObjectFile: case Options::kDynamicBundle: + case Options::kKextBundle: entryPoint = NULL; break; } return entryPoint; } -ObjectFile::Atom* Linker::dyldHelper() +ObjectFile::Atom* Linker::dyldClassicHelper() { - return fGlobalSymbolTable.find("dyld_stub_binding_helper"); + if ( fOptions.makeClassicDyldInfo() ) + return fGlobalSymbolTable.find("dyld_stub_binding_helper"); + else + return NULL; +} + +ObjectFile::Atom* Linker::dyldCompressedHelper() +{ + if ( fOptions.makeCompressedDyldInfo() ) { + // dyld_stub_binder is in libSystem.B.dylib + ObjectFile::Atom* atom = fGlobalSymbolTable.find("dyld_stub_binder"); + if ( atom == NULL ) { + this->addJustInTimeAtoms("dyld_stub_binder", true, false, true); + } + atom = fGlobalSymbolTable.find("dyld_stub_binder"); + return atom; + } + else + return NULL; } ObjectFile::Atom* Linker::dyldLazyLibraryHelper() @@ -2619,7 +2866,8 @@ void Linker::synthesizeDebugNotes(std::vector& allAtoms ObjectFile::Reader::Stab objStab; objStab.atom = NULL; objStab.type = N_OSO; - objStab.other = 0; + // linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries + objStab.other = atom->getFile()->updateCpuConstraint(0); objStab.desc = 1; objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); objStab.string = assureFullPath(atom->getFile()->getPath()); @@ -2696,6 +2944,9 @@ void Linker::synthesizeDebugNotes(std::vector& allAtoms else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) { // no stabs for .eh atoms } + else if ( (strncmp(atom->getName(), "__dtrace_probe$", 15) == 0) ) { + // no stabs for old style dtrace probes + } else { ObjectFile::Reader::Stab globalsStab; const char* name = atom->getName(); @@ -2847,9 +3098,10 @@ void Linker::writeOutput() fStartWriteTime = mach_absolute_time(); // tell writer about each segment's atoms fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), - this->dyldHelper(), this->dyldLazyLibraryHelper(), + this->dyldClassicHelper(),this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(), fCreateUUID, fCanScatter, - fCurrentCpuConstraint, fBiggerThanTwoGigOutput, + fCurrentCpuConstraint, fBiggerThanTwoGigOutput, + fRegularDefAtomsThatOverrideADylibsWeakDef, fGlobalSymbolTable.hasExternalWeakDefinitions()); } @@ -2914,6 +3166,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) } ::close(fd); + bool objSubtypeMustMatch = (fOptions.preferSubArchitecture() && !fOptions.allowSubArchitectureMismatches()); switch (fArchitecture) { case CPU_TYPE_POWERPC: if ( mach_o::relocatable::Reader::validFile(p) ) @@ -2921,7 +3174,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; case CPU_TYPE_POWERPC64: if ( mach_o::relocatable::Reader::validFile(p) ) @@ -2929,7 +3182,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; case CPU_TYPE_I386: if ( mach_o::relocatable::Reader::validFile(p) ) @@ -2937,7 +3190,7 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; case CPU_TYPE_X86_64: if ( mach_o::relocatable::Reader::validFile(p) ) @@ -2945,15 +3198,14 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); + if ( mach_o::relocatable::Reader::validFile(p, objSubtypeMustMatch, fOptions.subArchitecture()) ) + return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; + return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; } @@ -3076,7 +3328,7 @@ void Linker::processDylibs() if ( childParent != NULL ) { if ( strcmp(childParent, &myLeaf[1]) == 0 ) { // set re-export bit of info - std::map::iterator pos = fDylibOptionsMap.find(reader); + std::map::iterator pos = fDylibOptionsMap.find(reader); if ( pos != fDylibOptionsMap.end() ) { pos->second.fReExport = true; } @@ -3106,8 +3358,8 @@ void Linker::createReaders() this->addInputFile(this->createReader(entry), entry); } catch (const char* msg) { - if ( strstr(msg, "architecture") != NULL ) { - if ( fOptions.ignoreOtherArchInputFiles() ) { + if ( (strstr(msg, "architecture") != NULL) && !fOptions.errorOnOtherArchFiles() ) { + if ( fOptions.ignoreOtherArchInputFiles() ) { // ignore, because this is about an architecture not in use } else { @@ -3226,12 +3478,25 @@ void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) } } } - - } + ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + case Options::kKextBundle: + warning("unexpected dylib (%s) on link line", reader->getPath()); + break; + } + fNextInputOrdinal += mappedLen; if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { // this is a "blank" stub @@ -3338,7 +3603,7 @@ void Linker::createWriter() if ( ! alreadyInDynamicLibraries ) { ExecutableFile::DyLibUsed dylibInfo; dylibInfo.reader = it->second; - std::map::iterator pos = fDylibOptionsMap.find(it->second); + std::map::iterator pos = fDylibOptionsMap.find(it->second); if ( pos != fDylibOptionsMap.end() ) { dylibInfo.options = pos->second; } @@ -3443,18 +3708,6 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) bool useNew = true; bool checkVisibilityMismatch = false; const char* name = newAtom.getName(); - if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { - switch ( newAtom.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - fHasExternalTentativeDefinitions = true; - break; - case ObjectFile::Atom::kWeakDefinition: - fHasExternalWeakDefinitions = true; - break; - default: - break; - } - } //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); Mapper::iterator pos = fTable.find(name); ObjectFile::Atom* existingAtom = NULL; @@ -3659,8 +3912,26 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) } if ( useNew ) { fTable[name] = &newAtom; - if ( existingAtom != NULL ) + if ( existingAtom != NULL ) { fOwner.markDead(existingAtom); + if ( fOwner.fInitialLoadsDone ) { + //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->getName(), &newAtom); + fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom); + } + } + if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { + switch ( newAtom.getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + fHasExternalTentativeDefinitions = true; + ++fRequireCount; // added a tentative definition means loadUndefines() needs to continue + break; + case ObjectFile::Atom::kWeakDefinition: + fHasExternalWeakDefinitions = true; + break; + default: + break; + } + } } else { fOwner.markDead(&newAtom); @@ -3680,15 +3951,27 @@ ObjectFile::Atom* Linker::SymbolTable::find(const char* name) } -void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vector& undefines) +void Linker::SymbolTable::getUndefinesNames(std::vector& undefines) { for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( (it->second == NULL) || (andWeakDefintions && (it->second->getDefinitionKind()==ObjectFile::Atom::kWeakDefinition)) ) { + if ( it->second == NULL ) { undefines.push_back(it->first); } } } +void Linker::SymbolTable::getTentativesNames(std::vector& tents) +{ + for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { + if ( it->second != NULL ) { + if ( (it->second->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) + && (it->second->getScope() == ObjectFile::Atom::scopeGlobal) ) { + tents.push_back(it->first); + } + } + } +} + bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) @@ -3736,6 +4019,22 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi if ( leftIsTent != rightIsTent ) return rightIsTent; + // initializers are auto sorted to start of section + if ( !fInitializerSet.empty() ) { + bool leftFirst = (fInitializerSet.count(left) != 0); + bool rightFirst = (fInitializerSet.count(right) != 0); + if ( leftFirst != rightFirst ) + return leftFirst; + } + + // terminators are auto sorted to end of section + if ( !fTerminatorSet.empty() ) { + bool leftLast = (fTerminatorSet.count(left) != 0); + bool rightLast = (fTerminatorSet.count(right) != 0); + if ( leftLast != rightLast ) + return rightLast; + } + // lastly sort by atom ordinal. this is already sorted by .o order return left->getOrdinal() < right->getOrdinal(); } diff --git a/ld64/src/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp similarity index 94% rename from ld64/src/ObjectDump.cpp rename to ld64/src/other/ObjectDump.cpp index a06c7a2..c2ae578 100644 --- a/ld64/src/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -30,7 +30,7 @@ #include "MachOReaderRelocatable.hpp" -#define LTO_SUPPORT 0 +#define LTO_SUPPORT 1 #if LTO_SUPPORT #include "LTOReader.hpp" @@ -294,7 +294,7 @@ static void dumpAtom(ObjectFile::Atom* atom) uint8_t content[size]; atom->copyRawContent(content); printf("content: "); - if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { + if ( atom->getContentType() == ObjectFile::Atom::kCStringType ) { printf("\""); for (unsigned int i=0; i < size; ++i) { if(content[i]<'!' || content[i]>=127) @@ -312,6 +312,16 @@ static void dumpAtom(ObjectFile::Atom* atom) printf("\n"); } + // unwind info + if(!sPrintRestrict) { + if ( atom->beginUnwind() != atom->endUnwind() ) { + printf("unwind encodings:\n"); + for (ObjectFile::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) { + printf("\t 0x%04X 0x%08X\n", it->startOffset, it->unwindInfo); + } + } + } + // references if(!sPrintRestrict) { std::vector& references = atom->getReferences(); @@ -344,7 +354,11 @@ struct AtomSorter { if ( left == right ) return false; - return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); + int name = strcmp(left->getDisplayName(), right->getDisplayName()); + if ( name == 0 ) + return (left->getSize() < right->getSize()); + else + return ( name < 0); } }; @@ -437,6 +451,7 @@ int main(int argc, const char* argv[]) } ObjectFile::ReaderOptions options; + options.fAddCompactUnwindEncoding = true; try { for(int i=1; i < argc; ++i) { const char* arg = argv[i]; @@ -463,6 +478,10 @@ 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, "armv6") == 0 ) + sPreferredArch = CPU_TYPE_ARM; else throwf("unknown architecture %s", arch); } diff --git a/ld64/src/other/PruneTrie.cpp b/ld64/src/other/PruneTrie.cpp new file mode 100644 index 0000000..766ae94 --- /dev/null +++ b/ld64/src/other/PruneTrie.cpp @@ -0,0 +1,100 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" +#include "prune_trie.h" + + + + +/* + * prune_trie() is a C vended function that is used by strip(1) to prune out + * defined exported symbols from the export trie. It is passed a pointer to + * the start of bytes of the the trie and the size. The prune() funciton + * passed is called with each symbol name in the trie to determine if it is + * to be pruned (retuning 1) or not (returning 0). It writes the new trie + * back into the trie buffer and returns the new size in trie_new_size. + * If the pruning succeeds, NULL is returned. If there was an error processing + * the trie (e.f. it is malformed), then an error message string is returned. + * The error string can be freed. + */ +const char* +prune_trie( + uint8_t* trie_start, + uint32_t trie_start_size, + int (*prune)(const char *name), + uint32_t* trie_new_size) +{ + // convert trie to vector of entries + std::vector originalExports; + try { + parseTrie(trie_start, trie_start+trie_start_size, originalExports); + } + catch (const char* msg) { + return strdup(msg); + } + catch (...) { + return strdup("unexpected exception processing trie"); + } + + // prune entries into new vector of entries + std::vector newExports; + newExports.reserve(originalExports.size()); + for(std::vector::iterator it = originalExports.begin(); it != originalExports.end(); ++it) { + if ( prune(it->name) == 0 ) + newExports.push_back(*it); + } + + // create new export trie + std::vector newExportTrieBytes; + newExportTrieBytes.reserve(trie_start_size); + mach_o::trie::makeTrie(newExports, newExportTrieBytes); + // Need to align trie to 8 or 4 bytes. We don't know the arch, but if the incoming trie + // was not 8-byte aligned, then it can't be a 64-bit arch, so use 4-byte alignement. + if ( (trie_start_size % 8) != 0 ) { + // 4-byte align + while ( (newExportTrieBytes.size() % 4 ) != 0) + newExportTrieBytes.push_back(0); + } + else { + // 8-byte align + while ( (newExportTrieBytes.size() % 8 ) != 0) + newExportTrieBytes.push_back(0); + } + + // copy into place, zero pad + *trie_new_size = newExportTrieBytes.size(); + if ( *trie_new_size > trie_start_size ) { + char* msg; + asprintf(&msg, "new trie is larger (%d) than original (%d)", *trie_new_size, trie_start_size); + return msg; + } + memcpy(trie_start, &newExportTrieBytes[0], *trie_new_size); + bzero(trie_start+*trie_new_size, trie_start_size - *trie_new_size); + + // success + return NULL; +} diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp new file mode 100644 index 0000000..016fda3 --- /dev/null +++ b/ld64/src/other/dyldinfo.cpp @@ -0,0 +1,1460 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "MachOTrie.hpp" + +static bool printRebase = false; +static bool printBind = false; +static bool printWeakBind = false; +static bool printLazyBind = false; +static bool printOpcodes = false; +static bool printExport = false; +static bool printExportGraph = false; +static cpu_type_t sPreferredArch = CPU_TYPE_I386; + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +template +class DyldInfoPrinter +{ +public: + static bool validFile(const uint8_t* fileContent); + static DyldInfoPrinter* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) + { return new DyldInfoPrinter(fileContent, fileLength, path); } + virtual ~DyldInfoPrinter() {} + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + + DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path); + void printRebaseInfo(); + void printRebaseInfoOpcodes(); + void printBindingInfo(); + void printWeakBindingInfo(); + void printLazyBindingInfo(); + void printBindingInfoOpcodes(bool weakBinding); + void printWeakBindingInfoOpcodes(); + void printLazyBindingOpcodes(); + void printExportInfo(); + void printExportInfoGraph(); + void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset); + void processExportGraphNode(const uint8_t* const start, const uint8_t* const end, + const uint8_t* parent, const uint8_t* p, + char* cummulativeString, int curStrOffset); + const char* rebaseTypeName(uint8_t type); + const char* bindTypeName(uint8_t type); + pint_t segStartAddress(uint8_t segIndex); + const char* segmentName(uint8_t segIndex); + const char* sectionName(uint8_t segIndex, pint_t address); + const char* getSegAndSectName(uint8_t segIndex, pint_t address); + const char* ordinalName(int libraryOrdinal); + + + const char* fPath; + const macho_header

* fHeader; + uint64_t fLength; + const char* fStrings; + const char* fStringsEnd; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + const macho_dyld_info_command

* fInfo; + uint64_t fBaseAddress; + std::vector*>fSegments; + std::vector fDylibs; +}; + + + +template <> +bool DyldInfoPrinter::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 DyldInfoPrinter::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 DyldInfoPrinter::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_I386 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool DyldInfoPrinter::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_X86_64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool DyldInfoPrinter::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: + return true; + } + return false; +} + + +template +DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) + : fHeader(NULL), fLength(fileLength), + fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), fBaseAddress(0) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a mach-o file that can be checked"; + + fPath = strdup(path); + fHeader = (const macho_header

*)fileContent; + + // get LC_DYLD_INFO + const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; + const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > endOfLoadCommands ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + switch ( cmd->cmd() ) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fInfo = (macho_dyld_info_command

*)cmd; + break; + case macho_segment_command

::CMD: + { + const macho_segment_command

* segCmd = (const macho_segment_command

*)cmd; + fSegments.push_back(segCmd); + if ( (segCmd->fileoff() == 0) && (segCmd->filesize() != 0) ) + fBaseAddress = segCmd->vmaddr(); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LAZY_LOAD_DYLIB: + { + const macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + const char* lastSlash = strrchr(dylib->name(), '/'); + const char* leafName = (lastSlash != NULL) ? lastSlash+1 : dylib->name(); + const char* firstDot = strchr(leafName, '.'); + if ( firstDot != NULL ) { + char* t = strdup(leafName); + t[firstDot-leafName] = '\0'; + fDylibs.push_back(t); + } + else { + fDylibs.push_back(leafName); + } + } + break; + } + cmd = (const macho_load_command

*)endOfCmd; + } + + if ( printRebase ) + printRebaseInfo(); + if ( printBind ) + printBindingInfo(); + if ( printWeakBind ) + printWeakBindingInfo(); + if ( printLazyBind ) + printLazyBindingInfo(); + if ( printExport ) + printExportInfo(); + if ( printOpcodes ) { + printRebaseInfoOpcodes(); + printBindingInfoOpcodes(false); + printBindingInfoOpcodes(true); + printLazyBindingOpcodes(); + } + if ( printExportGraph ) + printExportInfoGraph(); +} + +static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throwf("uleb128 too big"); + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + +static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throwf("malformed sleb128"); + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + + +template +const char* DyldInfoPrinter::rebaseTypeName(uint8_t type) +{ + switch (type ){ + case REBASE_TYPE_POINTER: + return "pointer"; + case REBASE_TYPE_TEXT_ABSOLUTE32: + return "text abs32"; + case REBASE_TYPE_TEXT_PCREL32: + return "text rel32"; + } + return "!!unknown!!"; +} + + +template +const char* DyldInfoPrinter::bindTypeName(uint8_t type) +{ + switch (type ){ + case BIND_TYPE_POINTER: + return "pointer"; + case BIND_TYPE_TEXT_ABSOLUTE32: + return "text abs32"; + case BIND_TYPE_TEXT_PCREL32: + return "text rel32"; + } + return "!!unknown!!"; +} + + +template +typename A::P::uint_t DyldInfoPrinter::segStartAddress(uint8_t segIndex) +{ + if ( segIndex > fSegments.size() ) + throw "segment index out of range"; + return fSegments[segIndex]->vmaddr(); +} + +template +const char* DyldInfoPrinter::segmentName(uint8_t segIndex) +{ + if ( segIndex > fSegments.size() ) + throw "segment index out of range"; + return fSegments[segIndex]->segname(); +} + +template +const char* DyldInfoPrinter::sectionName(uint8_t segIndex, pint_t address) +{ + if ( segIndex > fSegments.size() ) + throw "segment index out of range"; + const macho_segment_command

* segCmd = fSegments[segIndex]; + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) { + if ( strlen(sect->sectname()) > 15 ) { + static char temp[18]; + strlcpy(temp, sect->sectname(), 17); + return temp; + } + else { + return sect->sectname(); + } + } + } + return "??"; +} + +template +const char* DyldInfoPrinter::getSegAndSectName(uint8_t segIndex, pint_t address) +{ + static char buffer[64]; + strcpy(buffer, segmentName(segIndex)); + strcat(buffer, "/"); + const macho_segment_command

* segCmd = fSegments[segIndex]; + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->addr() <= address) && (address < (sect->addr()+sect->size())) ) { + // section name may not be zero terminated + char* end = &buffer[strlen(buffer)]; + strlcpy(end, sect->sectname(), 16); + return buffer; + } + } + return "??"; +} + +template +const char* DyldInfoPrinter::ordinalName(int libraryOrdinal) +{ + switch ( libraryOrdinal) { + case BIND_SPECIAL_DYLIB_SELF: + return "this-image"; + case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: + return "main-executable"; + case BIND_SPECIAL_DYLIB_FLAT_LOOKUP: + return "flat-namespace"; + } + if ( libraryOrdinal < BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) + throw "unknown special ordinal"; + if ( libraryOrdinal > fDylibs.size() ) + throw "libraryOrdinal out of range"; + return fDylibs[libraryOrdinal-1]; +} + + +template +void DyldInfoPrinter::printRebaseInfo() +{ + if ( (fInfo == NULL) || (fInfo->rebase_off() == 0) ) { + printf("no compressed rebase info\n"); + } + else { + printf("rebase information:\n"); + printf("segment section address type\n"); + + const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off(); + const uint8_t* end = &p[fInfo->rebase_size()]; + + uint8_t type = 0; + uint64_t segOffset = 0; + uint32_t count; + uint32_t skip; + int segIndex; + pint_t segStartAddr = 0; + const char* segName = "??"; + const char* typeName = "??"; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + typeName = rebaseTypeName(type); + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segName = segmentName(segIndex); + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad rebase opcode %d", *p); + } + } + } + +} + + + +template +void DyldInfoPrinter::printRebaseInfoOpcodes() +{ + if ( (fInfo == NULL) || (fInfo->rebase_off() == 0) ) { + printf("no compressed rebase info\n"); + } + else { + printf("rebase opcodes:\n"); + const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off(); + const uint8_t* end = &p[fInfo->rebase_size()]; + + uint8_t type = 0; + uint64_t address = fBaseAddress; + uint32_t count; + uint32_t skip; + unsigned int segmentIndex; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + printf("REBASE_OPCODE_DONE()\n"); + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + printf("REBASE_OPCODE_SET_TYPE_IMM(%d)\n", type); + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + address = read_uleb128(p, end); + printf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", segmentIndex, address); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + address = read_uleb128(p, end); + printf("REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", address); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + address = immediate*sizeof(pint_t); + printf("REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", address); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + printf("REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", immediate); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", count); + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + skip = read_uleb128(p, end) + sizeof(pint_t); + printf("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", skip); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", count, skip); + break; + default: + throwf("bad rebase opcode %d", *p); + } + } + } + +} + + + + + + +template +void DyldInfoPrinter::printBindingInfoOpcodes(bool weakbinding) +{ + if ( fInfo == NULL ) { + printf("no compressed binding info\n"); + } + else if ( !weakbinding && (fInfo->bind_off() == 0) ) { + printf("no compressed binding info\n"); + } + else if ( weakbinding && (fInfo->weak_bind_off() == 0) ) { + printf("no compressed weak binding info\n"); + } + else { + const uint8_t* start; + const uint8_t* end; + if ( weakbinding ) { + printf("weak binding opcodes:\n"); + start = (uint8_t*)fHeader + fInfo->weak_bind_off(); + end = &start[fInfo->weak_bind_size()]; + } + else { + printf("binding opcodes:\n"); + start = (uint8_t*)fHeader + fInfo->bind_off(); + end = &start[fInfo->bind_size()]; + } + const uint8_t* p = start; + uint8_t type = 0; + uint8_t flags; + uint64_t address = fBaseAddress; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t segmentIndex = 0; + uint32_t count; + uint32_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + uint32_t opcodeOffset = p-start; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + printf("0x%04X BIND_OPCODE_DONE\n", opcodeOffset); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + printf("0x%04X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + flags = immediate; + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + printf("0x%04X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName); + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + printf("0x%04X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + printf("0x%04X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + address = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + skip = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND: + printf("0x%04X BIND_OPCODE_DO_BIND()\n", opcodeOffset); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + skip = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + skip = immediate*sizeof(pint_t) + sizeof(pint_t); + printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip); + break; + default: + throwf("unknown bind opcode %d", *p); + } + } + } + +} + + + +template +void DyldInfoPrinter::printBindingInfo() +{ + if ( (fInfo == NULL) || (fInfo->bind_off() == 0) ) { + printf("no compressed binding info\n"); + } + else { + printf("bind information:\n"); + printf("segment section address type weak addend dylib symbol\n"); + const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off(); + const uint8_t* end = &p[fInfo->bind_size()]; + + uint8_t type = 0; + uint8_t segIndex = 0; + uint64_t segOffset = 0; + const char* symbolName = NULL; + const char* fromDylib = "??"; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t count; + uint32_t skip; + pint_t segStartAddr = 0; + const char* segName = "??"; + const char* typeName = "??"; + const char* weak_import = ""; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + fromDylib = ordinalName(libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + fromDylib = ordinalName(libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + fromDylib = ordinalName(libraryOrdinal); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) + weak_import = "weak"; + else + weak_import = ""; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + typeName = bindTypeName(type); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segName = segmentName(segIndex); + segOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + segOffset += sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad bind opcode %d", *p); + } + } + } + +} + +template +void DyldInfoPrinter::printWeakBindingInfo() +{ + if ( (fInfo == NULL) || (fInfo->weak_bind_off() == 0) ) { + printf("no weak binding\n"); + } + else { + printf("weak binding information:\n"); + printf("segment section address type addend symbol\n"); + const uint8_t* p = (uint8_t*)fHeader + fInfo->weak_bind_off(); + const uint8_t* end = &p[fInfo->weak_bind_size()]; + + uint8_t type = 0; + uint8_t segIndex = 0; + uint64_t segOffset = 0; + const char* symbolName = NULL; + int64_t addend = 0; + uint32_t count; + uint32_t skip; + pint_t segStartAddr = 0; + const char* segName = "??"; + const char* typeName = "??"; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + if ( (immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0 ) + printf(" strong %s\n", symbolName ); + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + typeName = bindTypeName(type); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segName = segmentName(segIndex); + segOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName ); + segOffset += sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName ); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName ); + segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + printf("%-7s %-16s 0x%08llX %10s %5lld %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, symbolName ); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("unknown weak bind opcode %d", *p); + } + } + } + +} + + +template +void DyldInfoPrinter::printLazyBindingInfo() +{ + if ( fInfo == NULL ) { + printf("no compressed dyld info\n"); + } + else if ( fInfo->lazy_bind_off() == 0 ) { + printf("no compressed lazy binding info\n"); + } + else { + printf("lazy binding information:\n"); + printf("segment section address index dylib symbol\n"); + const uint8_t* const start = (uint8_t*)fHeader + fInfo->lazy_bind_off(); + const uint8_t* const end = &start[fInfo->lazy_bind_size()]; + + uint8_t type = BIND_TYPE_POINTER; + uint8_t segIndex = 0; + uint64_t segOffset = 0; + const char* symbolName = NULL; + const char* fromDylib = "??"; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t lazy_offset = 0; + pint_t segStartAddr = 0; + const char* segName = "??"; + const char* typeName = "??"; + for (const uint8_t* p=start; p < end; ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + lazy_offset = p-start; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + fromDylib = ordinalName(libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + fromDylib = ordinalName(libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + fromDylib = ordinalName(libraryOrdinal); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + typeName = bindTypeName(type); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segName = segmentName(segIndex); + segOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, lazy_offset, fromDylib, symbolName ); + segOffset += sizeof(pint_t); + break; + default: + throwf("bad lazy bind opcode %d", *p); + } + } + } + +} + +#if 0 + uint8_t type = BIND_TYPE_POINTER; + uint8_t flags; + uint64_t address = fBaseAddress; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t segmentIndex = 0; + uint32_t count; + uint32_t skip; + for (const uint8_t* p = start; p < end; ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + uint32_t opcodeOffset = p-start; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + printf("0x%08X BIND_OPCODE_DONE\n", opcodeOffset); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + printf("0x%08X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + printf("0x%08X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + printf("0x%08X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + flags = immediate; + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + printf("0x%08X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName); + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + printf("0x%08X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + printf("0x%08X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + address = read_uleb128(p, end); + printf("0x%08X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + skip = read_uleb128(p, end); + printf("0x%08X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND: + printf("0x%08X BIND_OPCODE_DO_BIND()\n", opcodeOffset); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + skip = read_uleb128(p, end); + printf("0x%08X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + skip = immediate*sizeof(pint_t) + sizeof(pint_t); + printf("0x%08X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + printf("0x%08X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip); + break; + default: + throwf("unknown bind opcode %d", *p); + } + } +#endif + +template +void DyldInfoPrinter::printLazyBindingOpcodes() +{ + if ( fInfo == NULL ) { + printf("no compressed dyld info\n"); + } + else if ( fInfo->lazy_bind_off() == 0 ) { + printf("no compressed lazy binding info\n"); + } + else { + printf("lazy binding opcodes:\n"); + const uint8_t* const start = (uint8_t*)fHeader + fInfo->lazy_bind_off(); + const uint8_t* const end = &start[fInfo->lazy_bind_size()]; + uint8_t type = BIND_TYPE_POINTER; + uint8_t flags; + uint64_t address = fBaseAddress; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t segmentIndex = 0; + uint32_t count; + uint32_t skip; + for (const uint8_t* p = start; p < end; ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + uint32_t opcodeOffset = p-start; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + printf("0x%04X BIND_OPCODE_DONE\n", opcodeOffset); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + printf("0x%04X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + flags = immediate; + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + printf("0x%04X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName); + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + printf("0x%04X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + printf("0x%04X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + address = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + skip = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND: + printf("0x%04X BIND_OPCODE_DO_BIND()\n", opcodeOffset); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + skip = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + skip = immediate*sizeof(pint_t) + sizeof(pint_t); + printf("0x%04X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + printf("0x%04X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip); + break; + default: + throwf("unknown bind opcode %d", *p); + } + } + } + +} + + +template +void DyldInfoPrinter::processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset) +{ + const uint8_t terminalSize = *p++; + const uint8_t* children = p + terminalSize; + if ( terminalSize != 0 ) { + uint32_t flags = read_uleb128(p, end); + uint64_t address = read_uleb128(p, end); + if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) + fprintf(stdout, "0x%08llX [weak_def] %s\n", address, cummulativeString); + else + fprintf(stdout, "0x%08llX %s\n", address, cummulativeString); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = read_uleb128(s, end); + processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen); + } +} + +struct SortExportsByAddress +{ + bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) + { + return ( left.address < right.address ); + } +}; + +template +void DyldInfoPrinter::printExportInfo() +{ + if ( (fInfo == NULL) || (fInfo->export_off() == 0) ) { + printf("no compressed export info\n"); + } + else { + const uint8_t* start = (uint8_t*)fHeader + fInfo->export_off(); + const uint8_t* end = &start[fInfo->export_size()]; + std::vector list; + parseTrie(start, end, list); + //std::sort(list.begin(), list.end(), SortExportsByAddress()); + for (std::vector::iterator it=list.begin(); it != list.end(); ++it) { + const char* flags = ""; + if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) + flags = "[weak_def] "; + fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name); + } + } +} + + +template +void DyldInfoPrinter::processExportGraphNode(const uint8_t* const start, const uint8_t* const end, + const uint8_t* parent, const uint8_t* p, + char* cummulativeString, int curStrOffset) +{ + const uint8_t* const me = p; + const uint8_t terminalSize = *p++; + const uint8_t* children = p + terminalSize; + if ( terminalSize != 0 ) { + uint32_t flags = read_uleb128(p, end); + uint64_t address = read_uleb128(p, end); + printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address); + } + else { + printf("\tnode%03ld;\n", (long)(me-start)); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + const char* edgeName = (char*)s; + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = read_uleb128(s, end); + printf("\tnode%03ld -> node%03d [ label=%s ] ;\n", (long)(me-start), childNodeOffet, edgeName); + processExportGraphNode(start, end, start, start+childNodeOffet, cummulativeString, curStrOffset+edgeStrLen); + } +} + +template +void DyldInfoPrinter::printExportInfoGraph() +{ + if ( (fInfo == NULL) || (fInfo->export_off() == 0) ) { + printf("no compressed export info\n"); + } + else { + const uint8_t* p = (uint8_t*)fHeader + fInfo->export_off(); + const uint8_t* end = &p[fInfo->export_size()]; + char cummulativeString[2000]; + printf("digraph {\n"); + processExportGraphNode(p, end, p, p, cummulativeString, 0); + printf("}\n"); + } +} + + + + + +static void dump(const char* path) +{ + struct stat stat_buf; + + try { + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throw "cannot open file"; + if ( ::fstat(fd, &stat_buf) != 0 ) + throwf("fstat(%s) failed, errno=%d\n", path, errno); + uint32_t length = stat_buf.st_size; + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == ((uint8_t*)(-1)) ) + throw "cannot map file"; + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + size_t offset = OSSwapBigToHostInt32(archs[i].offset); + size_t size = OSSwapBigToHostInt32(archs[i].size); + cpu_type_t cputype = OSSwapBigToHostInt32(archs[i].cputype); + if ( cputype == (uint32_t)sPreferredArch ) { + switch(cputype) { + case CPU_TYPE_POWERPC: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path); + else + throw "in universal file, ppc slice does not contain ppc mach-o"; + break; + case CPU_TYPE_I386: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path); + else + throw "in universal file, i386 slice does not contain i386 mach-o"; + break; + case CPU_TYPE_POWERPC64: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path); + else + throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; + break; + case CPU_TYPE_X86_64: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path); + else + throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; + break; + case CPU_TYPE_ARM: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path); + else + throw "in universal file, arm slice does not contain arm mach-o"; + break; + default: + throwf("in universal file, unknown architecture slice 0x%x\n", cputype); + } + } + } + } + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path); + } + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path); + } + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path); + } + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path); + } + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path); + } + else { + throw "not a known file type"; + } + } + catch (const char* msg) { + throwf("%s in %s", msg, path); + } +} + +static void usage() +{ + fprintf(stderr, "Usage: dyldinfo [-arch ] \n" + "\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n" + "\t-bind print addresses dyld will set based on symbolic lookups\n" + "\t-weak_bind print symbols which dyld must coalesce\n" + "\t-lazy_bind print addresses dyld will lazily set on first use\n" + "\t-export print addresses of all symbols this file exports\n" + "\t-opcodes print opcodes used to generate the rebase and binding information\n" + "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n" + ); +} + + +int main(int argc, const char* argv[]) +{ + if ( argc == 1 ) { + usage(); + return 0; + } + + try { + std::vector files; + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = ++i::iterator it=files.begin(); it != files.end(); ++it) { + printf("\n%s:\n", *it); + dump(*it); + } + } + } + catch (const char* msg) { + fprintf(stderr, "dyldinfo failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/ld64/src/machochecker.cpp b/ld64/src/other/machochecker.cpp similarity index 97% rename from ld64/src/machochecker.cpp rename to ld64/src/other/machochecker.cpp index 311809b..cb4a781 100644 --- a/ld64/src/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -285,6 +286,8 @@ void MachOChecker::checkLoadCommands() case LC_REEXPORT_DYLIB: case LC_SEGMENT_SPLIT_INFO: case LC_CODE_SIGNATURE: + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: break; case LC_ENCRYPTION_INFO: encryption_info = (macho_encryption_info_command

*)cmd; @@ -583,12 +586,26 @@ void MachOChecker::checkSymbolTable() StringSet externalNames; const macho_nlist

* const exportedStart = &fSymbols[fDynamicSymbolTable->iextdefsym()]; const macho_nlist

* const exportedEnd = &exportedStart[fDynamicSymbolTable->nextdefsym()]; - for(const macho_nlist

* p = exportedStart; p < exportedEnd; ++p) { + int i = fDynamicSymbolTable->iextdefsym(); + for(const macho_nlist

* p = exportedStart; p < exportedEnd; ++p, ++i) { const char* symName = &fStrings[p->n_strx()]; + if ( symName > fStringsEnd ) + throw "string index out of range"; + //fprintf(stderr, "sym[%d] = %s\n", i, symName); if ( externalNames.find(symName) != externalNames.end() ) throwf("duplicate external symbol: %s", symName); externalNames.insert(symName); } + // verify no undefines with same name as an external symbol + const macho_nlist

* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()]; + const macho_nlist

* const undefinesEnd = &undefinesStart[fDynamicSymbolTable->nundefsym()]; + for(const macho_nlist

* p = undefinesStart; p < undefinesEnd; ++p) { + const char* symName = &fStrings[p->n_strx()]; + if ( symName > fStringsEnd ) + throw "string index out of range"; + if ( externalNames.find(symName) != externalNames.end() ) + throwf("undefine with same name as external symbol: %s", symName); + } } } @@ -859,7 +876,8 @@ static void check(const char* path) int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) throw "cannot open file"; - ::fstat(fd, &stat_buf); + if ( ::fstat(fd, &stat_buf) != 0 ) + throwf("fstat(%s) failed, errno=%d\n", path, errno); uint32_t length = stat_buf.st_size; uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); if ( p == ((uint8_t*)(-1)) ) diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h b/ld64/src/other/prune_trie.h similarity index 53% rename from ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h rename to ld64/src/other/prune_trie.h index 5bb7c28..23af95e 100644 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing/func.h +++ b/ld64/src/other/prune_trie.h @@ -22,22 +22,33 @@ * @APPLE_LICENSE_HEADER_END@ */ -extern int global; -extern void foo(int); +#include -// this weak func() will have unwind info and a LSDA -inline int func() -{ - global = 1; - try { - foo(1); - global = 2; - } - catch (int x) { - foo(2); - global = 3; - } - return global; -} +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* + * prune_trie() is a C vended function that is used by strip(1) to prune out + * defined exported symbols from the export trie. It is passed a pointer to + * the start of bytes of the the trie and the size. The prune() funciton + * passed is called with each symbol name in the trie to determine if it is + * to be pruned (retuning 1) or not (returning 0). It writes the new trie + * back into the trie buffer and returns the new size in trie_new_size. + * If the pruning succeeds, NULL is returned. If there was an error processing + * the trie (e.f. it is malformed), then an error message string is returned. + * The error string can be freed. + */ +extern const char* +prune_trie( + uint8_t* trie_start, + uint32_t trie_start_size, + int (*prune)(const char *name), + uint32_t* trie_new_size); + +#if __cplusplus +} +#endif /* __cplusplus */ diff --git a/ld64/FireOpal/src/rebase.cpp b/ld64/src/other/rebase.cpp similarity index 83% rename from ld64/FireOpal/src/rebase.cpp rename to ld64/src/other/rebase.cpp index ad9b905..f8dc1ee 100644 --- a/ld64/FireOpal/src/rebase.cpp +++ b/ld64/src/other/rebase.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -91,11 +91,11 @@ class Rebaser : public AbstractRebaser void adjustDATA(); void doLocalRelocation(const macho_relocation_info

* reloc); pint_t* mappedAddressForVMAddress(uint32_t vmaddress); + void rebaseAt(int segIndex, uint64_t offset, uint8_t type); const macho_header

* fHeader; pint_t fOrignalVMRelocBaseAddress; pint_t fSlide; - pint_t fRelocBase; std::vector fVMMApping; }; @@ -124,7 +124,7 @@ MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) // map in whole file int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); if ( fd == -1 ) - throwf("can't open file, errno=%d", errno); + throwf("can't open file %s, errno=%d", path, errno); struct stat stat_buf; if ( fstat(fd, &stat_buf) == -1) throwf("can't stat open file %s, errno=%d", path, errno); @@ -411,10 +411,72 @@ void Rebaser::adjustSymbolTable() // FIXME ¥¥¥ adjust dylib_module if it exists } +static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throwf("uleb128 too big"); + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + +template +void Rebaser::rebaseAt(int segIndex, uint64_t offset, uint8_t type) +{ + //fprintf(stderr, "rebaseAt(seg=%d, offset=0x%08llX, type=%d\n", segIndex, offset, type); + static int lastSegIndex = -1; + static uint8_t* lastSegMappedStart = NULL; + if ( segIndex != lastSegIndex ) { + const macho_load_command

* const cmds = (macho_load_command

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

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

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

::CMD ) { + if ( segIndex == segCount ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + lastSegMappedStart = (uint8_t*)fHeader + seg->fileoff(); + lastSegIndex == segCount; + break; + } + ++segCount; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + } + + pint_t* locationToFix = (pint_t*)(lastSegMappedStart+offset); + uint32_t* locationToFix32 = (uint32_t*)(lastSegMappedStart+offset); + switch (type) { + case REBASE_TYPE_POINTER: + P::setP(*locationToFix, A::P::getP(*locationToFix) + fSlide); + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + E::set32(*locationToFix32, E::get32(*locationToFix32) + fSlide); + break; + default: + throwf("bad rebase type %d", type); + } +} + + template void Rebaser::adjustDATA() { const macho_dysymtab_command

* dysymtab = NULL; + const macho_dyld_info_command

* dyldInfo = NULL; // get symbol table info const macho_load_command

* const cmds = (macho_load_command

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

)); @@ -425,41 +487,113 @@ void Rebaser::adjustDATA() case LC_DYSYMTAB: dysymtab = (macho_dysymtab_command

*)cmd; break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); } - // walk all local relocations and slide every pointer - const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + dysymtab->locreloff()); - const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; - for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - this->doLocalRelocation(reloc); + // use new encoding of rebase info if present + if ( dyldInfo != NULL ) { + if ( dyldInfo->rebase_size() != 0 ) { + const uint8_t* p = (uint8_t*)fHeader + dyldInfo->rebase_off(); + const uint8_t* end = &p[dyldInfo->rebase_size()]; + + uint8_t type = 0; + uint64_t offset = 0; + uint32_t count; + uint32_t skip; + int segIndex; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + offset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + offset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + offset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + rebaseAt(segIndex, offset, type); + offset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + rebaseAt(segIndex, offset, type); + offset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + rebaseAt(segIndex, offset, type); + offset += read_uleb128(p, end) + sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + rebaseAt(segIndex, offset, type); + offset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad rebase opcode %d", *p); + } + } + + + + } } - - // walk non-lazy-pointers and slide the ones that are LOCAL - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - const macho_segment_command

* seg = (macho_segment_command

*)cmd; - const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); - for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - const uint32_t indirectTableOffset = sect->reserved1(); - uint32_t pointerCount = sect->size() / sizeof(pint_t); - pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); - for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { - if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { - P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); + else { + // walk all local relocations and slide every pointer + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + dysymtab->locreloff()); + const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + this->doLocalRelocation(reloc); + } + + // walk non-lazy-pointers and slide the ones that are LOCAL + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); + for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + uint32_t pointerCount = sect->size() / sizeof(pint_t); + pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); + for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { + if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { + P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); + } } } } } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + } } @@ -567,7 +701,6 @@ template void Rebaser::setRelocBase() { // reloc addresses are from the start of the mapped file (base address) - fRelocBase = (pint_t)fHeader; fOrignalVMRelocBaseAddress = this->getBaseAddress(); //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress); } @@ -587,7 +720,6 @@ void Rebaser::setRelocBase() if ( segCmd->initprot() & VM_PROT_WRITE ) { if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) { // found writable segment with address > 4GB past base address - fRelocBase = segCmd->fileoff() + (pint_t)fHeader; fOrignalVMRelocBaseAddress = segCmd->vmaddr(); return; } @@ -596,7 +728,6 @@ void Rebaser::setRelocBase() cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } // just use base address - fRelocBase = (pint_t)fHeader; fOrignalVMRelocBaseAddress = this->getBaseAddress(); } @@ -611,7 +742,6 @@ void Rebaser::setRelocBase() if ( cmd->cmd() == macho_segment_command

::CMD ) { const macho_segment_command

* segCmd = (const macho_segment_command

*)cmd; if ( segCmd->initprot() & VM_PROT_WRITE ) { - fRelocBase = segCmd->fileoff() + (pint_t)fHeader; fOrignalVMRelocBaseAddress = segCmd->vmaddr(); return; } diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp new file mode 100644 index 0000000..098d932 --- /dev/null +++ b/ld64/src/other/unwinddump.cpp @@ -0,0 +1,926 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + + __attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +template +class UnwindPrinter +{ +public: + static bool validFile(const uint8_t* fileContent); + static UnwindPrinter* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) + { return new UnwindPrinter(fileContent, fileLength, path); } + virtual ~UnwindPrinter() {} + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + + typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + + UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path); + bool findUnwindSection(); + void printUnwindSection(); + void getSymbolTableInfo(); + const char* functionName(pint_t addr); + static const char* archName(); + static void decode(uint32_t encoding, const uint8_t* funcStart, char* str); + + const char* fPath; + const macho_header

* fHeader; + uint64_t fLength; + const macho_section

* fUnwindSection; + const char* fStrings; + const char* fStringsEnd; + const macho_nlist

* fSymbols; + uint32_t fSymbolCount; + pint_t fMachHeaderAddress; +}; + + +template <> const char* UnwindPrinter::archName() { return "ppc"; } +template <> const char* UnwindPrinter::archName() { return "ppc64"; } +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) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool UnwindPrinter::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_X86_64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} + +template <> +bool UnwindPrinter::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: + return true; + } + return false; +} + + +template +UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) + : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL), + fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0) +{ + // sanity check + if ( ! validFile(fileContent) ) + throw "not a mach-o file that can be checked"; + + fPath = strdup(path); + fHeader = (const macho_header

*)fileContent; + + getSymbolTableInfo(); + + if ( findUnwindSection() ) + printUnwindSection(); +} + + +template +void UnwindPrinter::getSymbolTableInfo() +{ + 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(); + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > endOfLoadCommands ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + if ( cmd->cmd() == LC_SYMTAB) { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); + fStrings = (char*)fHeader + symtab->stroff(); + fStringsEnd = fStrings + symtab->strsize(); + } + cmd = (const macho_load_command

*)endOfCmd; + } +} + +template +const char* UnwindPrinter::functionName(pint_t addr) +{ + for (uint32_t i=0; i < fSymbolCount; ++i) { + uint8_t type = fSymbols[i].n_type(); + if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) { + if ( fSymbols[i].n_value() == addr ) { + const char* r = &fStrings[fSymbols[i].n_strx()]; + //fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r); + return r; + } + } + } + return "--anonymous function--"; +} + + + +template +bool UnwindPrinter::findUnwindSection() +{ + 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(); + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > endOfLoadCommands ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + if ( cmd->cmd() == macho_segment_command

::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) { + if ( (strcmp(sect->sectname(), "__unwind_info") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { + fUnwindSection = sect; + fMachHeaderAddress = segCmd->vmaddr(); + return fUnwindSection; + } + } + } + cmd = (const macho_load_command

*)endOfCmd; + } + return false; +} + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + + +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + *str = '\0'; + switch ( encoding & UNWIND_X86_64_MODE_MASK ) { + case UNWIND_X86_64_MODE_RBP_FRAME: + { + uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + if ( savedRegistersLocations == 0 ) { + strcpy(str, "rbp frame, no saved registers"); + } + else { + sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8); + bool needComma = false; + for (int i=0; i < 5; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + strcat(str, "-"); + break; + case UNWIND_X86_64_REG_RBX: + strcat(str, "rbx"); + break; + case UNWIND_X86_64_REG_R12: + strcat(str, "r12"); + break; + case UNWIND_X86_64_REG_R13: + strcat(str, "r13"); + break; + case UNWIND_X86_64_REG_R14: + strcat(str, "r14"); + break; + case UNWIND_X86_64_REG_R15: + strcat(str, "r15"); + break; + default: + strcat(str, "r?"); + } + savedRegistersLocations = (savedRegistersLocations >> 3); + if ( savedRegistersLocations == 0 ) + break; + } + } + } + break; + case UNWIND_X86_64_MODE_STACK_IMMD: + case UNWIND_X86_64_MODE_STACK_IND: + { + uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize))); + sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust); + } + else { + sprintf(str, "stack size=%d, ", stackSize*8); + } + if ( regCount == 0 ) { + strcat(str, "no registers saved"); + } + else { + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // renumber registers back to standard numbers + int registers[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (int i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registers[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + bool needComma = false; + for (int i=0; i < regCount; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch ( registers[i] ) { + case UNWIND_X86_64_REG_RBX: + strcat(str, "rbx"); + break; + case UNWIND_X86_64_REG_R12: + strcat(str, "r12"); + break; + case UNWIND_X86_64_REG_R13: + strcat(str, "r13"); + break; + case UNWIND_X86_64_REG_R14: + strcat(str, "r14"); + break; + case UNWIND_X86_64_REG_R15: + strcat(str, "r15"); + break; + case UNWIND_X86_64_REG_RBP: + strcat(str, "rbp"); + break; + default: + strcat(str, "r??"); + } + } + } + } + break; + case UNWIND_X86_64_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET); + break; + default: + if ( encoding == 0 ) + strcat(str, "no unwind information"); + else + strcat(str, "tbd "); + } + if ( encoding & UNWIND_HAS_LSDA ) { + strcat(str, " LSDA"); + } + +} + +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + *str = '\0'; + switch ( encoding & UNWIND_X86_MODE_MASK ) { + case UNWIND_X86_MODE_EBP_FRAME: + { + uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); + if ( savedRegistersLocations == 0 ) { + strcpy(str, "ebp frame, no saved registers"); + } + else { + sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4); + bool needComma = false; + for (int i=0; i < 5; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + strcat(str, "-"); + break; + case UNWIND_X86_REG_EBX: + strcat(str, "ebx"); + break; + case UNWIND_X86_REG_ECX: + strcat(str, "ecx"); + break; + case UNWIND_X86_REG_EDX: + strcat(str, "edx"); + break; + case UNWIND_X86_REG_EDI: + strcat(str, "edi"); + break; + case UNWIND_X86_REG_ESI: + strcat(str, "esi"); + break; + default: + strcat(str, "e??"); + } + savedRegistersLocations = (savedRegistersLocations >> 3); + if ( savedRegistersLocations == 0 ) + break; + } + } + } + break; + case UNWIND_X86_MODE_STACK_IMMD: + case UNWIND_X86_MODE_STACK_IND: + { + uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize))); + sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust); + } + else { + sprintf(str, "stack size=%d, ", stackSize*4); + } + if ( regCount == 0 ) { + strcat(str, "no saved regs"); + } + else { + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // renumber registers back to standard numbers + int registers[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (int i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registers[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + bool needComma = false; + for (int i=0; i < regCount; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch ( registers[i] ) { + case UNWIND_X86_REG_EBX: + strcat(str, "ebx"); + break; + case UNWIND_X86_REG_ECX: + strcat(str, "ecx"); + break; + case UNWIND_X86_REG_EDX: + strcat(str, "edx"); + break; + case UNWIND_X86_REG_EDI: + strcat(str, "edi"); + break; + case UNWIND_X86_REG_ESI: + strcat(str, "esi"); + break; + case UNWIND_X86_REG_EBP: + strcat(str, "ebp"); + break; + default: + strcat(str, "e??"); + } + } + } + } + break; + case UNWIND_X86_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET); + break; + default: + if ( encoding == 0 ) + strcat(str, "no unwind information"); + else + strcat(str, "tbd "); + } + if ( encoding & UNWIND_HAS_LSDA ) { + strcat(str, " LSDA"); + } + +} + + +template +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + + +} + +template +void UnwindPrinter::printUnwindSection() +{ + const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset(); + macho_unwind_info_section_header

* sectionHeader = (macho_unwind_info_section_header

*)(sectionContent); + + printf("Arch: %s, Section: __TEXT,__unwind_info (addr=0x%08llX, size=0x%08llX, fileOffset=0x%08X)\n", + archName(), fUnwindSection->addr(), fUnwindSection->size(), fUnwindSection->offset()); + printf("\tversion=0x%08X\n", sectionHeader->version()); + printf("\tcommonEncodingsArraySectionOffset=0x%08X\n", sectionHeader->commonEncodingsArraySectionOffset()); + printf("\tcommonEncodingsArrayCount=0x%08X\n", sectionHeader->commonEncodingsArrayCount()); + printf("\tpersonalityArraySectionOffset=0x%08X\n", sectionHeader->personalityArraySectionOffset()); + printf("\tpersonalityArrayCount=0x%08X\n", sectionHeader->personalityArrayCount()); + printf("\tindexSectionOffset=0x%08X\n", sectionHeader->indexSectionOffset()); + printf("\tindexCount=0x%08X\n", sectionHeader->indexCount()); + printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount()); + const uint32_t* commonEncodings = (uint32_t*)§ionContent[sectionHeader->commonEncodingsArraySectionOffset()]; + for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) { + printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i])); + } + printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount()); + const uint32_t* personalityArray = (uint32_t*)§ionContent[sectionHeader->personalityArraySectionOffset()]; + for (uint32_t i=0; i < sectionHeader->personalityArrayCount(); ++i) { + printf("\t\t[%2u]=0x%08X\n", i+1, A::P::E::get32(personalityArray[i])); + } + printf("\tfirst level index: (count=%u)\n", sectionHeader->indexCount()); + macho_unwind_info_section_header_index_entry

* indexes = (macho_unwind_info_section_header_index_entry

*)§ionContent[sectionHeader->indexSectionOffset()]; + for (uint32_t i=0; i < sectionHeader->indexCount(); ++i) { + printf("\t\t[%2u] funcOffset=0x%08X, pageOffset=0x%08X, lsdaOffset=0x%08X\n", + i, indexes[i].functionOffset(), indexes[i].secondLevelPagesSectionOffset(), indexes[i].lsdaIndexArraySectionOffset()); + } + uint32_t lsdaIndexArraySectionOffset = indexes[0].lsdaIndexArraySectionOffset(); + uint32_t lsdaIndexArrayEndSectionOffset = indexes[sectionHeader->indexCount()-1].lsdaIndexArraySectionOffset(); + uint32_t lsdaIndexArrayCount = (lsdaIndexArrayEndSectionOffset-lsdaIndexArraySectionOffset)/sizeof(macho_unwind_info_section_header_lsda_index_entry

); + printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount); + macho_unwind_info_section_header_lsda_index_entry

* lindex = (macho_unwind_info_section_header_lsda_index_entry

*)§ionContent[lsdaIndexArraySectionOffset]; + for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) { + printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", + i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), functionName(lindex[i].functionOffset()+fMachHeaderAddress)); + if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF ) + fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress)); + } + for (uint32_t i=0; i < sectionHeader->indexCount()-1; ++i) { + printf("\tsecond level index[%u] sectionOffset=0x%08X, count=%u, fileOffset=0x%08X\n", i, indexes[i].secondLevelPagesSectionOffset(), + sectionHeader->indexCount(), fUnwindSection->offset()+indexes[i].secondLevelPagesSectionOffset()); + macho_unwind_info_regular_second_level_page_header

* page = (macho_unwind_info_regular_second_level_page_header

*)§ionContent[indexes[i].secondLevelPagesSectionOffset()]; + if ( page->kind() == UNWIND_SECOND_LEVEL_REGULAR ) { + printf("\t\tkind=UNWIND_SECOND_LEVEL_REGULAR\n"); + printf("\t\tentryPageOffset=0x%08X\n", page->entryPageOffset()); + printf("\t\tentryCount=0x%08X\n", page->entryCount()); + const macho_unwind_info_regular_second_level_entry

* entry = (macho_unwind_info_regular_second_level_entry

*)((char*)page+page->entryPageOffset()); + for (uint32_t j=0; j < page->entryCount(); ++j) { + uint32_t funcOffset = entry[j].functionOffset(); + if ( entry[j].encoding() & UNWIND_HAS_LSDA ) { + // verify there is a corresponding entry in lsda table + bool found = false; + for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) { + if ( lindex[k].functionOffset() == funcOffset ) { + found = true; + break; + } + } + if ( !found ) { + fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress)); + } + } + char encodingString[100]; + decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", + j, funcOffset, entry[j].encoding(), encodingString, functionName(funcOffset+fMachHeaderAddress)); + } + } + else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) { + macho_unwind_info_compressed_second_level_page_header

* cp = (macho_unwind_info_compressed_second_level_page_header

*)page; + printf("\t\tkind=UNWIND_SECOND_LEVEL_COMPRESSED\n"); + printf("\t\tentryPageOffset=0x%08X\n", cp->entryPageOffset()); + printf("\t\tentryCount=0x%08X\n", cp->entryCount()); + printf("\t\tencodingsPageOffset=0x%08X\n", cp->encodingsPageOffset()); + printf("\t\tencodingsCount=0x%08X\n", cp->encodingsCount()); + const uint32_t* entries = (uint32_t*)(((uint8_t*)page)+cp->entryPageOffset()); + const uint32_t* encodings = (uint32_t*)(((uint8_t*)page)+cp->encodingsPageOffset()); + const uint32_t baseFunctionOffset = indexes[i].functionOffset(); + for (uint32_t j=0; j < cp->entryCount(); ++j) { + uint8_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entries[j]); + uint32_t encoding; + if ( encodingIndex < sectionHeader->commonEncodingsArrayCount() ) + encoding = A::P::E::get32(commonEncodings[encodingIndex]); + else + encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]); + char encodingString[100]; + uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset; + decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString); + const char* name = functionName(funcOff+fMachHeaderAddress); + if ( encoding & UNWIND_HAS_LSDA ) { + // verify there is a corresponding entry in lsda table + bool found = false; + for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) { + if ( lindex[k].functionOffset() == funcOff ) { + found = true; + break; + } + } + if ( !found ) { + fprintf(stderr, "MISSING LSDA entry for %s\n", name); + } + } + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n", + j, funcOff, encodingIndex, encoding, encodingString, name); + } + } + else { + fprintf(stderr, "\t\tbad page header\n"); + } + } + +} + +static void dump(const char* path, const std::set& onlyArchs) +{ + struct stat stat_buf; + + try { + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throw "cannot open file"; + if ( ::fstat(fd, &stat_buf) != 0 ) + throwf("fstat(%s) failed, errno=%d\n", path, errno); + uint32_t length = stat_buf.st_size; + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == ((uint8_t*)(-1)) ) + throw "cannot map file"; + ::close(fd); + const mach_header* mh = (mach_header*)p; + if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + const struct fat_header* fh = (struct fat_header*)p; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + size_t offset = OSSwapBigToHostInt32(archs[i].offset); + size_t size = OSSwapBigToHostInt32(archs[i].size); + unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); + if ( onlyArchs.count(cputype) ) { + switch(cputype) { + case CPU_TYPE_POWERPC: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path); + else + throw "in universal file, ppc slice does not contain ppc mach-o"; + break; + case CPU_TYPE_I386: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path); + else + throw "in universal file, i386 slice does not contain i386 mach-o"; + break; + case CPU_TYPE_POWERPC64: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path); + else + throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; + break; + case CPU_TYPE_X86_64: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path); + else + throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; + break; + case CPU_TYPE_ARM: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path); + else + throw "in universal file, arm slice does not contain arm mach-o"; + break; + default: + throwf("in universal file, unknown architecture slice 0x%x\n", cputype); + } + } + } + } + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) { + UnwindPrinter::make(p, length, path); + } + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) { + UnwindPrinter::make(p, length, path); + } + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) { + UnwindPrinter::make(p, length, path); + } + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) { + UnwindPrinter::make(p, length, path); + } + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) { + UnwindPrinter::make(p, length, path); + } + else { + throw "not a known file type"; + } + } + catch (const char* msg) { + throwf("%s in %s", msg, path); + } +} + + +int main(int argc, const char* argv[]) +{ + std::set onlyArchs; + std::vector files; + + try { + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = argv[++i]; + if ( strcmp(arch, "ppc") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC); + else if ( strcmp(arch, "ppc64") == 0 ) + onlyArchs.insert(CPU_TYPE_POWERPC64); + else if ( strcmp(arch, "i386") == 0 ) + onlyArchs.insert(CPU_TYPE_I386); + else if ( strcmp(arch, "x86_64") == 0 ) + onlyArchs.insert(CPU_TYPE_X86_64); + else if ( strcmp(arch, "arm") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM); + else + throwf("unknown architecture %s", arch); + } + else { + throwf("unknown option: %s\n", arg); + } + } + else { + files.push_back(arg); + } + } + + // use all architectures if no restrictions specified + if ( onlyArchs.size() == 0 ) { + onlyArchs.insert(CPU_TYPE_POWERPC); + onlyArchs.insert(CPU_TYPE_POWERPC64); + onlyArchs.insert(CPU_TYPE_I386); + onlyArchs.insert(CPU_TYPE_X86_64); + onlyArchs.insert(CPU_TYPE_ARM); + } + + // process each file + for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { + dump(*it, onlyArchs); + } + + } + catch (const char* msg) { + fprintf(stderr, "UnwindDump failed: %s\n", msg); + return 1; + } + + return 0; +} + + + diff --git a/ld64/src/rebase.cpp b/ld64/src/rebase.cpp deleted file mode 100644 index ad9b905..0000000 --- a/ld64/src/rebase.cpp +++ /dev/null @@ -1,945 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" - -static bool verbose = false; - -__attribute__((noreturn)) -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - - -class AbstractRebaser -{ -public: - virtual cpu_type_t getArchitecture() const = 0; - virtual uint64_t getBaseAddress() const = 0; - virtual uint64_t getVMSize() const = 0; - virtual void setBaseAddress(uint64_t) = 0; -}; - - -template -class Rebaser : public AbstractRebaser -{ -public: - Rebaser(const void* machHeader); - virtual ~Rebaser() {} - - virtual cpu_type_t getArchitecture() const; - virtual uint64_t getBaseAddress() const; - virtual uint64_t getVMSize() const; - virtual void setBaseAddress(uint64_t); - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - struct vmmap { pint_t vmaddr; pint_t vmsize; pint_t fileoff; }; - - void setRelocBase(); - void buildSectionTable(); - void adjustLoadCommands(); - void adjustSymbolTable(); - void adjustDATA(); - void doLocalRelocation(const macho_relocation_info

* reloc); - pint_t* mappedAddressForVMAddress(uint32_t vmaddress); - - const macho_header

* fHeader; - pint_t fOrignalVMRelocBaseAddress; - pint_t fSlide; - pint_t fRelocBase; - std::vector fVMMApping; -}; - - - -class MultiArchRebaser -{ -public: - MultiArchRebaser(const char* path, bool writable=false); - ~MultiArchRebaser(); - - const std::vector& getArchs() const { return fRebasers; } - void commit(); - -private: - std::vector fRebasers; - void* fMappingAddress; - uint64_t fFileSize; -}; - - - -MultiArchRebaser::MultiArchRebaser(const char* path, bool writable) - : fMappingAddress(0), fFileSize(0) -{ - // map in whole file - int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); - if ( fd == -1 ) - throwf("can't open file, errno=%d", errno); - struct stat stat_buf; - if ( fstat(fd, &stat_buf) == -1) - throwf("can't stat open file %s, errno=%d", path, errno); - if ( stat_buf.st_size < 20 ) - throwf("file too small %s", path); - const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; - const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); - uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); - if ( p == (uint8_t*)(-1) ) - throwf("can't map file %s, errno=%d", path, errno); - ::close(fd); - - // if fat file, process each architecture - const fat_header* fh = (fat_header*)p; - const mach_header* mh = (mach_header*)p; - if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - // Fat header is always big-endian - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); - try { - switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { - case CPU_TYPE_POWERPC: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; - case CPU_TYPE_POWERPC64: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; - case CPU_TYPE_I386: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; - case CPU_TYPE_X86_64: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; - case CPU_TYPE_ARM: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; - default: - throw "unknown file format"; - } - } - catch (const char* msg) { - fprintf(stderr, "rebase warning: %s for %s\n", msg, path); - } - } - } - else { - try { - if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { - fRebasers.push_back(new Rebaser(mh)); - } - else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { - fRebasers.push_back(new Rebaser(mh)); - } - else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { - fRebasers.push_back(new Rebaser(mh)); - } - else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { - fRebasers.push_back(new Rebaser(mh)); - } - else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { - fRebasers.push_back(new Rebaser(mh)); - } - else { - throw "unknown file format"; - } - } - catch (const char* msg) { - fprintf(stderr, "rebase warning: %s for %s\n", msg, path); - } - } - - fMappingAddress = p; - fFileSize = stat_buf.st_size; -} - - -MultiArchRebaser::~MultiArchRebaser() -{ - ::munmap(fMappingAddress, fFileSize); -} - -void MultiArchRebaser::commit() -{ - ::msync(fMappingAddress, fFileSize, MS_ASYNC); -} - - - -template -Rebaser::Rebaser(const void* machHeader) - : fHeader((const macho_header

*)machHeader) -{ - switch ( fHeader->filetype() ) { - case MH_DYLIB: - if ( (fHeader->flags() & MH_SPLIT_SEGS) != 0 ) - throw "split-seg dylibs cannot be rebased"; - break; - case MH_BUNDLE: - break; - default: - throw "file is not a dylib or bundle"; - } - -} - -template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC; } -template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC64; } -template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } -template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } -template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM; } - -template -uint64_t Rebaser::getBaseAddress() const -{ - uint64_t lowestSegmentAddress = LLONG_MAX; - 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; - if ( segCmd->vmaddr() < lowestSegmentAddress ) { - lowestSegmentAddress = segCmd->vmaddr(); - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - return lowestSegmentAddress; -} - -template -uint64_t Rebaser::getVMSize() const -{ - const macho_segment_command

* highestSegmentCmd = NULL; - 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; - if ( (highestSegmentCmd == NULL) || (segCmd->vmaddr() > highestSegmentCmd->vmaddr()) ) { - highestSegmentCmd = segCmd; - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - - return ((highestSegmentCmd->vmaddr() + highestSegmentCmd->vmsize() - this->getBaseAddress() + 4095) & (-4096)); -} - - -template -void Rebaser::setBaseAddress(uint64_t addr) -{ - // calculate slide - fSlide = addr - this->getBaseAddress(); - - // compute base address for relocations - this->setRelocBase(); - - // build cache of section index to section - this->buildSectionTable(); - - // update load commands - this->adjustLoadCommands(); - - // update symbol table - this->adjustSymbolTable(); - - // update writable segments that have internal pointers - this->adjustDATA(); -} - -template -void Rebaser::adjustLoadCommands() -{ - const macho_segment_command

* highestSegmentCmd = NULL; - 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) { - switch ( cmd->cmd() ) { - case LC_ID_DYLIB: - if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { - // clear timestamp so that any prebound clients are invalidated - macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; - dylib->set_timestamp(1); - } - break; - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { - // clear expected timestamps so that this image will load with invalid prebinding - macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; - dylib->set_timestamp(2); - } - break; - case macho_routines_command

::CMD: - // update -init command - { - struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; - routines->set_init_address(routines->init_address() + fSlide); - } - break; - case macho_segment_command

::CMD: - // update segment commands - { - macho_segment_command

* seg = (macho_segment_command

*)cmd; - seg->set_vmaddr(seg->vmaddr() + fSlide); - macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); - macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - sect->set_addr(sect->addr() + fSlide); - } - } - break; - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } -} - - -template -void Rebaser::buildSectionTable() -{ - // build vector of sections - 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

* seg = (macho_segment_command

*)cmd; - vmmap mapping; - mapping.vmaddr = seg->vmaddr(); - mapping.vmsize = seg->vmsize(); - mapping.fileoff = seg->fileoff(); - fVMMApping.push_back(mapping); - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } -} - - -template -void Rebaser::adjustSymbolTable() -{ - const macho_dysymtab_command

* dysymtab = NULL; - macho_nlist

* symbolTable = NULL; - - // get symbol table info - 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) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - symbolTable = (macho_nlist

*)(((uint8_t*)fHeader) + symtab->symoff()); - } - break; - case LC_DYSYMTAB: - dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - - // walk all exports and slide their n_value - macho_nlist

* lastExport = &symbolTable[dysymtab->iextdefsym()+dysymtab->nextdefsym()]; - for (macho_nlist

* entry = &symbolTable[dysymtab->iextdefsym()]; entry < lastExport; ++entry) { - if ( (entry->n_type() & N_TYPE) == N_SECT ) - entry->set_n_value(entry->n_value() + fSlide); - } - - // walk all local symbols and slide their n_value - macho_nlist

* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; - for (macho_nlist

* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { - if ( entry->n_sect() != NO_SECT ) - entry->set_n_value(entry->n_value() + fSlide); - } - - // FIXME ¥¥¥ adjust dylib_module if it exists -} - -template -void Rebaser::adjustDATA() -{ - const macho_dysymtab_command

* dysymtab = NULL; - - // get symbol table info - 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) { - switch (cmd->cmd()) { - case LC_DYSYMTAB: - dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - - // walk all local relocations and slide every pointer - const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + dysymtab->locreloff()); - const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; - for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - this->doLocalRelocation(reloc); - } - - // walk non-lazy-pointers and slide the ones that are LOCAL - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - const macho_segment_command

* seg = (macho_segment_command

*)cmd; - const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)fHeader) + dysymtab->indirectsymoff()); - for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - const uint32_t indirectTableOffset = sect->reserved1(); - uint32_t pointerCount = sect->size() / sizeof(pint_t); - pint_t* nonLazyPointer = (pint_t*)(((uint8_t*)fHeader) + sect->offset()); - for (uint32_t i=0; i < pointerCount; ++i, ++nonLazyPointer) { - if ( E::get32(indirectTable[indirectTableOffset + i]) == INDIRECT_SYMBOL_LOCAL ) { - P::setP(*nonLazyPointer, A::P::getP(*nonLazyPointer) + fSlide); - } - } - } - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - -} - - -template -typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(uint32_t vmaddress) -{ - for(typename std::vector::iterator it = fVMMApping.begin(); it != fVMMApping.end(); ++it) { - //fprintf(stderr, "vmaddr=0x%08lX, vmsize=0x%08lX\n", it->vmaddr, it->vmsize); - if ( (vmaddress >= it->vmaddr) && (vmaddress < (it->vmaddr+it->vmsize)) ) { - return (pint_t*)((vmaddress - it->vmaddr) + it->fileoff + (uint8_t*)fHeader); - } - } - throwf("reloc address 0x%08X not found", vmaddress); -} - - -template <> -void Rebaser::doLocalRelocation(const macho_relocation_info* reloc) -{ - if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) { - pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); - P::setP(*addr, P::getP(*addr) + fSlide); - } - else { - throw "invalid relocation type"; - } -} - -template <> -void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) -{ - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { - pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); - P::setP(*addr, P::getP(*addr) + fSlide); - } - } - else { - macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { - sreloc->set_r_value( sreloc->r_value() + fSlide ); - } - else { - throw "cannot rebase final linked image with scattered relocations"; - } - } -} - -template <> -void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) -{ - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { - pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); - P::setP(*addr, P::getP(*addr) + fSlide); - } - } - else { - macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) { - sreloc->set_r_value( sreloc->r_value() + fSlide ); - } - else { - throw "cannot rebase final linked image with scattered relocations"; - } - } -} - -template <> -void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) -{ - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - if ( reloc->r_type() == ARM_RELOC_VANILLA ) { - pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); - P::setP(*addr, P::getP(*addr) + fSlide); - } - } - else { - macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - if ( sreloc->r_type() == ARM_RELOC_PB_LA_PTR ) { - sreloc->set_r_value( sreloc->r_value() + fSlide ); - } - else { - throw "cannot rebase final linked image with scattered relocations"; - } - } -} - -template -void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) -{ - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { - pint_t* addr = mappedAddressForVMAddress(reloc->r_address() + fOrignalVMRelocBaseAddress); - P::setP(*addr, P::getP(*addr) + fSlide); - } - } - else { - throw "cannot rebase final linked image with scattered relocations"; - } -} - - -template -void Rebaser::setRelocBase() -{ - // reloc addresses are from the start of the mapped file (base address) - fRelocBase = (pint_t)fHeader; - fOrignalVMRelocBaseAddress = this->getBaseAddress(); - //fprintf(stderr, "fOrignalVMRelocBaseAddress=0x%08X\n", fOrignalVMRelocBaseAddress); -} - -template <> -void Rebaser::setRelocBase() -{ - // reloc addresses either: - // 1) from the base address if no writable segment is > 4GB from base address - // 2) from start of first writable segment - const macho_load_command

* 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; - if ( segCmd->initprot() & VM_PROT_WRITE ) { - if ( (segCmd->vmaddr() + segCmd->vmsize() - this->getBaseAddress()) > 0x100000000ULL ) { - // found writable segment with address > 4GB past base address - fRelocBase = segCmd->fileoff() + (pint_t)fHeader; - fOrignalVMRelocBaseAddress = segCmd->vmaddr(); - return; - } - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - // just use base address - fRelocBase = (pint_t)fHeader; - fOrignalVMRelocBaseAddress = this->getBaseAddress(); -} - -template <> -void Rebaser::setRelocBase() -{ - // reloc addresses are always based from the start of the first writable segment - 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; - if ( segCmd->initprot() & VM_PROT_WRITE ) { - fRelocBase = segCmd->fileoff() + (pint_t)fHeader; - fOrignalVMRelocBaseAddress = segCmd->vmaddr(); - return; - } - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - throw "no writable segment"; -} - - -static void copyFile(const char* srcFile, const char* dstFile) -{ - // open files - int src = open(srcFile, O_RDONLY); - if ( src == -1 ) - throwf("can't open file %s, errno=%d", srcFile, errno); - struct stat stat_buf; - if ( fstat(src, &stat_buf) == -1) - throwf("can't stat open file %s, errno=%d", srcFile, errno); - - // create new file with all same permissions to hold copy of dylib - ::unlink(dstFile); - int dst = open(dstFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); - if ( dst == -1 ) - throwf("can't create temp file %s, errnor=%d", dstFile, errno); - - // mark source as "don't cache" - (void)fcntl(src, F_NOCACHE, 1); - // we want to cache the dst because we are about to map it in and modify it - - // copy permission bits - if ( chmod(dstFile, stat_buf.st_mode & 07777) == -1 ) - throwf("can't chmod temp file %s, errno=%d", dstFile, errno); - if ( chown(dstFile, stat_buf.st_uid, stat_buf.st_gid) == -1) - throwf("can't chown temp file %s, errno=%d", dstFile, errno); - - // copy contents - ssize_t len; - const uint32_t kBufferSize = 128*1024; - static uint8_t* buffer = NULL; - if ( buffer == NULL ) { - vm_address_t addr = 0; - if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS ) - buffer = (uint8_t*)addr; - else - throw "can't allcoate copy buffer"; - } - while ( (len = read(src, buffer, kBufferSize)) > 0 ) { - if ( write(dst, buffer, len) == -1 ) - throwf("write failure copying feil %s, errno=%d", dstFile, errno); - } - - // close files - int result1 = close(dst); - int result2 = close(src); - if ( (result1 != 0) || (result2 != 0) ) - throw "can't close file"; -} - - -// scan dylibs and collect size info -// calculate new base address for each dylib -// rebase each file -// copy to temp and mmap -// update content -// unmap/flush -// rename - -struct archInfo { - cpu_type_t arch; - uint64_t vmSize; - uint64_t orgBase; - uint64_t newBase; -}; - -struct fileInfo -{ - fileInfo(const char* p) : path(p) {} - - const char* path; - std::vector archs; -}; - -// -// add archInfos to fileInfo for every slice of a fat file -// for ppc, there may be duplicate architectures (with different sub-types) -// -static void setSizes(fileInfo& info, const std::set& onlyArchs) -{ - const MultiArchRebaser mar(info.path); - const std::vector& rebasers = mar.getArchs(); - for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { - for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { - AbstractRebaser* rebaser = *rit; - if ( rebaser->getArchitecture() == *ait ) { - archInfo ai; - ai.arch = *ait; - ai.vmSize = rebaser->getVMSize(); - ai.orgBase = rebaser->getBaseAddress(); - ai.newBase = 0; - //fprintf(stderr, "base=0x%llX, size=0x%llX\n", ai.orgBase, ai.vmSize); - info.archs.push_back(ai); - } - } - } -} - -static const char* nameForArch(cpu_type_t arch) -{ - switch( arch ) { - case CPU_TYPE_POWERPC: - return "ppc"; - case CPU_TYPE_POWERPC64: - return "ppca64"; - case CPU_TYPE_I386: - return "i386"; - case CPU_TYPE_X86_64: - return "x86_64"; - case CPU_TYPE_ARM: - return "arm"; - } - return "unknown"; -} - -static void rebase(const fileInfo& info) -{ - // generate temp file name - char realFilePath[PATH_MAX]; - if ( realpath(info.path, realFilePath) == NULL ) { - throwf("realpath() failed on %s, errno=%d", info.path, errno); - } - const char* tempPath; - asprintf((char**)&tempPath, "%s_rebase", realFilePath); - - // copy whole file to temp file - copyFile(info.path, tempPath); - - try { - // rebase temp file - MultiArchRebaser mar(tempPath, true); - const std::vector& rebasers = mar.getArchs(); - for(std::vector::const_iterator fait=info.archs.begin(); fait != info.archs.end(); ++fait) { - for(std::vector::const_iterator rit=rebasers.begin(); rit != rebasers.end(); ++rit) { - if ( (*rit)->getArchitecture() == fait->arch ) { - (*rit)->setBaseAddress(fait->newBase); - if ( verbose ) - printf("%8s 0x%0llX -> 0x%0llX %s\n", nameForArch(fait->arch), fait->orgBase, fait->newBase, info.path); - } - } - } - - // flush temp file out to disk - mar.commit(); - - // rename - int result = rename(tempPath, info.path); - if ( result != 0 ) { - throwf("can't swap temporary rebased file: rename(%s,%s) returned errno=%d", tempPath, info.path, errno); - } - - // make sure every really gets out to disk - ::sync(); - } - catch (const char* msg) { - // delete temp file - ::unlink(tempPath); - - // throw exception with file name added - const char* newMsg; - asprintf((char**)&newMsg, "%s for file %s", msg, info.path); - throw newMsg; - } -} - -static uint64_t totalVMSize(cpu_type_t arch, std::vector& files) -{ - uint64_t totalSize = 0; - for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { - fileInfo& fi = *fit; - for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { - if ( fait->arch == arch ) - totalSize += fait->vmSize; - } - } - return totalSize; -} - -static uint64_t startAddress(cpu_type_t arch, std::vector& files, uint64_t lowAddress, uint64_t highAddress) -{ - if ( lowAddress != 0 ) - return lowAddress; - else if ( highAddress != 0 ) { - uint64_t totalSize = totalVMSize(arch, files); - if ( highAddress < totalSize ) - throwf("cannot use -high_address 0x%X because total size of images is greater: 0x%X", highAddress, totalSize); - return highAddress - totalSize; - } - else { - if ( (arch == CPU_TYPE_I386) || (arch == CPU_TYPE_POWERPC) ) { - // place dylibs below dyld - uint64_t topAddr = 0x8FE00000; - uint64_t totalSize = totalVMSize(arch, files); - if ( totalSize > topAddr ) - throwf("total size of images (0x%X) does not fit below 0x8FE00000", totalSize); - return topAddr - totalSize; - } - else if ( arch == CPU_TYPE_POWERPC64 ) { - return 0x200000000ULL; - } - else if ( arch == CPU_TYPE_X86_64 ) { - return 0x200000000ULL; - } - else if ( arch == CPU_TYPE_ARM ) { - // place dylibs below dyld - uint64_t topAddr = 0x2FE00000; - uint64_t totalSize = totalVMSize(arch, files); - if ( totalSize > topAddr ) - throwf("total size of images (0x%X) does not fit below 0x2FE00000", totalSize); - return topAddr - totalSize; - } - else - throw "unknown architecture"; - } -} - -static void usage() -{ - fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch ] files...\n"); -} - - -int main(int argc, const char* argv[]) -{ - std::vector files; - std::set onlyArchs; - uint64_t lowAddress = 0; - uint64_t highAddress = 0; - - try { - // parse command line options - char* endptr; - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - if ( arg[0] == '-' ) { - if ( strcmp(arg, "-v") == 0 ) { - verbose = true; - } - else if ( strcmp(arg, "-low_address") == 0 ) { - lowAddress = strtoull(argv[++i], &endptr, 16); - } - else if ( strcmp(arg, "-high_address") == 0 ) { - highAddress = strtoull(argv[++i], &endptr, 16); - } - else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = argv[++i]; - if ( strcmp(arch, "ppc") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC); - else if ( strcmp(arch, "ppc64") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC64); - else if ( strcmp(arch, "i386") == 0 ) - onlyArchs.insert(CPU_TYPE_I386); - else if ( strcmp(arch, "x86_64") == 0 ) - onlyArchs.insert(CPU_TYPE_X86_64); - else if ( strcmp(arch, "arm") == 0 ) - onlyArchs.insert(CPU_TYPE_ARM); - else if ( strcmp(arch, "armv6") == 0 ) - onlyArchs.insert(CPU_TYPE_ARM); - else - throwf("unknown architecture %s", arch); - } - else { - usage(); - throwf("unknown option: %s\n", arg); - } - } - else { - files.push_back(fileInfo(arg)); - } - } - - if ( files.size() == 0 ) - throw "no files specified"; - - // use all architectures if no restrictions specified - if ( onlyArchs.size() == 0 ) { - onlyArchs.insert(CPU_TYPE_POWERPC); - onlyArchs.insert(CPU_TYPE_POWERPC64); - onlyArchs.insert(CPU_TYPE_I386); - onlyArchs.insert(CPU_TYPE_X86_64); - onlyArchs.insert(CPU_TYPE_ARM); - } - - // scan files and collect sizes - for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { - setSizes(*it, onlyArchs); - } - - // assign new base address for each arch - for(std::set::iterator ait=onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { - cpu_type_t arch = *ait; - uint64_t baseAddress = startAddress(arch, files, lowAddress, highAddress); - for(std::vector::iterator fit=files.begin(); fit != files.end(); ++fit) { - fileInfo& fi = *fit; - for(std::vector::iterator fait=fi.archs.begin(); fait != fi.archs.end(); ++fait) { - if ( fait->arch == arch ) { - fait->newBase = baseAddress; - baseAddress += fait->vmSize; - baseAddress = (baseAddress + 4095) & (-4096); // page align - } - } - } - } - - // rebase each file if it contains something rebaseable - for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { - fileInfo& fi = *it; - if ( fi.archs.size() > 0 ) - rebase(fi); - } - - } - catch (const char* msg) { - fprintf(stderr, "rebase failed: %s\n", msg); - return 1; - } - - return 0; -} - - - diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 70e05f8..7222efa 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -9,16 +9,31 @@ ARCH ?= $(shell arch) VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6" MYDIR=$(shell cd ../../bin;pwd) +LD = ld +OBJECTDUMP = ObjectDump +MACHOCHECK = machocheck +OTOOL = otool +REBASE = rebase -# if run within Xcode, add the just built tools to the command path ifdef BUILT_PRODUCTS_DIR + # if run within Xcode, add the just built tools to the command path PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH} COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH} + LD = ${BUILT_PRODUCTS_DIR}/ld + OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump + MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck + REBASE = ${BUILT_PRODUCTS_DIR}/rebase else ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" + # if run from Terminal inside unit-test directory RELEASEDIR=$(shell cd ../../../build/Release;pwd) - PATH := ${RELEASEDIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${RELEASEDIR}:${MYDIR}:${COMPILER_PATH} + DEBUGDIR=$(shell cd ../../../build/Debug;pwd) + PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH} + LD = ${RELEASEDIR}/ld + OBJECTDUMP = ${RELEASEDIR}/ObjectDump + MACHOCHECK = ${RELEASEDIR}/machocheck + REBASE = ${RELEASEDIR}/rebase else PATH := ${MYDIR}:${PATH}: COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: @@ -27,27 +42,33 @@ endif export PATH export COMPILER_PATH -LD = ld -OBJECTDUMP = ObjectDump -MACHOCHECK = machocheck -OTOOL = otool -CC = gcc-4.0 -arch ${ARCH} +CC = gcc-4.2 -arch ${ARCH} ${SDKExtra} CCFLAGS = -Wall -std=c99 ASMFLAGS = -CXX = g++-4.0 -arch ${ARCH} +CXX = g++-4.2 -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall ifeq ($(ARCH),armv6) - LDFLAGS := -syslibroot /Developer/SDKs/Purple + SDKExtra = -isysroot /Developer/SDKs/Extra + LDFLAGS := -syslibroot /Developer/SDKs/Extra + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +ifeq ($(ARCH),armv7) + SDKExtra = -isysroot /Developer/SDKs/Extra + LDFLAGS := -syslibroot /Developer/SDKs/Extra override FILEARCH = arm else FILEARCH = $(ARCH) endif ifeq ($(ARCH),thumb) - LDFLAGS := -syslibroot /Developer/SDKs/Purple + SDKExtra = -isysroot /Developer/SDKs/Extra + LDFLAGS := -syslibroot /Developer/SDKs/Extra CCFLAGS += -mthumb CXXFLAGS += -mthumb override ARCH = armv6 @@ -56,21 +77,33 @@ else FILEARCH = $(ARCH) endif +ifeq ($(ARCH),thumb2) + SDKExtra = -isysroot /Developer/SDKs/Extra + LDFLAGS := -syslibroot /Developer/SDKs/Extra + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv7 + override FILEARCH = arm + CC = /Volumes/Leopard/Developer/Platforms/iPhoneOS.platform/usr/bin/gcc-4.2 -arch ${ARCH} +else + FILEARCH = $(ARCH) +endif + RM = rm RMFLAGS = -rf # utilites for Makefiles -PASS_IFF = pass-iff-exit-zero.pl +PASS_IFF = ${MYDIR}/pass-iff-exit-zero.pl PASS_IFF_SUCCESS = ${PASS_IFF} -PASS_IFF_EMPTY = pass-iff-no-stdin.pl -PASS_IFF_STDIN = pass-iff-stdin.pl -FAIL_IFF = fail-iff-exit-zero.pl +PASS_IFF_EMPTY = ${MYDIR}/pass-iff-no-stdin.pl +PASS_IFF_STDIN = ${MYDIR}/pass-iff-stdin.pl +FAIL_IFF = ${MYDIR}/fail-iff-exit-zero.pl FAIL_IFF_SUCCESS = ${FAIL_IFF} -PASS_IFF_ERROR = pass-iff-exit-non-zero.pl -FAIL_IF_ERROR = fail-if-exit-non-zero.pl -FAIL_IF_SUCCESS = fail-if-exit-zero.pl -FAIL_IF_EMPTY = fail-if-no-stdin.pl -FAIL_IF_STDIN = fail-if-stdin.pl +PASS_IFF_ERROR = ${MYDIR}/pass-iff-exit-non-zero.pl +FAIL_IF_ERROR = ${MYDIR}/fail-if-exit-non-zero.pl +FAIL_IF_SUCCESS = ${MYDIR}/fail-if-exit-zero.pl +FAIL_IF_EMPTY = ${MYDIR}/fail-if-no-stdin.pl +FAIL_IF_STDIN = ${MYDIR}/fail-if-stdin.pl PASS_IFF_GOOD_MACHO = ${PASS_IFF} ${MACHOCHECK} FAIL_IF_BAD_MACHO = ${FAIL_IF_ERROR} ${MACHOCHECK} FAIL_IF_BAD_OBJ = ${FAIL_IF_ERROR} ${OBJECTDUMP} >/dev/null diff --git a/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests index e21c2b3..fa96968 100755 --- a/ld64/unit-tests/run-all-unit-tests +++ b/ld64/unit-tests/run-all-unit-tests @@ -11,13 +11,16 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` [ "$PROCTORRUN" ] && exec ../proctor-run -all_archs="x86_64 armv6 thumb ppc ppc64 i386 " -valid_archs="x86_64 armv6 ppc ppc64 i386 " +all_archs="x86_64 ppc i386 " +#armv6 thumb +valid_archs="x86_64 armv6 ppc i386 " # clean first ../bin/make-recursive.pl clean > /dev/null mkdir /tmp/$$ +mkdir $BUILD_ROOT/lib +ln -s /Developer/usr/lib/libLTO.dylib ${BUILD_ROOT}/lib/libLTO.dylib for arch in $all_archs do echo "" diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile b/ld64/unit-tests/test-cases/archive-force-load/Makefile similarity index 62% rename from ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile rename to ld64/unit-tests/test-cases/archive-force-load/Makefile index e8a4a0e..132fdbd 100644 --- a/ld64/FireOpal/unit-tests/test-cases/tentative-and-archive/Makefile +++ b/ld64/unit-tests/test-cases/archive-force-load/Makefile @@ -2,14 +2,14 @@ # 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, @@ -17,34 +17,32 @@ # 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 _var which -# should *not* cause libfoo.a(foo.o) to be loaded. # -# ld crashes building XsanFS -# -undefined dynamic_lookup causes spurious extra symbols +# Check the -force_load option. +# Need a linker option to load all objects from one library # run: all all: - ${CC} ${CCFLAGS} foo.c -c -o foo.o - libtool -static foo.o -o libfoo.a - ${CC} ${CCFLAGS} main.c libfoo.a -o main - ${CC} ${CCFLAGS} main.c libfoo.a -o main -undefined dynamic_lookup - nm -m main | grep "looked up" | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + ${CC} ${CCFLAGS} baz.c -c -o baz.o + ${CC} ${CCFLAGS} bat.c -c -o bat.o + libtool -static foo.o bar.o -o libfoobar.a + libtool -static baz.o bat.o -o libbazbat.a + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar.a libbazbat.a -o main -Wl,-why_load + nm main | grep "_bar" | ${FAIL_IF_EMPTY} + nm main | grep "_foo" | ${FAIL_IF_EMPTY} + nm main | grep "_baz" | ${FAIL_IF_STDIN} + nm main | grep "_bat" | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} main - clean: - rm -rf main libfoo.a foo.o + rm -rf main *.o *.a diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/bar.c b/ld64/unit-tests/test-cases/archive-force-load/bar.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/symbol-moving/bar.c rename to ld64/unit-tests/test-cases/archive-force-load/bar.c diff --git a/ld64/unit-tests/test-cases/archive-force-load/bat.c b/ld64/unit-tests/test-cases/archive-force-load/bat.c new file mode 100644 index 0000000..f5a7956 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-force-load/bat.c @@ -0,0 +1 @@ +void bat() {} diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c b/ld64/unit-tests/test-cases/archive-force-load/baz.c similarity index 93% rename from ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c rename to ld64/unit-tests/test-cases/archive-force-load/baz.c index 256a0e3..4b361f6 100644 --- a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/baz.c +++ b/ld64/unit-tests/test-cases/archive-force-load/baz.c @@ -1 +1,2 @@ void baz() {} + diff --git a/ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/foo.c b/ld64/unit-tests/test-cases/archive-force-load/foo.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/dwarf-archive-all_load/foo.c rename to ld64/unit-tests/test-cases/archive-force-load/foo.c diff --git a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c b/ld64/unit-tests/test-cases/archive-force-load/main.c similarity index 88% rename from ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c rename to ld64/unit-tests/test-cases/archive-force-load/main.c index f44b624..fc047f7 100644 --- a/ld64/FireOpal/unit-tests/test-cases/switch-jump-table/main.c +++ b/ld64/unit-tests/test-cases/archive-force-load/main.c @@ -1,4 +1,5 @@ + int main() { return 0; -} \ No newline at end of file +} diff --git a/ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/branch-distance/Makefile similarity index 76% rename from ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile rename to ld64/unit-tests/test-cases/branch-distance/Makefile index c85f382..46472ee 100644 --- a/ld64/FireOpal/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/branch-distance/Makefile @@ -23,25 +23,22 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ifeq ($(ARCH),armv6) - ARCH_FLAGS = -mlong-branch -else - ARCH_FLAGS = -endif - # -# Simple test for branch islands +# test thumb2 branch ranges # run: all - all: - ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} - ${PASS_IFF_GOOD_MACHO} hello + ${CC} ${CCFLAGS} foo.s -c + ${CC} ${CCFLAGS} bar.s -c + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar ${ARCH_FLAGS} -nostdlib + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + ${CC} ${CCFLAGS} foobar.o -e _foo -o foobar2 ${ARCH_FLAGS} -nostdlib + ${PASS_IFF_GOOD_MACHO} foobar clean: - rm hello + rm *.o foobar* diff --git a/ld64/unit-tests/test-cases/branch-distance/bar.s b/ld64/unit-tests/test-cases/branch-distance/bar.s new file mode 100644 index 0000000..5b1f6dd --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-distance/bar.s @@ -0,0 +1,21 @@ + + .text +#if __thumb__ + .thumb_func _bar + .code 16 +#endif + + .globl _bar +_bar: + nop +#if __arm__ + bl _foo + blx _foo +// b _foo +#endif + + + + + .subsections_via_symbols + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/branch-distance/foo.s b/ld64/unit-tests/test-cases/branch-distance/foo.s new file mode 100644 index 0000000..18f3a81 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-distance/foo.s @@ -0,0 +1,33 @@ + + + .text + +#if __thumb__ + .thumb_func _foo + .code 16 +#endif + .globl _foo +_foo: + nop +#if __arm__ + bl _bar + blx _bar +// b _bar + + +_space1: + +#if __thumb2__ + .space 16*1024*1024 -100 +#elif __thumb__ + .space 4*1024*1024 -100 +#else + .space 16*1024*1024 -100 +#endif + +#endif // __arm__ + + + + .subsections_via_symbols + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile b/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile index c1ca816..c8b16ee 100644 --- a/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,6 +23,8 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +SHELL = bash # use bash shell so we can redirect just stderr + # # Test that cfstring literals are coalesced and dead stripped # There is 3 CFSTR in foo.c and 1 CFSTR in bar.c @@ -31,10 +33,8 @@ include ${TESTROOT}/include/common.makefile ifeq (,${findstring 64,$(ARCH)}) CFSTRING_SIZE = 16 - CFSTRING_SIZE_WRITABLE = 32 else CFSTRING_SIZE = 32 - CFSTRING_SIZE_WRITABLE = 64 endif @@ -45,8 +45,8 @@ all: ${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} # now try with -fwritable-strings - ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings - size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE_WRITABLE}" | ${PASS_IFF_STDIN} + ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings 2>/dev/null + size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE}" | ${PASS_IFF_STDIN} clean: rm -rf foo foo_writable diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c b/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c index 9a84abd..b78cd63 100644 --- a/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/bar.c @@ -1,5 +1,6 @@ #include +CFStringRef OtherCFString = CFSTR("other"); void bar() { diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c b/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c index fe1b30e..a5c794e 100644 --- a/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/foo.c @@ -2,10 +2,13 @@ extern void bar(); +extern CFStringRef OtherCFString; + void foo() { CFStringGetLength(CFSTR("hello")); CFStringGetLength(CFSTR("world")); + CFStringGetLength(OtherCFString); } diff --git a/ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile similarity index 66% rename from ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile rename to ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile index 76b4e95..72f365b 100644 --- a/ld64/FireOpal/unit-tests/test-cases/segment-order/Makefile +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,14 +24,32 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Validate linker puts non-standard segments in order of discovery +# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded # -all: - ${CC} ${CCFLAGS} main.c segKKK.s segJJJ.s segLLL.s -o main - nm -j -n main | grep _sym_ > symbol.order - ${FAIL_IF_ERROR} diff symbol.order expected.order - ${PASS_IFF_GOOD_MACHO} main +run: all-${ARCH} + + +all-ppc: + ${PASS_IFF} true + +all-arm: + ${PASS_IFF} true +all-i386: all-real + +all-x86_64: all-real + + +all-real: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib + dyldinfo -bind main | grep wfoo | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} main + + clean: - rm -rf main symbol.order + rm -rf libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c new file mode 100644 index 0000000..1906d16 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c @@ -0,0 +1,4 @@ + + +__attribute__((weak)) void wfoo() {} +void foo() {} diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c new file mode 100644 index 0000000..afd696d --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c @@ -0,0 +1,17 @@ + +extern void foo(); +extern void wfoo(); + +void* pfoo = &foo; +void* pwfoo = &wfoo; + +int main (void) +{ + if (pfoo != &foo) + return 1; + if (pwfoo != &wfoo) + return 1; + + return 0; +} + diff --git a/ld64/unit-tests/test-cases/code-signed-object-file/Makefile b/ld64/unit-tests/test-cases/code-signed-object-file/Makefile new file mode 100644 index 0000000..4871de3 --- /dev/null +++ b/ld64/unit-tests/test-cases/code-signed-object-file/Makefile @@ -0,0 +1,26 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that an object file can be code signed +# +# when linking a kext the static linker should leave a pad in the headers to allow code signing +# + +CODE_SIGN_ARCH = ${ARCH} +ifeq (${ARCH},ppc) + CODE_SIGN_ARCH = ppc7400 +endif + + +run: all + +all: + ${CC} foo.c -c -o foo.o + ${LD} -r foo.o -o foo2.o + codesign_allocate -i foo2.o -a ${CODE_SIGN_ARCH} 256 -o foo3.o + ${PASS_IFF} true + +clean: + rm foo.o foo2.o foo3.o diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c b/ld64/unit-tests/test-cases/code-signed-object-file/foo.c similarity index 73% rename from ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c rename to ld64/unit-tests/test-cases/code-signed-object-file/foo.c index 95ec91c..85e6cd8 100644 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/foo.c +++ b/ld64/unit-tests/test-cases/code-signed-object-file/foo.c @@ -1,6 +1 @@ - - void foo() {} - - - diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile similarity index 76% rename from ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile rename to ld64/unit-tests/test-cases/cstring-alt-segment/Makefile index 982f41d..4e55304 100644 --- a/ld64/FireOpal/unit-tests/test-cases/objc-selector-coalescing/Makefile +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,17 +23,17 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - # -# Test that two ObjC translation units that use the same selector -# link together. +# Check that __cstring sections in other segments are not coalesced +# ld coalesces C strings in different segments # run: all all: - ${CC} ${CCFLAGS} main.m other.m -o main -framework Foundation + ${CC} ${CCFLAGS} main.c custom.s -o main + size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main - + clean: - rm -rf main + rm main diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s new file mode 100644 index 0000000..33705e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s @@ -0,0 +1,8 @@ + + + + .section __MYSEG, __cstring, cstring_literals +LC: .ascii "hello" + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c b/ld64/unit-tests/test-cases/cstring-alt-segment/main.c similarity index 61% rename from ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c rename to ld64/unit-tests/test-cases/cstring-alt-segment/main.c index 246fed4..8fe18db 100644 --- a/ld64/FireOpal/unit-tests/test-cases/flat-indirect-undefines/main.c +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/main.c @@ -1,10 +1,7 @@ #include -extern void foo(); - - int main() { - foo(); + printf("hello"); return 0; } diff --git a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile b/ld64/unit-tests/test-cases/cstring-custom-section/Makefile similarity index 56% rename from ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile rename to ld64/unit-tests/test-cases/cstring-custom-section/Makefile index 3375aae..628d045 100644 --- a/ld64/FireOpal/unit-tests/test-cases/cfstring-utf16/Makefile +++ b/ld64/unit-tests/test-cases/cstring-custom-section/Makefile @@ -24,27 +24,24 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Test that utf16 cfstring literals are not coalesced. -# There is 3 CFSTR in foo.m and 1 CFSTR in bar.m -# After coalescing and dead stripping there should be only one CFSTR in the output +# Check that cstrings in custom sections are not uniqued with +# cstrings in the standard section. # -ifeq (,${findstring 64,$(ARCH)}) - CFSTRING_SIZE = 48 -else - CFSTRING_SIZE = 96 -endif - USTRING_SIZE = 37 - - - run: all all: - ${CC} ${CCFLAGS} foo.m bar.m -o foo -framework CoreFoundation -dead_strip - size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} - size -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} foo + ${CC} ${CCFLAGS} -c foo.s -o foo.o + ${CC} ${CCFLAGS} -c bar.s -o bar.o + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + size -l foobar.o | grep "(__TEXT, __cstring): 13" | ${FAIL_IF_EMPTY} + size -l foobar.o | grep "(__TEXT, __mystring): 15" | ${FAIL_IF_EMPTY} + otool -lv foobar.o | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib + size -l libfoobar.dylib | grep "__cstring: 13" | ${FAIL_IF_EMPTY} + size -l libfoobar.dylib | grep "__mystring: 15" | ${FAIL_IF_EMPTY} + otool -lv libfoobar.dylib | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoobar.dylib clean: - rm -rf foo + rm foo.o bar.o libfoobar.dylib foobar.o diff --git a/ld64/unit-tests/test-cases/cstring-custom-section/bar.s b/ld64/unit-tests/test-cases/cstring-custom-section/bar.s new file mode 100644 index 0000000..6aba48b --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-custom-section/bar.s @@ -0,0 +1,31 @@ + .cstring +LC0: + .ascii "bar\0" +LC1: + .ascii "coal\0" + .text + + + .section __TEXT, __mystring, cstring_literals +LC4: + .ascii "bar\0" +LC5: + .ascii "mycoal\0" + + + .data +#if __LP64__ + .quad LC0 + .quad LC1 + .quad LC4 + .quad LC5 +#else + .long LC0 + .long LC1 + .long LC4 + .long LC5 +#endif + + .subsections_via_symbols + + diff --git a/ld64/unit-tests/test-cases/cstring-custom-section/foo.s b/ld64/unit-tests/test-cases/cstring-custom-section/foo.s new file mode 100644 index 0000000..a855c91 --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-custom-section/foo.s @@ -0,0 +1,31 @@ + .cstring +LC0: + .ascii "foo\0" +LC1: + .ascii "coal\0" + .text + + + .section __TEXT, __mystring, cstring_literals +LC4: + .ascii "foo\0" +LC5: + .ascii "mycoal\0" + + + .data +#if __LP64__ + .quad LC0 + .quad LC1 + .quad LC4 + .quad LC5 +#else + .long LC0 + .long LC1 + .long LC4 + .long LC5 +#endif + + .subsections_via_symbols + + diff --git a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile b/ld64/unit-tests/test-cases/cstring-labels/Makefile similarity index 52% rename from ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile rename to ld64/unit-tests/test-cases/cstring-labels/Makefile index be0ba42..1c6ee7d 100644 --- a/ld64/FireOpal/unit-tests/test-cases/private-non-lazy/Makefile +++ b/ld64/unit-tests/test-cases/cstring-labels/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,31 +24,33 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# The point of this test is to check that a non-lazy-pointer -# in foo.o to a private-extern symbol in bar.o will -# properly survive ld -r +# ld -r for x86_64 is changing visibility of cstring constants # +# In order to coalesce strings from different .o files, the linker internally +# makes them weak/hidden. Test to make sure that weak/hidden does not leak out. +# + +ifeq (${ARCH},x86_64) + STRING_LABEL_COUNT = 3 +else + STRING_LABEL_COUNT = 1 +endif + + run: all all: ${CC} ${CCFLAGS} -c foo.c -o foo.o - ${FAIL_IF_BAD_OBJ} foo.o - ${CC} ${CCFLAGS} -c bar.c -o bar.o - ${FAIL_IF_BAD_OBJ} bar.o - - ${LD} -r foo.o bar.o -o foobar.o -arch ${ARCH} - ${FAIL_IF_BAD_OBJ} foobar.o - - ${CC} ${CCFLAGS} hello.c foobar.o -o hello - ${FAIL_IF_BAD_MACHO} hello - - ${LD} -r foo.o bar.o -o foobar2.o -arch ${ARCH} -keep_private_externs - ${FAIL_IF_BAD_OBJ} foobar2.o - - ${CC} ${CCFLAGS} hello.c foobar2.o -o hello2 - ${PASS_IFF_GOOD_MACHO} hello2 + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + nm -nm foobar.o | grep __cstring | grep "weak private external" | ${FAIL_IF_STDIN} + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o -keep_private_externs + nm -nm foobar.o | grep __cstring | grep "weak private external" | ${FAIL_IF_STDIN} + nm -m foobar.o | grep __cstring | wc -l | grep ${STRING_LABEL_COUNT} | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib + nm -m libfoobar.dylib | grep __cstring | wc -l | grep 1 | ${FAIL_IF_EMPTY} + ${PASS_IFF} /usr/bin/true clean: - rm -rf *.o hello hello2 + rm foo.o bar.o foobar.o libfoobar.dylib diff --git a/ld64/unit-tests/test-cases/cstring-labels/bar.c b/ld64/unit-tests/test-cases/cstring-labels/bar.c new file mode 100644 index 0000000..8b1d11d --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-labels/bar.c @@ -0,0 +1,2 @@ +const char* kBar = "hello"; +const char* kBar2 = "there"; diff --git a/ld64/unit-tests/test-cases/cstring-labels/foo.c b/ld64/unit-tests/test-cases/cstring-labels/foo.c new file mode 100644 index 0000000..0780b62 --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-labels/foo.c @@ -0,0 +1,6 @@ + +void func() {} + +const char kFoo[] = "foo"; + +const char* kFoo2 = "hello"; diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive-eh/Makefile similarity index 70% rename from ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile rename to ld64/unit-tests/test-cases/dead_strip-archive-eh/Makefile index 975ad29..a1d0207 100644 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib-objc/Makefile +++ b/ld64/unit-tests/test-cases/dead_strip-archive-eh/Makefile @@ -23,23 +23,23 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - # -# Verify that -lazy_library fails if an objc class is referenced +# Tests that a non-weak symbol in an archive cleanly overrides +# a weak symbol in a .o file with dead code stripping +# +# duplicate typeinfo in executable # run: all all: - ${CC} ${CCFLAGS} foo.m -dynamiclib -o libfoo.dylib -framework Foundation - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.m -Wl,-lazy_library,libfoo.dylib -o main -framework Foundation 2> fail.log - ${CC} ${CCFLAGS} main.m libfoo.dylib -o main -framework Foundation + ${CXX} ${CXXFLAGS} foo.cxx -c -o foo.o + ${CXX} ${CXXFLAGS} bar.cxx -c -o bar.o + libtool -static foo.o bar.o -o libfoobar.a + ${CXX} ${CXXFLAGS} main.cxx libfoobar.a -dead_strip -o main + dwarfdump --eh-frame --verify main >/dev/null ${PASS_IFF_GOOD_MACHO} main - - + clean: - rm libfoo.dylib main rm fail.log + rm -rf main foo.o bar.o libfoobar.a diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-eh/bar.cxx b/ld64/unit-tests/test-cases/dead_strip-archive-eh/bar.cxx new file mode 100644 index 0000000..1231527 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-eh/bar.cxx @@ -0,0 +1,14 @@ + +#include + +void doit() +{ + printf("hello there %s\n", "world"); + +} + +void bar() +{ + doit(); +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-eh/foo.cxx b/ld64/unit-tests/test-cases/dead_strip-archive-eh/foo.cxx new file mode 100644 index 0000000..e743d41 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-eh/foo.cxx @@ -0,0 +1,13 @@ +#include + +__attribute__((weak)) void doit() +{ + printf("hello %s\n", "world"); +} + + +void foo() +{ + doit(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c b/ld64/unit-tests/test-cases/dead_strip-archive-eh/main.cxx similarity index 98% rename from ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c rename to ld64/unit-tests/test-cases/dead_strip-archive-eh/main.cxx index 1f50353..fc6eda5 100644 --- a/ld64/FireOpal/unit-tests/test-cases/implicit_dylib/main.c +++ b/ld64/unit-tests/test-cases/dead_strip-archive-eh/main.cxx @@ -9,3 +9,4 @@ int main() return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive-weak/Makefile similarity index 73% rename from ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile rename to ld64/unit-tests/test-cases/dead_strip-archive-weak/Makefile index 4b4f912..c2d8f6c 100644 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-init-archive/Makefile +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,7 +24,10 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Tests that a -init function can be pulled from an archive when using -dead_strip. +# Tests that a non-weak symbol in an archive cleanly overrides +# a weak symbol in a .o file with dead code stripping +# +# duplicate typeinfo in executable # run: all @@ -32,9 +35,10 @@ 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 -dynamiclib -Os libfoo.a -dead_strip -o libbar.dylib -init _foo - ${PASS_IFF_GOOD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} main.c libfoo.a -dead_strip -o main + nm main | grep _foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + clean: - rm -rf libbar.dylib libfoo.a foo.o - + rm -rf main foo.o libfoo.a diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-weak/foo.c b/ld64/unit-tests/test-cases/dead_strip-archive-weak/foo.c new file mode 100644 index 0000000..79184c7 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak/foo.c @@ -0,0 +1,13 @@ + +extern void good(); + +void foo() +{ + good(); +} + +void loadme() +{ +// foo(); +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-weak/main.c b/ld64/unit-tests/test-cases/dead_strip-archive-weak/main.c new file mode 100644 index 0000000..c082574 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak/main.c @@ -0,0 +1,27 @@ + +extern void loadme(); + +void good() +{ +} + +void bad() +{ +} + +// foo is first found be dead stripping here +// then the use of loadme causes libfoo.a(foo.o) +// to be loaded which overrides foo +__attribute__((weak)) void foo() +{ + bad(); +} + +int main() +{ + foo(); + loadme(); + return 0; +} + + diff --git a/ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile similarity index 64% rename from ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile rename to ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile index 79ef53e..e60513e 100644 --- a/ld64/FireOpal/unit-tests/test-cases/prebound-main/Makefile +++ b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,29 +23,21 @@ 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 +# Test that N_NO_DEAD_STRIP bit survives ld -r # -ifeq (,${findstring 64,$(ARCH)}) - ifeq (${ARCH},i386) - KEYWORD = NOUNDEFS - else - KEYWORD = PREBOUND - endif -else - KEYWORD = NOUNDEFS -endif - - run: all -all: - ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 - otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + nm -m main.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY} + nm -m main.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY} + ${LD} -r main.o -o main2.o + nm -m main2.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY} + nm -m main2.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY} + ${CC} main2.o -o main + nm -m main | grep _bar | grep "no dead strip" | ${PASS_IFF_EMPTY} clean: - rm main + rm -rf main.o main2.o main diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c similarity index 87% rename from ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c rename to ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c index da2b2fd..7e206e5 100644 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip-archive-global/main.c +++ b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,12 +22,20 @@ * @APPLE_LICENSE_HEADER_END@ */ -extern void foo(); + +__attribute__((used)) static void foo() +{ +} + + +__attribute__((used)) void bar() +{ +} + int main() { foo(); + bar(); return 0; } - - diff --git a/ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile similarity index 70% rename from ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile rename to ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile index 060f12e..45886c0 100644 --- a/ld64/FireOpal/unit-tests/test-cases/special-labels/Makefile +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,18 +24,18 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# The point of this test is a sanity check that ld -# automatically strips labels starting with 'l' and 'L' +# Check that symbols in no-dead-strip sections can be coalesced. +# -dead_strip inhibits weak coalescing in no_dead_strip section # run: all all: - as -arch ${ARCH} -L extra.s -o extra.o - ${CC} ${CCFLAGS} main.c extra.o -o main - nm main | grep "lother" | ${FAIL_IF_STDIN} - nm main | grep "L123" | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} baz.c -c -o baz.o + libtool -static baz.o -o libbaz.a + ${CC} ${CCFLAGS} main.c foo.c libbaz.a -dead_strip -o main + nm -j main | grep _hidden | wc -l | grep 1 | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf main *.o + rm -rf main baz.o libbaz.a diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c new file mode 100644 index 0000000..7d76bdd --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c @@ -0,0 +1,7 @@ +void baz() +{ +} + + +#include "foo.c" + diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c new file mode 100644 index 0000000..4cd4cfb --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c @@ -0,0 +1,25 @@ + +// function can be coalesced and should not be dead stripped +void __attribute__ ((weak, section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo() +{ + +} + + +// function should not be exported, can be coalesced, and should not be dead stripped +void __attribute__ ((weak, visibility("hidden"), section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) hidden() +{ + +} + +// bar should be dead stripped +void __attribute__ ((weak, section ("__DATA,__text2"))) bar() +{ + +} + +__attribute__((constructor)) static void init() +{ + foo(); + hidden(); +} diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c new file mode 100644 index 0000000..e4c564c --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c @@ -0,0 +1,13 @@ + +// baz is in a lazily loaded archive +extern void baz(); + +int main() +{ + baz(); + return 0; +} + + +#include "foo.c" + diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile b/ld64/unit-tests/test-cases/dead_strippable_dylib/Makefile similarity index 74% rename from ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile rename to ld64/unit-tests/test-cases/dead_strippable_dylib/Makefile index 309655e..46cc5b4 100644 --- a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/Makefile +++ b/ld64/unit-tests/test-cases/dead_strippable_dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,7 +24,11 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Test -dead_strip_dylibs +# Test -mark_dead_strippable_dylib +# +# foo is used. +# bar is not used by should still be linked +# baz is not used and should be automatically not linked # @@ -33,18 +37,12 @@ run: all all: ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -Wl,-mark_dead_strippable_dylib ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib libbaz.dylib -o main - ${FAIL_IF_BAD_MACHO} main otool -L main | grep libfoo.dylib | ${FAIL_IF_EMPTY} otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY} - otool -L main | grep libbaz.dylib | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libbar.dylib libbaz.dylib -o main -Wl,-dead_strip_dylibs - ${FAIL_IF_BAD_MACHO} main - otool -L main | grep libfoo.dylib | ${FAIL_IF_STDIN} - otool -L main | grep libbar.dylib | ${FAIL_IF_EMPTY} otool -L main | grep libbaz.dylib | ${FAIL_IF_STDIN} - ${PASS_IFF} /usr/bin/true + ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/bar.c b/ld64/unit-tests/test-cases/dead_strippable_dylib/bar.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/bar.c rename to ld64/unit-tests/test-cases/dead_strippable_dylib/bar.c diff --git a/ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/baz.c b/ld64/unit-tests/test-cases/dead_strippable_dylib/baz.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/dead_strip_dylibs/baz.c rename to ld64/unit-tests/test-cases/dead_strippable_dylib/baz.c diff --git a/ld64/FireOpal/unit-tests/test-cases/allowable-client/foo.c b/ld64/unit-tests/test-cases/dead_strippable_dylib/foo.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/allowable-client/foo.c rename to ld64/unit-tests/test-cases/dead_strippable_dylib/foo.c diff --git a/ld64/FireOpal/unit-tests/test-cases/loader_path/main.c b/ld64/unit-tests/test-cases/dead_strippable_dylib/main.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/loader_path/main.c rename to ld64/unit-tests/test-cases/dead_strippable_dylib/main.c index 829ca5e..4f56fe0 100644 --- a/ld64/FireOpal/unit-tests/test-cases/loader_path/main.c +++ b/ld64/unit-tests/test-cases/dead_strippable_dylib/main.c @@ -1,3 +1,4 @@ + extern void foo(); int main() @@ -5,4 +6,3 @@ int main() foo(); return 0; } - diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/main.c b/ld64/unit-tests/test-cases/dtrace-static-probes/main.c index 8a417e9..79ce4eb 100644 --- a/ld64/unit-tests/test-cases/dtrace-static-probes/main.c +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/main.c @@ -10,7 +10,8 @@ typedef int weirdType2; int deadwood() { - BAR_COUNT1(2); + if ( BAR_COUNT1_ENABLED() ) + BAR_COUNT1(2); return 0; } @@ -19,9 +20,11 @@ int main() { int a = 1; while(a) { - FOO_COUNT1(1); + if ( FOO_COUNT1_ENABLED() ) + FOO_COUNT1(1); printf("test\n"); - BAR_COUNT1(2); + if ( BAR_COUNT1_ENABLED() ) + BAR_COUNT1(2); sleep(1); } diff --git a/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile b/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile index 8d38d99..aa02b11 100644 --- a/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile +++ b/ld64/unit-tests/test-cases/dwarf-archive-all_load/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -36,7 +36,7 @@ run: ${CC} ${CCFLAGS} -gdwarf-2 baz.c -c -o baz.o ${FAIL_IF_BAD_OBJ} baz.o libtool -static bar.o baz.o foo.o -o liball.a - ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs + ${CC} ${CCFLAGS} liball.a -all_load -dynamiclib -o liball.dylib -nodefaultlibs -lSystem ${FAIL_IF_BAD_MACHO} liball.dylib nm -fap liball.dylib | ./stabs-filter.pl > liball.dylib.stabs ${PASS_IFF} diff liball.dylib.stabs expected-stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index 4ab634d..0ef639d 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -23,11 +23,11 @@ 0000 FUN 0000 ENSYM 0000 GSYM _init -0000 STSYM .my_non_standard_name 0000 STSYM .my_non_standard_name_static +0000 STSYM .my_non_standard_name 0000 STSYM __ZZ3bariE8bar_init 0000 GSYM _uninit -0000 STSYM _sinit -0000 STSYM _suninit +0000 STSYM __ZL7suninit +0000 STSYM __ZL5sinit 0000 STSYM __ZZ3bariE10bar_uninit 0000 SO diff --git a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile new file mode 100644 index 0000000..27cd180 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Verify label in __eh_frame can be removed with no impact +# +run: all + +all: + ${CC} ${CCXXFLAGS} foo.c -c -o foo.o -fexceptions + ${CC} ${CCXXFLAGS} bar.c -c -o bar.o -fexceptions + ${CC} ${CCXXFLAGS} baz.c -c -o baz.o -fexceptions + ${LD} -r foo.o -no_eh_labels -o foo.no.o + ${LD} -r bar.o -no_eh_labels -o bar.no.o + ${LD} -r baz.o -no_eh_labels -o baz.no.o + ${LD} -r foo.o bar.o baz.o -o foobarbaz.o + ${LD} -r foo.no.o bar.no.o baz.no.o -o foobarbaz.no.o + ${OBJECTDUMP} -no_content -no_sort foobarbaz.o > foobarbaz.dump + ${OBJECTDUMP} -no_content -no_sort foobarbaz.no.o > foobarbaz.no.dump + ${FAIL_IF_ERROR} dwarfdump --eh-frame --verify foobarbaz.no.o >/dev/null + ${PASS_IFF_SUCCESS} diff foobarbaz.dump foobarbaz.no.dump + + +clean: + rm -f foo.o bar.o baz.o foo.no.o bar.no.o baz.no.o foobarbaz.* diff --git a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/bar.c b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/bar.c new file mode 100644 index 0000000..9bdd4f4 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/bar.c @@ -0,0 +1,18 @@ + +extern void other(); + +void bar() { + other(); + other(); +} + +void __attribute__((weak)) my_weak() { + other(); + other(); +} + +void bar2() { + other(); + other(); +} + diff --git a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/baz.c b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/baz.c new file mode 100644 index 0000000..fcc3490 --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/baz.c @@ -0,0 +1,18 @@ + +extern void other(); + +void baz() { + other(); + other(); +} + +void __attribute__((weak)) my_weak() { + other(); + other(); +} + +void baz2() { + other(); + other(); +} + diff --git a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/foo.c b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/foo.c new file mode 100644 index 0000000..22123ae --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/foo.c @@ -0,0 +1,18 @@ + +extern void other(); + +void foo() { + other(); + other(); +} + +void __attribute__((weak)) my_weak() { + other(); + other(); +} + +void foo2() { + other(); + other(); +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx b/ld64/unit-tests/test-cases/eh-coalescing-r/baz.cxx similarity index 98% rename from ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx rename to ld64/unit-tests/test-cases/eh-coalescing-r/baz.cxx index b48923c..3b2e31d 100644 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/bar.cxx +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/baz.cxx @@ -24,7 +24,7 @@ #include #include "func.h" -void bar() +void baz() { func(); } diff --git a/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile b/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile new file mode 100644 index 0000000..107400e --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile @@ -0,0 +1,23 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify that if symbols are stripped out of .o files +# the linker can still process unwind info from .o files +# correctly + +run: all + +all: + ${CXX} ${CCXXFLAGS} main.cxx -c -o main1.o -Os + #strip main1.o -u -s keep.exp -o main2.o + ${LD} main1.o -r -x -exported_symbols_list keep.exp -o main2.o + ${CXX} ${CCXXFLAGS} main1.o -Wl,-x -o main1 -exported_symbols_list keep.exp + ${CXX} ${CCXXFLAGS} main2.o -o main2 + unwinddump -arch ${ARCH} main1 > main1.unwind + unwinddump -arch ${ARCH} main2 > main2.unwind + ${PASS_IFF} diff main1.unwind main2.unwind + +clean: + rm -f main1* main2* diff --git a/ld64/unit-tests/test-cases/eh-stripped-symbols/keep.exp b/ld64/unit-tests/test-cases/eh-stripped-symbols/keep.exp new file mode 100644 index 0000000..70669ab --- /dev/null +++ b/ld64/unit-tests/test-cases/eh-stripped-symbols/keep.exp @@ -0,0 +1,3 @@ +_main +__Z3barv + diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m b/ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx similarity index 82% rename from ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m rename to ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx index 3f45e25..325937d 100644 --- a/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/test.m +++ b/ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx @@ -21,24 +21,32 @@ * * @APPLE_LICENSE_HEADER_END@ */ - -#include +#include +#include +void bar() +{ +} -@interface NSObject (stuff) -@end -@implementation NSObject (stuff) -@end +void foo2() +{ + int a = arc4random(); + int b = arc4random(); + fprintf(stderr, "hello %d %d\n", a, b); +} + +void foo1() +{ + foo2(); + fprintf(stderr, "world\n"); +} -@interface NSObject (other) -@end -@implementation NSObject (other) -- (id) init { return self; } -@end int main() { + foo1(); + bar(); return 0; -} +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/eh_frame/Makefile b/ld64/unit-tests/test-cases/eh_frame/Makefile index 6216d8e..f13626f 100644 --- a/ld64/unit-tests/test-cases/eh_frame/Makefile +++ b/ld64/unit-tests/test-cases/eh_frame/Makefile @@ -41,6 +41,7 @@ all: ${FAIL_IF_BAD_OBJ} foo2.o ${CXX} ${CCXXFLAGS} foo2.o bar.cxx -dynamiclib -o libfoobar.dylib ${FAIL_IF_BAD_MACHO} libfoobar.dylib + dwarfdump --eh-frame --verify libfoobar.dylib > /dev/null nm libfoobar.dylib | grep '.eh' | ${PASS_IFF_EMPTY} clean: diff --git a/ld64/unit-tests/test-cases/end-label/foo.s b/ld64/unit-tests/test-cases/end-label/foo.s index 888af5f..de19a05 100644 --- a/ld64/unit-tests/test-cases/end-label/foo.s +++ b/ld64/unit-tests/test-cases/end-label/foo.s @@ -3,9 +3,11 @@ .globl _start .globl _end + .globl _endAlias _start: .long 0 .long 0 _end: +_endAlias: diff --git a/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile b/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile index 10d1eb1..7b000e4 100644 --- a/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile +++ b/ld64/unit-tests/test-cases/exported-symbols-wildcards/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -31,47 +31,47 @@ run: all all: ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*bar' - nm -j -g -f libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect1 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f?o' - nm -j -g -f libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect2 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_foo*' - nm -j -g -f libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect3 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f*o*' - nm -j -g -f libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect4 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,_foo -Wl,-exported_symbol -Wl,'_*bar' - nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list list5 - nm -j -g -f libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect5 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-unexported_symbol -Wl,'_*2*' - nm -j -g -f libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect6 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[abcdef]o' - nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-f]o' - nm -j -g -f libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect7 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-z]o' - nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_f[a-fnop]o' - nm -j -g -f libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} + nm -j -g -f -s __TEXT __text libfoo.dylib | diff - expect8 | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} libfoo.dylib clean: diff --git a/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile b/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile index 25a50b4..6c9456e 100644 --- a/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile +++ b/ld64/unit-tests/test-cases/exported_symbols_list-eol/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -33,7 +33,7 @@ run: all all: ${CC} ${CCFLAGS} test.c -dynamiclib -exported_symbols_list test.exp -o libtest.dylib - nm -jg libtest.dylib | grep _ > test.nm + nm -jg libtest.dylib | grep '^_' > test.nm diff test.nm expected.nm ${PASS_IFF_GOOD_MACHO} libtest.dylib diff --git a/ld64/unit-tests/test-cases/filelist/Makefile b/ld64/unit-tests/test-cases/filelist/Makefile index 0b1a743..95a0541 100644 --- a/ld64/unit-tests/test-cases/filelist/Makefile +++ b/ld64/unit-tests/test-cases/filelist/Makefile @@ -27,21 +27,23 @@ include ${TESTROOT}/include/common.makefile PWD = $(shell pwd) # -# The point of this test is to check the two forms of the -# -filelist option +# Check the two forms of the -filelist option # run: all all: - ${CC} ${CCFLAGS} -c hello.c -o hello-${ARCH}.o - ${FAIL_IF_BAD_OBJ} hello-${ARCH}.o - echo "${PWD}/hello-${ARCH}.o" > "${PWD}/filelist1" - cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}" - ${FAIL_IF_BAD_MACHO} hello-${ARCH} - echo "hello-${ARCH}.o" > "${PWD}/filelist2" - cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" - ${PASS_IFF_GOOD_MACHO} hello-${ARCH} + ${CC} ${CCFLAGS} -c hello.c -o hello.o + ${FAIL_IF_BAD_OBJ} hello.o + echo "${PWD}/hello.o" > "${PWD}/filelist1" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist1" -o "${PWD}/hello" + ${FAIL_IF_BAD_MACHO} hello + echo "hello.o" > "${PWD}/filelist2" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello" + ${FAIL_IF_BAD_MACHO} hello + echo "${PWD}/hello.o" > "${PWD}/filelist,withComma" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist,withComma" -o "${PWD}/hello" + ${PASS_IFF_GOOD_MACHO} hello clean: - rm hello-* *.o filelist1 filelist2 + rm -f hello.o hello filelist1 filelist2 filelist,withComma diff --git a/ld64/unit-tests/test-cases/got-elimination/Makefile b/ld64/unit-tests/test-cases/got-elimination/Makefile index 3f70e74..6fae5ee 100644 --- a/ld64/unit-tests/test-cases/got-elimination/Makefile +++ b/ld64/unit-tests/test-cases/got-elimination/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -42,7 +42,7 @@ all-true: all-x86_64: ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib - otool -Iv libfoobar.dylib | grep 0x | ${FAIL_IF_STDIN} + otool -Iv libfoobar.dylib | grep 0x | grep _ | ${FAIL_IF_STDIN} ${CC} ${CCFLAGS} foo.c bar.c -dynamiclib -o libfoobar.dylib -flat_namespace otool -Iv libfoobar.dylib | grep 0x | ${PASS_IFF_STDIN} diff --git a/ld64/unit-tests/test-cases/implicit-common3/Makefile b/ld64/unit-tests/test-cases/implicit-common3/Makefile deleted file mode 100644 index 6785960..0000000 --- a/ld64/unit-tests/test-cases/implicit-common3/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section -# - -run: all - -all: - ${CC} ${CCFLAGS} -c test.c -o test-${ARCH}.o - ${FAIL_IF_BAD_OBJ} test-${ARCH}.o - ${CC} ${CCFLAGS} -c a.c -o a-${ARCH}.o - ${FAIL_IF_BAD_OBJ} a-${ARCH}.o - ${FAIL_IF_ERROR} ar -r libtest-${ARCH}.a test-${ARCH}.o 2>/dev/null - ${FAIL_IF_ERROR} ranlib libtest-${ARCH}.a - ${CC} ${CCFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} - ${PASS_IFF_GOOD_MACHO} a-${ARCH} - -clean: - rm -rf *.o *.a a-* diff --git a/ld64/unit-tests/test-cases/implicit-common3/a.c b/ld64/unit-tests/test-cases/implicit-common3/a.c deleted file mode 100644 index 110842f..0000000 --- a/ld64/unit-tests/test-cases/implicit-common3/a.c +++ /dev/null @@ -1,8 +0,0 @@ -extern int common_var; -int *fn(); - -int -main(int argc, char **argv) -{ - return 0!=&common_var; -} diff --git a/ld64/unit-tests/test-cases/implicit-common3/comment.txt b/ld64/unit-tests/test-cases/implicit-common3/comment.txt deleted file mode 100644 index a1710c1..0000000 --- a/ld64/unit-tests/test-cases/implicit-common3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -The point of this test is a sanity check that ld can link a program with a large zero-fill section diff --git a/ld64/unit-tests/test-cases/implicit-common3/test.c b/ld64/unit-tests/test-cases/implicit-common3/test.c deleted file mode 100644 index e0fd7e8..0000000 --- a/ld64/unit-tests/test-cases/implicit-common3/test.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -struct abc { - int a; - int b; - int c; -} struct_var; - -int common_var; -extern const int defined_var; - -int *fn() -{ - return &common_var; -} diff --git a/ld64/unit-tests/test-cases/indirect-dylib/Makefile b/ld64/unit-tests/test-cases/indirect-dylib/Makefile index e3056a5..126d736 100644 --- a/ld64/unit-tests/test-cases/indirect-dylib/Makefile +++ b/ld64/unit-tests/test-cases/indirect-dylib/Makefile @@ -43,4 +43,4 @@ all: grep ordinal fail.log | ${PASS_IFF_EMPTY} clean: - rm *.dylib main fail.log + rm -f *.dylib main fail.log diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile b/ld64/unit-tests/test-cases/init-order/Makefile similarity index 70% rename from ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile rename to ld64/unit-tests/test-cases/init-order/Makefile index 454a412..7e8c6e1 100644 --- a/ld64/FireOpal/unit-tests/test-cases/objc-exported_symbols_list/Makefile +++ b/ld64/unit-tests/test-cases/init-order/Makefile @@ -23,18 +23,20 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - # -# Test that ObjC class exports can be suppressed +# Test that initializers are automatically sorted to start of __text +# and terminators are sorted to end of __text +# +# automatically order initializers to start of __TEXT # run: all all: - ${CC} ${CCFLAGS} -dynamiclib foo.m -framework Foundation -exported_symbols_list foo.exp -o libfoo.dylib - nm -m libfoo.dylib | grep Foo | grep ') external' | ${FAIL_IF_EMPTY} - nm -m libfoo.dylib | grep Bar | grep non-external | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} libfoo.dylib + ${CXX} ${CXXFLAGS} main.cxx foo.cxx bar.cxx -o main -dead_strip + nm -s __TEXT __text -nj main | grep -v dyld_stub_binding_helper | c++filt > actual-order.txt + ${PASS_IFF} diff actual-order.txt expected-order.txt + clean: - rm -rf *~ libfoo.dylib + rm -rf main actual-order.txt diff --git a/ld64/unit-tests/test-cases/init-order/bar.cxx b/ld64/unit-tests/test-cases/init-order/bar.cxx new file mode 100644 index 0000000..43783a8 --- /dev/null +++ b/ld64/unit-tests/test-cases/init-order/bar.cxx @@ -0,0 +1,16 @@ + + + +class Bar { +public: + Bar() : a(20) {} + ~Bar() {} +private: + int a; +}; + + +Bar b1; +Bar b2; + + diff --git a/ld64/unit-tests/test-cases/init-order/expected-order.txt b/ld64/unit-tests/test-cases/init-order/expected-order.txt new file mode 100644 index 0000000..9a303d3 --- /dev/null +++ b/ld64/unit-tests/test-cases/init-order/expected-order.txt @@ -0,0 +1,20 @@ +M::M() +__static_initialization_and_destruction_0(int, int) +global constructors keyed to m1 +Foo::Foo() +__static_initialization_and_destruction_0(int, int) +global constructors keyed to f1 +Bar::Bar() +__static_initialization_and_destruction_0(int, int) +global constructors keyed to b1 +start +_main +M::~M() +Foo::~Foo() +Bar::~Bar() +___tcf_0 +___tcf_1 +___tcf_0 +___tcf_1 +___tcf_0 +___tcf_1 diff --git a/ld64/unit-tests/test-cases/init-order/foo.cxx b/ld64/unit-tests/test-cases/init-order/foo.cxx new file mode 100644 index 0000000..b593670 --- /dev/null +++ b/ld64/unit-tests/test-cases/init-order/foo.cxx @@ -0,0 +1,14 @@ + + + +class Foo { +public: + Foo() : a(20) {} + ~Foo() {} +private: + int a; +}; + + +Foo f1; +Foo f2; diff --git a/ld64/unit-tests/test-cases/init-order/main.cxx b/ld64/unit-tests/test-cases/init-order/main.cxx new file mode 100644 index 0000000..3e92107 --- /dev/null +++ b/ld64/unit-tests/test-cases/init-order/main.cxx @@ -0,0 +1,18 @@ +#include + +class M { +public: + M() : a(20) {} + ~M() {} +private: + int a; +}; + + +M m1; +M m2; + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/kext-basic/Makefile b/ld64/unit-tests/test-cases/kext-basic/Makefile new file mode 100644 index 0000000..d233171 --- /dev/null +++ b/ld64/unit-tests/test-cases/kext-basic/Makefile @@ -0,0 +1,29 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Sanity check that ld can link a kext +# + +ifeq (${ARCH},x86_64) + FILE_TYPE = KEXTBUNDLE +else + FILE_TYPE = OBJECT +endif + + +run: all + +all: + ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykext.c -o mykext.o + ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykextinfo.c -o mykextinfo.o + ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext + otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY} + nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY} + nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY} + otool -rv mykext | grep '_extern_global' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f mykext.o mykextinfo.o mykext diff --git a/ld64/unit-tests/test-cases/kext-basic/mykext.c b/ld64/unit-tests/test-cases/kext-basic/mykext.c new file mode 100644 index 0000000..51de101 --- /dev/null +++ b/ld64/unit-tests/test-cases/kext-basic/mykext.c @@ -0,0 +1,18 @@ +#include + + +int my_global = 3; +extern int extern_global; + +kern_return_t mykext_start (kmod_info_t * ki, void * d) { + ++my_global; + ++extern_global; + return KERN_SUCCESS; +} + + +kern_return_t mykext_stop (kmod_info_t * ki, void * d) { + --my_global; + --extern_global; + return KERN_SUCCESS; +} diff --git a/ld64/unit-tests/test-cases/kext-basic/mykextinfo.c b/ld64/unit-tests/test-cases/kext-basic/mykextinfo.c new file mode 100644 index 0000000..fe31e7b --- /dev/null +++ b/ld64/unit-tests/test-cases/kext-basic/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/ld64/unit-tests/test-cases/llvm-integration/Makefile b/ld64/unit-tests/test-cases/llvm-integration/Makefile index 42c71e4..30376df 100644 --- a/ld64/unit-tests/test-cases/llvm-integration/Makefile +++ b/ld64/unit-tests/test-cases/llvm-integration/Makefile @@ -43,8 +43,7 @@ run: all: zero one two three four five six seven eight nine ten \ eleven twelve thirteen fourteen fifteen sixteen seventeen \ - eighteen nineteen twenty - + eighteen nineteen zero: # @@ -117,7 +116,7 @@ five: # llvm : b5.c : Dfoo2 # MachO : main5.c : Dfoo3, Ufoo1 # - #echo "Five..." + #echo "verify that if call to mach-o is optimized away, mach-o func is dead stripped" ${LLVMGCC} ${CCFLAGS} --emit-llvm a5.c -c -o a5.o ${LLVMGCC} ${CCFLAGS} --emit-llvm b5.c -c -o b5.o ${LLVMGCC} ${CCFLAGS} main5.c -c -o main5.o @@ -204,7 +203,7 @@ twelve: # llvm : a12.c # MachO : main12.c # - #echo "Tweleve..." + #echo "verify tentative def in llvm .o referenced from mach-o" ${LLVMGCC} ${CCFLAGS} --emit-llvm a12.c -c -o a12.o ${LLVMGCC} ${CCFLAGS} main12.c -c -o main12.o ${LLVMGCC} ${CCFLAGS} a12.o main12.o -o main12.exe @@ -275,15 +274,16 @@ nineteen: twenty: #echo verify bitcode files in archives works - ${LLVMGCC} ${CCFLAGS} --emit-llvm a20.c -c -o a20.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b20.c -c -o b20.o - ar cru lib20.a a20.o b20.o - ${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe - nm main20.exe | grep _foo | ${PASS_IFF_STDIN} - + #${LLVMGCC} ${CCFLAGS} --emit-llvm a20.c -c -o a20.o + #${LLVMGCC} ${CCFLAGS} --emit-llvm b20.c -c -o b20.o + #libtool -static a20.o b20.o -o lib20.a + #${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe + #nm main20.exe | grep _foo | ${PASS_IFF_STDIN} + + clean: - rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a + rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a main21.preload lib21.a diff --git a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile b/ld64/unit-tests/test-cases/lto-archive-dylib/Makefile similarity index 68% rename from ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile rename to ld64/unit-tests/test-cases/lto-archive-dylib/Makefile index e3d0d6a..e538ee1 100644 --- a/ld64/FireOpal/unit-tests/test-cases/lazy-dylib/Makefile +++ b/ld64/unit-tests/test-cases/lto-archive-dylib/Makefile @@ -23,24 +23,24 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -SHELL = bash # use bash shell so we can redirect just stderr - # -# Verify that -lazy_library works for function calls -# but fails for data references +# LTO : 176.gcc and 177.mesa build failure at -O4 +# +# Check that LTO can bring in an archive member and that member needs a +# symbol from a dylib. # +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + run: all all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad.c -Wl,-lazy_library,libfoo.dylib -o bad 2> fail.log - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad2.c -Wl,-lazy_library,libfoo.dylib -o bad2 2> fail.log - - + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o -o main libfoo.a ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -f libfoo.dylib main bad bad2 fail.log +clean: + rm -rf main foo.o libfoo.a main.o diff --git a/ld64/unit-tests/test-cases/lto-archive-dylib/foo.c b/ld64/unit-tests/test-cases/lto-archive-dylib/foo.c new file mode 100644 index 0000000..1592c76 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-archive-dylib/foo.c @@ -0,0 +1,5 @@ +#include +void foo() +{ + fprintf(stderr, "hello\n"); +} diff --git a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c b/ld64/unit-tests/test-cases/lto-archive-dylib/main.c similarity index 79% rename from ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c rename to ld64/unit-tests/test-cases/lto-archive-dylib/main.c index 624e009..ae2f956 100644 --- a/ld64/FireOpal/unit-tests/test-cases/llvm-integration/main20.c +++ b/ld64/unit-tests/test-cases/lto-archive-dylib/main.c @@ -1,7 +1,8 @@ extern void foo(); -int main() +int main() { foo(); return 0; } + diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/Makefile b/ld64/unit-tests/test-cases/lto-llvm-options/Makefile similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/Makefile rename to ld64/unit-tests/test-cases/lto-llvm-options/Makefile diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/main.c b/ld64/unit-tests/test-cases/lto-llvm-options/main.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/lto-llvm-options/main.c rename to ld64/unit-tests/test-cases/lto-llvm-options/main.c diff --git a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile b/ld64/unit-tests/test-cases/lto-preload-pie/Makefile similarity index 64% rename from ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile rename to ld64/unit-tests/test-cases/lto-preload-pie/Makefile index 3fb8a25..063bc33 100644 --- a/ld64/FireOpal/unit-tests/test-cases/eh-coalescing-r/Makefile +++ b/ld64/unit-tests/test-cases/lto-preload-pie/Makefile @@ -23,25 +23,25 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - -# # -# comdat warnings in ld -r -# -# also use -falign-functions to force an out of order coalesing +# verify -preload -pie produces relocations # + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + run: all all: - ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o - ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32 - ${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o - ${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log - grep warning warnings.log | ${PASS_IFF_EMPTY} + ${LLVMGCC} ${CCFLAGS} --emit-llvm a.c -c -o a.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 + otool -rv main.preload | grep "Local relocation information" | ${PASS_IFF_STDIN} + + clean: - rm foo.o bar.o baz.o foobarbaz.o warnings.log + rm a.o b.o main.o main.preload diff --git a/ld64/unit-tests/test-cases/lto-preload-pie/a.c b/ld64/unit-tests/test-cases/lto-preload-pie/a.c new file mode 100644 index 0000000..c17b3e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-preload-pie/a.c @@ -0,0 +1,4 @@ + +extern const char* mystring; + +const char** myp = &mystring; diff --git a/ld64/unit-tests/test-cases/lto-preload-pie/b.c b/ld64/unit-tests/test-cases/lto-preload-pie/b.c new file mode 100644 index 0000000..b7ad5e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-preload-pie/b.c @@ -0,0 +1 @@ + const char* mystring = "hello"; diff --git a/ld64/unit-tests/test-cases/lto-preload-pie/main.c b/ld64/unit-tests/test-cases/lto-preload-pie/main.c new file mode 100644 index 0000000..8c9c61e --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-preload-pie/main.c @@ -0,0 +1,11 @@ + +extern const char** myp; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + return myp; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/Makefile b/ld64/unit-tests/test-cases/lto-weak-native-override/Makefile similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/Makefile rename to ld64/unit-tests/test-cases/lto-weak-native-override/Makefile diff --git a/ld64/unit-tests/test-cases/lto-weak-native-override/foo.c b/ld64/unit-tests/test-cases/lto-weak-native-override/foo.c new file mode 100644 index 0000000..f65d830 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-weak-native-override/foo.c @@ -0,0 +1,6 @@ + + +void foo() +{ + // do nothing +} diff --git a/ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/main.c b/ld64/unit-tests/test-cases/lto-weak-native-override/main.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/lto-weak-native-override/main.c rename to ld64/unit-tests/test-cases/lto-weak-native-override/main.c diff --git a/ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile b/ld64/unit-tests/test-cases/no-data-bundle/Makefile similarity index 78% rename from ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile rename to ld64/unit-tests/test-cases/no-data-bundle/Makefile index d6c0639..e1417fd 100644 --- a/ld64/FireOpal/unit-tests/test-cases/zero-fill/Makefile +++ b/ld64/unit-tests/test-cases/no-data-bundle/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,15 +24,15 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# The point of this test is a sanity check that ld -# can link a program with a large zero-fill section +# Check that a bundle built with no data links +# gcc DejaGnu failure: building longcall/dylib library # run: all all: - ${CC} ${CCFLAGS} test.c -o test-${ARCH} - ${PASS_IFF_GOOD_MACHO} test-${ARCH} + ${CC} ${CCFLAGS} foo.c -bundle -o foo.bundle + ${PASS_IFF_GOOD_MACHO} foo.bundle clean: - rm -rf test-* + rm foo.bundle diff --git a/ld64/unit-tests/test-cases/no-data-bundle/foo.c b/ld64/unit-tests/test-cases/no-data-bundle/foo.c new file mode 100644 index 0000000..5cb3e31 --- /dev/null +++ b/ld64/unit-tests/test-cases/no-data-bundle/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() +{ + rand(); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/no-object-symbols/Makefile b/ld64/unit-tests/test-cases/no-object-symbols/Makefile new file mode 100644 index 0000000..b213511 --- /dev/null +++ b/ld64/unit-tests/test-cases/no-object-symbols/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that when ld creates an object file with no symbols +# and no section content, that the segment size is zero +# and the LC_SYMTAB is empty. +# +# LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype +# + +run: all + +all: + as -arch ${ARCH} -n empty.s -o empty.o + ${LD} -r empty.o -x -o empty2.o + otool -lv empty2.o | egrep 'vmsize 0x0[0]+$$' | ${FAIL_IF_EMPTY} + otool -lv empty2.o | grep 'filesize 0' | ${FAIL_IF_EMPTY} + otool -lv empty2.o | grep 'nsyms 0' | ${FAIL_IF_EMPTY} + otool -lv empty2.o | grep 'symoff 0' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm empty.o empty2.o diff --git a/ld64/unit-tests/test-cases/no-object-symbols/empty.s b/ld64/unit-tests/test-cases/no-object-symbols/empty.s new file mode 100644 index 0000000..83fd519 --- /dev/null +++ b/ld64/unit-tests/test-cases/no-object-symbols/empty.s @@ -0,0 +1,2 @@ + + .text diff --git a/ld64/unit-tests/test-cases/non-lazy-r/Makefile b/ld64/unit-tests/test-cases/non-lazy-r/Makefile index 3c14103..d581048 100644 --- a/ld64/unit-tests/test-cases/non-lazy-r/Makefile +++ b/ld64/unit-tests/test-cases/non-lazy-r/Makefile @@ -49,12 +49,14 @@ hasnl: ${CC} ${CCFLAGS} -c other.c -o other.o ${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo # make sure there are two indirect symbols: _foo and LOCAL - otool -Iv fooall.o | grep "2 entries" | ${FAIL_IF_EMPTY} - otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep "3 entries" | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep _tent | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN} # make sure re-parsed correctly ${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY} ${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep '_tent$$non_lazy_ptr' | ${FAIL_IF_EMPTY} ${PASS_IFF} true clean: diff --git a/ld64/unit-tests/test-cases/non-lazy-r/foo.c b/ld64/unit-tests/test-cases/non-lazy-r/foo.c index 1fa325e..9d21475 100644 --- a/ld64/unit-tests/test-cases/non-lazy-r/foo.c +++ b/ld64/unit-tests/test-cases/non-lazy-r/foo.c @@ -10,3 +10,8 @@ extern int other; int getother() { return other; } + +extern int tent; + +int gettent() { return tent; } + diff --git a/ld64/unit-tests/test-cases/non-lazy-r/other.c b/ld64/unit-tests/test-cases/non-lazy-r/other.c index 6420437..68eb01d 100644 --- a/ld64/unit-tests/test-cases/non-lazy-r/other.c +++ b/ld64/unit-tests/test-cases/non-lazy-r/other.c @@ -1,2 +1,3 @@ int foo = 2; int other = 3; +int tent; diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile similarity index 68% rename from ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile rename to ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile index 9edc357..76a7347 100644 --- a/ld64/FireOpal/unit-tests/test-cases/objc-literal-pointers/Makefile +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -26,20 +26,30 @@ include ${TESTROOT}/include/common.makefile # # Verify an Objective-C object file when run through -# ld -r is unaltered. -# __cls_refs section losing S_LITERAL_POINTERS section type +# ld -r -x +# x86_64 obj-c runtime confused when static lib is stripped # -# note: i386 and ppc objc use some anonymous zerofill that moves and needs to be ignore to compare # +SELECTOR_REFS = "__OBJC,__message_refs" + +ifeq ($(ARCH),x86_64) + SELECTOR_REFS = "__DATA,__objc_selrefs" +endif +ifeq ($(ARCH),armv6) + SELECTOR_REFS = "__DATA,__objc_selrefs" +endif + + + run: all all: ${CC} ${CCFLAGS} test.m -c -o test.o - ObjectDump -no_content test.o | grep -v zero-fill-at> test.dump + ${OBJECTDUMP} -no_content test.o | grep -B3 -A6 ${SELECTOR_REFS} > test.dump - ${LD} -arch ${ARCH} -r test.o -o test-r.o - ObjectDump -no_content test-r.o | grep -v zero-fill-at > test-r.dump + ${LD} -arch ${ARCH} -r test.o -x -o test-r.o + ${OBJECTDUMP} -no_content test-r.o | grep -B3 -A6 ${SELECTOR_REFS} > test-r.dump diff test.dump test-r.dump | ${PASS_IFF_EMPTY} diff --git a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m similarity index 76% rename from ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m rename to ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m index 1ca2157..4837911 100644 --- a/ld64/FireOpal/unit-tests/test-cases/relocs-objc/test.m +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,39 +21,26 @@ * * @APPLE_LICENSE_HEADER_END@ */ -#include - - -@interface Foo : NSObject -{ - int ivar; -} -- (id) init; -- (void) foo; -@end + +#include +#include +@interface Foo @end @implementation Foo -- (id) init -{ - self = [super init]; - return self; ++(void)initialize { } ++(void)foo { + fprintf(stderr, "GOOD\n"); + exit(0); } - -- (void) foo -{ - [self class]; ++(void)bar { + fprintf(stderr, "BAD\n"); + abort(); } @end - - -@interface Base -@end - - -@implementation Base -@end - - - +void PublicFunction(void) +{ + [Foo foo]; + [Foo bar]; +} diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile index 9edc357..c0b9081 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile +++ b/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile @@ -36,10 +36,10 @@ run: all all: ${CC} ${CCFLAGS} test.m -c -o test.o - ObjectDump -no_content test.o | grep -v zero-fill-at> test.dump + ${OBJECTDUMP} -no_content test.o | grep -v zero-fill-at> test.dump - ${LD} -arch ${ARCH} -r test.o -o test-r.o - ObjectDump -no_content test-r.o | grep -v zero-fill-at > test-r.dump + ${LD} -arch ${ARCH} -r test.o -keep_private_externs -o test-r.o + ${OBJECTDUMP} -no_content test-r.o | grep -v zero-fill-at > test-r.dump diff test.dump test-r.dump | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/operator-new/Makefile b/ld64/unit-tests/test-cases/operator-new/Makefile index 8abf3e1..447ee87 100644 --- a/ld64/unit-tests/test-cases/operator-new/Makefile +++ b/ld64/unit-tests/test-cases/operator-new/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,6 +30,9 @@ all: # verify if operator new is overridden that WEAK_DEFINES is set ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} + # verify if operator new is overridden but not exported, WEAK_DEFINES is not set + ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx -Wl,-exported_symbol,_main + otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN} # verify if operator new is not overridden that WEAK_DEFINES is not set ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx index 3c99e35..b5d3272 100644 --- a/ld64/unit-tests/test-cases/operator-new/main.cxx +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -36,7 +36,7 @@ #if OP_NEW void* operator new(size_t s) throw (std::bad_alloc) { - return malloc(s);; + return malloc(s); } #endif diff --git a/ld64/unit-tests/test-cases/order_file-ans/Makefile b/ld64/unit-tests/test-cases/order_file-ans/Makefile index 23fe568..a2e0b1d 100644 --- a/ld64/unit-tests/test-cases/order_file-ans/Makefile +++ b/ld64/unit-tests/test-cases/order_file-ans/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,9 +30,9 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CXX} ${CXXFLAGS} main.cxx -DANCHOR=1 -o main -Wl,-order_file -Wl,main.order + ${CXX} ${CXXFLAGS} main.cxx -o main -Wl,-order_file -Wl,main.order ${FAIL_IF_BAD_MACHO} main - nm -n -g -j main | grep "_GLOBAL__N" > main.actual + nm -n -j main | grep "_GLOBAL__N" > main.actual ${PASS_IFF} diff main.actual main.expected diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.cxx b/ld64/unit-tests/test-cases/order_file-ans/main.cxx index b0412f9..b3c1edd 100644 --- a/ld64/unit-tests/test-cases/order_file-ans/main.cxx +++ b/ld64/unit-tests/test-cases/order_file-ans/main.cxx @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,9 +24,6 @@ #include -#if ANCHOR - int anchor = 4; -#endif namespace { struct myanonstruct { int a; }; diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.expected b/ld64/unit-tests/test-cases/order_file-ans/main.expected index 75e104f..4c510ae 100644 --- a/ld64/unit-tests/test-cases/order_file-ans/main.expected +++ b/ld64/unit-tests/test-cases/order_file-ans/main.expected @@ -1,4 +1,4 @@ -__Z3barPN17_GLOBAL__N_anchor12myanonstructE -__ZN3wow17_GLOBAL__N_anchor5innerEv -__ZN17_GLOBAL__N_anchor3bazEPNS_12myanonstructE -__ZN17_GLOBAL__N_anchor3fooEv +__Z3barPN12_GLOBAL__N_112myanonstructE +__ZN3wow12_GLOBAL__N_15innerEv +__ZN12_GLOBAL__N_13bazEPNS_12myanonstructE +__ZN12_GLOBAL__N_13fooEv diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.order b/ld64/unit-tests/test-cases/order_file-ans/main.order index 36dd786..4c510ae 100644 --- a/ld64/unit-tests/test-cases/order_file-ans/main.order +++ b/ld64/unit-tests/test-cases/order_file-ans/main.order @@ -1,4 +1,4 @@ -__Z3barPN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C12myanonstructE -__ZN3wow95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C5innerEv -__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3bazEPNS_12myanonstructE -__ZN95_GLOBAL__N__Volumes_my_src_ld64_unit_tests_test_cases_order_file_ans_main.cxx_00000000_38BA812C3fooEv +__Z3barPN12_GLOBAL__N_112myanonstructE +__ZN3wow12_GLOBAL__N_15innerEv +__ZN12_GLOBAL__N_13bazEPNS_12myanonstructE +__ZN12_GLOBAL__N_13fooEv diff --git a/ld64/unit-tests/test-cases/prebound-main/Makefile b/ld64/unit-tests/test-cases/prebound-main/Makefile index 79ef53e..285d767 100644 --- a/ld64/unit-tests/test-cases/prebound-main/Makefile +++ b/ld64/unit-tests/test-cases/prebound-main/Makefile @@ -1,25 +1,4 @@ -## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## + TESTROOT = ../.. include ${TESTROOT}/include/common.makefile @@ -43,7 +22,8 @@ endif run: all all: - ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 + # SnowLeopard is missing libmx.dylib which gcc thinks it needs + ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 -nostdlib -lcrt1.o -lSystem otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile similarity index 53% rename from ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile rename to ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile index a1dfd88..fdb13d5 100644 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/Makefile +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,34 +32,33 @@ include ${TESTROOT}/include/common.makefile run: all all: - + # -sub_library for 10.4 ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libmiddle.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.4 ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.4 - otool -L main | grep libbar | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libother.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.4 nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. -mmacosx-version-min=10.4 - otool -L main | grep libbar | ${FAIL_IF_STDIN} - + ${PASS_IFF_GOOD_MACHO} main # -sub_library for 10.5 ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5 ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libmiddle.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.5 ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.5 - otool -L main | grep libbar | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libother.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.5 nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. -mmacosx-version-min=10.5 - otool -L main | grep libbar | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main - - ${PASS_IFF} /usr/bin/true - clean: - - rm -rf libbar.dylib libfoo.dylib main + rm -rf libbar.dylib libfoo.dylib libmiddle.dylib libother.dylib main diff --git a/ld64/FireOpal/unit-tests/test-cases/indirect-path-search/bar.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/indirect-path-search/bar.c rename to ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c diff --git a/ld64/FireOpal/unit-tests/test-cases/blank-stubs/foo.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/blank-stubs/foo.c rename to ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c diff --git a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c similarity index 73% rename from ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c rename to ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c index 2b85b0e..672ef9a 100644 --- a/ld64/FireOpal/unit-tests/test-cases/re-export-optimizations/main.c +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c @@ -3,8 +3,6 @@ extern void bar(); int main() { -#if CALL_BAR bar(); -#endif return 0; } diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c new file mode 100644 index 0000000..d3578a6 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c @@ -0,0 +1,3 @@ + +void middle() {} + diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c new file mode 100644 index 0000000..0cd6dda --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c @@ -0,0 +1 @@ +void other() {} diff --git a/ld64/unit-tests/test-cases/rebase-basic/Makefile b/ld64/unit-tests/test-cases/rebase-basic/Makefile index ba262aa..9373aa3 100644 --- a/ld64/unit-tests/test-cases/rebase-basic/Makefile +++ b/ld64/unit-tests/test-cases/rebase-basic/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -38,16 +38,16 @@ all: ${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o - ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation + ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -Wl,-no_order_data -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib - ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib + ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -Wl,-no_order_data -o libfoo-alt.${ARCH}.dylib -framework Foundation -framework CoreFoundation -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib ${FAIL_IF_BAD_MACHO} libfoo-alt.${ARCH}.dylib - - rebase -arch ${ARCH} -low_address 0x12340000 libfoo.${ARCH}.dylib + + ${REBASE} -arch ${ARCH} -low_address 0x12340000 libfoo.${ARCH}.dylib ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib ${PASS_IFF} diff libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib clean: - rm *.o *.dylib + rm -f *.o *.dylib diff --git a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s index 06e10e8..73c31ea 100644 --- a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -21,6 +21,8 @@ * @APPLE_LICENSE_HEADER_END@ */ + + #if __arm__ .text .align 2 @@ -85,6 +87,18 @@ _test_branches: @ call external + addend bne _external+16 + +_pointer_diffs: + nop + bl 1f +1: nop + .long _foo-1b + .long _foo+10-1b + .long _test_branches-1b + .long _test_branches+3-1b + .long (_test_branches - _test_loads) + -2097152 + .long (_test_calls - _test_loads) + -2097152 + #endif #if __ppc__ || __ppc64__ @@ -249,6 +263,12 @@ _test_calls: # call internal + addend call _test_branches+0x19000 + # 16-bit call internal + callw _test_branches + + # 16-bit call internal + addend + callw _test_branches+13 + # call external call _external @@ -276,6 +296,9 @@ _pointer_diffs: movl _foo+10-1b(%eax),%esi movl _test_branches-1b(%eax),%esi movl _test_branches+3-1b(%eax),%esi + cmpl $(( (_test_branches - _test_loads) + -2097152 )),(%esp) + cmpl $(( (_test_calls - _test_loads) + -2097152 )),(%esp) + _word_relocs: callw _pointer_diffs @@ -426,6 +449,9 @@ L1: .quad _test_branches - _test_diffs .quad _test_branches - . .quad _test_branches - L1 .quad L1 - _prev + #tests support for 32-bit absolute pointers + .long _prev + .long L1 # the following generates: _foo cannot be undefined in a subtraction expression # but it should be ok (it will be a linker error if _foo and _bar are not in same linkage unit) diff --git a/ld64/FireOpal/unit-tests/test-cases/end-label/Makefile b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile similarity index 64% rename from ld64/FireOpal/unit-tests/test-cases/end-label/Makefile rename to ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile index 69f51a7..36fb47b 100644 --- a/ld64/FireOpal/unit-tests/test-cases/end-label/Makefile +++ b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,18 +24,28 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Check that ld maintains two symbols with the same address and in different sections +# i386 relocation error with negative offsets from local labels # -run: all +ifeq (${ARCH},i386) + TARGET = run-i386 +else + TARGET = run-other +endif -all: - ${CC} ${CCFLAGS} foo.s -c -o foo.o - ${CC} ${CCFLAGS} bar.s -c -o bar.o - ${LD} -r foo.o bar.o -o foobar.o - nm -m foobar.o | grep _next | grep __other | ${FAIL_IF_EMPTY} - ${LD} -r foobar.o -o foobar2.o - nm -m foobar2.o | grep _next | grep __other | ${PASS_IFF_STDIN} +run: ${TARGET} +run-other: + ${PASS_IFF} /usr/bin/true + + +run-i386: + ${CC} ${ASMFLAGS} test.s -c -o test.o + ${OBJECTDUMP} test.o | grep "__data@0 plus 0xFFFFFFE2" | ${FAIL_IF_EMPTY} + + ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o + ${OBJECTDUMP} test-r.o | grep "__data@0 plus 0xFFFFFFE2" | ${PASS_IFF_STDIN} + + clean: rm -rf *.o diff --git a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s b/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s similarity index 79% rename from ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s rename to ld64/unit-tests/test-cases/relocs-neg-from-local/test.s index 4559893..3890f35 100644 --- a/ld64/FireOpal/unit-tests/test-cases/multiple-entry-points/test.s +++ b/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,34 +21,23 @@ * @APPLE_LICENSE_HEADER_END@ */ + +#if __i386__ .text .align 2 - .globl _foo - .globl _foo2 - .globl _foo3 -_foo: -_foo2: -_foo3: - nop - - - - .align 2 -_bar: - nop - - - .align 2 - .globl _xx - .globl __xx -_xx: -__xx: - nop - - - .align 2 -_ok: - nop - +_negative_offset_from_local_label: + nop + .space 100 + movl -80+L3(,%eax,4), %edx + ret + + .data +L2: .space 50 +L3: .space 50 +_d: .long 0 + +#endif + + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile b/ld64/unit-tests/test-cases/section-names-long/Makefile similarity index 58% rename from ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile rename to ld64/unit-tests/test-cases/section-names-long/Makefile index 68d809c..f31d9d6 100644 --- a/ld64/FireOpal/unit-tests/test-cases/commons-order/Makefile +++ b/ld64/unit-tests/test-cases/section-names-long/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,17 +24,19 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Validate linker puts commons in the correct order. -# -fno-commons come first, followed by all other commons -# in .o order and alphabetically within each .o +# Validate long section names are preserved +# corrupt metaclass entry in dynamic library # all: - ${CC} ${CCFLAGS} baz.c -fno-common -c -o baz.o - ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main -mmacosx-version-min=10.5 - nm -j -n main | grep _common > symbol.order - ${FAIL_IF_ERROR} diff symbol.order expected.order + ${CC} ${CCFLAGS} main.c a.s c.s b.s -o main + nm -m main | grep __aaaaaaaaaaaaaa | grep __TEXT | grep _at | ${FAIL_IF_EMPTY} + nm -m main | grep __bbbbbbbbbbbbbb | grep __TEXT | grep _bt | ${FAIL_IF_EMPTY} + nm -m main | grep __cccccccccccccc | grep __TEXT | grep _ct | ${FAIL_IF_EMPTY} + nm -m main | grep __aaaaaaaaaaaaaa | grep __DATA | grep _ad | ${FAIL_IF_EMPTY} + nm -m main | grep __bbbbbbbbbbbbbb | grep __DATA | grep _bd | ${FAIL_IF_EMPTY} + nm -m main | grep __cccccccccccccc | grep __DATA | grep _cd | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf main baz.o symbol.order + rm -rf main diff --git a/ld64/unit-tests/test-cases/section-names-long/a.s b/ld64/unit-tests/test-cases/section-names-long/a.s new file mode 100644 index 0000000..d7e5847 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/a.s @@ -0,0 +1,9 @@ + + .section __TEXT,__aaaaaaaaaaaaaa +_at: .space 128 + + .section __DATA,__aaaaaaaaaaaaaa +_ad: .space 128 + + + diff --git a/ld64/unit-tests/test-cases/section-names-long/b.s b/ld64/unit-tests/test-cases/section-names-long/b.s new file mode 100644 index 0000000..a31d414 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/b.s @@ -0,0 +1,9 @@ + + .section __TEXT,__bbbbbbbbbbbbbb +_bt: .space 128 + + .section __DATA,__bbbbbbbbbbbbbb +_bd: .space 128 + + + diff --git a/ld64/unit-tests/test-cases/section-names-long/c.s b/ld64/unit-tests/test-cases/section-names-long/c.s new file mode 100644 index 0000000..383b159 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/c.s @@ -0,0 +1,11 @@ + + .section __TEXT,__cccccccccccccc +_ct: .space 128 + + + .section __DATA,__cccccccccccccc +_cd: .space 128 + + + + diff --git a/ld64/FireOpal/unit-tests/test-cases/commons-order/main.c b/ld64/unit-tests/test-cases/section-names-long/main.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/commons-order/main.c rename to ld64/unit-tests/test-cases/section-names-long/main.c diff --git a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile b/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile similarity index 54% rename from ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile rename to ld64/unit-tests/test-cases/shared-cache-dylib/Makefile index cf300a3..cd6dc04 100644 --- a/ld64/FireOpal/unit-tests/test-cases/exported-symbols-wildcards-dead_strip/Makefile +++ b/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,16 +24,23 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Tests the use of wildcards in exported symbol lists and dead stripping +# Verify only dylibs with install paths in /System/Library or /usr/lib +# get LC_SEGMENT_SPLIT_INFO # + run: all all: - ${CC} -dynamiclib foo.c -o libfoo.dylib -Wl,-exported_symbol -Wl,'_AB*' -dead_strip - nm -j -f libfoo.dylib | grep _good | ${FAIL_IF_EMPTY} - nm -j -f libfoo.dylib | grep _bad | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} libfoo.dylib - + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/lib/libfoo.dylib + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /System/Library/Frameworks/Foo.framework/Foo + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + clean: rm libfoo.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/symbol-moving/foo.c b/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/symbol-moving/foo.c rename to ld64/unit-tests/test-cases/shared-cache-dylib/foo.c diff --git a/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile b/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile index ee6a0e5..e9c09c4 100644 --- a/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile +++ b/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile @@ -24,7 +24,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Sanity check that -slow_stubs for i386 leaves no __IMPORT segment +# Sanity check that i386 for 10.6 does not use __IMPORT segment # run: all @@ -32,9 +32,9 @@ run: all all: - ${CC} ${CCFLAGS} hello.c -o hello -Wl,-slow_stubs + ${CC} ${CCFLAGS} hello.c -o hello size -l hello | grep __IMPORT | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib -Wl,-slow_stubs + ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} hello diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile index c0647b3..fcb7e7d 100644 --- a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,18 +23,14 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ifeq "${ARCH}" "i386" - POINTER_SEGMENT = __IMPORT - POINTER_SECTION = __pointers -else - POINTER_SEGMENT = __DATA - POINTER_SECTION = __nl_symbol_ptr -endif +POINTER_SEGMENT = __DATA +POINTER_SECTION = __nl_symbol_ptr # # Test that using strip -R to selectively strip symbol names # of of a .o file still works with ld. +# And for i386 that there are no __IMPORT/__pointers left # run: all @@ -51,6 +47,8 @@ all: ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers + size -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN} + size -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN} ${PASS_IFF} diff dylib1.pointers dylib2.pointers clean: diff --git a/ld64/unit-tests/test-cases/switch-jump-table/Makefile b/ld64/unit-tests/test-cases/switch-jump-table/Makefile index aacd78d..847dc78 100644 --- a/ld64/unit-tests/test-cases/switch-jump-table/Makefile +++ b/ld64/unit-tests/test-cases/switch-jump-table/Makefile @@ -25,14 +25,23 @@ include ${TESTROOT}/include/common.makefile # # Test that -mdynamic-no-pic jump table in the middle of -# a function does not cause relocations. +# a function does not cause relocations. # # SPEC2000/eon built with -mdynamic-no-pic won't run # -run: all +run: test-run-${ARCH} -all: + +test-run-ppc: + ${PASS_IFF} true + +test-run-x86_64: + ${PASS_IFF} true + +test-run-armv6: test-run-i386 + +test-run-i386: # check jump table in a weak function ${CC} ${CCFLAGS} main.c switch.s -o main otool -rv main | grep _foo | ${FAIL_IF_STDIN} @@ -48,7 +57,7 @@ all: # check jump table with -pie, should have no external and some local relocations ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-pie -read_only_relocs suppress otool -rv main | grep "External relocation" | ${FAIL_IF_STDIN} - otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY} +# otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/ld64/unit-tests/test-cases/symbol-moving/Makefile b/ld64/unit-tests/test-cases/symbol-moving/Makefile index fc85d7b..9e63ee5 100644 --- a/ld64/unit-tests/test-cases/symbol-moving/Makefile +++ b/ld64/unit-tests/test-cases/symbol-moving/Makefile @@ -90,4 +90,4 @@ all: clean: - rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a + rm -rf libbar.dylib libfoo.dylib main-10.4 main-10.5 main-10.4a main-10.5a Frameworks diff --git a/ld64/unit-tests/test-cases/tentative-and-archive/Makefile b/ld64/unit-tests/test-cases/tentative-and-archive/Makefile index e8a4a0e..ee5341d 100644 --- a/ld64/unit-tests/test-cases/tentative-and-archive/Makefile +++ b/ld64/unit-tests/test-cases/tentative-and-archive/Makefile @@ -29,22 +29,31 @@ 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 _var which -# should *not* cause libfoo.a(foo.o) to be loaded. +# should cause libfoo.a(foo.o) to be loaded which in turn +# should cause _bar from libbar.dylib to be used. # # ld crashes building XsanFS # -undefined dynamic_lookup causes spurious extra symbols +# need to search for definitions for common symbols # run: all all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib ${CC} ${CCFLAGS} foo.c -c -o foo.o - libtool -static foo.o -o libfoo.a - ${CC} ${CCFLAGS} main.c libfoo.a -o main + libtool -static foo.o -o libfoo.a + ${CC} ${CCFLAGS} main.c libfoo.a libbar.dylib -o main + # verify _foo got pulled in because _var was a tentative in main.o + nm main | grep "_foo" | ${FAIL_IF_EMPTY} + # verify -dead_strip pulls in non-tentative _var from libfoo.a + ${CC} ${CCFLAGS} main.c libfoo.a libbar.dylib -o main -dead_strip + nm -m main | grep "_var" | grep __data | ${FAIL_IF_EMPTY} + # verify dynamic_lookup works and has no duplicate symbols ${CC} ${CCFLAGS} main.c libfoo.a -o main -undefined dynamic_lookup - nm -m main | grep "looked up" | ${FAIL_IF_STDIN} + nm -m main | grep "looked up" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf main libfoo.a foo.o + rm -rf main libfoo.a foo.o libbar.dylib diff --git a/ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/bar.c b/ld64/unit-tests/test-cases/tentative-and-archive/bar.c similarity index 100% rename from ld64/FireOpal/unit-tests/test-cases/tentative-and-dylib/bar.c rename to ld64/unit-tests/test-cases/tentative-and-archive/bar.c diff --git a/ld64/unit-tests/test-cases/tentative-and-archive/main.c b/ld64/unit-tests/test-cases/tentative-and-archive/main.c index e5046a0..a01e6b0 100644 --- a/ld64/unit-tests/test-cases/tentative-and-archive/main.c +++ b/ld64/unit-tests/test-cases/tentative-and-archive/main.c @@ -1,5 +1,6 @@ int var; +int other_tent; int main() { diff --git a/ld64/unit-tests/test-cases/thumb-pointer/Makefile b/ld64/unit-tests/test-cases/thumb-pointer/Makefile new file mode 100644 index 0000000..b4364b8 --- /dev/null +++ b/ld64/unit-tests/test-cases/thumb-pointer/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# pointers to thumb symbols can be mangled +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + # verify no +1 thumb errors + ${FAIL_IF_ERROR} ${OBJECTDUMP} foo.o | grep "plus 0x00000001" | ${FAIL_IF_STDIN} + ${FAIL_IF_ERROR} ${OBJECTDUMP} bar.o | grep "plus 0x00000001" | ${FAIL_IF_STDIN} + ${LD} -arch ${ARCH} -r -keep_private_externs foo.o bar.o -o foobar.o + ${LD} -arch ${ARCH} -r -keep_private_externs foobar.o -o foobar2.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content foobar.o > foobar.o.dump + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content foobar2.o > foobar2.o.dump + # verify no +1 thumb errors in merged result + grep "plus 0x00000001" foobar.o.dump | ${FAIL_IF_STDIN} + # verify round trip though ld -r works + ${PASS_IFF} diff foobar.o.dump foobar2.o.dump + +clean: + rm -rf foo.o bar.o foobar.o foobar2.o foobar.o.dump foobar2.o.dump diff --git a/ld64/unit-tests/test-cases/thumb-pointer/bar.c b/ld64/unit-tests/test-cases/thumb-pointer/bar.c new file mode 100644 index 0000000..202a0b3 --- /dev/null +++ b/ld64/unit-tests/test-cases/thumb-pointer/bar.c @@ -0,0 +1,5 @@ + +void bar1() {} +void bar2() {} +char bar_array[3] = { 1,2,3 }; + diff --git a/ld64/unit-tests/test-cases/thumb-pointer/foo.c b/ld64/unit-tests/test-cases/thumb-pointer/foo.c new file mode 100644 index 0000000..96a8a33 --- /dev/null +++ b/ld64/unit-tests/test-cases/thumb-pointer/foo.c @@ -0,0 +1,14 @@ + + +extern void bar1(); +extern void bar2(); +extern char bar_array[]; + +void foo1() {} +void foo2() {} +char foo_array[3] = { 1,2,3 }; + + + +void* foostuff[] = { &foo1, &foo2, foo_array, &foo_array[3] }; +void* barstuff[] = { &bar1, &bar2, bar_array, &bar_array[3] }; diff --git a/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile b/ld64/unit-tests/test-cases/unexported_symbols_list-r/Makefile similarity index 74% rename from ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile rename to ld64/unit-tests/test-cases/unexported_symbols_list-r/Makefile index 44023e6..7aba765 100644 --- a/ld64/FireOpal/unit-tests/test-cases/objc-category-debug-notes/Makefile +++ b/ld64/unit-tests/test-cases/unexported_symbols_list-r/Makefile @@ -25,16 +25,16 @@ include ${TESTROOT}/include/common.makefile # -# Verify no GSYM for .objc_category_* -# Linker should not make GSYM debug note for .objc_category_* symbols# +# Verify -unexported_symbols_list works with -r +# Building kext x86_64 with unexported symbols file causes linking problems # run: all all: - ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation - nm -ap test | grep GSYM | grep category | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} test - + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -arch ${ARCH} foo.o -r -unexported_symbols_list foo.exp -o foo2.o + nm -m foo2.o | grep _foo | grep "non-external" | ${PASS_IFF_STDIN} + clean: - rm -rf test test.dSYM + rm -rf foo.o foo2.o diff --git a/ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.c b/ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.c new file mode 100644 index 0000000..7bb9849 --- /dev/null +++ b/ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.c @@ -0,0 +1,9 @@ + +extern int fooCount; +extern int barCount; + +void foo() { fooCount++; } +void bar() { barCount++; } +int global = 4; +int googoo = 5; + diff --git a/ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.exp b/ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.exp new file mode 100644 index 0000000..c39774c --- /dev/null +++ b/ld64/unit-tests/test-cases/unexported_symbols_list-r/foo.exp @@ -0,0 +1,3 @@ +_foo +_global + diff --git a/ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile b/ld64/unit-tests/test-cases/weak-def-flag/Makefile similarity index 65% rename from ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile rename to ld64/unit-tests/test-cases/weak-def-flag/Makefile index 8abf3e1..71d5db6 100644 --- a/ld64/FireOpal/unit-tests/test-cases/operator-new/Makefile +++ b/ld64/unit-tests/test-cases/weak-def-flag/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# Copyright (c) 2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,17 +24,23 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +# +# +# Tests that a main executable with a weak symbol has MH_WEAK_DEFINES +# Tests that a main executable with a weak symbol made non-global by +# an export list does not has MH_WEAK_DEFINES +# + run: all all: - # verify if operator new is overridden that WEAK_DEFINES is set - ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx + ${CC} ${CCFLAGS} main.c -o main otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} - # verify if operator new is not overridden that WEAK_DEFINES is not set - ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx - otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY} - - + ${CC} ${CCFLAGS} main.c -o main -Wl,-exported_symbol,_main + otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} main.c -o main -Wl,-exported_symbol,_my_weak + otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + clean: - ${RM} ${RMFLAGS} *~ main - + rm main diff --git a/ld64/unit-tests/test-cases/weak-def-flag/main.c b/ld64/unit-tests/test-cases/weak-def-flag/main.c new file mode 100644 index 0000000..9cb4dfb --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-flag/main.c @@ -0,0 +1,13 @@ +#include + +void __attribute__((weak)) my_weak() +{ + +} + +int main() +{ + my_weak(); + return 0; +} + diff --git a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile b/ld64/unit-tests/test-cases/weak_import-force/Makefile similarity index 65% rename from ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile rename to ld64/unit-tests/test-cases/weak_import-force/Makefile index e7cf05c..79ce07c 100644 --- a/ld64/FireOpal/unit-tests/test-cases/stub-generation-weak/Makefile +++ b/ld64/unit-tests/test-cases/weak_import-force/Makefile @@ -1,15 +1,15 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2008 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ -# +# # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. -# +# # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,26 +17,28 @@ # 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 ld generates correct stubs when some are weak_import +# Test that -weak_library marks all symbols used as weak # + run: all -all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} main.c libfoo.dylib -o main - otool -Iv main | grep '_foo' | ${FAIL_IF_EMPTY} - otool -Iv main | grep '_bar' | ${FAIL_IF_EMPTY} - otool -Iv main | grep '_baz' | ${FAIL_IF_EMPTY} +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} main.c -o main -weak_library libfoo.dylib libbar.dylib + nm -m main | grep _foo1 | grep weak | ${FAIL_IF_EMPTY} + nm -m main | grep _bar | grep weak | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} main - clean: - rm libfoo.dylib main + rm -rf libfoo.dylib libbar.dylib main diff --git a/ld64/unit-tests/test-cases/weak_import-force/bar.c b/ld64/unit-tests/test-cases/weak_import-force/bar.c new file mode 100644 index 0000000..611f2d9 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-force/bar.c @@ -0,0 +1,9 @@ + + + +void bar1() {} +void bar2() {} + + +int bar_data1 = 0; +int bar_data2 = 0; diff --git a/ld64/unit-tests/test-cases/weak_import-force/foo.c b/ld64/unit-tests/test-cases/weak_import-force/foo.c new file mode 100644 index 0000000..c0bbc1e --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-force/foo.c @@ -0,0 +1,9 @@ + + + +void foo1() {} +void foo2() {} + + +int foo_data1 = 0; +int foo_data2 = 0; diff --git a/ld64/unit-tests/test-cases/weak_import-force/main.c b/ld64/unit-tests/test-cases/weak_import-force/main.c new file mode 100644 index 0000000..3b9cdff --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-force/main.c @@ -0,0 +1,31 @@ + +extern void foo1(); +extern void foo2(); +extern void bar1(); +extern void bar2(); + +extern int foo_data1; +extern int foo_data2; +extern int bar_data1; +extern int bar_data2; + + + +// make external relocation to foo_data1 and bar_data1 +int* pfoo = &foo_data1; +int* pbar = &bar_data1; + + +int main (void) +{ + // make non-lazy reference to foo1 and bar1 + if ( &foo1 == &bar1 ) { + // make lazy reference to foo2 and bar2 + foo2(); + bar2(); + } + + // make non-lazy reference to foo_data2 and bar_data2 + return *pfoo + *pbar + foo_data2 + bar_data2; +} + diff --git a/ld64/unit-tests/test-cases/weak_import/Makefile b/ld64/unit-tests/test-cases/weak_import/Makefile index d1fa1f3..cdacfc9 100644 --- a/ld64/unit-tests/test-cases/weak_import/Makefile +++ b/ld64/unit-tests/test-cases/weak_import/Makefile @@ -34,7 +34,7 @@ all: ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib ${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} main.c -o main-${ARCH} libfoo-${ARCH}.dylib nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null @@ -43,10 +43,12 @@ all: nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null - otool -rv main-${ARCH} | grep _data6 > /dev/null + nm -m main-${ARCH} | grep _data5 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data6 | grep weak >/dev/null + #otool -rv main-${ARCH} | grep _data6 > /dev/null ${FAIL_IF_BAD_MACHO} main-${ARCH} - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null @@ -55,7 +57,7 @@ all: nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null - otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null + #otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib clean: From cbb12541bfd9dc477e4a99707b29d1f0f0e6696d Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:31:46 +0100 Subject: [PATCH 03/48] 95-8-4 --- ld64/ChangeLog | 4536 +---------------- ld64/doc/man/man1/ld.1 | 4 + ld64/src/ld/Architectures.hpp | 6 +- ld64/src/ld/LTOReader.hpp | 3 +- ld64/src/ld/MachOReaderDylib.hpp | 9 +- ld64/src/ld/MachOReaderRelocatable.hpp | 423 +- ld64/src/ld/MachOWriterExecutable.hpp | 278 +- ld64/src/ld/ObjectFile.h | 7 +- ld64/src/ld/Options.cpp | 20 +- ld64/src/ld/ld.cpp | 64 +- ld64/src/other/dyldinfo.cpp | 15 +- ld64/src/other/unwinddump.cpp | 390 +- ld64/unit-tests/run-all-unit-tests | 2 - .../coalesce_weak_def_in_dylib/Makefile | 55 - .../coalesce_weak_def_in_dylib/foo.c | 4 - .../coalesce_weak_def_in_dylib/main.c | 17 - .../test-cases/cstring-alt-segment/custom.s | 8 - .../test-cases/cstring-alt-segment/main.c | 7 - .../test-cases/cstring-labels/foo.c | 2 - .../dead_strip-r_symbol_desc/Makefile | 43 - .../test-cases/dead_strip-weak-coalesce/baz.c | 7 - .../test-cases/dead_strip-weak-coalesce/foo.c | 25 - .../dead_strip-weak-coalesce/main.c | 13 - .../eh-coalescing-no-labels/Makefile | 2 +- .../unit-tests/test-cases/init-order/Makefile | 4 +- .../test-cases/init-order/expected-order.txt | 2 + .../unit-tests/test-cases/kext-basic/Makefile | 1 + .../Makefile | 9 +- .../test-cases/label-on-end-of-section/foo.s | 17 + .../test-cases/no-data-bundle/foo.c | 6 - .../Makefile | 14 +- .../main.c | 19 +- .../objc-literal-pointers-strip/Makefile | 57 - .../test-cases/operator-new/Makefile | 5 +- .../test-cases/operator-new/main.cxx | 2 +- .../re-export-optimizations-indirect/Makefile | 64 - .../re-export-optimizations-indirect/bar.c | 5 - .../re-export-optimizations-indirect/foo.c | 4 - .../re-export-optimizations-indirect/main.c | 8 - .../re-export-optimizations-indirect/middle.c | 3 - .../re-export-optimizations-indirect/other.c | 1 - .../test-cases/relocs-neg-from-local/Makefile | 51 - .../test-cases/relocs-neg-from-local/test.s | 43 - .../Makefile | 8 +- .../test.m => section-labels/main.c} | 34 +- .../test-cases/section-names-long/Makefile | 42 - .../test-cases/section-names-long/a.s | 9 - .../test-cases/section-names-long/b.s | 9 - .../test-cases/section-names-long/c.s | 11 - .../test-cases/section-names-long/main.c | 4 - .../test-cases/shared-cache-dylib/Makefile | 46 - .../test-cases/shared-cache-dylib/foo.c | 3 - .../stripped-indirect-symbol-table/Makefile | 14 +- .../test-cases/weak_import/Makefile | 10 +- 54 files changed, 546 insertions(+), 5899 deletions(-) delete mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile delete mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c delete mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c delete mode 100644 ld64/unit-tests/test-cases/cstring-alt-segment/custom.s delete mode 100644 ld64/unit-tests/test-cases/cstring-alt-segment/main.c delete mode 100644 ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile delete mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c delete mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c delete mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c rename ld64/unit-tests/test-cases/{no-data-bundle => label-on-end-of-section}/Makefile (82%) create mode 100644 ld64/unit-tests/test-cases/label-on-end-of-section/foo.s delete mode 100644 ld64/unit-tests/test-cases/no-data-bundle/foo.c rename ld64/unit-tests/test-cases/{dead_strip-weak-coalesce => no_zero_fill_sections}/Makefile (70%) rename ld64/unit-tests/test-cases/{dead_strip-r_symbol_desc => no_zero_fill_sections}/main.c (90%) delete mode 100644 ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile delete mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile delete mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c delete mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c delete mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c delete mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c delete mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c delete mode 100644 ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile delete mode 100644 ld64/unit-tests/test-cases/relocs-neg-from-local/test.s rename ld64/unit-tests/test-cases/{cstring-alt-segment => section-labels}/Makefile (80%) rename ld64/unit-tests/test-cases/{objc-literal-pointers-strip/test.m => section-labels/main.c} (71%) delete mode 100644 ld64/unit-tests/test-cases/section-names-long/Makefile delete mode 100644 ld64/unit-tests/test-cases/section-names-long/a.s delete mode 100644 ld64/unit-tests/test-cases/section-names-long/b.s delete mode 100644 ld64/unit-tests/test-cases/section-names-long/c.s delete mode 100644 ld64/unit-tests/test-cases/section-names-long/main.c delete mode 100644 ld64/unit-tests/test-cases/shared-cache-dylib/Makefile delete mode 100644 ld64/unit-tests/test-cases/shared-cache-dylib/foo.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog index 79dcc59..ab1e984 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,174 +1,114 @@ ------ Tagged ld64-95.2.12 +----- Tagged ld64-95.9 -2009-07-02 Nick Kledzik +2009-02-13 Nick Kledzik - creation of __unwind_info section can fail if hundreds of functions cannot be compact encoded + Back out Linker changes for H2 hang + * src/ld/Options.cpp: remove fPreventPageCrossingBranches + * src/ld/MachOWriterExecutable.hpp: remove layout of __text so there are not page crossing branches + * src/ld/MachOReaderRelocatable.hpp: parse but ignore ARM_THUMB_32BIT_BRANCH reloc ------ Tagged ld64-95.2.11 +----- Tagged ld64-95.8.3 -2009-06-19 Nick Kledzik +2009-03-31 Nick Kledzik - Link Time Optimization errors out when targeting < 10.6 - - ------ Tagged ld64-95.2.10 - -2009-04-02 Nick Kledzik - - corrupt metaclass entry in dynamic library - * src/ld/ld.cpp: change Section constructor to copy segment and section names - - ------ Tagged ld64-95.2.9 - -2009-04-02 Nick Kledzik - - Update ld64 for new triples introduced in 6654669 to support ARM LLVM - * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples - - ------ Tagged ld64-95.2.8 - -2009-03-24 Nick Kledzik - - anonymous functions have the compact unwind info computed wrong - * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom - - ------ Tagged ld64-95.2.7 - -2009-03-11 Nick Kledzik - - AddressBook incorrectly gets _objc_msgSend from WebKit - * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib - that is already explictly or implicitly linked. - * unit-tests/test-cases/re-export-optimizations-indirect: add test case + ld might set MH_WEAK_DEFINES when it should not + * src/ld/MachOWriterExecutable.hpp: rescan fRegularDefAtomsThatOverrideADylibsWeakDef and only consider global ones -2009-03-10 Nick Kledzik +----- Tagged ld64-95.8.2 - dyld weak linking optimization leaves some symbols unbound - * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference - to a symbol in a dylib that is a weak definition - * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case +2009-03-18 Nick Kledzik + * src/ld/MachOReaderRelocatable.hpp: back out -force_cpusubtype_ALL changes -2009-03-10 Nick Kledzik - many OS i386 OS dylibs still have __IMPORT segment - * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr - * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem +----- Tagged ld64-95.8.1 +2009-03-17 Nick Kledzik ------ Tagged ld64-95.2.6 - -2009-02-27 Nick Kledzik - - ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef - that will be exported when computing MH_WEAK_DEFINES - * unit-tests/test-cases/operator-new: updated to reproduce issue + -dead_strip inhibits weak coalescing in no_dead_strip section + * src/ld/ld.cpp: in markDead() remove from fLiveRootAtoms - ------ Tagged ld64-95.2.5 - -2009-02-24 Nick Kledzik - - x86_64 obj-c runtime confused when static lib is stripped - * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings - * unit-tests/test-cases/objc-literal-pointers-strip: added test case +2009-03-17 Nick Kledzik ------ Tagged ld64-95.2.4 - -2009-02-23 Nick Kledzik + libgcc fails to build in with ld64-95.8 + * src/ld/MachOReaderRelocatable.hpp: interpret CPU_SUBTYPE_ARM_ALL as CPU_SUBTYPE_ARM_V4T + * src/ld/Options.cpp: interept -force_cpusubtype_ALL as -arch armv4t - * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs +----- Tagged ld64-95.8 -2009-02-18 Nick Kledzik +2009-02-09 Nick Kledzik - Writer::symbolIndex() uses a linear search and does not scale - * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better - + ld64-95.7 crashes building Foundation-678.39 in side build + * src/ld/MachOReaderRelocatable.hpp: handle a zero length section with a label before __cstring section -2009-02-18 Nick Kledzik - Use new compact encodings that handle all register permutations - * src/ld/Architectures.hpp: add kSectionOffset24 - * src/ld/ObjectFile.h: add getFDE() - * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding - * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed - * src/other/unwinddump.cpp: update unwinddump output to display register save set - +----- Tagged ld64-95.7 -2009-02-16 Nick Kledzik +2009-02-06 Nick Kledzik - runtime error with bundle for 10.5 that has weak external symols - * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions + * src/ld/ObjectFile.h: make fAddCompactUnwindEncoding false by default -2009-02-15 Nick Kledzik +2009-02-06 Nick Kledzik - i386 relocation error with negative offsets from local labels - * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label - * unit-tests/test-cases/relocs-neg-from-local: add test case + ER: add linker option to zero fill empty DATA sections on disk + * src/ld/Options.cpp: add support for -no_zero_fill_sections + * src/ld/MachOReaderRelocatable.hpp: isZeroFill() is only true if fOptimizeZeroFill + * doc/man/man1/ld.1: document -no_zero_fill_sections + * unit-tests/test-cases/no_zero_fill_sections: add test case -2009-02-12 Nick Kledzik +2009-02-05 Nick Kledzik - -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms - * unit-tests/test-cases/dead_strip-weak-coalesce: added test case + label getting resolved to the wrong address. + * src/ld/MachOWriterExecutable.hpp: add findAtomAndOffsetForSection() and use it to disambiguate + * unit-tests/test-cases/label-on-end-of-section: added test case -2009-02-12 Nick Kledzik +2009-01-27 Nick Kledzik - x86_64 weak_import broken for initialized data - * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader::addRelocReference() - * src/other/dyldinfo.cpp: update to display weak_import attribute - * unit-tests/test-cases/weak_import: updated test case + Warn -force_cpusubtype_ALL is not supported + * src/ld/Options.cpp: warn if fForceSubtypeAll and fArchitecture is CPU_TYPE_ARM -2009-02-06 Nick Kledzik - - ld parsing of __eh_frame unwind information is slow - * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 +----- Tagged ld64-95.6 +2009-01-25 Nick Kledzik ------ Tagged ld64-95.2.3 + Add support for section start/end labels + * src/ld/ObjectFile.h: add kSectionStart and kSectionEnd + * src/ld/MachOReaderRelocatable.hpp: create SectionBoundaryAtoms as needed + * src/ld/ld.cpp: sort SectionBoundaryAtoms correctly + * src/ld/MachOWriterExecutable.hpp: allow all relocations in preload images -2009-02-04 Nick Kledzik - ld: warning: can't add line info to anonymous symbol - * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs +----- Tagged ld64-95.5 +2009-01-15 Nick Kledzik ------ Tagged ld64-95.2.2 + * src/ld/MachOWriterExecutable.hpp: in hasPageCrossingBranches() ignore branches preceeded by a branch -2009-02-02 Nick Kledzik - ld -r does not preserve the N_NO_DEAD_STRIP bit - * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() - * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case +----- Tagged ld64-95.4 +2009-01-15 Nick Kledzik ------ Tagged ld64-95.2.1 + * src/ld/Options.cpp: handle -kext and -r the same for fPreventPageCrossingBranches -2009-01-29 Nick Kledzik - ld coalesces C strings in different segments - * src/ld/MachOReaderRelocatable.hpp: only do standard coalescing on __cstring section if is in __TEXT segment - * unit-tests/test-cases/cstring-alt-segment: add test case - +----- Tagged ld64-95.3 -2009-01-29 Nick Kledzik +2009-01-14 Nick Kledzik - gcc DejaGnu failure: building longcall/dylib library - * src/ld/MachOWriterExecutable.hpp: if no __DATA sections insert non-lazy pointers at end of __TEXT segment - * unit-tests/test-cases/no-data-bundle: added test case + linker should alter layout to prevent armv7 page crossing branches + * src/ld/Options.cpp: set fPreventPageCrossingBranches + * src/ld/MachOWriterExecutable.hpp: adjust layout of __text so there are not page crossing branches + * src/ld/MachOReaderRelocatable.hpp: support new ARM_THUMB_32BIT_BRANCH reloce ----- Tagged ld64-95.2 @@ -176,4359 +116,3 @@ 2009-01-06 Nick Kledzik strip -S fails with "new trie is larger than original" - * src/other/PruneTrie.cpp: don't align trie more than original trie was aligned - - ------ Tagged ld64-95.1 - -2008-12-21 Nick Kledzik - - * src/ld/MachOWriterExecutable.hpp: in new linkedit format, make sure only exported symbols - make it into weak binding info - - ------ Tagged ld64-95 - -2008-12-18 Nick Kledzik - - * src/ld/Options.cpp: move check for fSharedRegionEligible until fPrebind has stabilized - - -2008-12-18 Nick Kledzik - - Generate new compressed LINKEDIT when targeting 10.6 - * src/ld/Options.cpp: turn on compressed LINKEDIT by default - - ------ Tagged ld64-94.1 - -2008-12-16 Nick Kledzik - - * src/ld/Options.cpp: Fix -F handling in buildSearchPaths() - - ------ Tagged ld64-94 - -2008-12-15 Nick Kledzik - - * doc/man/man1/ld.1: document new options - - -2008-12-15 Nick Kledzik - - linker should enforce all .o files have same sub-type, and ignore sub-type of dylibs - * doc/man/man1/ld.1: update man page about -allow_sub_type_mismatches - * src/ld/ld.cpp: call validFile() with new arguments - * src/ld/MachOReaderRelocatable.hpp: add new arguments to validFile() - * src/ld/Options.cpp: Support LD_ALLOW_CPU_SUBTYPE_MISMATCHES and -allow_sub_type_mismatches - - -2008-12-15 Nick Kledzik - - -syslibroot should skip standard search paths not in the SDK - * src/ld/Options.cpp: in buildSearchPaths() if an SDK is specified don't add - standard search paths not in the SDK. - - -2008-12-15 Nick Kledzik - - ld: remove "can't make compact unwind encoding" warning - * src/ld/ObjectFile.h: add fWarnCompactUnwind - * src/ld/Options.cpp: -warn_compact_unwind --> fWarnCompactUnwind - * src/ld/MachOReaderRelocatable.hpp: test fWarnCompactUnwind before warning - - -2008-12-15 Nick Kledzik - - Add dtrace usdt support for arm to ld64 - * src/ld/MachOWriterExecutable.hpp: handle arm::kDtraceIsEnabledSite - * unit-tests/test-cases/dtrace-static-probes: use is-enabled in test case - - ------ Tagged ld64-93 - -2008-12-11 Nick Kledzik - - * src/ld/ObjectFile.h: add fIPhoneVersionMin to track min iPhoneOS version - * src/ld/Options.cpp: use fIPhoneVersionMin - - -2008-12-11 Nick Kledzik - - non-lazy pointer to non-global tentative definition encoded wrong - * src/ld/MachOWriterExecutable.hpp: don't use INDIRECT_SYMBOL_LOCAL for tentative definitions - * unit-tests/test-cases/non-lazy-r: updated test case - - -2008-12-11 Nick Kledzik - - kernel fails to boot when ld64 used for intermediate ld -r step - * src/ld/MachOWriterExecutable.hpp: in -r mode when generating a scattered sect-diff reloc for - i386/arm, special case when from target is not the atom - the relocation is in. - * unit-tests/test-cases/relocs-asm: update test case - - -2008-12-11 Nick Kledzik - - * src/ld/ld.cpp: handle new __program_vars section - * src/ld/MachOWriterExecutable.hpp: handle inserting synthesized sections when there is no __dyld section - - -2008-12-11 Nick Kledzik - - * src/ld/MachOReaderRelocatable.hpp: Fix getDescription() to work when direct reference is to anonymous atom - - -2008-12-10 Nick Kledzik - - * src/ld/Options.cpp: enable LD_FORCE_NO_PREBIND to be used with arm - - -2008-12-10 Nick Kledzik - - Developer tool to print the new compressed LINKEDIT information - * src/other/dyldinfo.cpp: fix typo in usage() - - -2008-12-05 Nick Kledzik - - SnowLeopard kernel should compile warning free - * src/ld/MachOReaderRelocatable.hpp: correct parse two global labels at end of section and make one an alias - * unit-tests/test-cases/end-label: update test case - - -2008-12-04 Nick Kledzik - - Better warning than "PPC_RELOC_JBSR should not be using an external relocation" - * src/ld/MachOReaderRelocatable.hpp: issue warning with .o path if it was compiled with -mlong-branch - - -2008-12-04 Nick Kledzik - - linker should not map __pointers -> __nl_symbol_ptr unless actually making new LINKEDIT - * src/ld/ObjectFile.h: add fMakeCompressedDyldInfo for readers to see - * src/ld/Options.cpp: set fMakeCompressedDyldInfo for readers to see - * src/ld/MachOReaderRelocatable.hpp: check fMakeCompressedDyldInfo - - -2008-12-02 Nick Kledzik - - * src/ld/debugline.c: fix error handling in line_open() - - -2008-11-26 Nick Kledzik - - vtable with thumb entries broke after ld -r - * src/ld/MachOReaderRelocatable.hpp: if target of reloc is thumb, mask thumb bit off addend - * unit-tests/test-cases/thumb-pointer: added test case - - -2008-11-26 Nick Kledzik - - * src/ld/Option.cpp: Fix how crashreporterBuffer is created to not miss some arguments - - -2008-11-24 Nick Kledzik - - Security.framework has some duplicate FDEs for some functions - * src/ld/ld.cpp: remove fDeadAtoms from fLiveAtoms when there are weak atoms overriden by late loads - * unit-tests/test-cases/dead_strip-archive-eh: added test case - - ------ Tagged ld64-92 - -2008-11-21 Nick Kledzik - - * src/ld/MachOReaderDylib.hpp: if export_size is zero, no need to parse trie - * src/abstraction/MachOTrie.hpp: gracefully handle empty trie - - -2008-11-21 Nick Kledzik - - strip(1) support for new compressed LINKEDIT information - * ld64.xcodeproj/project.pbxproj: build and install new libprunetrie.a - * src/other/prune_trie.h: added - * src/other/PruneTrie.cpp: implements prune_trie() - - -2008-11-21 Nick Kledzik - - * src/ld/ld.cpp: if an export file is used and all weak symbols are masked, don't set WEAK_DEFINES - * unit-tests/test-cases/weak-def-flag: added test case - - -2008-11-20 Nick Kledzik - - Generate new compressed LINKEDIT when targeting 10.6 - * src/ld/MachOWriterExecutable.hpp: support generating new compressed format - * src/ld/MachOReaderRelocatable.hpp: new compress format implies non-lazy pointers in __DATA for i386 - * src/ld/MachOReaderDylib.hpp: support linking aginst new format - * src/ld/Options.cpp: suppport -exported_symbols_order and -no_compact_linkedit - * src/ld/ld.cpp: track which atoms have weak counter parts in dylibs - * src/other/dyldinfo.cpp: added tool to display new LINKEDIT format - * ld64.xcodeproj/project.pbxproj: add dyldinfo tool - * unit-tests/*: lots of fixes to work with new format - - -2008-11-20 Nick Kledzik - - ld64 should preserve N_WEAK_REF when linking MH_KEXT_BUNDLEs - * src/ld/MachOWriterExecutable.hpp: set up fWeakImportMap in synthesizeKextGOT() - - -2008-11-19 Nick Kledzik - - VideoToolbox.framework has bad __TEXT.__eh_frame info - * src/ld/Options.cpp: add -no_eh_labels option for use with -r - * src/ld/MachOWriterExecutable.hpp: generate correct x86_64 labeless relocs in -r mode - * src/ld/MachOReaderRelocatable.hpp: now ignore all labels and relocations in - __TEXT/__eh_frame section and rely on getCFIs() from libunwind - * unit-tests/test-cases/eh-coalescing-no-labels: add test case - - -2008-11-19 Nick Kledzik - - LTO doesn't like dtrace symbols - * src/ld/LTOReader.hpp: ignore __dtrace_probe undefines in bitcode files - - -2008-11-14 Nick Kledzik - - * src/abstraction/MachOFileAbstraction.hpp: fix to work with 10.5 headers - - ------ Tagged ld64-91 - -2008-11-07 Nick Kledzik - - Remove COMPACT_UNWIND_SUPPORT conditionalizing - - -2008-11-06 Nick Kledzik - - Reorganize source layout. ld sources are now in "ld", - and other tools are in "other". - - -2008-11-05 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: start installing unwinddump tool - * src/UnwindDump.cpp: support -arch option - * doc/man/man1/unwinddump.1: create man page - - -2008-11-05 Nick Kledzik - - linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries - * src/ld.cpp: in synthesizeDebugNotes() set other field of OSO to be subtype - - -2008-11-05 Nick Kledzik - - Need a linker option to load all objects from one library - * src/Options.cpp: support -force_load option - * src/ArchiveReader.hpp: Add fForceLoad ivar - * doc/man/man1/ld.1: update man page with -force_load option - * unit-tests/test-cases/archive-force-load: add test case - - -2008-11-05 Nick Kledzik - - Dtrace Probe Warnings: SnowLeopard kernel should compile warning free - * src/ld.cpp: don't generate GSYM stabs for old style __dtrace_probe - * src/MachOReaderRelocatable.hpp: fix test for deciding if a symbol is an alias - - -2008-11-04 Nick Kledzik - - ADOBE: XCODE: ld: duplicate typeinfo in executable - * src/ld.cpp: in dead-strip mode, record overriden symbols and later rebind all uses - * unit-tests/test-cases/dead_strip-archive-weak: add test case - - -2008-11-03 Nick Kledzik - - support increased branch range in Thumb-2 - * src/MachOReaderRelocatable.hpp: handle full branch range in addRelocReference() - * unit-tests/test-cases/branch-distance: added test case - -2008-10-31 Devang Patel - - Sqlite 3.5.4 built with lvm-gcc-4.2 -O4 fails regression test - * src/LTOReader.hpp: Use real atom scope when real atom is available. - Preserve globals while optimizing an executable. - -2008-10-30 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support all encodings in getEncodedP() - - ------ Tagged ld64-90 - -2008-10-30 Nick Kledzik - - icc has dwarf unwind info that is different than gcc - * src/MachOReaderRelocatable.hpp: support more encodings in getEncodedP() - - -2008-10-23 Nick Kledzik - - build ld64 for x86_64 - * ld64.xcodeproj/project.pbxproj: add X86_64 to valid archs - - -2008-10-23 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: use generated @$(DERIVED_FILE_DIR)/linker_opts for extra - linker options. This allows linker to be built if LTO headers and libs are missing. - - -2008-10-23 Nick Kledzik - - Linker warning not shown in the Xcode build log - * src/Options.cpp: add colon to format string in warning() - - ------ Tagged ld64-89.3 - -2008-10-24 Nick Kledzik - - ld64-89 broke TOT OpenGL libProgrammability x86_64 build - * src/MachOReaderRelocatable.hpp: add cast in getEncodedP() - - ------ Tagged ld64-89.2 - -2008-10-23 Nick Kledzik - - SnowLeopard: Libsystem built with ld64-89.1 causes crashes - * src/MachOReaderRelocatable.hpp: when FDE information causes __text atom to be split, make the - atoms follow-on pairs. - - ------ Tagged ld64-89.1 - -2008-10-22 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: for x86_64 __eh_frame force direct references - - -2008-10-21 Nick Kledzik - - * src/ObjectDump.cpp: Use getContentType() to see if content type is a cstring - - ------ Tagged ld64-89 - -2008-10-21 Nick Kledzik - - 10A180 with QT-1119 roots: iTunes and QuickTime cannot play back purchased videos - linker should not need .eh labels - * src/MachOWriterExecutable.hpp: use kCFIType to set section attributes - * src/MachOReaderRelocatable.hpp: use libunwind's CFITuple to parse __eh_frame content - * src/ld.cpp: Add adjustScope() phase instead of demoting scope within symboltable.add() - * unit-tests/test-cases/eh-stripped-symbols: added test case - - ------ Tagged ld64-88.1 - -2008-10-16 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT - * src/MachOWriterExecutable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT - - -2008-09-30 Nick Kledzik - - OBJC2: Reorder __DATA,__objc_* sections by writedness - * src/ld.cpp: change sorting order of Sections - - -2008-09-29 Nick Kledzik - - Executable produced by XCode 3.2 on 10.6 crashes on 10.3.9 - * src/MachOWriterExecutable.hpp: set objc_module_info_addr field of module table - - ------ Tagged ld64-88 - -2008-09-25 Nick Kledzik - - kexts need to be built as MH_BUNDLE mach-o files - * src/ld.cpp: use getUndefinedProxyAtom() with kKextBundle - * src/MachOFileAbstraction.hpp: add MH_KEXT_BUNDLE - * src/Options.cpp: support -kext for all architectures - * src/MachOWriterExecutable.hpp: support kKextBundle to make a bundle like kext - * unit-tests/test-cases/kext-basic: added test case - - -2008-09-25 Nick Kledzik - - ld invoking wrong ld_classic - * src/Options.cpp: first look for ld_classic relative to ld itself - - -2008-09-25 Nick Kledzik - - ld fails to link references from 32 bit code into 64 bit code - Desired 32-bit absolute relocation - * src/Architectures.hpp: add x86_64::kPointer32 - * src/MachOReaderRelocatable.hpp: support X86_64_RELOC_UNSIGNED with length=2 - * src/MachOWriterExecutable.hpp: support x86_64::kPointer32 - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added 32-bit pointer tests - - -2008-09-25 Nick Kledzik - - Should be able to mark dylibs as auto-dead-dylib-strip - * src/Options.h: add fMarkDeadStrippableDylib - * src/MachOReaderDylib.hpp: check MH_DEAD_STRIPPABLE_DYLIB - * src/ObjectFile.h: add deadStrippable() - * src/MachOFileAbstraction.hpp: add MH_DEAD_STRIPPABLE_DYLIB - * src/Options.cpp: support -mark_dead_strippable_dylib - * src/MachOWriterExecutable.hpp: test reader->deadStrippable(), set MH_DEAD_STRIPPABLE_DYLIB - * doc/man/man1/ld.1: update man page - * unit-tests/test-cases/dead_strippable_dylib: added test case - - -2008-09-25 Nick Kledzik - - ER: Add -seg_page_size option - * src/Options.cpp: add -seg_page_size option - * src/MachOWriterExecutable.hpp: use new page size info when laying out segments - * doc/man/man1/ld.1: update man page - - -2008-09-24 Nick Kledzik - - -arch_errors_fatal not working - * src/ld.cpp: check fOptions.errorOnOtherArchFiles() - * src/Options.cpp: turn -arch_errors_fatal into fOptions.errorOnOtherArchFiles() - - -2008-09-24 Nick Kledzik - - CrashTracer: [USER] 1 crash in ld at ld: 0x5ce02 - * src/ld.cpp: abort if resolve() finds an unresolved reference, rather than allow a future crash - - -2008-09-24 Nick Kledzik - - linker crashes linking X86-64 with -fwritable-strings - * src/MachOReaderRelocatable.hpp: handle unbound cfstring references - * unit-tests/test-cases/cfstring-coalesce: update test case - - -2008-09-24 Nick Kledzik - - ld64: bl out of range (-17147704 max is +/-16M) on ppc - * src/MachOWriterExecutable.hpp: tweak branch island regions to be every 14MB instead of 15MB - - -2008-09-24 Nick Kledzik - - -filelist fails with comma in path - * src/Options.cpp: in loadFileList() first try without special comma meaning - * unit-tests/test-cases/filelist/Makefile: update test case - - -2008-09-23 Nick Kledzik - - nop not used when aligning functions in -r mode - * src/MachOWriterExecutable.hpp: change check for when to pad with nops to not test segment's name - - -2008-09-23 Nick Kledzik - - "-pie can only be used when linking a main executable" should be a warning, not an error - * src/Options.cpp: make -pie on a dylib or bundle be a warning instead of an error - - -2008-09-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: add warning if dwarf cannot be encoded as compact unwind - - -2008-09-18 Nick Kledzik - - * src/LTOReader.hpp: re-enable use of lto_codegen_debug_options() - - -2008-09-16 Nick Kledzik - - ld does not always set S_CSTRING_LITERALS on __TEXT,__cstring - * src/MachOReaderRelocatable.hpp: add getContentType() to SymbolAtom - * src/MachOWriterExecutable.hpp: for x86_64 don't override named cstrings with LC* name - - -2008-09-10 Nick Kledzik - - * Options.cpp: add __crashreporter_info__ to communicate command line to crash reporter - * ld64.xcodeproj/project.pbxproj: leave local symbols in ld to provide better crash reports - - -2008-09-08 Nick Kledzik - - 161569 GCC 4.2 - breakpoints no longer work for a large number of functions - * src/MachOReaderRelocatable.hpp: support DW_FORM_strp out-of-line strings when parsing line table - - -2008-09-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix compact unwind personality for dyld and -slow_stubs - - -2008-08-29 Nick Kledzik - - -weak_library no longer forces uses to be weak_import - * src/MachOWriterExecutable.hpp: use fWeakImport on dylib to force proxy atoms into fWeakImportMap - * unit-tests/test-cases/weak_import-force: added test case - - -2008-08-29 Nick Kledzik - - linker should order __DATA segment to reduce dyld dirtied pages - * src/Options.cpp: add fOrderData and support -no_data_order - * src/ld.cpp: modify tweakLayout() to sort atoms with relocations to start of __data section - - -2008-08-27 Nick Kledzik - - * src/Options.cpp: back out - - ------ Tagged ld64-87.5 - -2008-08-26 Nick Kledzik - - some projects show _Unwind_Resume coming from libSystem.B.dylib - * src/Options.cpp: swap any early symlinks to libSystem with libgcc_s - - ------ Tagged ld64-87.4 - -2008-08-25 Nick Kledzik - - some projects show _Unwind_Resume coming from libSystem.B.dylib - * src/Options.cpp: swap any early libSystem with libgcc_s - - -2008-08-15 Nick Kledzik - - Unable to build ppc debug builds (linker out of range error) - * src/MachOWriterExecutable.hpp: in addPPCBranchIslands() look ahead so large atoms don't push out branch islands - - ------ Tagged ld64-87.3.1 - -2008-09-08 Nick Kledzik - - i386 dylibs have incorrect personality pointers when put in dyld shared cache - * src/MachOWriterExecutable.hpp: in addCrossSegmentRef() handle kImageOffset32 to __IMPORT segment - - ------ Tagged ld64-87.3 - -2008-08-09 Nick Kledzik - - work around compiler gcc_except_table alignment - * src/ObjectFile.h: change getLSDA() to return a reference instead of an atom - * src/MachOReaderRelocatable.hpp: special case __eh_frame 64-bit pointer diff relocations - * src/MachOWriterExecutable.hpp: track lsda offset when creating __unwind_info section - * src/UnwindDump.cpp: log when LDSA content does not start with 0xFF - ------ Tagged ld64-87.2 - -2008-08-07 Nick Kledzik - - 10A141: libuwind falls back to dwarf and makes whole system super slow - * src/MachOWriterExecutable.hpp: Fix sign extension bug with x86_64::kPointerDiff24 - * src/UnwindDump.cpp: warn about mangled LSDA entries when dumping unwind section - - ------ Tagged ld64-87.1 - -2008-08-03 Nick Kledzik - - * src/LTOReader.hpp: Don't use lto_codegen_debug_options until newer libLTO.dylib is available - - ------ Tagged ld64-87 - -2008-07-21 Nick Kledzik - - * src/Options.cpp: Always set fAutoOrderInitializers=false for dyld - - -2008-07-21 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix when regular vs compressed __unwind_info pages are generated - * src/UnwindDump.cpp: fix function name decoding in regular pages - - -2008-07-21 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: don't allow ld to build for x86_64 until libdtrace.dylib is available - - -2008-07-18 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't crash if debug_line section has no line table - - -2008-07-18 Nick Kledzik - - Duplicate probe firings in Security.framework - * src/LTOReader.hpp: optimize() now returns atoms optimized away - * src/ObjectFile.h: optimize() should return if it did anything - * src/ArchiveReader.hpp: pass through optimize() result - * src/ld.cpp: rework dtrace probe processing as a new pass to prevent double counting - - -2008-07-15 Nick Kledzik - - automatically order initializers to start of __TEXT - * src/Options.cpp: add -no_order_inits option - * src/MachOReaderRelocatable.hpp: merge __StaticInit into __text - * src/ObjectFile.h: add fAutoOrderInitializers - * src/ld.cpp: sort initializer to start of __text and terminators to end - * doc/man/man1/ld.1: add doc about -no_order_inits - * unit-tests/test-cases/init-order: add test case - -2008-07-15 Nick Kledzik - - Only add LC_SEGMENT_SPLIT_INFO to dylibs that might be in the shared cache - * src/MachOWriterExecutable.hpp: re-layout load commands after split-seg data computed - * src/Options.cpp: non-public install name will disable split-seg load command - - -2008-07-14 Nick Kledzik - - ld -r for x86_64 is changing visibility of cstring constants - * src/MachOWriterExecutable.hpp: force x86_64 cstring labels to be local in -r mode - * unit-tests/test-cases/cstring-label: added test case - - -2008-07-11 Nick Kledzik - - ld not adding updating LC_SEGMENT_SPLIT_INFO with __unwind_info section - * src/MachOWriterExecutable.hpp: run createSplitSegContent() after __unwind_info section is created - -2008-07-10 Nick Kledzik - - * src/LTOReader.hpp: improve missing symbol error message - - -2008-07-09 Nick Kledzik - - linker should order __DATA segment to reduce dyld dirtied pages - * src/ld.cpp: first phase, order sections - - -2008-07-08 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: remove "coal" sections when creating a final linked image - - -2008-07-08 Nick Kledzik - - ld: add support for mllvm LTO options - * src/Options.cpp: support -mllvm option - * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options - * src/ld.cpp: pass llvmOptions to optimize() - * src/Options.h: add fLLVMOptions - * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() - * src/ObjectFile.h: add llvmOptions parameter to optimize() - * unit-tests/test-cases/lto-llvm-options: add test case - - -2008-07-07 Nick Kledzik - - Linker fails with: 24-bit pointer diff out of range in unwind info in unwind info from... - * src/MachOWriterExecutable.hpp: fix when to fallback to uncompressed unwind info - - -2008-07-03 Nick Kledzik - - ld crash with gcc-4.0 code that uses a zero sized array - * src/MachOReaderRelocatable.hpp: handle zero size atom in a zero sized section - - -2008-07-03 Nick Kledzik - - ld crashes when bad ppc relocs are found - * src/MachOReaderRelocatable.hpp: change all missing PAIR warnings to errors - - -2008-07-02 Nick Kledzik - - when linking a kext the static linker should leave a pad in the headers to allow code signing - * src/MachOWriterExecutable.hpp: add padding for load commands in object files - * unit-tests/test-cases/code-signed-object-file: added test case - - -2008-07-02 Nick Kledzik - - LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype - * src/MachOWriterExecutable.hpp: correctly set segment size info in object files - * unit-tests/test-cases/no-object-symbols: add test case - - -2008-06-26 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: enable ld and rebase targets to build for x86_64 - * src/rebase.cpp: remove unused fRelocBase field that was not 64-bit clean - * src/MachOReaderRelocatable.hpp: fix getEncodedP() to be 64-bit clean - - ------ Tagged ld64-86.3 - -2008-06-17 Nick Kledzik - - * src/ld.cpp: fix loadUndefines() to double check undefine symbol was not already loaded - - ------ Tagged ld64-86.2 - -2008-06-14 Nick Kledzik - - * srd/ld.cpp: Add NULL check in getTentativesNames() - - ------ Tagged ld64-86.1 - -2008-06-06 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix header padding calculation for dyld - - ------ Tagged ld64-86 - -2008-06-04 Nick Kledzik - - * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message - - -2008-06-04 Nick Kledzik - - * src/ObjectFile.h: add deadAtoms parameter to optimize() - * src/ld.cpp: ditto - * src/ArchiveReader.hpp: ditto - * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs - * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away - * unit-tests/test-cases/lto-weak-native-override: add test case - - -2008-06-04 Nick Kledzik - - LTO : 176.gcc and 177.mesa build failure at -O4 - * src/LTOReader.hpp: make sure internal is returned by getAtoms() - * unit-tests/test-cases/lto-archive-dylib: update test case - - -2008-06-03 Nick Kledzik - - fix for 5613343 need to search for definitions for common symbols is broken - * src/ld.cpp: modify loadUndefines() to check for undefines in all files and tentative definitions but only in archives - * src/machochecker.cpp: check for undefine symbols and external symbols with same name - * unit-tests/test-cases/tentative-and-archive: update test case - - -2008-06-03 Nick Kledzik - - linker produces wrong result for 16-bit call relocations - * src/MachOReaderRelocatable.hpp: properly parse i386 scattered relocs for word sized pc-rel vanilla - * src/MachOWriterExecutable.hpp: propery compute displacement for x86::kPCRel16 - * unit-tests/test-cases/relocs-asm: update test case with callw instructions - - -2008-06-03 Nick Kledzik - - Building kext x86_64 with unexported symbols file causes linking problems - * src/MachOWriterExecutable.hpp: better check when creating undefined proxy atoms - * unit-tests/test-cases/unexported_symbols_list-r: added test case - - -2008-06-02 Nick Kledzik - - S_CSTRING_LITERALS section type not preserved in executable - * src/ObjectFile.h: added ContentType - * src/MachOReaderRelocatable.hpp: set ContentType for anonymous string literals - * src/MachOWriterExecutable.hpp: set S_CSTRING_LITERALS if ContentType is kCStringType - * unit-tests/test-cases/cstring-custom-section: added test case - - -2008-06-02 Nick Kledzik - - linker should produce __unwind_info section in final linked images - * src/ld.cpp: sort __unwind_info then __eh_frame section to end of __TEXT - * src/Architectures.hpp: add kImageOffset32 and kPointerDiff24 - * src/ObjectFile.h: add compact unwind info support - * src/MachOReaderRelocatable.hpp: add compact unwind info support - * src/MachOFileAbstraction.hpp: add C++ wrappers for unwind section layout - * src/UnwindDump.cpp: new tool for dumping __unwind_info section - * src/MachOWriterExecutable.hpp: create __unwind_info section when needed - * src/ObjectDump.cpp: print unwind info - - -2008-06-02 Nick Kledzik - - * unit-tests/test-cases/llvm-integration: split out some test cases - * unit-tests/test-cases/lto-preload-pie: added - * unit-tests/test-cases/lto-archive-dylib: added - - -2008-05-30 Nick Kledzik - - * unit-tests: fixes to build all tests with with gcc-4.2 on SnowLeopard - - -2008-05-30 Nick Kledzik - - support -preload option to generate MH_PRELOAD binaries compatible with mtoc(1) and EFI - * src/ld.cpp: add entryPoint parameter to optimize() - * src/ArchiveReader.hpp: ditto - * src/ObjectFile.h: ditto - * src/LTOReader.hpp: use entryPoint parameter to optimize() - * src/Options.h: add kPreload and segment alignment - * src/Options.cpp: support -preload and -segalign - * src/MachOWriterExecutable.hpp: support kPreload and non-page aligned segments - - -2008-05-30 Nick Kledzik - - ld should warn if passed -r and also dylibs - * src/ld.cpp: check for spurious dylibs in Linker::addDylib() - - ------ Tagged ld64-85.6 - -2008-11-01 Nick Kledzik - - support increased branch range in Thumb-2 - * src/MachOWriterExecutable.hpp: in fixUpReferenceFinal() support new longer branch range - - -2008-11-01 Nick Kledzik - - ld warning: unknown option to -iphoneos_version_min, not 1.x or 2.x - * src/Options.cpp: In setIPhoneVersionMin() support 3.x - - ------ Tagged ld64-85.5 - -2008-09-17 Nick Kledzik - - vtable pointers can be missing thumb bit - * src/MachOWriterExecutable.hpp: Writer::fixUpReferenceFinal() OR in the 1 bit if the target - of a arm::kReadOnlyPointer is thumb. - - ------ Tagged ld64-85.4 - -2008-08-11 Nick Kledzik - - ld should ignore LD_PREBIND when processing a static archive - * src/MachOWriterExecutable.hpp: in setImportNlist() never use N_PBUD for object files - ------ Tagged ld64-85.3 - -2008-07-14 Nick Kledzik - - Prebinding busted in DTSB - * src/Options.cpp: check for libstdc++.6.0.[49] in seg_addr_table - - ------ Tagged ld64-85.2 - -2008-05-06 Nick Kledzik - - ARM ld should take W bit off of maxprot for __TEXT segment - * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments - - -2008-05-06 Nick Kledzik - - encryptable images may not be signable - * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section - - ------ Tagged ld64-85 (Xcode 3.1) - -2008-04-29 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include - - -2008-04-29 Nick Kledzik - - ld doesn't honor "rightmost" -syslibroot argument - * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots - - -2008-04-29 Nick Kledzik - - GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files - * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment - * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment - - -2008-04-17 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: better cpu subtype support - - -2008-04-14 Nick Kledzik - - ld64 has bad ARM branch island check - * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail - - -2008-04-10 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs - - ------ Tagged ld64-84.4 - -2008-04-10 Nick Kledzik - - SPEC2000/eon built with -mdynamic-no-pic won't run - * src/Architectures.hpp: added arm::kReadOnlyPointer - * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer - * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer - * src/machochecker.cpp: allow MH_PIE bit - * unit-tests/test-cases/switch-jump-table: added test cases - - ------ Tagged ld64-84.3 - -2008-04-09 Nick Kledzik - - -undefined dynamic_lookup busted - * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates - * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup - - ------ Tagged ld64-84.2 - -2008-04-04 Nick Kledzik - - * src/ld.cpp: don't add .eh symbols to symbol table in -r mode - * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing - - ------ Tagged ld64-84.1 - -2008-03-28 Nick Kledzik - - ld should prefer architecture-specific variant over generic in fat object file - * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture - * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files - * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc - - ------ Tagged ld64-84 - -2008-03-28 Nick Kledzik - - * src/LTOReader.hpp: don't print lto version, if lto is unavailable - - -2008-03-26 Nick Kledzik - - Add LD_WARN_COMMONS to BigBear builds - * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file - - -2008-03-26 Nick Kledzik - - Need encryption tag in mach-o file - linker should adjust arm final linked images so __text is never on the same page as the load commands - * src/MachOFileAbstraction.hpp: add support for encryption_info_command - * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption - * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom - * src/machochecker.cpp: validate LC_ENCRYPTION_INFO - - -2008-03-25 Nick Kledzik - - ld64 does not recognize LLVM bitcode archive files - * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp - * src/ArchiveReader.hpp: sniff each member and instantiate correct reader - * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader - * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp - * unit-tests/test-cases/llvm-integration: added test case - - -2008-03-25 Nick Kledzik - - ld64 should switch to new libLTO.dylib interface - Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc - * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface - * unit-tests/test-cases/llvm-integration: update and comment - * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib - * src/ld.cpp: rework and simplify Linker::optimize() - * src/ObjectDump.cpp: Add -nm option - - -2008-03-25 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem - * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem - - -2008-03-24 Nick Kledzik - - Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 - * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. - - -2008-03-21 Nick Kledzik - - * src/Options.cpp: warn if -seg1addr value is not page aligned - - -2008-03-21 Nick Kledzik - - Move ARM support outside of __OPEN_SOURCE__ - * src/ld.cpp: remove __OPEN_SOURCE__ around arm support - * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support - * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h - - ------ Tagged ld64-83.2 - -2008-03-15 Nick Kledzik - - ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results - * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files - * unit-tests/test-cases/objc-exported_symbols_list: added test case - - ------ Tagged ld64-83.1 - -2008-03-14 Nick Kledzik - - -iphone_version_min ==> -iphoneos_version_min - * src/Options.cpp: support -iphoneos_version_min as well - - ------ Tagged ld64-83 - -2008-03-10 Nick Kledzik - - ld needs to strip iphone_version_min option if invoking ld_classic - * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic - - -2008-03-04 Nick Kledzik - - ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) - * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs - * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework - * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools - * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() - * src/ld.cpp: pass lazy helper atom to writer - * doc/man/man1/ld.1: document new options - * unit-tests/test-cases/lazy-dylib-objc: add test case - * unit-tests/test-cases/lazy-dylib: add test case - - ------ Tagged ld64-82.7 - -2008-03-07 Nick Kledzik - - duplicate symbol literal-pointer@__OBJC@__message_refs@... - * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak - * unit-tests/test-cases/objc-selector-coalescing: added test case - - ------ Tagged ld64-82.6 - -2008-03-04 Nick Kledzik - - ld crashes building XsanFS for Snow Leopard Builds - * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() - * unit-tests/test-cases/tentative-and-archive: added test case - -2008-03-04 Nick Kledzik - - ld64 should not force building with gcc 4.0 - * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 - - -2008-02-29 Nick Kledzik - - Simulator frameworks are being build split-seg and not prebound - * src/Options.cpp: only splitseg if prebound - - -2008-02-29 Nick Kledzik - - Linker should not make GSYM debug note for .objc_category_* symbols - * src/ld.cpp: suppress GSYM debug notes for absolute symbols - * unit-tests/test-cases/objc-category-debug-notes: added test case - - -2008-02-29 Nick Kledzik - - non-ASCII CFString support is broken - * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring - * unit-tests/test-cases/cfstring-utf16: add test case - - -2008-02-25 Nick Kledzik - - ld -r -x - * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels - - ------ Tagged ld64-82.5 - -2008-02-12 Nick Kledzik - - x86_64: -stack_size failure when large __bss is used - * src/ld.cpp: only move section already in __DATA segment to new __huge section - * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section - - ------ Tagged ld64-82.4 - -2008-02-06 Nick Kledzik - - comdat warnings with ld -r of C++ .o files - * unit-tests/test-cases/eh-coalescing-r: added test case - * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static - - -2008-02-06 Devang Patel - - LTO of Bom framework with -dead_strip causes ld(1) crash - * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. - * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. - * unit-tests/test-cases/llvm-integration/a15.c: New. - * unit-tests/test-cases/llvm-integration/b15.c: New. - * unit-tests/test-cases/llvm-integration/c15.c: New. - -2008-02-05 Nick Kledzik - - * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used - ------ Tagged ld64-82.3 - -2008-02-04 Nick Kledzik - - ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves - * src/ObjectFile.h: add 10.6 - * src/Options.cpp: add 10.6 support - * src/MachOReaderDylib.hpp: recognize $os10.6$ - - ------ Tagged ld64-82.2 - -2008-01-30 Devang Patel - - Can't build 64-bit Intel binaries with LTO - ld64 fails to build with llvm-gcc-4.2 - * src/LLVMReader.hpp: Fix character count typo in strncmp call. - Use const char * to initialize temp. string. - * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction - instead of hard coding /Developer. - ------ Tagged ld64-82.1 - -2008-01-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs - - -2008-01-22 Nick Kledzik - - ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files - * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs - * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs - * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files - - ------ Tagged ld64-82 - -2008-01-18 Nick Kledzik - - Bad grammar used in ld warning: cannot exported hidden symbol - * src/ld.cpp: fix typo in warning string - - -2008-01-16 Nick Kledzik - - Bundle Loader does not work anymore when loader is a bundle - ld warns of incorrect architecture when linking a bundle to a bundle - * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages - * unit-tests/test-cases/bundle_loader: update test case - - -2008-01-16 Nick Kledzik - - ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) - * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S - - -2008-01-16 Nick Kledzik - - if ld crashes while writing output file, it should delete the half written file - * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete - output file on failure. - - -2008-01-16 Devang Patel - - * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. - - -2008-01-16 Nick Kledzik - - GC-supported library can't be linked into GC-required executable - * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and - allow gc-compatible code to be linked into anything. - * unit-tests/test-cases/objc-gc-checks: update test case - - -2008-01-15 Nick Kledzik - - no debug notes for custom named data - * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore - * unit-tests/test-cases/dwarf-debug-notes: update test case - ------ Tagged ld64-81.5 - -2008-01-14 Devang Patel - - llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 - * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references - after optimization. - * src/ld.cpp: Resolve additional unbounded references after optimization. - - -2008-01-14 Nick Kledzik - - PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes - * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs - * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs - - -2008-01-11 Nick Kledzik - - PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" - * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions - - -2008-01-11 Nick Kledzik - - * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list - - -2008-01-11 Nick Kledzik - - ld64(1) man page uses ambiguous term "suffix" - * doc/man/man1/ld.1: make meaning of "suffix" more explicit - - -2008-01-11 Nick Kledzik - - Obj-C Symbols in Leopard Can't Be Weak Linked - * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines - to dylibs to support Mac OS X 10.3.x dyld - - -2008-01-11 Nick Kledzik - - Unknown error with linker (dyld: unknown external relocation type) - * src/ld.cpp: fix crash when SO stabs are not balanced - - -2008-01-11 Devang Patel - - LTO does not work if expected output is a dynamic library - * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate - visibility info. - -2000-01-10 Nick Kledzik - - __cls_refs section is losing S_LITERAL_POINTERS section type - * src/MachOWriterExecutable.hpp: special case __cls_refs section - * unit-tests/test-cases/objc-literal-pointers: add test case - - -2008-01-03 Nick Kledzik - - wrong EH information might be used - Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom - has kGroupSubordinate references to the other atoms in the group. If the signature atom - is coalesced away, the linker follows kGroupSubordinate references and throws away the - other members of the group. - * unit-tests/test-cases/eh-coalescing: added test case - * src/ld.cpp: added markDead() and use propagate to subordinates - * src/Architectures.hpp: added kGroupSubordinate - * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom - and if used, from .eh atom to its LSDA atom. - * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp - ------ Tagged ld64-81.4.1 - -2007-12-19 Devang Patel - - * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. - -2007-12-19 Devang Patel - - * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). - -2007-12-19 Devang Patel - - print LLVM LTO version number in verbose mode - * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. - * src/Options.cpp: Use printLLVMVersion() in verbose mode. - -2007-12-19 Devang Patel - - print LLVM LTO version number in verbose mode - * src/Options.h: Add verbose() method to check fVerbose flag. - * src/LLVMReader.hpp: Print LLVM version string in verbose mode. - ------ Tagged ld64-81.4 - -2007-12-18 Devang Patel - - * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. - ------ Tagged ld64-81.3 - -2007-12-17 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths - - -2007-12-17 Devang Patel - - * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to - dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. - - -2007-12-14 Nick Kledzik - - gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) - * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs - * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static - - -2007-12-14 Devang Patel - - Enable Link Time Optimization in Opal - * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. - * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. - * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. - * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. - - -2007-12-13 Nick Kledzik - - SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... - * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly - ------ Tagged ld64-81.2 - - - -2007-12-07 Nick Kledzik - - support 8-bit relocations for i386 - * src/Architectures.hpp: add kPCRel8 - * src/MachOReaderRelocatable.hpp: support 8-bit pc-rel relocations for intel - * src/MachOWriterExecutable.hpp: support 8-bit pc-rel relocations for intel - * unit-tests/test-cases/relocs-asm: add test cases - - ------ Tagged ld64-81.1 - -2007-12-06 Nick Kledzik - - * src/MachOReaderDylib.hpp: rework cycle detection to remove some false positives - - -2007-12-05 Nick Kledzik - - Duplicate probe firings in Security.framework - * src/ld.cpp: check dtrace probe sites are not in fDeadAtoms before using - * unit-tests/test-cases/dtrace-static-probes-coalescing: add test case - - -2007-12-05 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix CFString coalescing to work with -fwritable-strings - * unit-tests/test-cases/cfstring-coalesce: add -fwritable-strings to test case - - ------ Tagged ld64-81 - -2007-11-15 Nick Kledzik - - ld64 should support runtime text relocations - * src/MachOWriterExecutable.hpp: add generatesLocalTextReloc() and generatesExternalTextReloc() - * src/Options.cpp: process -read_only_relocs option - * src/Options.h: add allowTextRelocs() and warnAboutTextRelocs() - * src/MachOReaderRelocatable.hpp: add hasLongBranchStubs() - * src/machochecker.cpp: allow relocs in read only segments, if section flags are set - * unit-tests/test-cases/read-only-relocs: update test case - - -2007-11-08 Devang Patel - - * ld64.xcodeproj/project.pbxproj: add new build phase "build configure.h" for - ld target. - * src/ld.cpp: Include "configure.h" - - ------ Tagged ld64-80.11 - -2008-02-12 Nick Kledzik - - Wrong section name for objc info for ARM when OBJC2 is used - * src/MachOWriterExecutable.hpp: switch segment/section name for ARM objc2 image info - ------ Tagged ld64-80.10 - -2008-02-11 Nick Kledzik - - ld64 does not support -aspen_version_min 2.0 - * src/Options.cpp: allow 2.x for -aspen_version_min - - -2008-02-11 Nick Kledzik - - ld_classic: unknown flag: -aspen_version_min - * src/Options.cpp: change -aspen_version_min x.x to -macosx_version_min 10.5 when invoking ld_classic - - ------ Tagged ld64-80.9 - -2008-01-29 Nick Kledzik - - -iphone_version_min ==> -aspen_version_min - * src/Options.cpp: support -aspen_version_min - - ------ Tagged ld64-80.8 - -2008-01-10 Nick Kledzik - - * src/Options.cpp: support transition to new objc ABI for ARM by allowing old .objc_class_name_* - style names in export files and map them to new _OBJC_CLASS_$_ style names. - - ------ Tagged ld64-80.7 - -2008-01-02 Nick Kledzik - - BigBear5A18 isn't fully prebound - * src/Options.cpp: make fNeedsModuleTable true for arm - ------ Tagged ld64-80.6 - -2007-11-30 Nick Kledzik - - -iphone_version_min - * src/Options.cpp: handle -iphone_version_min option - - ------ Tagged ld64-80.5 - -2007-11-26 Nick Kledzik - - need to special case some dylibs in seg_addr_table - * src/Options.cpp: retry seg_add_table lookup for a couple of unusual dylibs - - ------ Tagged ld64-80.4 - -2007-11-06 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix parsing of external and scattered thumb branch22 relocs - * unit-tests/test-cases/thumb-blx: add test case to keep blx issues from coming back - ------ Tagged ld64-80.3 - -2007-11-03 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: remove recalc of dstAddr which could cause thumb branches to be +2 - * src/MachOWriterExecutable.hpp: remove incorrect test for relocateableExternal - ------ Tagged ld64-80.2 - -2007-11-01 Nick Kledzik - - * src/ld.cpp: hack my own prototype for log2() until math.h is cleaned up - - ------ Tagged ld64-80.1 - -2007-11-01 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: add HEADER_SEARCH_PATHS for cross builds - * src/ld.cpp: temporarily disable LLVM_SUPPORT - * src/MachOWriterExecutable.hpp: Don't use CC_MD5() directly - - -2007-10-26 Nick Kledzik - - Cannot build with libm_static.a statically linked - * src/MachOWriterExecutable.hpp: Fix makesExternalRelocatableReference() for -r -d case - * unit-tests/test-cases/tentative-to-real-hidden: add test case - - ------ Tagged ld64-80 - -2007-10-24 Nick Kledzik - - linker should probably warn about trying to export a hidden symbol - * src/ld.cpp: if using -exported_symbols_list check each hidden atom as it is added to symbol table - * src/Options.h,.cpp: add hasExportMaskList() - * unit-tests/test-cases/exported_symbols_list-hidden: added test case - - -2007-10-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: keep old style dtrace probes externel for kernel builds - - -2007-10-23 Nick Kledzik - - unify error and warning messages - -w should suppress warnings - * src/ld.cpp: use warning() function - * src/Options.h: remove emitWarnings() - * src/MachOReaderDylib.hpp: use warning() function - * src/MachOReaderRelocatable.hpp: use warning() function - * src/Options.cpp: use and implement warning() - * src/MachOWriterExecutable.hpp: use warning() function - * unit-tests/test-cases/visibility-warning: verify -w suppresses warnings - - -2007-10-23 Devang Patel - - * src/ld.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/LLVMReader.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/MachOReaderDylib.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/ObjectFile.h: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/MachOReaderRelocatable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/ObjectDump.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. - - -2007-10-22 Nick Kledzik - - * src/Options.cpp: add support for LD_DEAD_STRIP and LD_WARN_COMMONS - * src/MachOReaderRelocatable.hpp: fix problem with -dead_strip of ObjC literal pointers - - -2007-10-22 Nick Kledzik - - * src/Options.cpp: have -static arm code link with ld_classic (for now) - - -2007-10-22 Nick Kledzik - - Recognize all arm architectures - * src/MachOReaderRelocatable.hpp: add support for all ARM sub-types - * unit-tests/test-cases/cpu-sub-types: add test cases for all combinations of ARM sub-types - - -2007-10-19 Nick Kledzik - - * src/*: merge in arm support - * unit-tests/test-cases/*: fix to work for arm and thumb - ------ Tagged ld64-79 - -2007-10-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: if -r mode, always set custom alignment (SET_COMM_ALIGN) on common symbols - * unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile: fix warning - * unit-tests/test-cases/static-executable/Makefile: fix spurious failure - - -2007-10-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix edge case in branch island generation - - -2007-10-12 Nick Kledzik - - Add option to create old, slow stubs for i386 - * src/ObjectFile.h/.cpp: support -read_only_stubs - * src/MachOWriterExecutable.hpp: enhance StubAtom to support old style __symbol_stub/__la_symbol_ptr stubs - * unit-tests/test-cases/slow-x86-stubs: add test case - - -2007-10-12 Nick Kledzik - - ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - * src/Options.cpp: in findFileUsingPaths() don't search for embedded dylibs - * unit-tests/test-cases/indirect-path-search/Makefile: added case for a dylib embedded in a framework - - -2007-10-11 Nick Kledzik - - add option to disable implicit load commands for indirectly used public dylibs - * src/Options.cpp: add support for -no_implicit_dylibs - * src/ObjectFile.h: add fImplicitlyLinkPublicDylibs - * src/MachOReaderDylib.hpp: test fImplicitlyLinkPublicDylibs before hoisting an implicitly linked dylib - * unit-tests/test-cases/implicit_dylib: add test case - - -2007-10-11 Nick Kledzik - - -interposable_list - * src/Options.h/cpp: Add fInterposeList and fInterposeMode to support -interposable_list - * src/MachOWriterExecutable.hpp: pass symbol name to fOptions.interposable() - * unit-tests/test-cases/interposable_list: add test case - - -2007-10-10 Nick Kledzik - - If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - * src/MachOWriterExecutable.hpp: automatically use LC_LOAD_WEAK_DYLIB if all symbols used from a dylib are weak_import - * unit-tests/test-cases/weak_dylib: added test case - - -2007-10-10 Nick Kledzik - - linker does not error when dylib ordinal exceeds 250 - * src/MachOWriterExecutable.hpp: error out if ordinals exceed max allowed - - -2007-10-10 Nick Kledzik - - overriding 'operator new' or 'operator delete' fails if no weak symbols are present - * src/ld.cpp: at end of checkUndefines() search dylibs for weak versions of any global external symbols - * src/ObjectFile.h: add hasWeakExternals() method to Reader - * src/MachOReaderDylib.hpp: implement hasWeakExternals() method in Reader - * src/ExecutableFile.h: add overridesDylibWeakDefines parameter to write() - * src/MachOWriterExecutable.hpp: use overridesDylibWeakDefines parameter to write() - * unit-tests/test-cases/operator-new: add test case - - -2007-10-05 Nick Kledzik - - No warning about tentative definition conflicting with dylib definition - .comm variables in shared library, worked with XCode 2.4.1, broken with XCode 3? - * src/ld.cpp: at end of checkUndefines() verify if any remaining commons conflict with dylibs - * doc/man/man1/ld.1: document -commons and -warn_commons options - * unit-tests/test-cases/tentative-and-dylib: added test case - - -2007-10-05 Nick Kledzik - - NS/CFString constants are not dead strippable - * src/MachOReaderRelocatable.hpp: break up __cfstring section into one atom per cfstring, make them coalesable - * unit-tests/test-cases/cfstring-coalesce: added test case - - -2007-10-05 Nick Kledzik - - Dead stripping + exported symbols list using wildcards doesn't seem to do the right thing - * src/Options.cpp/h: add hasWildCardExportRestrictList() - * src/ld.cpp: if dead stripping code and have wildcard exports, add all global atoms matching wildcards as roots - * unit-tests/test-cases/exported-symbols-wildcards-dead_strip: added test case - - -2007-10-04 Nick Kledzik - - ld shouldn't search /Network/Library/Frameworks by default - * src/Options.cpp: remove /Network/Library/Frameworks/ from default search path - * doc/man/man1/ld.1: document the change - - -2007-10-04 Nick Kledzik - - all binaries should get LD_UUID load commands, not just those with DWARF symbols - * src/ld.cpp: default fCreateUUID to be true for non object file output types - * unit-tests/test-cases/no-uuid/Makefile: update test case to match new rules - - ------ Tagged ld64-78 - -2007-09-27 Nick Kledzik - - range check load commands - * src/MachOReaderDylib.hpp: check that load commands all fit in load command size from header - * src/MachOReaderRelocatable.hpp: check that load commands all fit in load command size from header - - -2007-09-27 Nick Kledzik - - Xc8M2540a: ld64 crashes when linking Pascal program - * src/ld.cpp: fix findAtomAndOffset() to handle where there are no function atoms - - -2007-09-27 Nick Kledzik - - ADOBE Xcode 3: ld -dead_strip does not work with -init from an archive - * src/ld.cpp: add bool parameter to entryPoint() so -init atom not looked for too soon - * unit-tests/test-cases/dead_strip-init-archive: added test case - - -2007-09-26 Nick Kledzik - - Spurious link warnings for inline members of C++ template classes - * src/ld.cpp: check definition kinds before warning about visibility mismatches - * unit-tests/test-cases/visibility-warning: added test case - - -2007-09-26 Nick Kledzik - - an empty .o file with zero load commands will crash linker - * src/MachOReaderRelocatable.hpp: have Reader constructor return early of no load commands - * unit-tests/test-cases/empty-object: added test case - - -2007-09-26 Nick Kledzik - - 9a527: ppc64 branch islands fail with 4GB pagezeo - * src/MachOWriterExecutable.hpp: start range calculations at start of __text not at zero. - - ------ Tagged ld64-77 (Xcode 3.0) - -2007-07-23 Nick Kledzik - - Kernel is linked with some global symbols unsorted - * src/MachOWriterExecutable.hpp: Add NListNameSorter to allow global atoms and extra labels to be sorted - - -2007-07-20 Nick Kledzik - - Can't do objc_msgSendSuper dispatches after loading a Fix&Continue bundle - * src/MachOWriterExecutable.hpp: when calculating what kind of reloc to use, never use an - external reloc to reference 32-bit ObjC symbols. - - -2007-07-20 Nick Kledzik - - Runtime crash with ICC math library on Leopard - * src/MachOReaderRelocatable.hpp: detect if section starts with a symbol that is not - aligned to section and correct it. - - ------ Tagged ld64-76 - -2007-06-29 Nick Kledzik - - export hiding does not work for frameworks - * src/MachOReaderDylib.hpp: fix checks in isPublicLocation() - * unit-tests/test-cases/symbol-moving: update to test frameworks as well as dylibs - - -2007-06-27 Nick Kledzik - - linker should use undefines from flat dylibs when linking a main flat - * src/ObjectFile.h: added fLinkingMainExecutable - * src/Options.cpp: set up fLinkingMainExecutable - * src/MachOReaderDylib.hpp: when linking a main executable for flat namespace, the reader for - any loaded flat namespace dylib will have a new atoms that has references to all undefined - symbols in the dylib - * unit-tests/test-cases/flat-indirect-undefines: added test case - * doc/man/man1/ld.1: update man page to describe when dylib undefines are used - - -2007-06-27 Nick Kledzik - - OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found - * src/MachOReaderDylib.hpp: add assertNoReExportCycles() method - * unit-tests/test-cases/dylib-re-export-cycle: added test case - - -2007-06-27 Nick Kledzik - - ld64 has slightly different warning message formats than the old ld - * src/ld.cpp: standardize all warning messages to start with "ld: warning" - * src/MachOWriterExecutable.hpp: ditto - * src/MachOReaderRelocatable.hpp: ditto - * src/MachOReaderDylib.hpp:ditto - - -2007-06-26 Nick Kledzik - - -dead_strip can cause duplicate external commons - * src/ld.cpp: don't use discarded coalesced global atoms as dead strip roots - * src/machochecker.cpp: error if duplicate external symbols - * unit-tests/test-cases/commons-coalesced-dead_strip: added test case - - -2007-06-26 Nick Kledzik - - update man page that linker does not search indirect libraries with two-level namespace - * doc/man/man1/ld.1: add new "Indirect dynamic libraries" section to man page - - -2007-06-26 Nick Kledzik - - Xc9A466: Exports file cannot use Mac line ends - * src/Options.cpp: check for \r or \n when parsing .exp files - * unit-tests/test-cases/exported_symbols_list-eol: added test case - - ------ Tagged ld64-75 - -2007-05-31 Nick Kledzik - - Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB - * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 - - ------ Tagged ld64-74.5 - -2007-05-31 Nick Kledzik - - set OSO timestamp to zero for when building in buildit - * src/ld.cpp: check for RC_RELEASE and if exists set all OSO timestamps to zero - - -2007-05-30 Nick Kledzik - - BUILD_STABS now causes ld of xnu to bus error - * src/ld.cpp: Change || to && in collectStabs() - - ------ Tagged ld64-74.4 - -2007-05-18 Nick Kledzik - - static probes don't work with libraries in dyld shared cache - * src/OpaqueSection.hpp: the __TEXT segment is executable - - ------ Tagged ld64-74.3 - -2007-05-16 Nick Kledzik - - ppc: linker adds stubs to cstring references - * src/MachOWriterExecutable.hpp: update ppc stubableReference() to only allow high/low references - to be stubed if they reference a symbol in some other dylib. - * unit-tests/test-cases/stub-generation: added test case - - -2007-05-16 Nick Kledzik - - ppc64: need to make LOCAL indirect symbol table entry for now local symbol - * src/MachOWriterExecutable.hpp: factored local tests into indirectSymbolIsLocal() - * unit-tests/test-cases/non-lazy-r: added test case - - -2007-05-15 Nick Kledzik - - ld64 drops fix&continue bit in __OBJC, __image_info. - * src/MachOReaderRelocatable.hpp: implement objcReplacementClasses() - - -2007-05-15 Nick Kledzik - - support __image_info in __DATA segment for 64-bits - * src/MachOReaderRelocatable.hpp: use strncmp() for __objc_imageinfo since it is 16 bytes long - * src/MachOWriterExecutable.hpp: specialize segment/section names for synthesized objc image info section - - -2007-05-15 Nick Kledzik - - * unit-tests/include/common.makefile: set COMPILER_PATH so harness works with latest compiler - - ------ Tagged ld64-74.2 - -2007-05-11 Nick Kledzik - - ld64-74.1 breaks libstdc++ DejaGnu test (G5 only) - * src/MachOWriterExecutable.hpp: don't stub a reference if the target offset is non-zero - - ------ Tagged ld64-74.1 - -2007-05-09 Nick Kledzik - - * src/Options.h: add emitWarnings() - * src/Options.cpp: wire up -w to emitWarnings() - - -2007-05-09 Nick Kledzik - - ld64 won't link wine (regression from Tiger) - * src/Architectures.hpp: add x86::kPointerDiff16 and x86::kPCRel16 - * src/MachOReaderRelocatable.hpp: add support to parse new relocs - * src/MachOWriterExecutable.hpp: add support fo new relocs - - -2007-05-08 Nick Kledzik - - need way for ld and dyld to see different exported symbols in a dylib - * src/MachOReaderDylib.hpp: update parse and use $ld$ symbols - * src/Options.h: move VersionMin to ReaderOptions - * src/ObjectFile.h: move VersionMin to ReaderOptions - * src/Options.cpp: move VersionMin to ReaderOptions - * src/MachOWriterExecutable.hpp: move VersionMin to ReaderOptions - * unit-tests/test-cases/symbol-moving: added test case - - -2007-05-03 Nick Kledzik - - typo in error message for linking -pie - * src/MachOWriterExecutable.hpp: fix typo in error messages - - ------ Tagged ld64-74 - -2007-05-03 Nick Kledzik - - ld64 can't find @executable _path relative dylibs from our umbrella frameworks - ld64 should handle linking against dylibs that have @loader_path based dylib load commands - * src/ObjectFile.h: add from parameter to findDylib() - * src/MachOReaderDylib.hpp: supply from parameter to findDylib() - * src/ld.cpp: use from parameter for @loader_path substitution in findDylib() - * unit-tests/test-cases/re-export-relative-paths: added test case - - -2007-05-02 Nick Kledzik - - * src/ObjectFile.h: add fLogObjectFiles and fLogAllFiles - * src/Options.cpp: hook up -t to fLogAllFiles and -whatsloaded to fLogObjectFiles - * src/MachOReaderDylib.hpp: log if fLogAllFiles - * src/MachOReaderRelocatable.hpp: log if fLogObjectFiles or fLogAllFiles - * src/MachOReaderArchive.hpp: log if fLogAllFiles - * doc/man/man1/ld.1: update man page - - -2007-05-02 Nick Kledzik - - typo in message, frameowrk - * src/Options.cpp: fix typo - - -2007-05-01 Nick Kledzik - - "ld" man page is missing the description for many options - * doc/man/man1/ld.1: add documentation on all obsolete options - - -2007-05-01 Nick Kledzik - - ld doesn't handle -mlong-branch .o files that have had local symbols stripped - warning about dwarf line info with -mlong-branch - * src/MachOReaderRelocatable.hpp: don't lop -mlong-branch stubs off end of functions - * src/MachOWriterExecutable.hpp: allow code references besides BR24 to be stubable - - -2007-04-30 Nick Kledzik - - unable to link VTK because __textcoal_nt too large - * src/MachOReaderRelocatable.hpp: when doing a final link map __textcoal_nt to __text - - -2007-04-30 Nick Kledzik - - ld does not report error when -r is used and exported symbols are not defined. - ld leaves global common symbols not in exported symbols list. - * src/ld.cpp: stop special casing -r mode in checkUndefines() - * src/MachOWriterExecutable.hpp: don't create proxy atom in -r mode if it is supposed to be exported. - mark tentative definitions are private extern in -r mode even without -keep_private_externs - * unit-tests/test-cases/exported_symbols_list-r: added test case - - -2007-04-27 Nick Kledzik - - ld should keep looking when it finds a weak definition in a dylib - * src/ld.cpp: modified addJustInTimeAtoms() to keep looking when a weak defintion is found - * unit-tests/test-cases/weak-def-ordinal: added test case - - -2007-04-27 Nick Kledzik - - better error message for indirect dylibs missing required architecture - * src/ld.cpp: when loading indirect dylib add path to error messages - - -2007-04-25 Nick Kledzik - - the i386 slice of dyld does not need __IMPORT segment - * src/ObjectFile.h: add fForDyld - * src/Options.cpp: set up fForDyld - * src/MachOReaderRelocatable.hpp: if fForDyld, change __IMPORT segment to __DATA - * src/MachOWriterExecutable.hpp: recognize __DATA/__pointers in dyld as a non-lazy section - - -2007-04-24 Nick Kledzik - - ppc64: need to make LOCAL indirect symbol table entry for now local symbol - * src/MachOWriterExecutable.hpp: use INDIRECT_SYMBOL_LOCAL for any non-global symbol - * unit-tests/test-cases/strip_local: update test case - - -2007-04-24 Nick Kledzik - - ld64 -sectorder and -order_file files don't accept white space following the : - * src/Options.cpp: prune white space after colon and before symbol name - * unit-tests/test-cases/order_file: update test case to have a space after the colon - - -2007-04-24 Nick Kledzik - - ld64 corrupts debug symbol table entries, nm doesn't print them - * src/MachOWriterExecutable.hpp: properly set ilocalsym in module table - - -2007-04-24 Nick Kledzik - - support __image_info in __DATA segment for 64-bits - * src/MachOReaderRelocatable.hpp: look for new objc info section name too - - -2007-04-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix -non_global_symbols_strip_list to work with -r - * unit-tests/test-cases/local-symbol-partial-stripping: update test case - - - ------ Tagged ld64-73.7 - -2007-05-10 Nick Kledzik - - can't use dtrace static probes in x86_64 dylib - * src/MachOWriterExecutable.hpp: x86_64:kPointerDiff32 is ok in shared region - * unit-tests/test-cases/dtrace-static-probes: update to build dylib too - - -2007-05-09 Nick Kledzik - - 9A430: using -dead_strip with static dtrace probes causes ld to crash - * src/ld.cpp: fix markLive() to look at right name in dtrace probe refernce - * unit-tests/test-cases/dtrace-static-probes: added -dead_strip case - - ------ Tagged ld64-73.6 - -2007-04-17 Nick Kledzik - - Add options to do partial stripping of local symbols - * src/MachOWriterExecutable.hpp: use fOptions.keepLocalSymbol() - * src/Options.cpp: implement -non_global_symbols_no_strip_list and -non_global_symbols_strip_list - * src/Options.h: replace stripLocalSymbols() with localSymbolHandling() and keepLocalSymbol() - * doc/man/man1/ld.1: document -non_global_symbols_no_strip_list and -non_global_symbols_strip_list - * unit-tests/test-cases/local-symbol-partial-stripping: added test case - - ------ Tagged ld64-73.5 - -2007-04-17 Nick Kledzik - - ld64-73.3 XBS logging incorrectly reporting "direct" dynamic libraries - * src/ld.cpp: restore direct vs indirect library for LD_TRACE_DYLIBS logging - - -2007-04-16 Nick Kledzik - - data initialized to a weak imported symbol is missing relocation - * src/MachOWriterExecutable.hpp: check for A::kPointerWeakImport in buildExecutableFixups() - * unit-tests/test-cases/weak_import: updated test case to catch this problem - - -2007-04-13 Nick Kledzik - - Support -U - * src/MachOWriterExecutable.hpp: create proxies for -U symbols - * src/Options.cpp: process -U - * src/Options.h: add allowedUndefined() and someAllowedUndefines() - * src/ld.cpp: create proxies for -U symbols - * doc/man/man1/ld.1: document -U and -undefined options - * unit-tests/test-cases/undefined-dynamic-lookup: added test case - - ------ Tagged ld64-73.4 - -2007-04-12 Nick Kledzik - - ld changes needed to support read-only DOF - * src/Options.cpp: remove -read_only_dof - * src/Options.h: remove fReadOnlyDOFs - * src/ld.cpp: only generate read-only DOF sections - - ------ Tagged ld64-73.3.1 - -2007-04-13 Nick Kledzik - - -framework vecLib -framework Accelerate causes bad ordinals - * src/MachOWriterExecutable.hpp: fix bug optimizeDylibReferences() when there are two readers with same install name - - ------ Tagged ld64-73.3 - -2007-04-03 Nick Kledzik - - * src/ld.cpp: read-only-dofs should use 32-bit offsets for x86_64 - * src/MachOReaderDylib.hpp: if "public" re-export is not marked implict, still mark it as re-exported - - -2007-04-02 Nick Kledzik - - if replacement file for -dylib_file is missing, warn instead of error - * src/ld.cpp: a try/catch to turn -dylib_file error into a warning. - * unit-tests/test-cases/dylib_file-missing: add test case - * doc/man/man1/ld.1: update man page about -dead_strip_dylibs - - ------ Tagged ld64-73.2 - -2007-03-31 Nick Kledzik - - ld64-73: atom sorting error with duplicate zero sized bss symbols - * src/MachOReaderRelocatable.hpp: suppress warning on sorting zero size zero fill atoms - -2007-03-31 Nick Kledzik - - ld64-73 fails anything linking with -lm - * src/ld.cpp: when processing dylbs that are sylinks ensure that fDylibMap contains all paths - * src/MachOWriterExecutable.hpp: when dead stripping dylibs and renumbering ordinals make sure - aliases dylib get renumbered too - * unit-tests/test-cases/dylib-aliases: added - - ------ Tagged ld64-73.1 - -2007-03-30 Nick Kledzik - - * src/MachOWriterExecutable.hpp: back out use of LC_REEXPORT_DYLIB until rdar://problem/5009909 is in build fleet - - ------ Tagged ld64-73 - -2007-03-30 Nick Kledzik - - ER: -dead_strip_dylibs - linker should add implicit load commands for indirectly used public dylibs - * src/ObjectFile.h: change dylib reader interface to implictly/explicitlyLinked - * src/ld.cpp: use new dylib reader interface - * src/Options.h: add deadStripDylibs() - * src/Options.cpp: support -dead_strip_dylibs - * src/MachOReaderDylib.hpp: use new dylib reader interface - * src/MachOWriterExecutable.hpp: remove dylib load commands for unused dylibs and alter ordinals - * unit-tests/test-cases/re-export-optimizations: added - * unit-tests/test-cases/dead_strip_dylibs: added - - -2007-03-30 Nick Kledzik - - * src/Options.cpp: enable -lfoo to search for libfoo.so as well as libfoo.dylib, - remove seg addr table hack for transitioning to new linker - -2007-03-30 Nick Kledzik - - ADOBE XCODE3: Linker is slow with large C++ .o files - * src/MachOReaderRelocatable.hpp: the compiler generates stubs to weak functions in the - same translation unit. Don't treat those like the spurios stubs to static functions. - - -2007-03-29 Nick Kledzik - - ld64 should link mach_kernel during xnu builds to support dtrace - * src/MachOReaderRelocatable.hpp: To handle duplicate labels properly, rework how atoms sizes are set - by iterating through sorted fAtoms rather than fAddrToAtom, . Change default alignment of commons - to be the natural alignment of the size rounded up to the closest power of two and max it at 12. - Build atoms in reverse symbol table order so that global atoms are constructed before locals. - This assures that if there is a global and local label at the same location, the global label - will become the atom's name and the local will be an alias. Properly handle a label - at the end of a section. Handle R_ABS in relocations. Handle sect-diff relocs with addends. - Don't auto-strip 'l' symbols in static executables (mach_kernel). - * src/OpaqueSection.hpp: opaque_section now has an ordinal - * src/ld.cpp: opaque_section now requires an ordinal - * src/ObjectFile.h: add ReaderOptions.fForStatic - * src/Options.cpp: set fForStatic when building a static executable - * src/MachOWriterExecutable.hpp: add from atom to StubAtom. Properly write out i386 - sect-diff relocs with addends. properly write out ppc PICbase relocs where pic base - is not in the atom. - - -2007-03-27 Nick Kledzik - - Typo in ld man page (-exported_symbols_list) - * doc/man/man1/ld.1: fix typo - - -2007-03-26 Nick Kledzik - - consider generating LC_UUID from a checksum of the file - * src/Options.h: change emitUUID() to getUUIDMode() - * src/Options.cpp: support -random_uuid - * src/MachOWriterExecutable.hpp: set uuid to be md5 hash of entire output file - - -2007-03-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: restructure writeAtoms() to copy all atoms in memory if possible - - -2007-03-24 Nick Kledzik - - ld -r of stripped .o file can incorrectly merge non-lazy pointers - * src/MachOWriterExecutable.hpp: when generating a .o file, non-lazy pointer with target offsets should be - encoded as LOCAL in the indirect symbol table - * unit-tests/test-cases/stripped-indirect-symbol-table: added test case - - -2007-03-23 Nick Kledzik - - SWB: ld64-72 errors building with gcc-4.2 - * src/MachOReaderDylib.hpp: add curly brackets in switch cases - * src/MachOWriterExecutable.hpp: rearrange classes so there are no template specialization forward references - - -2007-03-23 Nick Kledzik - - * src/ld.cpp: fix -print_statistics when using -dead_strip - - -2007-03-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: generate better names for non-lazy pointers to the interior of atoms - - -2007-03-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: speed up ld -r a little by reversing relocs en mas - - -2007-03-16 Nick Kledzik - - ld Bus Error on missing command line arguments - * src/Options.cpp: check next argv[] is not NULL - - -2007-03-16 Nick Kledzik - - need to be able to order symbols in anonymous namespaces - * src/ld.cpp: add logic to do fuzzy matching of symbols with anonymous namespace usage - * unit-tests/test-cases/order_file-ans: added test case - - -2007-03-16 Nick Kledzik - - headerpad_max_install_names deprecated for 64-bit - * src/ld.cpp: make sure dylib load command order matches command line order - * src/Options.h: add maxMminimumHeaderPad() - * src/Options.cpp: add maxMminimumHeaderPad() set by -headerpad_max_install_names - * src/src/MachOWriterExecutable.hpp: check maxMminimumHeaderPad() - * doc/man/man1/ld.1: update man page about -headerpad_max_install_names - - -2007-03-16 Nick Kledzik - - Linker returns success although exported symbols are undefined. - * src/ld.cpp: turn missing symbols back into an error - - -2007-03-16 Nick Kledzik - - ld64 should handle linking against dylibs that have @loader_path based dylib load commands - * unit-tests/test-cases/loader_path: added test case - - -2007-03-16 Nick Kledzik - - linker should add implicit load commands for indirectly used public dylibs - Indirect libraries should be found using -F and -L options - Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB - * src/ld.cpp: reworked all dylib processing. Readers can now add the dylib list. - * src/Options.h: add findFileUsingPaths() - * src/MachOReaderDylib.hpp: look in re-exported children instead of requring linker to do that - * src/ObjectFile.h: add processIndirectLibraries(), remove getDependentLibraryPaths() - * src/machochecker.cpp: support LC_REEXPORT_DYLIB - * src/ExecutableFile.h: simplify DyLibUsed - * src/Options.cpp: add findFileUsingPaths(). add new re-export options - * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 - * doc/man/man1/ld.1: updated with new re-export options - * unit-tests/test-cases/indirect-path-search: added tests that -F and -L work with indirect dylibs - * unit-tests/test-cases/re-export-cases: added tests for all combinations of re-exporting - - -2007-03-14 Nick Kledzik - - sort external relocations to optimize dyld performance - * src/MachOWriterExecutable.hpp: added ExternalRelocSorter - * src/machochecker.cpp: verify external relocations are grouped by symbol number - * unit-tests/test-cases/external-reloc-sorting: added test case - - ------ Tagged ld64-72 - -2007-03-06 Nick Kledzik - - * src/Options.cpp: ignore .objc_category_name_* symbols in .exp files - - -2007-03-06 Nick Kledzik - - * src/Options.cpp: stop special casing mach_kernel and instead requre kernel to be built with -new_linker - - -2007-03-06 Nick Kledzik - - ld64-72 (experimental) is causing DejaGnu test failures - * src/MachOWriterExecutable.hpp: add optimizableGOTReferenceKind() to track GOT uses that cannot be optimized - - -2007-03-06 Nick Kledzik - - minimum header padding should be 32 to allow code signing - * src/Options.cpp: initialize fMinimumHeaderPad to 32 - * src/MachOWriterExecutable.hpp: better calculation of header padding - - -2007-03-06 Nick Kledzik - - Linker crashes with -flat_namespace against two-level dylibs that might have re-exports - * src/ld.cpp: flat namespace should not allow NULL indirect readers - - -2007-03-06 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't error on S_COALESCED sections with anonymous atoms - * src/MachOWriterExecutable.hpp: set MH_PIE bit when linking -pie - * ld64.xcodeproj/project.pbxproj: don't echo environment when running unit test - - -2007-03-01 Nick Kledzik - - * doc/man/man1/ld.1: Add descriptions to all "rarely used options" - - -2007-03-01 Nick Kledzik - - Remove support for Essential Symbols: Warn about use of -Sp option; remove man page entry - * src/Options.cpp: make -Sp obsolete - * doc/man/man1/ld.1: make -Sp obsolete - - -2007-03-01 Nick Kledzik - - Support -pie - * src/Options.h: Add positionIndependentExecutable() - * src/Options.cpp: Support -pie option to set positionIndependentExecutable() - * src/MachOWriterExecutable: if -pie is used, add extra local relocations and error if any - absolute addressing is used - - -2007-03-01 Nick Kledzik - - ld64 should link mach_kernel during xnu builds to support dtrace - * src/ld.cpp: Ensure segments are laid out in discovery order. Add support for kAbsoluteSymbol. - Warn when merging symbols of different visiblity. Warn when a tentative definition - is replaced by one a real definition with a smaller size. Lay out __common section - so that ones built with -fno-commons come before regular commons. - * src/ObjectFile.h: remove SegmentOffset ivar and getter/setters - * src/machochecker.cpp: allow images with no r/w segments - * src/MachOReaderRelocatable: Add AbsoluteAtom. Sort tentative definitions by name instead of by size - Add support for custom commons alignment. - * src/Options.cpp: Fix spurious -sectalign warnings. Don't use ld_classic when linking mach_kernel - * src/MachOWriterExecutable.hpp: Support kAbsoluteSymbol atoms. In -r mode, set custom alignment - for commons if alignment is not its size. Support global __dtrace_probe labels. - * src/ObjectDump.cpp: add support for kAbsoluteSymbol atoms. - * unit-tests/test-cases/commons-alignment: Added test case for custom commons alignment - * unit-tests/test-cases/absolute-symbol: Added test case for basic absolute symbols - * unit-tests/test-cases/segment-order: Added test case that segments lay out in discovery order - * unit-tests/test-cases/commons-order: Added test case that commons lay out correctly - * unit-tests/test-cases/end-label: Added test case that a label used to mark the end of a section does not - get associcated with the next section. - - -2007-02-23 Nick Kledzik - - gcc-5005: DejaGnu failures due to -frepo - * src/ld.cpp: Add quotes to referenced from name to make collect2 and -frepo happy - - -2007-02-22 Nick Kledzik - - * src/MachOWriterExecutable.hpp: rework how padding after load commands is calculated - - -2007-02-21 Nick Kledzik - - * src/MachOWriterExecutable.hpp: extend special case of __mh_execute_header to static executables too - - -2007-02-21 Nick Kledzik - - gcc link map option ( "-M" ) should be redirectable to file - * doc/man/man1/ld.1: added -map option description - * src/Options.h: added generatedMapPath() - * src/Options.cpp: set up generatedMapPath() if -map option is used - * src/MachOWriterExecutable.hpp: add writeMap() method to generate map file - - -2007-02-19 Nick Kledzik - - Implement GOT Load elimination optimization - * src/ld.cpp: track size of all atoms and if > 2GB sort large zero-fill atoms to end - * src/MachOWriterExecutable.hpp: If image size < 2GB, only generate GOT entries if value must be - updatable by dyld. If > 2GB, only eliminate GOT entries to non-zero-fill atoms. Any use - of an eliminated GOT entry has its code changed from MOVQ _foo@GOT(%rip) to LEAQ _foo(%rip). - * unit-tests/test-cases/large-data: added - * unit-tests/test-cases/got-elimination: added - - ------ Tagged ld64-71.2 - -2007-02-13 Nick Kledzik - - new ld ignores -segprot option - * src/Options.h: expose customSegmentProtections() - * src/Options.cpp: parse -segprot option and populate customSegmentProtections() - * src/MachOWriterExecutable.hpp: use customSegmentProtections() - - -2007-02-13 Nick Kledzik - - i386 -stack_addr doesn't work - * src/MachOWriterExecutable.hpp: use correct offset into thread state record - - ------ Tagged ld64-71.1 - -2007-02-07 Nick Kledzik - - * src/ld.cpp: sort __OBJC2 segment to be next to __OBJC segment - - -2007-02-07 Nick Kledzik - - * src/Options.cpp: change missing -seg_addr_table from an error to a warning - - -2007-02-06 Nick Kledzik - - Leopard 9A357: -dylib_file broken? - * src/MachOWriterExecutable.hpp: remove use of fInstallPathOverride - * src/Options.cpp: wire up -dylib_file option - * src/Options.h: remove fInstallPathOverride. add fDylibOverrides - * src/ld.cpp: check dylibOverrides() for indirect libraries - * unit-tests/test-cases/dylib_file: add test case - - -2007-02-05 Nick Kledzik - - * src/MachOReaderDylib.hpp: don't warn about zero size __image_info sections - - -2007-02-04 Rick Balocca - Enable the failing cases for missing command line arguments - -2007-02-04 Rick Balocca - Make sure that all .o's are checked by ObjectDump - and all macho are checked by machochecker - -2007-02-04 Rick Balocca - Fix an endian problem with machochecker - Fix blank-stubs Makefile - ------ Tagged ld64-71 - -2007-02-02 Rick Balocca - blank-stubs test case: handle the case of a native ppc compile--this - sets the subtype, which must be passed to lipo - -2007-02-01 Rick Balocca - make cpu-sub-types test more robust - -2007-02-01 Rick Balocca - auto-arch tests were resulting in a false FAILs - -2007-02-01 Rick Balocca - test cpu-sub-types was resulting in a false FAIL - -2007-02-01 Nick Kledzik - - STD:VSC: c99 -o writes to file that does not have write permission - * src/MachOWriterExecutable.hpp: check file is writable before using it - -2007-02-01 Nick Kledzik - - debug map (N_OSO) timestamps for object files in ranlib archive are incorrect - * src/MachOReaderArchive.hpp: parse modTime for .o files out of archive header - -2007-01-31 Nick Kledzik - - 9A354: ld -all_load does *NOT* produce the same dSYM as *.o or -u - * src/ld.cpp: when using -all_load don't assume that all atoms have same reader - * unit-tests/test-cases/dwarf-archive-all_load: added - ------ Tagged ld64-70.1 - -2007-01-31 Nick Kledzik - - * src/MachOWriterExecutable.hpp: in addObjectRelocs_powerpc() mask scattered r_address to 16-bits - ------ Tagged ld64-70 - - -2007-01-30 Nick Kledzik - - linker should verify GC consistency of modules being linked into library - Support cpu-sub-types for ppc - * src/ObjectFile.h: Add getObjCConstraint() and getCpuConstraint() - * src/MachOReaderRelocatable.hpp: don't make atom for __image_info section, instead parse constaints - * src/MachOReaderDylib.hpp: look at __image_info content to get constaints - * src/ld.cpp: add updateContraints() and checkObjc() - * src/MachOWriterExecutable.hpp: add ObjCInfoAtom to sythesize __image_info content - - -2007-01-28 Nick Kledzik - - src/*: remove ObjectFile::requiresFollowOnAtom() method - - -2007-01-28 Nick Kledzik - - src/ld.cpp: enable LLVM_SUPPORT by default - src/LLVMReader.hpp: don't use absolute paths for llvm headers and libraries - - -2007-01-26 Rick Balocca - * src/ObjectDump.cpp: The usage() message was incorrect. - - -2007-01-25 Rick Balocca - * unit-tests/test-cases/zero-fill3: It was reporting FAIL on ld64 error return. - It should have been checking for non-error return. - - -2007-01-24 Nick Kledzik - - x86 fast stubs should not cross 64-byte boundries - * src/MachOWriterExecutable.hpp: for x86, 64-byte align __jump_table section - and make 64-btye crossing stubs be empty entries with indirect symbol table - entry of INDIRECT_SYMBOL_ABS - - -2007-01-19 Nick Kledzik - - * src/Options.h: add readOnlyx86Stubs() - * src/Options.cpp: support -read_only_stubs - * src/MachOWriterExecutable.hpp: make __IMPORT segment not writable if -read_only_stubs is used - - -2007-01-16 Eric Christopher - - ld64 --help isn't recognized - * src/Options.cpp (Options::parse): Support --help and -help. - - -2007-01-15 Nick Kledzik - - * src/MachOFileAbstraction.hpp: add range checking on macho_scattered_relocation_info::set_r_address() - - -2007-01-14 Nick Kledzik - - Support wildcards in contents of -exported_symbols_list - * src/Options.h: add SetWithWildcards class - * src/Options.cpp: add -exported_symbol and -unexported_symbol and use SetWithWildcards - * doc/man/man1/ld.1: add -exported_symbol and wildcard explanation - * unit-tests/test-cases/exported-symbols-wildcards: added test case - - -2007-01-10 Nick Kledzik - - [U]SDT probes should use C calling convention - * src/Options.cpp: Add -read_only_dof - * src/ld.cpp: create __dof section(s) based on probe and isenabled sites - * src/MachOReaderRelocatable.hpp: parse new sdt 2.0 probes encoded in .o files - * src/MachOWriterExecutable.hpp: handle regenerating dtrace probes into .o files - * unit-tests/test-cases/dtrace-static-probes: added test case - - ------ Tagged ld64-69.8 - -2007-01-30 Nick Kledzik - - Support LD_FORCE_NO_SEG_ADDR_TABLE - * src/Options.cpp: Support LD_FORCE_NO_SEG_ADDR_TABLE - - ------ Tagged ld64-69.7 - -2007-01-25 Nick Kledzik - - Leopard9A351: CFM Apps Are Broken because CFM glue is missing - * src/MachOReaderRelocatable.hpp: check S_ATTR_NO_DEAD_STRIP in dontDeadStrip() - - ------ Tagged ld64-69.6 - -2007-01-24 Nick Kledzik - - LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive - * src/ld.cpp: create and use logArchive() - - ------ Tagged ld64-69.5 - -2007-01-22 Nick Kledzik - - 9A350: can't link ppc programs with ld_classic - * src/Options.cpp: Remove support for LD_NO_CLASSIC_LINKER. Add support for -classic_linker - - ------ Tagged ld64-69.4 - -2007-01-17 Nick Kledzik - - QTComponents does not link with ld64 - * src/MachOReaderRelocatable.hpp: handle N_RSYM and N_PSYM stabs - - ------ Tagged ld64-69.3 - -2007-01-03 Nick Kledzik - - * src/Options.cpp: If the same dylib is specified twice and the second is specified weak, make it weak - - ------ Tagged ld64-69.2 - -2006-12-18 Nick Kledzik - - -dead_strip without -exported_symbols_list should not strip global functions from archives - * src/ld.cpp: when adding a .o file from an archive, add all its global symbols to live roots - * unit-tests/test-cases/dead_strip-archive: added - - -2006-12-18 Nick Kledzik - - flat_namespace main executables do not need to indirect interior references - * src/MachOWriterExecutable.hpp: don't indirect references to global symbols in main executables - * unit-tests/test-cases/flat-main: updated to test for indirection - * unit-tests/test-cases/flat-dylib: added - - ------ Tagged ld64-69.1 - -2006-12-15 Nick Kledzik - - -flat_namespace does not work with -mdynamic-no-pic - * src/MachOWriterExecutable.hpp: rework checking for use of ppc absolute addressing to allow them as long as - the target is within the same linkage unit. - - -2006-12-15 Nick Kledzik - - -ObjC should only load .o with .objc_ symbols - * src/Options.cpp: remove warning from -ObjC and have it instead set fLoadAllObjcObjectsFromArchives - * src/MachOReaderArchive.hpp: when -ObjC is used, preload all .o files from archives that contain .objc_ symbols - - ------ Tagged ld64-69 - -2006-12-13 Nick Kledzik - - prebound interior pointers must be non-zero - * src/MachOWriterExecutable.hpp: in fixUpReference_powerpc() set lazy pointers bound to with the dylib to - their target value. Properly set REFERENCE_FLAG_UNDEFINED_* flags in reference table and n_desc - - -2006-12-09 Nick Kledzik - - ld64 fails to detect error that ld_classic does - * src/MachOWriterExecutable.hpp: check for absolute reloc to an external symbol - * src/MachOReaderRelocatable.hpp: ignore -mlong-branch stubs in .o files - - -2006-12-09 Nick Kledzik - - symbols with REFERENCED_DYNAMICALLY should never be stripped - * src/MachOWriterExecutable.hpp: update Writer::shouldExport() to check for kSymbolTableInAndNeverStrip - * unit-tests/test-cases/main-stripped: add test that dynamically referenced symbol cannot be stripped - - -2006-12-08 Nick Kledzik - - * unit-tests/test-cases/allowable-client: add variant test cases (e.g. CoreServices_profile) - * src/ld.cpp: allow frameworks with variant install names (e.g. CoreServices_profile) to be private clients - - -2006-12-08 Nick Kledzik - - * doc/man/man1/ld.1: rewrite man page - * src/Options.h: add warnObsolete() - * src/Options.cpp: use warnObsolete() on many options. Make nonWeak the weak-mis-match default. - Make -ObjC mean -all_load. - ------ Tagged ld64-68.3 - -2006-12-05 Nick Kledzik - - * src/ld.cpp: allow umbrella frameworks to have variant install names (e.g. CoreServices_profile) and still link - - ------ Tagged ld64-68.2 - -2006-12-05 Nick Kledzik - - * src/MachOWriterExecutable.cpp: Use N_PBUD in the symbol table for undefined symbols in prebound dylibs - - ------ Tagged ld64-68.1 - -2006-12-01 Nick Kledzik - - * src/Options.cpp: always generate module tables for 32-bit architectures so that ld_classic - can link against them - - ------ Tagged ld64-68 - -2006-12-01 Nick Kledzik - - seg_addr_table needs matching fuzziness - * src/Options.cpp: special case a how a dozen dylib are looked up in the seg_addr_table - - -2006-12-01 Nick Kledzik - - * src/Options.cpp: have all -static links for 32-bit archs roll over to ld_classic unless - LD_NO_CLASSIC_LINKER_STATIC is set. - * unit-tests/bin/make-recursive.pl: set LD_NO_CLASSIC_LINKER_STATIC for unit tests - - -2006-11-29 Nick Kledzik - - ld64-67: QTComponents fails to build - * src/MachOReaderRelocatable.hpp: don't error out when a local non-lazy pointer does not point to a symbol - * unit-tests/test-cases/strip_local: added test case - - -2006-11-28 Nick Kledzik - - Need a way to mark libraries usable by dynamic linker but unusable by static linker - * src/Options.cpp: allow -client_name to be used with main executables - * src/ld.cpp: generalize -allowable_client. Any dylib can now restrict who can link against it. As a convention - linking with -allowable_client '!' will mean no one can statically link with the dylib. It can still be loaded - dynamically, or by any existing clients, but no new clients can link with it. - * unit-tests/test-cases/allowable-client/Makefile: enable previously commented out test cases. Add test cases - of a dylib that allows no clients and just one client - -2006-11-27 Nick Kledzik - - -final_output should be used if -install_name not used - * src/Options.cpp: fall back to using -final_output for install name - - ------ Tagged ld64-67 - -2006-11-17 Nick Kledzik - - * src/MachOWriterExecutable.hpp: support __IMPORT segment being slide independently of __DATA segment in shared cache - - -2006-11-16 Nick Kledzik - - 9a303: ld -filelist Bus Error - * src/Options.cpp: add check that -filelist is followed by an argument - - -2006-11-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: when building split-seg dylibs, LINKEDIT goes in read-only side - - -2006-11-15 Nick Kledzik - - * src/MachOWriterExecutable.hpp: set proper attributes for __eh_frame in ld -r mode - * unit-tests/test-cases/eh_frame: added test case - - -2006-11-10 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: redirect references to static weak stubs to the real target - - -2006-11-09 Nick Kledzik - - * src/MachOWriterExecutable.hpp: r_address is offset from first LC_SEGMENT vmaddr - not from segment with lowest address - - ------ Tagged ld64-66.1 - -2006-11-09 Nick Kledzik - - * src/MachOWriterExecutable.hpp: initialize fModuleInfoAtom to zero - - -2006-11-08 Nick Kledzik - - FSF GCC's libjava doesn't link with Ochre ld64 - * src/MachOReaderRelocatable.hpp: ignore debug_line section if debug_info section is missing or empty - ------ Tagged ld64-66 - -2006-11-07 Nick Kledzik - - SWB: d64-65 does not built usage split-seg dylibs - * src/MachOWriterExecutable.hpp: when prebinding split-seg correctly set r_address fields and on - disk values for external relocations - * unit-tests/test-cases/prebound-split-seg: added test case - - -2006-11-03 Nick Kledzik - - * src/MachOReaderDylib.hpp: don't report dependent libraries if MH_NO_REEXPORTED_DYLIBS bit is set - * src/MachOWriterExecutable.hpp: set MH_NO_REEXPORTED_DYLIBS bit if dylib does not logically re-export any other dylibs - * unit-tests/test-cases/re-export-flag: added test case - * src/machochecker.cpp: validate use of MH_NO_REEXPORTED_DYLIBS - - -2006-11-02 Nick Kledzik - - Mysterious messages from ld64 with MACOSX_DEPLOYMENT_TARGET = 10.5 - * src/MachOWriterExecutable.hpp: kPointerWeakImport is a valid reference type to cross segments - - -2006-11-02 Nick Kledzik - - * src/Options.cpp,h: Add support for -rpath - * src/MachOFileAbstraction.hpp: add macho_rpath_command - * src/MachOWriterExecutable.hpp: add RPathLoadCommandsAtom to create LC_RPATH for each -rpath - - ------ Tagged ld64-65 - -2006-10-30 Nick Kledzik - - x86_64 default stack_addr is wrong - * src/Options.cpp: change default 64-bit stack location when using -stack_size - - -2006-10-30 Nick Kledzik - - dylibs need modules for 10.3 and for ld_classic in Salt - * src/MachOWriterExecutable.hpp: add ModuleInfoLinkEditAtom to create module table stuff - * src/Options.cpp,h: Add needsModuleTable() - * src/MachOFileAbstraction.hpp: Add macho_dylib_module, macho_dylib_reference, and macho_dylib_table_of_contents - - -2006-10-27 Nick Kledzik - - * unit-tests/test-cases/no-uuid/Makefile: add -gstabs+ to be compatible with latest compiler - * unit-tests/test-cases/stabs-coalesce/Makefile: add -gstabs+ to be compatible with latest compiler - - -2006-10-26 Nick Kledzik - - i386 -mdynamic-no-pic switch statement jump table is out of line - * src/MachOWriterExecutable.hpp: for i386 don't check for direct references to weak symbols - - -2006-10-26 Devang Patel - - * src/LLVMReader.hpp: Supply final output file path to optimizer. - -2006-10-26 Devang Patel - - * src/ObjectFile.h: Make setSection* methods virtual. - * src/LLVMReader.hpp: Override setSection* methods. - -2006-10-26 Devang Patel - - * unit-tests/test-case/llvm-integration/a13.h: New. - * unit-tests/test-case/llvm-integration/a13.cc: New. - * unit-tests/test-case/llvm-integration/main13.cc: New. - -2006-10-26 Devang Patel - - * src/options.h, src/options.cpp: Add -save-temps command line option. - * src/LLVMReader.hpp: Use saveTemps option. - - -2006-10-26 Devang Patel - - * src/LLVMReader.hpp: Remove invalid module from memory. - -2006-10-26 Devang Patel - - * src/LLVMReader.hpp: Collect symbol alignment info from LLVM optimizer. - -2006-10-21 Eric Christopher - - * src/ld.cpp (Linker::Linker): Check for LD_NO_CLASSIC_LINKER before - invoking ld_classic. - * unit-tests/test-cases/relocs-literals/Makefile: Run for -mdynamic-no-pic - and pic. - * unit-tests/test-cases/static-executable/Makefile: Skip for 64-bit. Add - -dead_strip to command line. - ------ Tagged ld64-64.2 - -2006-10-19 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: stop copying LLVMReader.hpp into man1 directory - ------ Tagged ld64-64.1 - -2006-10-19 Nick Kledzik - - ld64-63.1 erroneously coalesces an empty string with a non-empty string - * src/MachOReaderRelocatable.hpp: rework cstring parsing to not assume all strings are start - at section alignment boundaries, and when coalescing empty strings always use one with greatest - alignment requirement - * src/MachOWriterExecutable.hpp: in -r mode, don't pad end of cstring section - * src/ObjectFile.h: correctly name leadingZeros() as trailingZeros() - * src/ld.cpp: leadingZeros() --> trailingZeros() - - -2006-10-18 Eric Christopher - - * unit-tests/test-cases/read-only-relocs/Makefile: Skip for x86_64. - * unit-tests/test-cases/llvm-integration/Makefile: Skip if llvm isn't - present. - -2006-10-18 Nick Kledzik - - ld64 change required to go with assembler cstring change - ld64 should error when a local relocation references an address outside its section - * src/MachOReaderRelocatable.hpp: for x86_64 in order to work with local or external relocations to cstrings - change parser to allow atoms with a pending name that is resolved after references are instantiated. - Make direct references to kRegularDefinition atoms. - * src/MachOWriterExecutable.hpp: in -r mode for x86_64 generate L* labels for cstrings and use external relocations - * unit-tests/test-cases/relocs-literals/test.c: add two cases of cstring literal plus addend - - -2006-10-06 Nick Kledzik - - check MACOSX_DEPLOYMENT_TARGET if -macosx_version_min is not used - * src/Options.cpp: if -macosx_version_min is not used, check MACOSX_DEPLOYMENT_TARGET, if - that is unused, default to 10.5 - ------ Tagged ld64-64 - -2006-10-06 Nick Kledzik - - crash in ppc64 program - bl to saveFP, but saveFP is too far away? - * src/MachOWriterExecutable.hpp: in addPPCBranchIslands(), properly account for growth of __text - - -2006-10-06 Nick Kledzik - - Linker-defined alias converts reference into definition and generates error. - * src/MachOReaderRelocatable.hpp: only alias symbols actually in the symbol table - - -2006-10-06 Nick Kledzik - - * unit-tests/test-cases/dwarf-debug-notes/Makefile: crt1.o no longer has stabs, so don't need to strip it - * unit-tests/test-cases/dwarf-debug-notes-r/Makefile: crt1.o no longer has stabs, so don't need to strip it - - -2006-10-06 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: rework dwarf line parsing to fix warnings that starting - showing up with gcc-5421 - - -2006-10-05 Eric Christopher - - ld64 needs to support libtool options - * src/Options.cpp (Options::parse): Add -noall_load, -install_name, - -current_version and -compatibility_version. - -2006-10-03 Eric Christopher - - * src/Options.cpp (Options::gotoClassicLinker): Use execvp - to call ld_classic. - -2006-10-03 Eric Christopher - - * unit-tests/test-cases/tentative-to-real/Makefile: Clean up after tests. - -2006-10-03 Eric Christopher - - * unit-tests/include/common.makefile (VALID_ARCHS): Add x86_64. - (OTOOL): Remove munging based on ARCH. - -2006-09-29 Nick Kledzik - - problem merging .o files built with and without -fno-common - src/Options.*: make MakeTentativeDefinitionsReal a reader option - src/ObjectFile.h: make MakeTentativeDefinitionsReal a reader option - src/MachOWriterExecutable.hpp: make MakeTentativeDefinitionsReal a reader option - src/MachOReaderRelocatable.hpp: only assign a section name of __common to - tentative defintions when making a final linked image - - -2006-09-28 Nick Kledzik - - src/Options.h/.cpp: add support for -segaddr option - src/MachOWriterExecutable.hpp: In Writer::assignFileOffsets(), use -segaddr info - - -2006-09-28 Nick Kledzik - - Emit new CPU subtypes for ppc64 and x86-64 when targeting 10.5 or later - src/MachOWriterExecutable.hpp: set high bit of cpusubtype of 64-bit main executables when targeting 10.5 or later - - -2006-09-28 Devang Patel - - Add LLVM LTO support - src/LLVMReader.hpp: New file. - src/ld.cpp: Add optimization phase. Use LLVM LTO. - unit-tests/test-cases/llvm-integration: New tests. - -2006-09-27 Nick Kledzik - - ld64.xcodeproj/project.pbxproj: remove accidental install of source file into man1 - - -2006-09-25 Nick Kledzik - - src/Architectures.hpp: add kPointerDiff16 for ppc and ppc64 - src/MachOReaderRelocatable.hpp: support kPointerDiff16 - src/MachOWriterExecutable.hpp: support kPointerDiff16 - ------ Tagged ld64-63.1 - -2006-09-22 Nick Kledzik - - src/MachOWriterExecutable.hpp: include stubs in LC_SEGMENT_SPLIT_INFO - - -2006-09-21 Nick Kledzik - - src/Options.cpp: disable split-seg dylibs for 64-bit architectures - - -2006-09-19 Nick Kledzik - - src/MachOReaderRelocatable.hpp: rework __cstring parsing to better handle mixed alignment cstrings - src/MachOWriterExecutable.hpp: in -r mode, make all __cstrings aligned to section alignment - - -2006-09-19 Nick Kledzik - - src/MachOWriterExecutable.hpp: rework encoding of LC_SEGMENT_SPLIT_INFO - - -2006-09-19 Nick Kledzik - - src/Options.cpp: check for -search_paths_first in first pass - - ------ Tagged ld64-63 - -2006-09-15 Nick Kledzik - - src/Options.cpp: since the ld64 will repeatedly search an archive, and some project list archives - multiple times on command line to work with traditional linkers, automatically ignore duplicate libraries - unit-tests/test-cases/archive-duplicate: added test case - - -2006-09-15 Nick Kledzik - - src/Options.cpp: support -r -static - src/MachOWriterExecutable.hpp: support -r -static an don't generate LC_DYSYMTAB - - -2006-09-14 Nick Kledzik - - src/MachOWriterExecutable.hpp: in -r mode references to weak symbols should not create external relocations - as that can cause nmedit to errror later. - - -2006-09-13 Nick Kledzik - - ld64: Handle .objc_class_name exports specially - src/Options.cpp: add hack so that .objc_class_name_XXX in -exported_symbols_list imples _OBJC_CLASS_$_XXX - src/ld.cpp: add hack to supporess errors about .objc_class_name_XXX or _OBJC_CLASS_$_XXX being undefined - - -2006-09-12 Nick Kledzik - - Support -prebind when targeting ppc and OS < 10.4 - src/Options.h: add splitSeg() and baseWritableAddress() - src/Options.cpp: Add support for -seg_addr_table and LD_SEG_ADDR_TABLE, and -prebind and LD_PREBIND. - src/src/MachOWriterExecutable.hpp: support split-seg and canonical prebound files to be generated - - -2006-09-11 Nick Kledzik - - Linking a dylib or binary from identical binaries should produce the same output - src/MachOWriterExecutable.hpp: set the timestamps to be constant - - -2006-09-11 Nick Kledzik - - Linker support for ordering all sections and symbols - src/Options.cpp: Add -order_file_statistics. Allow architecture prefixes in order files - src/ld.cpp: Use fOptions.printOrderFileStatistics() - - -2006-09-11 Nick Kledzik - - Support -sectorder - unit-tests/test-cases/order_file: added test case - src/ld.cpp: Implement order file support in Linker::sortAtoms() - src/Options.h: add Options.orderedSymbols() - src/Options.cpp: add parseOrderFile(), implement -order_file - - -2006-09-07 Nick Kledzik - - need -i for 64-bit (or equivalent) - Support -i for aliasing exported symbols - unit-tests/test-cases/alias-objects: added - unit-tests/test-cases/alias-command-line: added - src/ObjectFile.h: Added Atom::getOrdinal() as new way to sort atoms. Added ReaderOptions.fAliases - src/MachOReaderRelocatable.hpp: Added SymbolAliasAtom to handle multiple symbols to same address - src/MachOReaderArchive.hpp: implement Atom::getOrdinal() to space out atom ordinals across member objects - src/Options.cpp: support -i, -alias, -alias_list. Move search of /Network/Library/Frameworks to after /System/Library/Frameworks - src/MachOWriterExecutable.hpp: pad out seg_info data. Implement getOrdinal(). - src/ObjectDump.cpp: call constructors directly instead of using make() wrapper - - -2006-09-01 Nick Kledzik - - Need the ability to tag libraries/plug-ins with security attributes - src/MachOReaderDylib.hpp: add warning if using -root_safe or -setuid_safe and link against dylib that is not - src/ObjectFile.h: add ReaderOption fRootSafe and fSetuidSafe - src/Options.cpp: handle -root_safe or -setuid_safe command line options - src/MachOWriterExecutable.hpp: set MH_ROOT_SAFE and MH_SETUID_SAFE flags - - -2006-08-31 Nick Kledzik - - src/ld.cpp: Add Linker::processDTrace() for processing dtrace static probes - src/OpaqueSection.hpp: renamed, add symbol name, add ability to add references - ld64.xcodeproj/project.pbxproj: remove SectCreate.cpp, add OpaqueSection.hpp - - -2006-08-28 Nick Kledzik - - Add convention for removing symbols at link time - Assembler -L option causes ld64 to split stubs - unit-tests/test-cases/special-labels: added test case - src/MachOReaderRelocatable.hpp: ignore L* labels, make l* labels as kSymbolTableNotIn - - -2006-08-28 Nick Kledzik - - src/lObjectFile.h: refactor isTargetUnbound() into getTargetBinding() - src/ld.cpp: create __dof section in final linked images from dtrace static probes - src/Architectures.hpp: add kDtraceProbe - src/Options.h/cpp: Add support for -dtrace - src/machochecker.cpp: support LC_SEGMENT_SPLIT_INFO - src/MachOWriterExecutable.hpp: support kDtraceProbe - src/MachOReaderRelocatable.hpp: suppport kDtraceProbe - - -2006-08-25 Nick Kledzik - - generate LC_SEGMENT_SPLIT_INFO for 10.5 or later dylibs - src/Options.h&.cpp: implement sharedRegionEligible() to control when LC_SEGMENT_SPLIT_INFO is added - src/MachOFileAbstraction.hpp: add macho_linkedit_data_command - src/MachOWriterExecutable.hpp: generate LC_SEGMENT_SPLIT_INFO load command and linkedit content - ------ Tagged ld64-62 - -2006-08-15 Nick Kledzik - - wrong error message when symbol is found in unused indirect library - src/ld.cpp: remove indirect libraries if they are not re-exported - unit-tests/test-cases/indirect-dylib: added test case - - -2006-08-15 Nick Kledzik - - alignment needs to be richer - src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info - src/ld.cpp: modify SymbolTable::add() to work with new Alignment type - src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers() - src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address - src/ObjectDump.cpp: print richer Alignment info - unit-tests/test-cases/align-modulus: added test case - - -2006-08-11 Nick Kledzik - - remove OPEN_SOURCE conditionals around x86_64 support - - -2006-07-31 Nick Kledzik - - ld64 while linking cc1 [ when dead_strip is ON] - src/ld.cpp: Add ivar fAtomsWithUnresolvedReferences to track atoms not initially resolvable - unit-tests/test-cases/dead_strip-archive: added test case - - -2006-07-31 Nick Kledzik - - x86_64: instructions with immediate and rip-relative operands need to use new relocation types - src/MachOWriterExecutable.hpp: generate new reloc types in -r mode - src/MachOReaderRelocatable.hpp: parse new reloc types - unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type - - -2006-07-18 Nick Kledzik - - src/MachOReaderRelocatable.hpp: suppress warning about dwarf info parsing for one benign no-op case - the compiler emits when there are not functions in the __text section - - -2006-07-17 Nick Kledzik - - faster debug note generation - src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a - pass per .o file. Added timing info for collectDebugInfo() to -print_statistics - unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r - unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order - - -2006-07-17 Nick Kledzik - - ld64 VSIZE is 1.18GB when building Finder ppc64 - src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped - ------ Tagged ld64-61.1 - -2006-07-11 Nick Kledzik - - ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name - src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message - -2006-07-11 Nick Kledzik - - If -arch is missing, rollover to ld_classic does not happen - src/Options.h: make gotoClassicLinker() public - src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386 - ------ Tagged ld64-61 - -2006-06-29 Nick Kledzik - - ld64 should be renamed to ld - src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen - src/ld.cpp: alter version string - ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64 - doc/man/man1/ld.1: added - ------ Tagged ld64-60 - -2006-06-28 Nick Kledzik - - Can't link large ppc64 program: ld64 says "bl out of range" - MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions - and properly chain together branch islands - MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references - only when done - -2006-06-28 Nick Kledzik - - MySQL-36 fails to build with ld64-59 - src/MachOReaderRelocatable.hpp: back out fix for 4585335 - src/MachOWriterExecutable.hpp: back out fix for 4585335 - -2006-06-27 Nick Kledzik - - src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how - dwarf debug notes are formed. - -2006-06-23 Nick Kledzik - - - - ld64 doesn't support variant linking -framework fw,_debug - src/Options.cpp: enhance findFramework() to support suffixes - ------ Tagged ld64-59 - -2006-06-22 Nick Kledzik - - ld64 lost DWARF debug notes - src/MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later - unit-tests/test-cases/dwarf-debug-notes-r: added test case - -2006-06-21 Nick Kledzik - - python 64-bit address miscalculation - src/MachOReaderRelocatable.hpp: change getTargetOffset() to sign extend the 32-bit value to 64-bits - -2006-06-21 Nick Kledzik - - ld64 seems to offset things incorrectly when using -r - src/MachOWriterExecutable.hpp: in -r mode, virtual sections should not increment address - - ------ Tagged ld64-58 - -2006-06-16 Nick Kledzik - - src/rebase.cpp: fix page alignment problem - src/rebase.cpp: fix endianess problem with local non-lazy pointers - -2006-06-15 Nick Kledzik - - src/rebase.cpp: fix to build in CurryWeed - ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed - -2006-06-15 Nick Kledzik - - Support .objc_class_name_* symbols - src/ObjectFile.h: Add kSymbolTableInAsAbsolute - src/MachOReaderRelocatable.hpp: synthesize references to required objc classes - src/MachOWriterExecutable.hpp: write objc_class_name as absolute symbol - unit-tests/test-cases/objc-references: added - -2006-06-15 Nick Kledzik - - SECTION_ATTRIBUTES unset in ppc64 mach-o header - src/MachOWriterExecutable.hpp: add section attribute for sections with code - -2006-06-15 Nick Kledzik - - ld64 bogus duplicate symbol name linking GNU libobjc - src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes - -2006-06-15 Nick Kledzik - - x86_64: ".align" directive not honored - src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size - -2006-06-14 Nick Kledzik - - jump table into middle of weak symbol causes error - src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols - src/MachOWriterExecutable.hpp: do not error on absolute references to interior of weak symbols - -2006-06-13 Nick Kledzik - - src/Options.cpp: allow -image_base as an alias for -seg1addr - -2006-06-13 Nick Kledzik - - implement -d - src/Options.h: add fMakeTentativeDefinitionsReal - src/Options.cpp: set fMakeTentativeDefinitionsReal if -d option is found - src/MachOWriterExecutable.hpp: turn tentative into real definition if makeTentativeDefinitionsReal - unit-tests/test-cases/btentative-to-real: added test case - -2006-06-13 Nick Kledzik - - implement -bundle_loader - src/Options.h: add fBundleLoader bit to DynamicLibraryOptions - src/Options.cpp: handle -bundle_loader - src/ld.cpp: pass fBundleLoader bit to MachOReaderDylib - src/MachOReaderDylib.hpp: support reading MH_EXECUTE files if fBundleLoader is set - src/MachOWriterExecutable.hpp: set bundle loader ordinal as EXECUTABLE_ORDINAL - unit-tests/test-cases/bundle_loader: added test case - -2006-06-12 Nick Kledzik - - -syslibroot can cause "can't find ordinal for imported" error - src/MachOReaderDylib.hpp: in Reader::reExports() compare install path in addition to load path - - -2006-06-10 Nick Kledzik - - Need rebasing tool - src/rebase.cpp: added - unit-tests/test-cases/rebase-basic: added - doc/man/man1/rebase.1: added - ld64.xcodeproj/project.pbxproj: added rebase target. changed all targets to build with dwarf - - -2006-06-10 Nick Kledzik - - src/machochecker.cpp: add some ppc reloc sanity checking - ------ Tagged ld64-57 - -2006-06-06 Nick Kledzik - - ld64 is not adding a final '/' char on the initial directory-name SO stab debug map entry - ld.cpp: Change Linker::synthesizeStabs() to assure directory SO always has a trailing slash - unit-tests/test-cases/dwarf-debug-notes/expected-stabs: update with trailing / - -2006-06-06 Nick Kledzik - - -sectcreate of a 0-byte section fails - MachOWriterExecutable.cpp: Don't error out on zero length segments - MachOWriterExecutable.cpp: For ppc64 reloc base address is the first writable segment iff - there is a writable segment >4GB from base address - -2006-06-04 Eric Christopher - - Radar 4560240 - Radar 3964999 - * src/ld.cpp (createReader): Fixed error message. - (resolve): Ditto. - (resolveFrom): Ditto. - (checkUndefines): Ditto. - ------ Tagged ld64-56 - -2006-05-23 Nick Kledzik - - No debug notes for ObjC methods when linking with ld64 - ld.cpp: don't limit debug notes to functions starting with underscore - -2006-05-22 Nick Kledzik - - ld64 spends much time in mach_o::relocatable::Reader::findAtomByName - * src/MachOReaderRelocatable.hpp: add makeReferenceToSymbol() so that x86_64 does not need to do by-name lookups - -2006-05-22 Nick Kledzik - - remove inferring warning - * ld.cpp: Remove "inferring" warning. If a link failed and now arch was specifed add which arch was - inferred to error message - -2006-05-19 Nick Kledzik - - ld64 does not honor -arch_multiple - * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message - -2006-05-19 Nick Kledzik - - Support S_16BYTE_LITERALS section types - * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS - * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS - -2006-05-19 Nick Kledzik - - "warning can't parse dwarf compilation unit info" warnings building debug - * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing - ------ Tagged ld64-55 - -2006-05-18 Nick Kledzik - - Default the pagezero size to 4GB for x86-64 - * src/Options.cpp: Chnage default the pagezero size to 4GB for x86-64 - -2006-05-18 Nick Kledzik - - x86_64 CarbonCore fails to link with "atom not found in symbolIndex" - * src/MachOWriterExecutable.hpp: in buildObjectFileFixups() don't call addObjectRelocs() on kNoFixUp references - -2006-05-18 Nick Kledzik - - ld64: .section defaults to read-only - * src/MachOReaderRelocatable.hpp: default unknown segments to r/w - -2006-05-18 Nick Kledzik - - -fvisibility=hidden causes crashes for x86_64 - * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions - -2006-05-12 Nick Kledzik - - * src/Architectures.hpp: add x86::kAbsolute32 - * src/MachOReaderRelocatable.hpp: generate x86::kAbsolute32 for mdynamic-no-pic instructions - * src/MachOWriterExecutable.hpp: process x86::kAbsolute32 reference kind - ------ Tagged ld64-54 - -2006-05-11 Nick Kledzik - - CF-393 failes to link for x86_64 - * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer::fixUpReferenceRelocatable - -2006-05-11 Nick Kledzik - - warning arch x86_64 not found using i386 - * src/ld.cpp: remove hack to allow x86_64 to link against i386 dylibs - - -2006-05-10 Nick Kledzik - - x86_64: .objc_class_name symbol names scrambled - * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections - - -2006-05-08 Nick Kledzik - - Support -dead_strip - * src/Options.h/cpp: implement -why_load and -why_live. Enable -dead_strip. - * src/MachOReaderArchive.hpp: implement -why_load - * src/MachOReaderRelocatable.hpp: suppress GCC_except_table* symbols in final output - * src/ld.cpp: implement dead code stripping - * unit-tests/test-cases/dead_strip: added - ------ Tagged ld64-53 - -2006-05-05 Nick Kledzik - - * src/Options.cpp: make 10.4 be minimum OS version for newer architectures - -2006-05-05 Nick Kledzik - - N_SO symbols in 64-bit builds have a zero address for n.n_value - * src/ld.cpp: for SO stabs, associate first and last atom in the SO range - * src/MachOWriterExecutable.hpp: use atom associated with SO stab to set ins n_value - -2006-05-05 Nick Kledzik - - * MachOWriterExecutable.hpp: fix end FUN stab to have length of function - - -2006-05-02 Nick Kledzik - - 64-bit main executables should have 4GB zero page by default - * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64 - 64 bit: apps with -mdynamic-no-pic seg fault when page zero > 4GB - * src/MachOWriterExecutable.cpp: rework pagezero for ppc64 so that if any mdynamic-no-pic code - is found, the code is kept in the low 2GB, and a new segment is create to map away up to 4GB. - -2006-05-02 Nick Kledzik - - * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack - overlaps shared region - ------ Tagged ld64-52.1 - -2006-05-01 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle - the __data section too. - ------ Tagged ld64-52 - -2006-04-28 Nick Kledzik - - 64-bit: 9A152 TextEdit crashes in dlopen on bring-up - * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection - -2006-04-28 Nick Kledzik - - 64 Bit: Development build of ppc64 TextEdit gets confused about static variables - * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol - - - -2006-04-21 Nick Kledzik - - * src/Options.cpp: fix default address for ppc64 custom stack - * src/MachOWriterExecutable.cpp: fix set up of ppc64 custom stack - - -2006-04-14 Nick Kledzik - - * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name - ------ Tagged ld64-51.1 - -2006-04-13 Nick Kledzik - - 64-bit: 9A152 TextEdit crashes in dlopen on bring-up - * src/MachOReaderRelocatable.hpp: when detecting anonymous non-lazy-pointers disqualify data - that points to static or global symbols - * src/ld.cpp: print version of ld64 in error messages - - ------ Tagged ld64-51 - -2006-04-11 Nick Kledzik - - exported symbols not properly stripped - * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope() - -2006-03-31 Nick Kledzik - - ld64 fails when linking debug ppc64 HIToolbox - * src/MachOReaderRelocatable.hpp: handle anonymous non-lazy pointers encoded with local relocations - * src/MachOWriterExecutable.hpp: in -r mode, only generated INDIRECT_SYMBOL_LOCAL for non-lazy targets that - - -2006-03-31 Nick Kledzik - - ld64 should remove generated file if link errors out - * src/MachOWriterExecutable.hpp: catch exceptions in Writer::write(), delete output file, and rethrow - - ------ Tagged ld64-50 - - -2006-03-29 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols - * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space - -2006-03-28 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info - ------ Tagged ld64-49.1 - -2006-03-25 Nick Kledzik - - * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB - ------ Tagged ld64-49 - -2006-03-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: dyld is allowed to have synthesized non-lazy pointers - ld64 is after processing bad GSYM stabs - * src/MachOReaderRelocatable.hpp: if a GSYM is found that does not match any data symbol, suppress it - -2006-03-23 Nick Kledzik - - * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceFinal() fix when x86::kPointer is for an - external relocation - -2006-03-23 Nick Kledzik - - * src/Options.cpp: change macosx-min-version to default to a per-architecture setting - add warning if -pagezero_size is not page aligned - * src/MachOWriterExecutable.hpp: properly handle external relocations for ppc64 with 4GB pagezero - * src/machochecker.cpp: sanity check relocation records - ------ Tagged ld64-48 - -2006-03-21 Nick Kledzik - - 64bit: passing function pointer to another function passes the wrong function address - * src/MachOReaderRelocatable.hpp: when processing a non-lazy pointer to a static function, don't accidentally - match it to a STAB symbol. - -2006-03-21 Nick Kledzik - - .eh symbols make up 13% of libstdc++'s stripped binary size - * src/ObjectFile.h: add ReaderOptions.fForFinalLinkedImage - * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage - * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image - -2006-03-21 Nick Kledzik - - ld64 does not parse optional second argument to -filelist - * unit-tests/test-cases/filelist: added - * src/Options.cpp: in Options::loadFileList() handle comma option - - ------ Tagged ld64-47.1 - - ------ Tagged ld64-47 - - ------ Tagged ld64-46 - -2006-03-10 Nick Kledzik - - ld64 should figure out architecture from .o files - * unit-tests/test-cases/auto-arch: added - * src/ld.cpp: added Linker::inferArchitecture() to scan .o files are infer architecture to link - * src/MachOReaderArchive.hpp: enhanced validFile() to look deeper into archive and really valdate - * src/MachOWriterExecutable.hpp: stop using fOptions.architecture() - * src/Options.cpp: stop defaulting to ppc64 - - -2006-03-09 Nick Kledzik - - Need "intentionally left blank" dylib stubs - * unit-tests/include/common.makefile: add VALID_ARCHS - * unit-tests/run-all-unit-tests: set up VALID_ARCHS - * unit-tests/test-cases/blank-stubs: add test case - * src/ld.cpp: in addDylib(), detect and ignore blank stubs - * src/MachOReaderDylib.hpp: in constructor, handle blank stubs - -2006-03-09 Nick Kledzik - - crash in stub with 2GB pagezero - * src/MachOWriterExecutable.hpp: StubAtom can't be no-pic if a large zero-page is used - -2006-03-06 Nick Kledzik - - * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two - ------ Tagged ld64-45 - - -2006-03-06 Nick Kledzik - - LP64/9A122: ld64: hang when trying to link DiscRecording framework - * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion - - ------ Tagged ld64-44 - -2006-03-04 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix again test for detection of anonymous non-lazy-pointer. - Error out if .o file contains old __DWARFA style dwarf. - -2006-03-02 Nick Kledzik - - * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. - ------ Tagged ld64-43 - - -2006-03-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: tighten detection of anonymous non-lazy-pointer - ------ Tagged ld64-42 - -2006-02-28 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment - -2006-02-28 Nick Kledzik - - SWB: ld64-37 (can't resolve symbol ___dso_handle) - * src/MachOWriterExecutable.hpp: add class DsoHandleAtom - -2006-02-28 Nick Kledzik - - * unit-tests/test-cases/literals-coalesce-alignment: added test case - * src/ld.cpp: when coalescing strings pick one with greater alignment - ld64: CG link failed because lo14 reference to anonymous non-lazy-pointer not aligned - * unit-tests/test-cases/relocs-c/test.c: tweak to fail like 4458660 - * src/MachOReaderRelocatable.hpp: detect anonymous non-lazy-pointer and transform into real non-lazy-pointers - ------ Tagged ld64-41 - -2006-02-24 Nick Kledzik - - * src/Options.cpp: Warning about -no_dead_strip_inits_and_terms and -i options. - Fix -weak-l option. - ------ Tagged ld64-40 - -2006-02-24 Nick Kledzik - - Leopard9A113: ppc64 libstdc++.dylib initializer crashes in pthread_once - * unit-tests/test-cases/multiple-entry-points: added - * src/MachOReaderRelocatable.hpp: make sure that if there are multiple symbols with the same - address, that we properly make zero length atoms for all but last symbol - -2006-02-24 Nick Kledzik - - * src/Options.cpp: ld64 doesn't realpath(3) B&I tracing paths - -2006-02-24 Nick Kledzik - - * src/Options.cpp: 9A110: ld64 can't deal with section names >16 chars - -2006-02-23 Nick Kledzik - - * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations - * src/Options.cpp: use vector.reserve() to minimize re-allocations - * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations - * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations - * src/ld.cpp: use vector.reserve() to minimize re-allocations - -2006-02-23 Nick Kledzik - - ld64 creates corrupt executables (and has malloc errors) with -headerpad option - * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom::setSize() to update fLargestAtomSize - * unit-tests/test-cases/header-pad: added - -2006-02-23 Nick Kledzik - - ld64 creates invalid static executables - * src/MachOWriterExecutable.hpp: Change MachHeaderAtom::copyRawContent() to create correct header - for static executables. Change SymbolTableLoadCommandsAtom to skip LC_DYSYMTAB for static executables - * src/machochecker.cpp: Add tests that static executables are well formed - * unit-tests/test-cases/static-executable: added - -2006-02-22 Nick Kledzik - - * src/Options.cpp: chnage printf on unknown arg to a throw - ------ Tagged ld64-39 - -2006-02-20 Nick Kledzik - - * unit-tests/test-cases/read-only-relocs: added new test case - * src/MachOWriterExecutable.hpp: detect and error on relocs in read-only sections - * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs - -2006-02-20 Nick Kledzik - - * unit-tests/test-cases/stabs-coalesce: added new test case - * src/ld.cpp.hpp: in collectStabs removed unused stabs - ------ Tagged ld64-38 - -2006-02-17 Nick Kledzik - - * src/MachOWriterExecutable.hpp: set correct n_sect field of stabs - -2006-02-15 Nick Kledzik - - * src/MachOReaderArchive.hpp: with -all_load skip over both kinds of SYMDEFs - * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case - ------ Tagged ld64-37 - -2006-02-13 Eric Christopher - - * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. - Adjust whitespace. - -2006-02-13 Nick Kledzik - - * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceRelocatable() fix kPCRel32 for external case - -2006-02-13 Nick Kledzik - - * unit-tests/test-cases/zero-fill: added - * src/machochecker.cpp: check that S_ZEROFILL have no file offset - * src/MachOWriterExecutable.hpp: rework assignFileOffsets() to fix rdar://problem/4441145 - -2006-02-12 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix use of first zero-length c-string in .o file - -2006-02-12 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix uninitialized fAlignment - -2006-02-12 Nick Kledzik - - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add pointer-diff cases - * src/Architectures.hpp: make size explicit in ppc/ppc64 kPointerDiff - * src/MachOReaderRelocatable.hpp: don't allow kPointerDiff64 for ppc (just ppc64) - * src/MachOWriterExecutable.cpp: set proper r_length for ld -r of kPointerDiff - ------ Tagged ld64-36 - -2006-02-08 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions - -2006-02-08 Nick Kledzik - - * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section - Keep S_COALESCED attribute for __eh_frame - -2006-02-08 Nick Kledzik - - * src/ld.cpp: Temporarily turn allowable client errors into warnings - * unit-tests/test-cases/allowable-clientMakefile: Temporarily let warnings be ok for above - * src/MachOWriterExecutable.hpp: fix ld -r to not use external relocations for symbols make static - -2006-02-08 Nick Kledzik - - * src/ld.cpp: A sibling in an umbrella can always link with its other siblings - * unit-tests/test-cases/allowable-client: add test case for above - -2006-02-08 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support LOCAL non-lazy pointers to hidden symbols - * src/machochecker.cpp: verify indirect symbol table - * unit-tests/test-cases/private-non-lazy: added test case - -2006-02-07 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix calculation of file offsets in ld -r mode - * src/machochecker.cpp: verify segment file offsets are within file - ------ Tagged ld64-35 - -2006-02-06 Nick Kledzik - - * ld.cpp: allow parent of sub-framework to link - * unit-tests/test-cases/allowable-client/Makefile: added cases for parent and clients of parent - -2006-02-04 Nick Kledzik - - * unit-tests/test-cases/relocs-c/test.c: added some array cases - * src/MachOReaderRelocatable.hpp: factor out makeReferenceToEH() - * src/MachOWriterExecutable.hpp: add initial support for non-lazy pointer synthesis - ------ Tagged ld64-34 - -2006-02-04 Nick Kledzik - - * src/ld.cpp: fix -no_arch_warnings - fix -undefined warning - Do BINCL/EINCL optimization for gfull stabs - Implement "essential symbols" for stabs (-Sp) - Fix allowable clients to only test on direct libraries - * src/MachOReaderRelocatable.hpp: support BINCL/EINCL stabs - -2006-02-03 Nick Kledzik - - * src/machochecker.cpp: add code to check load command alignment - * src/MachOWriterExecutable.hpp: make load command alignment depend on architecture - -2006-02-03 Nick Kledzik - - * unit-tests/test-cases/literals-coalesce: added - * src/MachOReaderRelocatable.hpp: assure all targets of low14 ppc relocs are at least 4-byte alignmented - ------ Tagged ld64-33 - -2006-02-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals - * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 - ------ Tagged ld64-32 - -2006-02-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms - -2006-02-02 Nick Kledzik - - * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one - * unit-tests/test-cases/archive-weak: add test case for above - * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself - * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list - -2006-02-01 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 - -2006-02-01 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms - ------ Tagged ld64-31 - -2006-02-01 Eric Christopher - - * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling... - * unit-tests/include/common.makefile: ... here. - * unit-tests/bin/fail-if-stdin.pl: New. - * unit-tests/test-cases/no-uuid: Ditto. - * src/ld.cpp (Linker::) Add fCreateUUID. - (::Linker): Initialize. - (::collectStabs): Use. Set if dwarf or we have a UUID already. - (::writeOutput): Pass as argument to Writer::write along with option. - * src/Options.h (Option::emitUUID): Declare. - (Option::fEmitUUID): Ditto. - * src/Options.cpp (Option::emitUUID): New. - (parse): Handle -no_uuid. - * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID. - * src/ExecutableFile.h (Writer::Write): Add createUUID boolean. - * src/MachOWriterExecutable: Add UUID forward declaration. - (fUUIDAtom): New. - (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size - to zero at start. - (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID. - (MachHeaderAtom::copyRawContent): Don't count a load command if its size is - 0. - (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit. - - -2006-01-31 Nick Kledzik - - * unit-tests/test-cases/dwarf-debug-notes : Added - * src/ld.cpp: don't generate debug note for .eh symbols - * src/MachOReaderRelocatable.hpp: make dwarf line info to atom matching faster and better - -2006-01-31 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard - * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers - -2006-01-31 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: better error message for bad relocs - * src/ObjectDump.cpp: add emacs tab settings - * src/SectCreate.h: ditto - * src/SectCreate.cpp: ditto - * src/machochecker.cpp: ditto - * src/ExecutableFile.h: ditto - -2006-01-30 Eric Christopher - - * src/ExecutableFile.h: Indent. - -2006-01-30 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: performance improvements - * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore - -2006-01-30 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above - -2006-01-29 Nick Kledzik - - * unit-tests/test-cases/weak_import: added test case - * src/ld.cpp: move code for weak_import mismatch to writer - * src/ObjectFile.h: remove ImportWeakness methods - * src/MachOReaderDylib.hpp: ditto - * src/SectCreate.cpp: ditto - * src/Architectures.hpp: add new ReferenceKinds for weak_imports - * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds - * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches - -2006-01-29 Nick Kledzik - - * src/Options.cpp: verify -allow_stack_execute is only used on main executables - -2006-01-29 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff - * src/debugline.c: sync with latest dwarf reader from Geoff - -2006-01-27 Eric Christopher - - * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. - -2006-01-27 Eric Christopher - - * src/Options.h (Options): Add hasExecutableStack, fExecutableStack. - * src/Options.cpp (Options::hasExecutableStack): New. - (Options::parse): Parse -allow_stack_execute. - * src/MachOWriterExecutable.hpp (MachHeaderAtom::copyRawContent): - Implement MH_ALLOW_STACK_EXECUTION. - * unit-tests/include/common.makefile (FAIL_IF_EMPTY): New. - * unit-tests/bin/fail-if-no-stdin.pl: New file. - * unit-tests/test-cases/allow-stack-execute: New directory. - -2006-01-27 Nick Kledzik - - * src/MachOFileAbstraction.hpp: rely on latest system headers - * src/MachOWriterExecutable.hpp: fix ppc stubs. - wrote new relocationNeededInFinalLinkedImage() to replace common code - -2006-01-27 Eric Christopher - - * src/ld.cpp (logTraceInfo): New. - (Linker::addArchive): Use. - (Linker::addDylib): Ditto. - * src/ObjectFile (ReaderOptions::fTraceOutputFile): New. - * src/MachOReaderArchive.hpp (Reader::Reader): Move trace - logging to Linker::addArchive. - * src/Options.cpp (parsePreCommandLineEnvironment): Check - LD_PRINT_FILE if tracing dylibs or archives. - -2006-01-26 Nick Kledzik - - * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes - -2006-01-26 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state - -2006-01-26 Nick Kledzik - - Rewrite all stabs processing. - Move sythesize of debug notes into ld.cpp - -2006-01-26 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs - -2006-01-25 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: special case building in Curry - -2006-01-25 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis - -2006-01-24 Eric Christopher - - * src/ld.cpp (Linker::createReaders): Change logging title to XBS. - (Linker::addDylib): Ditto. - * src/MachOReaderArchive.hpp (Reader::Reader): Ditto. - * src/Options.h (fPrintOptions): New. - * src/Options.cpp (Options::Options): Initialize above. - (Options::checkForFile): Change logging title to XBS. - (Options::findFramework): Ditto. - (Options::parse): Add log for options. - (Options::parsePreCommandLineEnvironmentSettings): Add LD_TRACE_ARCHIVES, - LD_TRACE_DYLIBS, and LD_PRINT_OPTIONS. - -2006-01-24 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: better C++ eh parsing - -2006-01-23 Eric Christopher - - * unit-tests/bin/fail-if-exit-zero.pl: New. - * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use. - * unit-tests/allowable-client: New test. - * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib. - * src/Options.h (allowableClients): New. - (clientName): Ditto. - (fAllowableClients): Ditto. - (fClientName): Ditto. - * src/Options.cpp: Implement above. - (parse): Handle -allowable_client and -client_name. - * src/MachOReaderDylib.hpp (getAllowableClients): New. - (fAllowableClients): Ditto. - (Reader): Process LC_SUB_CLIENT load command. - * src/ObjectFile.h (parentUmbrella): New. - (getAllowableClients): New. - * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New. - -2006-01-23 Nick Kledzik - - * unit-tests/test-cases/archive-basic: added - * src/ld.cpp: fix shadowed local variable - * src/FileAbstraction.hpp: ld64 shouldn't inline when building debug - -2006-01-23 Nick Kledzik - - * src/ld.cpp: fix symbol not found error message - * src/MachOReaderDylib.hpp: add logging to hash table - * src/MachOReaderRelocatable.hpp: enable stabs processing. Handle static functions with stubs - handle labeled cstrings. - * src/MachOWriterExecutable.hpp: properly suppress atoms not in symbol table. fix low14 error check. - add StubAtomHelper. - * unit-tests/test-cases/relocs-literals/test.c: add more interesting edge cases - -2006-01-17 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes - -2006-01-16 Nick Kledzik - - * src/debugline.{sh}: added - * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf - * src/MachOWriterExecutable.hpp: fix lazy pointer section - * src/ObjectDump.hpp: Fix conditionalization - * unit-tests/test-cases/dwarf-strip: added - -2006-01-11 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 - * src/ObjectDump.hpp: Support -arch option - -2006-01-10 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix stubs for ppc64 - * src/MachOFileAbstraction.hpp: fix typo for macho_routines - * ld64.xcodeproj/project.pbxproj: add machochecker target - * src/machochecker.cpp: new skeleton for checking mach-o file bit - * unit-tests/: Add support for running machochecker - -2006-01-10 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed - * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime - -2006-01-09 Nick Kledzik - - * track modification time of .o files so that sythesized OSO stab will have it - -2006-01-09 Nick Kledzik - - * src/MachOFileAbstraction.hpp: add macho_uuid_command - * src/MachOWriterExecutable.cpp: add UUID load command to generated files - -2006-01-09 Nick Kledzik - - * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped - * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped - -2006-01-05 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support new relocations - -2006-01-05 Nick Kledzik - - * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB - * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor - -2006-01-05 Nick Kledzik - - refactor: transform Atom::dontStripName() to getSymbolTableInclusion() - * src/ld.cpp: pass dyld_stub_binding_helper to writer - * src/MachOReaderRelocatable.hpp: update synthesized stabs - Ignore stubs and lazy pointers in .o files - Support initializers and terminators - * src/MachOWriterExecutable.hpp: synthesize stubs and lazy pointers as needed - * ld64.xcodeproj/project.pbxproj: change Release target to build with dwarf - -2006-01-03 Eric Christopher - - * src/Options.h (multipleDefinitionsInDylibs): Declare. - (overridingDefinitionInDependentDylib): Ditto. - (warnOnMultipleDefinitionsInObjectFiles): Ditto. - (multiplyDefined): Remove. - (multiplyDefinedUnused): Ditto. - (fMultiplyDefined): Ditto. - (fWarnOnMultiplyDefined): New. - (fMultiplyDefinedDynamic): Ditto. - * src/Options.cpp (Options::Options): Initialize above. - (overridingDefinitionInDependentDylib): New. - (multipleDefinitionsInDylibs): Ditto. - (warnOnMultipleDefinitionsInObjectFiles): Ditto. - (parse): Update comments. Fix parsing of -y option. - Update error message for -dead_strip. Parse above - options. - -2006-01-02 Nick Kledzik - - * Refactor: move Atom::writeContent() to Writer - -2005-12-23 Nick Kledzik - - * Reworked, simplify, and document test harness - * unit-tests/README: Added - -2005-12-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fixes for Objective-C - * unit-tests/test-cases/relocs-objc: Added - -2005-12-22 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair - * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf - -2005-12-21 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Fix parsing of literal sections - * src/MachOWriterExecutable.hpp: Fix writing of literal sections - * unit-tests/test-cases/relocs-literals: Added - -2005-12-15 Eric Christopher - - * src/Options.h (enum Treatment): New. - (enum PICTreatment): Delete. - (enum VersionMin): New. - (prebind): Declare. - (macosxVersionMin): Ditto. - (multiplyDefined): Ditto. - (multiplyDefinedUnused): Ditto. - (setVersionMin): Ditto. - (setPICTreatment): Delete. - (setReadOnlyRelocTreatment): Ditto. - (picTreatment): Adjust return type. - (parseTreatment): New. - (fPrebind): Ditto. - (fVersionMin): Ditto. - (fPICTreatment): Change type. - (fMultiplyDefined): New. - (fMultiplyDefinedUnused): Ditto. - (fLimitUndefinedSymbols): Ditto. - - * src/Options.cpp: Fix whitespace. Add comments on options. - (Options::Options): Add initializers for new variables. - (Options::prebind): New. - (Options::macosxVersionMin): Ditto. - (Options::parseTreatment): Ditto. - (Options::setVersionMin): Ditto. - (Options::setReadOnlyRelocTreatment): Delete. - (Options::setPICTreatment): Ditto. - (Options::Parse): Update for above. Add comments. - -2005-12-15 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Add comments about dwarf - -2005-12-14 Nick Kledzik - - * src/ELFFileAbstraction.hpp: Added - * src/ELFReaderRelocatable.hpp: Added - * Lot of fixes for new architecture - * Added __OPEN_SOURCE__ to "Preprocessor Macros" to disable new architecture support by default - -2005-12-13 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections - * unit-tests/test-cases/dwarf-ignore: added - -2005-12-12 Nick Kledzik - - * Added test harness and three initial tests: - relocs-asm, relocs-c, and hello-world - -2005-12-12 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Massive refactoring: - Now there are three Atom classes, Chopping into Atoms - is done on label boundaries or by knowledge of special - sections, Share lots of ppc/ppc64 code. - Stabs process code is temporarily disabled. - -2005-12-12 Nick Kledzik - - * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort - -2005-12-11 Eric Christopher - - * src/Options.cpp: Reformat. - * src/Options.h: Ditto. - -2005-12-07 Eric Christopher - - * src/MachOReaderRelocatable.hpp (Atom::getAlignment): - When calculating alignment of an Atom, take into account - the alignment from which we pulled the Atom. - -2005-12-06 Nick Kledzik - - * src/Options.cpp src/Options.h: Add design comments - -2005-12-05 Eric Christopher - - * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and - i386 linkers. - -2005-12-05 Eric Christopher - - * ChangeLog: New file. - -2005-12-02 Nick Kledzik - - * src/ObjectFile.h: Add design comments - -2005-11-30 Nick Kledzik - - * Fix uses of __OPEN_SOURCE__ - -2005-11-28 Nick Kledzik - - * Refactor Atom to use getDefinitionKind() - -2005-11-21 Nick Kledzik - - * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode - -2005-11-18 Nick Kledzik - - * x86 tweaks - -2005-11-18 Nick Kledzik - - * src/ObjectDump.cpp: make work with command line arguments - -2005-11-18 Nick Kledzik - - * Massive rework to remove preprocessor conditionals and use templates - -2005-11-14 Nick Kledzik - - * Created new Subversion repository for ld64 from cvs tag ld64-27.2 diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index c910e04..3a84e0a 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -271,6 +271,10 @@ When targeting Mac OS X 10.6 or later, the format of the exported symbol informa make lookups of popular symbols faster. This option is used to pass a file containing a list of the symbols most frequently used by clients of the dynamic library being built. Not all exported symbols need to be listed. +.It Fl no_zero_fill_sections +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. .El .Ss Options when creating a dynamic library (dylib) .Bl -tag diff --git a/ld64/src/ld/Architectures.hpp b/ld64/src/ld/Architectures.hpp index ceb448a..0d141cf 100644 --- a/ld64/src/ld/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -61,7 +61,7 @@ struct x86 enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kPointerDiff16, kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, - kImageOffset32, kPointerDiff24, kSectionOffset24, + kImageOffset32, kPointerDiff24, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; @@ -74,7 +74,7 @@ struct x86_64 kBranchPCRel32, kBranchPCRel32WeakImport, kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, kGOTNoFixUp, - kImageOffset32, kPointerDiff24, kSectionOffset24, + kImageOffset32, kPointerDiff24, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; @@ -83,7 +83,7 @@ struct arm typedef Pointer32 P; enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kReadOnlyPointer, - kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, + kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; diff --git a/ld64/src/ld/LTOReader.hpp b/ld64/src/ld/LTOReader.hpp index 1b8f2c3..75659e1 100644 --- a/ld64/src/ld/LTOReader.hpp +++ b/ld64/src/ld/LTOReader.hpp @@ -358,7 +358,6 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path kind = ObjectFile::Atom::kWeakDefinition; break; case LTO_SYMBOL_DEFINITION_UNDEFINED: - case LTO_SYMBOL_DEFINITION_WEAKUNDEF: kind = ObjectFile::Atom::kExternalDefinition; break; default: @@ -407,7 +406,7 @@ const char* Reader::tripletPrefixForArch(cpu_type_t arch) case CPU_TYPE_X86_64: return "x86_64-"; case CPU_TYPE_ARM: - return "arm"; + return "arm-"; } return ""; } diff --git a/ld64/src/ld/MachOReaderDylib.hpp b/ld64/src/ld/MachOReaderDylib.hpp index 8aea6ef..13faf3d 100644 --- a/ld64/src/ld/MachOReaderDylib.hpp +++ b/ld64/src/ld/MachOReaderDylib.hpp @@ -780,18 +780,13 @@ void Reader::processIndirectLibraries(DylibHander* handler) //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); ((Reader*)child)->setImplicitlyLinked(); } - else if ( child->explicitlyLinked() || child->implicitlyLinked() ) { - //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); - } - else { + else fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } } else { // add all child's symbols to me fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); } } else if ( !fExplictReExportFound ) { diff --git a/ld64/src/ld/MachOReaderRelocatable.hpp b/ld64/src/ld/MachOReaderRelocatable.hpp index e576313..ef8cfb2 100644 --- a/ld64/src/ld/MachOReaderRelocatable.hpp +++ b/ld64/src/ld/MachOReaderRelocatable.hpp @@ -288,6 +288,7 @@ class BaseAtom : public ObjectFile::Atom virtual uint32_t getOrdinal() const { return fOrdinal; } virtual void setOrdinal(uint32_t value) { fOrdinal = value; } virtual const void* getSectionRecord() const = 0; + virtual unsigned int getSectionIndex() const = 0; virtual bool isAlias() const { return false; } virtual uint8_t getLSDAReferenceKind() const { return 0; } virtual uint8_t getPersonalityReferenceKind() const { return 0; } @@ -295,7 +296,6 @@ class BaseAtom : public ObjectFile::Atom virtual ObjectFile::UnwindInfo::iterator beginUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[0] : NULL; } virtual ObjectFile::UnwindInfo::iterator endUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[1] : NULL; } virtual ObjectFile::Reference* getLSDA(); - virtual ObjectFile::Reference* getFDE(); virtual Atom* getPersonalityPointer(); virtual void setCompactUnwindEncoding(uint64_t ehAtomAddress); @@ -320,19 +320,6 @@ ObjectFile::Reference* BaseAtom::getLSDA() return NULL; } -ObjectFile::Reference* BaseAtom::getFDE() -{ - const uint8_t groupKind = this->getLSDAReferenceKind(); - const std::vector& refs = this->getReferences(); - for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kCFIType) ) { - return ref; - } - } - return NULL; -} - ObjectFile::Atom* BaseAtom::getPersonalityPointer() { const uint8_t personalityKind = this->getPersonalityReferenceKind(); @@ -397,21 +384,36 @@ class BaseAtomSorter else if ( rightAlias ) { return false; } - else { - // they must be tentative defintions - switch ( left->getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - // sort tentative definitions by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - case ObjectFile::Atom::kAbsoluteSymbol: - // sort absolute symbols with same address by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - default: - // hack for rdar://problem/5102873 - if ( !left->isZeroFill() || !right->isZeroFill() ) - warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); - break; - } + // one might be a section start or end label + switch ( left->getContentType() ) { + case ObjectFile::Atom::kSectionStart: + return true; + case ObjectFile::Atom::kSectionEnd: + return false; + default: + break; + } + switch ( right->getContentType() ) { + case ObjectFile::Atom::kSectionStart: + return false; + case ObjectFile::Atom::kSectionEnd: + return true; + default: + break; + } + // they could be tentative defintions + switch ( left->getDefinitionKind() ) { + case ObjectFile::Atom::kTentativeDefinition: + // sort tentative definitions by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + case ObjectFile::Atom::kAbsoluteSymbol: + // sort absolute symbols with same address by name + return ( strcmp(left->getName(), right->getName()) < 0 ); + default: + // hack for rdar://problem/5102873 + if ( !left->isZeroFill() || !right->isZeroFill() ) + warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); + break; } } return false; @@ -439,7 +441,7 @@ class SymbolAtom : public BaseAtom virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } virtual bool dontDeadStrip() const; - virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } + virtual bool isZeroFill() const; virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } virtual uint64_t getSize() const { return fSize; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } @@ -458,6 +460,7 @@ class SymbolAtom : public BaseAtom virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return fAddress; } virtual const void* getSectionRecord() const { return (const void*)fSection; } + virtual unsigned int getSectionIndex() const { return 1 + (fSection - fOwner.fSectionsStart); } virtual uint8_t getLSDAReferenceKind() const; virtual uint8_t getPersonalityReferenceKind() const; virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); @@ -643,6 +646,12 @@ ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const return *((ObjectFile::Atom*)NULL); } +template +bool SymbolAtom::isZeroFill() const +{ + return ( ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL) && fOwner.fOptions.fOptimizeZeroFill ); +} + class Beyond { @@ -717,6 +726,7 @@ class SymbolAliasAtom : public BaseAtom virtual const ObjectFile::ReaderOptions& getOptions() const { return fAliasOf.getOptions(); } virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } + virtual unsigned int getSectionIndex() const { return fAliasOf.getSectionIndex(); } virtual bool isAlias() const { return true; } protected: @@ -778,7 +788,7 @@ class TentativeAtom : public BaseAtom virtual const char* getDisplayName() const { return getName(); } virtual ObjectFile::Atom::Scope getScope() const { return fScope; } virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } - virtual bool isZeroFill() const { return true; } + virtual bool isZeroFill() const { return fOwner.fOptions.fOptimizeZeroFill; } virtual bool isThumb() const { return false; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } @@ -800,6 +810,7 @@ class TentativeAtom : public BaseAtom virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } virtual const void* getSectionRecord() const { return NULL; } + virtual unsigned int getSectionIndex() const { return 0; } protected: typedef typename A::P P; @@ -912,10 +923,11 @@ class AnonymousAtom : public BaseAtom virtual void setSize(uint64_t size) { fSize = size; } virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info); + virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); } virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return fAddress; } virtual const void* getSectionRecord() const { return (const void*)fSection; } + virtual unsigned int getSectionIndex() const { return fSectionIndex; } BaseAtom* redirectTo() { return fRedirect; } bool isWeakImportStub() { return fWeakImportStub; } void resolveName(); @@ -952,6 +964,7 @@ class AnonymousAtom : public BaseAtom ObjectFile::Atom::Scope fScope; ObjectFile::Atom::DefinitionKind fKind; ObjectFile::Atom::ContentType fType; + unsigned int fSectionIndex; }; template @@ -959,7 +972,7 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition), - fType(ObjectFile::Atom::kUnclassifiedType) + fType(ObjectFile::Atom::kUnclassifiedType), fSectionIndex(1 + (section - owner.fSectionsStart)) { fSegment = new Segment(fSection); fRedirect = this; @@ -1034,7 +1047,7 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio case S_CSTRING_LITERALS: { const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - if ( (strcmp(fSection->sectname(), "__cstring") == 0) && (strcmp(section->segname(), "__TEXT") == 0) ) + if ( strcmp(fSection->sectname(), "__cstring") == 0 ) asprintf((char**)&fSynthesizedName, "cstring=%s", str); else asprintf((char**)&fSynthesizedName, "cstring%s%s=%s", fSection->segname(), fSection->sectname(), str); @@ -1122,16 +1135,6 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: { - // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when - // generating the new compressed LINKEDIT format - if ( (type == S_NON_LAZY_SYMBOL_POINTERS) && fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { - macho_section

* dummySection = new macho_section

(*fSection); - dummySection->set_segname("__DATA"); - dummySection->set_sectname("__nl_symbol_ptr"); - fSection = dummySection; - fSegment = new Segment(fSection); - } - fDontDeadStrip = false; fScope = ObjectFile::Atom::scopeLinkageUnit; uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); @@ -1187,6 +1190,16 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio strcat(str, "$non_lazy_ptr"); fSynthesizedName = str; + // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when + // generating the new compressed LINKEDIT format + if ( fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { + macho_section

* dummySection = new macho_section

(*fSection); + dummySection->set_segname("__DATA"); + dummySection->set_sectname("__nl_symbol_ptr"); + fSection = dummySection; + fSegment = new Segment(fSection); + } + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) fKind = ObjectFile::Atom::kWeakDefinition; @@ -1213,13 +1226,6 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } template bool AnonymousAtom::cstringsHaveLabels() { return false; } -template -void AnonymousAtom::addLineInfo(const ObjectFile::LineInfo& info) -{ - // don't warn if line table has entries for stubs - if ( (fSection->flags() & SECTION_TYPE) != S_SYMBOL_STUBS ) - warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); -} template void AnonymousAtom::resolveName() @@ -1324,7 +1330,7 @@ ObjectFile::Atom::Scope AnonymousAtom::getScope() const template bool AnonymousAtom::isZeroFill() const { - return ( (fSection->flags() & SECTION_TYPE) == S_ZEROFILL ); + return ( ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL) && fOwner.fOptions.fOptimizeZeroFill ); } @@ -1434,6 +1440,7 @@ class AbsoluteAtom : public BaseAtom virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } virtual const void* getSectionRecord() const { return NULL; } + virtual unsigned int getSectionIndex() const { return 0; } protected: typedef typename A::P P; @@ -1472,6 +1479,110 @@ AbsoluteAtom::AbsoluteAtom(Reader& owner, const macho_nlist

* symbol) } +// +// An SectionBoundaryAtom represent the start or end of a section +// +template +class SectionBoundaryAtom : public BaseAtom +{ +public: + virtual ObjectFile::Reader* getFile() const { return &fOwner; } + virtual bool getTranslationUnitSource(const char** dir, const char** name) const + { return fOwner.getTranslationUnitSource(dir, name); } + virtual const char* getName() const { return fSymbolName; } + virtual const char* getDisplayName() const { return fDisplayName; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kWeakDefinition; } + virtual ObjectFile::Atom::ContentType getContentType() const { return fStart ? ObjectFile::Atom::kSectionStart : ObjectFile::Atom::kSectionEnd; } + virtual bool isZeroFill() const { return false; } + virtual bool isThumb() const { return false; } + virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + virtual bool dontDeadStrip() const { return false; } + virtual uint64_t getSize() const { return 0; } + virtual std::vector& getReferences() const { return fgNoReferences; } + virtual bool mustRemainInSection() const { return true; } + virtual const char* getSectionName() const { return fSectionName; } + virtual ObjectFile::Segment& getSegment() const { return *fSegment; } + virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } + virtual std::vector* getLineInfo() const { return NULL; } + virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(ObjectFile::Atom::Scope newScope) { } + virtual void setSize(uint64_t size) { } + virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } + virtual void sortReferences() { } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } + virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } + virtual uint64_t getObjectAddress() const { return 0; } + virtual const void* getSectionRecord() const { return NULL; } + virtual unsigned int getSectionIndex() const { return 0; } + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + typedef typename A::ReferenceKinds Kinds; + friend class Reader; + + + class Segment : public ObjectFile::Segment + { + public: + Segment(const char* name, bool r, bool w, bool x): + fName(name), fReadable(r), fWritable(w), fExecutable(x) {} + + virtual const char* getName() const { return fName; } + virtual bool isContentReadable() const { return fReadable; } + virtual bool isContentWritable() const { return fWritable; } + virtual bool isContentExecutable() const { return fExecutable; } + private: + const char* fName; + bool fReadable; + bool fWritable; + bool fExecutable; + }; + + SectionBoundaryAtom(Reader&, bool start, const char* symbolName, const char* segSectName); + virtual ~SectionBoundaryAtom() {} + + Reader& fOwner; + class Segment* fSegment; + const char* fSymbolName; + const char* fSectionName; + const char* fDisplayName; + bool fStart; + static std::vector fgNoReferences; +}; + +template +std::vector SectionBoundaryAtom::fgNoReferences; + +// examples: +// section$start$__DATA$__my +// section$end$__DATA$__my +template +SectionBoundaryAtom::SectionBoundaryAtom(Reader& owner, bool start, const char* symbolName, const char* segSectName) + : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start) +{ + const char* segSectDividor = strrchr(segSectName, '$'); + if ( segSectDividor == NULL ) + throwf("malformed section reference name: %s", symbolName); + fSectionName = segSectDividor + 1; + int segNameLen = segSectDividor - segSectName; + if ( segNameLen > 16 ) + throwf("malformed section reference name: %s", symbolName); + char segName[18]; + strlcpy(segName, segSectName, segNameLen+1); + if ( strcmp(segName, "__TEXT") == 0 ) + fSegment = new Segment("__TEXT", true, false, true); + else if ( strcmp(segName, "__DATA") == 0 ) + fSegment = new Segment("__DATA", true, true, false); + else + fSegment = new Segment(strdup(segName), true, true, false); + + asprintf((char**)&fDisplayName, "%s of section '%s' in segment '%s'", (start ? "start" : "end"), fSectionName, segName); +} + /// @@ -1499,13 +1610,11 @@ class ObjectFileAddressSpace private: const void* mappedAddress(pint_t addr, pint_t* relocTarget=NULL); pint_t relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount); - void buildRelocatedMap(const macho_section

* sect, std::map& map); Reader& fReader; const uint8_t* fMappingStart; const macho_section

* fSectionsStart; const macho_section

* fSectionsEnd; - std::map fEHFrameOffsetToTargetMap; }; @@ -1525,43 +1634,26 @@ const void* ObjectFileAddressSpace::mappedAddress(pint_t addr, pint_t* relocT fMappingStart = (uint8_t*)fReader.fHeader; fSectionsStart = (macho_section

*)((char*)fReader.fSegment + sizeof(macho_segment_command

)); fSectionsEnd = &fSectionsStart[fReader.fSegment->nsects()]; - // find __eh_frame section and build map of relocations for performance - buildRelocatedMap(fReader.fehFrameSection, fEHFrameOffsetToTargetMap); - } - // special case lookups in __eh_frame section to be fast - const macho_section

* ehSect = fReader.fehFrameSection; - if ( (ehSect->addr() <= addr) && (addr < (ehSect->addr()+ehSect->size())) ) { - pint_t offsetOfAddrInSection = addr - ehSect->addr(); - if ( relocTarget != NULL ) { - std::map::iterator pos = fEHFrameOffsetToTargetMap.find(offsetOfAddrInSection); - if ( pos != fEHFrameOffsetToTargetMap.end() ) - *relocTarget = pos->second; - else - *relocTarget = 0; - } - return fMappingStart + ehSect->offset() + offsetOfAddrInSection; } - else { - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { - pint_t offsetOfAddrInSection = addr - sect->addr(); - if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - const uint32_t indirectTableOffset = sect->reserved1(); - const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); - const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); - // return pointer to symbol name which this non-lazy-pointer will point to - if ( relocTarget != NULL ) - *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; - } - else { - if ( relocTarget != NULL ) - *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); - } - return fMappingStart + sect->offset() + offsetOfAddrInSection; + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + pint_t offsetOfAddrInSection = addr - sect->addr(); + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); + const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); + // return pointer to symbol name which this non-lazy-pointer will point to + if ( relocTarget != NULL ) + *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; + } + else { + if ( relocTarget != NULL ) + *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); } + return fMappingStart + sect->offset() + offsetOfAddrInSection; } - throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); } + throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); } @@ -1664,6 +1756,7 @@ class Reader : public ObjectFile::Reader friend class AnonymousAtom; friend class TentativeAtom; friend class AbsoluteAtom; + friend class SectionBoundaryAtom; friend class SymbolAtom; typedef std::map AddrToAtomMap; @@ -1676,6 +1769,7 @@ class Reader : public ObjectFile::Reader static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); static const char* assureFullPath(const char* path); AtomAndOffset findAtomAndOffset(pint_t addr); + AtomAndOffset findAtomAndOffsetForSection(pint_t addr, unsigned int sectionIndex); AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr); Reference* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr); Reference* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr); @@ -1822,12 +1916,8 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { if ( (strcmp(sect->sectname(), "__eh_frame") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { fehFrameSection = sect; - const char* msg = libunwind::CFI_Parser >::getCFIs(fObjectAddressSpace, sect->addr(), - sect->size(), fFDEInfos, fCIEInfos); - if ( msg != NULL ) { - throwf("malformed __eh_frame section: %s", msg); - } - else { + if ( libunwind::CFI_Parser >::getCFIs(fObjectAddressSpace, sect->addr(), sect->size(), + fCIEInfos, fFDEInfos) ) { //fprintf(stderr, "%lu CIEs, %lu FDEs\n", fCIEInfos.size(), fFDEInfos.size()); // add anonymous atoms for each CIE for (typename std::vector::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { @@ -1849,6 +1939,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } } + else { + throw "malformed __eh_frame section"; + } } } @@ -1947,6 +2040,13 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { fAtoms.push_back(new TentativeAtom(*this, &sym)); } + else if ( (type == N_UNDF) && (sym.n_value() == 0) ) { + const char* symName = &fStrings[sym.n_strx()]; + if ( strncmp(symName, "section$start$", 14) == 0) + fAtoms.push_back(new SectionBoundaryAtom(*this, true, symName, &symName[14])); + else if ( strncmp(symName, "section$end$", 12) == 0) + fAtoms.push_back(new SectionBoundaryAtom(*this, false, symName, &symName[12])); + } else if ( type == N_ABS ) { const char* symName = &fStrings[sym.n_strx()]; if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) { @@ -2089,8 +2189,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { stringAddr = sect->addr() + sectOffset; stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; - // add if not already an atom at that address - if ( fAddrToAtom.find(stringAddr) == fAddrToAtom.end() ) { + // add if not already a non-zero length atom at that address + typename AddrToAtomMap::iterator pos = fAddrToAtom.find(stringAddr); + if ( (pos == fAddrToAtom.end()) || (pos->second->getSize() == 0) ) { BaseAtom* newAtom = new AnonymousAtom(*this, sect, stringAddr, stringLen); if ( stringLen == 1 ) { // because of padding it may look like there are lots of empty strings, keep track of all @@ -2724,44 +2825,6 @@ ObjectFile::Atom* Reader::getFunctionAtomFromLSDAAddress(pint_t addr) } -template <> -void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) -{ - // mach-o x86_64 is different, the content of a section with a relocation is the addend - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fReader.fHeader) + sect->reloff()); - const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { - std::map::iterator pos; - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - pos = map.find(reloc->r_address()); - if ( pos != map.end() ) - pos->second += fReader.fSymbols[reloc->r_symbolnum()].n_value(); - else - map[reloc->r_address()] = fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_SUBTRACTOR: - map[reloc->r_address()] = -fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_GOT: - // there is no good address to return here. - // GOT slots are synthsized by the linker - // this is used for the reference to the personality function in CIEs - map[reloc->r_address()] = 0; - break; - default: - fprintf(stderr, "ObjectFileAddressSpace::buildRelocatedMap() unexpected relocation at r_address=0x%08X\n", reloc->r_address()); - break; - } - } -} - -template -void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) -{ - // in all architectures except x86_64, the section contents are already fixed up to point - // to content in the same object file. -} template <> uint64_t ObjectFileAddressSpace::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) @@ -3037,15 +3100,14 @@ uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; - char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); + if ( (result & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); + warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); } return result; } @@ -3055,15 +3117,14 @@ uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; - char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); + if ( (result & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); + warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); } return result; } @@ -3121,14 +3182,12 @@ uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; - char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); + if ( (result & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else - if ( fOwner.fOptions.fWarnCompactUnwind ) warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); } return result; @@ -3139,14 +3198,12 @@ uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; - char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); + if ( (result & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else - if ( fOwner.fOptions.fWarnCompactUnwind ) warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); } return result; @@ -3464,8 +3521,11 @@ Reference* Reader::makeReferenceToSymbol(Kinds kind, pint_t atAd // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references // instead check scope of target const char* symbolName = &fStrings[toSymbol->n_strx()]; - if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toSymbol->n_value(), toSymbol->n_value()+toOffset)); + if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) { + AtomAndOffset targetAO = findAtomAndOffsetForSection(toSymbol->n_value(), toSymbol->n_sect()); + targetAO.offset = toOffset; + return new Reference(kind, findAtomAndOffset(atAddr), targetAO); + } else return new Reference(kind, findAtomAndOffset(atAddr), symbolName, toOffset); } @@ -3492,6 +3552,29 @@ BaseAtom* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAdd return NULL; } +template +AtomAndOffset Reader::findAtomAndOffsetForSection(pint_t addr, unsigned int expectedSectionIndex) +{ + AtomAndOffset ao = findAtomAndOffset(addr); + if ( ao.atom != NULL ) { + if ( ((BaseAtom*)(ao.atom))->getSectionIndex() == expectedSectionIndex ) + return ao; + } + // The atom found is not in the section expected. + // This probably means there was a label at the end of the section. + // Do a slow sequential lookup + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); ++it) { + BaseAtom* atom = *it; + if ( atom->getSectionIndex() == expectedSectionIndex ) { + pint_t objAddr = atom->getObjectAddress(); + if ( (objAddr == addr) || ((objAddr < addr) && (objAddr+atom->getSize() > addr)) ) { + return AtomAndOffset(atom, addr-atom->getObjectAddress()); + } + } + } + // no atom found + return AtomAndOffset(NULL); +} template AtomAndOffset Reader::findAtomAndOffset(pint_t addr) @@ -3528,10 +3611,9 @@ AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) return result; } // getting here means we have a scattered relocation to an address without a label - // so, find the atom that contains the baseAddr, and offset from that to the readAddr - AtomAndOffset result = findAtomAndOffset(baseAddr); - result.offset += (realAddr-baseAddr); - return result; + // we should never get here... + // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section + return findAtomAndOffset(realAddr); } @@ -4123,7 +4205,7 @@ bool Reader::addRelocReference_powerpc(const macho_section* se makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); } else { - makeReference(A::kPointer, srcAddr, pointerValue); + new Reference(A::kPointer, findAtomAndOffset(srcAddr), findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum())); } } break; @@ -4411,9 +4493,8 @@ bool Reader::addRelocReference(const macho_section* sect, const mac makeByNameReference(kind, srcAddr, targetName, pointerValue); } else { - // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol - ObjectFile::Atom* atom = findAtomAndOffset(pointerValue).atom; - const char* targetName = atom->getName(); + AtomAndOffset targetAO = findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum()); + const char* targetName = targetAO.atom->getName(); if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); addDtraceExtraInfos(srcAddr, &targetName[16]); @@ -4422,11 +4503,12 @@ bool Reader::addRelocReference(const macho_section* sect, const mac makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); addDtraceExtraInfos(srcAddr, &targetName[20]); } - else if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(x86::kPCRel32WeakImport, srcAddr, pointerValue); + // if this is a reference to a stub, we need to see if the stub is for a weak imported symbol + else if ( reloc->r_pcrel() && (targetAO.atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + && ((AnonymousAtom*)targetAO.atom)->isWeakImportStub() ) + new Reference(x86::kPCRel32WeakImport, findAtomAndOffset(srcAddr), targetAO); else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(kind, srcAddr, pointerValue); + new Reference(kind, findAtomAndOffset(srcAddr), targetAO); else { // find absolute symbol that corresponds to pointerValue AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue); @@ -4583,10 +4665,7 @@ bool Reader::addRelocReference(const macho_section* sect, con kind = x86_64::kPointer32; break; case 3: - if ( reloc->r_extern() && isWeakImportSymbol(targetSymbol) ) - kind = x86_64::kPointerWeakImport; - else - kind = x86_64::kPointer; + kind = x86_64::kPointer; break; } dstAddr = E::get64(*((uint64_t*)fixUpPtr)); @@ -4986,7 +5065,7 @@ bool Reader::addRelocReference(const macho_section* sect, } else { AtomAndOffset at = findAtomAndOffset(srcAddr); - AtomAndOffset to = findAtomAndOffset(pointerValue); + AtomAndOffset to = findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum()); if ( to.atom->isThumb() ) to.offset &= -2; new Reference(kind, at, to); @@ -5198,7 +5277,7 @@ const char* Reference::getDescription() const sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); break; case x86::kImageOffset32: - sprintf(temp, "offset 0x%04X, 32-bit offset of ", fFixUpOffsetInSrc); + sprintf(temp, "offset 0x%04X, 32bit offset of ", fFixUpOffsetInSrc); break; case x86::kPointerDiff24: sprintf(temp, "offset 0x%04X, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", @@ -5206,9 +5285,6 @@ const char* Reference::getDescription() const this->getFromTargetDisplayName(), fFromTarget.offset ); return temp; break; - case x86::kSectionOffset24: - sprintf(temp, "offset 0x%04X, 24-bit section offset of ", fFixUpOffsetInSrc); - break; case x86::kDtraceProbe: sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); break; @@ -5533,9 +5609,6 @@ const char* Reference::getDescription() const case x86_64::kImageOffset32: sprintf(temp, "offset 0x%04llX, 32bit offset of ", fFixUpOffsetInSrc); break; - case x86_64::kSectionOffset24: - sprintf(temp, "offset 0x%04llX, 24-bit section offset of ", fFixUpOffsetInSrc); - break; case x86_64::kDtraceProbe: sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); break; diff --git a/ld64/src/ld/MachOWriterExecutable.hpp b/ld64/src/ld/MachOWriterExecutable.hpp index b019af2..9e02efa 100644 --- a/ld64/src/ld/MachOWriterExecutable.hpp +++ b/ld64/src/ld/MachOWriterExecutable.hpp @@ -453,7 +453,6 @@ class Writer : public ExecutableFile::Writer std::vector fLocalSymbolAtoms; std::vector > fLocalExtraLabels; std::vector > fGlobalExtraLabels; - std::map fAtomToSymbolIndex; class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; class CompressedRebaseInfoLinkEditAtom* fCompressedRebaseInfoAtom; class CompressedBindingInfoLinkEditAtom* fCompressedBindingInfoAtom; @@ -973,20 +972,17 @@ class UnwindInfoAtom : public WriterAtom virtual void copyRawContent(uint8_t buffer[]) const; void addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsda, - ObjectFile::Atom* personalityPointer); + ObjectFile::Reference* lsda, ObjectFile::Atom* personalityPointer); void generate(); private: using WriterAtom::fWriter; typedef typename A::P P; - struct Info { ObjectFile::Atom* func; ObjectFile::Atom* fde; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; + struct Info { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; struct LSDAEntry { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; }; - struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fde; }; + struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; }; struct CompressedFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fromFunc; }; - struct CompressedEncodingFixUp { uint8_t* contentPointer; ObjectFile::Atom* fde; }; - bool encodingMeansUseDwarf(compact_unwind_encoding_t encoding); void compressDuplicates(std::vector& uniqueInfos); void findCommonEncoding(const std::vector& uniqueInfos, std::map& commonEncodings); void makeLsdaIndex(const std::vector& uniqueInfos, std::map& lsdaIndexOffsetMap); @@ -1009,7 +1005,6 @@ class UnwindInfoAtom : public WriterAtom std::vector fLSDAIndex; std::vector fRegFixUps; std::vector fCompressedFixUps; - std::vector fCompressedEncodingFixUps; std::vector fReferences; }; @@ -3240,8 +3235,6 @@ void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent desc |= N_ARM_THUMB_DEF; if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) desc |= REFERENCED_DYNAMICALLY; - if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) - desc |= N_NO_DEAD_STRIP; if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { desc |= N_WEAK_DEF; fHasWeakExports = true; @@ -3326,14 +3319,8 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr const char* symbolName = this->symbolTableName(atom); char anonName[32]; if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { - if ( stringsNeedLabelsInObjects() && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { - // don't use 'l' labels for x86_64 strings - // x86_64 obj-c runtime confused when static lib is stripped - } - else { - sprintf(anonName, "l%u", fAnonNameIndex++); - symbolName = anonName; - } + sprintf(anonName, "l%u", fAnonNameIndex++); + symbolName = anonName; } entry->set_n_strx(this->fStringsAtom->add(symbolName)); @@ -3356,8 +3343,6 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr // set n_desc uint16_t desc = 0; - if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) - desc |= N_NO_DEAD_STRIP; if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) desc |= N_WEAK_DEF; if ( atom->isThumb() ) @@ -3496,27 +3481,6 @@ void Writer::buildSymbolTable() // set up module table if ( fModuleInfoAtom != NULL ) fModuleInfoAtom->setName(); - - // create atom to symbol index map - // imports - int i = 0; - for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableImportStartIndex; - ++i; - } - // locals - i = 0; - for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableLocalStartIndex; - ++i; - } - // exports - i = 0; - for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableExportStartIndex; - ++i; - } - } @@ -3726,9 +3690,30 @@ void Writer::addStabs(uint32_t startIndex) template uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) { - std::map::iterator pos = fAtomToSymbolIndex.find(&atom); - if ( pos != fAtomToSymbolIndex.end() ) - return pos->second; + // search imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableImportStartIndex; + ++i; + } + + // search locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableLocalStartIndex; + ++i; + } + + // search exports + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + if ( &atom == *it ) + return i + fSymbolTableExportStartIndex; + ++i; + } + throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); } @@ -3933,9 +3918,6 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref case x86_64::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86_64::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - case x86_64::kDtraceTypeReference: case x86_64::kDtraceProbe: // generates no relocs @@ -4058,9 +4040,6 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere case x86::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - case x86::kDtraceTypeReference: case x86::kDtraceProbe: // generates no relocs @@ -4759,6 +4738,14 @@ bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& template <> bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { + switch ( fOptions.outputKind()) { + case Options::kStaticExecutable: + case Options::kPreload: + // all relocations allowed in static executables + return false; + default: + break; + } if ( ref.getKind() == arm::kReadOnlyPointer ) { switch ( ref.getTarget().getDefinitionKind() ) { case ObjectFile::Atom::kTentativeDefinition: @@ -4772,7 +4759,7 @@ bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& re return true; case ObjectFile::Atom::kAbsoluteSymbol: // absolute symbbols only allowed in static executables - return ( fOptions.outputKind() != Options::kStaticExecutable); + return true; } } return false; @@ -5260,23 +5247,11 @@ void Writer::buildExecutableFixups() uint64_t addresss = atom->getAddress(); if ( targetRequiresWeakBinding(ref->getTarget()) ) { fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, 0)); - // if this is a non-lazy pointer to a weak definition within this linkage unit + // if this is a non-lazy pointer to a weak definition with this linkage unit // the pointer needs to initially point within linkage unit and have - // rebase command to slide it. - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - // unless if this is a hybrid format, in which case the non-lazy pointer - // is zero on disk. So use a bind instead of a rebase to set initial value - if ( fOptions.makeClassicDyldInfo() ) - fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, 0)); - else - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - } - // if this is a non-lazy pointer to a weak definition in a dylib, - // the pointer needs to initially bind to the dylib - else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, pointerTarget->getName(), false, addresss, 0)); - } + // rease command to slide it. + if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); } else { int ordinal = compressedOrdinalForImortedAtom(pointerTarget); @@ -5345,16 +5320,6 @@ void Writer::buildExecutableFixups() uint8_t type = BIND_TYPE_POINTER; if ( targetRequiresWeakBinding(ref->getTarget()) ) { fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, addend)); - if ( fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // hybrid linkedit puts addend in data, so we need bind phase to reset pointer to local definifion - fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, addend)); - } - // if this is a pointer to a weak definition in a dylib, - // the pointer needs to initially bind to the dylib - else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); - fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, ref->getTarget().getName(), false, addresss, addend)); - } } else { int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); @@ -6543,9 +6508,9 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; } } - else if ( !fOptions.makeClassicDyldInfo() + else if ( fOptions.makeCompressedDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition + // lazy pointer is initially set to point directly to weak definition LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); } else { @@ -6579,15 +6544,6 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob temp |= (displacement & 0x00FFFFFF); LittleEndian::set32(*fixUp, temp); break; - case x86::kSectionOffset24: - displacement = ref->getTarget().getSectionOffset(); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*fixUp); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*fixUp, temp); - break; case x86::kDtraceProbeSite: // change call site to a NOP dtraceProbeSite = (uint8_t*)fixUp; @@ -6777,8 +6733,6 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co throw "internal linker error, kPointerDiff24 can't be encoded into object files"; case x86::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; case x86::kDtraceProbe: case x86::kDtraceTypeReference: // nothing to fix up @@ -6808,9 +6762,9 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const if ( &ref->getTarget() != NULL ) { //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal) { - if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition + if ( fOptions.makeCompressedDyldInfo() + && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // lazy pointer is initially set to point directly to weak definition LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); } else { @@ -6879,15 +6833,6 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const temp |= (displacement & 0x00FFFFFF); LittleEndian::set32(*((uint32_t*)fixUp), temp); break; - case x86_64::kSectionOffset24: - displacement = ref->getTarget().getSectionOffset(); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*((uint32_t*)fixUp)); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*((uint32_t*)fixUp), temp); - break; case x86_64::kPCRel32GOTLoad: case x86_64::kPCRel32GOTLoadWeakImport: // if GOT entry was optimized away, change movq instruction to a leaq @@ -7105,8 +7050,6 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, throw "internal linker error, kPointerDiff24 can't be encoded into object files"; case x86_64::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86_64::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; case x86_64::kDtraceTypeReference: case x86_64::kDtraceProbe: // nothing to fix up @@ -8310,12 +8253,12 @@ void Writer::synthesizeUnwindInfoTable() if ( atom->beginUnwind() == atom->endUnwind() ) { // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) - fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL, NULL); + fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL); } else { // atom has unwind for ( ObjectFile::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { - fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getFDE(), atom->getLSDA(), atom->getPersonalityPointer()); + fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getLSDA(), atom->getPersonalityPointer()); } } } @@ -10045,15 +9988,10 @@ void LoadCommandsPaddingAtom::setSize(uint64_t newSize) template void UnwindInfoAtom::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsdaRef, - ObjectFile::Atom* personalityPointer) + ObjectFile::Reference* lsdaRef, ObjectFile::Atom* personalityPointer) { Info info; info.func = func; - if ( fdeRef != NULL ) - info.fde = &fdeRef->getTarget(); - else - info.fde = NULL; if ( lsdaRef != NULL ) { info.lsda = &lsdaRef->getTarget(); info.lsdaOffset = lsdaRef->getTargetOffset(); @@ -10069,24 +10007,6 @@ void UnwindInfoAtom::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, u // encoding, info.lsda, info.lsdaOffset, personalityPointer, func->getDisplayName()); } -template <> -bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); -} - -template <> -bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); -} - -template -bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return false; -} - template void UnwindInfoAtom::compressDuplicates(std::vector& uniqueInfos) @@ -10100,10 +10020,9 @@ void UnwindInfoAtom::compressDuplicates(std::vector& uniqueInfos) last.personalityPointer = NULL; last.encoding = 0xFFFFFFFF; for(typename std::vector::iterator it=fInfos.begin(); it != fInfos.end(); ++it) { - Info& newInfo = *it; - bool newNeedsDwarf = encodingMeansUseDwarf(newInfo.encoding); + Info newInfo = *it; // remove infos which have same encoding and personalityPointer as last one - if ( newNeedsDwarf || (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) + if ( (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) || (newInfo.lsda != NULL) || (last.lsda != NULL) ) { uniqueInfos.push_back(newInfo); } @@ -10119,9 +10038,6 @@ void UnwindInfoAtom::findCommonEncoding(const std::vector& uniqueInfos, std::map encodingsUsed; unsigned int mostCommonEncodingUsageCount = 0; for(typename std::vector::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - // never put dwarf into common table - if ( encodingMeansUseDwarf(it->encoding) ) - continue; std::map::iterator pos = encodingsUsed.find(it->encoding); if ( pos == encodingsUsed.end() ) { encodingsUsed[it->encoding] = 1; @@ -10197,8 +10113,7 @@ unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vector::makeCompressedSecondLevelPage(const std::vector< uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) { const bool log = false; - if (log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); // first pass calculates how many compressed entries we could fit in this sized page // keep adding entries to page until: // 1) encoding table plus entry table plus header exceed page size @@ -10235,20 +10149,14 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< encodingIndex = pos->second; } else { - // no commmon entry, so add one on this page - uint32_t encoding = info.encoding; - if ( encodingMeansUseDwarf(encoding) ) { - // make unique pseudo encoding so this dwarf will gets is own encoding entry slot - encoding += (index+1); - } - std::map::iterator ppos = pageSpecificEncodings.find(encoding); + std::map::iterator ppos = pageSpecificEncodings.find(info.encoding); if ( ppos != pageSpecificEncodings.end() ) { encodingIndex = pos->second; } else { encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); if ( encodingIndex <= 255 ) { - pageSpecificEncodings[encoding] = encodingIndex; + pageSpecificEncodings[info.encoding] = encodingIndex; } else { canDo = false; // case 3) @@ -10270,72 +10178,52 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< ++entryCount; } // check room for entry - if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { + if ( (pageSpecificEncodings.size()+entryCount) > space4 ) { canDo = false; // case 1) --entryCount; if (log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); } - //if (log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); } - // check for cases where it would be better to use a regular (non-compressed) page - const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + // sanity check that we fit more entries into this page than a regular page would hold + const int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + pageSpecificEncodings.size()*sizeof(uint32_t) + entryCount*sizeof(uint32_t); - if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { - const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - if ( entryCount < regularEntriesPerPage ) { - return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); - } + const int regularEntriesPerPage = (compressPageUsed - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + if ( entryCount < regularEntriesPerPage ) { + return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); } - - // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 - uint32_t pad = 0; - if ( compressPageUsed == (pageSize-4) ) - pad = 4; - + // second pass fills in page - uint8_t* pageStart = pageEnd - compressPageUsed - pad; + uint8_t* pageStart = pageEnd - compressPageUsed; macho_unwind_info_compressed_second_level_page_header

* page = (macho_unwind_info_compressed_second_level_page_header

*)pageStart; page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); page->set_entryPageOffset(sizeof(macho_unwind_info_compressed_second_level_page_header

)); page->set_entryCount(entryCount); page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); page->set_encodingsCount(pageSpecificEncodings.size()); - uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; // fill in entry table uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; ObjectFile::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { const Info& info = uniqueInfos[i]; uint8_t encodingIndex; - if ( encodingMeansUseDwarf(info.encoding) ) { - // dwarf entries are always in page specific encodings - encodingIndex = pageSpecificEncodings[info.encoding+i]; - } - else { - std::map::const_iterator pos = commonEncodings.find(info.encoding); - if ( pos != commonEncodings.end() ) - encodingIndex = pos->second; - else - encodingIndex = pageSpecificEncodings[info.encoding]; - } + std::map::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) + encodingIndex = pos->second; + else + encodingIndex = pageSpecificEncodings[info.encoding]; uint32_t entryIndex = i - endIndex + entryCount; A::P::E::set32(entiresArray[entryIndex], encodingIndex << 24); - CompressedFixUp funcStartFixUp; - funcStartFixUp.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); - funcStartFixUp.func = info.func; - funcStartFixUp.fromFunc = firstFunc; - fCompressedFixUps.push_back(funcStartFixUp); - if ( encodingMeansUseDwarf(info.encoding) ) { - CompressedEncodingFixUp dwarfStartFixup; - dwarfStartFixup.contentPointer = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]); - dwarfStartFixup.fde = info.fde; - fCompressedEncodingFixUps.push_back(dwarfStartFixup); - } + CompressedFixUp fixup; + fixup.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); + fixup.func = info.func; + fixup.fromFunc = firstFunc; + fCompressedFixUps.push_back(fixup); } // fill in encodings table - for(std::map::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { + uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; + for(std::map::iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { A::P::E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); } @@ -10383,8 +10271,8 @@ void UnwindInfoAtom::generate() fPagesSize = 0; if ( fPagesContentForDelete == NULL ) throw "could not allocate space for compact unwind info"; - ObjectFile::Atom* secondLevelFirstFuncs[pageCount*3]; - uint8_t* secondLevelPagesStarts[pageCount*3]; + ObjectFile::Atom* secondLevelFirstFuncs[pageCount]; + uint8_t* secondLevelPagesStarts[pageCount]; // make last second level page smaller so that all other second level pages can be page aligned uint32_t maxLastPageSize = unwindSectionInfo->fFileOffset % 4096; @@ -10478,18 +10366,12 @@ void UnwindInfoAtom::generate() for (typename std::vector::iterator it = fRegFixUps.begin(); it != fRegFixUps.end(); ++it) { uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; fReferences.push_back(new WriterReference(offset, A::kImageOffset32, it->func)); - if ( it->fde != NULL ) - fReferences.push_back(new WriterReference(offset+4, A::kSectionOffset24, it->fde)); } // make references for compressed second level entries for (typename std::vector::iterator it = fCompressedFixUps.begin(); it != fCompressedFixUps.end(); ++it) { uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; fReferences.push_back(new WriterReference(offset, A::kPointerDiff24, it->func, 0, it->fromFunc, 0)); } - for (typename std::vector::iterator it = fCompressedEncodingFixUps.begin(); it != fCompressedEncodingFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference(offset, A::kSectionOffset24, it->fde)); - } // update section record with new size unwindSectionInfo->fSize = this->getSize(); @@ -11236,7 +11118,7 @@ void CompressedBindingInfoLinkEditAtom::encode() std::vector mid; const SegmentInfo* currentSegment = NULL; unsigned int segIndex = 0; - int ordinal = 0x80000000; + int ordinal = 0; const char* symbolName = NULL; uint8_t type = 0; uint64_t address = (uint64_t)(-1); @@ -11340,12 +11222,10 @@ void CompressedBindingInfoLinkEditAtom::encode() p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; p->operand1 = p->operand1/sizeof(pint_t); } - else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { - p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; - } } dst->opcode = BIND_OPCODE_DONE; + // convert to compressed encoding const static bool log = false; fEncodedData.reserve(info.size()*2); diff --git a/ld64/src/ld/ObjectFile.h b/ld64/src/ld/ObjectFile.h index 8b9a2d3..87396fb 100644 --- a/ld64/src/ld/ObjectFile.h +++ b/ld64/src/ld/ObjectFile.h @@ -66,11 +66,12 @@ class ReaderOptions fForFinalLinkedImage(false), fNoEHLabels(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), fImplicitlyLinkPublicDylibs(true), - fAddCompactUnwindEncoding(true), + fAddCompactUnwindEncoding(false), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), fMakeCompressedDyldInfo(false), fAutoOrderInitializers(true), + fOptimizeZeroFill(true), fLogObjectFiles(false), fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} @@ -102,6 +103,7 @@ class ReaderOptions bool fRemoveDwarfUnwindIfCompactExists; bool fMakeCompressedDyldInfo; bool fAutoOrderInitializers; + bool fOptimizeZeroFill; bool fLogObjectFiles; bool fLogAllFiles; bool fTraceDylibs; @@ -280,7 +282,7 @@ class Atom public: enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; - enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType }; + enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType, kSectionStart, kSectionEnd }; enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; virtual Reader* getFile() const = 0; @@ -308,7 +310,6 @@ class Atom virtual UnwindInfo::iterator beginUnwind() { return NULL; } virtual UnwindInfo::iterator endUnwind() { return NULL; } virtual Reference* getLSDA() { return NULL; } - virtual Reference* getFDE() { return NULL; } virtual Atom* getPersonalityPointer() { return NULL; } uint64_t getSectionOffset() const { return fSectionOffset; } diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 03969cd..58bb74c 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -101,7 +101,8 @@ Options::Options(int argc, const char* argv[]) fSharedRegionEligible(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), - fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), + fUsingLazyDylibLinking(false), fEncryptable(true), + fOrderData(true), fMarkDeadStrippableDylib(false), fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), fSaveTempFiles(false) { this->checkForClassic(argc, argv); @@ -2510,6 +2511,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { fAllowCpuSubtypeMismatches = true; } + else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { + fReaderOptions.fOptimizeZeroFill = false; + } else { throwf("unknown option: %s", arg); } @@ -2776,6 +2780,7 @@ void Options::parsePostCommandLineEnvironmentSettings() // allow build system to force on -warn_commons if ( getenv("LD_WARN_COMMONS") != NULL ) fWarnCommons = true; + } void Options::reconfigureDefaults() @@ -3064,7 +3069,7 @@ void Options::reconfigureDefaults() fEncryptable = false; if ( fArchitecture != CPU_TYPE_ARM ) fEncryptable = false; - + // don't move inits in dyld because dyld wants certain // entries point at stable locations at the start of __text if ( fOutputKind == Options::kDyld ) @@ -3125,6 +3130,11 @@ void Options::reconfigureDefaults() // only ARM enforces that cpu-sub-types must match if ( fArchitecture != CPU_TYPE_ARM ) fAllowCpuSubtypeMismatches = true; + + // only final linked images can not optimize zero fill sections + if ( fOutputKind == Options::kObjectFile ) + fReaderOptions.fOptimizeZeroFill = true; + } void Options::checkIllegalOptionCombinations() @@ -3464,6 +3474,12 @@ void Options::checkIllegalOptionCombinations() } } + // -force_cpusubtype_ALL is not supported for ARM + if ( fForceSubtypeAll ) { + if ( fArchitecture == CPU_TYPE_ARM ) { + warning("-force_cpusubtype_ALL will become unsupported for ARM architectures"); + } + } } diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index ded2a19..6d93c0b 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -1,5 +1,5 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -86,11 +86,11 @@ class CStringEquals class Section : public ObjectFile::Section { public: - static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool createIfNeeded=true); + static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded=true); static void assignIndexes(); const char* getName() { return fSectionName; } private: - Section(const char* sectionName, const char* segmentName, bool zeroFill); + Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill); struct Sorter { static int segmentOrdinal(const char* segName); @@ -101,9 +101,10 @@ class Section : public ObjectFile::Section typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; //typedef std::map NameToSection; - char fSectionName[18]; - char fSegmentName[18]; + const char* fSectionName; + const char* fSegmentName; bool fZeroFill; + bool fUntrustedZeroFill; static NameToSection fgMapping; static std::vector fgSections; @@ -114,12 +115,9 @@ Section::NameToSection Section::fgMapping; std::vector Section::fgSections; Section::NameToOrdinal Section::fgSegmentDiscoverOrder; -Section::Section(const char* sectionName, const char* segmentName, bool zeroFill) - : fZeroFill(zeroFill) +Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill) + : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill) { - strlcpy(fSectionName, sectionName, sizeof(fSectionName)); - strlcpy(fSegmentName, segmentName, sizeof(fSegmentName)); - this->fIndex = fgSections.size() + 20; // room for 20 standard sections // special placement of some sections if ( strcmp(segmentName, "__TEXT") == 0 ) { @@ -179,12 +177,17 @@ Section::Section(const char* sectionName, const char* segmentName, bool zeroFill //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); } -Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill, bool createIfNeeded) +Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded) { NameToSection::iterator pos = fgMapping.find(sectionName); if ( pos != fgMapping.end() ) { - if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) + if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) { + if ( !untrustedZeroFill && pos->second->fUntrustedZeroFill ) { + pos->second->fZeroFill = zeroFill; + pos->second->fUntrustedZeroFill = false; + } return pos->second; + } // otherwise same section name is used in different segments, look slow way for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) @@ -196,13 +199,13 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze return NULL; // does not exist, so make a new one - Section* sect = new Section(sectionName, segmentName, zeroFill); + Section* sect = new Section(sectionName, segmentName, zeroFill, untrustedZeroFill); fgMapping[sectionName] = sect; fgSections.push_back(sect); if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { // special case __StaticInit to be right after __text - find("__StaticInit", "__TEXT", false); + find("__StaticInit", "__TEXT", false, true); } // remember segment discovery order @@ -658,7 +661,7 @@ void Linker::optimize() for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { ObjectFile::Atom *atom = *itr; if ( atom->getSection() == NULL ) - atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); + atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill(), true)); } // resolve new undefines @@ -871,8 +874,17 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) } // record section orders so output file can have same order - if (atom.getSectionName()) - atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); + if (atom.getSectionName()) { + bool untrusted = false; + switch ( atom.getContentType() ) { + case ObjectFile::Atom::kSectionStart: + case ObjectFile::Atom::kSectionEnd: + untrusted = true; + default: + break; + } + atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill(), untrusted)); + } } @@ -2213,7 +2225,7 @@ void Linker::tweakLayout() throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment - Section* hugeZeroFills = Section::find("__huge", "__DATA", true); + Section* hugeZeroFills = Section::find("__huge", "__DATA", true, true); for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { ObjectFile::Atom* atom = *it; if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) ) @@ -2232,7 +2244,7 @@ void Linker::tweakLayout() } // move all functions pointed to by __mod_init_func section to front of __text - Section* initSection = Section::find("__mod_init_func", "__DATA", false, false); + Section* initSection = Section::find("__mod_init_func", "__DATA", false, true, false); if ( initSection != NULL ) { for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { if ( (*it)->getSection() == initSection ) { @@ -2263,7 +2275,7 @@ void Linker::tweakLayout() break; } const bool hasPreferredLoadAddress = (fOptions.baseAddress() != 0); - Section* dataSection = Section::find("__data", "__DATA", false, false); + Section* dataSection = Section::find("__data", "__DATA", false, true, false); if ( dataSection != NULL ) { for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { ObjectFile::Atom* dataAtom = *it; @@ -3984,6 +3996,12 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi unsigned int rightSectionIndex = right->getSection()->getIndex(); if ( leftSectionIndex != rightSectionIndex) return (leftSectionIndex < rightSectionIndex); + + // magic section$start symbol always sorts to the start of its section + if ( left->getContentType() == ObjectFile::Atom::kSectionStart ) + return true; + if ( right->getContentType() == ObjectFile::Atom::kSectionStart ) + return false; // if a -order_file is specified, then sorting is altered to sort those symbols first if ( fOverriddenOrdinalMap != NULL ) { @@ -4019,6 +4037,12 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi if ( leftIsTent != rightIsTent ) return rightIsTent; + // magic section$end symbol always sorts to the end of its section + if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) + return false; + if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) + return true; + // initializers are auto sorted to start of section if ( !fInitializerSet.empty() ) { bool leftFirst = (fInitializerSet.count(left) != 0); diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 016fda3..4af50ff 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -722,7 +722,7 @@ void DyldInfoPrinter::printBindingInfo() } else { printf("bind information:\n"); - printf("segment section address type weak addend dylib symbol\n"); + printf("segment section address type addend dylib symbol\n"); const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off(); const uint8_t* end = &p[fInfo->bind_size()]; @@ -738,7 +738,6 @@ void DyldInfoPrinter::printBindingInfo() pint_t segStartAddr = 0; const char* segName = "??"; const char* typeName = "??"; - const char* weak_import = ""; bool done = false; while ( !done && (p < end) ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; @@ -771,10 +770,6 @@ void DyldInfoPrinter::printBindingInfo() while (*p != '\0') ++p; ++p; - if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) - weak_import = "weak"; - else - weak_import = ""; break; case BIND_OPCODE_SET_TYPE_IMM: type = immediate; @@ -793,22 +788,22 @@ void DyldInfoPrinter::printBindingInfo() segOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); segOffset += sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); segOffset += read_uleb128(p, end) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); segOffset += skip + sizeof(pint_t); } break; diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 098d932..d8d699d 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -83,7 +83,6 @@ class UnwindPrinter void getSymbolTableInfo(); const char* functionName(pint_t addr); static const char* archName(); - static void decode(uint32_t encoding, const uint8_t* funcStart, char* str); const char* fPath; const macho_header

* fHeader; @@ -252,7 +251,7 @@ const char* UnwindPrinter::functionName(pint_t addr) } } } - return "--anonymous function--"; + return "??"; } @@ -289,379 +288,8 @@ bool UnwindPrinter::findUnwindSection() return false; } -#define EXTRACT_BITS(value, mask) \ - ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) -template <> -void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) -{ - *str = '\0'; - switch ( encoding & UNWIND_X86_64_MODE_MASK ) { - case UNWIND_X86_64_MODE_RBP_FRAME: - { - uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); - uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); - if ( savedRegistersLocations == 0 ) { - strcpy(str, "rbp frame, no saved registers"); - } - else { - sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8); - bool needComma = false; - for (int i=0; i < 5; ++i) { - if ( needComma ) - strcat(str, ","); - else - needComma = true; - switch (savedRegistersLocations & 0x7) { - case UNWIND_X86_64_REG_NONE: - strcat(str, "-"); - break; - case UNWIND_X86_64_REG_RBX: - strcat(str, "rbx"); - break; - case UNWIND_X86_64_REG_R12: - strcat(str, "r12"); - break; - case UNWIND_X86_64_REG_R13: - strcat(str, "r13"); - break; - case UNWIND_X86_64_REG_R14: - strcat(str, "r14"); - break; - case UNWIND_X86_64_REG_R15: - strcat(str, "r15"); - break; - default: - strcat(str, "r?"); - } - savedRegistersLocations = (savedRegistersLocations >> 3); - if ( savedRegistersLocations == 0 ) - break; - } - } - } - break; - case UNWIND_X86_64_MODE_STACK_IMMD: - case UNWIND_X86_64_MODE_STACK_IND: - { - uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); - uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); - uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); - uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); - if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) { - // stack size is encoded in subl $xxx,%esp instruction - uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize))); - sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust); - } - else { - sprintf(str, "stack size=%d, ", stackSize*8); - } - if ( regCount == 0 ) { - strcat(str, "no registers saved"); - } - else { - int permunreg[6]; - switch ( regCount ) { - case 6: - permunreg[0] = permutation/120; - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; - permunreg[5] = 0; - break; - case 5: - permunreg[0] = permutation/120; - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; - break; - case 4: - permunreg[0] = permutation/60; - permutation -= (permunreg[0]*60); - permunreg[1] = permutation/12; - permutation -= (permunreg[1]*12); - permunreg[2] = permutation/3; - permutation -= (permunreg[2]*3); - permunreg[3] = permutation; - break; - case 3: - permunreg[0] = permutation/20; - permutation -= (permunreg[0]*20); - permunreg[1] = permutation/4; - permutation -= (permunreg[1]*4); - permunreg[2] = permutation; - break; - case 2: - permunreg[0] = permutation/5; - permutation -= (permunreg[0]*5); - permunreg[1] = permutation; - break; - case 1: - permunreg[0] = permutation; - break; - } - // renumber registers back to standard numbers - int registers[6]; - bool used[7] = { false, false, false, false, false, false, false }; - for (int i=0; i < regCount; ++i) { - int renum = 0; - for (int u=1; u < 7; ++u) { - if ( !used[u] ) { - if ( renum == permunreg[i] ) { - registers[i] = u; - used[u] = true; - break; - } - ++renum; - } - } - } - bool needComma = false; - for (int i=0; i < regCount; ++i) { - if ( needComma ) - strcat(str, ","); - else - needComma = true; - switch ( registers[i] ) { - case UNWIND_X86_64_REG_RBX: - strcat(str, "rbx"); - break; - case UNWIND_X86_64_REG_R12: - strcat(str, "r12"); - break; - case UNWIND_X86_64_REG_R13: - strcat(str, "r13"); - break; - case UNWIND_X86_64_REG_R14: - strcat(str, "r14"); - break; - case UNWIND_X86_64_REG_R15: - strcat(str, "r15"); - break; - case UNWIND_X86_64_REG_RBP: - strcat(str, "rbp"); - break; - default: - strcat(str, "r??"); - } - } - } - } - break; - case UNWIND_X86_64_MODE_DWARF: - sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET); - break; - default: - if ( encoding == 0 ) - strcat(str, "no unwind information"); - else - strcat(str, "tbd "); - } - if ( encoding & UNWIND_HAS_LSDA ) { - strcat(str, " LSDA"); - } - -} - -template <> -void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) -{ - *str = '\0'; - switch ( encoding & UNWIND_X86_MODE_MASK ) { - case UNWIND_X86_MODE_EBP_FRAME: - { - uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET); - uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); - if ( savedRegistersLocations == 0 ) { - strcpy(str, "ebp frame, no saved registers"); - } - else { - sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4); - bool needComma = false; - for (int i=0; i < 5; ++i) { - if ( needComma ) - strcat(str, ","); - else - needComma = true; - switch (savedRegistersLocations & 0x7) { - case UNWIND_X86_REG_NONE: - strcat(str, "-"); - break; - case UNWIND_X86_REG_EBX: - strcat(str, "ebx"); - break; - case UNWIND_X86_REG_ECX: - strcat(str, "ecx"); - break; - case UNWIND_X86_REG_EDX: - strcat(str, "edx"); - break; - case UNWIND_X86_REG_EDI: - strcat(str, "edi"); - break; - case UNWIND_X86_REG_ESI: - strcat(str, "esi"); - break; - default: - strcat(str, "e??"); - } - savedRegistersLocations = (savedRegistersLocations >> 3); - if ( savedRegistersLocations == 0 ) - break; - } - } - } - break; - case UNWIND_X86_MODE_STACK_IMMD: - case UNWIND_X86_MODE_STACK_IND: - { - uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); - uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); - uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); - uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); - if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) { - // stack size is encoded in subl $xxx,%esp instruction - uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize))); - sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust); - } - else { - sprintf(str, "stack size=%d, ", stackSize*4); - } - if ( regCount == 0 ) { - strcat(str, "no saved regs"); - } - else { - int permunreg[6]; - switch ( regCount ) { - case 6: - permunreg[0] = permutation/120; - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; - permunreg[5] = 0; - break; - case 5: - permunreg[0] = permutation/120; - permutation -= (permunreg[0]*120); - permunreg[1] = permutation/24; - permutation -= (permunreg[1]*24); - permunreg[2] = permutation/6; - permutation -= (permunreg[2]*6); - permunreg[3] = permutation/2; - permutation -= (permunreg[3]*2); - permunreg[4] = permutation; - break; - case 4: - permunreg[0] = permutation/60; - permutation -= (permunreg[0]*60); - permunreg[1] = permutation/12; - permutation -= (permunreg[1]*12); - permunreg[2] = permutation/3; - permutation -= (permunreg[2]*3); - permunreg[3] = permutation; - break; - case 3: - permunreg[0] = permutation/20; - permutation -= (permunreg[0]*20); - permunreg[1] = permutation/4; - permutation -= (permunreg[1]*4); - permunreg[2] = permutation; - break; - case 2: - permunreg[0] = permutation/5; - permutation -= (permunreg[0]*5); - permunreg[1] = permutation; - break; - case 1: - permunreg[0] = permutation; - break; - } - // renumber registers back to standard numbers - int registers[6]; - bool used[7] = { false, false, false, false, false, false, false }; - for (int i=0; i < regCount; ++i) { - int renum = 0; - for (int u=1; u < 7; ++u) { - if ( !used[u] ) { - if ( renum == permunreg[i] ) { - registers[i] = u; - used[u] = true; - break; - } - ++renum; - } - } - } - bool needComma = false; - for (int i=0; i < regCount; ++i) { - if ( needComma ) - strcat(str, ","); - else - needComma = true; - switch ( registers[i] ) { - case UNWIND_X86_REG_EBX: - strcat(str, "ebx"); - break; - case UNWIND_X86_REG_ECX: - strcat(str, "ecx"); - break; - case UNWIND_X86_REG_EDX: - strcat(str, "edx"); - break; - case UNWIND_X86_REG_EDI: - strcat(str, "edi"); - break; - case UNWIND_X86_REG_ESI: - strcat(str, "esi"); - break; - case UNWIND_X86_REG_EBP: - strcat(str, "ebp"); - break; - default: - strcat(str, "e??"); - } - } - } - } - break; - case UNWIND_X86_MODE_DWARF: - sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET); - break; - default: - if ( encoding == 0 ) - strcat(str, "no unwind information"); - else - strcat(str, "tbd "); - } - if ( encoding & UNWIND_HAS_LSDA ) { - strcat(str, " LSDA"); - } - -} - - -template -void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) -{ - - -} template void UnwindPrinter::printUnwindSection() @@ -681,7 +309,7 @@ void UnwindPrinter::printUnwindSection() printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount()); const uint32_t* commonEncodings = (uint32_t*)§ionContent[sectionHeader->commonEncodingsArraySectionOffset()]; for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) { - printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i])); + printf("\t\tencoding[%2u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i])); } printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount()); const uint32_t* personalityArray = (uint32_t*)§ionContent[sectionHeader->personalityArraySectionOffset()]; @@ -715,10 +343,10 @@ void UnwindPrinter::printUnwindSection() printf("\t\tentryCount=0x%08X\n", page->entryCount()); const macho_unwind_info_regular_second_level_entry

* entry = (macho_unwind_info_regular_second_level_entry

*)((char*)page+page->entryPageOffset()); for (uint32_t j=0; j < page->entryCount(); ++j) { - uint32_t funcOffset = entry[j].functionOffset(); if ( entry[j].encoding() & UNWIND_HAS_LSDA ) { // verify there is a corresponding entry in lsda table bool found = false; + uint32_t funcOffset = entry[j].functionOffset(); for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) { if ( lindex[k].functionOffset() == funcOffset ) { found = true; @@ -729,10 +357,8 @@ void UnwindPrinter::printUnwindSection() fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress)); } } - char encodingString[100]; - decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", - j, funcOffset, entry[j].encoding(), encodingString, functionName(funcOffset+fMachHeaderAddress)); + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X %s\n", + j, entry[j].functionOffset(), entry[j].encoding(), functionName(entry[j].functionOffset()+fMachHeaderAddress)); } } else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) { @@ -752,9 +378,7 @@ void UnwindPrinter::printUnwindSection() encoding = A::P::E::get32(commonEncodings[encodingIndex]); else encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]); - char encodingString[100]; uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset; - decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString); const char* name = functionName(funcOff+fMachHeaderAddress); if ( encoding & UNWIND_HAS_LSDA ) { // verify there is a corresponding entry in lsda table @@ -769,8 +393,8 @@ void UnwindPrinter::printUnwindSection() fprintf(stderr, "MISSING LSDA entry for %s\n", name); } } - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n", - j, funcOff, encodingIndex, encoding, encodingString, name); + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%2u]=0x%08X %s\n", + j, funcOff, encodingIndex, encoding, name); } } else { diff --git a/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests index fa96968..842ce77 100755 --- a/ld64/unit-tests/run-all-unit-tests +++ b/ld64/unit-tests/run-all-unit-tests @@ -19,8 +19,6 @@ valid_archs="x86_64 armv6 ppc i386 " ../bin/make-recursive.pl clean > /dev/null mkdir /tmp/$$ -mkdir $BUILD_ROOT/lib -ln -s /Developer/usr/lib/libLTO.dylib ${BUILD_ROOT}/lib/libLTO.dylib for arch in $all_archs do echo "" diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile deleted file mode 100644 index 72f365b..0000000 --- a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded -# - -run: all-${ARCH} - - -all-ppc: - ${PASS_IFF} true - -all-arm: - ${PASS_IFF} true - -all-i386: all-real - -all-x86_64: all-real - - -all-real: - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib - ${FAIL_IF_BAD_MACHO} libfoo.dylib - - ${CC} ${CCFLAGS} main.c -o main libfoo.dylib - dyldinfo -bind main | grep wfoo | ${FAIL_IF_EMPTY} - - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm -rf libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c deleted file mode 100644 index 1906d16..0000000 --- a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c +++ /dev/null @@ -1,4 +0,0 @@ - - -__attribute__((weak)) void wfoo() {} -void foo() {} diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c deleted file mode 100644 index afd696d..0000000 --- a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c +++ /dev/null @@ -1,17 +0,0 @@ - -extern void foo(); -extern void wfoo(); - -void* pfoo = &foo; -void* pwfoo = &wfoo; - -int main (void) -{ - if (pfoo != &foo) - return 1; - if (pwfoo != &wfoo) - return 1; - - return 0; -} - diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s deleted file mode 100644 index 33705e5..0000000 --- a/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s +++ /dev/null @@ -1,8 +0,0 @@ - - - - .section __MYSEG, __cstring, cstring_literals -LC: .ascii "hello" - - - diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/main.c b/ld64/unit-tests/test-cases/cstring-alt-segment/main.c deleted file mode 100644 index 8fe18db..0000000 --- a/ld64/unit-tests/test-cases/cstring-alt-segment/main.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main() -{ - printf("hello"); - return 0; -} diff --git a/ld64/unit-tests/test-cases/cstring-labels/foo.c b/ld64/unit-tests/test-cases/cstring-labels/foo.c index 0780b62..efed306 100644 --- a/ld64/unit-tests/test-cases/cstring-labels/foo.c +++ b/ld64/unit-tests/test-cases/cstring-labels/foo.c @@ -1,6 +1,4 @@ -void func() {} - const char kFoo[] = "foo"; const char* kFoo2 = "hello"; diff --git a/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile deleted file mode 100644 index e60513e..0000000 --- a/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that N_NO_DEAD_STRIP bit survives ld -r -# - -run: all - -all: - ${CC} ${CCFLAGS} main.c -c -o main.o - nm -m main.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY} - nm -m main.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY} - ${LD} -r main.o -o main2.o - nm -m main2.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY} - nm -m main2.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY} - ${CC} main2.o -o main - nm -m main | grep _bar | grep "no dead strip" | ${PASS_IFF_EMPTY} - -clean: - rm -rf main.o main2.o main diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c deleted file mode 100644 index 7d76bdd..0000000 --- a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c +++ /dev/null @@ -1,7 +0,0 @@ -void baz() -{ -} - - -#include "foo.c" - diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c deleted file mode 100644 index 4cd4cfb..0000000 --- a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c +++ /dev/null @@ -1,25 +0,0 @@ - -// function can be coalesced and should not be dead stripped -void __attribute__ ((weak, section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo() -{ - -} - - -// function should not be exported, can be coalesced, and should not be dead stripped -void __attribute__ ((weak, visibility("hidden"), section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) hidden() -{ - -} - -// bar should be dead stripped -void __attribute__ ((weak, section ("__DATA,__text2"))) bar() -{ - -} - -__attribute__((constructor)) static void init() -{ - foo(); - hidden(); -} diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c deleted file mode 100644 index e4c564c..0000000 --- a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c +++ /dev/null @@ -1,13 +0,0 @@ - -// baz is in a lazily loaded archive -extern void baz(); - -int main() -{ - baz(); - return 0; -} - - -#include "foo.c" - diff --git a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile index 27cd180..8ce50d6 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile +++ b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile @@ -43,7 +43,7 @@ all: ${LD} -r foo.no.o bar.no.o baz.no.o -o foobarbaz.no.o ${OBJECTDUMP} -no_content -no_sort foobarbaz.o > foobarbaz.dump ${OBJECTDUMP} -no_content -no_sort foobarbaz.no.o > foobarbaz.no.dump - ${FAIL_IF_ERROR} dwarfdump --eh-frame --verify foobarbaz.no.o >/dev/null + ${FAIL_IF_ERROR} dwarfdump --eh-frame --verify >/dev/null ${PASS_IFF_SUCCESS} diff foobarbaz.dump foobarbaz.no.dump diff --git a/ld64/unit-tests/test-cases/init-order/Makefile b/ld64/unit-tests/test-cases/init-order/Makefile index 7e8c6e1..bb67908 100644 --- a/ld64/unit-tests/test-cases/init-order/Makefile +++ b/ld64/unit-tests/test-cases/init-order/Makefile @@ -33,8 +33,8 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CXX} ${CXXFLAGS} main.cxx foo.cxx bar.cxx -o main -dead_strip - nm -s __TEXT __text -nj main | grep -v dyld_stub_binding_helper | c++filt > actual-order.txt + ${CXX} ${CXXFLAGS} main.cxx foo.cxx bar.cxx -o main + nm -s __TEXT __text -nj main | c++filt > actual-order.txt ${PASS_IFF} diff actual-order.txt expected-order.txt diff --git a/ld64/unit-tests/test-cases/init-order/expected-order.txt b/ld64/unit-tests/test-cases/init-order/expected-order.txt index 9a303d3..1de60f3 100644 --- a/ld64/unit-tests/test-cases/init-order/expected-order.txt +++ b/ld64/unit-tests/test-cases/init-order/expected-order.txt @@ -8,6 +8,8 @@ Bar::Bar() __static_initialization_and_destruction_0(int, int) global constructors keyed to b1 start +dyld_stub_binding_helper +__dyld_func_lookup _main M::~M() Foo::~Foo() diff --git a/ld64/unit-tests/test-cases/kext-basic/Makefile b/ld64/unit-tests/test-cases/kext-basic/Makefile index d233171..6a318b3 100644 --- a/ld64/unit-tests/test-cases/kext-basic/Makefile +++ b/ld64/unit-tests/test-cases/kext-basic/Makefile @@ -23,6 +23,7 @@ all: nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY} nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY} otool -rv mykext | grep '_extern_global' | ${FAIL_IF_EMPTY} + otool -rv mykext | grep '_OSRuntimeFinalizeCPP' | ${FAIL_IF_EMPTY} ${PASS_IFF} true clean: diff --git a/ld64/unit-tests/test-cases/no-data-bundle/Makefile b/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile similarity index 82% rename from ld64/unit-tests/test-cases/no-data-bundle/Makefile rename to ld64/unit-tests/test-cases/label-on-end-of-section/Makefile index e1417fd..a6f1537 100644 --- a/ld64/unit-tests/test-cases/no-data-bundle/Makefile +++ b/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile @@ -24,15 +24,14 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Check that a bundle built with no data links -# gcc DejaGnu failure: building longcall/dylib library +# Check that ld handles labels at the end of a section # run: all all: - ${CC} ${CCFLAGS} foo.c -bundle -o foo.bundle - ${PASS_IFF_GOOD_MACHO} foo.bundle + ${CC} ${CCFLAGS} foo.s -c -o foo.o + ${OBJECTDUMP} foo.o | grep "pointer to _end" | ${PASS_IFF_STDIN} clean: - rm foo.bundle + rm foo.o diff --git a/ld64/unit-tests/test-cases/label-on-end-of-section/foo.s b/ld64/unit-tests/test-cases/label-on-end-of-section/foo.s new file mode 100644 index 0000000..9544a76 --- /dev/null +++ b/ld64/unit-tests/test-cases/label-on-end-of-section/foo.s @@ -0,0 +1,17 @@ + + + .section __MY, __data +_start: + .long 0 +_end: + +# _var is a pointer to the end of the __MY/__data section + .data +_var: +#if __x86_64__ + .quad _end +#else + .long _end +#endif + + .subsections_via_symbols \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/no-data-bundle/foo.c b/ld64/unit-tests/test-cases/no-data-bundle/foo.c deleted file mode 100644 index 5cb3e31..0000000 --- a/ld64/unit-tests/test-cases/no-data-bundle/foo.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -void foo() -{ - rand(); -} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile b/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile similarity index 70% rename from ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile rename to ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile index 45886c0..8db13a7 100644 --- a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile +++ b/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Inc. All rights reserved. +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,18 +24,16 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Check that symbols in no-dead-strip sections can be coalesced. -# -dead_strip inhibits weak coalescing in no_dead_strip section +# Check that the -no_zero_fill_sections works # run: all all: - ${CC} ${CCFLAGS} baz.c -c -o baz.o - libtool -static baz.o -o libbaz.a - ${CC} ${CCFLAGS} main.c foo.c libbaz.a -dead_strip -o main - nm -j main | grep _hidden | wc -l | grep 1 | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main -Wl,-no_zero_fill_sections + size -l main | grep __bss | grep "offset 0" | ${FAIL_IF_STDIN} + size -l main | grep __common | grep "offset 0" | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf main baz.o libbaz.a + rm main diff --git a/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c b/ld64/unit-tests/test-cases/no_zero_fill_sections/main.c similarity index 90% rename from ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c rename to ld64/unit-tests/test-cases/no_zero_fill_sections/main.c index 7e206e5..a04a2ca 100644 --- a/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c +++ b/ld64/unit-tests/test-cases/no_zero_fill_sections/main.c @@ -21,21 +21,14 @@ * * @APPLE_LICENSE_HEADER_END@ */ +#include - -__attribute__((used)) static void foo() -{ -} - - -__attribute__((used)) void bar() -{ -} - +static int a[2000]; +int b[2000]; int main() { - foo(); - bar(); + a[0] = 0; + b[0] = 0; return 0; -} +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile deleted file mode 100644 index 76a7347..0000000 --- a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# Verify an Objective-C object file when run through -# ld -r -x -# x86_64 obj-c runtime confused when static lib is stripped -# -# - -SELECTOR_REFS = "__OBJC,__message_refs" - -ifeq ($(ARCH),x86_64) - SELECTOR_REFS = "__DATA,__objc_selrefs" -endif -ifeq ($(ARCH),armv6) - SELECTOR_REFS = "__DATA,__objc_selrefs" -endif - - - -run: all - -all: - ${CC} ${CCFLAGS} test.m -c -o test.o - ${OBJECTDUMP} -no_content test.o | grep -B3 -A6 ${SELECTOR_REFS} > test.dump - - ${LD} -arch ${ARCH} -r test.o -x -o test-r.o - ${OBJECTDUMP} -no_content test-r.o | grep -B3 -A6 ${SELECTOR_REFS} > test-r.dump - - diff test.dump test-r.dump | ${PASS_IFF_EMPTY} - -clean: - rm -rf test.o test.dump test-r.o test-r.dump diff --git a/ld64/unit-tests/test-cases/operator-new/Makefile b/ld64/unit-tests/test-cases/operator-new/Makefile index 447ee87..8abf3e1 100644 --- a/ld64/unit-tests/test-cases/operator-new/Makefile +++ b/ld64/unit-tests/test-cases/operator-new/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,9 +30,6 @@ all: # verify if operator new is overridden that WEAK_DEFINES is set ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} - # verify if operator new is overridden but not exported, WEAK_DEFINES is not set - ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx -Wl,-exported_symbol,_main - otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN} # verify if operator new is not overridden that WEAK_DEFINES is not set ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx index b5d3272..3c99e35 100644 --- a/ld64/unit-tests/test-cases/operator-new/main.cxx +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -36,7 +36,7 @@ #if OP_NEW void* operator new(size_t s) throw (std::bad_alloc) { - return malloc(s); + return malloc(s);; } #endif diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile deleted file mode 100644 index fdb13d5..0000000 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile +++ /dev/null @@ -1,64 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Test that a public re-exported library is automatically added as a dependent -# unless nothing is used from it. -# - - -run: all - -all: - -# -sub_library for 10.4 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libmiddle.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.4 - ${FAIL_IF_BAD_MACHO} libother.dylib - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.4 - nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - -# -sub_library for 10.5 - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} libmiddle.dylib - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.5 - ${FAIL_IF_BAD_MACHO} libother.dylib - ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.5 - nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - - -clean: - rm -rf libbar.dylib libfoo.dylib libmiddle.dylib libother.dylib main diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c deleted file mode 100644 index 9c18401..0000000 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c +++ /dev/null @@ -1,5 +0,0 @@ - -int bar (void) -{ - return 1; -} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c deleted file mode 100644 index d0cdf47..0000000 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c +++ /dev/null @@ -1,4 +0,0 @@ -int foo (void) -{ - return 1; -} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c deleted file mode 100644 index 672ef9a..0000000 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c +++ /dev/null @@ -1,8 +0,0 @@ - -extern void bar(); - -int main() -{ - bar(); - return 0; -} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c deleted file mode 100644 index d3578a6..0000000 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c +++ /dev/null @@ -1,3 +0,0 @@ - -void middle() {} - diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c deleted file mode 100644 index 0cd6dda..0000000 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c +++ /dev/null @@ -1 +0,0 @@ -void other() {} diff --git a/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile deleted file mode 100644 index 36fb47b..0000000 --- a/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# i386 relocation error with negative offsets from local labels -# - -ifeq (${ARCH},i386) - TARGET = run-i386 -else - TARGET = run-other -endif - -run: ${TARGET} - -run-other: - ${PASS_IFF} /usr/bin/true - - -run-i386: - ${CC} ${ASMFLAGS} test.s -c -o test.o - ${OBJECTDUMP} test.o | grep "__data@0 plus 0xFFFFFFE2" | ${FAIL_IF_EMPTY} - - ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o - ${OBJECTDUMP} test-r.o | grep "__data@0 plus 0xFFFFFFE2" | ${PASS_IFF_STDIN} - - -clean: - rm -rf *.o diff --git a/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s b/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s deleted file mode 100644 index 3890f35..0000000 --- a/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - - -#if __i386__ - .text - .align 2 - -_negative_offset_from_local_label: - nop - .space 100 - movl -80+L3(,%eax,4), %edx - ret - - .data -L2: .space 50 -L3: .space 50 -_d: .long 0 - -#endif - - diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile b/ld64/unit-tests/test-cases/section-labels/Makefile similarity index 80% rename from ld64/unit-tests/test-cases/cstring-alt-segment/Makefile rename to ld64/unit-tests/test-cases/section-labels/Makefile index 4e55304..d783b16 100644 --- a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile +++ b/ld64/unit-tests/test-cases/section-labels/Makefile @@ -24,16 +24,14 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Check that __cstring sections in other segments are not coalesced -# ld coalesces C strings in different segments +# Check that ld resolves the magic section start/end symbols. # run: all all: - ${CC} ${CCFLAGS} main.c custom.s -o main - size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c -o main ${PASS_IFF_GOOD_MACHO} main clean: - rm main + rm main diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m b/ld64/unit-tests/test-cases/section-labels/main.c similarity index 71% rename from ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m rename to ld64/unit-tests/test-cases/section-labels/main.c index 4837911..a8c4bfa 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m +++ b/ld64/unit-tests/test-cases/section-labels/main.c @@ -21,26 +21,22 @@ * * @APPLE_LICENSE_HEADER_END@ */ - - #include -#include -@interface Foo @end -@implementation Foo -+(void)initialize { } -+(void)foo { - fprintf(stderr, "GOOD\n"); - exit(0); -} -+(void)bar { - fprintf(stderr, "BAD\n"); - abort(); -} -@end +struct stuff { int a; int b; }; + +struct stuff stuff1 __attribute__ ((section ("__DATA,__my"))) = { 1, 2}; +struct stuff stuff2 __attribute__ ((section ("__DATA,__my"))) = { 3 ,4 }; + +extern struct stuff* stuff_start __asm("section$start$__DATA$__my"); +extern struct stuff* stuff_end __asm("section$end$__DATA$__my"); + -void PublicFunction(void) +int main() { - [Foo foo]; - [Foo bar]; -} + struct stuff* p; + for (p = stuff_start; p < stuff_end; ++p) { + p->a = 0; + } + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/section-names-long/Makefile b/ld64/unit-tests/test-cases/section-names-long/Makefile deleted file mode 100644 index f31d9d6..0000000 --- a/ld64/unit-tests/test-cases/section-names-long/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Validate long section names are preserved -# corrupt metaclass entry in dynamic library -# - -all: - ${CC} ${CCFLAGS} main.c a.s c.s b.s -o main - nm -m main | grep __aaaaaaaaaaaaaa | grep __TEXT | grep _at | ${FAIL_IF_EMPTY} - nm -m main | grep __bbbbbbbbbbbbbb | grep __TEXT | grep _bt | ${FAIL_IF_EMPTY} - nm -m main | grep __cccccccccccccc | grep __TEXT | grep _ct | ${FAIL_IF_EMPTY} - nm -m main | grep __aaaaaaaaaaaaaa | grep __DATA | grep _ad | ${FAIL_IF_EMPTY} - nm -m main | grep __bbbbbbbbbbbbbb | grep __DATA | grep _bd | ${FAIL_IF_EMPTY} - nm -m main | grep __cccccccccccccc | grep __DATA | grep _cd | ${FAIL_IF_EMPTY} - ${PASS_IFF_GOOD_MACHO} main - -clean: - rm -rf main diff --git a/ld64/unit-tests/test-cases/section-names-long/a.s b/ld64/unit-tests/test-cases/section-names-long/a.s deleted file mode 100644 index d7e5847..0000000 --- a/ld64/unit-tests/test-cases/section-names-long/a.s +++ /dev/null @@ -1,9 +0,0 @@ - - .section __TEXT,__aaaaaaaaaaaaaa -_at: .space 128 - - .section __DATA,__aaaaaaaaaaaaaa -_ad: .space 128 - - - diff --git a/ld64/unit-tests/test-cases/section-names-long/b.s b/ld64/unit-tests/test-cases/section-names-long/b.s deleted file mode 100644 index a31d414..0000000 --- a/ld64/unit-tests/test-cases/section-names-long/b.s +++ /dev/null @@ -1,9 +0,0 @@ - - .section __TEXT,__bbbbbbbbbbbbbb -_bt: .space 128 - - .section __DATA,__bbbbbbbbbbbbbb -_bd: .space 128 - - - diff --git a/ld64/unit-tests/test-cases/section-names-long/c.s b/ld64/unit-tests/test-cases/section-names-long/c.s deleted file mode 100644 index 383b159..0000000 --- a/ld64/unit-tests/test-cases/section-names-long/c.s +++ /dev/null @@ -1,11 +0,0 @@ - - .section __TEXT,__cccccccccccccc -_ct: .space 128 - - - .section __DATA,__cccccccccccccc -_cd: .space 128 - - - - diff --git a/ld64/unit-tests/test-cases/section-names-long/main.c b/ld64/unit-tests/test-cases/section-names-long/main.c deleted file mode 100644 index df77448..0000000 --- a/ld64/unit-tests/test-cases/section-names-long/main.c +++ /dev/null @@ -1,4 +0,0 @@ - - -int main() { return 0; } - diff --git a/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile b/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile deleted file mode 100644 index cd6dc04..0000000 --- a/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -## -# Copyright (c) 2009 Apple Inc. All rights reserved. -# -# @APPLE_LICENSE_HEADER_START@ -# -# This file contains Original Code and/or Modifications of Original Code -# as defined in and that are subject to the Apple Public Source License -# Version 2.0 (the 'License'). You may not use this file except in -# compliance with the License. Please obtain a copy of the License at -# http://www.opensource.apple.com/apsl/ and read it before using this -# file. -# -# The Original Code and all software distributed under the License are -# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -# Please see the License for the specific language governing rights and -# limitations under the License. -# -# @APPLE_LICENSE_HEADER_END@ -## -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - -# -# Verify only dylibs with install paths in /System/Library or /usr/lib -# get LC_SEGMENT_SPLIT_INFO -# - - -run: all - -all: - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/lib/libfoo.dylib - otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /System/Library/Frameworks/Foo.framework/Foo - otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib - otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN} - ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib - otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} libfoo.dylib - -clean: - rm libfoo.dylib diff --git a/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c b/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c deleted file mode 100644 index 6924ac6..0000000 --- a/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c +++ /dev/null @@ -1,3 +0,0 @@ - -void foo() {} - diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile index fcb7e7d..c0647b3 100644 --- a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# Copyright (c) 2007 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,14 +23,18 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -POINTER_SEGMENT = __DATA -POINTER_SECTION = __nl_symbol_ptr +ifeq "${ARCH}" "i386" + POINTER_SEGMENT = __IMPORT + POINTER_SECTION = __pointers +else + POINTER_SEGMENT = __DATA + POINTER_SECTION = __nl_symbol_ptr +endif # # Test that using strip -R to selectively strip symbol names # of of a .o file still works with ld. -# And for i386 that there are no __IMPORT/__pointers left # run: all @@ -47,8 +51,6 @@ all: ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers - size -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN} - size -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN} ${PASS_IFF} diff dylib1.pointers dylib2.pointers clean: diff --git a/ld64/unit-tests/test-cases/weak_import/Makefile b/ld64/unit-tests/test-cases/weak_import/Makefile index cdacfc9..d1fa1f3 100644 --- a/ld64/unit-tests/test-cases/weak_import/Makefile +++ b/ld64/unit-tests/test-cases/weak_import/Makefile @@ -34,7 +34,7 @@ all: ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib ${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib - ${CC} ${CCFLAGS} main.c -o main-${ARCH} libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null @@ -43,12 +43,10 @@ all: nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null - nm -m main-${ARCH} | grep _data5 | grep -v weak >/dev/null - nm -m main-${ARCH} | grep _data6 | grep weak >/dev/null - #otool -rv main-${ARCH} | grep _data6 > /dev/null + otool -rv main-${ARCH} | grep _data6 > /dev/null ${FAIL_IF_BAD_MACHO} main-${ARCH} - ${CC} ${CCFLAGS} main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null @@ -57,7 +55,7 @@ all: nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null - #otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null + otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib clean: From 6e7bb46ca0447d9bbaedc3ba68673b6a14191269 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:32:39 +0100 Subject: [PATCH 04/48] 96-5 --- ld64/ChangeLog | 4805 ++++++++++++++++- ld64/ld64.xcodeproj/project.pbxproj | 14 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 2 +- ld64/src/ld/Architectures.hpp | 7 +- ld64/src/ld/ExecutableFile.h | 13 +- ld64/src/ld/LTOReader.hpp | 51 +- ld64/src/ld/MachOReaderDylib.hpp | 9 +- ld64/src/ld/MachOReaderRelocatable.hpp | 280 +- ld64/src/ld/MachOWriterExecutable.hpp | 1485 +++-- ld64/src/ld/ObjectFile.h | 8 +- ld64/src/ld/Options.cpp | 114 +- ld64/src/ld/Options.h | 6 +- ld64/src/ld/ld.cpp | 103 +- ld64/src/other/dyldinfo.cpp | 541 +- ld64/src/other/unwinddump.cpp | 390 +- .../test-cases/branch-islands/Makefile | 10 +- .../test-cases/branch-islands/space.s | 43 +- .../test-cases/cfstring-utf16/Makefile | 12 +- .../coalesce_weak_def_in_dylib/Makefile | 55 + .../coalesce_weak_def_in_dylib/foo.c | 4 + .../coalesce_weak_def_in_dylib/main.c | 17 + .../test-cases/cstring-alt-segment/Makefile | 39 + .../test-cases/cstring-alt-segment/custom.s | 8 + .../test-cases/cstring-alt-segment/main.c | 7 + .../test-cases/cstring-labels/foo.c | 2 + .../dead_strip-r_symbol_desc/Makefile | 43 + .../dead_strip-r_symbol_desc/main.c | 41 + .../dead_strip-weak-coalesce/Makefile | 41 + .../test-cases/dead_strip-weak-coalesce/baz.c | 7 + .../test-cases/dead_strip-weak-coalesce/foo.c | 25 + .../dead_strip-weak-coalesce/main.c | 13 + .../eh-coalescing-no-labels/Makefile | 2 +- .../unit-tests/test-cases/init-order/Makefile | 4 +- .../test-cases/init-order/expected-order.txt | 2 - .../unit-tests/test-cases/kext-basic/Makefile | 1 - .../label-on-end-of-section/Makefile | 0 .../test-cases/label-on-end-of-section/foo.s | 0 .../lto-dead_strip-all-hidden/Makefile | 41 + .../lto-dead_strip-all-hidden/bar.c | 4 + .../test-cases/lto-llvm-options/Makefile | 4 +- .../test-cases/lto-objc-archive/Makefile | 47 + .../test-cases/lto-objc-archive/bar.h | 6 + .../test-cases/lto-objc-archive/bar.m | 7 + .../test-cases/lto-objc-archive/bar2.c | 2 + .../test-cases/lto-objc-archive/foo.h | 8 + .../test-cases/lto-objc-archive/foo.m | 8 + .../test-cases/lto-objc-archive/foo2.c | 2 + .../test-cases/lto-objc-archive/main.m | 20 + .../test-cases/lto-weak_import/Makefile | 43 + .../test-cases/lto-weak_import/foo.c | 3 + .../test-cases/lto-weak_import/main.c | 13 + .../test-cases/no-data-bundle/Makefile | 38 + .../test-cases/no-data-bundle/foo.c | 6 + .../test-cases/no_zero_fill_sections/Makefile | 0 .../test-cases/no_zero_fill_sections/main.c | 0 .../objc-literal-pointers-strip/Makefile | 57 + .../objc-literal-pointers-strip/test.m | 46 + .../test-cases/operator-new/Makefile | 5 +- .../test-cases/operator-new/main.cxx | 2 +- .../re-export-optimizations-indirect/Makefile | 64 + .../re-export-optimizations-indirect/bar.c | 5 + .../re-export-optimizations-indirect/foo.c | 4 + .../re-export-optimizations-indirect/main.c | 8 + .../re-export-optimizations-indirect/middle.c | 3 + .../re-export-optimizations-indirect/other.c | 1 + .../test-cases/relocs-neg-from-local/Makefile | 51 + .../test-cases/relocs-neg-from-local/test.s | 43 + .../test-cases/section-names-long/Makefile | 42 + .../test-cases/section-names-long/a.s | 9 + .../test-cases/section-names-long/b.s | 9 + .../test-cases/section-names-long/c.s | 11 + .../test-cases/section-names-long/main.c | 4 + .../test-cases/shared-cache-dylib/Makefile | 46 + .../test-cases/shared-cache-dylib/foo.c | 3 + .../stripped-indirect-symbol-table/Makefile | 14 +- .../test-cases/weak_import/Makefile | 10 +- 76 files changed, 8047 insertions(+), 846 deletions(-) create mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c create mode 100644 ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c create mode 100644 ld64/unit-tests/test-cases/cstring-alt-segment/Makefile create mode 100644 ld64/unit-tests/test-cases/cstring-alt-segment/custom.s create mode 100644 ld64/unit-tests/test-cases/cstring-alt-segment/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c mode change 100644 => 100755 ld64/unit-tests/test-cases/label-on-end-of-section/Makefile mode change 100644 => 100755 ld64/unit-tests/test-cases/label-on-end-of-section/foo.s create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/bar.c create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/bar.h create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/bar.m create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/bar2.c create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/foo.h create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/foo.m create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/foo2.c create mode 100644 ld64/unit-tests/test-cases/lto-objc-archive/main.m create mode 100644 ld64/unit-tests/test-cases/lto-weak_import/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-weak_import/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-weak_import/main.c create mode 100644 ld64/unit-tests/test-cases/no-data-bundle/Makefile create mode 100644 ld64/unit-tests/test-cases/no-data-bundle/foo.c mode change 100644 => 100755 ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile mode change 100644 => 100755 ld64/unit-tests/test-cases/no_zero_fill_sections/main.c create mode 100644 ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c create mode 100644 ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c create mode 100644 ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile create mode 100644 ld64/unit-tests/test-cases/relocs-neg-from-local/test.s create mode 100644 ld64/unit-tests/test-cases/section-names-long/Makefile create mode 100644 ld64/unit-tests/test-cases/section-names-long/a.s create mode 100644 ld64/unit-tests/test-cases/section-names-long/b.s create mode 100644 ld64/unit-tests/test-cases/section-names-long/c.s create mode 100644 ld64/unit-tests/test-cases/section-names-long/main.c create mode 100644 ld64/unit-tests/test-cases/shared-cache-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/shared-cache-dylib/foo.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog index ab1e984..77b9256 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,13 +1,155 @@ ------ Tagged ld64-95.9 -2009-02-13 Nick Kledzik +----- Tagged ld64-96.5 - Back out Linker changes for H2 hang - * src/ld/Options.cpp: remove fPreventPageCrossingBranches - * src/ld/MachOWriterExecutable.hpp: remove layout of __text so there are not page crossing branches - * src/ld/MachOReaderRelocatable.hpp: parse but ignore ARM_THUMB_32BIT_BRANCH reloc +2009-07-28 Nick Kledzik + Thumb mode compilation isn't working on 3.1 beta 2 + * Fix instructions used in kBranchIslandToThumb1 case + + +----- Tagged ld64-96.4 + +2009-06-22 Nick Kledzik + + platform linker should use platform ld_classic + * Fix Options::gotoClassicLinker() to use realpath()s + + +2009-06-22 Nick Kledzik + + * Fix Options::setIPhoneVersionMin() to handle 2.x through 9.x + + +----- Tagged ld64-96.3 + +2009-06-17 Nick Kledzik + + * Change section sorting so that arm and ppc stub section is immediately after __text section + + +2009-06-17 Nick Kledzik + + don't use no-PIC stubs in any dylibs - it might conflict with codesigning + * In StubAtom::StubAtom() don't use kStubNoPIC for OS dylibs + + +----- Tagged ld64-96.2 + +2009-06-09 Nick Kledzik + + * Back out page-cross branch work around + + +----- Tagged ld64-96.1 + +2009-06-05 Nick Kledzik + + * Fix "duplicate symbol cache-line-crossing-stub" error by giving placeholders unique names + * Fix -pie by allowing relocs in x86 stubs and stub helpers + + +----- Tagged ld64-96 + +2009-06-04 Nick Kledzik + + * Darwin x86_64 static codegen is really dynamic code gen, so use LTO_CODEGEN_PIC_MODEL_DYNAMIC + + +2009-06-03 Nick Kledzik + + use lto_codegen_set_assembler_path() + + +2009-06-03 Nick Kledzik + + * In src/ld/LTOReader.hpp, move where -save-temps .bc file is saved to be after code model set + + +2009-06-01 Nick Kledzik + + Link Time Optimization error with 'dead code strip' + hidden symbol + * scan newAtoms returned from optimize() looking for ones already in the symbol table + * add test case unit-tests/test-cases/lto-dead_strip-all-hidden + + +2009-05-19 Nick Kledzik + + make dyld stubs smaller/faster + * Add new addSynthesizedAtoms() method to Writer, called before atoms are sorted + * Fix throwf() and warning() to check printf types + * Add arm::kPointerDiff12 to support fast arm stubs + * Add new ContentType values for stubs and (non)lazy pointers + * Add new variant of arm stub this is one instruction + + +2009-05-13 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: add warnings to dyldinfo target + * src/other/dyldinfo.cpp: support classic LINKEDIT format + + +2009-05-12 Nick Kledzik + + * src/ld/MachOWriterExecutable.hpp: fix optimization to skip branch islands with ARM bl instructions + + +2009-05-12 Nick Kledzik + + creation of __unwind_info section can fail if hundreds of functions cannot be compact encoded + * src/ld/MachOWriterExecutable.hpp: fix when to make regular vs compressed pages + + +2009-05-08 Nick Kledzik + + * src/ld/ld.cpp: enhance -save-temps to also write out optimized bitcode file + + +2009-05-08 Nick Kledzik + + * src/ld/ld.cpp: fix -order_file_statistics to print each symbol not found and correct total + + +2009-05-08 Nick Kledzik + + linker should be able to coalesce UTF16 strings + * src/ld/MachOReaderRelocatable.hpp: parse __ustring section by labels but synthesize an + atom name based on the content. Leverage for __cfstring section + * unit-tests/test-cases/cfstring-utf16: update test case + + +2009-05-07 Nick Kledzik + + * src/ld/MachOWriterExecutable.hpp: put branch islands further apart if there is no thumb code + + +2009-05-07 Nick Kledzik + + LINKEDIT optimizations for iPhone + * src/ld/ObjectFile.h: Recognize iPhoneOS 3.1 + * src/ld/Options.cpp: iPhoneOS 3.1 => use compressed LINKEDIT + * src/ld/MachOWriterExecutable.hpp: support generating compressed LINKEDIT for arm + + +2009-05-04 Nick Kledzik + + Add linker support for ARM branch islands + Add labels to linker synthesized jump islands + * src/ld/MachOWriterExecutable.hpp: reworked BranchIslandAtom and createBranchIslands to support arm/thumb + * src/ld/ObjectFile.h: added kBranchIsland + * unit-tests/test-cases/branch-islands: updated test case to check arm/thumb + + +2009-04-30 Nick Kledzik + + * src/ld/Options.cpp: fix custom stack base address for arm + + +2009-04-30 Nick Kledzik + + likely incorrect warning about common symbols + * src/ld/Options.cpp: ignore LD_WARN_COMMONS in -r mode + ----- Tagged ld64-95.8.3 @@ -111,8 +253,4659 @@ * src/ld/MachOReaderRelocatable.hpp: support new ARM_THUMB_32BIT_BRANCH reloce +----- Tagged ld64-95.2.10 + +2009-04-02 Nick Kledzik + + corrupt metaclass entry in dynamic library + * src/ld/ld.cpp: change Section constructor to copy segment and section names + + +----- Tagged ld64-95.2.9 + +2009-04-02 Nick Kledzik + + Update ld64 for new triples introduced in 6654669 to support ARM LLVM + * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples + + +----- Tagged ld64-95.2.8 + +2009-03-24 Nick Kledzik + + anonymous functions have the compact unwind info computed wrong + * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom + + +----- Tagged ld64-95.2.7 + +2009-03-11 Nick Kledzik + + AddressBook incorrectly gets _objc_msgSend from WebKit + * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib + that is already explictly or implicitly linked. + * unit-tests/test-cases/re-export-optimizations-indirect: add test case + + +2009-03-10 Nick Kledzik + + dyld weak linking optimization leaves some symbols unbound + * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference + to a symbol in a dylib that is a weak definition + * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case + + +2009-03-10 Nick Kledzik + + many OS i386 OS dylibs still have __IMPORT segment + * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr + * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem + + +----- Tagged ld64-95.2.6 + +2009-02-27 Nick Kledzik + + ld might set MH_WEAK_DEFINES when it should not + * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef + that will be exported when computing MH_WEAK_DEFINES + * unit-tests/test-cases/operator-new: updated to reproduce issue + + +----- Tagged ld64-95.2.5 + +2009-02-24 Nick Kledzik + + x86_64 obj-c runtime confused when static lib is stripped + * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings + * unit-tests/test-cases/objc-literal-pointers-strip: added test case + + +----- Tagged ld64-95.2.4 + +2009-02-23 Nick Kledzik + + * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs + + +2009-02-18 Nick Kledzik + + Writer::symbolIndex() uses a linear search and does not scale + * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better + + +2009-02-18 Nick Kledzik + + Use new compact encodings that handle all register permutations + * src/ld/Architectures.hpp: add kSectionOffset24 + * src/ld/ObjectFile.h: add getFDE() + * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding + * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed + * src/other/unwinddump.cpp: update unwinddump output to display register save set + + +2009-02-16 Nick Kledzik + + runtime error with bundle for 10.5 that has weak external symols + * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions + + +2009-02-15 Nick Kledzik + + i386 relocation error with negative offsets from local labels + * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label + * unit-tests/test-cases/relocs-neg-from-local: add test case + + +2009-02-12 Nick Kledzik + + -dead_strip inhibits weak coalescing in no_dead_strip section + * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms + * unit-tests/test-cases/dead_strip-weak-coalesce: added test case + + +2009-02-12 Nick Kledzik + + x86_64 weak_import broken for initialized data + * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader::addRelocReference() + * src/other/dyldinfo.cpp: update to display weak_import attribute + * unit-tests/test-cases/weak_import: updated test case + + +2009-02-06 Nick Kledzik + + ld parsing of __eh_frame unwind information is slow + * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 + + +----- Tagged ld64-95.2.3 + +2009-02-04 Nick Kledzik + + ld: warning: can't add line info to anonymous symbol + * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs + + +----- Tagged ld64-95.2.2 + +2009-02-02 Nick Kledzik + + ld -r does not preserve the N_NO_DEAD_STRIP bit + * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() + * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case + + +----- Tagged ld64-95.2.1 + +----- Tagged ld64-95.2.10 + +2009-04-02 Nick Kledzik + + corrupt metaclass entry in dynamic library + * src/ld/ld.cpp: change Section constructor to copy segment and section names + + +----- Tagged ld64-95.2.9 + +2009-04-02 Nick Kledzik + + Update ld64 for new triples introduced in 6654669 to support ARM LLVM + * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples + + +----- Tagged ld64-95.2.8 + +2009-03-24 Nick Kledzik + + anonymous functions have the compact unwind info computed wrong + * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom + + +----- Tagged ld64-95.2.7 + +2009-03-11 Nick Kledzik + + AddressBook incorrectly gets _objc_msgSend from WebKit + * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib + that is already explictly or implicitly linked. + * unit-tests/test-cases/re-export-optimizations-indirect: add test case + + +2009-03-10 Nick Kledzik + + dyld weak linking optimization leaves some symbols unbound + * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference + to a symbol in a dylib that is a weak definition + * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case + + +2009-03-10 Nick Kledzik + + many OS i386 OS dylibs still have __IMPORT segment + * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr + * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem + + +----- Tagged ld64-95.2.6 + +2009-02-27 Nick Kledzik + + ld might set MH_WEAK_DEFINES when it should not + * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef + that will be exported when computing MH_WEAK_DEFINES + * unit-tests/test-cases/operator-new: updated to reproduce issue + + +----- Tagged ld64-95.2.5 + +2009-02-24 Nick Kledzik + + x86_64 obj-c runtime confused when static lib is stripped + * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings + * unit-tests/test-cases/objc-literal-pointers-strip: added test case + + +----- Tagged ld64-95.2.4 + +2009-02-23 Nick Kledzik + + * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs + + +2009-02-18 Nick Kledzik + + Writer::symbolIndex() uses a linear search and does not scale + * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better + + +2009-02-18 Nick Kledzik + + Use new compact encodings that handle all register permutations + * src/ld/Architectures.hpp: add kSectionOffset24 + * src/ld/ObjectFile.h: add getFDE() + * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding + * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed + * src/other/unwinddump.cpp: update unwinddump output to display register save set + + +2009-02-16 Nick Kledzik + + runtime error with bundle for 10.5 that has weak external symols + * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions + + +2009-02-15 Nick Kledzik + + i386 relocation error with negative offsets from local labels + * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label + * unit-tests/test-cases/relocs-neg-from-local: add test case + + +2009-02-12 Nick Kledzik + + -dead_strip inhibits weak coalescing in no_dead_strip section + * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms + * unit-tests/test-cases/dead_strip-weak-coalesce: added test case + + +2009-02-12 Nick Kledzik + + x86_64 weak_import broken for initialized data + * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader::addRelocReference() + * src/other/dyldinfo.cpp: update to display weak_import attribute + * unit-tests/test-cases/weak_import: updated test case + + +2009-02-06 Nick Kledzik + + ld parsing of __eh_frame unwind information is slow + * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 + + +----- Tagged ld64-95.2.3 + +2009-02-04 Nick Kledzik + + ld: warning: can't add line info to anonymous symbol + * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs + + +----- Tagged ld64-95.2.2 + +2009-02-02 Nick Kledzik + + ld -r does not preserve the N_NO_DEAD_STRIP bit + * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() + * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case + + +----- Tagged ld64-95.2.1 + +2009-01-29 Nick Kledzik + + gcc DejaGnu failure: building longcall/dylib library + * src/ld/MachOWriterExecutable.hpp: if no __DATA sections insert non-lazy pointers at end of __TEXT segment + * unit-tests/test-cases/no-data-bundle: added test case + + ----- Tagged ld64-95.2 2009-01-06 Nick Kledzik strip -S fails with "new trie is larger than original" + * src/other/PruneTrie.cpp: don't align trie more than original trie was aligned + + +----- Tagged ld64-95.1 + +2008-12-21 Nick Kledzik + + * src/ld/MachOWriterExecutable.hpp: in new linkedit format, make sure only exported symbols + make it into weak binding info + + +----- Tagged ld64-95 + +2008-12-18 Nick Kledzik + + * src/ld/Options.cpp: move check for fSharedRegionEligible until fPrebind has stabilized + + +2008-12-18 Nick Kledzik + + Generate new compressed LINKEDIT when targeting 10.6 + * src/ld/Options.cpp: turn on compressed LINKEDIT by default + + +----- Tagged ld64-94.1 + +2008-12-16 Nick Kledzik + + * src/ld/Options.cpp: Fix -F handling in buildSearchPaths() + + +----- Tagged ld64-94 + +2008-12-15 Nick Kledzik + + * doc/man/man1/ld.1: document new options + + +2008-12-15 Nick Kledzik + + linker should enforce all .o files have same sub-type, and ignore sub-type of dylibs + * doc/man/man1/ld.1: update man page about -allow_sub_type_mismatches + * src/ld/ld.cpp: call validFile() with new arguments + * src/ld/MachOReaderRelocatable.hpp: add new arguments to validFile() + * src/ld/Options.cpp: Support LD_ALLOW_CPU_SUBTYPE_MISMATCHES and -allow_sub_type_mismatches + + +2008-12-15 Nick Kledzik + + -syslibroot should skip standard search paths not in the SDK + * src/ld/Options.cpp: in buildSearchPaths() if an SDK is specified don't add + standard search paths not in the SDK. + + +2008-12-15 Nick Kledzik + + ld: remove "can't make compact unwind encoding" warning + * src/ld/ObjectFile.h: add fWarnCompactUnwind + * src/ld/Options.cpp: -warn_compact_unwind --> fWarnCompactUnwind + * src/ld/MachOReaderRelocatable.hpp: test fWarnCompactUnwind before warning + + +2008-12-15 Nick Kledzik + + Add dtrace usdt support for arm to ld64 + * src/ld/MachOWriterExecutable.hpp: handle arm::kDtraceIsEnabledSite + * unit-tests/test-cases/dtrace-static-probes: use is-enabled in test case + + +----- Tagged ld64-93 + +2008-12-11 Nick Kledzik + + * src/ld/ObjectFile.h: add fIPhoneVersionMin to track min iPhoneOS version + * src/ld/Options.cpp: use fIPhoneVersionMin + + +2008-12-11 Nick Kledzik + + non-lazy pointer to non-global tentative definition encoded wrong + * src/ld/MachOWriterExecutable.hpp: don't use INDIRECT_SYMBOL_LOCAL for tentative definitions + * unit-tests/test-cases/non-lazy-r: updated test case + + +2008-12-11 Nick Kledzik + + kernel fails to boot when ld64 used for intermediate ld -r step + * src/ld/MachOWriterExecutable.hpp: in -r mode when generating a scattered sect-diff reloc for + i386/arm, special case when from target is not the atom + the relocation is in. + * unit-tests/test-cases/relocs-asm: update test case + + +2008-12-11 Nick Kledzik + + * src/ld/ld.cpp: handle new __program_vars section + * src/ld/MachOWriterExecutable.hpp: handle inserting synthesized sections when there is no __dyld section + + +2008-12-11 Nick Kledzik + + * src/ld/MachOReaderRelocatable.hpp: Fix getDescription() to work when direct reference is to anonymous atom + + +2008-12-10 Nick Kledzik + + * src/ld/Options.cpp: enable LD_FORCE_NO_PREBIND to be used with arm + + +2008-12-10 Nick Kledzik + + Developer tool to print the new compressed LINKEDIT information + * src/other/dyldinfo.cpp: fix typo in usage() + + +2008-12-05 Nick Kledzik + + SnowLeopard kernel should compile warning free + * src/ld/MachOReaderRelocatable.hpp: correct parse two global labels at end of section and make one an alias + * unit-tests/test-cases/end-label: update test case + + +2008-12-04 Nick Kledzik + + Better warning than "PPC_RELOC_JBSR should not be using an external relocation" + * src/ld/MachOReaderRelocatable.hpp: issue warning with .o path if it was compiled with -mlong-branch + + +2008-12-04 Nick Kledzik + + linker should not map __pointers -> __nl_symbol_ptr unless actually making new LINKEDIT + * src/ld/ObjectFile.h: add fMakeCompressedDyldInfo for readers to see + * src/ld/Options.cpp: set fMakeCompressedDyldInfo for readers to see + * src/ld/MachOReaderRelocatable.hpp: check fMakeCompressedDyldInfo + + +2008-12-02 Nick Kledzik + + * src/ld/debugline.c: fix error handling in line_open() + + +2008-11-26 Nick Kledzik + + vtable with thumb entries broke after ld -r + * src/ld/MachOReaderRelocatable.hpp: if target of reloc is thumb, mask thumb bit off addend + * unit-tests/test-cases/thumb-pointer: added test case + + +2008-11-26 Nick Kledzik + + * src/ld/Option.cpp: Fix how crashreporterBuffer is created to not miss some arguments + + +2008-11-24 Nick Kledzik + + Security.framework has some duplicate FDEs for some functions + * src/ld/ld.cpp: remove fDeadAtoms from fLiveAtoms when there are weak atoms overriden by late loads + * unit-tests/test-cases/dead_strip-archive-eh: added test case + + +----- Tagged ld64-92 + +2008-11-21 Nick Kledzik + + * src/ld/MachOReaderDylib.hpp: if export_size is zero, no need to parse trie + * src/abstraction/MachOTrie.hpp: gracefully handle empty trie + + +2008-11-21 Nick Kledzik + + strip(1) support for new compressed LINKEDIT information + * ld64.xcodeproj/project.pbxproj: build and install new libprunetrie.a + * src/other/prune_trie.h: added + * src/other/PruneTrie.cpp: implements prune_trie() + + +2008-11-21 Nick Kledzik + + * src/ld/ld.cpp: if an export file is used and all weak symbols are masked, don't set WEAK_DEFINES + * unit-tests/test-cases/weak-def-flag: added test case + + +2008-11-20 Nick Kledzik + + Generate new compressed LINKEDIT when targeting 10.6 + * src/ld/MachOWriterExecutable.hpp: support generating new compressed format + * src/ld/MachOReaderRelocatable.hpp: new compress format implies non-lazy pointers in __DATA for i386 + * src/ld/MachOReaderDylib.hpp: support linking aginst new format + * src/ld/Options.cpp: suppport -exported_symbols_order and -no_compact_linkedit + * src/ld/ld.cpp: track which atoms have weak counter parts in dylibs + * src/other/dyldinfo.cpp: added tool to display new LINKEDIT format + * ld64.xcodeproj/project.pbxproj: add dyldinfo tool + * unit-tests/*: lots of fixes to work with new format + + +2008-11-20 Nick Kledzik + + ld64 should preserve N_WEAK_REF when linking MH_KEXT_BUNDLEs + * src/ld/MachOWriterExecutable.hpp: set up fWeakImportMap in synthesizeKextGOT() + + +2008-11-19 Nick Kledzik + + VideoToolbox.framework has bad __TEXT.__eh_frame info + * src/ld/Options.cpp: add -no_eh_labels option for use with -r + * src/ld/MachOWriterExecutable.hpp: generate correct x86_64 labeless relocs in -r mode + * src/ld/MachOReaderRelocatable.hpp: now ignore all labels and relocations in + __TEXT/__eh_frame section and rely on getCFIs() from libunwind + * unit-tests/test-cases/eh-coalescing-no-labels: add test case + + +2008-11-19 Nick Kledzik + + LTO doesn't like dtrace symbols + * src/ld/LTOReader.hpp: ignore __dtrace_probe undefines in bitcode files + + +2008-11-14 Nick Kledzik + + * src/abstraction/MachOFileAbstraction.hpp: fix to work with 10.5 headers + + +----- Tagged ld64-91 + +2008-11-07 Nick Kledzik + + Remove COMPACT_UNWIND_SUPPORT conditionalizing + + +2008-11-06 Nick Kledzik + + Reorganize source layout. ld sources are now in "ld", + and other tools are in "other". + + +2008-11-05 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: start installing unwinddump tool + * src/UnwindDump.cpp: support -arch option + * doc/man/man1/unwinddump.1: create man page + + +2008-11-05 Nick Kledzik + + linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries + * src/ld.cpp: in synthesizeDebugNotes() set other field of OSO to be subtype + + +2008-11-05 Nick Kledzik + + Need a linker option to load all objects from one library + * src/Options.cpp: support -force_load option + * src/ArchiveReader.hpp: Add fForceLoad ivar + * doc/man/man1/ld.1: update man page with -force_load option + * unit-tests/test-cases/archive-force-load: add test case + + +2008-11-05 Nick Kledzik + + Dtrace Probe Warnings: SnowLeopard kernel should compile warning free + * src/ld.cpp: don't generate GSYM stabs for old style __dtrace_probe + * src/MachOReaderRelocatable.hpp: fix test for deciding if a symbol is an alias + + +2008-11-04 Nick Kledzik + + ADOBE: XCODE: ld: duplicate typeinfo in executable + * src/ld.cpp: in dead-strip mode, record overriden symbols and later rebind all uses + * unit-tests/test-cases/dead_strip-archive-weak: add test case + + +2008-11-03 Nick Kledzik + + support increased branch range in Thumb-2 + * src/MachOReaderRelocatable.hpp: handle full branch range in addRelocReference() + * unit-tests/test-cases/branch-distance: added test case + +2008-10-31 Devang Patel + + Sqlite 3.5.4 built with lvm-gcc-4.2 -O4 fails regression test + * src/LTOReader.hpp: Use real atom scope when real atom is available. + Preserve globals while optimizing an executable. + +2008-10-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support all encodings in getEncodedP() + + +----- Tagged ld64-90 + +2008-10-30 Nick Kledzik + + icc has dwarf unwind info that is different than gcc + * src/MachOReaderRelocatable.hpp: support more encodings in getEncodedP() + + +2008-10-23 Nick Kledzik + + build ld64 for x86_64 + * ld64.xcodeproj/project.pbxproj: add X86_64 to valid archs + + +2008-10-23 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: use generated @$(DERIVED_FILE_DIR)/linker_opts for extra + linker options. This allows linker to be built if LTO headers and libs are missing. + + +2008-10-23 Nick Kledzik + + Linker warning not shown in the Xcode build log + * src/Options.cpp: add colon to format string in warning() + + +----- Tagged ld64-89.3 + +2008-10-24 Nick Kledzik + + ld64-89 broke TOT OpenGL libProgrammability x86_64 build + * src/MachOReaderRelocatable.hpp: add cast in getEncodedP() + + +----- Tagged ld64-89.2 + +2008-10-23 Nick Kledzik + + SnowLeopard: Libsystem built with ld64-89.1 causes crashes + * src/MachOReaderRelocatable.hpp: when FDE information causes __text atom to be split, make the + atoms follow-on pairs. + + +----- Tagged ld64-89.1 + +2008-10-22 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: for x86_64 __eh_frame force direct references + + +2008-10-21 Nick Kledzik + + * src/ObjectDump.cpp: Use getContentType() to see if content type is a cstring + + +----- Tagged ld64-89 + +2008-10-21 Nick Kledzik + + 10A180 with QT-1119 roots: iTunes and QuickTime cannot play back purchased videos + linker should not need .eh labels + * src/MachOWriterExecutable.hpp: use kCFIType to set section attributes + * src/MachOReaderRelocatable.hpp: use libunwind's CFITuple to parse __eh_frame content + * src/ld.cpp: Add adjustScope() phase instead of demoting scope within symboltable.add() + * unit-tests/test-cases/eh-stripped-symbols: added test case + + +----- Tagged ld64-88.1 + +2008-10-16 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT + * src/MachOWriterExecutable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT + + +2008-09-30 Nick Kledzik + + OBJC2: Reorder __DATA,__objc_* sections by writedness + * src/ld.cpp: change sorting order of Sections + + +2008-09-29 Nick Kledzik + + Executable produced by XCode 3.2 on 10.6 crashes on 10.3.9 + * src/MachOWriterExecutable.hpp: set objc_module_info_addr field of module table + + +----- Tagged ld64-88 + +2008-09-25 Nick Kledzik + + kexts need to be built as MH_BUNDLE mach-o files + * src/ld.cpp: use getUndefinedProxyAtom() with kKextBundle + * src/MachOFileAbstraction.hpp: add MH_KEXT_BUNDLE + * src/Options.cpp: support -kext for all architectures + * src/MachOWriterExecutable.hpp: support kKextBundle to make a bundle like kext + * unit-tests/test-cases/kext-basic: added test case + + +2008-09-25 Nick Kledzik + + ld invoking wrong ld_classic + * src/Options.cpp: first look for ld_classic relative to ld itself + + +2008-09-25 Nick Kledzik + + ld fails to link references from 32 bit code into 64 bit code + Desired 32-bit absolute relocation + * src/Architectures.hpp: add x86_64::kPointer32 + * src/MachOReaderRelocatable.hpp: support X86_64_RELOC_UNSIGNED with length=2 + * src/MachOWriterExecutable.hpp: support x86_64::kPointer32 + * unit-tests/test-cases/relocs-asm/relocs-asm.s: added 32-bit pointer tests + + +2008-09-25 Nick Kledzik + + Should be able to mark dylibs as auto-dead-dylib-strip + * src/Options.h: add fMarkDeadStrippableDylib + * src/MachOReaderDylib.hpp: check MH_DEAD_STRIPPABLE_DYLIB + * src/ObjectFile.h: add deadStrippable() + * src/MachOFileAbstraction.hpp: add MH_DEAD_STRIPPABLE_DYLIB + * src/Options.cpp: support -mark_dead_strippable_dylib + * src/MachOWriterExecutable.hpp: test reader->deadStrippable(), set MH_DEAD_STRIPPABLE_DYLIB + * doc/man/man1/ld.1: update man page + * unit-tests/test-cases/dead_strippable_dylib: added test case + + +2008-09-25 Nick Kledzik + + ER: Add -seg_page_size option + * src/Options.cpp: add -seg_page_size option + * src/MachOWriterExecutable.hpp: use new page size info when laying out segments + * doc/man/man1/ld.1: update man page + + +2008-09-24 Nick Kledzik + + -arch_errors_fatal not working + * src/ld.cpp: check fOptions.errorOnOtherArchFiles() + * src/Options.cpp: turn -arch_errors_fatal into fOptions.errorOnOtherArchFiles() + + +2008-09-24 Nick Kledzik + + CrashTracer: [USER] 1 crash in ld at ld: 0x5ce02 + * src/ld.cpp: abort if resolve() finds an unresolved reference, rather than allow a future crash + + +2008-09-24 Nick Kledzik + + linker crashes linking X86-64 with -fwritable-strings + * src/MachOReaderRelocatable.hpp: handle unbound cfstring references + * unit-tests/test-cases/cfstring-coalesce: update test case + + +2008-09-24 Nick Kledzik + + ld64: bl out of range (-17147704 max is +/-16M) on ppc + * src/MachOWriterExecutable.hpp: tweak branch island regions to be every 14MB instead of 15MB + + +2008-09-24 Nick Kledzik + + -filelist fails with comma in path + * src/Options.cpp: in loadFileList() first try without special comma meaning + * unit-tests/test-cases/filelist/Makefile: update test case + + +2008-09-23 Nick Kledzik + + nop not used when aligning functions in -r mode + * src/MachOWriterExecutable.hpp: change check for when to pad with nops to not test segment's name + + +2008-09-23 Nick Kledzik + + "-pie can only be used when linking a main executable" should be a warning, not an error + * src/Options.cpp: make -pie on a dylib or bundle be a warning instead of an error + + +2008-09-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: add warning if dwarf cannot be encoded as compact unwind + + +2008-09-18 Nick Kledzik + + * src/LTOReader.hpp: re-enable use of lto_codegen_debug_options() + + +2008-09-16 Nick Kledzik + + ld does not always set S_CSTRING_LITERALS on __TEXT,__cstring + * src/MachOReaderRelocatable.hpp: add getContentType() to SymbolAtom + * src/MachOWriterExecutable.hpp: for x86_64 don't override named cstrings with LC* name + + +2008-09-10 Nick Kledzik + + * Options.cpp: add __crashreporter_info__ to communicate command line to crash reporter + * ld64.xcodeproj/project.pbxproj: leave local symbols in ld to provide better crash reports + + +2008-09-08 Nick Kledzik + + 161569 GCC 4.2 - breakpoints no longer work for a large number of functions + * src/MachOReaderRelocatable.hpp: support DW_FORM_strp out-of-line strings when parsing line table + + +2008-09-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix compact unwind personality for dyld and -slow_stubs + + +2008-08-29 Nick Kledzik + + -weak_library no longer forces uses to be weak_import + * src/MachOWriterExecutable.hpp: use fWeakImport on dylib to force proxy atoms into fWeakImportMap + * unit-tests/test-cases/weak_import-force: added test case + + +2008-08-29 Nick Kledzik + + linker should order __DATA segment to reduce dyld dirtied pages + * src/Options.cpp: add fOrderData and support -no_data_order + * src/ld.cpp: modify tweakLayout() to sort atoms with relocations to start of __data section + + +2008-08-27 Nick Kledzik + + * src/Options.cpp: back out + + +----- Tagged ld64-87.5 + +2008-08-26 Nick Kledzik + + some projects show _Unwind_Resume coming from libSystem.B.dylib + * src/Options.cpp: swap any early symlinks to libSystem with libgcc_s + + +----- Tagged ld64-87.4 + +2008-08-25 Nick Kledzik + + some projects show _Unwind_Resume coming from libSystem.B.dylib + * src/Options.cpp: swap any early libSystem with libgcc_s + + +2008-08-15 Nick Kledzik + + Unable to build ppc debug builds (linker out of range error) + * src/MachOWriterExecutable.hpp: in addPPCBranchIslands() look ahead so large atoms don't push out branch islands + + +----- Tagged ld64-87.3.1 + +2008-09-08 Nick Kledzik + + i386 dylibs have incorrect personality pointers when put in dyld shared cache + * src/MachOWriterExecutable.hpp: in addCrossSegmentRef() handle kImageOffset32 to __IMPORT segment + + +----- Tagged ld64-87.3 + +2008-08-09 Nick Kledzik + + work around compiler gcc_except_table alignment + * src/ObjectFile.h: change getLSDA() to return a reference instead of an atom + * src/MachOReaderRelocatable.hpp: special case __eh_frame 64-bit pointer diff relocations + * src/MachOWriterExecutable.hpp: track lsda offset when creating __unwind_info section + * src/UnwindDump.cpp: log when LDSA content does not start with 0xFF + +----- Tagged ld64-87.2 + +2008-08-07 Nick Kledzik + + 10A141: libuwind falls back to dwarf and makes whole system super slow + * src/MachOWriterExecutable.hpp: Fix sign extension bug with x86_64::kPointerDiff24 + * src/UnwindDump.cpp: warn about mangled LSDA entries when dumping unwind section + + +----- Tagged ld64-87.1 + +2008-08-03 Nick Kledzik + + * src/LTOReader.hpp: Don't use lto_codegen_debug_options until newer libLTO.dylib is available + + +----- Tagged ld64-87 + +2008-07-21 Nick Kledzik + + * src/Options.cpp: Always set fAutoOrderInitializers=false for dyld + + +2008-07-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix when regular vs compressed __unwind_info pages are generated + * src/UnwindDump.cpp: fix function name decoding in regular pages + + +2008-07-21 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: don't allow ld to build for x86_64 until libdtrace.dylib is available + + +2008-07-18 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't crash if debug_line section has no line table + + +2008-07-18 Nick Kledzik + + Duplicate probe firings in Security.framework + * src/LTOReader.hpp: optimize() now returns atoms optimized away + * src/ObjectFile.h: optimize() should return if it did anything + * src/ArchiveReader.hpp: pass through optimize() result + * src/ld.cpp: rework dtrace probe processing as a new pass to prevent double counting + + +2008-07-15 Nick Kledzik + + automatically order initializers to start of __TEXT + * src/Options.cpp: add -no_order_inits option + * src/MachOReaderRelocatable.hpp: merge __StaticInit into __text + * src/ObjectFile.h: add fAutoOrderInitializers + * src/ld.cpp: sort initializer to start of __text and terminators to end + * doc/man/man1/ld.1: add doc about -no_order_inits + * unit-tests/test-cases/init-order: add test case + +2008-07-15 Nick Kledzik + + Only add LC_SEGMENT_SPLIT_INFO to dylibs that might be in the shared cache + * src/MachOWriterExecutable.hpp: re-layout load commands after split-seg data computed + * src/Options.cpp: non-public install name will disable split-seg load command + + +2008-07-14 Nick Kledzik + + ld -r for x86_64 is changing visibility of cstring constants + * src/MachOWriterExecutable.hpp: force x86_64 cstring labels to be local in -r mode + * unit-tests/test-cases/cstring-label: added test case + + +2008-07-11 Nick Kledzik + + ld not adding updating LC_SEGMENT_SPLIT_INFO with __unwind_info section + * src/MachOWriterExecutable.hpp: run createSplitSegContent() after __unwind_info section is created + +2008-07-10 Nick Kledzik + + * src/LTOReader.hpp: improve missing symbol error message + + +2008-07-09 Nick Kledzik + + linker should order __DATA segment to reduce dyld dirtied pages + * src/ld.cpp: first phase, order sections + + +2008-07-08 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: remove "coal" sections when creating a final linked image + + +2008-07-08 Nick Kledzik + + ld: add support for mllvm LTO options + * src/Options.cpp: support -mllvm option + * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options + * src/ld.cpp: pass llvmOptions to optimize() + * src/Options.h: add fLLVMOptions + * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() + * src/ObjectFile.h: add llvmOptions parameter to optimize() + * unit-tests/test-cases/lto-llvm-options: add test case + + +2008-07-07 Nick Kledzik + + Linker fails with: 24-bit pointer diff out of range in unwind info in unwind info from... + * src/MachOWriterExecutable.hpp: fix when to fallback to uncompressed unwind info + + +2008-07-03 Nick Kledzik + + ld crash with gcc-4.0 code that uses a zero sized array + * src/MachOReaderRelocatable.hpp: handle zero size atom in a zero sized section + + +2008-07-03 Nick Kledzik + + ld crashes when bad ppc relocs are found + * src/MachOReaderRelocatable.hpp: change all missing PAIR warnings to errors + + +2008-07-02 Nick Kledzik + + when linking a kext the static linker should leave a pad in the headers to allow code signing + * src/MachOWriterExecutable.hpp: add padding for load commands in object files + * unit-tests/test-cases/code-signed-object-file: added test case + + +2008-07-02 Nick Kledzik + + LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype + * src/MachOWriterExecutable.hpp: correctly set segment size info in object files + * unit-tests/test-cases/no-object-symbols: add test case + + +2008-06-26 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: enable ld and rebase targets to build for x86_64 + * src/rebase.cpp: remove unused fRelocBase field that was not 64-bit clean + * src/MachOReaderRelocatable.hpp: fix getEncodedP() to be 64-bit clean + + +----- Tagged ld64-86.3 + +2008-06-17 Nick Kledzik + + * src/ld.cpp: fix loadUndefines() to double check undefine symbol was not already loaded + + +----- Tagged ld64-86.2 + +2008-06-14 Nick Kledzik + + * srd/ld.cpp: Add NULL check in getTentativesNames() + + +----- Tagged ld64-86.1 + +2008-06-06 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix header padding calculation for dyld + + +----- Tagged ld64-86 + +2008-06-04 Nick Kledzik + + * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + + +2008-06-04 Nick Kledzik + + * src/ObjectFile.h: add deadAtoms parameter to optimize() + * src/ld.cpp: ditto + * src/ArchiveReader.hpp: ditto + * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs + * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away + * unit-tests/test-cases/lto-weak-native-override: add test case + + +2008-06-04 Nick Kledzik + + LTO : 176.gcc and 177.mesa build failure at -O4 + * src/LTOReader.hpp: make sure internal is returned by getAtoms() + * unit-tests/test-cases/lto-archive-dylib: update test case + + +2008-06-03 Nick Kledzik + + fix for 5613343 need to search for definitions for common symbols is broken + * src/ld.cpp: modify loadUndefines() to check for undefines in all files and tentative definitions but only in archives + * src/machochecker.cpp: check for undefine symbols and external symbols with same name + * unit-tests/test-cases/tentative-and-archive: update test case + + +2008-06-03 Nick Kledzik + + linker produces wrong result for 16-bit call relocations + * src/MachOReaderRelocatable.hpp: properly parse i386 scattered relocs for word sized pc-rel vanilla + * src/MachOWriterExecutable.hpp: propery compute displacement for x86::kPCRel16 + * unit-tests/test-cases/relocs-asm: update test case with callw instructions + + +2008-06-03 Nick Kledzik + + Building kext x86_64 with unexported symbols file causes linking problems + * src/MachOWriterExecutable.hpp: better check when creating undefined proxy atoms + * unit-tests/test-cases/unexported_symbols_list-r: added test case + + +2008-06-02 Nick Kledzik + + S_CSTRING_LITERALS section type not preserved in executable + * src/ObjectFile.h: added ContentType + * src/MachOReaderRelocatable.hpp: set ContentType for anonymous string literals + * src/MachOWriterExecutable.hpp: set S_CSTRING_LITERALS if ContentType is kCStringType + * unit-tests/test-cases/cstring-custom-section: added test case + + +2008-06-02 Nick Kledzik + + linker should produce __unwind_info section in final linked images + * src/ld.cpp: sort __unwind_info then __eh_frame section to end of __TEXT + * src/Architectures.hpp: add kImageOffset32 and kPointerDiff24 + * src/ObjectFile.h: add compact unwind info support + * src/MachOReaderRelocatable.hpp: add compact unwind info support + * src/MachOFileAbstraction.hpp: add C++ wrappers for unwind section layout + * src/UnwindDump.cpp: new tool for dumping __unwind_info section + * src/MachOWriterExecutable.hpp: create __unwind_info section when needed + * src/ObjectDump.cpp: print unwind info + + +2008-06-02 Nick Kledzik + + * unit-tests/test-cases/llvm-integration: split out some test cases + * unit-tests/test-cases/lto-preload-pie: added + * unit-tests/test-cases/lto-archive-dylib: added + + +2008-05-30 Nick Kledzik + + * unit-tests: fixes to build all tests with with gcc-4.2 on SnowLeopard + + +2008-05-30 Nick Kledzik + + support -preload option to generate MH_PRELOAD binaries compatible with mtoc(1) and EFI + * src/ld.cpp: add entryPoint parameter to optimize() + * src/ArchiveReader.hpp: ditto + * src/ObjectFile.h: ditto + * src/LTOReader.hpp: use entryPoint parameter to optimize() + * src/Options.h: add kPreload and segment alignment + * src/Options.cpp: support -preload and -segalign + * src/MachOWriterExecutable.hpp: support kPreload and non-page aligned segments + + +2008-05-30 Nick Kledzik + + ld should warn if passed -r and also dylibs + * src/ld.cpp: check for spurious dylibs in Linker::addDylib() + + +----- Tagged ld64-85.6 + +2008-11-01 Nick Kledzik + + support increased branch range in Thumb-2 + * src/MachOWriterExecutable.hpp: in fixUpReferenceFinal() support new longer branch range + + +2008-11-01 Nick Kledzik + + ld warning: unknown option to -iphoneos_version_min, not 1.x or 2.x + * src/Options.cpp: In setIPhoneVersionMin() support 3.x + + +----- Tagged ld64-85.5 + +2008-09-17 Nick Kledzik + + vtable pointers can be missing thumb bit + * src/MachOWriterExecutable.hpp: Writer::fixUpReferenceFinal() OR in the 1 bit if the target + of a arm::kReadOnlyPointer is thumb. + + +----- Tagged ld64-85.4 + +2008-08-11 Nick Kledzik + + ld should ignore LD_PREBIND when processing a static archive + * src/MachOWriterExecutable.hpp: in setImportNlist() never use N_PBUD for object files + +----- Tagged ld64-85.3 + +2008-07-14 Nick Kledzik + + Prebinding busted in DTSB + * src/Options.cpp: check for libstdc++.6.0.[49] in seg_addr_table + + +----- Tagged ld64-85.2 + +2008-05-06 Nick Kledzik + + ARM ld should take W bit off of maxprot for __TEXT segment + * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments + + +2008-05-06 Nick Kledzik + + encryptable images may not be signable + * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section + + +----- Tagged ld64-85 (Xcode 3.1) + +2008-04-29 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include + + +2008-04-29 Nick Kledzik + + ld doesn't honor "rightmost" -syslibroot argument + * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + + +2008-04-29 Nick Kledzik + + GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files + * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment + * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment + + +2008-04-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better cpu subtype support + + +2008-04-14 Nick Kledzik + + ld64 has bad ARM branch island check + * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail + + +2008-04-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs + + +----- Tagged ld64-84.4 + +2008-04-10 Nick Kledzik + + SPEC2000/eon built with -mdynamic-no-pic won't run + * src/Architectures.hpp: added arm::kReadOnlyPointer + * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer + * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer + * src/machochecker.cpp: allow MH_PIE bit + * unit-tests/test-cases/switch-jump-table: added test cases + + +----- Tagged ld64-84.3 + +2008-04-09 Nick Kledzik + + -undefined dynamic_lookup busted + * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates + * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup + + +----- Tagged ld64-84.2 + +2008-04-04 Nick Kledzik + + * src/ld.cpp: don't add .eh symbols to symbol table in -r mode + * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing + + +----- Tagged ld64-84.1 + +2008-03-28 Nick Kledzik + + ld should prefer architecture-specific variant over generic in fat object file + * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture + * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files + * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc + + +----- Tagged ld64-84 + +2008-03-28 Nick Kledzik + + * src/LTOReader.hpp: don't print lto version, if lto is unavailable + + +2008-03-26 Nick Kledzik + + Add LD_WARN_COMMONS to BigBear builds + * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file + + +2008-03-26 Nick Kledzik + + Need encryption tag in mach-o file + linker should adjust arm final linked images so __text is never on the same page as the load commands + * src/MachOFileAbstraction.hpp: add support for encryption_info_command + * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption + * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom + * src/machochecker.cpp: validate LC_ENCRYPTION_INFO + + +2008-03-25 Nick Kledzik + + ld64 does not recognize LLVM bitcode archive files + * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp + * src/ArchiveReader.hpp: sniff each member and instantiate correct reader + * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader + * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp + * unit-tests/test-cases/llvm-integration: added test case + + +2008-03-25 Nick Kledzik + + ld64 should switch to new libLTO.dylib interface + Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc + * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface + * unit-tests/test-cases/llvm-integration: update and comment + * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib + * src/ld.cpp: rework and simplify Linker::optimize() + * src/ObjectDump.cpp: Add -nm option + + +2008-03-25 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem + * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem + + +2008-03-24 Nick Kledzik + + Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 + * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. + + +2008-03-21 Nick Kledzik + + * src/Options.cpp: warn if -seg1addr value is not page aligned + + +2008-03-21 Nick Kledzik + + Move ARM support outside of __OPEN_SOURCE__ + * src/ld.cpp: remove __OPEN_SOURCE__ around arm support + * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support + * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support + * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check + * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support + * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support + * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h + + +----- Tagged ld64-83.2 + +2008-03-15 Nick Kledzik + + ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results + * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files + * unit-tests/test-cases/objc-exported_symbols_list: added test case + + +----- Tagged ld64-83.1 + +2008-03-14 Nick Kledzik + + -iphone_version_min ==> -iphoneos_version_min + * src/Options.cpp: support -iphoneos_version_min as well + + +----- Tagged ld64-83 + +2008-03-10 Nick Kledzik + + ld needs to strip iphone_version_min option if invoking ld_classic + * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic + + +2008-03-04 Nick Kledzik + + ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) + * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs + * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework + * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools + * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() + * src/ld.cpp: pass lazy helper atom to writer + * doc/man/man1/ld.1: document new options + * unit-tests/test-cases/lazy-dylib-objc: add test case + * unit-tests/test-cases/lazy-dylib: add test case + + +----- Tagged ld64-82.7 + +2008-03-07 Nick Kledzik + + duplicate symbol literal-pointer@__OBJC@__message_refs@... + * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak + * unit-tests/test-cases/objc-selector-coalescing: added test case + + +----- Tagged ld64-82.6 + +2008-03-04 Nick Kledzik + + ld crashes building XsanFS for Snow Leopard Builds + * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() + * unit-tests/test-cases/tentative-and-archive: added test case + +2008-03-04 Nick Kledzik + + ld64 should not force building with gcc 4.0 + * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 + + +2008-02-29 Nick Kledzik + + Simulator frameworks are being build split-seg and not prebound + * src/Options.cpp: only splitseg if prebound + + +2008-02-29 Nick Kledzik + + Linker should not make GSYM debug note for .objc_category_* symbols + * src/ld.cpp: suppress GSYM debug notes for absolute symbols + * unit-tests/test-cases/objc-category-debug-notes: added test case + + +2008-02-29 Nick Kledzik + + non-ASCII CFString support is broken + * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring + * unit-tests/test-cases/cfstring-utf16: add test case + + +2008-02-25 Nick Kledzik + + ld -r -x + * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels + + +----- Tagged ld64-82.5 + +2008-02-12 Nick Kledzik + + x86_64: -stack_size failure when large __bss is used + * src/ld.cpp: only move section already in __DATA segment to new __huge section + * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section + + +----- Tagged ld64-82.4 + +2008-02-06 Nick Kledzik + + comdat warnings with ld -r of C++ .o files + * unit-tests/test-cases/eh-coalescing-r: added test case + * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static + + +2008-02-06 Devang Patel + + LTO of Bom framework with -dead_strip causes ld(1) crash + * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. + * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. + * unit-tests/test-cases/llvm-integration/a15.c: New. + * unit-tests/test-cases/llvm-integration/b15.c: New. + * unit-tests/test-cases/llvm-integration/c15.c: New. + +2008-02-05 Nick Kledzik + + * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used + +----- Tagged ld64-82.3 + +2008-02-04 Nick Kledzik + + ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves + * src/ObjectFile.h: add 10.6 + * src/Options.cpp: add 10.6 support + * src/MachOReaderDylib.hpp: recognize $os10.6$ + + +----- Tagged ld64-82.2 + +2008-01-30 Devang Patel + + Can't build 64-bit Intel binaries with LTO + ld64 fails to build with llvm-gcc-4.2 + * src/LLVMReader.hpp: Fix character count typo in strncmp call. + Use const char * to initialize temp. string. + * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction + instead of hard coding /Developer. + +----- Tagged ld64-82.1 + +2008-01-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs + + +2008-01-22 Nick Kledzik + + ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files + * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs + * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs + * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files + + +----- Tagged ld64-82 + +2008-01-18 Nick Kledzik + + Bad grammar used in ld warning: cannot exported hidden symbol + * src/ld.cpp: fix typo in warning string + + +2008-01-16 Nick Kledzik + + Bundle Loader does not work anymore when loader is a bundle + ld warns of incorrect architecture when linking a bundle to a bundle + * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages + * unit-tests/test-cases/bundle_loader: update test case + + +2008-01-16 Nick Kledzik + + ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) + * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S + + +2008-01-16 Nick Kledzik + + if ld crashes while writing output file, it should delete the half written file + * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete + output file on failure. + + +2008-01-16 Devang Patel + + * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. + + +2008-01-16 Nick Kledzik + + GC-supported library can't be linked into GC-required executable + * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and + allow gc-compatible code to be linked into anything. + * unit-tests/test-cases/objc-gc-checks: update test case + + +2008-01-15 Nick Kledzik + + no debug notes for custom named data + * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore + * unit-tests/test-cases/dwarf-debug-notes: update test case + +----- Tagged ld64-81.5 + +2008-01-14 Devang Patel + + llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 + * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references + after optimization. + * src/ld.cpp: Resolve additional unbounded references after optimization. + + +2008-01-14 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes + * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs + * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs + + +2008-01-11 Nick Kledzik + + PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" + * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing + * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions + + +2008-01-11 Nick Kledzik + + * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list + + +2008-01-11 Nick Kledzik + + ld64(1) man page uses ambiguous term "suffix" + * doc/man/man1/ld.1: make meaning of "suffix" more explicit + + +2008-01-11 Nick Kledzik + + Obj-C Symbols in Leopard Can't Be Weak Linked + * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines + to dylibs to support Mac OS X 10.3.x dyld + + +2008-01-11 Nick Kledzik + + Unknown error with linker (dyld: unknown external relocation type) + * src/ld.cpp: fix crash when SO stabs are not balanced + + +2008-01-11 Devang Patel + + LTO does not work if expected output is a dynamic library + * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate + visibility info. + +2000-01-10 Nick Kledzik + + __cls_refs section is losing S_LITERAL_POINTERS section type + * src/MachOWriterExecutable.hpp: special case __cls_refs section + * unit-tests/test-cases/objc-literal-pointers: add test case + + +2008-01-03 Nick Kledzik + + wrong EH information might be used + Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom + has kGroupSubordinate references to the other atoms in the group. If the signature atom + is coalesced away, the linker follows kGroupSubordinate references and throws away the + other members of the group. + * unit-tests/test-cases/eh-coalescing: added test case + * src/ld.cpp: added markDead() and use propagate to subordinates + * src/Architectures.hpp: added kGroupSubordinate + * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom + and if used, from .eh atom to its LSDA atom. + * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp + +----- Tagged ld64-81.4.1 + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. + +2007-12-19 Devang Patel + + * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. + * src/Options.cpp: Use printLLVMVersion() in verbose mode. + +2007-12-19 Devang Patel + + print LLVM LTO version number in verbose mode + * src/Options.h: Add verbose() method to check fVerbose flag. + * src/LLVMReader.hpp: Print LLVM version string in verbose mode. + +----- Tagged ld64-81.4 + +2007-12-18 Devang Patel + + * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. + +----- Tagged ld64-81.3 + +2007-12-17 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths + + +2007-12-17 Devang Patel + + * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to + dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. + + +2007-12-14 Nick Kledzik + + gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) + * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs + * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static + + +2007-12-14 Devang Patel + + Enable Link Time Optimization in Opal + * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. + * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. + * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. + * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. + + +2007-12-13 Nick Kledzik + + SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... + * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly + +----- Tagged ld64-81.2 + + + +2007-12-07 Nick Kledzik + + support 8-bit relocations for i386 + * src/Architectures.hpp: add kPCRel8 + * src/MachOReaderRelocatable.hpp: support 8-bit pc-rel relocations for intel + * src/MachOWriterExecutable.hpp: support 8-bit pc-rel relocations for intel + * unit-tests/test-cases/relocs-asm: add test cases + + +----- Tagged ld64-81.1 + +2007-12-06 Nick Kledzik + + * src/MachOReaderDylib.hpp: rework cycle detection to remove some false positives + + +2007-12-05 Nick Kledzik + + Duplicate probe firings in Security.framework + * src/ld.cpp: check dtrace probe sites are not in fDeadAtoms before using + * unit-tests/test-cases/dtrace-static-probes-coalescing: add test case + + +2007-12-05 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix CFString coalescing to work with -fwritable-strings + * unit-tests/test-cases/cfstring-coalesce: add -fwritable-strings to test case + + +----- Tagged ld64-81 + +2007-11-15 Nick Kledzik + + ld64 should support runtime text relocations + * src/MachOWriterExecutable.hpp: add generatesLocalTextReloc() and generatesExternalTextReloc() + * src/Options.cpp: process -read_only_relocs option + * src/Options.h: add allowTextRelocs() and warnAboutTextRelocs() + * src/MachOReaderRelocatable.hpp: add hasLongBranchStubs() + * src/machochecker.cpp: allow relocs in read only segments, if section flags are set + * unit-tests/test-cases/read-only-relocs: update test case + + +2007-11-08 Devang Patel + + * ld64.xcodeproj/project.pbxproj: add new build phase "build configure.h" for + ld target. + * src/ld.cpp: Include "configure.h" + + +----- Tagged ld64-80.11 + +2008-02-12 Nick Kledzik + + Wrong section name for objc info for ARM when OBJC2 is used + * src/MachOWriterExecutable.hpp: switch segment/section name for ARM objc2 image info + +----- Tagged ld64-80.10 + +2008-02-11 Nick Kledzik + + ld64 does not support -aspen_version_min 2.0 + * src/Options.cpp: allow 2.x for -aspen_version_min + + +2008-02-11 Nick Kledzik + + ld_classic: unknown flag: -aspen_version_min + * src/Options.cpp: change -aspen_version_min x.x to -macosx_version_min 10.5 when invoking ld_classic + + +----- Tagged ld64-80.9 + +2008-01-29 Nick Kledzik + + -iphone_version_min ==> -aspen_version_min + * src/Options.cpp: support -aspen_version_min + + +----- Tagged ld64-80.8 + +2008-01-10 Nick Kledzik + + * src/Options.cpp: support transition to new objc ABI for ARM by allowing old .objc_class_name_* + style names in export files and map them to new _OBJC_CLASS_$_ style names. + + +----- Tagged ld64-80.7 + +2008-01-02 Nick Kledzik + + BigBear5A18 isn't fully prebound + * src/Options.cpp: make fNeedsModuleTable true for arm + +----- Tagged ld64-80.6 + +2007-11-30 Nick Kledzik + + -iphone_version_min + * src/Options.cpp: handle -iphone_version_min option + + +----- Tagged ld64-80.5 + +2007-11-26 Nick Kledzik + + need to special case some dylibs in seg_addr_table + * src/Options.cpp: retry seg_add_table lookup for a couple of unusual dylibs + + +----- Tagged ld64-80.4 + +2007-11-06 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix parsing of external and scattered thumb branch22 relocs + * unit-tests/test-cases/thumb-blx: add test case to keep blx issues from coming back + +----- Tagged ld64-80.3 + +2007-11-03 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: remove recalc of dstAddr which could cause thumb branches to be +2 + * src/MachOWriterExecutable.hpp: remove incorrect test for relocateableExternal + +----- Tagged ld64-80.2 + +2007-11-01 Nick Kledzik + + * src/ld.cpp: hack my own prototype for log2() until math.h is cleaned up + + +----- Tagged ld64-80.1 + +2007-11-01 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: add HEADER_SEARCH_PATHS for cross builds + * src/ld.cpp: temporarily disable LLVM_SUPPORT + * src/MachOWriterExecutable.hpp: Don't use CC_MD5() directly + + +2007-10-26 Nick Kledzik + + Cannot build with libm_static.a statically linked + * src/MachOWriterExecutable.hpp: Fix makesExternalRelocatableReference() for -r -d case + * unit-tests/test-cases/tentative-to-real-hidden: add test case + + +----- Tagged ld64-80 + +2007-10-24 Nick Kledzik + + linker should probably warn about trying to export a hidden symbol + * src/ld.cpp: if using -exported_symbols_list check each hidden atom as it is added to symbol table + * src/Options.h,.cpp: add hasExportMaskList() + * unit-tests/test-cases/exported_symbols_list-hidden: added test case + + +2007-10-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: keep old style dtrace probes externel for kernel builds + + +2007-10-23 Nick Kledzik + + unify error and warning messages + -w should suppress warnings + * src/ld.cpp: use warning() function + * src/Options.h: remove emitWarnings() + * src/MachOReaderDylib.hpp: use warning() function + * src/MachOReaderRelocatable.hpp: use warning() function + * src/Options.cpp: use and implement warning() + * src/MachOWriterExecutable.hpp: use warning() function + * unit-tests/test-cases/visibility-warning: verify -w suppresses warnings + + +2007-10-23 Devang Patel + + * src/ld.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/LLVMReader.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/MachOReaderDylib.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/ObjectFile.h: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/MachOReaderRelocatable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check + * src/MachOWriterExecutable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. + * src/ObjectDump.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. + + +2007-10-22 Nick Kledzik + + * src/Options.cpp: add support for LD_DEAD_STRIP and LD_WARN_COMMONS + * src/MachOReaderRelocatable.hpp: fix problem with -dead_strip of ObjC literal pointers + + +2007-10-22 Nick Kledzik + + * src/Options.cpp: have -static arm code link with ld_classic (for now) + + +2007-10-22 Nick Kledzik + + Recognize all arm architectures + * src/MachOReaderRelocatable.hpp: add support for all ARM sub-types + * unit-tests/test-cases/cpu-sub-types: add test cases for all combinations of ARM sub-types + + +2007-10-19 Nick Kledzik + + * src/*: merge in arm support + * unit-tests/test-cases/*: fix to work for arm and thumb + +----- Tagged ld64-79 + +2007-10-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: if -r mode, always set custom alignment (SET_COMM_ALIGN) on common symbols + * unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile: fix warning + * unit-tests/test-cases/static-executable/Makefile: fix spurious failure + + +2007-10-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix edge case in branch island generation + + +2007-10-12 Nick Kledzik + + Add option to create old, slow stubs for i386 + * src/ObjectFile.h/.cpp: support -read_only_stubs + * src/MachOWriterExecutable.hpp: enhance StubAtom to support old style __symbol_stub/__la_symbol_ptr stubs + * unit-tests/test-cases/slow-x86-stubs: add test case + + +2007-10-12 Nick Kledzik + + ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + * src/Options.cpp: in findFileUsingPaths() don't search for embedded dylibs + * unit-tests/test-cases/indirect-path-search/Makefile: added case for a dylib embedded in a framework + + +2007-10-11 Nick Kledzik + + add option to disable implicit load commands for indirectly used public dylibs + * src/Options.cpp: add support for -no_implicit_dylibs + * src/ObjectFile.h: add fImplicitlyLinkPublicDylibs + * src/MachOReaderDylib.hpp: test fImplicitlyLinkPublicDylibs before hoisting an implicitly linked dylib + * unit-tests/test-cases/implicit_dylib: add test case + + +2007-10-11 Nick Kledzik + + -interposable_list + * src/Options.h/cpp: Add fInterposeList and fInterposeMode to support -interposable_list + * src/MachOWriterExecutable.hpp: pass symbol name to fOptions.interposable() + * unit-tests/test-cases/interposable_list: add test case + + +2007-10-10 Nick Kledzik + + If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + * src/MachOWriterExecutable.hpp: automatically use LC_LOAD_WEAK_DYLIB if all symbols used from a dylib are weak_import + * unit-tests/test-cases/weak_dylib: added test case + + +2007-10-10 Nick Kledzik + + linker does not error when dylib ordinal exceeds 250 + * src/MachOWriterExecutable.hpp: error out if ordinals exceed max allowed + + +2007-10-10 Nick Kledzik + + overriding 'operator new' or 'operator delete' fails if no weak symbols are present + * src/ld.cpp: at end of checkUndefines() search dylibs for weak versions of any global external symbols + * src/ObjectFile.h: add hasWeakExternals() method to Reader + * src/MachOReaderDylib.hpp: implement hasWeakExternals() method in Reader + * src/ExecutableFile.h: add overridesDylibWeakDefines parameter to write() + * src/MachOWriterExecutable.hpp: use overridesDylibWeakDefines parameter to write() + * unit-tests/test-cases/operator-new: add test case + + +2007-10-05 Nick Kledzik + + No warning about tentative definition conflicting with dylib definition + .comm variables in shared library, worked with XCode 2.4.1, broken with XCode 3? + * src/ld.cpp: at end of checkUndefines() verify if any remaining commons conflict with dylibs + * doc/man/man1/ld.1: document -commons and -warn_commons options + * unit-tests/test-cases/tentative-and-dylib: added test case + + +2007-10-05 Nick Kledzik + + NS/CFString constants are not dead strippable + * src/MachOReaderRelocatable.hpp: break up __cfstring section into one atom per cfstring, make them coalesable + * unit-tests/test-cases/cfstring-coalesce: added test case + + +2007-10-05 Nick Kledzik + + Dead stripping + exported symbols list using wildcards doesn't seem to do the right thing + * src/Options.cpp/h: add hasWildCardExportRestrictList() + * src/ld.cpp: if dead stripping code and have wildcard exports, add all global atoms matching wildcards as roots + * unit-tests/test-cases/exported-symbols-wildcards-dead_strip: added test case + + +2007-10-04 Nick Kledzik + + ld shouldn't search /Network/Library/Frameworks by default + * src/Options.cpp: remove /Network/Library/Frameworks/ from default search path + * doc/man/man1/ld.1: document the change + + +2007-10-04 Nick Kledzik + + all binaries should get LD_UUID load commands, not just those with DWARF symbols + * src/ld.cpp: default fCreateUUID to be true for non object file output types + * unit-tests/test-cases/no-uuid/Makefile: update test case to match new rules + + +----- Tagged ld64-78 + +2007-09-27 Nick Kledzik + + range check load commands + * src/MachOReaderDylib.hpp: check that load commands all fit in load command size from header + * src/MachOReaderRelocatable.hpp: check that load commands all fit in load command size from header + + +2007-09-27 Nick Kledzik + + Xc8M2540a: ld64 crashes when linking Pascal program + * src/ld.cpp: fix findAtomAndOffset() to handle where there are no function atoms + + +2007-09-27 Nick Kledzik + + ADOBE Xcode 3: ld -dead_strip does not work with -init from an archive + * src/ld.cpp: add bool parameter to entryPoint() so -init atom not looked for too soon + * unit-tests/test-cases/dead_strip-init-archive: added test case + + +2007-09-26 Nick Kledzik + + Spurious link warnings for inline members of C++ template classes + * src/ld.cpp: check definition kinds before warning about visibility mismatches + * unit-tests/test-cases/visibility-warning: added test case + + +2007-09-26 Nick Kledzik + + an empty .o file with zero load commands will crash linker + * src/MachOReaderRelocatable.hpp: have Reader constructor return early of no load commands + * unit-tests/test-cases/empty-object: added test case + + +2007-09-26 Nick Kledzik + + 9a527: ppc64 branch islands fail with 4GB pagezeo + * src/MachOWriterExecutable.hpp: start range calculations at start of __text not at zero. + + +----- Tagged ld64-77 (Xcode 3.0) + +2007-07-23 Nick Kledzik + + Kernel is linked with some global symbols unsorted + * src/MachOWriterExecutable.hpp: Add NListNameSorter to allow global atoms and extra labels to be sorted + + +2007-07-20 Nick Kledzik + + Can't do objc_msgSendSuper dispatches after loading a Fix&Continue bundle + * src/MachOWriterExecutable.hpp: when calculating what kind of reloc to use, never use an + external reloc to reference 32-bit ObjC symbols. + + +2007-07-20 Nick Kledzik + + Runtime crash with ICC math library on Leopard + * src/MachOReaderRelocatable.hpp: detect if section starts with a symbol that is not + aligned to section and correct it. + + +----- Tagged ld64-76 + +2007-06-29 Nick Kledzik + + export hiding does not work for frameworks + * src/MachOReaderDylib.hpp: fix checks in isPublicLocation() + * unit-tests/test-cases/symbol-moving: update to test frameworks as well as dylibs + + +2007-06-27 Nick Kledzik + + linker should use undefines from flat dylibs when linking a main flat + * src/ObjectFile.h: added fLinkingMainExecutable + * src/Options.cpp: set up fLinkingMainExecutable + * src/MachOReaderDylib.hpp: when linking a main executable for flat namespace, the reader for + any loaded flat namespace dylib will have a new atoms that has references to all undefined + symbols in the dylib + * unit-tests/test-cases/flat-indirect-undefines: added test case + * doc/man/man1/ld.1: update man page to describe when dylib undefines are used + + +2007-06-27 Nick Kledzik + + OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found + * src/MachOReaderDylib.hpp: add assertNoReExportCycles() method + * unit-tests/test-cases/dylib-re-export-cycle: added test case + + +2007-06-27 Nick Kledzik + + ld64 has slightly different warning message formats than the old ld + * src/ld.cpp: standardize all warning messages to start with "ld: warning" + * src/MachOWriterExecutable.hpp: ditto + * src/MachOReaderRelocatable.hpp: ditto + * src/MachOReaderDylib.hpp:ditto + + +2007-06-26 Nick Kledzik + + -dead_strip can cause duplicate external commons + * src/ld.cpp: don't use discarded coalesced global atoms as dead strip roots + * src/machochecker.cpp: error if duplicate external symbols + * unit-tests/test-cases/commons-coalesced-dead_strip: added test case + + +2007-06-26 Nick Kledzik + + update man page that linker does not search indirect libraries with two-level namespace + * doc/man/man1/ld.1: add new "Indirect dynamic libraries" section to man page + + +2007-06-26 Nick Kledzik + + Xc9A466: Exports file cannot use Mac line ends + * src/Options.cpp: check for \r or \n when parsing .exp files + * unit-tests/test-cases/exported_symbols_list-eol: added test case + + +----- Tagged ld64-75 + +2007-05-31 Nick Kledzik + + Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB + * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 + + +----- Tagged ld64-74.5 + +2007-05-31 Nick Kledzik + + set OSO timestamp to zero for when building in buildit + * src/ld.cpp: check for RC_RELEASE and if exists set all OSO timestamps to zero + + +2007-05-30 Nick Kledzik + + BUILD_STABS now causes ld of xnu to bus error + * src/ld.cpp: Change || to && in collectStabs() + + +----- Tagged ld64-74.4 + +2007-05-18 Nick Kledzik + + static probes don't work with libraries in dyld shared cache + * src/OpaqueSection.hpp: the __TEXT segment is executable + + +----- Tagged ld64-74.3 + +2007-05-16 Nick Kledzik + + ppc: linker adds stubs to cstring references + * src/MachOWriterExecutable.hpp: update ppc stubableReference() to only allow high/low references + to be stubed if they reference a symbol in some other dylib. + * unit-tests/test-cases/stub-generation: added test case + + +2007-05-16 Nick Kledzik + + ppc64: need to make LOCAL indirect symbol table entry for now local symbol + * src/MachOWriterExecutable.hpp: factored local tests into indirectSymbolIsLocal() + * unit-tests/test-cases/non-lazy-r: added test case + + +2007-05-15 Nick Kledzik + + ld64 drops fix&continue bit in __OBJC, __image_info. + * src/MachOReaderRelocatable.hpp: implement objcReplacementClasses() + + +2007-05-15 Nick Kledzik + + support __image_info in __DATA segment for 64-bits + * src/MachOReaderRelocatable.hpp: use strncmp() for __objc_imageinfo since it is 16 bytes long + * src/MachOWriterExecutable.hpp: specialize segment/section names for synthesized objc image info section + + +2007-05-15 Nick Kledzik + + * unit-tests/include/common.makefile: set COMPILER_PATH so harness works with latest compiler + + +----- Tagged ld64-74.2 + +2007-05-11 Nick Kledzik + + ld64-74.1 breaks libstdc++ DejaGnu test (G5 only) + * src/MachOWriterExecutable.hpp: don't stub a reference if the target offset is non-zero + + +----- Tagged ld64-74.1 + +2007-05-09 Nick Kledzik + + * src/Options.h: add emitWarnings() + * src/Options.cpp: wire up -w to emitWarnings() + + +2007-05-09 Nick Kledzik + + ld64 won't link wine (regression from Tiger) + * src/Architectures.hpp: add x86::kPointerDiff16 and x86::kPCRel16 + * src/MachOReaderRelocatable.hpp: add support to parse new relocs + * src/MachOWriterExecutable.hpp: add support fo new relocs + + +2007-05-08 Nick Kledzik + + need way for ld and dyld to see different exported symbols in a dylib + * src/MachOReaderDylib.hpp: update parse and use $ld$ symbols + * src/Options.h: move VersionMin to ReaderOptions + * src/ObjectFile.h: move VersionMin to ReaderOptions + * src/Options.cpp: move VersionMin to ReaderOptions + * src/MachOWriterExecutable.hpp: move VersionMin to ReaderOptions + * unit-tests/test-cases/symbol-moving: added test case + + +2007-05-03 Nick Kledzik + + typo in error message for linking -pie + * src/MachOWriterExecutable.hpp: fix typo in error messages + + +----- Tagged ld64-74 + +2007-05-03 Nick Kledzik + + ld64 can't find @executable _path relative dylibs from our umbrella frameworks + ld64 should handle linking against dylibs that have @loader_path based dylib load commands + * src/ObjectFile.h: add from parameter to findDylib() + * src/MachOReaderDylib.hpp: supply from parameter to findDylib() + * src/ld.cpp: use from parameter for @loader_path substitution in findDylib() + * unit-tests/test-cases/re-export-relative-paths: added test case + + +2007-05-02 Nick Kledzik + + * src/ObjectFile.h: add fLogObjectFiles and fLogAllFiles + * src/Options.cpp: hook up -t to fLogAllFiles and -whatsloaded to fLogObjectFiles + * src/MachOReaderDylib.hpp: log if fLogAllFiles + * src/MachOReaderRelocatable.hpp: log if fLogObjectFiles or fLogAllFiles + * src/MachOReaderArchive.hpp: log if fLogAllFiles + * doc/man/man1/ld.1: update man page + + +2007-05-02 Nick Kledzik + + typo in message, frameowrk + * src/Options.cpp: fix typo + + +2007-05-01 Nick Kledzik + + "ld" man page is missing the description for many options + * doc/man/man1/ld.1: add documentation on all obsolete options + + +2007-05-01 Nick Kledzik + + ld doesn't handle -mlong-branch .o files that have had local symbols stripped + warning about dwarf line info with -mlong-branch + * src/MachOReaderRelocatable.hpp: don't lop -mlong-branch stubs off end of functions + * src/MachOWriterExecutable.hpp: allow code references besides BR24 to be stubable + + +2007-04-30 Nick Kledzik + + unable to link VTK because __textcoal_nt too large + * src/MachOReaderRelocatable.hpp: when doing a final link map __textcoal_nt to __text + + +2007-04-30 Nick Kledzik + + ld does not report error when -r is used and exported symbols are not defined. + ld leaves global common symbols not in exported symbols list. + * src/ld.cpp: stop special casing -r mode in checkUndefines() + * src/MachOWriterExecutable.hpp: don't create proxy atom in -r mode if it is supposed to be exported. + mark tentative definitions are private extern in -r mode even without -keep_private_externs + * unit-tests/test-cases/exported_symbols_list-r: added test case + + +2007-04-27 Nick Kledzik + + ld should keep looking when it finds a weak definition in a dylib + * src/ld.cpp: modified addJustInTimeAtoms() to keep looking when a weak defintion is found + * unit-tests/test-cases/weak-def-ordinal: added test case + + +2007-04-27 Nick Kledzik + + better error message for indirect dylibs missing required architecture + * src/ld.cpp: when loading indirect dylib add path to error messages + + +2007-04-25 Nick Kledzik + + the i386 slice of dyld does not need __IMPORT segment + * src/ObjectFile.h: add fForDyld + * src/Options.cpp: set up fForDyld + * src/MachOReaderRelocatable.hpp: if fForDyld, change __IMPORT segment to __DATA + * src/MachOWriterExecutable.hpp: recognize __DATA/__pointers in dyld as a non-lazy section + + +2007-04-24 Nick Kledzik + + ppc64: need to make LOCAL indirect symbol table entry for now local symbol + * src/MachOWriterExecutable.hpp: use INDIRECT_SYMBOL_LOCAL for any non-global symbol + * unit-tests/test-cases/strip_local: update test case + + +2007-04-24 Nick Kledzik + + ld64 -sectorder and -order_file files don't accept white space following the : + * src/Options.cpp: prune white space after colon and before symbol name + * unit-tests/test-cases/order_file: update test case to have a space after the colon + + +2007-04-24 Nick Kledzik + + ld64 corrupts debug symbol table entries, nm doesn't print them + * src/MachOWriterExecutable.hpp: properly set ilocalsym in module table + + +2007-04-24 Nick Kledzik + + support __image_info in __DATA segment for 64-bits + * src/MachOReaderRelocatable.hpp: look for new objc info section name too + + +2007-04-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix -non_global_symbols_strip_list to work with -r + * unit-tests/test-cases/local-symbol-partial-stripping: update test case + + + +----- Tagged ld64-73.7 + +2007-05-10 Nick Kledzik + + can't use dtrace static probes in x86_64 dylib + * src/MachOWriterExecutable.hpp: x86_64:kPointerDiff32 is ok in shared region + * unit-tests/test-cases/dtrace-static-probes: update to build dylib too + + +2007-05-09 Nick Kledzik + + 9A430: using -dead_strip with static dtrace probes causes ld to crash + * src/ld.cpp: fix markLive() to look at right name in dtrace probe refernce + * unit-tests/test-cases/dtrace-static-probes: added -dead_strip case + + +----- Tagged ld64-73.6 + +2007-04-17 Nick Kledzik + + Add options to do partial stripping of local symbols + * src/MachOWriterExecutable.hpp: use fOptions.keepLocalSymbol() + * src/Options.cpp: implement -non_global_symbols_no_strip_list and -non_global_symbols_strip_list + * src/Options.h: replace stripLocalSymbols() with localSymbolHandling() and keepLocalSymbol() + * doc/man/man1/ld.1: document -non_global_symbols_no_strip_list and -non_global_symbols_strip_list + * unit-tests/test-cases/local-symbol-partial-stripping: added test case + + +----- Tagged ld64-73.5 + +2007-04-17 Nick Kledzik + + ld64-73.3 XBS logging incorrectly reporting "direct" dynamic libraries + * src/ld.cpp: restore direct vs indirect library for LD_TRACE_DYLIBS logging + + +2007-04-16 Nick Kledzik + + data initialized to a weak imported symbol is missing relocation + * src/MachOWriterExecutable.hpp: check for A::kPointerWeakImport in buildExecutableFixups() + * unit-tests/test-cases/weak_import: updated test case to catch this problem + + +2007-04-13 Nick Kledzik + + Support -U + * src/MachOWriterExecutable.hpp: create proxies for -U symbols + * src/Options.cpp: process -U + * src/Options.h: add allowedUndefined() and someAllowedUndefines() + * src/ld.cpp: create proxies for -U symbols + * doc/man/man1/ld.1: document -U and -undefined options + * unit-tests/test-cases/undefined-dynamic-lookup: added test case + + +----- Tagged ld64-73.4 + +2007-04-12 Nick Kledzik + + ld changes needed to support read-only DOF + * src/Options.cpp: remove -read_only_dof + * src/Options.h: remove fReadOnlyDOFs + * src/ld.cpp: only generate read-only DOF sections + + +----- Tagged ld64-73.3.1 + +2007-04-13 Nick Kledzik + + -framework vecLib -framework Accelerate causes bad ordinals + * src/MachOWriterExecutable.hpp: fix bug optimizeDylibReferences() when there are two readers with same install name + + +----- Tagged ld64-73.3 + +2007-04-03 Nick Kledzik + + * src/ld.cpp: read-only-dofs should use 32-bit offsets for x86_64 + * src/MachOReaderDylib.hpp: if "public" re-export is not marked implict, still mark it as re-exported + + +2007-04-02 Nick Kledzik + + if replacement file for -dylib_file is missing, warn instead of error + * src/ld.cpp: a try/catch to turn -dylib_file error into a warning. + * unit-tests/test-cases/dylib_file-missing: add test case + * doc/man/man1/ld.1: update man page about -dead_strip_dylibs + + +----- Tagged ld64-73.2 + +2007-03-31 Nick Kledzik + + ld64-73: atom sorting error with duplicate zero sized bss symbols + * src/MachOReaderRelocatable.hpp: suppress warning on sorting zero size zero fill atoms + +2007-03-31 Nick Kledzik + + ld64-73 fails anything linking with -lm + * src/ld.cpp: when processing dylbs that are sylinks ensure that fDylibMap contains all paths + * src/MachOWriterExecutable.hpp: when dead stripping dylibs and renumbering ordinals make sure + aliases dylib get renumbered too + * unit-tests/test-cases/dylib-aliases: added + + +----- Tagged ld64-73.1 + +2007-03-30 Nick Kledzik + + * src/MachOWriterExecutable.hpp: back out use of LC_REEXPORT_DYLIB until rdar://problem/5009909 is in build fleet + + +----- Tagged ld64-73 + +2007-03-30 Nick Kledzik + + ER: -dead_strip_dylibs + linker should add implicit load commands for indirectly used public dylibs + * src/ObjectFile.h: change dylib reader interface to implictly/explicitlyLinked + * src/ld.cpp: use new dylib reader interface + * src/Options.h: add deadStripDylibs() + * src/Options.cpp: support -dead_strip_dylibs + * src/MachOReaderDylib.hpp: use new dylib reader interface + * src/MachOWriterExecutable.hpp: remove dylib load commands for unused dylibs and alter ordinals + * unit-tests/test-cases/re-export-optimizations: added + * unit-tests/test-cases/dead_strip_dylibs: added + + +2007-03-30 Nick Kledzik + + * src/Options.cpp: enable -lfoo to search for libfoo.so as well as libfoo.dylib, + remove seg addr table hack for transitioning to new linker + +2007-03-30 Nick Kledzik + + ADOBE XCODE3: Linker is slow with large C++ .o files + * src/MachOReaderRelocatable.hpp: the compiler generates stubs to weak functions in the + same translation unit. Don't treat those like the spurios stubs to static functions. + + +2007-03-29 Nick Kledzik + + ld64 should link mach_kernel during xnu builds to support dtrace + * src/MachOReaderRelocatable.hpp: To handle duplicate labels properly, rework how atoms sizes are set + by iterating through sorted fAtoms rather than fAddrToAtom, . Change default alignment of commons + to be the natural alignment of the size rounded up to the closest power of two and max it at 12. + Build atoms in reverse symbol table order so that global atoms are constructed before locals. + This assures that if there is a global and local label at the same location, the global label + will become the atom's name and the local will be an alias. Properly handle a label + at the end of a section. Handle R_ABS in relocations. Handle sect-diff relocs with addends. + Don't auto-strip 'l' symbols in static executables (mach_kernel). + * src/OpaqueSection.hpp: opaque_section now has an ordinal + * src/ld.cpp: opaque_section now requires an ordinal + * src/ObjectFile.h: add ReaderOptions.fForStatic + * src/Options.cpp: set fForStatic when building a static executable + * src/MachOWriterExecutable.hpp: add from atom to StubAtom. Properly write out i386 + sect-diff relocs with addends. properly write out ppc PICbase relocs where pic base + is not in the atom. + + +2007-03-27 Nick Kledzik + + Typo in ld man page (-exported_symbols_list) + * doc/man/man1/ld.1: fix typo + + +2007-03-26 Nick Kledzik + + consider generating LC_UUID from a checksum of the file + * src/Options.h: change emitUUID() to getUUIDMode() + * src/Options.cpp: support -random_uuid + * src/MachOWriterExecutable.hpp: set uuid to be md5 hash of entire output file + + +2007-03-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: restructure writeAtoms() to copy all atoms in memory if possible + + +2007-03-24 Nick Kledzik + + ld -r of stripped .o file can incorrectly merge non-lazy pointers + * src/MachOWriterExecutable.hpp: when generating a .o file, non-lazy pointer with target offsets should be + encoded as LOCAL in the indirect symbol table + * unit-tests/test-cases/stripped-indirect-symbol-table: added test case + + +2007-03-23 Nick Kledzik + + SWB: ld64-72 errors building with gcc-4.2 + * src/MachOReaderDylib.hpp: add curly brackets in switch cases + * src/MachOWriterExecutable.hpp: rearrange classes so there are no template specialization forward references + + +2007-03-23 Nick Kledzik + + * src/ld.cpp: fix -print_statistics when using -dead_strip + + +2007-03-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: generate better names for non-lazy pointers to the interior of atoms + + +2007-03-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: speed up ld -r a little by reversing relocs en mas + + +2007-03-16 Nick Kledzik + + ld Bus Error on missing command line arguments + * src/Options.cpp: check next argv[] is not NULL + + +2007-03-16 Nick Kledzik + + need to be able to order symbols in anonymous namespaces + * src/ld.cpp: add logic to do fuzzy matching of symbols with anonymous namespace usage + * unit-tests/test-cases/order_file-ans: added test case + + +2007-03-16 Nick Kledzik + + headerpad_max_install_names deprecated for 64-bit + * src/ld.cpp: make sure dylib load command order matches command line order + * src/Options.h: add maxMminimumHeaderPad() + * src/Options.cpp: add maxMminimumHeaderPad() set by -headerpad_max_install_names + * src/src/MachOWriterExecutable.hpp: check maxMminimumHeaderPad() + * doc/man/man1/ld.1: update man page about -headerpad_max_install_names + + +2007-03-16 Nick Kledzik + + Linker returns success although exported symbols are undefined. + * src/ld.cpp: turn missing symbols back into an error + + +2007-03-16 Nick Kledzik + + ld64 should handle linking against dylibs that have @loader_path based dylib load commands + * unit-tests/test-cases/loader_path: added test case + + +2007-03-16 Nick Kledzik + + linker should add implicit load commands for indirectly used public dylibs + Indirect libraries should be found using -F and -L options + Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB + * src/ld.cpp: reworked all dylib processing. Readers can now add the dylib list. + * src/Options.h: add findFileUsingPaths() + * src/MachOReaderDylib.hpp: look in re-exported children instead of requring linker to do that + * src/ObjectFile.h: add processIndirectLibraries(), remove getDependentLibraryPaths() + * src/machochecker.cpp: support LC_REEXPORT_DYLIB + * src/ExecutableFile.h: simplify DyLibUsed + * src/Options.cpp: add findFileUsingPaths(). add new re-export options + * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 + * doc/man/man1/ld.1: updated with new re-export options + * unit-tests/test-cases/indirect-path-search: added tests that -F and -L work with indirect dylibs + * unit-tests/test-cases/re-export-cases: added tests for all combinations of re-exporting + + +2007-03-14 Nick Kledzik + + sort external relocations to optimize dyld performance + * src/MachOWriterExecutable.hpp: added ExternalRelocSorter + * src/machochecker.cpp: verify external relocations are grouped by symbol number + * unit-tests/test-cases/external-reloc-sorting: added test case + + +----- Tagged ld64-72 + +2007-03-06 Nick Kledzik + + * src/Options.cpp: ignore .objc_category_name_* symbols in .exp files + + +2007-03-06 Nick Kledzik + + * src/Options.cpp: stop special casing mach_kernel and instead requre kernel to be built with -new_linker + + +2007-03-06 Nick Kledzik + + ld64-72 (experimental) is causing DejaGnu test failures + * src/MachOWriterExecutable.hpp: add optimizableGOTReferenceKind() to track GOT uses that cannot be optimized + + +2007-03-06 Nick Kledzik + + minimum header padding should be 32 to allow code signing + * src/Options.cpp: initialize fMinimumHeaderPad to 32 + * src/MachOWriterExecutable.hpp: better calculation of header padding + + +2007-03-06 Nick Kledzik + + Linker crashes with -flat_namespace against two-level dylibs that might have re-exports + * src/ld.cpp: flat namespace should not allow NULL indirect readers + + +2007-03-06 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't error on S_COALESCED sections with anonymous atoms + * src/MachOWriterExecutable.hpp: set MH_PIE bit when linking -pie + * ld64.xcodeproj/project.pbxproj: don't echo environment when running unit test + + +2007-03-01 Nick Kledzik + + * doc/man/man1/ld.1: Add descriptions to all "rarely used options" + + +2007-03-01 Nick Kledzik + + Remove support for Essential Symbols: Warn about use of -Sp option; remove man page entry + * src/Options.cpp: make -Sp obsolete + * doc/man/man1/ld.1: make -Sp obsolete + + +2007-03-01 Nick Kledzik + + Support -pie + * src/Options.h: Add positionIndependentExecutable() + * src/Options.cpp: Support -pie option to set positionIndependentExecutable() + * src/MachOWriterExecutable: if -pie is used, add extra local relocations and error if any + absolute addressing is used + + +2007-03-01 Nick Kledzik + + ld64 should link mach_kernel during xnu builds to support dtrace + * src/ld.cpp: Ensure segments are laid out in discovery order. Add support for kAbsoluteSymbol. + Warn when merging symbols of different visiblity. Warn when a tentative definition + is replaced by one a real definition with a smaller size. Lay out __common section + so that ones built with -fno-commons come before regular commons. + * src/ObjectFile.h: remove SegmentOffset ivar and getter/setters + * src/machochecker.cpp: allow images with no r/w segments + * src/MachOReaderRelocatable: Add AbsoluteAtom. Sort tentative definitions by name instead of by size + Add support for custom commons alignment. + * src/Options.cpp: Fix spurious -sectalign warnings. Don't use ld_classic when linking mach_kernel + * src/MachOWriterExecutable.hpp: Support kAbsoluteSymbol atoms. In -r mode, set custom alignment + for commons if alignment is not its size. Support global __dtrace_probe labels. + * src/ObjectDump.cpp: add support for kAbsoluteSymbol atoms. + * unit-tests/test-cases/commons-alignment: Added test case for custom commons alignment + * unit-tests/test-cases/absolute-symbol: Added test case for basic absolute symbols + * unit-tests/test-cases/segment-order: Added test case that segments lay out in discovery order + * unit-tests/test-cases/commons-order: Added test case that commons lay out correctly + * unit-tests/test-cases/end-label: Added test case that a label used to mark the end of a section does not + get associcated with the next section. + + +2007-02-23 Nick Kledzik + + gcc-5005: DejaGnu failures due to -frepo + * src/ld.cpp: Add quotes to referenced from name to make collect2 and -frepo happy + + +2007-02-22 Nick Kledzik + + * src/MachOWriterExecutable.hpp: rework how padding after load commands is calculated + + +2007-02-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: extend special case of __mh_execute_header to static executables too + + +2007-02-21 Nick Kledzik + + gcc link map option ( "-M" ) should be redirectable to file + * doc/man/man1/ld.1: added -map option description + * src/Options.h: added generatedMapPath() + * src/Options.cpp: set up generatedMapPath() if -map option is used + * src/MachOWriterExecutable.hpp: add writeMap() method to generate map file + + +2007-02-19 Nick Kledzik + + Implement GOT Load elimination optimization + * src/ld.cpp: track size of all atoms and if > 2GB sort large zero-fill atoms to end + * src/MachOWriterExecutable.hpp: If image size < 2GB, only generate GOT entries if value must be + updatable by dyld. If > 2GB, only eliminate GOT entries to non-zero-fill atoms. Any use + of an eliminated GOT entry has its code changed from MOVQ _foo@GOT(%rip) to LEAQ _foo(%rip). + * unit-tests/test-cases/large-data: added + * unit-tests/test-cases/got-elimination: added + + +----- Tagged ld64-71.2 + +2007-02-13 Nick Kledzik + + new ld ignores -segprot option + * src/Options.h: expose customSegmentProtections() + * src/Options.cpp: parse -segprot option and populate customSegmentProtections() + * src/MachOWriterExecutable.hpp: use customSegmentProtections() + + +2007-02-13 Nick Kledzik + + i386 -stack_addr doesn't work + * src/MachOWriterExecutable.hpp: use correct offset into thread state record + + +----- Tagged ld64-71.1 + +2007-02-07 Nick Kledzik + + * src/ld.cpp: sort __OBJC2 segment to be next to __OBJC segment + + +2007-02-07 Nick Kledzik + + * src/Options.cpp: change missing -seg_addr_table from an error to a warning + + +2007-02-06 Nick Kledzik + + Leopard 9A357: -dylib_file broken? + * src/MachOWriterExecutable.hpp: remove use of fInstallPathOverride + * src/Options.cpp: wire up -dylib_file option + * src/Options.h: remove fInstallPathOverride. add fDylibOverrides + * src/ld.cpp: check dylibOverrides() for indirect libraries + * unit-tests/test-cases/dylib_file: add test case + + +2007-02-05 Nick Kledzik + + * src/MachOReaderDylib.hpp: don't warn about zero size __image_info sections + + +2007-02-04 Rick Balocca + Enable the failing cases for missing command line arguments + +2007-02-04 Rick Balocca + Make sure that all .o's are checked by ObjectDump + and all macho are checked by machochecker + +2007-02-04 Rick Balocca + Fix an endian problem with machochecker + Fix blank-stubs Makefile + +----- Tagged ld64-71 + +2007-02-02 Rick Balocca + blank-stubs test case: handle the case of a native ppc compile--this + sets the subtype, which must be passed to lipo + +2007-02-01 Rick Balocca + make cpu-sub-types test more robust + +2007-02-01 Rick Balocca + auto-arch tests were resulting in a false FAILs + +2007-02-01 Rick Balocca + test cpu-sub-types was resulting in a false FAIL + +2007-02-01 Nick Kledzik + + STD:VSC: c99 -o writes to file that does not have write permission + * src/MachOWriterExecutable.hpp: check file is writable before using it + +2007-02-01 Nick Kledzik + + debug map (N_OSO) timestamps for object files in ranlib archive are incorrect + * src/MachOReaderArchive.hpp: parse modTime for .o files out of archive header + +2007-01-31 Nick Kledzik + + 9A354: ld -all_load does *NOT* produce the same dSYM as *.o or -u + * src/ld.cpp: when using -all_load don't assume that all atoms have same reader + * unit-tests/test-cases/dwarf-archive-all_load: added + +----- Tagged ld64-70.1 + +2007-01-31 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in addObjectRelocs_powerpc() mask scattered r_address to 16-bits + +----- Tagged ld64-70 + + +2007-01-30 Nick Kledzik + + linker should verify GC consistency of modules being linked into library + Support cpu-sub-types for ppc + * src/ObjectFile.h: Add getObjCConstraint() and getCpuConstraint() + * src/MachOReaderRelocatable.hpp: don't make atom for __image_info section, instead parse constaints + * src/MachOReaderDylib.hpp: look at __image_info content to get constaints + * src/ld.cpp: add updateContraints() and checkObjc() + * src/MachOWriterExecutable.hpp: add ObjCInfoAtom to sythesize __image_info content + + +2007-01-28 Nick Kledzik + + src/*: remove ObjectFile::requiresFollowOnAtom() method + + +2007-01-28 Nick Kledzik + + src/ld.cpp: enable LLVM_SUPPORT by default + src/LLVMReader.hpp: don't use absolute paths for llvm headers and libraries + + +2007-01-26 Rick Balocca + * src/ObjectDump.cpp: The usage() message was incorrect. + + +2007-01-25 Rick Balocca + * unit-tests/test-cases/zero-fill3: It was reporting FAIL on ld64 error return. + It should have been checking for non-error return. + + +2007-01-24 Nick Kledzik + + x86 fast stubs should not cross 64-byte boundries + * src/MachOWriterExecutable.hpp: for x86, 64-byte align __jump_table section + and make 64-btye crossing stubs be empty entries with indirect symbol table + entry of INDIRECT_SYMBOL_ABS + + +2007-01-19 Nick Kledzik + + * src/Options.h: add readOnlyx86Stubs() + * src/Options.cpp: support -read_only_stubs + * src/MachOWriterExecutable.hpp: make __IMPORT segment not writable if -read_only_stubs is used + + +2007-01-16 Eric Christopher + + ld64 --help isn't recognized + * src/Options.cpp (Options::parse): Support --help and -help. + + +2007-01-15 Nick Kledzik + + * src/MachOFileAbstraction.hpp: add range checking on macho_scattered_relocation_info::set_r_address() + + +2007-01-14 Nick Kledzik + + Support wildcards in contents of -exported_symbols_list + * src/Options.h: add SetWithWildcards class + * src/Options.cpp: add -exported_symbol and -unexported_symbol and use SetWithWildcards + * doc/man/man1/ld.1: add -exported_symbol and wildcard explanation + * unit-tests/test-cases/exported-symbols-wildcards: added test case + + +2007-01-10 Nick Kledzik + + [U]SDT probes should use C calling convention + * src/Options.cpp: Add -read_only_dof + * src/ld.cpp: create __dof section(s) based on probe and isenabled sites + * src/MachOReaderRelocatable.hpp: parse new sdt 2.0 probes encoded in .o files + * src/MachOWriterExecutable.hpp: handle regenerating dtrace probes into .o files + * unit-tests/test-cases/dtrace-static-probes: added test case + + +----- Tagged ld64-69.8 + +2007-01-30 Nick Kledzik + + Support LD_FORCE_NO_SEG_ADDR_TABLE + * src/Options.cpp: Support LD_FORCE_NO_SEG_ADDR_TABLE + + +----- Tagged ld64-69.7 + +2007-01-25 Nick Kledzik + + Leopard9A351: CFM Apps Are Broken because CFM glue is missing + * src/MachOReaderRelocatable.hpp: check S_ATTR_NO_DEAD_STRIP in dontDeadStrip() + + +----- Tagged ld64-69.6 + +2007-01-24 Nick Kledzik + + LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive + * src/ld.cpp: create and use logArchive() + + +----- Tagged ld64-69.5 + +2007-01-22 Nick Kledzik + + 9A350: can't link ppc programs with ld_classic + * src/Options.cpp: Remove support for LD_NO_CLASSIC_LINKER. Add support for -classic_linker + + +----- Tagged ld64-69.4 + +2007-01-17 Nick Kledzik + + QTComponents does not link with ld64 + * src/MachOReaderRelocatable.hpp: handle N_RSYM and N_PSYM stabs + + +----- Tagged ld64-69.3 + +2007-01-03 Nick Kledzik + + * src/Options.cpp: If the same dylib is specified twice and the second is specified weak, make it weak + + +----- Tagged ld64-69.2 + +2006-12-18 Nick Kledzik + + -dead_strip without -exported_symbols_list should not strip global functions from archives + * src/ld.cpp: when adding a .o file from an archive, add all its global symbols to live roots + * unit-tests/test-cases/dead_strip-archive: added + + +2006-12-18 Nick Kledzik + + flat_namespace main executables do not need to indirect interior references + * src/MachOWriterExecutable.hpp: don't indirect references to global symbols in main executables + * unit-tests/test-cases/flat-main: updated to test for indirection + * unit-tests/test-cases/flat-dylib: added + + +----- Tagged ld64-69.1 + +2006-12-15 Nick Kledzik + + -flat_namespace does not work with -mdynamic-no-pic + * src/MachOWriterExecutable.hpp: rework checking for use of ppc absolute addressing to allow them as long as + the target is within the same linkage unit. + + +2006-12-15 Nick Kledzik + + -ObjC should only load .o with .objc_ symbols + * src/Options.cpp: remove warning from -ObjC and have it instead set fLoadAllObjcObjectsFromArchives + * src/MachOReaderArchive.hpp: when -ObjC is used, preload all .o files from archives that contain .objc_ symbols + + +----- Tagged ld64-69 + +2006-12-13 Nick Kledzik + + prebound interior pointers must be non-zero + * src/MachOWriterExecutable.hpp: in fixUpReference_powerpc() set lazy pointers bound to with the dylib to + their target value. Properly set REFERENCE_FLAG_UNDEFINED_* flags in reference table and n_desc + + +2006-12-09 Nick Kledzik + + ld64 fails to detect error that ld_classic does + * src/MachOWriterExecutable.hpp: check for absolute reloc to an external symbol + * src/MachOReaderRelocatable.hpp: ignore -mlong-branch stubs in .o files + + +2006-12-09 Nick Kledzik + + symbols with REFERENCED_DYNAMICALLY should never be stripped + * src/MachOWriterExecutable.hpp: update Writer::shouldExport() to check for kSymbolTableInAndNeverStrip + * unit-tests/test-cases/main-stripped: add test that dynamically referenced symbol cannot be stripped + + +2006-12-08 Nick Kledzik + + * unit-tests/test-cases/allowable-client: add variant test cases (e.g. CoreServices_profile) + * src/ld.cpp: allow frameworks with variant install names (e.g. CoreServices_profile) to be private clients + + +2006-12-08 Nick Kledzik + + * doc/man/man1/ld.1: rewrite man page + * src/Options.h: add warnObsolete() + * src/Options.cpp: use warnObsolete() on many options. Make nonWeak the weak-mis-match default. + Make -ObjC mean -all_load. + +----- Tagged ld64-68.3 + +2006-12-05 Nick Kledzik + + * src/ld.cpp: allow umbrella frameworks to have variant install names (e.g. CoreServices_profile) and still link + + +----- Tagged ld64-68.2 + +2006-12-05 Nick Kledzik + + * src/MachOWriterExecutable.cpp: Use N_PBUD in the symbol table for undefined symbols in prebound dylibs + + +----- Tagged ld64-68.1 + +2006-12-01 Nick Kledzik + + * src/Options.cpp: always generate module tables for 32-bit architectures so that ld_classic + can link against them + + +----- Tagged ld64-68 + +2006-12-01 Nick Kledzik + + seg_addr_table needs matching fuzziness + * src/Options.cpp: special case a how a dozen dylib are looked up in the seg_addr_table + + +2006-12-01 Nick Kledzik + + * src/Options.cpp: have all -static links for 32-bit archs roll over to ld_classic unless + LD_NO_CLASSIC_LINKER_STATIC is set. + * unit-tests/bin/make-recursive.pl: set LD_NO_CLASSIC_LINKER_STATIC for unit tests + + +2006-11-29 Nick Kledzik + + ld64-67: QTComponents fails to build + * src/MachOReaderRelocatable.hpp: don't error out when a local non-lazy pointer does not point to a symbol + * unit-tests/test-cases/strip_local: added test case + + +2006-11-28 Nick Kledzik + + Need a way to mark libraries usable by dynamic linker but unusable by static linker + * src/Options.cpp: allow -client_name to be used with main executables + * src/ld.cpp: generalize -allowable_client. Any dylib can now restrict who can link against it. As a convention + linking with -allowable_client '!' will mean no one can statically link with the dylib. It can still be loaded + dynamically, or by any existing clients, but no new clients can link with it. + * unit-tests/test-cases/allowable-client/Makefile: enable previously commented out test cases. Add test cases + of a dylib that allows no clients and just one client + +2006-11-27 Nick Kledzik + + -final_output should be used if -install_name not used + * src/Options.cpp: fall back to using -final_output for install name + + +----- Tagged ld64-67 + +2006-11-17 Nick Kledzik + + * src/MachOWriterExecutable.hpp: support __IMPORT segment being slide independently of __DATA segment in shared cache + + +2006-11-16 Nick Kledzik + + 9a303: ld -filelist Bus Error + * src/Options.cpp: add check that -filelist is followed by an argument + + +2006-11-16 Nick Kledzik + + * src/MachOWriterExecutable.hpp: when building split-seg dylibs, LINKEDIT goes in read-only side + + +2006-11-15 Nick Kledzik + + * src/MachOWriterExecutable.hpp: set proper attributes for __eh_frame in ld -r mode + * unit-tests/test-cases/eh_frame: added test case + + +2006-11-10 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: redirect references to static weak stubs to the real target + + +2006-11-09 Nick Kledzik + + * src/MachOWriterExecutable.hpp: r_address is offset from first LC_SEGMENT vmaddr - not from segment with lowest address + + +----- Tagged ld64-66.1 + +2006-11-09 Nick Kledzik + + * src/MachOWriterExecutable.hpp: initialize fModuleInfoAtom to zero + + +2006-11-08 Nick Kledzik + + FSF GCC's libjava doesn't link with Ochre ld64 + * src/MachOReaderRelocatable.hpp: ignore debug_line section if debug_info section is missing or empty + +----- Tagged ld64-66 + +2006-11-07 Nick Kledzik + + SWB: d64-65 does not built usage split-seg dylibs + * src/MachOWriterExecutable.hpp: when prebinding split-seg correctly set r_address fields and on + disk values for external relocations + * unit-tests/test-cases/prebound-split-seg: added test case + + +2006-11-03 Nick Kledzik + + * src/MachOReaderDylib.hpp: don't report dependent libraries if MH_NO_REEXPORTED_DYLIBS bit is set + * src/MachOWriterExecutable.hpp: set MH_NO_REEXPORTED_DYLIBS bit if dylib does not logically re-export any other dylibs + * unit-tests/test-cases/re-export-flag: added test case + * src/machochecker.cpp: validate use of MH_NO_REEXPORTED_DYLIBS + + +2006-11-02 Nick Kledzik + + Mysterious messages from ld64 with MACOSX_DEPLOYMENT_TARGET = 10.5 + * src/MachOWriterExecutable.hpp: kPointerWeakImport is a valid reference type to cross segments + + +2006-11-02 Nick Kledzik + + * src/Options.cpp,h: Add support for -rpath + * src/MachOFileAbstraction.hpp: add macho_rpath_command + * src/MachOWriterExecutable.hpp: add RPathLoadCommandsAtom to create LC_RPATH for each -rpath + + +----- Tagged ld64-65 + +2006-10-30 Nick Kledzik + + x86_64 default stack_addr is wrong + * src/Options.cpp: change default 64-bit stack location when using -stack_size + + +2006-10-30 Nick Kledzik + + dylibs need modules for 10.3 and for ld_classic in Salt + * src/MachOWriterExecutable.hpp: add ModuleInfoLinkEditAtom to create module table stuff + * src/Options.cpp,h: Add needsModuleTable() + * src/MachOFileAbstraction.hpp: Add macho_dylib_module, macho_dylib_reference, and macho_dylib_table_of_contents + + +2006-10-27 Nick Kledzik + + * unit-tests/test-cases/no-uuid/Makefile: add -gstabs+ to be compatible with latest compiler + * unit-tests/test-cases/stabs-coalesce/Makefile: add -gstabs+ to be compatible with latest compiler + + +2006-10-26 Nick Kledzik + + i386 -mdynamic-no-pic switch statement jump table is out of line + * src/MachOWriterExecutable.hpp: for i386 don't check for direct references to weak symbols + + +2006-10-26 Devang Patel + + * src/LLVMReader.hpp: Supply final output file path to optimizer. + +2006-10-26 Devang Patel + + * src/ObjectFile.h: Make setSection* methods virtual. + * src/LLVMReader.hpp: Override setSection* methods. + +2006-10-26 Devang Patel + + * unit-tests/test-case/llvm-integration/a13.h: New. + * unit-tests/test-case/llvm-integration/a13.cc: New. + * unit-tests/test-case/llvm-integration/main13.cc: New. + +2006-10-26 Devang Patel + + * src/options.h, src/options.cpp: Add -save-temps command line option. + * src/LLVMReader.hpp: Use saveTemps option. + + +2006-10-26 Devang Patel + + * src/LLVMReader.hpp: Remove invalid module from memory. + +2006-10-26 Devang Patel + + * src/LLVMReader.hpp: Collect symbol alignment info from LLVM optimizer. + +2006-10-21 Eric Christopher + + * src/ld.cpp (Linker::Linker): Check for LD_NO_CLASSIC_LINKER before + invoking ld_classic. + * unit-tests/test-cases/relocs-literals/Makefile: Run for -mdynamic-no-pic + and pic. + * unit-tests/test-cases/static-executable/Makefile: Skip for 64-bit. Add + -dead_strip to command line. + +----- Tagged ld64-64.2 + +2006-10-19 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: stop copying LLVMReader.hpp into man1 directory + +----- Tagged ld64-64.1 + +2006-10-19 Nick Kledzik + + ld64-63.1 erroneously coalesces an empty string with a non-empty string + * src/MachOReaderRelocatable.hpp: rework cstring parsing to not assume all strings are start + at section alignment boundaries, and when coalescing empty strings always use one with greatest + alignment requirement + * src/MachOWriterExecutable.hpp: in -r mode, don't pad end of cstring section + * src/ObjectFile.h: correctly name leadingZeros() as trailingZeros() + * src/ld.cpp: leadingZeros() --> trailingZeros() + + +2006-10-18 Eric Christopher + + * unit-tests/test-cases/read-only-relocs/Makefile: Skip for x86_64. + * unit-tests/test-cases/llvm-integration/Makefile: Skip if llvm isn't + present. + +2006-10-18 Nick Kledzik + + ld64 change required to go with assembler cstring change + ld64 should error when a local relocation references an address outside its section + * src/MachOReaderRelocatable.hpp: for x86_64 in order to work with local or external relocations to cstrings + change parser to allow atoms with a pending name that is resolved after references are instantiated. + Make direct references to kRegularDefinition atoms. + * src/MachOWriterExecutable.hpp: in -r mode for x86_64 generate L* labels for cstrings and use external relocations + * unit-tests/test-cases/relocs-literals/test.c: add two cases of cstring literal plus addend + + +2006-10-06 Nick Kledzik + + check MACOSX_DEPLOYMENT_TARGET if -macosx_version_min is not used + * src/Options.cpp: if -macosx_version_min is not used, check MACOSX_DEPLOYMENT_TARGET, if + that is unused, default to 10.5 + +----- Tagged ld64-64 + +2006-10-06 Nick Kledzik + + crash in ppc64 program - bl to saveFP, but saveFP is too far away? + * src/MachOWriterExecutable.hpp: in addPPCBranchIslands(), properly account for growth of __text + + +2006-10-06 Nick Kledzik + + Linker-defined alias converts reference into definition and generates error. + * src/MachOReaderRelocatable.hpp: only alias symbols actually in the symbol table + + +2006-10-06 Nick Kledzik + + * unit-tests/test-cases/dwarf-debug-notes/Makefile: crt1.o no longer has stabs, so don't need to strip it + * unit-tests/test-cases/dwarf-debug-notes-r/Makefile: crt1.o no longer has stabs, so don't need to strip it + + +2006-10-06 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: rework dwarf line parsing to fix warnings that starting + showing up with gcc-5421 + + +2006-10-05 Eric Christopher + + ld64 needs to support libtool options + * src/Options.cpp (Options::parse): Add -noall_load, -install_name, + -current_version and -compatibility_version. + +2006-10-03 Eric Christopher + + * src/Options.cpp (Options::gotoClassicLinker): Use execvp + to call ld_classic. + +2006-10-03 Eric Christopher + + * unit-tests/test-cases/tentative-to-real/Makefile: Clean up after tests. + +2006-10-03 Eric Christopher + + * unit-tests/include/common.makefile (VALID_ARCHS): Add x86_64. + (OTOOL): Remove munging based on ARCH. + +2006-09-29 Nick Kledzik + + problem merging .o files built with and without -fno-common + src/Options.*: make MakeTentativeDefinitionsReal a reader option + src/ObjectFile.h: make MakeTentativeDefinitionsReal a reader option + src/MachOWriterExecutable.hpp: make MakeTentativeDefinitionsReal a reader option + src/MachOReaderRelocatable.hpp: only assign a section name of __common to + tentative defintions when making a final linked image + + +2006-09-28 Nick Kledzik + + src/Options.h/.cpp: add support for -segaddr option + src/MachOWriterExecutable.hpp: In Writer::assignFileOffsets(), use -segaddr info + + +2006-09-28 Nick Kledzik + + Emit new CPU subtypes for ppc64 and x86-64 when targeting 10.5 or later + src/MachOWriterExecutable.hpp: set high bit of cpusubtype of 64-bit main executables when targeting 10.5 or later + + +2006-09-28 Devang Patel + + Add LLVM LTO support + src/LLVMReader.hpp: New file. + src/ld.cpp: Add optimization phase. Use LLVM LTO. + unit-tests/test-cases/llvm-integration: New tests. + +2006-09-27 Nick Kledzik + + ld64.xcodeproj/project.pbxproj: remove accidental install of source file into man1 + + +2006-09-25 Nick Kledzik + + src/Architectures.hpp: add kPointerDiff16 for ppc and ppc64 + src/MachOReaderRelocatable.hpp: support kPointerDiff16 + src/MachOWriterExecutable.hpp: support kPointerDiff16 + +----- Tagged ld64-63.1 + +2006-09-22 Nick Kledzik + + src/MachOWriterExecutable.hpp: include stubs in LC_SEGMENT_SPLIT_INFO + + +2006-09-21 Nick Kledzik + + src/Options.cpp: disable split-seg dylibs for 64-bit architectures + + +2006-09-19 Nick Kledzik + + src/MachOReaderRelocatable.hpp: rework __cstring parsing to better handle mixed alignment cstrings + src/MachOWriterExecutable.hpp: in -r mode, make all __cstrings aligned to section alignment + + +2006-09-19 Nick Kledzik + + src/MachOWriterExecutable.hpp: rework encoding of LC_SEGMENT_SPLIT_INFO + + +2006-09-19 Nick Kledzik + + src/Options.cpp: check for -search_paths_first in first pass + + +----- Tagged ld64-63 + +2006-09-15 Nick Kledzik + + src/Options.cpp: since the ld64 will repeatedly search an archive, and some project list archives + multiple times on command line to work with traditional linkers, automatically ignore duplicate libraries + unit-tests/test-cases/archive-duplicate: added test case + + +2006-09-15 Nick Kledzik + + src/Options.cpp: support -r -static + src/MachOWriterExecutable.hpp: support -r -static an don't generate LC_DYSYMTAB + + +2006-09-14 Nick Kledzik + + src/MachOWriterExecutable.hpp: in -r mode references to weak symbols should not create external relocations + as that can cause nmedit to errror later. + + +2006-09-13 Nick Kledzik + + ld64: Handle .objc_class_name exports specially + src/Options.cpp: add hack so that .objc_class_name_XXX in -exported_symbols_list imples _OBJC_CLASS_$_XXX + src/ld.cpp: add hack to supporess errors about .objc_class_name_XXX or _OBJC_CLASS_$_XXX being undefined + + +2006-09-12 Nick Kledzik + + Support -prebind when targeting ppc and OS < 10.4 + src/Options.h: add splitSeg() and baseWritableAddress() + src/Options.cpp: Add support for -seg_addr_table and LD_SEG_ADDR_TABLE, and -prebind and LD_PREBIND. + src/src/MachOWriterExecutable.hpp: support split-seg and canonical prebound files to be generated + + +2006-09-11 Nick Kledzik + + Linking a dylib or binary from identical binaries should produce the same output + src/MachOWriterExecutable.hpp: set the timestamps to be constant + + +2006-09-11 Nick Kledzik + + Linker support for ordering all sections and symbols + src/Options.cpp: Add -order_file_statistics. Allow architecture prefixes in order files + src/ld.cpp: Use fOptions.printOrderFileStatistics() + + +2006-09-11 Nick Kledzik + + Support -sectorder + unit-tests/test-cases/order_file: added test case + src/ld.cpp: Implement order file support in Linker::sortAtoms() + src/Options.h: add Options.orderedSymbols() + src/Options.cpp: add parseOrderFile(), implement -order_file + + +2006-09-07 Nick Kledzik + + need -i for 64-bit (or equivalent) + Support -i for aliasing exported symbols + unit-tests/test-cases/alias-objects: added + unit-tests/test-cases/alias-command-line: added + src/ObjectFile.h: Added Atom::getOrdinal() as new way to sort atoms. Added ReaderOptions.fAliases + src/MachOReaderRelocatable.hpp: Added SymbolAliasAtom to handle multiple symbols to same address + src/MachOReaderArchive.hpp: implement Atom::getOrdinal() to space out atom ordinals across member objects + src/Options.cpp: support -i, -alias, -alias_list. Move search of /Network/Library/Frameworks to after /System/Library/Frameworks + src/MachOWriterExecutable.hpp: pad out seg_info data. Implement getOrdinal(). + src/ObjectDump.cpp: call constructors directly instead of using make() wrapper + + +2006-09-01 Nick Kledzik + + Need the ability to tag libraries/plug-ins with security attributes + src/MachOReaderDylib.hpp: add warning if using -root_safe or -setuid_safe and link against dylib that is not + src/ObjectFile.h: add ReaderOption fRootSafe and fSetuidSafe + src/Options.cpp: handle -root_safe or -setuid_safe command line options + src/MachOWriterExecutable.hpp: set MH_ROOT_SAFE and MH_SETUID_SAFE flags + + +2006-08-31 Nick Kledzik + + src/ld.cpp: Add Linker::processDTrace() for processing dtrace static probes + src/OpaqueSection.hpp: renamed, add symbol name, add ability to add references + ld64.xcodeproj/project.pbxproj: remove SectCreate.cpp, add OpaqueSection.hpp + + +2006-08-28 Nick Kledzik + + Add convention for removing symbols at link time + Assembler -L option causes ld64 to split stubs + unit-tests/test-cases/special-labels: added test case + src/MachOReaderRelocatable.hpp: ignore L* labels, make l* labels as kSymbolTableNotIn + + +2006-08-28 Nick Kledzik + + src/lObjectFile.h: refactor isTargetUnbound() into getTargetBinding() + src/ld.cpp: create __dof section in final linked images from dtrace static probes + src/Architectures.hpp: add kDtraceProbe + src/Options.h/cpp: Add support for -dtrace + src/machochecker.cpp: support LC_SEGMENT_SPLIT_INFO + src/MachOWriterExecutable.hpp: support kDtraceProbe + src/MachOReaderRelocatable.hpp: suppport kDtraceProbe + + +2006-08-25 Nick Kledzik + + generate LC_SEGMENT_SPLIT_INFO for 10.5 or later dylibs + src/Options.h&.cpp: implement sharedRegionEligible() to control when LC_SEGMENT_SPLIT_INFO is added + src/MachOFileAbstraction.hpp: add macho_linkedit_data_command + src/MachOWriterExecutable.hpp: generate LC_SEGMENT_SPLIT_INFO load command and linkedit content + +----- Tagged ld64-62 + +2006-08-15 Nick Kledzik + + wrong error message when symbol is found in unused indirect library + src/ld.cpp: remove indirect libraries if they are not re-exported + unit-tests/test-cases/indirect-dylib: added test case + + +2006-08-15 Nick Kledzik + + alignment needs to be richer + src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info + src/ld.cpp: modify SymbolTable::add() to work with new Alignment type + src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers() + src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address + src/ObjectDump.cpp: print richer Alignment info + unit-tests/test-cases/align-modulus: added test case + + +2006-08-11 Nick Kledzik + + remove OPEN_SOURCE conditionals around x86_64 support + + +2006-07-31 Nick Kledzik + + ld64 while linking cc1 [ when dead_strip is ON] + src/ld.cpp: Add ivar fAtomsWithUnresolvedReferences to track atoms not initially resolvable + unit-tests/test-cases/dead_strip-archive: added test case + + +2006-07-31 Nick Kledzik + + x86_64: instructions with immediate and rip-relative operands need to use new relocation types + src/MachOWriterExecutable.hpp: generate new reloc types in -r mode + src/MachOReaderRelocatable.hpp: parse new reloc types + unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type + + +2006-07-18 Nick Kledzik + + src/MachOReaderRelocatable.hpp: suppress warning about dwarf info parsing for one benign no-op case + the compiler emits when there are not functions in the __text section + + +2006-07-17 Nick Kledzik + + faster debug note generation + src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a + pass per .o file. Added timing info for collectDebugInfo() to -print_statistics + unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r + unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order + + +2006-07-17 Nick Kledzik + + ld64 VSIZE is 1.18GB when building Finder ppc64 + src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped + +----- Tagged ld64-61.1 + +2006-07-11 Nick Kledzik + + ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name + src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message + +2006-07-11 Nick Kledzik + + If -arch is missing, rollover to ld_classic does not happen + src/Options.h: make gotoClassicLinker() public + src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386 + +----- Tagged ld64-61 + +2006-06-29 Nick Kledzik + + ld64 should be renamed to ld + src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen + src/ld.cpp: alter version string + ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64 + doc/man/man1/ld.1: added + +----- Tagged ld64-60 + +2006-06-28 Nick Kledzik + + Can't link large ppc64 program: ld64 says "bl out of range" + MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions + and properly chain together branch islands + MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references + only when done + +2006-06-28 Nick Kledzik + + MySQL-36 fails to build with ld64-59 + src/MachOReaderRelocatable.hpp: back out fix for 4585335 + src/MachOWriterExecutable.hpp: back out fix for 4585335 + +2006-06-27 Nick Kledzik + + src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how + dwarf debug notes are formed. + +2006-06-23 Nick Kledzik + + + + ld64 doesn't support variant linking -framework fw,_debug + src/Options.cpp: enhance findFramework() to support suffixes + +----- Tagged ld64-59 + +2006-06-22 Nick Kledzik + + ld64 lost DWARF debug notes + src/MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later + unit-tests/test-cases/dwarf-debug-notes-r: added test case + +2006-06-21 Nick Kledzik + + python 64-bit address miscalculation + src/MachOReaderRelocatable.hpp: change getTargetOffset() to sign extend the 32-bit value to 64-bits + +2006-06-21 Nick Kledzik + + ld64 seems to offset things incorrectly when using -r + src/MachOWriterExecutable.hpp: in -r mode, virtual sections should not increment address + + +----- Tagged ld64-58 + +2006-06-16 Nick Kledzik + + src/rebase.cpp: fix page alignment problem + src/rebase.cpp: fix endianess problem with local non-lazy pointers + +2006-06-15 Nick Kledzik + + src/rebase.cpp: fix to build in CurryWeed + ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed + +2006-06-15 Nick Kledzik + + Support .objc_class_name_* symbols + src/ObjectFile.h: Add kSymbolTableInAsAbsolute + src/MachOReaderRelocatable.hpp: synthesize references to required objc classes + src/MachOWriterExecutable.hpp: write objc_class_name as absolute symbol + unit-tests/test-cases/objc-references: added + +2006-06-15 Nick Kledzik + + SECTION_ATTRIBUTES unset in ppc64 mach-o header + src/MachOWriterExecutable.hpp: add section attribute for sections with code + +2006-06-15 Nick Kledzik + + ld64 bogus duplicate symbol name linking GNU libobjc + src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes + +2006-06-15 Nick Kledzik + + x86_64: ".align" directive not honored + src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size + +2006-06-14 Nick Kledzik + + jump table into middle of weak symbol causes error + src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols + src/MachOWriterExecutable.hpp: do not error on absolute references to interior of weak symbols + +2006-06-13 Nick Kledzik + + src/Options.cpp: allow -image_base as an alias for -seg1addr + +2006-06-13 Nick Kledzik + + implement -d + src/Options.h: add fMakeTentativeDefinitionsReal + src/Options.cpp: set fMakeTentativeDefinitionsReal if -d option is found + src/MachOWriterExecutable.hpp: turn tentative into real definition if makeTentativeDefinitionsReal + unit-tests/test-cases/btentative-to-real: added test case + +2006-06-13 Nick Kledzik + + implement -bundle_loader + src/Options.h: add fBundleLoader bit to DynamicLibraryOptions + src/Options.cpp: handle -bundle_loader + src/ld.cpp: pass fBundleLoader bit to MachOReaderDylib + src/MachOReaderDylib.hpp: support reading MH_EXECUTE files if fBundleLoader is set + src/MachOWriterExecutable.hpp: set bundle loader ordinal as EXECUTABLE_ORDINAL + unit-tests/test-cases/bundle_loader: added test case + +2006-06-12 Nick Kledzik + + -syslibroot can cause "can't find ordinal for imported" error + src/MachOReaderDylib.hpp: in Reader::reExports() compare install path in addition to load path + + +2006-06-10 Nick Kledzik + + Need rebasing tool + src/rebase.cpp: added + unit-tests/test-cases/rebase-basic: added + doc/man/man1/rebase.1: added + ld64.xcodeproj/project.pbxproj: added rebase target. changed all targets to build with dwarf + + +2006-06-10 Nick Kledzik + + src/machochecker.cpp: add some ppc reloc sanity checking + +----- Tagged ld64-57 + +2006-06-06 Nick Kledzik + + ld64 is not adding a final '/' char on the initial directory-name SO stab debug map entry + ld.cpp: Change Linker::synthesizeStabs() to assure directory SO always has a trailing slash + unit-tests/test-cases/dwarf-debug-notes/expected-stabs: update with trailing / + +2006-06-06 Nick Kledzik + + -sectcreate of a 0-byte section fails + MachOWriterExecutable.cpp: Don't error out on zero length segments + MachOWriterExecutable.cpp: For ppc64 reloc base address is the first writable segment iff + there is a writable segment >4GB from base address + +2006-06-04 Eric Christopher + + Radar 4560240 + Radar 3964999 + * src/ld.cpp (createReader): Fixed error message. + (resolve): Ditto. + (resolveFrom): Ditto. + (checkUndefines): Ditto. + +----- Tagged ld64-56 + +2006-05-23 Nick Kledzik + + No debug notes for ObjC methods when linking with ld64 + ld.cpp: don't limit debug notes to functions starting with underscore + +2006-05-22 Nick Kledzik + + ld64 spends much time in mach_o::relocatable::Reader::findAtomByName + * src/MachOReaderRelocatable.hpp: add makeReferenceToSymbol() so that x86_64 does not need to do by-name lookups + +2006-05-22 Nick Kledzik + + remove inferring warning + * ld.cpp: Remove "inferring" warning. If a link failed and now arch was specifed add which arch was + inferred to error message + +2006-05-19 Nick Kledzik + + ld64 does not honor -arch_multiple + * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message + +2006-05-19 Nick Kledzik + + Support S_16BYTE_LITERALS section types + * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS + * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS + +2006-05-19 Nick Kledzik + + "warning can't parse dwarf compilation unit info" warnings building debug + * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing + +----- Tagged ld64-55 + +2006-05-18 Nick Kledzik + + Default the pagezero size to 4GB for x86-64 + * src/Options.cpp: Chnage default the pagezero size to 4GB for x86-64 + +2006-05-18 Nick Kledzik + + x86_64 CarbonCore fails to link with "atom not found in symbolIndex" + * src/MachOWriterExecutable.hpp: in buildObjectFileFixups() don't call addObjectRelocs() on kNoFixUp references + +2006-05-18 Nick Kledzik + + ld64: .section defaults to read-only + * src/MachOReaderRelocatable.hpp: default unknown segments to r/w + +2006-05-18 Nick Kledzik + + -fvisibility=hidden causes crashes for x86_64 + * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions + +2006-05-12 Nick Kledzik + + * src/Architectures.hpp: add x86::kAbsolute32 + * src/MachOReaderRelocatable.hpp: generate x86::kAbsolute32 for mdynamic-no-pic instructions + * src/MachOWriterExecutable.hpp: process x86::kAbsolute32 reference kind + +----- Tagged ld64-54 + +2006-05-11 Nick Kledzik + + CF-393 failes to link for x86_64 + * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer::fixUpReferenceRelocatable + +2006-05-11 Nick Kledzik + + warning arch x86_64 not found using i386 + * src/ld.cpp: remove hack to allow x86_64 to link against i386 dylibs + + +2006-05-10 Nick Kledzik + + x86_64: .objc_class_name symbol names scrambled + * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections + + +2006-05-08 Nick Kledzik + + Support -dead_strip + * src/Options.h/cpp: implement -why_load and -why_live. Enable -dead_strip. + * src/MachOReaderArchive.hpp: implement -why_load + * src/MachOReaderRelocatable.hpp: suppress GCC_except_table* symbols in final output + * src/ld.cpp: implement dead code stripping + * unit-tests/test-cases/dead_strip: added + +----- Tagged ld64-53 + +2006-05-05 Nick Kledzik + + * src/Options.cpp: make 10.4 be minimum OS version for newer architectures + +2006-05-05 Nick Kledzik + + N_SO symbols in 64-bit builds have a zero address for n.n_value + * src/ld.cpp: for SO stabs, associate first and last atom in the SO range + * src/MachOWriterExecutable.hpp: use atom associated with SO stab to set ins n_value + +2006-05-05 Nick Kledzik + + * MachOWriterExecutable.hpp: fix end FUN stab to have length of function + + +2006-05-02 Nick Kledzik + + 64-bit main executables should have 4GB zero page by default + * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64 + 64 bit: apps with -mdynamic-no-pic seg fault when page zero > 4GB + * src/MachOWriterExecutable.cpp: rework pagezero for ppc64 so that if any mdynamic-no-pic code + is found, the code is kept in the low 2GB, and a new segment is create to map away up to 4GB. + +2006-05-02 Nick Kledzik + + * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack + overlaps shared region + +----- Tagged ld64-52.1 + +2006-05-01 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle + the __data section too. + +----- Tagged ld64-52 + +2006-04-28 Nick Kledzik + + 64-bit: 9A152 TextEdit crashes in dlopen on bring-up + * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection + +2006-04-28 Nick Kledzik + + 64 Bit: Development build of ppc64 TextEdit gets confused about static variables + * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol + + + +2006-04-21 Nick Kledzik + + * src/Options.cpp: fix default address for ppc64 custom stack + * src/MachOWriterExecutable.cpp: fix set up of ppc64 custom stack + + +2006-04-14 Nick Kledzik + + * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name + +----- Tagged ld64-51.1 + +2006-04-13 Nick Kledzik + + 64-bit: 9A152 TextEdit crashes in dlopen on bring-up + * src/MachOReaderRelocatable.hpp: when detecting anonymous non-lazy-pointers disqualify data + that points to static or global symbols + * src/ld.cpp: print version of ld64 in error messages + + +----- Tagged ld64-51 + +2006-04-11 Nick Kledzik + + exported symbols not properly stripped + * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope() + +2006-03-31 Nick Kledzik + + ld64 fails when linking debug ppc64 HIToolbox + * src/MachOReaderRelocatable.hpp: handle anonymous non-lazy pointers encoded with local relocations + * src/MachOWriterExecutable.hpp: in -r mode, only generated INDIRECT_SYMBOL_LOCAL for non-lazy targets that + + +2006-03-31 Nick Kledzik + + ld64 should remove generated file if link errors out + * src/MachOWriterExecutable.hpp: catch exceptions in Writer::write(), delete output file, and rethrow + + +----- Tagged ld64-50 + + +2006-03-29 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols + * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space + +2006-03-28 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info + +----- Tagged ld64-49.1 + +2006-03-25 Nick Kledzik + + * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB + +----- Tagged ld64-49 + +2006-03-24 Nick Kledzik + + * src/MachOWriterExecutable.hpp: dyld is allowed to have synthesized non-lazy pointers + ld64 is after processing bad GSYM stabs + * src/MachOReaderRelocatable.hpp: if a GSYM is found that does not match any data symbol, suppress it + +2006-03-23 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceFinal() fix when x86::kPointer is for an + external relocation + +2006-03-23 Nick Kledzik + + * src/Options.cpp: change macosx-min-version to default to a per-architecture setting + add warning if -pagezero_size is not page aligned + * src/MachOWriterExecutable.hpp: properly handle external relocations for ppc64 with 4GB pagezero + * src/machochecker.cpp: sanity check relocation records + +----- Tagged ld64-48 + +2006-03-21 Nick Kledzik + + 64bit: passing function pointer to another function passes the wrong function address + * src/MachOReaderRelocatable.hpp: when processing a non-lazy pointer to a static function, don't accidentally + match it to a STAB symbol. + +2006-03-21 Nick Kledzik + + .eh symbols make up 13% of libstdc++'s stripped binary size + * src/ObjectFile.h: add ReaderOptions.fForFinalLinkedImage + * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage + * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image + +2006-03-21 Nick Kledzik + + ld64 does not parse optional second argument to -filelist + * unit-tests/test-cases/filelist: added + * src/Options.cpp: in Options::loadFileList() handle comma option + + +----- Tagged ld64-47.1 + + +----- Tagged ld64-47 + + +----- Tagged ld64-46 + +2006-03-10 Nick Kledzik + + ld64 should figure out architecture from .o files + * unit-tests/test-cases/auto-arch: added + * src/ld.cpp: added Linker::inferArchitecture() to scan .o files are infer architecture to link + * src/MachOReaderArchive.hpp: enhanced validFile() to look deeper into archive and really valdate + * src/MachOWriterExecutable.hpp: stop using fOptions.architecture() + * src/Options.cpp: stop defaulting to ppc64 + + +2006-03-09 Nick Kledzik + + Need "intentionally left blank" dylib stubs + * unit-tests/include/common.makefile: add VALID_ARCHS + * unit-tests/run-all-unit-tests: set up VALID_ARCHS + * unit-tests/test-cases/blank-stubs: add test case + * src/ld.cpp: in addDylib(), detect and ignore blank stubs + * src/MachOReaderDylib.hpp: in constructor, handle blank stubs + +2006-03-09 Nick Kledzik + + crash in stub with 2GB pagezero + * src/MachOWriterExecutable.hpp: StubAtom can't be no-pic if a large zero-page is used + +2006-03-06 Nick Kledzik + + * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two + +----- Tagged ld64-45 + + +2006-03-06 Nick Kledzik + + LP64/9A122: ld64: hang when trying to link DiscRecording framework + * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion + + +----- Tagged ld64-44 + +2006-03-04 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix again test for detection of anonymous non-lazy-pointer. + Error out if .o file contains old __DWARFA style dwarf. + +2006-03-02 Nick Kledzik + + * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. + +----- Tagged ld64-43 + + +2006-03-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: tighten detection of anonymous non-lazy-pointer + +----- Tagged ld64-42 + +2006-02-28 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment + +2006-02-28 Nick Kledzik + + SWB: ld64-37 (can't resolve symbol ___dso_handle) + * src/MachOWriterExecutable.hpp: add class DsoHandleAtom + +2006-02-28 Nick Kledzik + + * unit-tests/test-cases/literals-coalesce-alignment: added test case + * src/ld.cpp: when coalescing strings pick one with greater alignment + ld64: CG link failed because lo14 reference to anonymous non-lazy-pointer not aligned + * unit-tests/test-cases/relocs-c/test.c: tweak to fail like 4458660 + * src/MachOReaderRelocatable.hpp: detect anonymous non-lazy-pointer and transform into real non-lazy-pointers + +----- Tagged ld64-41 + +2006-02-24 Nick Kledzik + + * src/Options.cpp: Warning about -no_dead_strip_inits_and_terms and -i options. + Fix -weak-l option. + +----- Tagged ld64-40 + +2006-02-24 Nick Kledzik + + Leopard9A113: ppc64 libstdc++.dylib initializer crashes in pthread_once + * unit-tests/test-cases/multiple-entry-points: added + * src/MachOReaderRelocatable.hpp: make sure that if there are multiple symbols with the same + address, that we properly make zero length atoms for all but last symbol + +2006-02-24 Nick Kledzik + + * src/Options.cpp: ld64 doesn't realpath(3) B&I tracing paths + +2006-02-24 Nick Kledzik + + * src/Options.cpp: 9A110: ld64 can't deal with section names >16 chars + +2006-02-23 Nick Kledzik + + * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations + * src/Options.cpp: use vector.reserve() to minimize re-allocations + * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations + * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations + * src/ld.cpp: use vector.reserve() to minimize re-allocations + +2006-02-23 Nick Kledzik + + ld64 creates corrupt executables (and has malloc errors) with -headerpad option + * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom::setSize() to update fLargestAtomSize + * unit-tests/test-cases/header-pad: added + +2006-02-23 Nick Kledzik + + ld64 creates invalid static executables + * src/MachOWriterExecutable.hpp: Change MachHeaderAtom::copyRawContent() to create correct header + for static executables. Change SymbolTableLoadCommandsAtom to skip LC_DYSYMTAB for static executables + * src/machochecker.cpp: Add tests that static executables are well formed + * unit-tests/test-cases/static-executable: added + +2006-02-22 Nick Kledzik + + * src/Options.cpp: chnage printf on unknown arg to a throw + +----- Tagged ld64-39 + +2006-02-20 Nick Kledzik + + * unit-tests/test-cases/read-only-relocs: added new test case + * src/MachOWriterExecutable.hpp: detect and error on relocs in read-only sections + * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs + +2006-02-20 Nick Kledzik + + * unit-tests/test-cases/stabs-coalesce: added new test case + * src/ld.cpp.hpp: in collectStabs removed unused stabs + +----- Tagged ld64-38 + +2006-02-17 Nick Kledzik + + * src/MachOWriterExecutable.hpp: set correct n_sect field of stabs + +2006-02-15 Nick Kledzik + + * src/MachOReaderArchive.hpp: with -all_load skip over both kinds of SYMDEFs + * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case + +----- Tagged ld64-37 + +2006-02-13 Eric Christopher + + * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. + Adjust whitespace. + +2006-02-13 Nick Kledzik + + * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceRelocatable() fix kPCRel32 for external case + +2006-02-13 Nick Kledzik + + * unit-tests/test-cases/zero-fill: added + * src/machochecker.cpp: check that S_ZEROFILL have no file offset + * src/MachOWriterExecutable.hpp: rework assignFileOffsets() to fix rdar://problem/4441145 + +2006-02-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix use of first zero-length c-string in .o file + +2006-02-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix uninitialized fAlignment + +2006-02-12 Nick Kledzik + + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add pointer-diff cases + * src/Architectures.hpp: make size explicit in ppc/ppc64 kPointerDiff + * src/MachOReaderRelocatable.hpp: don't allow kPointerDiff64 for ppc (just ppc64) + * src/MachOWriterExecutable.cpp: set proper r_length for ld -r of kPointerDiff + +----- Tagged ld64-36 + +2006-02-08 Nick Kledzik + + * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions + +2006-02-08 Nick Kledzik + + * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section + Keep S_COALESCED attribute for __eh_frame + +2006-02-08 Nick Kledzik + + * src/ld.cpp: Temporarily turn allowable client errors into warnings + * unit-tests/test-cases/allowable-clientMakefile: Temporarily let warnings be ok for above + * src/MachOWriterExecutable.hpp: fix ld -r to not use external relocations for symbols make static + +2006-02-08 Nick Kledzik + + * src/ld.cpp: A sibling in an umbrella can always link with its other siblings + * unit-tests/test-cases/allowable-client: add test case for above + +2006-02-08 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support LOCAL non-lazy pointers to hidden symbols + * src/machochecker.cpp: verify indirect symbol table + * unit-tests/test-cases/private-non-lazy: added test case + +2006-02-07 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix calculation of file offsets in ld -r mode + * src/machochecker.cpp: verify segment file offsets are within file + +----- Tagged ld64-35 + +2006-02-06 Nick Kledzik + + * ld.cpp: allow parent of sub-framework to link + * unit-tests/test-cases/allowable-client/Makefile: added cases for parent and clients of parent + +2006-02-04 Nick Kledzik + + * unit-tests/test-cases/relocs-c/test.c: added some array cases + * src/MachOReaderRelocatable.hpp: factor out makeReferenceToEH() + * src/MachOWriterExecutable.hpp: add initial support for non-lazy pointer synthesis + +----- Tagged ld64-34 + +2006-02-04 Nick Kledzik + + * src/ld.cpp: fix -no_arch_warnings + fix -undefined warning + Do BINCL/EINCL optimization for gfull stabs + Implement "essential symbols" for stabs (-Sp) + Fix allowable clients to only test on direct libraries + * src/MachOReaderRelocatable.hpp: support BINCL/EINCL stabs + +2006-02-03 Nick Kledzik + + * src/machochecker.cpp: add code to check load command alignment + * src/MachOWriterExecutable.hpp: make load command alignment depend on architecture + +2006-02-03 Nick Kledzik + + * unit-tests/test-cases/literals-coalesce: added + * src/MachOReaderRelocatable.hpp: assure all targets of low14 ppc relocs are at least 4-byte alignmented + +----- Tagged ld64-33 + +2006-02-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals + * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 + +----- Tagged ld64-32 + +2006-02-02 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms + +2006-02-02 Nick Kledzik + + * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one + * unit-tests/test-cases/archive-weak: add test case for above + * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself + * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list + +2006-02-01 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 + +2006-02-01 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms + +----- Tagged ld64-31 + +2006-02-01 Eric Christopher + + * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling... + * unit-tests/include/common.makefile: ... here. + * unit-tests/bin/fail-if-stdin.pl: New. + * unit-tests/test-cases/no-uuid: Ditto. + * src/ld.cpp (Linker::) Add fCreateUUID. + (::Linker): Initialize. + (::collectStabs): Use. Set if dwarf or we have a UUID already. + (::writeOutput): Pass as argument to Writer::write along with option. + * src/Options.h (Option::emitUUID): Declare. + (Option::fEmitUUID): Ditto. + * src/Options.cpp (Option::emitUUID): New. + (parse): Handle -no_uuid. + * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID. + * src/ExecutableFile.h (Writer::Write): Add createUUID boolean. + * src/MachOWriterExecutable: Add UUID forward declaration. + (fUUIDAtom): New. + (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size + to zero at start. + (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID. + (MachHeaderAtom::copyRawContent): Don't count a load command if its size is + 0. + (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit. + + +2006-01-31 Nick Kledzik + + * unit-tests/test-cases/dwarf-debug-notes : Added + * src/ld.cpp: don't generate debug note for .eh symbols + * src/MachOReaderRelocatable.hpp: make dwarf line info to atom matching faster and better + +2006-01-31 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard + * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers + +2006-01-31 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better error message for bad relocs + * src/ObjectDump.cpp: add emacs tab settings + * src/SectCreate.h: ditto + * src/SectCreate.cpp: ditto + * src/machochecker.cpp: ditto + * src/ExecutableFile.h: ditto + +2006-01-30 Eric Christopher + + * src/ExecutableFile.h: Indent. + +2006-01-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: performance improvements + * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore + +2006-01-30 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above + +2006-01-29 Nick Kledzik + + * unit-tests/test-cases/weak_import: added test case + * src/ld.cpp: move code for weak_import mismatch to writer + * src/ObjectFile.h: remove ImportWeakness methods + * src/MachOReaderDylib.hpp: ditto + * src/SectCreate.cpp: ditto + * src/Architectures.hpp: add new ReferenceKinds for weak_imports + * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds + * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches + +2006-01-29 Nick Kledzik + + * src/Options.cpp: verify -allow_stack_execute is only used on main executables + +2006-01-29 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff + * src/debugline.c: sync with latest dwarf reader from Geoff + +2006-01-27 Eric Christopher + + * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. + +2006-01-27 Eric Christopher + + * src/Options.h (Options): Add hasExecutableStack, fExecutableStack. + * src/Options.cpp (Options::hasExecutableStack): New. + (Options::parse): Parse -allow_stack_execute. + * src/MachOWriterExecutable.hpp (MachHeaderAtom::copyRawContent): + Implement MH_ALLOW_STACK_EXECUTION. + * unit-tests/include/common.makefile (FAIL_IF_EMPTY): New. + * unit-tests/bin/fail-if-no-stdin.pl: New file. + * unit-tests/test-cases/allow-stack-execute: New directory. + +2006-01-27 Nick Kledzik + + * src/MachOFileAbstraction.hpp: rely on latest system headers + * src/MachOWriterExecutable.hpp: fix ppc stubs. + wrote new relocationNeededInFinalLinkedImage() to replace common code + +2006-01-27 Eric Christopher + + * src/ld.cpp (logTraceInfo): New. + (Linker::addArchive): Use. + (Linker::addDylib): Ditto. + * src/ObjectFile (ReaderOptions::fTraceOutputFile): New. + * src/MachOReaderArchive.hpp (Reader::Reader): Move trace + logging to Linker::addArchive. + * src/Options.cpp (parsePreCommandLineEnvironment): Check + LD_PRINT_FILE if tracing dylibs or archives. + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state + +2006-01-26 Nick Kledzik + + Rewrite all stabs processing. + Move sythesize of debug notes into ld.cpp + +2006-01-26 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs + +2006-01-25 Nick Kledzik + + * ld64.xcodeproj/project.pbxproj: special case building in Curry + +2006-01-25 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis + +2006-01-24 Eric Christopher + + * src/ld.cpp (Linker::createReaders): Change logging title to XBS. + (Linker::addDylib): Ditto. + * src/MachOReaderArchive.hpp (Reader::Reader): Ditto. + * src/Options.h (fPrintOptions): New. + * src/Options.cpp (Options::Options): Initialize above. + (Options::checkForFile): Change logging title to XBS. + (Options::findFramework): Ditto. + (Options::parse): Add log for options. + (Options::parsePreCommandLineEnvironmentSettings): Add LD_TRACE_ARCHIVES, + LD_TRACE_DYLIBS, and LD_PRINT_OPTIONS. + +2006-01-24 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: better C++ eh parsing + +2006-01-23 Eric Christopher + + * unit-tests/bin/fail-if-exit-zero.pl: New. + * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use. + * unit-tests/allowable-client: New test. + * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib. + * src/Options.h (allowableClients): New. + (clientName): Ditto. + (fAllowableClients): Ditto. + (fClientName): Ditto. + * src/Options.cpp: Implement above. + (parse): Handle -allowable_client and -client_name. + * src/MachOReaderDylib.hpp (getAllowableClients): New. + (fAllowableClients): Ditto. + (Reader): Process LC_SUB_CLIENT load command. + * src/ObjectFile.h (parentUmbrella): New. + (getAllowableClients): New. + * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New. + +2006-01-23 Nick Kledzik + + * unit-tests/test-cases/archive-basic: added + * src/ld.cpp: fix shadowed local variable + * src/FileAbstraction.hpp: ld64 shouldn't inline when building debug + +2006-01-23 Nick Kledzik + + * src/ld.cpp: fix symbol not found error message + * src/MachOReaderDylib.hpp: add logging to hash table + * src/MachOReaderRelocatable.hpp: enable stabs processing. Handle static functions with stubs + handle labeled cstrings. + * src/MachOWriterExecutable.hpp: properly suppress atoms not in symbol table. fix low14 error check. + add StubAtomHelper. + * unit-tests/test-cases/relocs-literals/test.c: add more interesting edge cases + +2006-01-17 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes + +2006-01-16 Nick Kledzik + + * src/debugline.{sh}: added + * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf + * src/MachOWriterExecutable.hpp: fix lazy pointer section + * src/ObjectDump.hpp: Fix conditionalization + * unit-tests/test-cases/dwarf-strip: added + +2006-01-11 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 + * src/ObjectDump.hpp: Support -arch option + +2006-01-10 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix stubs for ppc64 + * src/MachOFileAbstraction.hpp: fix typo for macho_routines + * ld64.xcodeproj/project.pbxproj: add machochecker target + * src/machochecker.cpp: new skeleton for checking mach-o file bit + * unit-tests/: Add support for running machochecker + +2006-01-10 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed + * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime + +2006-01-09 Nick Kledzik + + * track modification time of .o files so that sythesized OSO stab will have it + +2006-01-09 Nick Kledzik + + * src/MachOFileAbstraction.hpp: add macho_uuid_command + * src/MachOWriterExecutable.cpp: add UUID load command to generated files + +2006-01-09 Nick Kledzik + + * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped + * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped + +2006-01-05 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: support new relocations + +2006-01-05 Nick Kledzik + + * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB + * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor + +2006-01-05 Nick Kledzik + + refactor: transform Atom::dontStripName() to getSymbolTableInclusion() + * src/ld.cpp: pass dyld_stub_binding_helper to writer + * src/MachOReaderRelocatable.hpp: update synthesized stabs + Ignore stubs and lazy pointers in .o files + Support initializers and terminators + * src/MachOWriterExecutable.hpp: synthesize stubs and lazy pointers as needed + * ld64.xcodeproj/project.pbxproj: change Release target to build with dwarf + +2006-01-03 Eric Christopher + + * src/Options.h (multipleDefinitionsInDylibs): Declare. + (overridingDefinitionInDependentDylib): Ditto. + (warnOnMultipleDefinitionsInObjectFiles): Ditto. + (multiplyDefined): Remove. + (multiplyDefinedUnused): Ditto. + (fMultiplyDefined): Ditto. + (fWarnOnMultiplyDefined): New. + (fMultiplyDefinedDynamic): Ditto. + * src/Options.cpp (Options::Options): Initialize above. + (overridingDefinitionInDependentDylib): New. + (multipleDefinitionsInDylibs): Ditto. + (warnOnMultipleDefinitionsInObjectFiles): Ditto. + (parse): Update comments. Fix parsing of -y option. + Update error message for -dead_strip. Parse above + options. + +2006-01-02 Nick Kledzik + + * Refactor: move Atom::writeContent() to Writer + +2005-12-23 Nick Kledzik + + * Reworked, simplify, and document test harness + * unit-tests/README: Added + +2005-12-23 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fixes for Objective-C + * unit-tests/test-cases/relocs-objc: Added + +2005-12-22 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair + * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf + +2005-12-21 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Fix parsing of literal sections + * src/MachOWriterExecutable.hpp: Fix writing of literal sections + * unit-tests/test-cases/relocs-literals: Added + +2005-12-15 Eric Christopher + + * src/Options.h (enum Treatment): New. + (enum PICTreatment): Delete. + (enum VersionMin): New. + (prebind): Declare. + (macosxVersionMin): Ditto. + (multiplyDefined): Ditto. + (multiplyDefinedUnused): Ditto. + (setVersionMin): Ditto. + (setPICTreatment): Delete. + (setReadOnlyRelocTreatment): Ditto. + (picTreatment): Adjust return type. + (parseTreatment): New. + (fPrebind): Ditto. + (fVersionMin): Ditto. + (fPICTreatment): Change type. + (fMultiplyDefined): New. + (fMultiplyDefinedUnused): Ditto. + (fLimitUndefinedSymbols): Ditto. + + * src/Options.cpp: Fix whitespace. Add comments on options. + (Options::Options): Add initializers for new variables. + (Options::prebind): New. + (Options::macosxVersionMin): Ditto. + (Options::parseTreatment): Ditto. + (Options::setVersionMin): Ditto. + (Options::setReadOnlyRelocTreatment): Delete. + (Options::setPICTreatment): Ditto. + (Options::Parse): Update for above. Add comments. + +2005-12-15 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Add comments about dwarf + +2005-12-14 Nick Kledzik + + * src/ELFFileAbstraction.hpp: Added + * src/ELFReaderRelocatable.hpp: Added + * Lot of fixes for new architecture + * Added __OPEN_SOURCE__ to "Preprocessor Macros" to disable new architecture support by default + +2005-12-13 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections + * unit-tests/test-cases/dwarf-ignore: added + +2005-12-12 Nick Kledzik + + * Added test harness and three initial tests: + relocs-asm, relocs-c, and hello-world + +2005-12-12 Nick Kledzik + + * src/MachOReaderRelocatable.hpp: Massive refactoring: + Now there are three Atom classes, Chopping into Atoms + is done on label boundaries or by knowledge of special + sections, Share lots of ppc/ppc64 code. + Stabs process code is temporarily disabled. + +2005-12-12 Nick Kledzik + + * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort + +2005-12-11 Eric Christopher + + * src/Options.cpp: Reformat. + * src/Options.h: Ditto. + +2005-12-07 Eric Christopher + + * src/MachOReaderRelocatable.hpp (Atom::getAlignment): + When calculating alignment of an Atom, take into account + the alignment from which we pulled the Atom. + +2005-12-06 Nick Kledzik + + * src/Options.cpp src/Options.h: Add design comments + +2005-12-05 Eric Christopher + + * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and + i386 linkers. + +2005-12-05 Eric Christopher + + * ChangeLog: New file. + +2005-12-02 Nick Kledzik + + * src/ObjectFile.h: Add design comments + +2005-11-30 Nick Kledzik + + * Fix uses of __OPEN_SOURCE__ + +2005-11-28 Nick Kledzik + + * Refactor Atom to use getDefinitionKind() + +2005-11-21 Nick Kledzik + + * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode + +2005-11-18 Nick Kledzik + + * x86 tweaks + +2005-11-18 Nick Kledzik + + * src/ObjectDump.cpp: make work with command line arguments + +2005-11-18 Nick Kledzik + + * Massive rework to remove preprocessor conditionals and use templates + +2005-11-14 Nick Kledzik + + * Created new Subversion repository for ld64 from cvs tag ld64-27.2 diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index fd0213c..55fd5ea 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -726,7 +726,6 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -951,12 +950,23 @@ ALWAYS_SEARCH_USER_PATHS = NO; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNUSED_LABEL = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = /usr/local/bin; PREBINDING = NO; PRODUCT_NAME = dyldinfo; + WARNING_CFLAGS = "-Wall"; }; name = Debug; }; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 330a8cb..50982a1 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -471,7 +471,7 @@ class macho_uuid_command { void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(uint8_t uuid[16]) INLINE { memcpy(&fields.uuid, uuid, 16); } + void set_uuid(uint8_t u[16]) INLINE { memcpy(&fields.uuid, u, 16); } typedef typename P::E E; private: diff --git a/ld64/src/ld/Architectures.hpp b/ld64/src/ld/Architectures.hpp index 0d141cf..2a449f1 100644 --- a/ld64/src/ld/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -61,7 +61,7 @@ struct x86 enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kPointerDiff16, kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, - kImageOffset32, kPointerDiff24, + kImageOffset32, kPointerDiff24, kSectionOffset24, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; @@ -74,7 +74,7 @@ struct x86_64 kBranchPCRel32, kBranchPCRel32WeakImport, kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, kGOTNoFixUp, - kImageOffset32, kPointerDiff24, + kImageOffset32, kPointerDiff24, kSectionOffset24, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; @@ -82,7 +82,8 @@ struct arm { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kReadOnlyPointer, + enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, + kPointerDiff32=kPointerDiff, kReadOnlyPointer, kPointerDiff12, kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; diff --git a/ld64/src/ld/ExecutableFile.h b/ld64/src/ld/ExecutableFile.h index 675d3da..dca22f7 100644 --- a/ld64/src/ld/ExecutableFile.h +++ b/ld64/src/ld/ExecutableFile.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -51,15 +51,18 @@ namespace ExecutableFile { virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) = 0; virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; + virtual void addSynthesizedAtoms(const std::vector& existingAtoms, + class ObjectFile::Atom* dyldClassicHelperAtom, + class ObjectFile::Atom* dyldCompressedHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool biggerThanTwoGigs, + uint32_t dylibSymbolCount, + std::vector& newAtoms) = 0; virtual uint64_t write(std::vector& atoms, std::vector& stabs, class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, std::set& atomsThatOverrideWeak, bool hasExternalWeakDefinitions) = 0; diff --git a/ld64/src/ld/LTOReader.hpp b/ld64/src/ld/LTOReader.hpp index 75659e1..0bd200e 100644 --- a/ld64/src/ld/LTOReader.hpp +++ b/ld64/src/ld/LTOReader.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -406,7 +406,7 @@ const char* Reader::tripletPrefixForArch(cpu_type_t arch) case CPU_TYPE_X86_64: return "x86_64-"; case CPU_TYPE_ARM: - return "arm-"; + return "arm"; } return ""; } @@ -522,14 +522,6 @@ bool Reader::optimize(const std::vector& allAtoms, std::vect warning("could not produce merged bitcode file"); } - // if requested, save off merged bitcode file - if ( saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } - // set code-gen model lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; switch ( outputKind ) { @@ -551,12 +543,38 @@ bool Reader::optimize(const std::vector& allAtoms, std::vect model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; break; case Options::kStaticExecutable: - model = LTO_CODEGEN_PIC_MODEL_STATIC; + // darwin x86_64 "static" code model is really dynamic code model + if ( fArchitecture == CPU_TYPE_X86_64 ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_STATIC; break; } if ( ::lto_codegen_set_pic_model(generator, model) ) throwf("could not create set codegen model: %s", lto_get_error_message()); + // if requested, save off merged bitcode file + if ( saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + +#if LTO_API_VERSION >= 3 + // find assembler next to linker + char path[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "as"); + struct stat statInfo; + if ( stat(path, &statInfo) == 0 ) + ::lto_codegen_set_assembler_path(generator, path); + } + } +#endif // run code generator size_t machOFileLen; const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); @@ -573,7 +591,12 @@ bool Reader::optimize(const std::vector& allAtoms, std::vect ::write(fd, machOFile, machOFileLen); ::close(fd); } - } + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } // parse generated mach-o file into a MachOReader ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal); @@ -685,9 +708,9 @@ ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32 }; // namespace lto -extern void printLTOVersion(Options &opts); +extern void printLTOVersion(Options& opts); -void printLTOVersion(Options &opts) { +void printLTOVersion(Options& opts) { const char* vers = lto_get_version(); if ( vers != NULL ) fprintf(stderr, "%s\n", vers); diff --git a/ld64/src/ld/MachOReaderDylib.hpp b/ld64/src/ld/MachOReaderDylib.hpp index 13faf3d..8aea6ef 100644 --- a/ld64/src/ld/MachOReaderDylib.hpp +++ b/ld64/src/ld/MachOReaderDylib.hpp @@ -780,13 +780,18 @@ void Reader::processIndirectLibraries(DylibHander* handler) //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); ((Reader*)child)->setImplicitlyLinked(); } - else + else if ( child->explicitlyLinked() || child->implicitlyLinked() ) { + //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } + else { fReExportedChildren.push_back(child); + //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + } } else { // add all child's symbols to me fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); + //fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); } } else if ( !fExplictReExportFound ) { diff --git a/ld64/src/ld/MachOReaderRelocatable.hpp b/ld64/src/ld/MachOReaderRelocatable.hpp index ef8cfb2..e6a182c 100644 --- a/ld64/src/ld/MachOReaderRelocatable.hpp +++ b/ld64/src/ld/MachOReaderRelocatable.hpp @@ -296,6 +296,7 @@ class BaseAtom : public ObjectFile::Atom virtual ObjectFile::UnwindInfo::iterator beginUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[0] : NULL; } virtual ObjectFile::UnwindInfo::iterator endUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[1] : NULL; } virtual ObjectFile::Reference* getLSDA(); + virtual ObjectFile::Reference* getFDE(); virtual Atom* getPersonalityPointer(); virtual void setCompactUnwindEncoding(uint64_t ehAtomAddress); @@ -320,6 +321,19 @@ ObjectFile::Reference* BaseAtom::getLSDA() return NULL; } +ObjectFile::Reference* BaseAtom::getFDE() +{ + const uint8_t groupKind = this->getLSDAReferenceKind(); + const std::vector& refs = this->getReferences(); + for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { + ObjectFile::Reference* ref = *it; + if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kCFIType) ) { + return ref; + } + } + return NULL; +} + ObjectFile::Atom* BaseAtom::getPersonalityPointer() { const uint8_t personalityKind = this->getPersonalityReferenceKind(); @@ -593,6 +607,7 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const fAlignment.modulus = 0; } + template bool SymbolAtom::dontDeadStrip() const { @@ -923,7 +938,7 @@ class AnonymousAtom : public BaseAtom virtual void setSize(uint64_t size) { fSize = size; } virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info); virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } virtual uint64_t getObjectAddress() const { return fAddress; } virtual const void* getSectionRecord() const { return (const void*)fSection; } @@ -1043,11 +1058,38 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio if ( !fOwner.fOptions.fNoEHLabels ) fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; } + else if ( section == owner.fUTF16Section ) { + if ( fOwner.fOptions.fForFinalLinkedImage ) { + fDontDeadStrip = false; + fScope = ObjectFile::Atom::scopeLinkageUnit; + fKind = ObjectFile::Atom::kWeakDefinition; + char* name = new char[16+5*size]; + strcpy(name, "utf16-string="); + char* s = &name[13]; + const uint16_t* words = (uint16_t*)((char*)(owner.fHeader) + section->offset() + addr - section->addr()); + unsigned int wordCount = size/2; + // note, the compiler sometimes puts trailing zeros on the end of the data + if ( E::get32(words[wordCount-1]) == 0 ) + --wordCount; + bool needSeperator = false; + for(unsigned int i=0; i < wordCount; ++i) { + if ( needSeperator ) + strcpy(s++, "."); + sprintf(s, "%04X", E::get32(words[i])); + s += 4; + needSeperator = true; + } + fSynthesizedName = name; + } + else { + asprintf((char**)&fSynthesizedName, "lutf16-0x%X", addr); + } + } break; case S_CSTRING_LITERALS: { const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - if ( strcmp(fSection->sectname(), "__cstring") == 0 ) + if ( (strcmp(fSection->sectname(), "__cstring") == 0) && (strcmp(section->segname(), "__TEXT") == 0) ) asprintf((char**)&fSynthesizedName, "cstring=%s", str); else asprintf((char**)&fSynthesizedName, "cstring%s%s=%s", fSection->segname(), fSection->sectname(), str); @@ -1135,6 +1177,16 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: { + // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when + // generating the new compressed LINKEDIT format + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) && fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { + macho_section

* dummySection = new macho_section

(*fSection); + dummySection->set_segname("__DATA"); + dummySection->set_sectname("__nl_symbol_ptr"); + fSection = dummySection; + fSegment = new Segment(fSection); + } + fDontDeadStrip = false; fScope = ObjectFile::Atom::scopeLinkageUnit; uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); @@ -1159,6 +1211,7 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio // add direct reference to target later, because its atom may not be constructed yet fOwner.fLocalNonLazys.push_back(this); fScope = ObjectFile::Atom::scopeTranslationUnit; + fType = ObjectFile::Atom::kNonLazyPointer; return; } else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) { @@ -1178,31 +1231,26 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio } fScope = ObjectFile::Atom::scopeTranslationUnit; fOwner.fLocalNonLazys.push_back(this); + fType = ObjectFile::Atom::kNonLazyPointer; return; } const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; char* str = new char[strlen(name)+16]; strcpy(str, name); - if ( type == S_LAZY_SYMBOL_POINTERS ) + if ( type == S_LAZY_SYMBOL_POINTERS ) { strcat(str, "$lazy_ptr"); - else + fType = ObjectFile::Atom::kLazyPointer; + } + else { strcat(str, "$non_lazy_ptr"); + fType = ObjectFile::Atom::kNonLazyPointer; + } fSynthesizedName = str; - // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when - // generating the new compressed LINKEDIT format - if ( fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { - macho_section

* dummySection = new macho_section

(*fSection); - dummySection->set_segname("__DATA"); - dummySection->set_sectname("__nl_symbol_ptr"); - fSection = dummySection; - fSegment = new Segment(fSection); - } - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) fKind = ObjectFile::Atom::kWeakDefinition; - + if ( (targetSymbol->n_type() & N_EXT) == 0 ) { // target is translation unit scoped, so add direct reference to target //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); @@ -1217,7 +1265,7 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio } break; default: - throwf("section type %d not supported with address=0x%08X", type, addr); + throwf("section type %d not supported with address=0x%08llX", type, (uint64_t)addr); } //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); } @@ -1226,6 +1274,13 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } template bool AnonymousAtom::cstringsHaveLabels() { return false; } +template +void AnonymousAtom::addLineInfo(const ObjectFile::LineInfo& info) +{ + // don't warn if line table has entries for stubs + if ( (fSection->flags() & SECTION_TYPE) != S_SYMBOL_STUBS ) + warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); +} template void AnonymousAtom::resolveName() @@ -1273,6 +1328,9 @@ void AnonymousAtom::resolveName() if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) { asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]); } + else if ( (superStr != NULL) && (strncmp(superStr, "utf16-string=", 13) == 0) ) { + asprintf((char**)&fSynthesizedName, "cfstring-utf16=%s", &superStr[13]); + } else { // compiled with -fwritable-strings or a non-ASCII string fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable @@ -1610,11 +1668,13 @@ class ObjectFileAddressSpace private: const void* mappedAddress(pint_t addr, pint_t* relocTarget=NULL); pint_t relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount); + void buildRelocatedMap(const macho_section

* sect, std::map& map); Reader& fReader; const uint8_t* fMappingStart; const macho_section

* fSectionsStart; const macho_section

* fSectionsEnd; + std::map fEHFrameOffsetToTargetMap; }; @@ -1634,26 +1694,43 @@ const void* ObjectFileAddressSpace::mappedAddress(pint_t addr, pint_t* relocT fMappingStart = (uint8_t*)fReader.fHeader; fSectionsStart = (macho_section

*)((char*)fReader.fSegment + sizeof(macho_segment_command

)); fSectionsEnd = &fSectionsStart[fReader.fSegment->nsects()]; + // find __eh_frame section and build map of relocations for performance + buildRelocatedMap(fReader.fehFrameSection, fEHFrameOffsetToTargetMap); + } + // special case lookups in __eh_frame section to be fast + const macho_section

* ehSect = fReader.fehFrameSection; + if ( (ehSect->addr() <= addr) && (addr < (ehSect->addr()+ehSect->size())) ) { + pint_t offsetOfAddrInSection = addr - ehSect->addr(); + if ( relocTarget != NULL ) { + std::map::iterator pos = fEHFrameOffsetToTargetMap.find(offsetOfAddrInSection); + if ( pos != fEHFrameOffsetToTargetMap.end() ) + *relocTarget = pos->second; + else + *relocTarget = 0; + } + return fMappingStart + ehSect->offset() + offsetOfAddrInSection; } - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { - pint_t offsetOfAddrInSection = addr - sect->addr(); - if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - const uint32_t indirectTableOffset = sect->reserved1(); - const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); - const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); - // return pointer to symbol name which this non-lazy-pointer will point to - if ( relocTarget != NULL ) - *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; - } - else { - if ( relocTarget != NULL ) - *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); + else { + for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + pint_t offsetOfAddrInSection = addr - sect->addr(); + if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { + const uint32_t indirectTableOffset = sect->reserved1(); + const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); + const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); + // return pointer to symbol name which this non-lazy-pointer will point to + if ( relocTarget != NULL ) + *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; + } + else { + if ( relocTarget != NULL ) + *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); + } + return fMappingStart + sect->offset() + offsetOfAddrInSection; } - return fMappingStart + sect->offset() + offsetOfAddrInSection; } + throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); } - throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); } @@ -1811,6 +1888,7 @@ class Reader : public ObjectFile::Reader ObjectFile::Reader::DebugInfoKind fDebugInfo; bool fHasUUID; const macho_section

* fehFrameSection; + const macho_section

* fUTF16Section; std::set fLSDAAtoms; const macho_section

* fDwarfDebugInfoSect; const macho_section

* fDwarfDebugAbbrevSect; @@ -1838,7 +1916,7 @@ template Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header

*)fileContent), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), - fDebugInfo(kDebugInfoNone), fHasUUID(false), fehFrameSection(NULL), + fDebugInfo(kDebugInfoNone), fHasUUID(false), fehFrameSection(NULL), fUTF16Section(NULL), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), @@ -1916,8 +1994,12 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { if ( (strcmp(sect->sectname(), "__eh_frame") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { fehFrameSection = sect; - if ( libunwind::CFI_Parser >::getCFIs(fObjectAddressSpace, sect->addr(), sect->size(), - fCIEInfos, fFDEInfos) ) { + const char* msg = libunwind::CFI_Parser >::getCFIs(fObjectAddressSpace, sect->addr(), + sect->size(), fFDEInfos, fCIEInfos); + if ( msg != NULL ) { + throwf("malformed __eh_frame section: %s", msg); + } + else { //fprintf(stderr, "%lu CIEs, %lu FDEs\n", fCIEInfos.size(), fFDEInfos.size()); // add anonymous atoms for each CIE for (typename std::vector::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { @@ -1939,8 +2021,29 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } } - else { - throw "malformed __eh_frame section"; + } + else if ( (strcmp(sect->sectname(), "__ustring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { + // if there is a __ustring section parse it into AnonymousAtoms based on labels + fUTF16Section = sect; + std::vector utf16Addreses; + for (int i=fSymbolCount-1; i >= 0 ; --i) { + const macho_nlist

& sym = fSymbols[i]; + if ( (sym.n_type() & N_STAB) == 0 ) { + uint8_t type = (sym.n_type() & N_TYPE); + if ( type == N_SECT ) { + if ( &fSectionsStart[sym.n_sect()-1] == fUTF16Section ) { + utf16Addreses.push_back(sym.n_value()); + } + } + } + } + utf16Addreses.push_back(fUTF16Section->addr()+fUTF16Section->size()); + std::sort(utf16Addreses.begin(), utf16Addreses.end()); + for(int i=utf16Addreses.size()-2; i >=0 ; --i) { + pint_t size = utf16Addreses[i+1] - utf16Addreses[i]; + AnonymousAtom* strAtom = new AnonymousAtom(*this, fUTF16Section, utf16Addreses[i], size); + fAtoms.push_back(strAtom); + fAddrToAtom[utf16Addreses[i]] = strAtom; } } } @@ -1972,6 +2075,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, else if ( section == fehFrameSection ) { // ignore labels in __eh_frame section } + else if ( section == fUTF16Section ) { + // ignore labels in __ustring section + } else { // ignore labels for atoms in other sections switch ( section->flags() & SECTION_TYPE ) { @@ -2825,6 +2931,44 @@ ObjectFile::Atom* Reader::getFunctionAtomFromLSDAAddress(pint_t addr) } +template <> +void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) +{ + // mach-o x86_64 is different, the content of a section with a relocation is the addend + const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fReader.fHeader) + sect->reloff()); + const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + std::map::iterator pos; + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + pos = map.find(reloc->r_address()); + if ( pos != map.end() ) + pos->second += fReader.fSymbols[reloc->r_symbolnum()].n_value(); + else + map[reloc->r_address()] = fReader.fSymbols[reloc->r_symbolnum()].n_value(); + break; + case X86_64_RELOC_SUBTRACTOR: + map[reloc->r_address()] = -fReader.fSymbols[reloc->r_symbolnum()].n_value(); + break; + case X86_64_RELOC_GOT: + // there is no good address to return here. + // GOT slots are synthsized by the linker + // this is used for the reference to the personality function in CIEs + map[reloc->r_address()] = 0; + break; + default: + fprintf(stderr, "ObjectFileAddressSpace::buildRelocatedMap() unexpected relocation at r_address=0x%08X\n", reloc->r_address()); + break; + } + } +} + +template +void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) +{ + // in all architectures except x86_64, the section contents are already fixed up to point + // to content in the same object file. +} template <> uint64_t ObjectFileAddressSpace::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) @@ -3100,14 +3244,15 @@ uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; + char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); - if ( (result & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); + warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); } return result; } @@ -3117,14 +3262,15 @@ uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; + char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); - if ( (result & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); + warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); } return result; } @@ -3182,12 +3328,14 @@ uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; + char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); - if ( (result & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else + if ( fOwner.fOptions.fWarnCompactUnwind ) warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); } return result; @@ -3198,12 +3346,14 @@ uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) { pint_t lsda; pint_t personality; + char warningBuffer[1024]; uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality); - if ( (result & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) { + fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); + if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { //if ( fOwner.fOptions.fForDyld ) // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); //else + if ( fOwner.fOptions.fWarnCompactUnwind ) warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); } return result; @@ -3572,8 +3722,8 @@ AtomAndOffset Reader::findAtomAndOffsetForSection(pint_t addr, unsigned int e } } } - // no atom found - return AtomAndOffset(NULL); + // no atom found that matched section, fall back to one orginally found + return ao; } template @@ -3611,9 +3761,10 @@ AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) return result; } // getting here means we have a scattered relocation to an address without a label - // we should never get here... - // one case we do get here is because sometimes the compiler generates non-lazy pointers in the __data section - return findAtomAndOffset(realAddr); + // so, find the atom that contains the baseAddr, and offset from that to the readAddr + AtomAndOffset result = findAtomAndOffset(baseAddr); + result.offset += (realAddr-baseAddr); + return result; } @@ -4665,7 +4816,10 @@ bool Reader::addRelocReference(const macho_section* sect, con kind = x86_64::kPointer32; break; case 3: - kind = x86_64::kPointer; + if ( reloc->r_extern() && isWeakImportSymbol(targetSymbol) ) + kind = x86_64::kPointerWeakImport; + else + kind = x86_64::kPointer; break; } dstAddr = E::get64(*((uint64_t*)fixUpPtr)); @@ -5073,7 +5227,7 @@ bool Reader::addRelocReference(const macho_section* sect, break; case ARM_THUMB_32BIT_BRANCH: - // work around for + // ignore old unnecessary relocs break; default: @@ -5277,7 +5431,7 @@ const char* Reference::getDescription() const sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); break; case x86::kImageOffset32: - sprintf(temp, "offset 0x%04X, 32bit offset of ", fFixUpOffsetInSrc); + sprintf(temp, "offset 0x%04X, 32-bit offset of ", fFixUpOffsetInSrc); break; case x86::kPointerDiff24: sprintf(temp, "offset 0x%04X, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", @@ -5285,6 +5439,9 @@ const char* Reference::getDescription() const this->getFromTargetDisplayName(), fFromTarget.offset ); return temp; break; + case x86::kSectionOffset24: + sprintf(temp, "offset 0x%04X, 24-bit section offset of ", fFixUpOffsetInSrc); + break; case x86::kDtraceProbe: sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); break; @@ -5609,6 +5766,9 @@ const char* Reference::getDescription() const case x86_64::kImageOffset32: sprintf(temp, "offset 0x%04llX, 32bit offset of ", fFixUpOffsetInSrc); break; + case x86_64::kSectionOffset24: + sprintf(temp, "offset 0x%04llX, 24-bit section offset of ", fFixUpOffsetInSrc); + break; case x86_64::kDtraceProbe: sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); break; @@ -5671,6 +5831,16 @@ const char* Reference::getDescription() const fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); return temp; } + case arm::kPointerDiff12: + { + // by-name references have quoted names + const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; + const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; + sprintf(temp, "offset 0x%04X, 12-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", + fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, + fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); + return temp; + } case arm::kReadOnlyPointer: sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc); break; diff --git a/ld64/src/ld/MachOWriterExecutable.hpp b/ld64/src/ld/MachOWriterExecutable.hpp index 9e02efa..2ed0feb 100644 --- a/ld64/src/ld/MachOWriterExecutable.hpp +++ b/ld64/src/ld/MachOWriterExecutable.hpp @@ -107,6 +107,7 @@ template class FastStubHelperHelperAtom; template class LazyPointerAtom; template class NonLazyPointerAtom; template class DylibLoadCommandsAtom; +template class BranchIslandAtom; // SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes @@ -286,15 +287,18 @@ class Writer : public ExecutableFile::Writer virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses); virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); + virtual void addSynthesizedAtoms(const std::vector& existingAtoms, + class ObjectFile::Atom* dyldClassicHelperAtom, + class ObjectFile::Atom* dyldCompressedHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool biggerThanTwoGigs, + uint32_t dylibSymbolCount, + std::vector& newAtoms); virtual uint64_t write(std::vector& atoms, std::vector& stabs, class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, std::set& atomsThatOverrideWeak, bool hasExternalWeakDefinitions); @@ -305,15 +309,19 @@ class Writer : public ExecutableFile::Writer enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; void assignFileOffsets(); - void synthesizeStubs(); - void synthesizeKextGOT(); + void synthesizeStubs(const std::vector& existingAtoms, + std::vector& newAtoms); + void synthesizeKextGOT(const std::vector& existingAtoms, + std::vector& newAtoms); void createSplitSegContent(); void synthesizeUnwindInfoTable(); void insertDummyStubs(); void partitionIntoSections(); bool addBranchIslands(); - bool addPPCBranchIslands(); - bool isBranch24Reference(uint8_t kind); + bool createBranchIslands(); + bool isBranchThatMightNeedIsland(uint8_t kind); + uint32_t textSizeWhenMightNeedBranchIslands(); + uint32_t maxDistanceBetweenIslands(); void adjustLoadCommandsAndPadding(); void createDynamicLinkerCommand(); void createDylibCommands(); @@ -422,6 +430,7 @@ class Writer : public ExecutableFile::Writer friend class LazyPointerAtom; friend class NonLazyPointerAtom; friend class DylibLoadCommandsAtom; + friend class BranchIslandAtom; const char* fFilePath; Options& fOptions; @@ -453,6 +462,7 @@ class Writer : public ExecutableFile::Writer std::vector fLocalSymbolAtoms; std::vector > fLocalExtraLabels; std::vector > fGlobalExtraLabels; + std::map fAtomToSymbolIndex; class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; class CompressedRebaseInfoLinkEditAtom* fCompressedRebaseInfoAtom; class CompressedBindingInfoLinkEditAtom* fCompressedBindingInfoAtom; @@ -492,6 +502,7 @@ class Writer : public ExecutableFile::Writer uint32_t fSymbolTableImportCount; uint32_t fSymbolTableImportStartIndex; uint32_t fLargestAtomSize; + uint32_t fDylibSymbolCountUpperBound; bool fEmitVirtualSections; bool fHasWeakExports; bool fReferencesWeakImports; @@ -500,6 +511,7 @@ class Writer : public ExecutableFile::Writer bool fNoReExportedDylibs; bool fBiggerThanTwoGigs; bool fSlideable; + bool fHasThumbBranches; std::map fWeakImportMap; std::set fDylibReadersWithNonWeakImports; std::set fDylibReadersWithWeakImports; @@ -972,17 +984,20 @@ class UnwindInfoAtom : public WriterAtom virtual void copyRawContent(uint8_t buffer[]) const; void addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* lsda, ObjectFile::Atom* personalityPointer); + ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsda, + ObjectFile::Atom* personalityPointer); void generate(); private: using WriterAtom::fWriter; typedef typename A::P P; - struct Info { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; + struct Info { ObjectFile::Atom* func; ObjectFile::Atom* fde; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; struct LSDAEntry { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; }; - struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; }; + struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fde; }; struct CompressedFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fromFunc; }; + struct CompressedEncodingFixUp { uint8_t* contentPointer; ObjectFile::Atom* fde; }; + bool encodingMeansUseDwarf(compact_unwind_encoding_t encoding); void compressDuplicates(std::vector& uniqueInfos); void findCommonEncoding(const std::vector& uniqueInfos, std::map& commonEncodings); void makeLsdaIndex(const std::vector& uniqueInfos, std::map& lsdaIndexOffsetMap); @@ -1005,6 +1020,7 @@ class UnwindInfoAtom : public WriterAtom std::vector fLSDAIndex; std::vector fRegFixUps; std::vector fCompressedFixUps; + std::vector fCompressedEncodingFixUps; std::vector fReferences; }; @@ -1451,17 +1467,25 @@ template class BranchIslandAtom : public WriterAtom { public: - BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset); + BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, + ObjectFile::Atom& finalTarget, uint32_t finalTargetOffset); virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } virtual uint64_t getSize() const; + virtual bool isThumb() const { return (fIslandKind == kBranchIslandToThumb2); } + virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kBranchIsland; } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } virtual const char* getSectionName() const { return "__text"; } virtual void copyRawContent(uint8_t buffer[]) const; + uint64_t getFinalTargetAdress() const { return fFinalTarget.getAddress() + fFinalTargetOffset; } private: using WriterAtom::fWriter; + enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1 }; const char* fName; ObjectFile::Atom& fTarget; - uint32_t fTargetOffset; + ObjectFile::Atom& fFinalTarget; + uint32_t fFinalTargetOffset; + IslandKind fIslandKind; }; template @@ -1471,20 +1495,26 @@ class StubAtom : public WriterAtom StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib); virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStub; } virtual uint64_t getSize() const; virtual ObjectFile::Alignment getAlignment() const; virtual const char* getSectionName() const { return "__symbol_stub1"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; ObjectFile::Atom* getTarget() { return &fTarget; } + virtual uint32_t getOrdinal() const { return fSortingOrdinal; } + void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } private: static const char* stubName(const char* importName); - bool pic() const { return fWriter.fSlideable; } + friend class LazyPointerAtom; using WriterAtom::fWriter; + enum StubKind { kStubPIC, kStubNoPIC, kStubShort, kJumpTable }; const char* fName; ObjectFile::Atom& fTarget; std::vector fReferences; bool fForLazyDylib; + StubKind fKind; + uint32_t fSortingOrdinal; }; @@ -1496,11 +1526,13 @@ class FastStubHelperHelperAtom : public WriterAtom virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } virtual uint64_t getSize() const; virtual const char* getSectionName() const { return "__stub_helper"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual uint32_t getOrdinal() const { return 0; } protected: using WriterAtom::fWriter; std::vector fReferences; @@ -1514,11 +1546,13 @@ class HybridStubHelperHelperAtom : public WriterAtom virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } virtual uint64_t getSize() const; virtual const char* getSectionName() const { return "__stub_helper"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual uint32_t getOrdinal() const { return 0; } protected: using WriterAtom::fWriter; std::vector fReferences; @@ -1537,10 +1571,12 @@ class StubHelperAtom : public WriterAtom virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } virtual const char* getSectionName() const { return "__stub_helper"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } ObjectFile::Atom* getTarget() { return &fTarget; } virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } + virtual uint32_t getOrdinal() const { return 1; } protected: static const char* stubName(const char* importName); using WriterAtom::fWriter; @@ -1596,14 +1632,17 @@ class LazyPointerAtom : public WriterAtom LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib); virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + virtual ObjectFile::Atom::ContentType getContentType() const { return fForLazyDylib ? ObjectFile::Atom::kLazyDylibPointer : ObjectFile::Atom::kLazyPointer; } virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const { return fForLazyDylib ? "__ld_symbol_ptr" : "__la_symbol_ptr"; } + virtual const char* getSectionName() const; virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; ObjectFile::Atom* getTarget() { return &fExternalTarget; } void setLazyBindingInfoOffset(uint32_t off) { fLazyBindingOffset = off; } uint32_t getLazyBindingInfoOffset() { return fLazyBindingOffset; } + virtual uint32_t getOrdinal() const { return fSortingOrdinal; } + void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } private: using WriterAtom::fWriter; static const char* lazyPointerName(const char* importName); @@ -1612,7 +1651,9 @@ class LazyPointerAtom : public WriterAtom ObjectFile::Atom& fExternalTarget; std::vector fReferences; bool fForLazyDylib; + bool fCloseStub; uint32_t fLazyBindingOffset; + uint32_t fSortingOrdinal; }; @@ -1625,17 +1666,21 @@ class NonLazyPointerAtom : public WriterAtom NonLazyPointerAtom(Writer& writer); virtual const char* getName() const { return fName; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } + virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kNonLazyPointer; } virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } virtual const char* getSectionName() const { return (fWriter.fOptions.outputKind() == Options::kKextBundle) ? "__got" : "__nl_symbol_ptr"; } virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } virtual void copyRawContent(uint8_t buffer[]) const; ObjectFile::Atom* getTarget() { return fTarget; } + virtual uint32_t getOrdinal() const { return fSortingOrdinal; } + void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } private: using WriterAtom::fWriter; static const char* nonlazyPointerName(const char* importName); const char* fName; ObjectFile::Atom* fTarget; std::vector fReferences; + uint32_t fSortingOrdinal; }; @@ -1953,7 +1998,7 @@ void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const buffer[2] = 0x00; buffer[3] = 0x00; buffer[4] = 0x00; - buffer[5] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[5] = 0xFF; // jmp *_fast_lazy_bind buffer[6] = 0x25; buffer[7] = 0x00; buffer[8] = 0x00; @@ -1963,6 +2008,69 @@ void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const } +template <> +FastStubHelperHelperAtom::FastStubHelperHelperAtom(Writer& writer) + : WriterAtom(writer, Segment::fgTextSegment) +{ + fReferences.push_back(new WriterReference(28, arm::kPointerDiff, new NonLazyPointerAtom(writer), 0, this, 16)); + fReferences.push_back(new WriterReference(32, arm::kPointerDiff, writer.fFastStubGOTAtom, 0, this, 28)); +} + +template <> +uint64_t FastStubHelperHelperAtom::getSize() const +{ + return 36; +} + +template <> +void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + // push lazy-info-offset + OSWriteLittleInt32(&buffer[ 0], 0, 0xe52dc004); // str ip, [sp, #-4]! + // push address of dyld_mageLoaderCache + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59fc010); // ldr ip, L1 + OSWriteLittleInt32(&buffer[ 8], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[12], 0, 0xe52dc004); // str ip, [sp, #-4]! + // jump through _fast_lazy_bind + OSWriteLittleInt32(&buffer[16], 0, 0xe59fc008); // ldr ip, L2 + OSWriteLittleInt32(&buffer[20], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[24], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[28], 0, 0x00000000); // L1: .long fFastStubGOTAtom - (helperhelper+16) + OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // L2: .long _fast_lazy_bind - (helperhelper+28) +} + +template <> +ObjectFile::Alignment StubHelperAtom::getAlignment() const { return ObjectFile::Alignment(2); } + +template <> +FastStubHelperAtom::FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, + class LazyPointerAtom& lazyPointer, bool forLazyDylib) + : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) +{ + if ( fgHelperHelperAtom == NULL ) { + fgHelperHelperAtom = new FastStubHelperHelperAtom::FastStubHelperHelperAtom(fWriter); + fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); + } + fReferences.push_back(new WriterReference(4, arm::kBranch24, fgHelperHelperAtom)); +} + +template <> +uint64_t FastStubHelperAtom::getSize() const +{ + return 12; +} + +template <> +void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const +{ + OSWriteLittleInt32(&buffer[0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[4], 0, 0xea000000); // b _helperhelper + // the lazy binding info is created later than this helper atom, so there + // is no Reference to update. Instead we blast the offset here. + OSWriteLittleInt32(&buffer[8], 0, fLazyPointerAtom.getLazyBindingInfoOffset()); +} + + template <> HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) @@ -2151,13 +2259,22 @@ void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const memcpy(&buffer[1], &offset, 4); } - +template +const char* LazyPointerAtom::getSectionName() const +{ + if ( fCloseStub ) + return "__lazy_symbol"; + else if ( fForLazyDylib ) + return "__ld_symbol_ptr"; + else + return "__la_symbol_ptr"; +} // specialize lazy pointer for x86_64 to initially pointer to stub helper template <> LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fLazyBindingOffset(0) + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) { if ( forLazyDylib ) writer.fAllSynthesizedLazyDylibPointers.push_back(this); @@ -2190,7 +2307,7 @@ LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Ato template <> LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) { if ( forLazyDylib ) writer.fAllSynthesizedLazyDylibPointers.push_back(this); @@ -2219,10 +2336,45 @@ LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& tar fReferences.push_back(new WriterReference(0, x86::kPointer, helper)); } +// specialize lazy pointer for arm to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) +{ + if ( forLazyDylib ) + writer.fAllSynthesizedLazyDylibPointers.push_back(this); + else + writer.fAllSynthesizedLazyPointers.push_back(this); + + // The one instruction stubs must be close to the lazy pointers + if ( stub.fKind == StubAtom::kStubShort ) + fCloseStub = true; + + ObjectFile::Atom* helper; + if ( forLazyDylib ) { + if ( writer.fDyldLazyDylibHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + helper = writer.fDyldLazyDylibHelper; + } + else if ( writer.fOptions.makeCompressedDyldInfo() ) { + if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) + helper = ⌖ + else + helper = new FastStubHelperAtom(writer, target, *this, forLazyDylib); + } + else { + if ( writer.fDyldClassicHelperAtom == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + helper = writer.fDyldClassicHelperAtom; + } + fReferences.push_back(new WriterReference(0, arm::kPointer, helper)); +} + template LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib) + fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) { if ( forLazyDylib ) writer.fAllSynthesizedLazyDylibPointers.push_back(this); @@ -2288,20 +2440,6 @@ void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const -template <> -bool StubAtom::pic() const -{ - // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB. - // Usually that only happens if page zero is very large - return ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ); -} - - -template <> -bool StubAtom::pic() const -{ - return fWriter.fSlideable; -} template <> ObjectFile::Alignment StubAtom::getAlignment() const @@ -2346,7 +2484,8 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forL lp = new LazyPointerAtom(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); } } - if ( pic() ) { + fKind = ( fWriter.fSlideable ? kStubPIC : kStubNoPIC ); + if ( fKind == kStubPIC ) { // picbase is 8 bytes into atom fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, this, 8)); fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, this, 8)); @@ -2375,7 +2514,11 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; lp = new LazyPointerAtom(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); } - if ( pic() ) { + if ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ) + fKind = kStubPIC; + else + fKind = kStubNoPIC; + if ( fKind == kStubPIC ) { // picbase is 8 bytes into atom fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, this, 8)); fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, this, 8)); @@ -2392,14 +2535,16 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forL fName(NULL), fTarget(target), fForLazyDylib(forLazyDylib) { if ( writer.fOptions.makeCompressedDyldInfo() || forLazyDylib ) { + fKind = kStubNoPIC; fName = stubName(target.getName()); LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); writer.fAllSynthesizedStubs.push_back(this); } else { + fKind = kJumpTable; if ( &target == NULL ) - fName = "cache-line-crossing-stub"; + asprintf((char**)&fName, "cache-line-crossing-stub %p", this); else { fName = stubName(target.getName()); writer.fAllSynthesizedStubs.push_back(this); @@ -2423,34 +2568,38 @@ StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forL : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) { writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp; - if ( fWriter.fOptions.prebind() && !forLazyDylib ) { - // for prebound arm, lazy pointer starts out pointing to target symbol's address - // if target is a weak definition within this linkage unit or zero if in some dylib - lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + if ( (writer.fDylibSymbolCountUpperBound < 900) + && writer.fOptions.makeCompressedDyldInfo() + && (writer.fOptions.outputKind() != Options::kDynamicLibrary) + && !forLazyDylib ) { + // dylibs might have __TEXT and __DATA pulled apart to live in shared region + // if > 1000 stubs, the displacement to the lazy pointer my be > 12 bits. + fKind = kStubShort; + } + else if ( fWriter.fSlideable ) { + fKind = kStubPIC; } else { - // for non-prebound arm, lazy pointer starts out pointing to dyld_stub_binding_helper glue code - ObjectFile::Atom* helper; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - helper = writer.fDyldLazyDylibHelper; - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldClassicHelperAtom; - } - lp = new LazyPointerAtom(writer, *helper, *this, forLazyDylib); + fKind = kStubNoPIC; + } + LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); + switch ( fKind ) { + case kStubPIC: + fReferences.push_back(new WriterReference(12, arm::kPointerDiff, lp, 0, this, 12)); + break; + case kStubNoPIC: + fReferences.push_back(new WriterReference(8, arm::kReadOnlyPointer, lp)); + break; + case kStubShort: + fReferences.push_back(new WriterReference(0, arm::kPointerDiff12, lp, 0, this, 8)); + break; + default: + throw "internal error"; } - if ( pic() ) - fReferences.push_back(new WriterReference(12, arm::kPointerDiff, lp, 0, this, 12)); - else - fReferences.push_back(new WriterReference(8, arm::kPointer, lp)); } + + template const char* StubAtom::stubName(const char* name) { @@ -2462,29 +2611,43 @@ const char* StubAtom::stubName(const char* name) template <> uint64_t StubAtom::getSize() const { - return ( pic() ? 32 : 16 ); + + return ( (fKind == kStubPIC) ? 32 : 16 ); } template <> uint64_t StubAtom::getSize() const { - return ( pic() ? 32 : 16 ); + return ( (fKind == kStubPIC) ? 32 : 16 ); } template <> uint64_t StubAtom::getSize() const { - return ( pic() ? 16 : 12 ); + switch ( fKind ) { + case kStubPIC: + return 16; + case kStubNoPIC: + return 12; + case kStubShort: + return 4; + default: + throw "internal error"; + } } template <> uint64_t StubAtom::getSize() const { - if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) - return 6; - else - return 5; + switch ( fKind ) { + case kStubNoPIC: + return 6; + case kJumpTable: + return 5; + default: + throw "internal error"; + } } template <> @@ -2496,16 +2659,20 @@ uint64_t StubAtom::getSize() const template <> ObjectFile::Alignment StubAtom::getAlignment() const { - if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) - return 1; - else - return 0; // special case x86 self-modifying stubs to be byte aligned + switch ( fKind ) { + case kStubNoPIC: + return 1; + case kJumpTable: + return 0; // special case x86 self-modifying stubs to be byte aligned + default: + throw "internal error"; + } } template <> void StubAtom::copyRawContent(uint8_t buffer[]) const { - if ( pic() ) { + if ( fKind == kStubPIC ) { OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 @@ -2526,7 +2693,7 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const template <> void StubAtom::copyRawContent(uint8_t buffer[]) const { - if ( pic() ) { + if ( fKind == kStubPIC ) { OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 @@ -2547,31 +2714,35 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const template <> void StubAtom::copyRawContent(uint8_t buffer[]) const { - if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) { - buffer[0] = 0xFF; // jmp *foo$lazy_pointer - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - } - else { - if ( fWriter.fOptions.prebind() ) { - uint32_t address = this->getAddress(); - int32_t rel32 = 0 - (address+5); - buffer[0] = 0xE9; - buffer[1] = rel32 & 0xFF; - buffer[2] = (rel32 >> 8) & 0xFF; - buffer[3] = (rel32 >> 16) & 0xFF; - buffer[4] = (rel32 >> 24) & 0xFF; - } - else { - buffer[0] = 0xF4; - buffer[1] = 0xF4; - buffer[2] = 0xF4; - buffer[3] = 0xF4; - buffer[4] = 0xF4; - } + switch ( fKind ) { + case kStubNoPIC: + buffer[0] = 0xFF; // jmp *foo$lazy_pointer + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + break; + case kJumpTable: + if ( fWriter.fOptions.prebind() ) { + uint32_t address = this->getAddress(); + int32_t rel32 = 0 - (address+5); + buffer[0] = 0xE9; + buffer[1] = rel32 & 0xFF; + buffer[2] = (rel32 >> 8) & 0xFF; + buffer[3] = (rel32 >> 16) & 0xFF; + buffer[4] = (rel32 >> 24) & 0xFF; + } + else { + buffer[0] = 0xF4; + buffer[1] = 0xF4; + buffer[2] = 0xF4; + buffer[3] = 0xF4; + buffer[4] = 0xF4; + } + break; + default: + throw "internal error"; } } @@ -2589,16 +2760,23 @@ void StubAtom::copyRawContent(uint8_t buffer[]) const template <> void StubAtom::copyRawContent(uint8_t buffer[]) const { - if ( pic() ) { - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 - OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) - } - else { - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] - OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + switch ( fKind ) { + case kStubPIC: + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + break; + case kStubNoPIC: + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + break; + case kStubShort: + OSWriteLittleInt32(&buffer[ 0], 0, 0xE59FF000);// ldr pc, [pc, #foo$lazy_ptr] + break; + default: + throw "internal error"; } } @@ -2612,28 +2790,41 @@ ObjectFile::Alignment StubAtom::getAlignment() const template <> const char* StubAtom::getSectionName() const { - return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); + return ( (fKind == kStubPIC) ? "__picsymbolstub1" : "__symbol_stub1"); } template <> const char* StubAtom::getSectionName() const { - return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); + return ( (fKind == kStubPIC) ? "__picsymbolstub1" : "__symbol_stub1"); } template <> const char* StubAtom::getSectionName() const { - return ( pic() ? "__picsymbolstub4" : "__symbol_stub4"); + switch ( fKind ) { + case kStubPIC: + return "__picsymbolstub4"; + case kStubNoPIC: + return "__symbol_stub4"; + case kStubShort: + return "__symbolstub1"; + default: + throw "internal error"; + } } template <> const char* StubAtom::getSectionName() const { - if ( fWriter.fOptions.makeCompressedDyldInfo() || fForLazyDylib ) - return "__symbol_stub"; - else - return "__jump_table"; + switch ( fKind ) { + case kStubNoPIC: + return "__symbol_stub"; + case kJumpTable: + return "__jump_table"; + default: + throw "internal error"; + } } @@ -2678,7 +2869,7 @@ Writer::Writer(const char* path, Options& options, std::vector::Writer(const char* path, Options& options, std::vector::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses)); } +template +void Writer::addSynthesizedAtoms(const std::vector& existingAtoms, + class ObjectFile::Atom* dyldClassicHelperAtom, + class ObjectFile::Atom* dyldCompressedHelperAtom, + class ObjectFile::Atom* dyldLazyDylibHelperAtom, + bool biggerThanTwoGigs, + uint32_t dylibSymbolCount, + std::vector& newAtoms) +{ + fDyldClassicHelperAtom = dyldClassicHelperAtom; + fDyldCompressedHelperAtom = dyldCompressedHelperAtom; + fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; + fBiggerThanTwoGigs = biggerThanTwoGigs; + fDylibSymbolCountUpperBound = dylibSymbolCount; + + // create inter-library stubs + synthesizeStubs(existingAtoms, newAtoms); +} + template uint64_t Writer::write(std::vector& atoms, std::vector& stabs, class ObjectFile::Atom* entryPointAtom, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - bool biggerThanTwoGigs, std::set& atomsThatOverrideWeak, bool hasExternalWeakDefinitions) { fAllAtoms = &atoms; fStabs = &stabs; fEntryPoint = entryPointAtom; - fDyldClassicHelperAtom = dyldClassicHelperAtom; - fDyldCompressedHelperAtom = dyldCompressedHelperAtom; - fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; fCanScatter = canScatter; fCpuConstraint = cpuConstraint; - fBiggerThanTwoGigs = biggerThanTwoGigs; fHasWeakExports = hasExternalWeakDefinitions; // dyld needs to search this image as if it had weak exports fRegularDefAtomsThatOverrideADylibsWeakDef = &atomsThatOverrideWeak; @@ -3110,9 +3312,6 @@ uint64_t Writer::write(std::vector& atoms, // check for mdynamic-no-pic codegen scanForAbsoluteReferences(); - // create inter-library stubs - synthesizeStubs(); - // create table of unwind info synthesizeUnwindInfoTable(); @@ -3235,6 +3434,8 @@ void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent desc |= N_ARM_THUMB_DEF; if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) desc |= REFERENCED_DYNAMICALLY; + if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { desc |= N_WEAK_DEF; fHasWeakExports = true; @@ -3319,8 +3520,14 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr const char* symbolName = this->symbolTableName(atom); char anonName[32]; if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { - sprintf(anonName, "l%u", fAnonNameIndex++); - symbolName = anonName; + if ( stringsNeedLabelsInObjects() && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { + // don't use 'l' labels for x86_64 strings + // x86_64 obj-c runtime confused when static lib is stripped + } + else { + sprintf(anonName, "l%u", fAnonNameIndex++); + symbolName = anonName; + } } entry->set_n_strx(this->fStringsAtom->add(symbolName)); @@ -3343,6 +3550,8 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr // set n_desc uint16_t desc = 0; + if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) desc |= N_WEAK_DEF; if ( atom->isThumb() ) @@ -3481,6 +3690,27 @@ void Writer::buildSymbolTable() // set up module table if ( fModuleInfoAtom != NULL ) fModuleInfoAtom->setName(); + + // create atom to symbol index map + // imports + int i = 0; + for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { + fAtomToSymbolIndex[*it] = i + fSymbolTableImportStartIndex; + ++i; + } + // locals + i = 0; + for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { + fAtomToSymbolIndex[*it] = i + fSymbolTableLocalStartIndex; + ++i; + } + // exports + i = 0; + for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { + fAtomToSymbolIndex[*it] = i + fSymbolTableExportStartIndex; + ++i; + } + } @@ -3516,62 +3746,69 @@ void Writer::collectExportedAndImportedAndLocalAtoms() fImportedAtoms.reserve(100); fExportedAtoms.reserve(atomCount/2); fLocalSymbolAtoms.reserve(atomCount); - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - // only named atoms go in symbol table - if ( atom->getName() != NULL ) { - // put atom into correct bucket: imports, exports, locals - //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - fImportedAtoms.push_back(atom); - break; - case ObjectFile::Atom::kTentativeDefinition: - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { - fImportedAtoms.push_back(atom); - break; + + for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { + std::vector& sectionInfos = (*segit)->fSections; + for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { + std::vector& sectionAtoms = (*secit)->fAtoms; + for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { + ObjectFile::Atom* atom = *ait; + // only named atoms go in symbol table + if ( atom->getName() != NULL ) { + // put atom into correct bucket: imports, exports, locals + //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); + switch ( atom->getDefinitionKind() ) { + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + fImportedAtoms.push_back(atom); + break; + case ObjectFile::Atom::kTentativeDefinition: + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { + fImportedAtoms.push_back(atom); + break; + } + // else fall into + case ObjectFile::Atom::kWeakDefinition: + if ( stringsNeedLabelsInObjects() + && (fOptions.outputKind() == Options::kObjectFile) + && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn) + && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) + && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { + fLocalSymbolAtoms.push_back(atom); + break; + } + // else fall into + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + if ( this->shouldExport(*atom) ) + fExportedAtoms.push_back(atom); + else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) + && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) + fLocalSymbolAtoms.push_back(atom); + break; } - // else fall into - case ObjectFile::Atom::kWeakDefinition: - if ( stringsNeedLabelsInObjects() - && (fOptions.outputKind() == Options::kObjectFile) - && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn) - && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { - fLocalSymbolAtoms.push_back(atom); - break; + } + // when geneating a .o file, dtrace static probes become local labels + if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == A::kDtraceProbe ) { + // dtrace probe points to be add back into generated .o file + this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + } } - // else fall into - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - if ( this->shouldExport(*atom) ) - fExportedAtoms.push_back(atom); - else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) - && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) - fLocalSymbolAtoms.push_back(atom); - break; - } - } - // when geneating a .o file, dtrace static probes become local labels - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); } - } - } - // when linking kernel, old style dtrace static probes become global labels - else if ( fOptions.readerOptions().fForStatic ) { - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + // when linking kernel, old style dtrace static probes become global labels + else if ( fOptions.readerOptions().fForStatic ) { + std::vector& references = atom->getReferences(); + for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { + ObjectFile::Reference* ref = *rit; + if ( ref->getKind() == A::kDtraceProbe ) { + // dtrace probe points to be add back into generated .o file + this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); + } + } } } } @@ -3690,30 +3927,9 @@ void Writer::addStabs(uint32_t startIndex) template uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) { - // search imports - int i = 0; - for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableImportStartIndex; - ++i; - } - - // search locals - i = 0; - for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableLocalStartIndex; - ++i; - } - - // search exports - i = 0; - for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - if ( &atom == *it ) - return i + fSymbolTableExportStartIndex; - ++i; - } - + std::map::iterator pos = fAtomToSymbolIndex.find(&atom); + if ( pos != fAtomToSymbolIndex.end() ) + return pos->second; throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); } @@ -3918,6 +4134,9 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref case x86_64::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; + case x86_64::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; + case x86_64::kDtraceTypeReference: case x86_64::kDtraceProbe: // generates no relocs @@ -4040,6 +4259,9 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere case x86::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; + case x86::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; + case x86::kDtraceTypeReference: case x86::kDtraceProbe: // generates no relocs @@ -4170,6 +4392,9 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere fSectionRelocs.push_back(reloc1); return 1; + case arm::kPointerDiff12: + throw "internal error. no reloc for 12-bit pointer diffs"; + case arm::kDtraceTypeReference: case arm::kDtraceProbe: // generates no relocs @@ -4889,6 +5114,9 @@ bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, cons reloc.set_r_type(GENERIC_RELOC_VANILLA); fInternalRelocs.push_back(reloc); atomSection->fHasTextLocalRelocs = true; + if ( fOptions.makeCompressedDyldInfo() ) { + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset())); + } return true; } return false; @@ -5247,11 +5475,23 @@ void Writer::buildExecutableFixups() uint64_t addresss = atom->getAddress(); if ( targetRequiresWeakBinding(ref->getTarget()) ) { fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, 0)); - // if this is a non-lazy pointer to a weak definition with this linkage unit + // if this is a non-lazy pointer to a weak definition within this linkage unit // the pointer needs to initially point within linkage unit and have - // rease command to slide it. - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); + // rebase command to slide it. + if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { + // unless if this is a hybrid format, in which case the non-lazy pointer + // is zero on disk. So use a bind instead of a rebase to set initial value + if ( fOptions.makeClassicDyldInfo() ) + fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, 0)); + else + fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); + } + // if this is a non-lazy pointer to a weak definition in a dylib, + // the pointer needs to initially bind to the dylib + else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { + int ordinal = compressedOrdinalForImortedAtom(pointerTarget); + fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, pointerTarget->getName(), false, addresss, 0)); + } } else { int ordinal = compressedOrdinalForImortedAtom(pointerTarget); @@ -5320,6 +5560,16 @@ void Writer::buildExecutableFixups() uint8_t type = BIND_TYPE_POINTER; if ( targetRequiresWeakBinding(ref->getTarget()) ) { fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, addend)); + if ( fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // hybrid linkedit puts addend in data, so we need bind phase to reset pointer to local definifion + fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, addend)); + } + // if this is a pointer to a weak definition in a dylib, + // the pointer needs to initially bind to the dylib + else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { + int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); + fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, ref->getTarget().getName(), false, addresss, addend)); + } } else { int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); @@ -5847,7 +6097,7 @@ uint64_t Writer::writeAtoms() uint64_t atomSize = atom->getSize(); if ( streaming ) { if ( atomSize > fLargestAtomSize ) - throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX", + throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%llX > 0x%X", atom->getDisplayName(), atomSize, fLargestAtomSize); } else { @@ -6038,6 +6288,13 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; } } + else if ( !fOptions.makeClassicDyldInfo() + && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // when using only compressed dyld info, pointer is initially set to point directly to weak definition + if ( ref->getTarget().isThumb() ) + targetAddr |= 1; + LittleEndian::set32(*fixUp, targetAddr); + } else { // external relocation ==> pointer contains addend LittleEndian::set32(*fixUp, ref->getTargetOffset()); @@ -6078,15 +6335,25 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob case arm::kBranch24WeakImport: case arm::kBranch24: displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); + // check if this is a branch to a branch island that can be skipped + if ( ref->getTarget().getContentType() == ObjectFile::Atom::kBranchIsland ) { + uint64_t finalTargetAddress = ((BranchIslandAtom*)(&(ref->getTarget())))->getFinalTargetAdress(); + int64_t altDisplacment = finalTargetAddress - (inAtom->getAddress() + ref->getFixUpOffset()); + if ( (altDisplacment < 33554428LL) && (altDisplacment > (-33554432LL)) ) { + //fprintf(stderr, "using altDisplacment = %lld\n", altDisplacment); + // yes, we can skip the branch island + displacement = altDisplacment; + } + } // The pc added will be +8 from the pc displacement -= 8; - // fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); + //fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); // max positive displacement is 0x007FFFFF << 2 // max negative displacement is 0xFF800000 << 2 if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); + throwf("b/bl/blx out of range (%lld max is +/-32M) from 0x%08llX %s in %s to 0x%08llX %s in %s", + displacement, inAtom->getAddress(), inAtom->getDisplayName(), inAtom->getFile()->getPath(), + ref->getTarget().getAddress(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); } instruction = LittleEndian::get32(*fixUp); // Make sure we are calling arm with bl, thumb with blx @@ -6233,8 +6500,22 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob case arm::kDtraceProbe: // nothing to fix up break; - default: - throw "boom shaka laka"; + case arm::kPointerDiff12: + displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( (displacement > 4092LL) || (displacement <-4092LL) ) { + throwf("ldr 12-bit displacement out of range (%lld max +/-4096) in %s", displacement, inAtom->getDisplayName()); + } + instruction = LittleEndian::get32(*fixUp); + if ( displacement >= 0 ) { + instruction &= 0xFFFFF000; + instruction |= ((uint32_t)displacement & 0xFFF); + } + else { + instruction &= 0xFF7FF000; + instruction |= ((uint32_t)(-displacement) & 0xFFF); + } + LittleEndian::set32(*fixUp, instruction); + break; } } @@ -6466,6 +6747,8 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co case arm::kDtraceTypeReference: // nothing to fix up break; + case arm::kPointerDiff12: + throw "internal error. no reloc for 12-bit pointer diffs"; } } @@ -6508,9 +6791,9 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob break; } } - else if ( fOptions.makeCompressedDyldInfo() + else if ( !fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // lazy pointer is initially set to point directly to weak definition + // when using only compressed dyld info, pointer is initially set to point directly to weak definition LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); } else { @@ -6544,6 +6827,15 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob temp |= (displacement & 0x00FFFFFF); LittleEndian::set32(*fixUp, temp); break; + case x86::kSectionOffset24: + displacement = ref->getTarget().getSectionOffset(); + if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) + throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); + temp = LittleEndian::get32(*fixUp); + temp &= 0xFF000000; + temp |= (displacement & 0x00FFFFFF); + LittleEndian::set32(*fixUp, temp); + break; case x86::kDtraceProbeSite: // change call site to a NOP dtraceProbeSite = (uint8_t*)fixUp; @@ -6733,6 +7025,8 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co throw "internal linker error, kPointerDiff24 can't be encoded into object files"; case x86::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; + case x86::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; case x86::kDtraceProbe: case x86::kDtraceTypeReference: // nothing to fix up @@ -6762,9 +7056,9 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const if ( &ref->getTarget() != NULL ) { //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal) { - if ( fOptions.makeCompressedDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // lazy pointer is initially set to point directly to weak definition + if ( !fOptions.makeClassicDyldInfo() + && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { + // when using only compressed dyld info, pointer is initially set to point directly to weak definition LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); } else { @@ -6833,6 +7127,15 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const temp |= (displacement & 0x00FFFFFF); LittleEndian::set32(*((uint32_t*)fixUp), temp); break; + case x86_64::kSectionOffset24: + displacement = ref->getTarget().getSectionOffset(); + if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) + throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); + temp = LittleEndian::get32(*((uint32_t*)fixUp)); + temp &= 0xFF000000; + temp |= (displacement & 0x00FFFFFF); + LittleEndian::set32(*((uint32_t*)fixUp), temp); + break; case x86_64::kPCRel32GOTLoad: case x86_64::kPCRel32GOTLoadWeakImport: // if GOT entry was optimized away, change movq instruction to a leaq @@ -6894,7 +7197,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const } else { if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - fprintf(stderr, "call out of range from %s (%llX) in %s to %s (%llX) in %s\n", + fprintf(stderr, "reference out of range from %s (%llX) in %s to %s (%llX) in %s\n", inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); throw "rel32 out of range"; } @@ -7050,6 +7353,8 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, throw "internal linker error, kPointerDiff24 can't be encoded into object files"; case x86_64::kImageOffset32: throw "internal linker error, kImageOffset32 can't be encoded into object files"; + case x86_64::kSectionOffset24: + throw "internal linker error, kSectionOffset24 can't be encoded into object files"; case x86_64::kDtraceTypeReference: case x86_64::kDtraceProbe: // nothing to fix up @@ -7433,8 +7738,10 @@ bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const Object switch ( (arm::ReferenceKinds)kind ) { case arm::kBranch24: case arm::kBranch24WeakImport: + return true; case arm::kThumbBranch22: case arm::kThumbBranch22WeakImport: + fHasThumbBranches = true; return true; case arm::kNoFixUp: case arm::kFollowOn: @@ -7447,6 +7754,7 @@ bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const Object case arm::kDtraceProbeSite: case arm::kDtraceIsEnabledSite: case arm::kDtraceTypeReference: + case arm::kPointerDiff12: return false; } return false; @@ -7704,6 +8012,10 @@ void Writer::scanForAbsoluteReferences() if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { ObjectFile::Atom* atom = *it; + if ( atom->getContentType() == ObjectFile::Atom::kStub ) + continue; + if ( atom->getContentType() == ObjectFile::Atom::kStubHelper ) + continue; std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* ref = *rit; @@ -7799,11 +8111,12 @@ void Writer::insertDummyStubs() template -void Writer::synthesizeKextGOT() +void Writer::synthesizeKextGOT(const std::vector& existingAtoms, + std::vector& newAtoms) { // walk every atom and reference - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; + for (std::vector::const_iterator it=existingAtoms.begin(); it != existingAtoms.end(); it++) { + const ObjectFile::Atom* atom = *it; std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* ref = *rit; @@ -7826,6 +8139,7 @@ void Writer::synthesizeKextGOT() if ( pos == fGOTMap.end() ) { nlp = new NonLazyPointerAtom(*this, target); fGOTMap[&target] = nlp; + newAtoms.push_back(nlp); } else { nlp = pos->second; @@ -7845,36 +8159,12 @@ void Writer::synthesizeKextGOT() } } } - - // add non-lazy pointers to fAllAtoms - if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__data") == 0) ) { - // found end of __data section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - throw "can't insert non-lazy pointers, __data section not found"; - } - } - } template -void Writer::synthesizeStubs() +void Writer::synthesizeStubs(const std::vector& existingAtoms, + std::vector& newAtoms) { switch ( fOptions.outputKind() ) { case Options::kObjectFile: @@ -7883,7 +8173,7 @@ void Writer::synthesizeStubs() return; case Options::kKextBundle: // new kext need a synthesized GOT only - synthesizeKextGOT(); + synthesizeKextGOT(existingAtoms, newAtoms); return; case Options::kStaticExecutable: case Options::kDyld: @@ -7895,7 +8185,7 @@ void Writer::synthesizeStubs() } // walk every atom and reference - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { + for (std::vector::const_iterator it=existingAtoms.begin(); it != existingAtoms.end(); it++) { ObjectFile::Atom* atom = *it; std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { @@ -8032,184 +8322,35 @@ void Writer::synthesizeStubs() // sort stubs std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); - std::sort(fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end(), AtomByNameSorter()); - // add dummy self-modifying stubs (x86 only) if ( ! fOptions.makeCompressedDyldInfo() ) this->insertDummyStubs(); + // set ordinals so sorting is preserved + uint32_t sortOrder = 0; + for (typename std::vector*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) + (*it)->setSortingOrdinal(sortOrder++); + std::sort(fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end(), AtomByNameSorter()); // sort lazy pointers std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); + sortOrder = 0; + for (typename std::vector*>::iterator it=fAllSynthesizedLazyPointers.begin(); it != fAllSynthesizedLazyPointers.end(); it++) + (*it)->setSortingOrdinal(sortOrder++); std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter()); - - - // add stubs to fAllAtoms - if ( fAllSynthesizedStubs.size() != 0 ) { - std::vector textStubs; - std::vector importStubs; - for (typename std::vector*>::iterator sit=fAllSynthesizedStubs.begin(); sit != fAllSynthesizedStubs.end(); ++sit) { - ObjectFile::Atom* stubAtom = *sit; - if ( strcmp(stubAtom->getSegment().getName(), "__TEXT") == 0 ) - textStubs.push_back(stubAtom); - else - importStubs.push_back(stubAtom); - } - // any helper stubs go right after regular stubs - if ( fAllSynthesizedStubHelpers.size() != 0 ) - textStubs.insert(textStubs.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); - // insert text stubs right after __text section - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__text") == 0) ) { - // found end of __text section, insert stubs here - fAllAtoms->insert(it, textStubs.begin(), textStubs.end()); - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( importStubs.size() != 0 ) { - // insert __IMPORTS stubs right before __LINKEDIT - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - // for i386 where stubs are not in __TEXT segment - if ( ((prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__IMPORT") == 0)) - || (strcmp(atom->getSegment().getName(), "__LINKEDIT") == 0) ) { - // insert stubs at end of __IMPORT segment, or before __LINKEDIT - fAllAtoms->insert(it, importStubs.begin(), importStubs.end()); - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - } - } - - - // add non-lazy pointers to fAllAtoms - if ( fAllSynthesizedNonLazyPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - // first try to insert at end of __nl_symbol_ptr - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) { - // found end of __nl_symbol_ptr section, insert non-lazy pointers at end of it - fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - // next try to insert after __dyld section - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( strcmp(atom->getSegment().getName(), "__DATA") == 0 ) { - const char* prevSectionName = (prevAtom != NULL) ? prevAtom->getSectionName() : ""; - if ( (strcmp(prevSectionName, "__dyld") != 0) - && (strcmp(prevSectionName, "__program_vars") != 0) - && (strcmp(prevSectionName, "__mod_init_func") != 0) ) { - // found end of __dyld section, insert non-lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - inserted = true; - break; - } - } - } - prevAtom = atom; - } - if ( !inserted ) { - // might not be any __DATA sections, insert after end of __TEXT - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && (strcmp(prevAtom->getSegment().getName(), "__TEXT") == 0) && (strcmp(atom->getSegment().getName(), "__TEXT") != 0)) { - // found end of __TEXT segment, insert non-lazy pointers at end of it - fAllAtoms->insert(it, fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - } - if ( !inserted ) - throw "can't insert non-lazy pointers, __dyld section not found"; - } - } - - // add lazy dylib pointers to fAllAtoms - if ( fAllSynthesizedLazyDylibPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && - ( (strcmp(prevAtom->getSectionName(), "__dyld") == 0) - || (strcmp(prevAtom->getSectionName(), "__program_vars") == 0) - || (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) ) { - // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - throw "can't insert lazy pointers, __dyld section not found"; - } - } - - // add lazy pointers to fAllAtoms - if ( fAllSynthesizedLazyPointers.size() != 0 ) { - ObjectFile::Section* curSection = NULL; - ObjectFile::Atom* prevAtom = NULL; - bool inserted = false; - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - ObjectFile::Section* nextSection = atom->getSection(); - if ( nextSection != curSection ) { - if ( (prevAtom != NULL) && - ( (strcmp(prevAtom->getSectionName(), "__dyld") == 0) - || (strcmp(prevAtom->getSectionName(), "__program_vars") == 0) - || (strcmp(prevAtom->getSectionName(), "__nl_symbol_ptr") == 0) ) ) { - // found end of __dyld section, insert lazy pointers here - fAllAtoms->insert(it, fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); - inserted = true; - break; - } - curSection = nextSection; - } - prevAtom = atom; - } - if ( !inserted ) { - throw "can't insert lazy pointers, __dyld section not found"; - } - } + // sort non-lazy pointers + std::sort(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), AtomByNameSorter()); + sortOrder = 0; + for (typename std::vector*>::iterator it=fAllSynthesizedNonLazyPointers.begin(); it != fAllSynthesizedNonLazyPointers.end(); it++) + (*it)->setSortingOrdinal(sortOrder++); + std::sort(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), AtomByNameSorter()); + + // tell linker about all synthesized atoms + newAtoms.insert(newAtoms.end(), fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end()); + newAtoms.insert(newAtoms.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); + newAtoms.insert(newAtoms.end(), fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); + newAtoms.insert(newAtoms.end(), fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); + newAtoms.insert(newAtoms.end(), fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); } @@ -8253,12 +8394,12 @@ void Writer::synthesizeUnwindInfoTable() if ( atom->beginUnwind() == atom->endUnwind() ) { // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) - fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL); + fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL, NULL); } else { // atom has unwind for ( ObjectFile::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { - fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getLSDA(), atom->getPersonalityPointer()); + fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getFDE(), atom->getLSDA(), atom->getPersonalityPointer()); } } } @@ -8266,7 +8407,6 @@ void Writer::synthesizeUnwindInfoTable() } - template void Writer::partitionIntoSections() { @@ -8364,43 +8504,38 @@ void Writer::partitionIntoSections() fLoadCommandsSection = currentSectionInfo; fLoadCommandsSegment = currentSegmentInfo; } - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) ) - currentSectionInfo->fAllLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_sym_ptr2") == 0) ) - currentSectionInfo->fAllLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__ld_symbol_ptr") == 0) ) - currentSectionInfo->fAllLazyDylibPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) ) - currentSectionInfo->fAllNonLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) - currentSectionInfo->fAllNonLazyPointers = true; - if ( (fOptions.outputKind() == Options::kDyld) && (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__pointers") == 0) ) - currentSectionInfo->fAllNonLazyPointers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub1") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub1") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub2") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__picsymbolstub4") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub4") == 0) ) - currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { - currentSectionInfo->fAllSelfModifyingStubs = true; - currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary - } - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__stub_helper") == 0) ) - currentSectionInfo->fAllStubHelpers = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__eh_frame") == 0) ) - currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned - curSection = atom->getSection(); - if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers || currentSectionInfo->fAllLazyDylibPointers - || currentSectionInfo->fAllStubs || currentSectionInfo->fAllSelfModifyingStubs ) { + switch ( atom->getContentType() ) { + case ObjectFile::Atom::kLazyPointer: + currentSectionInfo->fAllLazyPointers = true; + fSymbolTableCommands->needDynamicTable(); + break; + case ObjectFile::Atom::kNonLazyPointer: + currentSectionInfo->fAllNonLazyPointers = true; fSymbolTableCommands->needDynamicTable(); + break; + case ObjectFile::Atom::kLazyDylibPointer: + currentSectionInfo->fAllLazyDylibPointers = true; + break; + case ObjectFile::Atom::kStubHelper: + currentSectionInfo->fAllStubHelpers = true; + break; + case ObjectFile::Atom::kCFIType: + currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned + break; + case ObjectFile::Atom::kStub: + if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { + currentSectionInfo->fAllSelfModifyingStubs = true; + currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary + } + else { + currentSectionInfo->fAllStubs = true; + } + fSymbolTableCommands->needDynamicTable(); + break; + default: + break; } + curSection = atom->getSection(); } // any non-zero fill atoms make whole section marked not-zero-fill if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) @@ -8463,13 +8598,13 @@ class TargetAndOffsetComparor template <> bool Writer::addBranchIslands() { - return this->addPPCBranchIslands(); + return this->createBranchIslands(); } template <> bool Writer::addBranchIslands() { - return this->addPPCBranchIslands(); + return this->createBranchIslands(); } template <> @@ -8489,13 +8624,11 @@ bool Writer::addBranchIslands() template <> bool Writer::addBranchIslands() { - // arm branch islands not (yet) supported - // you can instead compile with -mlong-call - return false; + return this->createBranchIslands(); } template <> -bool Writer::isBranch24Reference(uint8_t kind) +bool Writer::isBranchThatMightNeedIsland(uint8_t kind) { switch (kind) { case ppc::kBranch24: @@ -8506,7 +8639,7 @@ bool Writer::isBranch24Reference(uint8_t kind) } template <> -bool Writer::isBranch24Reference(uint8_t kind) +bool Writer::isBranchThatMightNeedIsland(uint8_t kind) { switch (kind) { case ppc64::kBranch24: @@ -8516,17 +8649,77 @@ bool Writer::isBranch24Reference(uint8_t kind) return false; } +template <> +bool Writer::isBranchThatMightNeedIsland(uint8_t kind) +{ + switch (kind) { + case arm::kBranch24: + case arm::kBranch24WeakImport: + case arm::kThumbBranch22: + case arm::kThumbBranch22WeakImport: + return true; + } + return false; +} + +template <> +uint32_t Writer::textSizeWhenMightNeedBranchIslands() +{ + return 16000000; +} + +template <> +uint32_t Writer::textSizeWhenMightNeedBranchIslands() +{ + return 16000000; +} + +template <> +uint32_t Writer::textSizeWhenMightNeedBranchIslands() +{ + if ( fHasThumbBranches == false ) + return 32000000; // ARM can branch +/- 32MB + else if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + return 16000000; // thumb2 can branch +/- 16MB + else + return 4000000; // thumb1 can branch +/- 4MB +} + +template <> +uint32_t Writer::maxDistanceBetweenIslands() +{ + return 14*1024*1024; +} + +template <> +uint32_t Writer::maxDistanceBetweenIslands() +{ + return 14*1024*1024; +} + +template <> +uint32_t Writer::maxDistanceBetweenIslands() +{ + if ( fHasThumbBranches == false ) + return 30*1024*1024; + else if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + return 14*1024*1024; + else + return 3500000; +} + + // // PowerPC can do PC relative branches as far as +/-16MB. // If a branch target is >16MB then we insert one or more // "branch islands" between the branch and its target that -// allows island hoping to the target. +// allows island hopping to the target. // // Branch Island Algorithm // // If the __TEXT segment < 16MB, then no branch islands needed // Otherwise, every 14MB into the __TEXT segment a region is -// added which can contain branch islands. Every out of range +// added which can contain branch islands. Every out-of-range // bl instruction is checked. If it crosses a region, an island // is added to that region with the same target and the bl is // adjusted to target the island instead. @@ -8535,18 +8728,18 @@ bool Writer::isBranch24Reference(uint8_t kind) // could grow the __TEXT enough that other previously in-range // bl branches could be pushed out of range. We reduce the // probability this could happen by placing the ranges every -// 15MB which means the region would have to be 1MB (256K islands) +// 14MB which means the region would have to be 2MB (512,000 islands) // before any branches could be pushed out of range. // template -bool Writer::addPPCBranchIslands() +bool Writer::createBranchIslands() { bool log = false; bool result = false; // Can only possibly need branch islands if __TEXT segment > 16M - if ( fLoadCommandsSegment->fSize > 16000000 ) { + if ( fLoadCommandsSegment->fSize > textSizeWhenMightNeedBranchIslands() ) { if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); - const uint32_t kBetweenRegions = 14*1024*1024; // place regions of islands every 14MB in __text section + const uint32_t kBetweenRegions = maxDistanceBetweenIslands(); // place regions of islands every 14MB in __text section SectionInfo* textSection = NULL; for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { if ( strcmp((*it)->fSectionName, "__text") == 0 ) { @@ -8560,7 +8753,7 @@ bool Writer::addPPCBranchIslands() AtomToIsland regionsMap[kIslandRegionsCount]; std::vector regionsIslands[kIslandRegionsCount]; unsigned int islandCount = 0; - if ( log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + if (log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); // create islands for branch references that are out of range for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { @@ -8568,68 +8761,62 @@ bool Writer::addPPCBranchIslands() std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* ref = *rit; - if ( this->isBranch24Reference(ref->getKind()) ) { + if ( this->isBranchThatMightNeedIsland(ref->getKind()) ) { ObjectFile::Atom& target = ref->getTarget(); int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); int64_t displacement = dstAddr - srcAddr; TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; - const int64_t kFifteenMegLimit = kBetweenRegions; - if ( displacement > kFifteenMegLimit ) { + const int64_t kBranchLimit = kBetweenRegions; + if ( displacement > kBranchLimit ) { // create forward branch chain ObjectFile::Atom* nextTarget = ⌖ - uint64_t nextTargetOffset = ref->getTargetOffset(); for (int i=kIslandRegionsCount-1; i >=0 ; --i) { AtomToIsland* region = ®ionsMap[i]; int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress(); if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, nextTargetOffset); + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, *finalTargetAndOffset.atom, finalTargetAndOffset.offset); island->setSection(textSection); (*region)[finalTargetAndOffset] = island; if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); regionsIslands[i].push_back(island); ++islandCount; nextTarget = island; - nextTargetOffset = 0; } else { nextTarget = pos->second; - nextTargetOffset = 0; } } } if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName()); - ref->setTarget(*nextTarget, nextTargetOffset); + ref->setTarget(*nextTarget, 0); } - else if ( displacement < (-kFifteenMegLimit) ) { + else if ( displacement < (-kBranchLimit) ) { // create back branching chain ObjectFile::Atom* prevTarget = ⌖ - uint64_t prevTargetOffset = ref->getTargetOffset(); for (int i=0; i < kIslandRegionsCount ; ++i) { AtomToIsland* region = ®ionsMap[i]; int64_t islandRegionAddr = kBetweenRegions * (i+1); if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, prevTargetOffset); + BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, *finalTargetAndOffset.atom, finalTargetAndOffset.offset); island->setSection(textSection); (*region)[finalTargetAndOffset] = island; if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); regionsIslands[i].push_back(island); ++islandCount; prevTarget = island; - prevTargetOffset = 0; } else { prevTarget = pos->second; - prevTargetOffset = 0; } } } if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); - ref->setTarget(*prevTarget, prevTargetOffset); + ref->setTarget(*prevTarget, 0); } } } @@ -8657,6 +8844,7 @@ bool Writer::addPPCBranchIslands() uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); islandAtom->setSectionOffset(sectionOffset); + if ( log ) fprintf(stderr, "assigning __text offset 0x%08llx to %s\n", sectionOffset, islandAtom->getDisplayName()); sectionOffset += islandAtom->getSize(); } ++regionIndex; @@ -8678,6 +8866,7 @@ bool Writer::addPPCBranchIslands() uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); islandAtom->setSectionOffset(sectionOffset); + if ( log ) fprintf(stderr, "assigning __text offset 0x%08llx to %s\n", sectionOffset, islandAtom->getDisplayName()); sectionOffset += islandAtom->getSize(); } } @@ -8712,6 +8901,7 @@ void Writer::adjustLoadCommandsAndPadding() offset += atomSize; fLoadCommandsSection->fSize = offset; } + const uint32_t sizeOfLoadCommandsPlusHeader = offset + sizeof(macho_header); std::vector& sectionInfos = fLoadCommandsSegment->fSections; const int sectionCount = sectionInfos.size(); @@ -8736,11 +8926,6 @@ void Writer::adjustLoadCommandsAndPadding() // mach-o MH_PRELOAD files need no padding between load commands and first section paddingSize = 0; } - else if ( fOptions.makeEncryptable() ) { - // want load commands to end on a page boundary, so __text starts on page boundary - paddingSize = 4096 - ((totalSizeOfTEXTLessHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); - fEncryptionLoadCommand->setStartEncryptionOffset(totalSizeOfTEXTLessHeaderAndLoadCommands+paddingSize); - } else { // work backwards from end of segment and lay out sections so that extra room goes to padding atom uint64_t addr = 0; @@ -8769,6 +8954,19 @@ void Writer::adjustLoadCommandsAndPadding() int extraPages = (minPad - paddingSize + fOptions.segmentAlignment() - 1)/fOptions.segmentAlignment(); paddingSize += extraPages * fOptions.segmentAlignment(); } + + if ( fOptions.makeEncryptable() ) { + // load commands must be on a separate non-encrypted page + int loadCommandsPage = (sizeOfLoadCommandsPlusHeader + minPad)/fOptions.segmentAlignment(); + int textPage = (sizeOfLoadCommandsPlusHeader + paddingSize)/fOptions.segmentAlignment(); + if ( loadCommandsPage == textPage ) { + paddingSize += fOptions.segmentAlignment(); + textPage += 1; + } + + //paddingSize = 4096 - ((totalSizeOfTEXTLessHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); + fEncryptionLoadCommand->setStartEncryptionOffset(textPage*fOptions.segmentAlignment()); + } } // adjust atom size and update section size @@ -8874,7 +9072,6 @@ void Writer::assignFileOffsets() else { address = ( (address+alignment-1) & (-alignment) ); } - // adjust file offset to match address if ( prevSection != NULL ) { if ( virtualSectionOccupyAddressSpace || !prevSection->fVirtualSection ) @@ -9679,7 +9876,7 @@ void DylibLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const cmd->set_cmd(LC_LAZY_LOAD_DYLIB); else if ( fInfo.options.fWeakImport || autoWeakLoadDylib ) cmd->set_cmd(LC_LOAD_WEAK_DYLIB); - else if ( fInfo.options.fReExport && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) + else if ( fInfo.options.fReExport && fWriter.fOptions.useSimplifiedDylibReExports() ) cmd->set_cmd(LC_REEXPORT_DYLIB); else cmd->set_cmd(LC_LOAD_DYLIB); @@ -9988,10 +10185,15 @@ void LoadCommandsPaddingAtom::setSize(uint64_t newSize) template void UnwindInfoAtom::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* lsdaRef, ObjectFile::Atom* personalityPointer) + ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsdaRef, + ObjectFile::Atom* personalityPointer) { Info info; info.func = func; + if ( fdeRef != NULL ) + info.fde = &fdeRef->getTarget(); + else + info.fde = NULL; if ( lsdaRef != NULL ) { info.lsda = &lsdaRef->getTarget(); info.lsdaOffset = lsdaRef->getTargetOffset(); @@ -10007,6 +10209,24 @@ void UnwindInfoAtom::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, u // encoding, info.lsda, info.lsdaOffset, personalityPointer, func->getDisplayName()); } +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) +{ + return ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); +} + +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) +{ + return ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); +} + +template +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) +{ + return false; +} + template void UnwindInfoAtom::compressDuplicates(std::vector& uniqueInfos) @@ -10020,9 +10240,10 @@ void UnwindInfoAtom::compressDuplicates(std::vector& uniqueInfos) last.personalityPointer = NULL; last.encoding = 0xFFFFFFFF; for(typename std::vector::iterator it=fInfos.begin(); it != fInfos.end(); ++it) { - Info newInfo = *it; + Info& newInfo = *it; + bool newNeedsDwarf = encodingMeansUseDwarf(newInfo.encoding); // remove infos which have same encoding and personalityPointer as last one - if ( (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) + if ( newNeedsDwarf || (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) || (newInfo.lsda != NULL) || (last.lsda != NULL) ) { uniqueInfos.push_back(newInfo); } @@ -10038,6 +10259,9 @@ void UnwindInfoAtom::findCommonEncoding(const std::vector& uniqueInfos, std::map encodingsUsed; unsigned int mostCommonEncodingUsageCount = 0; for(typename std::vector::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { + // never put dwarf into common table + if ( encodingMeansUseDwarf(it->encoding) ) + continue; std::map::iterator pos = encodingsUsed.find(it->encoding); if ( pos == encodingsUsed.end() ) { encodingsUsed[it->encoding] = 1; @@ -10113,7 +10337,8 @@ unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vector::makeCompressedSecondLevelPage(const std::vector< uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) { const bool log = false; + if (log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); // first pass calculates how many compressed entries we could fit in this sized page // keep adding entries to page until: // 1) encoding table plus entry table plus header exceed page size @@ -10149,14 +10375,20 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< encodingIndex = pos->second; } else { - std::map::iterator ppos = pageSpecificEncodings.find(info.encoding); + // no commmon entry, so add one on this page + uint32_t encoding = info.encoding; + if ( encodingMeansUseDwarf(encoding) ) { + // make unique pseudo encoding so this dwarf will gets is own encoding entry slot + encoding += (index+1); + } + std::map::iterator ppos = pageSpecificEncodings.find(encoding); if ( ppos != pageSpecificEncodings.end() ) { encodingIndex = pos->second; } else { encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); if ( encodingIndex <= 255 ) { - pageSpecificEncodings[info.encoding] = encodingIndex; + pageSpecificEncodings[encoding] = encodingIndex; } else { canDo = false; // case 3) @@ -10178,52 +10410,72 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< ++entryCount; } // check room for entry - if ( (pageSpecificEncodings.size()+entryCount) > space4 ) { + if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { canDo = false; // case 1) --entryCount; if (log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); } + //if (log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); } - // sanity check that we fit more entries into this page than a regular page would hold - const int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + // check for cases where it would be better to use a regular (non-compressed) page + const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + pageSpecificEncodings.size()*sizeof(uint32_t) + entryCount*sizeof(uint32_t); - const int regularEntriesPerPage = (compressPageUsed - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - if ( entryCount < regularEntriesPerPage ) { - return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); + if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { + const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + if ( entryCount < regularEntriesPerPage ) { + return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); + } } - + + // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 + uint32_t pad = 0; + if ( compressPageUsed == (pageSize-4) ) + pad = 4; + // second pass fills in page - uint8_t* pageStart = pageEnd - compressPageUsed; + uint8_t* pageStart = pageEnd - compressPageUsed - pad; macho_unwind_info_compressed_second_level_page_header

* page = (macho_unwind_info_compressed_second_level_page_header

*)pageStart; page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); page->set_entryPageOffset(sizeof(macho_unwind_info_compressed_second_level_page_header

)); page->set_entryCount(entryCount); page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); page->set_encodingsCount(pageSpecificEncodings.size()); + uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; // fill in entry table uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; ObjectFile::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { const Info& info = uniqueInfos[i]; uint8_t encodingIndex; - std::map::const_iterator pos = commonEncodings.find(info.encoding); - if ( pos != commonEncodings.end() ) - encodingIndex = pos->second; - else - encodingIndex = pageSpecificEncodings[info.encoding]; + if ( encodingMeansUseDwarf(info.encoding) ) { + // dwarf entries are always in page specific encodings + encodingIndex = pageSpecificEncodings[info.encoding+i]; + } + else { + std::map::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) + encodingIndex = pos->second; + else + encodingIndex = pageSpecificEncodings[info.encoding]; + } uint32_t entryIndex = i - endIndex + entryCount; A::P::E::set32(entiresArray[entryIndex], encodingIndex << 24); - CompressedFixUp fixup; - fixup.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); - fixup.func = info.func; - fixup.fromFunc = firstFunc; - fCompressedFixUps.push_back(fixup); + CompressedFixUp funcStartFixUp; + funcStartFixUp.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); + funcStartFixUp.func = info.func; + funcStartFixUp.fromFunc = firstFunc; + fCompressedFixUps.push_back(funcStartFixUp); + if ( encodingMeansUseDwarf(info.encoding) ) { + CompressedEncodingFixUp dwarfStartFixup; + dwarfStartFixup.contentPointer = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]); + dwarfStartFixup.fde = info.fde; + fCompressedEncodingFixUps.push_back(dwarfStartFixup); + } } // fill in encodings table - uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; - for(std::map::iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { + for(std::map::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { A::P::E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); } @@ -10271,8 +10523,8 @@ void UnwindInfoAtom::generate() fPagesSize = 0; if ( fPagesContentForDelete == NULL ) throw "could not allocate space for compact unwind info"; - ObjectFile::Atom* secondLevelFirstFuncs[pageCount]; - uint8_t* secondLevelPagesStarts[pageCount]; + ObjectFile::Atom* secondLevelFirstFuncs[pageCount*3]; + uint8_t* secondLevelPagesStarts[pageCount*3]; // make last second level page smaller so that all other second level pages can be page aligned uint32_t maxLastPageSize = unwindSectionInfo->fFileOffset % 4096; @@ -10366,12 +10618,18 @@ void UnwindInfoAtom::generate() for (typename std::vector::iterator it = fRegFixUps.begin(); it != fRegFixUps.end(); ++it) { uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; fReferences.push_back(new WriterReference(offset, A::kImageOffset32, it->func)); + if ( it->fde != NULL ) + fReferences.push_back(new WriterReference(offset+4, A::kSectionOffset24, it->fde)); } // make references for compressed second level entries for (typename std::vector::iterator it = fCompressedFixUps.begin(); it != fCompressedFixUps.end(); ++it) { uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; fReferences.push_back(new WriterReference(offset, A::kPointerDiff24, it->func, 0, it->fromFunc, 0)); } + for (typename std::vector::iterator it = fCompressedEncodingFixUps.begin(); it != fCompressedEncodingFixUps.end(); ++it) { + uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; + fReferences.push_back(new WriterReference(offset, A::kSectionOffset24, it->fde)); + } // update section record with new size unwindSectionInfo->fSize = this->getSize(); @@ -10670,27 +10928,48 @@ const char* StringsLinkEditAtom::stringForIndex(int32_t index) const template -BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) - : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset) +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, + ObjectFile::Atom& finalTarget, uint32_t finalTargetOffset) + : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fFinalTarget(finalTarget), fFinalTargetOffset(finalTargetOffset) { - char* buf = new char[strlen(name)+32]; - if ( targetOffset == 0 ) { + if ( finalTargetOffset == 0 ) { if ( islandRegion == 0 ) - sprintf(buf, "%s$island", name); + asprintf((char**)&fName, "%s$island", name); else - sprintf(buf, "%s$island_%d", name, islandRegion); + asprintf((char**)&fName, "%s$island$%d", name, islandRegion+1); } else { - sprintf(buf, "%s_plus_%d$island_%d", name, targetOffset, islandRegion); + asprintf((char**)&fName, "%s_plus_%d$island$%d", name, finalTargetOffset, islandRegion); + } + + if ( finalTarget.isThumb() ) { + if ( writer.fOptions.preferSubArchitecture() && writer.fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + fIslandKind = kBranchIslandToThumb2; + } + else { + fIslandKind = kBranchIslandToThumb1; + } + } + else { + fIslandKind = kBranchIslandToARM; } - fName = buf; } template <> void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const { - int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int64_t displacement; + const int64_t bl_sixteenMegLimit = 0x00FFFFFF; + if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { + displacement = getFinalTargetAdress() - this->getAddress(); + if ( (displacement > bl_sixteenMegLimit) && (displacement < (-bl_sixteenMegLimit)) ) { + displacement = fTarget.getAddress() - this->getAddress(); + } + } + else { + displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress(); + } int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); OSWriteBigInt32(buffer, 0, branchInstruction); } @@ -10698,11 +10977,123 @@ void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const template <> void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const { - int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int64_t displacement; + const int64_t bl_sixteenMegLimit = 0x00FFFFFF; + if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { + displacement = getFinalTargetAdress() - this->getAddress(); + if ( (displacement > bl_sixteenMegLimit) && (displacement < (-bl_sixteenMegLimit)) ) { + displacement = fTarget.getAddress() - this->getAddress(); + } + } + else { + displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress(); + } int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); OSWriteBigInt32(buffer, 0, branchInstruction); } +template <> +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const +{ + const bool log = false; + switch ( fIslandKind ) { + case kBranchIslandToARM: + { + int64_t displacement; + // an ARM branch can branch farther than a thumb branch. The branch + // island generation was conservative and put islands every thumb + // branch distance apart. Check to see if this is a an island + // hopping branch that could be optimized to go directly to target. + if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { + displacement = getFinalTargetAdress() - this->getAddress() - 8; + if ( (displacement < 33554428LL) && (displacement > (-33554432LL)) ) { + // can skip branch island and jump straight to target + if (log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", fName, getFinalTargetAdress(), this->getAddress()); + } + else { + // ultimate target is too far, jump to island + displacement = fTarget.getAddress() - this->getAddress() - 8; + if (log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", fName, fTarget.getAddress()); + } + } + else { + // target of island is ultimate target + displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress() - 8; + if (log) fprintf(stderr, "%s: jump to target at 0x%08llX\n", fName, fTarget.getAddress()); + } + uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; + int32_t branchInstruction = 0xEA000000 | imm24; + OSWriteLittleInt32(buffer, 0, branchInstruction); + } + break; + case kBranchIslandToThumb2: + { + int64_t displacement; + // an ARM branch can branch farther than a thumb branch. The branch + // island generation was conservative and put islands every thumb + // branch distance apart. Check to see if this is a an island + // hopping branch that could be optimized to go directly to target. + if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { + displacement = getFinalTargetAdress() - this->getAddress() - 4; + if ( (displacement < 16777214) && (displacement > (-16777216LL)) ) { + // can skip branch island and jump straight to target + if (log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", fName, getFinalTargetAdress(), this->getAddress()); + } + else { + // ultimate target is too far, jump to island + displacement = fTarget.getAddress() - this->getAddress() - 4; + if (log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", fName, fTarget.getAddress()); + } + } + else { + // target of island is ultimate target + displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress() - 4; + if (log) fprintf(stderr, "%s: jump to target at 0x%08llX\n", fName, fTarget.getAddress()); + } + if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { + throwf("internal branch island error: thumb2 b/bx out of range (%lld max is +/-16M) from %s to %s in %s", + displacement, this->getDisplayName(), + fTarget.getDisplayName(), fTarget.getFile()->getPath()); + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + uint32_t opcode = 0x9000F000; + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + OSWriteLittleInt32(buffer, 0, newInstruction); + } + break; + case kBranchIslandToThumb1: + { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to target which means no island hopping + int64_t displacement = getFinalTargetAdress() - (this->getAddress() + 12); + if ( fFinalTarget.isThumb() ) + displacement |= 1; + if (log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this + } + break; + }; +} + template <> uint64_t BranchIslandAtom::getSize() const { @@ -10715,6 +11106,20 @@ uint64_t BranchIslandAtom::getSize() const return 4; } +template <> +uint64_t BranchIslandAtom::getSize() const +{ + switch ( fIslandKind ) { + case kBranchIslandToARM: + return 4; + case kBranchIslandToThumb1: + return 16; + case kBranchIslandToThumb2: + return 4; + }; + throw "internal error: no ARM branch island kind"; +} + template @@ -11118,7 +11523,7 @@ void CompressedBindingInfoLinkEditAtom::encode() std::vector mid; const SegmentInfo* currentSegment = NULL; unsigned int segIndex = 0; - int ordinal = 0; + int ordinal = 0x80000000; const char* symbolName = NULL; uint8_t type = 0; uint64_t address = (uint64_t)(-1); @@ -11222,10 +11627,12 @@ void CompressedBindingInfoLinkEditAtom::encode() p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; p->operand1 = p->operand1/sizeof(pint_t); } + else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { + p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; + } } dst->opcode = BIND_OPCODE_DONE; - // convert to compressed encoding const static bool log = false; fEncodedData.reserve(info.size()*2); diff --git a/ld64/src/ld/ObjectFile.h b/ld64/src/ld/ObjectFile.h index 87396fb..7bba72a 100644 --- a/ld64/src/ld/ObjectFile.h +++ b/ld64/src/ld/ObjectFile.h @@ -66,7 +66,7 @@ class ReaderOptions fForFinalLinkedImage(false), fNoEHLabels(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), fImplicitlyLinkPublicDylibs(true), - fAddCompactUnwindEncoding(false), + fAddCompactUnwindEncoding(true), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), fMakeCompressedDyldInfo(false), @@ -77,7 +77,7 @@ class ReaderOptions fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; - enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0 }; + enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k3_0, k3_1 }; struct AliasPair { const char* realName; @@ -282,7 +282,8 @@ class Atom public: enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; - enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType, kSectionStart, kSectionEnd }; + enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType, kSectionStart, kSectionEnd, kBranchIsland, + kLazyPointer, kStub, kNonLazyPointer, kLazyDylibPointer, kStubHelper }; enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; virtual Reader* getFile() const = 0; @@ -310,6 +311,7 @@ class Atom virtual UnwindInfo::iterator beginUnwind() { return NULL; } virtual UnwindInfo::iterator endUnwind() { return NULL; } virtual Reference* getLSDA() { return NULL; } + virtual Reference* getFDE() { return NULL; } virtual Atom* getPersonalityPointer() { return NULL; } uint64_t getSectionOffset() const { return fSectionOffset; } diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 58bb74c..6521abe 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -103,7 +103,8 @@ Options::Options(int argc, const char* argv[]) fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), - fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), fSaveTempFiles(false) + fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), + fUseSimplifiedDylibReExports(false), fSaveTempFiles(false) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -1261,41 +1262,24 @@ void Options::setIPhoneVersionMin(const char* version) { if ( version == NULL ) throw "-iphoneos_version_min argument missing"; - - if ( strncmp(version, "1.", 2) == 0 ) { - warning("pre-2.0 iPhone OS version not supported"); + if ( ! isdigit(version[0]) ) + throw "-iphoneos_version_min argument is not a number"; + if ( version[1] != '.' ) + throw "-iphoneos_version_min argument is missing period as second character"; + if ( ! isdigit(version[2]) ) + throw "-iphoneos_version_min argument is not a number"; + + if ( version[0] == '2' ) fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - } - else if ( strncmp(version, "2.", 2) == 0 ) { - int num = version[2] - '0'; - switch ( num ) { - case 0: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - break; - case 1: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1; - break; - case 2: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; - break; - default: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; - break; - } - } - else if ( strncmp(version, "3.", 2) == 0 ) { - int num = version[2] - '0'; - switch ( num ) { - case 0: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; - break; - default: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; - break; - } - } + else if ( (version[0] == '3') && (version[2] == '0') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + else if ( (version[0] == '3') && (version[2] >= '1') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; + else if ( (version[0] >= '4') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; else { - warning("unknown option to -iphoneos_version_min, not 2.x or 3.x"); + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + warning("unknown option to -iphoneos_version_min, not 2.x, 3.x, or 4.x"); } } @@ -3006,7 +2990,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { - if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_0) ) + if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_1) ) if ( !fPrebind ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) @@ -3091,7 +3075,9 @@ void Options::reconfigureDefaults() break; } - // only use compressed LINKEDIT for x86_64 and i386 + // only use compressed LINKEDIT for: + // x86_64 and i386 on Mac OS X 10.6 or later + // arm on iPhoneOS 3.1 or later if ( fMakeCompressedDyldInfo ) { switch (fArchitecture) { case CPU_TYPE_I386: @@ -3101,13 +3087,19 @@ void Options::reconfigureDefaults() else if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_5 ) fMakeCompressedDyldInfo = false; break; - case CPU_TYPE_POWERPC: case CPU_TYPE_ARM: + if ( fReaderOptions.fIPhoneVersionMin >= ObjectFile::ReaderOptions::k3_1 ) + fMakeClassicDyldInfo = false; + else if ( fReaderOptions.fIPhoneVersionMin < ObjectFile::ReaderOptions::k3_1 ) + fMakeCompressedDyldInfo = false; + break; + case CPU_TYPE_POWERPC: case CPU_TYPE_POWERPC64: default: fMakeCompressedDyldInfo = false; } } + // only use compressed LINKEDIT for final linked images if ( fMakeCompressedDyldInfo ) { @@ -3134,7 +3126,27 @@ void Options::reconfigureDefaults() // only final linked images can not optimize zero fill sections if ( fOutputKind == Options::kObjectFile ) fReaderOptions.fOptimizeZeroFill = true; - + + // only dynamic final linked images should warn about use of commmons + if ( fWarnCommons ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kPreload: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kKextBundle: + fWarnCommons = false; + break; + } + } + + // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB + if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) + fUseSimplifiedDylibReExports = true; } void Options::checkIllegalOptionCombinations() @@ -3231,12 +3243,12 @@ void Options::checkIllegalOptionCombinations() warning("custom stack placement overlaps and will disable shared region"); break; case CPU_TYPE_ARM: - if ( fStackSize > 0xFFFFFFFF ) - throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackSize > 0x2F000000 ) + throw "-stack_size must be < 752MB"; if ( fStackAddr == 0 ) - fStackAddr = 0x30000000; - if ( fStackAddr > 0x40000000) - throw "-stack_addr must be < 1G for arm"; + fStackAddr = 0x2F000000; + if ( fStackAddr > 0x30000000) + throw "-stack_addr must be < 0x30000000 for arm"; case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: if ( fStackAddr == 0 ) { @@ -3359,7 +3371,7 @@ void Options::checkIllegalOptionCombinations() if ( fZeroPageSize != ULLONG_MAX ) { for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { if ( (it->address >= 0) && (it->address < fZeroPageSize) ) - throwf("-segaddr %s 0x%X conflicts with -pagezero_size", it->name, it->address); + throwf("-segaddr %s 0x%llX conflicts with -pagezero_size", it->name, it->address); } } // verify no duplicates @@ -3584,13 +3596,17 @@ void Options::checkForClassic(int argc, const char* argv[]) void Options::gotoClassicLinker(int argc, const char* argv[]) { argv[0] = "ld_classic"; + char rawPath[PATH_MAX]; char path[PATH_MAX]; uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { - char* lastSlash = strrchr(path, '/'); - if ( lastSlash != NULL ) { - strcpy(lastSlash+1, "ld_classic"); - execvp(path, (char**)argv); + if ( _NSGetExecutablePath(rawPath, &bufSize) != -1 ) { + if ( realpath(rawPath, path) != NULL ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "ld_classic"); + argv[0] = path; + execvp(path, (char**)argv); + } } } // in case of error in above, try searching for ld_classic via PATH diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 9b8890e..2fa364e 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -35,8 +35,8 @@ #include "ObjectFile.h" -extern void throwf (const char* format, ...) __attribute__ ((noreturn)); -extern void warning(const char* format, ...); +extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); +extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); class LibraryOptions { @@ -231,6 +231,7 @@ class Options bool errorOnOtherArchFiles() { return fErrorOnOtherArchFiles; } bool markAutoDeadStripDylib() { return fMarkDeadStrippableDylib; } bool removeEHLabels() { return fReaderOptions.fNoEHLabels; } + bool useSimplifiedDylibReExports() { return fUseSimplifiedDylibReExports; } private: class CStringEquals @@ -380,6 +381,7 @@ class Options bool fMakeCompressedDyldInfo; bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; + bool fUseSimplifiedDylibReExports; std::vector fInitialUndefines; NameSet fAllowedUndefined; NameSet fWhyLive; diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 6d93c0b..f9d9ecb 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -1,5 +1,5 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -101,8 +101,8 @@ class Section : public ObjectFile::Section typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; //typedef std::map NameToSection; - const char* fSectionName; - const char* fSegmentName; + char fSectionName[18]; + char fSegmentName[18]; bool fZeroFill; bool fUntrustedZeroFill; @@ -116,22 +116,49 @@ std::vector Section::fgSections; Section::NameToOrdinal Section::fgSegmentDiscoverOrder; Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill) - : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill) + : fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill) { + strlcpy(fSectionName, sectionName, sizeof(fSectionName)); + strlcpy(fSegmentName, segmentName, sizeof(fSegmentName)); + this->fIndex = fgSections.size() + 20; // room for 20 standard sections // special placement of some sections if ( strcmp(segmentName, "__TEXT") == 0 ) { + // sort mach header and load commands to start of TEXT + if ( strcmp(sectionName, "._mach_header") == 0 ) + this->fIndex = 1; + else if ( strcmp(sectionName, "._load_commands") == 0 ) + this->fIndex = 2; + else if ( strcmp(sectionName, "._load_cmds_pad") == 0 ) + this->fIndex = 3; + // sort __text after load commands + else if ( strcmp(sectionName, "__text") == 0 ) + this->fIndex = 10; + // sort arm/ppc stubs after text to make branch islands feasible + else if ( strcmp(sectionName, "__picsymbolstub4") == 0 ) + this->fIndex = 11; + else if ( strcmp(sectionName, "__symbol_stub4") == 0 ) + this->fIndex = 11; + else if ( strcmp(sectionName, "__picsymbolstub1") == 0 ) + this->fIndex = 11; + else if ( strcmp(sectionName, "__symbol_stub1") == 0 ) + this->fIndex = 11; // sort unwind info to end of segment - if ( strcmp(sectionName, "__eh_frame") == 0 ) + else if ( strcmp(sectionName, "__eh_frame") == 0 ) this->fIndex = INT_MAX; else if ( strcmp(sectionName, "__unwind_info") == 0 ) this->fIndex = INT_MAX-1; else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) - this->fIndex = INT_MAX-2; + this->fIndex = INT_MAX-2; + else if ( strcmp(sectionName, "__symbolstub1") == 0 ) + this->fIndex = INT_MAX-3; // sort to end of __TEXT to be close to lazy pointers } else if ( strcmp(segmentName, "__DATA") == 0 ) { + // sort arm lazy symbol pointers that must be at start of __DATA + if ( strcmp(sectionName, "__lazy_symbol") == 0 ) + this->fIndex = 0; // sort sections dyld will touch to start of segment - if ( strcmp(sectionName, "__dyld") == 0 ) + else if ( strcmp(sectionName, "__dyld") == 0 ) this->fIndex = 1; else if ( strcmp(sectionName, "__program_vars") == 0 ) this->fIndex = 1; @@ -171,6 +198,8 @@ Section::Section(const char* sectionName, const char* segmentName, bool zeroFill this->fIndex = 18; else if ( strcmp(sectionName, "__objc_imageinfo") == 0 ) this->fIndex = 19; + else if ( strcmp(sectionName, "__huge") == 0 ) + this->fIndex = INT_MAX; } @@ -313,6 +342,7 @@ class Linker : public ObjectFile::Reader::DylibHander { void loadAndResolve(); void processDTrace(); void checkObjC(); + void addSynthesizedAtoms(); void loadUndefines(); void checkUndefines(); void resolveReferences(); @@ -366,12 +396,14 @@ class Linker : public ObjectFile::Reader::DylibHander { void require(const char* name); bool add(ObjectFile::Atom& atom); ObjectFile::Atom* find(const char* name); + void erase(const char* name); unsigned int getRequireCount() { return fRequireCount; } void getUndefinesNames(std::vector& undefines); void getTentativesNames(std::vector& tents); bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } void setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; } + uint32_t dylibSymbolCount() { return fDylibSymbolCount; } Mapper::iterator begin() { return fTable.begin(); } Mapper::iterator end() { return fTable.end(); } @@ -381,6 +413,7 @@ class Linker : public ObjectFile::Reader::DylibHander { unsigned int fRequireCount; bool fHasExternalTentativeDefinitions; bool fHasExternalWeakDefinitions; + uint32_t fDylibSymbolCount; }; class AtomSorter @@ -631,6 +664,20 @@ void Linker::loadAndResolve() } } +void Linker::addSynthesizedAtoms() +{ + // give write a chance to synthesize stub, GOT, and lazy pointer atoms + std::vector newAtoms; + fOutputFile->addSynthesizedAtoms(fAllAtoms, this->dyldClassicHelper(), + this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(), + fBiggerThanTwoGigOutput, + fGlobalSymbolTable.dylibSymbolCount(), + newAtoms); + + // add all newly created atoms to fAllAtoms and update symbol table + this->addAtoms(newAtoms); +} + void Linker::optimize() { // give each reader a chance to do any optimizations @@ -648,6 +695,24 @@ void Linker::optimize() // only do next steps if some optimization was actually done if ( didSomething ) { + + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + for(std::vector::iterator itr = newAtoms.begin(); itr != newAtoms.end(); ++itr) { + ObjectFile::Atom* atom = *itr; + const char* name = atom->getName(); + if ( name != NULL ) { + ObjectFile::Atom* existingAtom = fGlobalSymbolTable.find(name); + if ( (existingAtom != NULL) && fLiveAtoms.count(existingAtom) == 0 ) { + // While dead code stripping, the atoms were not removed from fGlobalSymbolTable + // for performance reasons. Normally, libLTO will never recreate an atom + // that was previously dead stripped away, but if it does remove + // the remnents of the previous so the new one can be added + fGlobalSymbolTable.erase(name); + } + } + } + } + // add all newly created atoms to fAllAtoms and update symbol table this->addAtoms(newAtoms); @@ -748,6 +813,7 @@ void Linker::link() this->checkObjC(); this->processDTrace(); this->tweakLayout(); + this->addSynthesizedAtoms(); this->sortSections(); this->sortAtoms(); this->writeDotOutput(); @@ -1871,7 +1937,7 @@ void Linker::processDTrace() uint64_t offset = offsetsInDOF[i]; //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); if ( offset > dofSectionSize ) - throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); } this->addAtoms(reader->getAtoms()); @@ -2192,10 +2258,15 @@ void Linker::sortAtoms() theOrdinalOverrideMap[atom] = index; if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); } + ++matchCount; } else { - ++matchCount; - //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName); + if ( fOptions.printOrderFileStatistics() ) { + if ( it->objectFileName == NULL ) + warning("can't find match for order_file entry: %s", it->symbolName); + else + warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName); + } } ++index; } @@ -2209,7 +2280,7 @@ void Linker::sortAtoms() //fprintf(stderr, "Sorted atoms:\n"); //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - // fprintf(stderr, "\t%p, %u %s\t%s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath()); + // fprintf(stderr, "\t%s, %u %s\t%s\n", (*it)->getSectionName(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath()); //} } @@ -3110,9 +3181,8 @@ void Linker::writeOutput() fStartWriteTime = mach_absolute_time(); // tell writer about each segment's atoms fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), - this->dyldClassicHelper(),this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(), fCreateUUID, fCanScatter, - fCurrentCpuConstraint, fBiggerThanTwoGigOutput, + fCurrentCpuConstraint, fRegularDefAtomsThatOverrideADylibsWeakDef, fGlobalSymbolTable.hasExternalWeakDefinitions()); } @@ -3940,6 +4010,10 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) case ObjectFile::Atom::kWeakDefinition: fHasExternalWeakDefinitions = true; break; + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + ++fDylibSymbolCount; + break; default: break; } @@ -3962,6 +4036,9 @@ ObjectFile::Atom* Linker::SymbolTable::find(const char* name) return NULL; } +void Linker::SymbolTable::erase(const char* name) { + fTable.erase(name); +} void Linker::SymbolTable::getUndefinesNames(std::vector& undefines) { diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 4af50ff..5c42471 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.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-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -47,6 +47,7 @@ static bool printOpcodes = false; static bool printExport = false; static bool printExportGraph = false; static cpu_type_t sPreferredArch = CPU_TYPE_I386; +static cpu_type_t sPreferredSubArch = 0; __attribute__((noreturn)) @@ -97,6 +98,13 @@ class DyldInfoPrinter void printLazyBindingOpcodes(); void printExportInfo(); void printExportInfoGraph(); + void printRelocRebaseInfo(); + void printSymbolTableExportInfo(); + void printClassicLazyBindingInfo(); + void printClassicBindingInfo(); + pint_t relocBase(); + const char* relocTypeName(uint8_t r_type); + uint8_t segmentIndexForAddress(pint_t addr); void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, char* cummulativeString, int curStrOffset); void processExportGraphNode(const uint8_t* const start, const uint8_t* const end, @@ -109,6 +117,9 @@ class DyldInfoPrinter const char* sectionName(uint8_t segIndex, pint_t address); const char* getSegAndSectName(uint8_t segIndex, pint_t address); const char* ordinalName(int libraryOrdinal); + const char* classicOrdinalName(int libraryOrdinal); + pint_t* mappedAddressForVMAddress(pint_t vmaddress); + const char* fPath; @@ -120,6 +131,10 @@ class DyldInfoPrinter uint32_t fSymbolCount; const macho_dyld_info_command

* fInfo; uint64_t fBaseAddress; + const macho_dysymtab_command

* fDynamicSymbolTable; + const macho_segment_command

* fFirstSegment; + const macho_segment_command

* fFirstWritableSegment; + bool fWriteableSegmentWithAddrOver4G; std::vector*>fSegments; std::vector fDylibs; }; @@ -216,11 +231,12 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) return false; } - template DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) : fHeader(NULL), fLength(fileLength), - fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), fBaseAddress(0) + fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), + fBaseAddress(0), fDynamicSymbolTable(NULL), fFirstSegment(NULL), fFirstWritableSegment(NULL), + fWriteableSegmentWithAddrOver4G(false) { // sanity check if ( ! validFile(fileContent) ) @@ -236,7 +252,6 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen const macho_load_command

* const cmds = (macho_load_command

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

)); const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { - uint32_t size = cmd->cmdsize(); const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); if ( endOfCmd > endOfLoadCommands ) throwf("load command #%d extends beyond the end of the load commands", i); @@ -253,6 +268,14 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen fSegments.push_back(segCmd); if ( (segCmd->fileoff() == 0) && (segCmd->filesize() != 0) ) fBaseAddress = segCmd->vmaddr(); + if ( fFirstSegment == NULL ) + fFirstSegment = segCmd; + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) { + if ( fFirstWritableSegment == NULL ) + fFirstWritableSegment = segCmd; + if ( segCmd->vmaddr() > 0x100000000ULL ) + fWriteableSegmentWithAddrOver4G = true; + } } break; case LC_LOAD_DYLIB: @@ -274,20 +297,48 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen } } break; + case LC_DYSYMTAB: + fDynamicSymbolTable = (macho_dysymtab_command

*)cmd; + break; + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolCount = symtab->nsyms(); + fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); + fStrings = (char*)fHeader + symtab->stroff(); + fStringsEnd = fStrings + symtab->strsize(); + } + break; } cmd = (const macho_load_command

*)endOfCmd; } - if ( printRebase ) - printRebaseInfo(); - if ( printBind ) - printBindingInfo(); + if ( printRebase ) { + if ( fInfo != NULL ) + printRebaseInfo(); + else + printRelocRebaseInfo(); + } + if ( printBind ) { + if ( fInfo != NULL ) + printBindingInfo(); + else + printClassicBindingInfo(); + } if ( printWeakBind ) printWeakBindingInfo(); - if ( printLazyBind ) - printLazyBindingInfo(); - if ( printExport ) - printExportInfo(); + if ( printLazyBind ) { + if ( fInfo != NULL ) + printLazyBindingInfo(); + else + printClassicLazyBindingInfo(); + } + if ( printExport ) { + if ( fInfo != NULL ) + printExportInfo(); + else + printSymbolTableExportInfo(); + } if ( printOpcodes ) { printRebaseInfoOpcodes(); printBindingInfoOpcodes(false); @@ -427,6 +478,29 @@ const char* DyldInfoPrinter::getSegAndSectName(uint8_t segIndex, pint_t addre return "??"; } +template +uint8_t DyldInfoPrinter::segmentIndexForAddress(pint_t address) +{ + for(unsigned int i=0; i < fSegments.size(); ++i) { + if ( (fSegments[i]->vmaddr() <= address) && (address < (fSegments[i]->vmaddr()+fSegments[i]->vmsize())) ) { + return i; + } + } + throwf("address 0x%llX is not in any segment", (uint64_t)address); +} + +template +typename A::P::uint_t* DyldInfoPrinter::mappedAddressForVMAddress(pint_t vmaddress) +{ + for(unsigned int i=0; i < fSegments.size(); ++i) { + if ( (fSegments[i]->vmaddr() <= vmaddress) && (vmaddress < (fSegments[i]->vmaddr()+fSegments[i]->vmsize())) ) { + unsigned long offsetInMappedFile = fSegments[i]->fileoff()+vmaddress-fSegments[i]->vmaddr(); + return (pint_t*)((uint8_t*)fHeader + offsetInMappedFile); + } + } + throwf("address 0x%llX is not in any segment", (uint64_t)vmaddress); +} + template const char* DyldInfoPrinter::ordinalName(int libraryOrdinal) { @@ -440,11 +514,26 @@ const char* DyldInfoPrinter::ordinalName(int libraryOrdinal) } if ( libraryOrdinal < BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) throw "unknown special ordinal"; - if ( libraryOrdinal > fDylibs.size() ) + if ( libraryOrdinal > (int)fDylibs.size() ) throw "libraryOrdinal out of range"; return fDylibs[libraryOrdinal-1]; } +template +const char* DyldInfoPrinter::classicOrdinalName(int libraryOrdinal) +{ + switch ( libraryOrdinal) { + case SELF_LIBRARY_ORDINAL: + return "this-image"; + case EXECUTABLE_ORDINAL: + return "main-executable"; + case DYNAMIC_LOOKUP_ORDINAL: + return "flat-namespace"; + } + if ( libraryOrdinal > (int)fDylibs.size() ) + throw "libraryOrdinal out of range"; + return fDylibs[libraryOrdinal-1]; +} template void DyldInfoPrinter::printRebaseInfo() @@ -453,7 +542,7 @@ void DyldInfoPrinter::printRebaseInfo() printf("no compressed rebase info\n"); } else { - printf("rebase information:\n"); + printf("rebase information (from compressed dyld info):\n"); printf("segment section address type\n"); const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off(); @@ -722,7 +811,7 @@ void DyldInfoPrinter::printBindingInfo() } else { printf("bind information:\n"); - printf("segment section address type addend dylib symbol\n"); + printf("segment section address type weak addend dylib symbol\n"); const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off(); const uint8_t* end = &p[fInfo->bind_size()]; @@ -738,6 +827,7 @@ void DyldInfoPrinter::printBindingInfo() pint_t segStartAddr = 0; const char* segName = "??"; const char* typeName = "??"; + const char* weak_import = ""; bool done = false; while ( !done && (p < end) ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; @@ -770,6 +860,10 @@ void DyldInfoPrinter::printBindingInfo() while (*p != '\0') ++p; ++p; + if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) + weak_import = "weak"; + else + weak_import = ""; break; case BIND_OPCODE_SET_TYPE_IMM: type = immediate; @@ -788,22 +882,22 @@ void DyldInfoPrinter::printBindingInfo() segOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); segOffset += sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); segOffset += read_uleb128(p, end) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); segOffset += skip + sizeof(pint_t); } break; @@ -909,7 +1003,7 @@ void DyldInfoPrinter::printLazyBindingInfo() printf("no compressed lazy binding info\n"); } else { - printf("lazy binding information:\n"); + printf("lazy binding information (from lazy_bind part of dyld info):\n"); printf("segment section address index dylib symbol\n"); const uint8_t* const start = (uint8_t*)fHeader + fInfo->lazy_bind_off(); const uint8_t* const end = &start[fInfo->lazy_bind_size()]; @@ -985,90 +1079,6 @@ void DyldInfoPrinter::printLazyBindingInfo() } -#if 0 - uint8_t type = BIND_TYPE_POINTER; - uint8_t flags; - uint64_t address = fBaseAddress; - const char* symbolName = NULL; - int libraryOrdinal = 0; - int64_t addend = 0; - uint32_t segmentIndex = 0; - uint32_t count; - uint32_t skip; - for (const uint8_t* p = start; p < end; ) { - uint8_t immediate = *p & BIND_IMMEDIATE_MASK; - uint8_t opcode = *p & BIND_OPCODE_MASK; - uint32_t opcodeOffset = p-start; - ++p; - switch (opcode) { - case BIND_OPCODE_DONE: - printf("0x%08X BIND_OPCODE_DONE\n", opcodeOffset); - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - libraryOrdinal = immediate; - printf("0x%08X BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - libraryOrdinal = read_uleb128(p, end); - printf("0x%08X BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%d)\n", opcodeOffset, libraryOrdinal); - break; - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - // the special ordinals are negative numbers - if ( immediate == 0 ) - libraryOrdinal = 0; - else { - int8_t signExtended = BIND_OPCODE_MASK | immediate; - libraryOrdinal = signExtended; - } - printf("0x%08X BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%d)\n", opcodeOffset, libraryOrdinal); - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - flags = immediate; - symbolName = (char*)p; - while (*p != '\0') - ++p; - ++p; - printf("0x%08X BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%02X, %s)\n", opcodeOffset, flags, symbolName); - break; - case BIND_OPCODE_SET_TYPE_IMM: - type = immediate; - printf("0x%08X BIND_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type); - break; - case BIND_OPCODE_SET_ADDEND_SLEB: - addend = read_sleb128(p, end); - printf("0x%08X BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", opcodeOffset, addend); - break; - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - segmentIndex = immediate; - address = read_uleb128(p, end); - printf("0x%08X BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x%02X, 0x%08llX)\n", opcodeOffset, segmentIndex, address); - break; - case BIND_OPCODE_ADD_ADDR_ULEB: - skip = read_uleb128(p, end); - printf("0x%08X BIND_OPCODE_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); - break; - case BIND_OPCODE_DO_BIND: - printf("0x%08X BIND_OPCODE_DO_BIND()\n", opcodeOffset); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - skip = read_uleb128(p, end); - printf("0x%08X BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%08X)\n", opcodeOffset, skip); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - skip = immediate*sizeof(pint_t) + sizeof(pint_t); - printf("0x%08X BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0x%08X)\n", opcodeOffset, skip); - break; - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - count = read_uleb128(p, end); - skip = read_uleb128(p, end); - printf("0x%08X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip); - break; - default: - throwf("unknown bind opcode %d", *p); - } - } -#endif - template void DyldInfoPrinter::printLazyBindingOpcodes() { @@ -1211,6 +1221,7 @@ void DyldInfoPrinter::printExportInfo() printf("no compressed export info\n"); } else { + printf("export information (from trie):\n"); const uint8_t* start = (uint8_t*)fHeader + fInfo->export_off(); const uint8_t* end = &start[fInfo->export_size()]; std::vector list; @@ -1236,6 +1247,7 @@ void DyldInfoPrinter::processExportGraphNode(const uint8_t* const start, cons const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { uint32_t flags = read_uleb128(p, end); + (void)flags; // currently unused uint64_t address = read_uleb128(p, end); printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address); } @@ -1275,8 +1287,307 @@ void DyldInfoPrinter::printExportInfoGraph() } +template <> +ppc::P::uint_t DyldInfoPrinter::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +ppc64::P::uint_t DyldInfoPrinter::relocBase() +{ + if ( fWriteableSegmentWithAddrOver4G ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86::P::uint_t DyldInfoPrinter::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +x86_64::P::uint_t DyldInfoPrinter::relocBase() +{ + // check for split-seg + return fFirstWritableSegment->vmaddr(); +} + +template <> +arm::P::uint_t DyldInfoPrinter::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == GENERIC_RELOC_VANILLA ) + return "pointer"; + else if ( r_type == PPC_RELOC_PB_LA_PTR ) + return "pb pointer"; + else + return "??"; +} + +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == GENERIC_RELOC_VANILLA ) + return "pointer"; + else + return "??"; +} + +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == GENERIC_RELOC_VANILLA ) + return "pointer"; + else if ( r_type == GENERIC_RELOC_PB_LA_PTR ) + return "pb pointer"; + else + return "??"; +} + +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == X86_64_RELOC_UNSIGNED ) + return "pointer"; + else + return "??"; +} + +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == ARM_RELOC_VANILLA ) + return "pointer"; + else if ( r_type == ARM_RELOC_PB_LA_PTR ) + return "pb pointer"; + else + return "??"; +} + + +template +void DyldInfoPrinter::printRelocRebaseInfo() +{ + if ( fDynamicSymbolTable == NULL ) { + printf("no classic dynamic symbol table"); + } + else { + printf("rebase information (from local relocation records and indirect symbol table):\n"); + printf("segment section address type\n"); + // walk all local relocations + pint_t rbase = relocBase(); + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + fDynamicSymbolTable->locreloff()); + const macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + pint_t addr = reloc->r_address() + rbase; + uint8_t segIndex = segmentIndexForAddress(addr); + const char* typeName = relocTypeName(reloc->r_type()); + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + printf("%-8s %-16s 0x%08llX %s\n", segName, sectName, (uint64_t)addr, typeName); + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + pint_t addr = sreloc->r_address() + rbase; + uint8_t segIndex = segmentIndexForAddress(addr); + const char* typeName = relocTypeName(sreloc->r_type()); + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + printf("%-8s %-16s 0x%08llX %s\n", segName, sectName, (uint64_t)addr, typeName); + } + } + // look for local non-lazy-pointers + const uint32_t* indirectSymbolTable = (uint32_t*)(((uint8_t*)fHeader) + fDynamicSymbolTable->indirectsymoff()); + uint8_t segIndex = 0; + for(typename std::vector*>::iterator segit=fSegments.begin(); segit != fSegments.end(); ++segit, ++segIndex) { + const macho_segment_command

* segCmd = *segit; + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t type = sect->flags() & SECTION_TYPE; + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { + uint32_t indirectOffset = sect->reserved1(); + uint32_t count = sect->size() / sizeof(pint_t); + for (uint32_t i=0; i < count; ++i) { + uint32_t symbolIndex = E::get32(indirectSymbolTable[indirectOffset+i]); + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { + pint_t addr = sect->addr() + i*sizeof(pint_t); + const char* typeName = "pointer"; + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + printf("%-8s %-16s 0x%08llX %s\n", segName, sectName, (uint64_t)addr, typeName); + } + } + } + } + } + } +} + + +template +void DyldInfoPrinter::printSymbolTableExportInfo() +{ + if ( fDynamicSymbolTable == NULL ) { + printf("no classic dynamic symbol table"); + } + else { + printf("export information (from symbol table):\n"); + const macho_nlist

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

* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) { + const char* flags = ""; + if ( sym->n_desc() & N_WEAK_DEF ) + flags = "[weak_def] "; + pint_t thumb = 0; + if ( sym->n_desc() & N_ARM_THUMB_DEF ) + thumb = 1; + printf("0x%08llX %s%s\n", sym->n_value()+thumb, flags, &fStrings[sym->n_strx()]); + } + } +} + + +template +void DyldInfoPrinter::printClassicBindingInfo() +{ + if ( fDynamicSymbolTable == NULL ) { + printf("no classic dynamic symbol table"); + } + else { + printf("binding information (from relocations and indirect symbol table):\n"); + printf("segment section address type weak addend dylib symbol\n"); + // walk all external relocations + pint_t rbase = relocBase(); + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(((uint8_t*)fHeader) + fDynamicSymbolTable->extreloff()); + const macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicSymbolTable->nextrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + pint_t addr = reloc->r_address() + rbase; + uint32_t symbolIndex = reloc->r_symbolnum(); + const macho_nlist

* sym = &fSymbols[symbolIndex]; + const char* symbolName = &fStrings[sym->n_strx()]; + const char* weak_import = (sym->n_desc() & N_WEAK_REF) ? "weak" : ""; + const char* fromDylib = classicOrdinalName(GET_LIBRARY_ORDINAL(sym->n_desc())); + uint8_t segIndex = segmentIndexForAddress(addr); + const char* typeName = relocTypeName(reloc->r_type()); + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + const pint_t* addressMapped = mappedAddressForVMAddress(addr); + int64_t addend = P::getP(*addressMapped); + if ( fHeader->flags() & MH_PREBOUND ) { + // In prebound binaries the content is already pointing to the target. + // To get the addend requires subtracting out the base address it was prebound to. + addend -= sym->n_value(); + } + printf("%-8s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectName, (uint64_t)addr, + typeName, weak_import, addend, fromDylib, symbolName); + } + // look for non-lazy pointers + const uint32_t* indirectSymbolTable = (uint32_t*)(((uint8_t*)fHeader) + fDynamicSymbolTable->indirectsymoff()); + for(typename std::vector*>::iterator segit=fSegments.begin(); segit != fSegments.end(); ++segit) { + const macho_segment_command

* segCmd = *segit; + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t type = sect->flags() & SECTION_TYPE; + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { + uint32_t indirectOffset = sect->reserved1(); + uint32_t count = sect->size() / sizeof(pint_t); + for (uint32_t i=0; i < count; ++i) { + uint32_t symbolIndex = E::get32(indirectSymbolTable[indirectOffset+i]); + if ( symbolIndex != INDIRECT_SYMBOL_LOCAL ) { + const macho_nlist

* sym = &fSymbols[symbolIndex]; + const char* symbolName = &fStrings[sym->n_strx()]; + const char* weak_import = (sym->n_desc() & N_WEAK_REF) ? "weak" : ""; + const char* fromDylib = classicOrdinalName(GET_LIBRARY_ORDINAL(sym->n_desc())); + pint_t addr = sect->addr() + i*sizeof(pint_t); + uint8_t segIndex = segmentIndexForAddress(addr); + const char* typeName = "pointer"; + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + int64_t addend = 0; + printf("%-8s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectName, (uint64_t)addr, + typeName, weak_import, addend, fromDylib, symbolName); + } + } + } + } + } + } +} + + +template +void DyldInfoPrinter::printClassicLazyBindingInfo() +{ + if ( fDynamicSymbolTable == NULL ) { + printf("no classic dynamic symbol table"); + } + else { + printf("lazy binding information (from section records and indirect symbol table):\n"); + printf("segment section address index dylib symbol\n"); + const uint32_t* indirectSymbolTable = (uint32_t*)(((uint8_t*)fHeader) + fDynamicSymbolTable->indirectsymoff()); + for(typename std::vector*>::iterator segit=fSegments.begin(); segit != fSegments.end(); ++segit) { + const macho_segment_command

* segCmd = *segit; + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t type = sect->flags() & SECTION_TYPE; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + uint32_t indirectOffset = sect->reserved1(); + uint32_t count = sect->size() / sizeof(pint_t); + for (uint32_t i=0; i < count; ++i) { + uint32_t symbolIndex = E::get32(indirectSymbolTable[indirectOffset+i]); + const macho_nlist

* sym = &fSymbols[symbolIndex]; + const char* symbolName = &fStrings[sym->n_strx()]; + const char* fromDylib = classicOrdinalName(GET_LIBRARY_ORDINAL(sym->n_desc())); + pint_t addr = sect->addr() + i*sizeof(pint_t); + uint8_t segIndex = segmentIndexForAddress(addr); + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s\n", segName, sectName, (uint64_t)addr, symbolIndex, fromDylib, symbolName); + } + } + else if ( (type == S_SYMBOL_STUBS) && (((sect->flags() & S_ATTR_SELF_MODIFYING_CODE) != 0)) && (sect->reserved2() == 5) ) { + // i386 self-modifying stubs + uint32_t indirectOffset = sect->reserved1(); + uint32_t count = sect->size() / 5; + for (uint32_t i=0; i < count; ++i) { + uint32_t symbolIndex = E::get32(indirectSymbolTable[indirectOffset+i]); + if ( symbolIndex != INDIRECT_SYMBOL_ABS ) { + const macho_nlist

* sym = &fSymbols[symbolIndex]; + const char* symbolName = &fStrings[sym->n_strx()]; + const char* fromDylib = classicOrdinalName(GET_LIBRARY_ORDINAL(sym->n_desc())); + pint_t addr = sect->addr() + i*5; + uint8_t segIndex = segmentIndexForAddress(addr); + const char* segName = segmentName(segIndex); + const char* sectName = sectionName(segIndex, addr); + printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s\n", segName, sectName, (uint64_t)addr, symbolIndex, fromDylib, symbolName); + } + } + } + } + } + } +} static void dump(const char* path) { @@ -1301,7 +1612,9 @@ static void dump(const char* path) size_t offset = OSSwapBigToHostInt32(archs[i].offset); size_t size = OSSwapBigToHostInt32(archs[i].size); cpu_type_t cputype = OSSwapBigToHostInt32(archs[i].cputype); - if ( cputype == (uint32_t)sPreferredArch ) { + cpu_type_t cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); + if ( (cputype == sPreferredArch) + && ((sPreferredSubArch==0) || (sPreferredSubArch==cpusubtype)) ) { switch(cputype) { case CPU_TYPE_POWERPC: if ( DyldInfoPrinter::validFile(p + offset) ) @@ -1399,6 +1712,24 @@ 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 throwf("unknown architecture %s", arch); } diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index d8d699d..098d932 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -83,6 +83,7 @@ class UnwindPrinter void getSymbolTableInfo(); const char* functionName(pint_t addr); static const char* archName(); + static void decode(uint32_t encoding, const uint8_t* funcStart, char* str); const char* fPath; const macho_header

* fHeader; @@ -251,7 +252,7 @@ const char* UnwindPrinter::functionName(pint_t addr) } } } - return "??"; + return "--anonymous function--"; } @@ -288,8 +289,379 @@ bool UnwindPrinter::findUnwindSection() return false; } +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + *str = '\0'; + switch ( encoding & UNWIND_X86_64_MODE_MASK ) { + case UNWIND_X86_64_MODE_RBP_FRAME: + { + uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + if ( savedRegistersLocations == 0 ) { + strcpy(str, "rbp frame, no saved registers"); + } + else { + sprintf(str, "rbp frame, at -%d:", savedRegistersOffset*8); + bool needComma = false; + for (int i=0; i < 5; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + strcat(str, "-"); + break; + case UNWIND_X86_64_REG_RBX: + strcat(str, "rbx"); + break; + case UNWIND_X86_64_REG_R12: + strcat(str, "r12"); + break; + case UNWIND_X86_64_REG_R13: + strcat(str, "r13"); + break; + case UNWIND_X86_64_REG_R14: + strcat(str, "r14"); + break; + case UNWIND_X86_64_REG_R15: + strcat(str, "r15"); + break; + default: + strcat(str, "r?"); + } + savedRegistersLocations = (savedRegistersLocations >> 3); + if ( savedRegistersLocations == 0 ) + break; + } + } + } + break; + case UNWIND_X86_64_MODE_STACK_IMMD: + case UNWIND_X86_64_MODE_STACK_IND: + { + uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + if ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = x86_64::P::E::get32(*((uint32_t*)(funcStart+stackSize))); + sprintf(str, "stack size=0x%08X, ", subl + 8*stackAdjust); + } + else { + sprintf(str, "stack size=%d, ", stackSize*8); + } + if ( regCount == 0 ) { + strcat(str, "no registers saved"); + } + else { + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // renumber registers back to standard numbers + int registers[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (int i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registers[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + bool needComma = false; + for (int i=0; i < regCount; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch ( registers[i] ) { + case UNWIND_X86_64_REG_RBX: + strcat(str, "rbx"); + break; + case UNWIND_X86_64_REG_R12: + strcat(str, "r12"); + break; + case UNWIND_X86_64_REG_R13: + strcat(str, "r13"); + break; + case UNWIND_X86_64_REG_R14: + strcat(str, "r14"); + break; + case UNWIND_X86_64_REG_R15: + strcat(str, "r15"); + break; + case UNWIND_X86_64_REG_RBP: + strcat(str, "rbp"); + break; + default: + strcat(str, "r??"); + } + } + } + } + break; + case UNWIND_X86_64_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET); + break; + default: + if ( encoding == 0 ) + strcat(str, "no unwind information"); + else + strcat(str, "tbd "); + } + if ( encoding & UNWIND_HAS_LSDA ) { + strcat(str, " LSDA"); + } + +} + +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + *str = '\0'; + switch ( encoding & UNWIND_X86_MODE_MASK ) { + case UNWIND_X86_MODE_EBP_FRAME: + { + uint32_t savedRegistersOffset = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS); + if ( savedRegistersLocations == 0 ) { + strcpy(str, "ebp frame, no saved registers"); + } + else { + sprintf(str, "ebp frame, at -%d:", savedRegistersOffset*4); + bool needComma = false; + for (int i=0; i < 5; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + strcat(str, "-"); + break; + case UNWIND_X86_REG_EBX: + strcat(str, "ebx"); + break; + case UNWIND_X86_REG_ECX: + strcat(str, "ecx"); + break; + case UNWIND_X86_REG_EDX: + strcat(str, "edx"); + break; + case UNWIND_X86_REG_EDI: + strcat(str, "edi"); + break; + case UNWIND_X86_REG_ESI: + strcat(str, "esi"); + break; + default: + strcat(str, "e??"); + } + savedRegistersLocations = (savedRegistersLocations >> 3); + if ( savedRegistersLocations == 0 ) + break; + } + } + } + break; + case UNWIND_X86_MODE_STACK_IMMD: + case UNWIND_X86_MODE_STACK_IND: + { + uint32_t stackSize = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + if ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_STACK_IND ) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = x86::P::E::get32(*((uint32_t*)(funcStart+stackSize))); + sprintf(str, "stack size=0x%08X, ", subl+4*stackAdjust); + } + else { + sprintf(str, "stack size=%d, ", stackSize*4); + } + if ( regCount == 0 ) { + strcat(str, "no saved regs"); + } + else { + int permunreg[6]; + switch ( regCount ) { + case 6: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // renumber registers back to standard numbers + int registers[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (int i=0; i < regCount; ++i) { + int renum = 0; + for (int u=1; u < 7; ++u) { + if ( !used[u] ) { + if ( renum == permunreg[i] ) { + registers[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + bool needComma = false; + for (int i=0; i < regCount; ++i) { + if ( needComma ) + strcat(str, ","); + else + needComma = true; + switch ( registers[i] ) { + case UNWIND_X86_REG_EBX: + strcat(str, "ebx"); + break; + case UNWIND_X86_REG_ECX: + strcat(str, "ecx"); + break; + case UNWIND_X86_REG_EDX: + strcat(str, "edx"); + break; + case UNWIND_X86_REG_EDI: + strcat(str, "edi"); + break; + case UNWIND_X86_REG_ESI: + strcat(str, "esi"); + break; + case UNWIND_X86_REG_EBP: + strcat(str, "ebp"); + break; + default: + strcat(str, "e??"); + } + } + } + } + break; + case UNWIND_X86_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_DWARF_SECTION_OFFSET); + break; + default: + if ( encoding == 0 ) + strcat(str, "no unwind information"); + else + strcat(str, "tbd "); + } + if ( encoding & UNWIND_HAS_LSDA ) { + strcat(str, " LSDA"); + } + +} + + +template +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + + +} template void UnwindPrinter::printUnwindSection() @@ -309,7 +681,7 @@ void UnwindPrinter::printUnwindSection() printf("\tcommon encodings: (count=%u)\n", sectionHeader->commonEncodingsArrayCount()); const uint32_t* commonEncodings = (uint32_t*)§ionContent[sectionHeader->commonEncodingsArraySectionOffset()]; for (uint32_t i=0; i < sectionHeader->commonEncodingsArrayCount(); ++i) { - printf("\t\tencoding[%2u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i])); + printf("\t\tencoding[%3u]=0x%08X\n", i, A::P::E::get32(commonEncodings[i])); } printf("\tpersonalities: (count=%u)\n", sectionHeader->personalityArrayCount()); const uint32_t* personalityArray = (uint32_t*)§ionContent[sectionHeader->personalityArraySectionOffset()]; @@ -343,10 +715,10 @@ void UnwindPrinter::printUnwindSection() printf("\t\tentryCount=0x%08X\n", page->entryCount()); const macho_unwind_info_regular_second_level_entry

* entry = (macho_unwind_info_regular_second_level_entry

*)((char*)page+page->entryPageOffset()); for (uint32_t j=0; j < page->entryCount(); ++j) { + uint32_t funcOffset = entry[j].functionOffset(); if ( entry[j].encoding() & UNWIND_HAS_LSDA ) { // verify there is a corresponding entry in lsda table bool found = false; - uint32_t funcOffset = entry[j].functionOffset(); for (uint32_t k=0; k < lsdaIndexArrayCount; ++k) { if ( lindex[k].functionOffset() == funcOffset ) { found = true; @@ -357,8 +729,10 @@ void UnwindPrinter::printUnwindSection() fprintf(stderr, "MISSING LSDA entry for %s\n", functionName(funcOffset+fMachHeaderAddress)); } } - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X %s\n", - j, entry[j].functionOffset(), entry[j].encoding(), functionName(entry[j].functionOffset()+fMachHeaderAddress)); + char encodingString[100]; + decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", + j, funcOffset, entry[j].encoding(), encodingString, functionName(funcOffset+fMachHeaderAddress)); } } else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) { @@ -378,7 +752,9 @@ void UnwindPrinter::printUnwindSection() encoding = A::P::E::get32(commonEncodings[encodingIndex]); else encoding = A::P::E::get32(encodings[encodingIndex-sectionHeader->commonEncodingsArrayCount()]); + char encodingString[100]; uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset; + decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString); const char* name = functionName(funcOff+fMachHeaderAddress); if ( encoding & UNWIND_HAS_LSDA ) { // verify there is a corresponding entry in lsda table @@ -393,8 +769,8 @@ void UnwindPrinter::printUnwindSection() fprintf(stderr, "MISSING LSDA entry for %s\n", name); } } - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%2u]=0x%08X %s\n", - j, funcOff, encodingIndex, encoding, name); + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n", + j, funcOff, encodingIndex, encoding, encodingString, name); } } else { diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/branch-islands/Makefile index c85f382..471e446 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/branch-islands/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2008-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,13 +23,6 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ifeq ($(ARCH),armv6) - ARCH_FLAGS = -mlong-branch -else - ARCH_FLAGS = -endif - - # # Simple test for branch islands @@ -38,7 +31,6 @@ endif run: all - all: ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} ${PASS_IFF_GOOD_MACHO} hello diff --git a/ld64/unit-tests/test-cases/branch-islands/space.s b/ld64/unit-tests/test-cases/branch-islands/space.s index dd28637..0218bea 100644 --- a/ld64/unit-tests/test-cases/branch-islands/space.s +++ b/ld64/unit-tests/test-cases/branch-islands/space.s @@ -1,5 +1,5 @@ -#if __ppc__ +#if __ppc__ .text @@ -27,13 +27,50 @@ _space2: #if __arm__ + .text +_prejunk: + mov r0, #1 + nop + +#if __thumb2__ + // thumb2 branches are +/- 16MB +_space1: + .space 14*1024*1024 +_space2: + .space 14*1024*1024 +_space3: + .space 14*1024*1024 + +#elif __thumb__ + // thumb1 branches are +/- 4MB _space1: - .space 32*1024*1024 + 2 + .space 3*1024*1024 +_space2: + .space 3*1024*1024 +_space3: + .space 3*1024*1024 + +#else + // ARM branches are +/- 32MB +_space1: + .space 14*1024*1024 +_space2: + .space 14*1024*1024 +_space3: + .space 14*1024*1024 #endif + .align 5 +_junk: + mov r0, #1 + nop + + +_space4: + .space 2*1024*1024 +#endif .subsections_via_symbols - \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile index 3375aae..3957151 100644 --- a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile +++ b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2008-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,17 +24,17 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Test that utf16 cfstring literals are not coalesced. +# Test that utf16 cfstring literals are coalesced. # There is 3 CFSTR in foo.m and 1 CFSTR in bar.m -# After coalescing and dead stripping there should be only one CFSTR in the output +# After coalescing and dead stripping there should be only two CFSTR in the output # ifeq (,${findstring 64,$(ARCH)}) - CFSTRING_SIZE = 48 + CFSTRING_SIZE = 32 else - CFSTRING_SIZE = 96 + CFSTRING_SIZE = 64 endif - USTRING_SIZE = 37 + USTRING_SIZE = 27 diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile new file mode 100644 index 0000000..72f365b --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded +# + +run: all-${ARCH} + + +all-ppc: + ${PASS_IFF} true + +all-arm: + ${PASS_IFF} true + +all-i386: all-real + +all-x86_64: all-real + + +all-real: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + + ${CC} ${CCFLAGS} main.c -o main libfoo.dylib + dyldinfo -bind main | grep wfoo | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c new file mode 100644 index 0000000..1906d16 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/foo.c @@ -0,0 +1,4 @@ + + +__attribute__((weak)) void wfoo() {} +void foo() {} diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c new file mode 100644 index 0000000..afd696d --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/main.c @@ -0,0 +1,17 @@ + +extern void foo(); +extern void wfoo(); + +void* pfoo = &foo; +void* pwfoo = &wfoo; + +int main (void) +{ + if (pfoo != &foo) + return 1; + if (pwfoo != &wfoo) + return 1; + + return 0; +} + diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile new file mode 100644 index 0000000..4e55304 --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that __cstring sections in other segments are not coalesced +# ld coalesces C strings in different segments +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c custom.s -o main + size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s new file mode 100644 index 0000000..33705e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s @@ -0,0 +1,8 @@ + + + + .section __MYSEG, __cstring, cstring_literals +LC: .ascii "hello" + + + diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/main.c b/ld64/unit-tests/test-cases/cstring-alt-segment/main.c new file mode 100644 index 0000000..8fe18db --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + printf("hello"); + return 0; +} diff --git a/ld64/unit-tests/test-cases/cstring-labels/foo.c b/ld64/unit-tests/test-cases/cstring-labels/foo.c index efed306..0780b62 100644 --- a/ld64/unit-tests/test-cases/cstring-labels/foo.c +++ b/ld64/unit-tests/test-cases/cstring-labels/foo.c @@ -1,4 +1,6 @@ +void func() {} + const char kFoo[] = "foo"; const char* kFoo2 = "hello"; diff --git a/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile new file mode 100644 index 0000000..e60513e --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that N_NO_DEAD_STRIP bit survives ld -r +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + nm -m main.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY} + nm -m main.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY} + ${LD} -r main.o -o main2.o + nm -m main2.o | grep _bar | grep "no dead strip" | ${FAIL_IF_EMPTY} + nm -m main2.o | grep _foo | grep "no dead strip" | ${FAIL_IF_EMPTY} + ${CC} main2.o -o main + nm -m main | grep _bar | grep "no dead strip" | ${PASS_IFF_EMPTY} + +clean: + rm -rf main.o main2.o main diff --git a/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c new file mode 100644 index 0000000..7e206e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-r_symbol_desc/main.c @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +__attribute__((used)) static void foo() +{ +} + + +__attribute__((used)) void bar() +{ +} + + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile new file mode 100644 index 0000000..45886c0 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that symbols in no-dead-strip sections can be coalesced. +# -dead_strip inhibits weak coalescing in no_dead_strip section +# + +run: all + +all: + ${CC} ${CCFLAGS} baz.c -c -o baz.o + libtool -static baz.o -o libbaz.a + ${CC} ${CCFLAGS} main.c foo.c libbaz.a -dead_strip -o main + nm -j main | grep _hidden | wc -l | grep 1 | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main baz.o libbaz.a diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c new file mode 100644 index 0000000..7d76bdd --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/baz.c @@ -0,0 +1,7 @@ +void baz() +{ +} + + +#include "foo.c" + diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c new file mode 100644 index 0000000..4cd4cfb --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/foo.c @@ -0,0 +1,25 @@ + +// function can be coalesced and should not be dead stripped +void __attribute__ ((weak, section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) foo() +{ + +} + + +// function should not be exported, can be coalesced, and should not be dead stripped +void __attribute__ ((weak, visibility("hidden"), section ("__TEXT,__text_no_strip,regular,no_dead_strip"))) hidden() +{ + +} + +// bar should be dead stripped +void __attribute__ ((weak, section ("__DATA,__text2"))) bar() +{ + +} + +__attribute__((constructor)) static void init() +{ + foo(); + hidden(); +} diff --git a/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c new file mode 100644 index 0000000..e4c564c --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-weak-coalesce/main.c @@ -0,0 +1,13 @@ + +// baz is in a lazily loaded archive +extern void baz(); + +int main() +{ + baz(); + return 0; +} + + +#include "foo.c" + diff --git a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile index 8ce50d6..27cd180 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile +++ b/ld64/unit-tests/test-cases/eh-coalescing-no-labels/Makefile @@ -43,7 +43,7 @@ all: ${LD} -r foo.no.o bar.no.o baz.no.o -o foobarbaz.no.o ${OBJECTDUMP} -no_content -no_sort foobarbaz.o > foobarbaz.dump ${OBJECTDUMP} -no_content -no_sort foobarbaz.no.o > foobarbaz.no.dump - ${FAIL_IF_ERROR} dwarfdump --eh-frame --verify >/dev/null + ${FAIL_IF_ERROR} dwarfdump --eh-frame --verify foobarbaz.no.o >/dev/null ${PASS_IFF_SUCCESS} diff foobarbaz.dump foobarbaz.no.dump diff --git a/ld64/unit-tests/test-cases/init-order/Makefile b/ld64/unit-tests/test-cases/init-order/Makefile index bb67908..7e8c6e1 100644 --- a/ld64/unit-tests/test-cases/init-order/Makefile +++ b/ld64/unit-tests/test-cases/init-order/Makefile @@ -33,8 +33,8 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CXX} ${CXXFLAGS} main.cxx foo.cxx bar.cxx -o main - nm -s __TEXT __text -nj main | c++filt > actual-order.txt + ${CXX} ${CXXFLAGS} main.cxx foo.cxx bar.cxx -o main -dead_strip + nm -s __TEXT __text -nj main | grep -v dyld_stub_binding_helper | c++filt > actual-order.txt ${PASS_IFF} diff actual-order.txt expected-order.txt diff --git a/ld64/unit-tests/test-cases/init-order/expected-order.txt b/ld64/unit-tests/test-cases/init-order/expected-order.txt index 1de60f3..9a303d3 100644 --- a/ld64/unit-tests/test-cases/init-order/expected-order.txt +++ b/ld64/unit-tests/test-cases/init-order/expected-order.txt @@ -8,8 +8,6 @@ Bar::Bar() __static_initialization_and_destruction_0(int, int) global constructors keyed to b1 start -dyld_stub_binding_helper -__dyld_func_lookup _main M::~M() Foo::~Foo() diff --git a/ld64/unit-tests/test-cases/kext-basic/Makefile b/ld64/unit-tests/test-cases/kext-basic/Makefile index 6a318b3..d233171 100644 --- a/ld64/unit-tests/test-cases/kext-basic/Makefile +++ b/ld64/unit-tests/test-cases/kext-basic/Makefile @@ -23,7 +23,6 @@ all: nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY} nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY} otool -rv mykext | grep '_extern_global' | ${FAIL_IF_EMPTY} - otool -rv mykext | grep '_OSRuntimeFinalizeCPP' | ${FAIL_IF_EMPTY} ${PASS_IFF} true clean: diff --git a/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile b/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile old mode 100644 new mode 100755 diff --git a/ld64/unit-tests/test-cases/label-on-end-of-section/foo.s b/ld64/unit-tests/test-cases/label-on-end-of-section/foo.s old mode 100644 new mode 100755 diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile new file mode 100644 index 0000000..060e5a1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# 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 + ${PASS_IFF_GOOD_MACHO} libbar.dylib + +clean: + rm bar.o libbar.dylib diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/bar.c b/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/bar.c new file mode 100644 index 0000000..b3e3958 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/bar.c @@ -0,0 +1,4 @@ + + +void bar() {} + diff --git a/ld64/unit-tests/test-cases/lto-llvm-options/Makefile b/ld64/unit-tests/test-cases/lto-llvm-options/Makefile index 5d4e991..1edf08a 100644 --- a/ld64/unit-tests/test-cases/lto-llvm-options/Makefile +++ b/ld64/unit-tests/test-cases/lto-llvm-options/Makefile @@ -34,10 +34,10 @@ LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} run: all all: - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o -fvisibility=hidden ${LLVMGCC} ${CCFLAGS} main.o -o main nm main | grep _foo | ${FAIL_IF_STDIN} - ${LLVMGCC} ${CCFLAGS} main.o -o main2 -Wl,-mllvm -Wl,--disable-inlining + ${LLVMGCC} ${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/ld64/unit-tests/test-cases/lto-objc-archive/Makefile b/ld64/unit-tests/test-cases/lto-objc-archive/Makefile new file mode 100644 index 0000000..57fae1d --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# link failure with -O4 on i386# +# 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.m -c -o foo.o + ${CC} ${CCFLAGS} bar.m -c -o bar.o + ${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 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main foo.o bar.o foo2.o bar2.o libfoobar.a main.o diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/bar.h b/ld64/unit-tests/test-cases/lto-objc-archive/bar.h new file mode 100644 index 0000000..b7daf1f --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/bar.h @@ -0,0 +1,6 @@ +#include + +@interface Bar : NSObject +@end + +extern void bar2(); diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/bar.m b/ld64/unit-tests/test-cases/lto-objc-archive/bar.m new file mode 100644 index 0000000..ef32e97 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/bar.m @@ -0,0 +1,7 @@ +#include "bar.h" + +@implementation Bar +- (void) test { + bar2(); +} +@end diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/bar2.c b/ld64/unit-tests/test-cases/lto-objc-archive/bar2.c new file mode 100644 index 0000000..1422109 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/bar2.c @@ -0,0 +1,2 @@ + +void bar2() {} diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/foo.h b/ld64/unit-tests/test-cases/lto-objc-archive/foo.h new file mode 100644 index 0000000..6c3fab2 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/foo.h @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + + +extern void foo2(); + diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/foo.m b/ld64/unit-tests/test-cases/lto-objc-archive/foo.m new file mode 100644 index 0000000..90543e0 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/foo.m @@ -0,0 +1,8 @@ + +#include "foo.h" + +@implementation Foo +- (void) test { + foo2(); +} +@end diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/foo2.c b/ld64/unit-tests/test-cases/lto-objc-archive/foo2.c new file mode 100644 index 0000000..9d2ab14 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/foo2.c @@ -0,0 +1,2 @@ + +void foo2() {} diff --git a/ld64/unit-tests/test-cases/lto-objc-archive/main.m b/ld64/unit-tests/test-cases/lto-objc-archive/main.m new file mode 100644 index 0000000..b84612d --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-archive/main.m @@ -0,0 +1,20 @@ + +#include +#include "foo.h" +#include "bar.h" + + +@interface FooSubClass : Foo +@end + + +@implementation FooSubClass +@end + + +int main() +{ + [Bar alloc]; + return 0; +} + diff --git a/ld64/unit-tests/test-cases/lto-weak_import/Makefile b/ld64/unit-tests/test-cases/lto-weak_import/Makefile new file mode 100644 index 0000000..e53ad48 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-weak_import/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# link failure with -O4 on i386# +# 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 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main.o libfoo.dylib diff --git a/ld64/unit-tests/test-cases/lto-weak_import/foo.c b/ld64/unit-tests/test-cases/lto-weak_import/foo.c new file mode 100644 index 0000000..b586675 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-weak_import/foo.c @@ -0,0 +1,3 @@ + + +void foo() {} diff --git a/ld64/unit-tests/test-cases/lto-weak_import/main.c b/ld64/unit-tests/test-cases/lto-weak_import/main.c new file mode 100644 index 0000000..d36ba48 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-weak_import/main.c @@ -0,0 +1,13 @@ + +#include + +extern void foo() __attribute__((weak_import)); + + +int main() +{ + if ( &foo != NULL ) + foo(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/no-data-bundle/Makefile b/ld64/unit-tests/test-cases/no-data-bundle/Makefile new file mode 100644 index 0000000..e1417fd --- /dev/null +++ b/ld64/unit-tests/test-cases/no-data-bundle/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that a bundle built with no data links +# gcc DejaGnu failure: building longcall/dylib library +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -bundle -o foo.bundle + ${PASS_IFF_GOOD_MACHO} foo.bundle + +clean: + rm foo.bundle diff --git a/ld64/unit-tests/test-cases/no-data-bundle/foo.c b/ld64/unit-tests/test-cases/no-data-bundle/foo.c new file mode 100644 index 0000000..5cb3e31 --- /dev/null +++ b/ld64/unit-tests/test-cases/no-data-bundle/foo.c @@ -0,0 +1,6 @@ +#include + +void foo() +{ + rand(); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile b/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile old mode 100644 new mode 100755 diff --git a/ld64/unit-tests/test-cases/no_zero_fill_sections/main.c b/ld64/unit-tests/test-cases/no_zero_fill_sections/main.c old mode 100644 new mode 100755 diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile new file mode 100644 index 0000000..76a7347 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify an Objective-C object file when run through +# ld -r -x +# x86_64 obj-c runtime confused when static lib is stripped +# +# + +SELECTOR_REFS = "__OBJC,__message_refs" + +ifeq ($(ARCH),x86_64) + SELECTOR_REFS = "__DATA,__objc_selrefs" +endif +ifeq ($(ARCH),armv6) + SELECTOR_REFS = "__DATA,__objc_selrefs" +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} test.m -c -o test.o + ${OBJECTDUMP} -no_content test.o | grep -B3 -A6 ${SELECTOR_REFS} > test.dump + + ${LD} -arch ${ARCH} -r test.o -x -o test-r.o + ${OBJECTDUMP} -no_content test-r.o | grep -B3 -A6 ${SELECTOR_REFS} > test-r.dump + + diff test.dump test-r.dump | ${PASS_IFF_EMPTY} + +clean: + rm -rf test.o test.dump test-r.o test-r.dump diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m new file mode 100644 index 0000000..4837911 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m @@ -0,0 +1,46 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include + +@interface Foo @end +@implementation Foo ++(void)initialize { } ++(void)foo { + fprintf(stderr, "GOOD\n"); + exit(0); +} ++(void)bar { + fprintf(stderr, "BAD\n"); + abort(); +} +@end + +void PublicFunction(void) +{ + [Foo foo]; + [Foo bar]; +} diff --git a/ld64/unit-tests/test-cases/operator-new/Makefile b/ld64/unit-tests/test-cases/operator-new/Makefile index 8abf3e1..447ee87 100644 --- a/ld64/unit-tests/test-cases/operator-new/Makefile +++ b/ld64/unit-tests/test-cases/operator-new/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,6 +30,9 @@ all: # verify if operator new is overridden that WEAK_DEFINES is set ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} + # verify if operator new is overridden but not exported, WEAK_DEFINES is not set + ${CXX} ${CXXFLAGS} -DOP_NEW -I${TESTROOT}/include -o main main.cxx -Wl,-exported_symbol,_main + otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN} # verify if operator new is not overridden that WEAK_DEFINES is not set ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx otool -hv main | grep WEAK_DEFINES | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx index 3c99e35..b5d3272 100644 --- a/ld64/unit-tests/test-cases/operator-new/main.cxx +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -36,7 +36,7 @@ #if OP_NEW void* operator new(size_t s) throw (std::bad_alloc) { - return malloc(s);; + return malloc(s); } #endif diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile new file mode 100644 index 0000000..fdb13d5 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile @@ -0,0 +1,64 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a public re-exported library is automatically added as a dependent +# unless nothing is used from it. +# + + +run: all + +all: + +# -sub_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libmiddle.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libother.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.4 + nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +# -sub_library for 10.5 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libmiddle.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libother.dylib + ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.5 + nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf libbar.dylib libfoo.dylib libmiddle.dylib libother.dylib main diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c new file mode 100644 index 0000000..672ef9a --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/main.c @@ -0,0 +1,8 @@ + +extern void bar(); + +int main() +{ + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c new file mode 100644 index 0000000..d3578a6 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/middle.c @@ -0,0 +1,3 @@ + +void middle() {} + diff --git a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c new file mode 100644 index 0000000..0cd6dda --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/other.c @@ -0,0 +1 @@ +void other() {} diff --git a/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile new file mode 100644 index 0000000..36fb47b --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# i386 relocation error with negative offsets from local labels +# + +ifeq (${ARCH},i386) + TARGET = run-i386 +else + TARGET = run-other +endif + +run: ${TARGET} + +run-other: + ${PASS_IFF} /usr/bin/true + + +run-i386: + ${CC} ${ASMFLAGS} test.s -c -o test.o + ${OBJECTDUMP} test.o | grep "__data@0 plus 0xFFFFFFE2" | ${FAIL_IF_EMPTY} + + ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o + ${OBJECTDUMP} test-r.o | grep "__data@0 plus 0xFFFFFFE2" | ${PASS_IFF_STDIN} + + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s b/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s new file mode 100644 index 0000000..3890f35 --- /dev/null +++ b/ld64/unit-tests/test-cases/relocs-neg-from-local/test.s @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#if __i386__ + .text + .align 2 + +_negative_offset_from_local_label: + nop + .space 100 + movl -80+L3(,%eax,4), %edx + ret + + .data +L2: .space 50 +L3: .space 50 +_d: .long 0 + +#endif + + diff --git a/ld64/unit-tests/test-cases/section-names-long/Makefile b/ld64/unit-tests/test-cases/section-names-long/Makefile new file mode 100644 index 0000000..f31d9d6 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate long section names are preserved +# corrupt metaclass entry in dynamic library +# + +all: + ${CC} ${CCFLAGS} main.c a.s c.s b.s -o main + nm -m main | grep __aaaaaaaaaaaaaa | grep __TEXT | grep _at | ${FAIL_IF_EMPTY} + nm -m main | grep __bbbbbbbbbbbbbb | grep __TEXT | grep _bt | ${FAIL_IF_EMPTY} + nm -m main | grep __cccccccccccccc | grep __TEXT | grep _ct | ${FAIL_IF_EMPTY} + nm -m main | grep __aaaaaaaaaaaaaa | grep __DATA | grep _ad | ${FAIL_IF_EMPTY} + nm -m main | grep __bbbbbbbbbbbbbb | grep __DATA | grep _bd | ${FAIL_IF_EMPTY} + nm -m main | grep __cccccccccccccc | grep __DATA | grep _cd | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/ld64/unit-tests/test-cases/section-names-long/a.s b/ld64/unit-tests/test-cases/section-names-long/a.s new file mode 100644 index 0000000..d7e5847 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/a.s @@ -0,0 +1,9 @@ + + .section __TEXT,__aaaaaaaaaaaaaa +_at: .space 128 + + .section __DATA,__aaaaaaaaaaaaaa +_ad: .space 128 + + + diff --git a/ld64/unit-tests/test-cases/section-names-long/b.s b/ld64/unit-tests/test-cases/section-names-long/b.s new file mode 100644 index 0000000..a31d414 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/b.s @@ -0,0 +1,9 @@ + + .section __TEXT,__bbbbbbbbbbbbbb +_bt: .space 128 + + .section __DATA,__bbbbbbbbbbbbbb +_bd: .space 128 + + + diff --git a/ld64/unit-tests/test-cases/section-names-long/c.s b/ld64/unit-tests/test-cases/section-names-long/c.s new file mode 100644 index 0000000..383b159 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/c.s @@ -0,0 +1,11 @@ + + .section __TEXT,__cccccccccccccc +_ct: .space 128 + + + .section __DATA,__cccccccccccccc +_cd: .space 128 + + + + diff --git a/ld64/unit-tests/test-cases/section-names-long/main.c b/ld64/unit-tests/test-cases/section-names-long/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-names-long/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile b/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile new file mode 100644 index 0000000..cd6dc04 --- /dev/null +++ b/ld64/unit-tests/test-cases/shared-cache-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify only dylibs with install paths in /System/Library or /usr/lib +# get LC_SEGMENT_SPLIT_INFO +# + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/lib/libfoo.dylib + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /System/Library/Frameworks/Foo.framework/Foo + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + otool -lv libfoo.dylib | grep LC_SEGMENT_SPLIT_INFO | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c b/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile index c0647b3..fcb7e7d 100644 --- a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,18 +23,14 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ifeq "${ARCH}" "i386" - POINTER_SEGMENT = __IMPORT - POINTER_SECTION = __pointers -else - POINTER_SEGMENT = __DATA - POINTER_SECTION = __nl_symbol_ptr -endif +POINTER_SEGMENT = __DATA +POINTER_SECTION = __nl_symbol_ptr # # Test that using strip -R to selectively strip symbol names # of of a .o file still works with ld. +# And for i386 that there are no __IMPORT/__pointers left # run: all @@ -51,6 +47,8 @@ all: ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers + size -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN} + size -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN} ${PASS_IFF} diff dylib1.pointers dylib2.pointers clean: diff --git a/ld64/unit-tests/test-cases/weak_import/Makefile b/ld64/unit-tests/test-cases/weak_import/Makefile index d1fa1f3..cdacfc9 100644 --- a/ld64/unit-tests/test-cases/weak_import/Makefile +++ b/ld64/unit-tests/test-cases/weak_import/Makefile @@ -34,7 +34,7 @@ all: ${CC} ${CCFLAGS} -dynamiclib -single_module foo.c -o libfoo-${ARCH}.dylib ${FAIL_IF_BAD_MACHO} libfoo-${ARCH}.dylib - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main-${ARCH} libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} main.c -o main-${ARCH} libfoo-${ARCH}.dylib nm -m main-${ARCH} | grep _func1 | grep -v weak >/dev/null nm -m main-${ARCH} | grep _func2 | grep weak >/dev/null nm -m main-${ARCH} | grep _func3 | grep -v weak >/dev/null @@ -43,10 +43,12 @@ all: nm -m main-${ARCH} | grep _data2 | grep weak >/dev/null nm -m main-${ARCH} | grep _data3 | grep -v weak >/dev/null nm -m main-${ARCH} | grep _data4 | grep weak >/dev/null - otool -rv main-${ARCH} | grep _data6 > /dev/null + nm -m main-${ARCH} | grep _data5 | grep -v weak >/dev/null + nm -m main-${ARCH} | grep _data6 | grep weak >/dev/null + #otool -rv main-${ARCH} | grep _data6 > /dev/null ${FAIL_IF_BAD_MACHO} main-${ARCH} - ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib + ${CC} ${CCFLAGS} main.c -dynamiclib -o main-${ARCH}.dylib libfoo-${ARCH}.dylib nm -m main-${ARCH}.dylib | grep _func1 | grep -v weak >/dev/null nm -m main-${ARCH}.dylib | grep _func2 | grep weak >/dev/null nm -m main-${ARCH}.dylib | grep _func3 | grep -v weak >/dev/null @@ -55,7 +57,7 @@ all: nm -m main-${ARCH}.dylib | grep _data2 | grep weak >/dev/null nm -m main-${ARCH}.dylib | grep _data3 | grep -v weak >/dev/null nm -m main-${ARCH}.dylib | grep _data4 | grep weak >/dev/null - otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null + #otool -rv main-${ARCH}.dylib | grep _data6 > /dev/null ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib clean: From 4a88279eedd2cc9f21f36383347e41e62eac8810 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:33:41 +0100 Subject: [PATCH 05/48] 97-2 --- ld64/ChangeLog | 131 +++++++++++++++ ld64/src/ld/LTOReader.hpp | 21 +++ ld64/src/ld/MachOReaderDylib.hpp | 142 +++++++++++----- ld64/src/ld/MachOReaderRelocatable.hpp | 115 ++++++++++++- ld64/src/ld/MachOWriterExecutable.hpp | 48 +++++- ld64/src/ld/ObjectFile.h | 2 +- ld64/src/ld/Options.cpp | 48 +++++- ld64/src/ld/ld.cpp | 154 +++++++++++++++--- ld64/src/other/ObjectDump.cpp | 26 ++- ld64/src/other/machochecker.cpp | 13 +- .../test-cases/archive-basic/Makefile | 2 +- .../test-cases/archive-duplicate/Makefile | 4 +- .../dead_strip-entry-archive/Makefile | 40 +++++ .../test-cases/dead_strip-entry-archive/bar.c | 4 + .../test-cases/dead_strip-entry-archive/foo.c | 6 + .../test-cases/empty-dylib/Makefile | 39 +++++ .../unit-tests/test-cases/empty-dylib/empty.c | 0 .../test-cases/empty-dylib/justdata.c | 1 + .../unit-tests/test-cases/kext-basic/Makefile | 2 +- .../section-labels-zero-fill/Makefile | 46 ++++++ .../section-labels-zero-fill/both.c | 43 +++++ .../test-cases/section-labels-zero-fill/bss.c | 38 +++++ .../section-labels-zero-fill/common.c | 37 +++++ .../static-executable-weak-defines/Makefile | 35 ++++ .../static-executable-weak-defines/test.c | 13 ++ 25 files changed, 922 insertions(+), 88 deletions(-) create mode 100644 ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-entry-archive/bar.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-entry-archive/foo.c create mode 100644 ld64/unit-tests/test-cases/empty-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/empty-dylib/empty.c create mode 100644 ld64/unit-tests/test-cases/empty-dylib/justdata.c create mode 100644 ld64/unit-tests/test-cases/section-labels-zero-fill/Makefile create mode 100644 ld64/unit-tests/test-cases/section-labels-zero-fill/both.c create mode 100644 ld64/unit-tests/test-cases/section-labels-zero-fill/bss.c create mode 100644 ld64/unit-tests/test-cases/section-labels-zero-fill/common.c create mode 100644 ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile create mode 100644 ld64/unit-tests/test-cases/static-executable-weak-defines/test.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog index 77b9256..ffe4190 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,4 +1,135 @@ +----- Tagged ld64-97.2 + +2009-09-25 Nick Kledzik + + 'unknown DWARF EH encoding' message logged to console with clang-50 + + +----- Tagged ld64-97.1 + +2009-07-28 Nick Kledzik + + make better no-PIC branch island to thumb1 + * Add kBranchIslandNoPicToThumb1 as 8-byte no-PIC island + + +2009-07-20 Nick Kledzik + + section$start$__DATA$__bss can fail + * In SectionBoundaryAtom constructor make __bss and __common be zero-fill + * In AtomSorter, make sure end kSectionEnd sort after kTentativeDefinition + * unit-tests/test-cases/section-labels-zero-fill: add test cases + + +----- Tagged ld64-97 + +2009-07-13 Nick Kledzik + + empty dylib should have subtype from command line + In Linker::writeOutput() for ARM, set initial fCurrentCpuConstraint to be command line subtype + + +2009-07-09 Nick Kledzik + + crash when using -dead_strip and LTO with unresolved intrinsic + * In Linker::optimize() after final deadStripResolve() call checkUndefines() one last time + + +2009-07-09 Nick Kledzik + + ld64 can not find a -e entry point from an archive + * src/ld/ld.cpp: add searchArchives parameter to entryPoint() for use by deadStripResolve() + * unit-tests/test-cases/dead_strip-entry-archive: added test case + + +2009-07-08 Nick Kledzik + + __objc_classrefs section could be coalesced + * In machoReader, chop up __objc_classrefs section into pointer size atoms + and make each weak and hidden with a name based on its target name. + + +2009-06-26 Nick Kledzik + + "ld: symbol dyld_stub_binding_helper not defined" for xnu built with clang for x86_64 + * Fix relocationNeededInFinalLinkedImage() to say weak-defs don't require indirection in static executables + * Added unit-tests/test-cases/static-executable-weak-defines + + +2009-06-26 Nick Kledzik + + Error msg for missing -init symbol is misleading/unclear + * In Linker::checkUndefines() check all ways that the command line could add an undefined + and then search for close matches/typos. + + +2009-06-25 Nick Kledzik + + We need ld-symbol-moving-symbols for ARM/Embedded + * In mach_o::dylib::Reader::addSymbol() parse iPhoneOS version strings + * Update IPhoneVersionMin + + +2009-06-25 Nick Kledzik + + Linker makes subtype-zero file from empty subtype-nonzero file. + * In Linker::writeOutput() set inittal fCurrentCpuConstraint to be command line subtype + * Fix fArchitectureName to have arm sub-types + + +2009-06-24 Nick Kledzik + + ld should do a better job of reporting attempts to link directories + * In Options::buildSearchPaths() sanity check -L and -F options + + +2009-06-24 Nick Kledzik + + better error message when libLTO.dylib not loadable + * in Linker::createReader() provide detail error messages + + +----- Tagged ld64-96.10 + +2009-08-31 Nick Kledzik + + empty dylib has been __mh_dylib_header n_sect + * rework patch to use MinimalTextAtom to ensure there is always a __text section + + +----- Tagged ld64-96.9 + +2009-08-31 Nick Kledzik + + empty dylib has been __mh_dylib_header n_sect + * suppress __mh_dylib_header from symbol table if there is no __text section + + Implicitly add dyld_stub_binder to libSystem.dylib so iPhone clients can build + against SnowLeopard libSystem.dylib. + + +----- Tagged ld64-96.8 + +2009-08-30 Nick Kledzik + + SWB: gcc-5577.1 fails to build vecLib + + +----- Tagged ld64-96.7 + +2009-08-29 Nick Kledzik + + Linker makes subtype-zero file from empty subtype-nonzero file. + + +----- Tagged ld64-96.6 + +2009-07-30 Nick Kledzik + + ld: ldr 12-bit displacement out of range on SnowLeopard with gcc-5648 + * in Section::Section() set fIndex to ensure __symbolstub1 sorts to end of __TEXT segment + ----- Tagged ld64-96.5 diff --git a/ld64/src/ld/LTOReader.hpp b/ld64/src/ld/LTOReader.hpp index 0bd200e..9d00b00 100644 --- a/ld64/src/ld/LTOReader.hpp +++ b/ld64/src/ld/LTOReader.hpp @@ -267,6 +267,7 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); + static const char* fileKind(const uint8_t* fileContent); static bool loaded() { return (::lto_get_version() != NULL); } Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, @@ -358,6 +359,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path kind = ObjectFile::Atom::kWeakDefinition; break; case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: kind = ObjectFile::Atom::kExternalDefinition; break; default: @@ -416,6 +418,25 @@ bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); } +const char* Reader::fileKind(const uint8_t* p) +{ + if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { + uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + switch (arch) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + return "arm"; + } + return "unknown bitcode architecture"; + } + return NULL; +} + bool Reader::optimize(const std::vector& allAtoms, std::vector& newAtoms, std::vector& additionalUndefines, const std::set& deadAtoms, std::vector& newlyDeadAtoms, diff --git a/ld64/src/ld/MachOReaderDylib.hpp b/ld64/src/ld/MachOReaderDylib.hpp index 8aea6ef..1d8f89b 100644 --- a/ld64/src/ld/MachOReaderDylib.hpp +++ b/ld64/src/ld/MachOReaderDylib.hpp @@ -307,7 +307,8 @@ class Reader : public ObjectFile::Reader bool fLazyLoaded; ObjectFile::Reader::ObjcConstraint fObjcContraint; std::vector fReExportedChildren; - const ObjectFile::ReaderOptions::MacVersionMin fDeploymentVersionMin; + const ObjectFile::ReaderOptions::MacVersionMin fMacDeploymentVersionMin; + const ObjectFile::ReaderOptions::IPhoneVersionMin fIPhoneDeploymentVersionMin; std::vector fFlatImports; static bool fgLogHashtable; @@ -330,7 +331,8 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), fObjcContraint(ObjectFile::Reader::kObjcNone), - fDeploymentVersionMin(options.fMacVersionMin) + fMacDeploymentVersionMin(options.fMacVersionMin), + fIPhoneDeploymentVersionMin(options.fIPhoneVersionMin) { // sanity check if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) @@ -593,6 +595,13 @@ void Reader::addDyldFastStub() addSymbol("dyld_stub_binder", false); } +// hack for bring up of iPhoneOS builds on SnowLeopard +template <> +void Reader::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false); +} + template void Reader::addDyldFastStub() { @@ -610,53 +619,100 @@ void Reader::addSymbol(const char* name, bool weakDef) const char* symAction = &name[4]; const char* symCond = strchr(symAction, '$'); if ( symCond != NULL ) { - ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; - if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { - switch ( symCond[6] - '0' ) { - case 0: - case 1: - symVersionCondition = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - symVersionCondition = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - symVersionCondition = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - symVersionCondition = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - symVersionCondition = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - symVersionCondition = ObjectFile::ReaderOptions::k10_6; - break; - } - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); + if ( fMacDeploymentVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { + ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; + // ex: $ld$add$os10.6$_foo + if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { + switch ( symCond[6] - '0' ) { + case 0: + case 1: + symVersionCondition = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + symVersionCondition = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + symVersionCondition = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + symVersionCondition = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + symVersionCondition = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + symVersionCondition = ObjectFile::ReaderOptions::k10_6; + break; + } + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fMacDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } } } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } } else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); + warning("bad symbol version: %s in dylib %s", name, this->getPath()); } } - else { - warning("bad symbol version: %s in dylib %s", name, this->getPath()); + else if ( fIPhoneDeploymentVersionMin != ObjectFile::ReaderOptions::kMinIPhoneVersionUnset ) { + ObjectFile::ReaderOptions::IPhoneVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinIPhoneVersionUnset; + // ex: $ld$add$os3.1$_foo + if ( (strncmp(symCond, "$os", 3) == 0) && isdigit(symCond[3]) && (symCond[4] == '.') ) { + if ( (symCond[3] == '2') && (symCond[5] == '0') ) + symVersionCondition = ObjectFile::ReaderOptions::k2_0; + else if ( (symCond[3] == '2') && (symCond[5] == '1') ) + symVersionCondition = ObjectFile::ReaderOptions::k2_1; + else if ( (symCond[3] == '2') && (symCond[5] >= '2') ) + symVersionCondition = ObjectFile::ReaderOptions::k2_2; + else if ( (symCond[3] == '3') && (symCond[5] == '0') ) + symVersionCondition = ObjectFile::ReaderOptions::k3_0; + else if ( (symCond[3] == '3') && (symCond[5] == '1') ) + symVersionCondition = ObjectFile::ReaderOptions::k3_1; + else if ( (symCond[3] == '3') && (symCond[5] >= '2') ) + symVersionCondition = ObjectFile::ReaderOptions::k3_2; + else if ( (symCond[3] >= '4') ) + symVersionCondition = ObjectFile::ReaderOptions::k4_0; + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fIPhoneDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } + } + } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol version: %s in dylib %s, symCond=%s", name, this->getPath(), symCond); + } } } else { diff --git a/ld64/src/ld/MachOReaderRelocatable.hpp b/ld64/src/ld/MachOReaderRelocatable.hpp index e6a182c..0792f51 100644 --- a/ld64/src/ld/MachOReaderRelocatable.hpp +++ b/ld64/src/ld/MachOReaderRelocatable.hpp @@ -1058,6 +1058,13 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio if ( !fOwner.fOptions.fNoEHLabels ) fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; } + else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { + fSynthesizedName = "objc-class-pointer-name-PENDING"; + fScope = ObjectFile::Atom::scopeLinkageUnit; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + fKind = ObjectFile::Atom::kWeakDefinition; + } else if ( section == owner.fUTF16Section ) { if ( fOwner.fOptions.fForFinalLinkedImage ) { fDontDeadStrip = false; @@ -1355,6 +1362,16 @@ void AnonymousAtom::resolveName() if ( funcAtom != NULL ) asprintf((char**)&fSynthesizedName, "%s.lsda", funcAtom->getDisplayName()); } + else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { + std::vector& references = this->getReferences(); + if ( references.size() != 1 ) + throwf("__objc_classrefs element missing reloc (count=%ld) for target class in %s", references.size(), fOwner.getPath()); + const char* targetName = references[0]->getTargetName(); + if ( strncmp(targetName, "_OBJC_CLASS_$_", 14) == 0 ) + asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", &targetName[14]); + else + asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", targetName); + } } @@ -1552,7 +1569,7 @@ class SectionBoundaryAtom : public BaseAtom virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kWeakDefinition; } virtual ObjectFile::Atom::ContentType getContentType() const { return fStart ? ObjectFile::Atom::kSectionStart : ObjectFile::Atom::kSectionEnd; } - virtual bool isZeroFill() const { return false; } + virtual bool isZeroFill() const { return fZeroFill; } virtual bool isThumb() const { return false; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } virtual bool dontDeadStrip() const { return false; } @@ -1609,6 +1626,7 @@ class SectionBoundaryAtom : public BaseAtom const char* fSectionName; const char* fDisplayName; bool fStart; + bool fZeroFill; static std::vector fgNoReferences; }; @@ -1620,7 +1638,7 @@ std::vector SectionBoundaryAtom::fgNoReferences; // section$end$__DATA$__my template SectionBoundaryAtom::SectionBoundaryAtom(Reader& owner, bool start, const char* symbolName, const char* segSectName) - : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start) + : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start), fZeroFill(false) { const char* segSectDividor = strrchr(segSectName, '$'); if ( segSectDividor == NULL ) @@ -1633,8 +1651,11 @@ SectionBoundaryAtom::SectionBoundaryAtom(Reader& owner, bool start, const strlcpy(segName, segSectName, segNameLen+1); if ( strcmp(segName, "__TEXT") == 0 ) fSegment = new Segment("__TEXT", true, false, true); - else if ( strcmp(segName, "__DATA") == 0 ) + else if ( strcmp(segName, "__DATA") == 0 ) { fSegment = new Segment("__DATA", true, true, false); + if ( (strcmp(fSectionName, "__bss") == 0) || (strcmp(fSectionName, "__common") == 0) ) + fZeroFill = true; + } else fSegment = new Segment(strdup(segName), true, true, false); @@ -1801,6 +1822,7 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); + static const char* fileKind(const uint8_t* fileContent); Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); virtual ~Reader() {} @@ -2268,6 +2290,10 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { atomSize = 4 * sizeof(pint_t); } + // special case class reference sections + else if ( (strncmp(sect->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0) ) { + atomSize = sizeof(pint_t);; + } break; } if ( atomSize != 0 ) { @@ -3214,7 +3240,9 @@ typename A::P::uint_t ObjectFileAddressSpace::getEncodedP(pint_t& addr, pint_ // do nothing break; case DW_EH_PE_pcrel: - result += startAddr; + // pc-rel sdata4 should return zero if content is zero + if ( (result != 0) || ((encoding & DW_EH_PE_indirect) != 0) ) + result += startAddr; break; case DW_EH_PE_textrel: throw "DW_EH_PE_textrel pointer encoding not supported"; @@ -4127,6 +4155,85 @@ bool Reader::validFile(const uint8_t* fileContent, bool subtypeMustMatch, c return true; } + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_POWERPC_750: + return "ppc750"; + case CPU_SUBTYPE_POWERPC_7400: + return "ppc7400"; + case CPU_SUBTYPE_POWERPC_7450: + return "ppc7450"; + case CPU_SUBTYPE_POWERPC_970: + return "ppc970"; + case CPU_SUBTYPE_POWERPC_ALL: + return "ppc"; + } + return "ppc???"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return NULL; + return "ppc64"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_I386 ) + return NULL; + return "i386"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return NULL; + return "x86_64"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + 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"; + } + return "arm???"; +} + + template bool Reader::isWeakImportSymbol(const macho_nlist

* sym) { diff --git a/ld64/src/ld/MachOWriterExecutable.hpp b/ld64/src/ld/MachOWriterExecutable.hpp index 2ed0feb..9596733 100644 --- a/ld64/src/ld/MachOWriterExecutable.hpp +++ b/ld64/src/ld/MachOWriterExecutable.hpp @@ -967,6 +967,23 @@ class LoadCommandsPaddingAtom : public WriterAtom uint64_t fSize; }; +template +class MinimalTextAtom : public WriterAtom +{ +public: + MinimalTextAtom(Writer& writer) + : WriterAtom(writer, headerSegment(writer)) {} + virtual const char* getDisplayName() const { return "minimal text"; } + virtual uint64_t getSize() const { return 0; } + virtual const char* getSectionName() const { return "__text"; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + +private: + using WriterAtom::fWriter; +}; + + template class UnwindInfoAtom : public WriterAtom { @@ -1480,7 +1497,7 @@ class BranchIslandAtom : public WriterAtom uint64_t getFinalTargetAdress() const { return fFinalTarget.getAddress() + fFinalTargetOffset; } private: using WriterAtom::fWriter; - enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1 }; + enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1, kBranchIslandNoPicToThumb1 }; const char* fName; ObjectFile::Atom& fTarget; ObjectFile::Atom& fFinalTarget; @@ -2869,7 +2886,7 @@ Writer::Writer(const char* path, Options& options, std::vector::Writer(const char* path, Options& options, std::vector(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MinimalTextAtom(*this)); if ( fOptions.needsUnwindInfoSection() ) fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); @@ -2941,6 +2959,7 @@ Writer::Writer(const char* path, Options& options, std::vector(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MinimalTextAtom(*this)); if ( fOptions.needsUnwindInfoSection() ) fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); @@ -5273,7 +5292,10 @@ typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(cons else return kRelocNone; case ObjectFile::Atom::kWeakDefinition: - // all calls to global weak definitions get indirected + // in static executables, references to weak definitions are not indirected + if ( fOptions.outputKind() == Options::kStaticExecutable) + return kRelocNone; + // in dynamic code, all calls to global weak definitions get indirected if ( this->shouldExport(target) ) return kRelocExternal; else if ( fSlideable ) @@ -10947,7 +10969,10 @@ BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int i fIslandKind = kBranchIslandToThumb2; } else { - fIslandKind = kBranchIslandToThumb1; + if ( writer.fSlideable ) + fIslandKind = kBranchIslandToThumb1; + else + fIslandKind = kBranchIslandNoPicToThumb1; } } else { @@ -11091,6 +11116,19 @@ void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this } break; + case kBranchIslandNoPicToThumb1: + { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to target which means no island hopping + uint32_t targetAddr = getFinalTargetAdress(); + if ( fFinalTarget.isThumb() ) + targetAddr |= 1; + if (log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); + OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] + OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this + } + break; }; } @@ -11116,6 +11154,8 @@ uint64_t BranchIslandAtom::getSize() const return 16; case kBranchIslandToThumb2: return 4; + case kBranchIslandNoPicToThumb1: + return 8; }; throw "internal error: no ARM branch island kind"; } diff --git a/ld64/src/ld/ObjectFile.h b/ld64/src/ld/ObjectFile.h index 7bba72a..e3ad601 100644 --- a/ld64/src/ld/ObjectFile.h +++ b/ld64/src/ld/ObjectFile.h @@ -77,7 +77,7 @@ class ReaderOptions fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; - enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k3_0, k3_1 }; + enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0, k3_1, k3_2, k4_0 }; struct AliasPair { const char* realName; diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 6521abe..f63a123 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -1269,14 +1269,20 @@ void Options::setIPhoneVersionMin(const char* version) if ( ! isdigit(version[2]) ) throw "-iphoneos_version_min argument is not a number"; - if ( version[0] == '2' ) + if ( (version[0] == '2') && (version[2] == '0') ) fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + else if ( (version[0] == '2') && (version[2] == '1') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1; + else if ( (version[0] == '2') && (version[2] >= '2') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; else if ( (version[0] == '3') && (version[2] == '0') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - else if ( (version[0] == '3') && (version[2] >= '1') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; + else if ( (version[0] == '3') && (version[2] == '1') ) fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; + else if ( (version[0] == '3') && (version[2] >= '2') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_2; else if ( (version[0] >= '4') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k4_0; else { fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; warning("unknown option to -iphoneos_version_min, not 2.x, 3.x, or 4.x"); @@ -2537,10 +2543,36 @@ void Options::buildSearchPaths(int argc, const char* argv[]) frameworkPaths.reserve(10); // scan through argv looking for -L, -F, -Z, and -syslibroot options for(int i=0; i < argc; ++i) { - if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) - libraryPaths.push_back(&argv[i][2]); - else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) - frameworkPaths.push_back(&argv[i][2]); + if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) { + const char* libSearchDir = &argv[i][2]; + if ( libSearchDir[0] == '\0' ) + throw "-L must be immediately followed by a directory path (no space)"; + struct stat statbuf; + if ( stat(libSearchDir, &statbuf) == 0 ) { + if ( statbuf.st_mode & S_IFDIR ) + libraryPaths.push_back(libSearchDir); + else + warning("path '%s' following -L not a directory", libSearchDir); + } + else { + warning("directory '%s' following -L not found", libSearchDir); + } + } + else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) { + const char* frameworkSearchDir = &argv[i][2]; + if ( frameworkSearchDir[0] == '\0' ) + throw "-F must be immediately followed by a directory path (no space)"; + struct stat statbuf; + if ( stat(frameworkSearchDir, &statbuf) == 0 ) { + if ( statbuf.st_mode & S_IFDIR ) + frameworkPaths.push_back(frameworkSearchDir); + else + warning("path '%s' following -F not a directory", frameworkSearchDir); + } + else { + warning("directory '%s' following -F not found", frameworkSearchDir); + } + } else if ( strcmp(argv[i], "-Z") == 0 ) addStandardLibraryDirectories = false; else if ( strcmp(argv[i], "-v") == 0 ) { diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index f9d9ecb..6b6d114 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -143,15 +143,16 @@ Section::Section(const char* sectionName, const char* segmentName, bool zeroFill this->fIndex = 11; else if ( strcmp(sectionName, "__symbol_stub1") == 0 ) this->fIndex = 11; + // sort fast arm stubs to end of __TEXT to be close to lazy pointers + else if ( strcmp(sectionName, "__symbolstub1") == 0 ) + this->fIndex = INT_MAX; // sort unwind info to end of segment else if ( strcmp(sectionName, "__eh_frame") == 0 ) - this->fIndex = INT_MAX; - else if ( strcmp(sectionName, "__unwind_info") == 0 ) this->fIndex = INT_MAX-1; - else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) + else if ( strcmp(sectionName, "__unwind_info") == 0 ) this->fIndex = INT_MAX-2; - else if ( strcmp(sectionName, "__symbolstub1") == 0 ) - this->fIndex = INT_MAX-3; // sort to end of __TEXT to be close to lazy pointers + else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) + this->fIndex = INT_MAX-3; } else if ( strcmp(segmentName, "__DATA") == 0 ) { // sort arm lazy symbol pointers that must be at start of __DATA @@ -332,6 +333,7 @@ class Linker : public ObjectFile::Reader::DylibHander { }; ObjectFile::Reader* createReader(const Options::FileInfo&); + const char* fileArch(const void* p); void addAtom(ObjectFile::Atom& atom); void addAtoms(std::vector& atoms); void buildAtomList(); @@ -359,7 +361,7 @@ class Linker : public ObjectFile::Reader::DylibHander { static const char* truncateStabString(const char* str); void collectDebugInfo(); void writeOutput(); - ObjectFile::Atom* entryPoint(bool orInit); + ObjectFile::Atom* entryPoint(bool orInit, bool searchArchives=false); ObjectFile::Atom* dyldClassicHelper(); ObjectFile::Atom* dyldCompressedHelper(); ObjectFile::Atom* dyldLazyLibraryHelper(); @@ -541,6 +543,22 @@ Linker::Linker(int argc, const char* argv[]) break; case CPU_TYPE_ARM: fArchitectureName = "arm"; + if ( fOptions.preferSubArchitecture() ) { + switch ( fOptions.subArchitecture() ) { + case CPU_SUBTYPE_ARM_V4T: + fArchitectureName = "armv4t"; + break; + case CPU_SUBTYPE_ARM_V5TEJ: + fArchitectureName = "armv5"; + break; + case CPU_SUBTYPE_ARM_V6: + fArchitectureName = "armv6"; + break; + case CPU_SUBTYPE_ARM_V7: + fArchitectureName = "armv7"; + break; + } + } break; default: fArchitectureName = "unknown architecture"; @@ -746,6 +764,7 @@ void Linker::optimize() // LTO may optimize away some atoms, so dead stripping must be redone fLiveAtoms.clear(); this->deadStripResolve(); + this->checkUndefines(); } else { // LTO may require new library symbols to be loaded, so redo @@ -1218,10 +1237,44 @@ void Linker::checkUndefines() } } // scan command line options - if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { - fprintf(stderr, " -exported_symbols_list command line option\n"); + if ( !foundAtomReference ) { + // might be from -init command line option + if ( (fOptions.initFunctionName() != NULL) && (strcmp(name, fOptions.initFunctionName()) == 0) ) { + fprintf(stderr, " -init command line option\n"); + } + // or might be from exported symbol option + else if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbol[s_list] command line option\n"); + } + else { + bool isInitialUndefine = false; + std::vector& clundefs = fOptions.initialUndefines(); + for (std::vector::iterator uit = clundefs.begin(); uit != clundefs.end(); ++uit) { + if ( strcmp(*uit, name) == 0 ) { + isInitialUndefine = true; + break; + } + } + if ( isInitialUndefine ) + fprintf(stderr, " -u command line option\n"); + } ++unresolvableExportsCount; } + // be helpful and check for typos + bool printedStart = false; + for (SymbolTable::Mapper::iterator sit=fGlobalSymbolTable.begin(); sit != fGlobalSymbolTable.end(); ++sit) { + if ( (sit->second != NULL) && (strstr(sit->first, name) != NULL) ) { + if ( ! printedStart ) { + fprintf(stderr, " (maybe you meant: %s", sit->first); + printedStart = true; + } + else { + fprintf(stderr, ", %s ", sit->first); + } + } + } + if ( printedStart ) + fprintf(stderr, ")\n"); } } if ( doError ) @@ -1593,7 +1646,7 @@ void Linker::moveToFrontOfSection(ObjectFile::Atom* atom) void Linker::deadStripResolve() { // add main() to live roots - ObjectFile::Atom* entryPoint = this->entryPoint(false); + ObjectFile::Atom* entryPoint = this->entryPoint(false, true); if ( entryPoint != NULL ) fLiveRootAtoms.insert(entryPoint); @@ -2460,7 +2513,7 @@ void Linker::writeDotOutput() } } -ObjectFile::Atom* Linker::entryPoint(bool orInit) +ObjectFile::Atom* Linker::entryPoint(bool orInit, bool searchArchives) { // if main executable, find entry point atom ObjectFile::Atom* entryPoint = NULL; @@ -2470,6 +2523,11 @@ ObjectFile::Atom* Linker::entryPoint(bool orInit) case Options::kDyld: case Options::kPreload: entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + if ( (entryPoint == NULL) && searchArchives ) { + // ld64 can not find a -e entry point from an archive + this->addJustInTimeAtoms(fOptions.entryName(), false, true, false); + entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + } if ( entryPoint == NULL ) { throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); } @@ -3175,6 +3233,12 @@ void Linker::collectDebugInfo() void Linker::writeOutput() { + // ld -r of empty .o file should preserve sub-type + // empty dylib should have subtype from command line + if ( fOptions.preferSubArchitecture() && (fOptions.architecture() == CPU_TYPE_ARM) ) { + fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)fOptions.subArchitecture(); + } + if ( fOptions.forceCpuSubtypeAll() ) fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; @@ -3187,6 +3251,33 @@ void Linker::writeOutput() fGlobalSymbolTable.hasExternalWeakDefinitions()); } +const char* Linker::fileArch(const void* p) +{ + const uint8_t* bytes = (uint8_t*)p; + const char* result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + + result = lto::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + + return "unsupported file format"; +} + ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) { // map in whole file @@ -3203,8 +3294,10 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) // if fat file, skip to architecture we want // Note: fat header is always big-endian + bool isFatFile = false; const fat_header* fh = (fat_header*)p; if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + isFatFile = true; const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); uint32_t sliceToUse; bool sliceFound = false; @@ -3295,8 +3388,28 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) if ( lto::Reader::validFile(p, len, fArchitecture) ) { return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); } - else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) { - throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded"; + else if ( lto::Reader::fileKind((uint8_t*)p) != NULL ) { + if ( lto::Reader::loaded() ) { + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); + } + else { + const char* libLTO = "libLTO.dylib"; + char ldPath[PATH_MAX]; + char tmpPath[PATH_MAX]; + char libLTOPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tmpPath) != NULL ) { + char* lastSlash = strrchr(tmpPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib/libLTO.dylib"); + libLTO = tmpPath; + if ( realpath(tmpPath, libLTOPath) != NULL ) + libLTO = libLTOPath; + } + } + throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); + } } #endif // error handling @@ -3304,7 +3417,10 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) throwf("missing required architecture %s in file", fArchitectureName); } else { - throw "file is not of required architecture"; + if ( isFatFile ) + throwf("file is universal but does not contain a(n) %s slice", fArchitectureName); + else + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); } } @@ -4107,6 +4223,12 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi } } + // magic section$end symbol always sorts to the end of its section + if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) + return false; + if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) + return true; + // the __common section can have real or tentative definitions // we want the real ones to sort before tentative ones bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); @@ -4114,12 +4236,6 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi if ( leftIsTent != rightIsTent ) return rightIsTent; - // magic section$end symbol always sorts to the end of its section - if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) - return false; - if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) - return true; - // initializers are auto sorted to start of section if ( !fInitializerSet.empty() ) { bool leftFirst = (fInitializerSet.count(left) != 0); diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index c2ae578..25352b1 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -40,7 +40,8 @@ static bool sDumpContent= true; static bool sDumpStabs = false; static bool sSort = true; static bool sNMmode = false; -static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; +static cpu_type_t sPreferredArch = CPU_TYPE_I386; +static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF; static const char* sMatchName; static int sPrintRestrict; static int sPrintAlign; @@ -405,8 +406,11 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { - p = p + OSSwapBigToHostInt32(archs[i].offset); - mh = (struct mach_header*)p; + if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) { + p = p + OSSwapBigToHostInt32(archs[i].offset); + mh = (struct mach_header*)p; + break; + } } } } @@ -480,8 +484,22 @@ int main(int argc, const char* argv[]) sPreferredArch = CPU_TYPE_X86_64; else if ( strcmp(arch, "arm") == 0 ) sPreferredArch = CPU_TYPE_ARM; - else if ( strcmp(arch, "armv6") == 0 ) + 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 throwf("unknown architecture %s", arch); } diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index cb4a781..68fdbb8 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -106,6 +106,7 @@ class MachOChecker bool fWriteableSegmentWithAddrOver4G; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; + uint32_t fSectionCount; }; @@ -210,7 +211,7 @@ 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) + fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fSectionCount(0) { // sanity check if ( ! validFile(fileContent) ) @@ -387,6 +388,7 @@ void MachOChecker::checkLoadCommands() throwf("section %s file offset not within segment", sect->sectname()); } checkSection(segCmd, sect); + ++fSectionCount; } } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); @@ -606,6 +608,15 @@ void MachOChecker::checkSymbolTable() if ( externalNames.find(symName) != externalNames.end() ) throwf("undefine with same name as external symbol: %s", symName); } + // verify all N_SECT values are valid + for(const macho_nlist

* p = fSymbols; p < &fSymbols[fSymbolCount]; ++p) { + uint8_t type = p->n_type(); + if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) { + if ( p->n_sect() > fSectionCount ) { + throwf("symbol '%s' has n_sect=%d which is too large", &fStrings[p->n_strx()], p->n_sect()); + } + } + } } } diff --git a/ld64/unit-tests/test-cases/archive-basic/Makefile b/ld64/unit-tests/test-cases/archive-basic/Makefile index 39c0ef9..65c5f56 100644 --- a/ld64/unit-tests/test-cases/archive-basic/Makefile +++ b/ld64/unit-tests/test-cases/archive-basic/Makefile @@ -39,7 +39,7 @@ all: ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -L. -o main-${ARCH} ${FAIL_IF_BAD_MACHO} main-${ARCH} nm main-${ARCH} | grep "_bar" | ${PASS_IFF_EMPTY} - ${CC} ${CCFLAGS} main.c -all_load -lfoobar-${ARCH} -L. -o main-${ARCH} + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar-${ARCH}.a -o main-${ARCH} ${FAIL_IF_BAD_MACHO} main-${ARCH} clean: diff --git a/ld64/unit-tests/test-cases/archive-duplicate/Makefile b/ld64/unit-tests/test-cases/archive-duplicate/Makefile index 61f4bc7..e339b18 100644 --- a/ld64/unit-tests/test-cases/archive-duplicate/Makefile +++ b/ld64/unit-tests/test-cases/archive-duplicate/Makefile @@ -36,9 +36,9 @@ all: ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a - ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -lfoobar-${ARCH} -L. -o main-${ARCH} -all_load + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar-${ARCH}.a -lfoobar-${ARCH} -L. -o main-${ARCH} ${FAIL_IF_BAD_MACHO} main-${ARCH} - ${CC} ${CCFLAGS} main.c ./libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} -all_load + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} ${PASS_IFF_GOOD_MACHO} main-${ARCH} clean: diff --git a/ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile b/ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile new file mode 100644 index 0000000..1317c48 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# ld64 can not find a -e entry point from an archive +# + +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 -dead_strip -o foo -Wl,-e,_foo + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf foo libfoo.a foo.o + diff --git a/ld64/unit-tests/test-cases/dead_strip-entry-archive/bar.c b/ld64/unit-tests/test-cases/dead_strip-entry-archive/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-entry-archive/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/ld64/unit-tests/test-cases/dead_strip-entry-archive/foo.c b/ld64/unit-tests/test-cases/dead_strip-entry-archive/foo.c new file mode 100644 index 0000000..95ec91c --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-entry-archive/foo.c @@ -0,0 +1,6 @@ + + +void foo() {} + + + diff --git a/ld64/unit-tests/test-cases/empty-dylib/Makefile b/ld64/unit-tests/test-cases/empty-dylib/Makefile new file mode 100644 index 0000000..a12e7c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/empty-dylib/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2009 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 + +# +# Check that the n_sect number for __mh_dylib_header is valid when there is no __text section +# + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib justdata.c -o libjustdata.dylib -Wl,-no_compact_unwind + ${PASS_IFF_GOOD_MACHO} libjustdata.dylib + ${CC} ${CCFLAGS} -dynamiclib empty.c -o libempty.dylib -Wl,-no_compact_unwind + ${PASS_IFF_GOOD_MACHO} libempty.dylib + +clean: + rm -rf libempty.dylib libjustdata.dylib diff --git a/ld64/unit-tests/test-cases/empty-dylib/empty.c b/ld64/unit-tests/test-cases/empty-dylib/empty.c new file mode 100644 index 0000000..e69de29 diff --git a/ld64/unit-tests/test-cases/empty-dylib/justdata.c b/ld64/unit-tests/test-cases/empty-dylib/justdata.c new file mode 100644 index 0000000..266e00d --- /dev/null +++ b/ld64/unit-tests/test-cases/empty-dylib/justdata.c @@ -0,0 +1 @@ +int data = 1; diff --git a/ld64/unit-tests/test-cases/kext-basic/Makefile b/ld64/unit-tests/test-cases/kext-basic/Makefile index d233171..4844cd7 100644 --- a/ld64/unit-tests/test-cases/kext-basic/Makefile +++ b/ld64/unit-tests/test-cases/kext-basic/Makefile @@ -18,7 +18,7 @@ run: all all: ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykext.c -o mykext.o ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykextinfo.c -o mykextinfo.o - ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext + unset LD_NO_CLASSIC_LINKER_STATIC && ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext -Wl,-w 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/ld64/unit-tests/test-cases/section-labels-zero-fill/Makefile b/ld64/unit-tests/test-cases/section-labels-zero-fill/Makefile new file mode 100644 index 0000000..658a164 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-labels-zero-fill/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld resolves the magic section start/end symbols. +# + +run: all + +all: + ${CC} ${CCFLAGS} common.c -o common + otool -lv common | grep -A8 __common | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} common + ${CC} ${CCFLAGS} bss.c -o bss + otool -lv bss | grep -A8 __bss | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} bss + ${CC} ${CCFLAGS} both.c -o both + otool -lv both | grep -A8 __common | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + otool -lv both | grep -A8 __bss | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} both + +clean: + rm -f common bss both + diff --git a/ld64/unit-tests/test-cases/section-labels-zero-fill/both.c b/ld64/unit-tests/test-cases/section-labels-zero-fill/both.c new file mode 100644 index 0000000..fc1eabc --- /dev/null +++ b/ld64/unit-tests/test-cases/section-labels-zero-fill/both.c @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int bss_start __asm("section$start$__DATA$__bss"); +extern int bss_end __asm("section$end$__DATA$__bss"); +extern int common_start __asm("section$start$__DATA$__common"); +extern int common_end __asm("section$end$__DATA$__common"); + +int mycommon[2]; +static int mybss[2]; + +int main() +{ + mybss[0] = 0; + printf("bss start = %p\n", &bss_start); + printf("bss end = %p\n", &bss_end); + printf("common start = %p\n", &common_start); + printf("common end = %p\n", &common_end); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/section-labels-zero-fill/bss.c b/ld64/unit-tests/test-cases/section-labels-zero-fill/bss.c new file mode 100644 index 0000000..d9b580b --- /dev/null +++ b/ld64/unit-tests/test-cases/section-labels-zero-fill/bss.c @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int bss_start __asm("section$start$__DATA$__bss"); +extern int bss_end __asm("section$end$__DATA$__bss"); + +static int mybss[2]; + +int main() +{ + mybss[0] = 0; + printf("bss start = %p\n", &bss_start); + printf("bss end = %p\n", &bss_end); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/section-labels-zero-fill/common.c b/ld64/unit-tests/test-cases/section-labels-zero-fill/common.c new file mode 100644 index 0000000..57a1270 --- /dev/null +++ b/ld64/unit-tests/test-cases/section-labels-zero-fill/common.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int common_start __asm("section$start$__DATA$__common"); +extern int common_end __asm("section$end$__DATA$__common"); + +int mycommon[2]; + +int main() +{ + printf("common start = %p\n", &common_start); + printf("common end = %p\n", &common_end); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile b/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile new file mode 100644 index 0000000..05588b5 --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile @@ -0,0 +1,35 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that ld can link a static executable with a weak definition +# + +all: + ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test diff --git a/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c b/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c new file mode 100644 index 0000000..3e03861 --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c @@ -0,0 +1,13 @@ + + + +__attribute__((weak)) int foo() +{ + return 0; +} + + +int entry() +{ + return foo(); +} From bac7122ebc4c52b8498d814212bfb0e3df31682d Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:34:24 +0100 Subject: [PATCH 06/48] 97-14 --- ld64/ChangeLog | 112 ++++++++++++++++++ ld64/doc/man/man1/ld.1 | 8 +- ld64/ld64.xcodeproj/project.pbxproj | 1 + ld64/src/ld/MachOReaderRelocatable.hpp | 96 ++++++++++++--- ld64/src/ld/MachOWriterExecutable.hpp | 74 +++++++----- ld64/src/ld/ObjectFile.h | 2 +- ld64/src/ld/Options.cpp | 42 ++++++- ld64/src/ld/Options.h | 3 + ld64/src/ld/ld.cpp | 22 ++-- .../test-cases/relocs-asm/relocs-asm.s | 110 +++++++++++++++-- .../test-cases/tentative-to-real-r/Makefile | 45 +++++++ .../test-cases/tentative-to-real-r/test.c | 13 ++ .../test-cases/weak-def-flag/Makefile | 6 +- .../weak-def-flag/main-strip-weak.c | 16 +++ 14 files changed, 479 insertions(+), 71 deletions(-) create mode 100644 ld64/unit-tests/test-cases/tentative-to-real-r/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-to-real-r/test.c create mode 100644 ld64/unit-tests/test-cases/weak-def-flag/main-strip-weak.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog index ffe4190..38543bc 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,4 +1,116 @@ +----- Tagged ld64-97.14 + +2010-04-20 Nick Kledzik + + if last section is zero-fill don't add size to filesize total in -r mode + +----- Tagged ld64-97.13 + +2010-03-17 Nick Kledzik + + Support for iPhoneSimulator with OBJC 2.0 ABI + + +----- Tagged ld64-97.12 + +2010-03-17 Nick Kledzik + + if last section is zero-fill don't add size to filesize total in -r mode + +----- Tagged ld64-97.11 + +2010-01-26 Nick Kledzik + + Libc-624.1 causes latent ld bug + * build linker -no_pie + + +----- Tagged ld64-97.10 + +2010-01-26 Nick Kledzik + + LC_SEGMENT command 0 filesize field greater than vmsize field + * Move __DATA to end in -r mode + + +2010-01-26 Nick Kledzik + + symboled __ustring strings of just 0x0000 mis-parsed + * Parse __ustring section based on content - not just labels + + +----- Tagged ld64-97.9 + +2010-01-14 Nick Kledzik + + LC_SEGMENT command 0 filesize field greater than vmsize field + * for i386 -r mode sort __IMPORT segment before __DATA segment + + +2010-01-11 Nick Kledzik + + * fix ARM -r -d references to tentative definitions + +----- Tagged ld64-97.8 + +2009-12-08 Nick Kledzik + + many mach-o images in Barolo have MH_WEAK_DEFINES bit incorrectly set + * don't let auto-strip weak symbols set MH_WEAK_DEFINES + + +----- Tagged ld64-97.7 + +2009-11-30 Nick Kledzik + + llvmgcc now puts const short arrays in __ustring section, linker does not handle that + * Only auto-coalesce UTF16 strings that are labeled with "___utf16_string*" + + +----- Tagged ld64-97.6 + +2009-11-06 Nick Kledzik + + make -pie default for x86_64 for 10.7 and later + + +2009-10-28 Nick Kledzik + + Add a -no_pie flag + * support -no_pie and LD_NO_PIE + + +----- Tagged ld64-97.5 + +2009-10-27 Nick Kledzik + + crash when __ustring section has zero length string + * stop trying to suppress tailing 0x0000 from synthesized string used to coalese utf16 strings + + +----- Tagged ld64-97.4 + +2009-10-21 Nick Kledzik + + missing thumb bit when thumb function takes address of itself + * move toao.atom == srcao.atom test to after fromao.atom == srcao.atom test + + +----- Tagged ld64-97.3 + +2009-10-06 Nick Kledzik + + * Add missing LittleEndian::set32() in arm::kPointerDiff + +2009-10-05 Nick Kledzik + + ARM: handle pointer-diff to weak thumb that is overridden by non-weak ARM + * When parsing ARM relocations, if target is a thumb function, remove one from addend + * When writing out content for arm::kPointerDiff, add one if target is thumb + * When writing ARM_RELOC_SECTDIFF, use target offsets if they fit in function + + ----- Tagged ld64-97.2 2009-09-25 Nick Kledzik diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index 3a84e0a..b6363fc 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -308,10 +308,12 @@ This option is also called -dylib_current_version for compatibility. .Ss Options when creating a main executable .Bl -tag .It Fl pie -This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5, the OS -will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled +This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5 and later, the OS +the OS will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some -security. +security. When targeting Mac OS X 10.7 or later PIE is the default for x86_64 main executables. +.It Fl no_pie +Do not make a position independent executable (PIE). This is the default, except for x86_64 for 10.7 or later. .It Fl pagezero_size Ar size By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence will cause a bus error if a NULL pointer is dereferenced. The argument diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 55fd5ea..9c5493c 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -765,6 +765,7 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ( + "-Wl,-no_pie", "@$(DERIVED_FILE_DIR)/linker_opts", "-Wl,-exported_symbol,__mh_execute_header", ); diff --git a/ld64/src/ld/MachOReaderRelocatable.hpp b/ld64/src/ld/MachOReaderRelocatable.hpp index 0792f51..2aa7926 100644 --- a/ld64/src/ld/MachOReaderRelocatable.hpp +++ b/ld64/src/ld/MachOReaderRelocatable.hpp @@ -1075,9 +1075,6 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio char* s = &name[13]; const uint16_t* words = (uint16_t*)((char*)(owner.fHeader) + section->offset() + addr - section->addr()); unsigned int wordCount = size/2; - // note, the compiler sometimes puts trailing zeros on the end of the data - if ( E::get32(words[wordCount-1]) == 0 ) - --wordCount; bool needSeperator = false; for(unsigned int i=0; i < wordCount; ++i) { if ( needSeperator ) @@ -2044,28 +2041,76 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } } - else if ( (strcmp(sect->sectname(), "__ustring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { - // if there is a __ustring section parse it into AnonymousAtoms based on labels + else if ( (strcmp(sect->sectname(), "__ustring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) && (sect->size() != 0) ) { + // if there is a __ustring section parse it into atoms fUTF16Section = sect; + // first find all cleave points + const uint16_t* words = (uint16_t*)((char*)(fHeader) + fUTF16Section->offset()); + unsigned int wordCount = fUTF16Section->size()/2; std::vector utf16Addreses; + bool inString = false; + for (unsigned int i=0; i < wordCount; ++i) { + if ( inString ) { + if ( words[i] == 0x0000 ) { + inString = false; + } + } + else { + if ( words[i] == 0x0000 ) { + // skip over zero padding + } + else { + inString = true; + utf16Addreses.push_back(fUTF16Section->addr() + i*2); + } + } + } + utf16Addreses.push_back(fUTF16Section->addr() + sect->size()); + // build map of symbols + std::map* > symbolMap; for (int i=fSymbolCount-1; i >= 0 ; --i) { const macho_nlist

& sym = fSymbols[i]; if ( (sym.n_type() & N_STAB) == 0 ) { uint8_t type = (sym.n_type() & N_TYPE); if ( type == N_SECT ) { if ( &fSectionsStart[sym.n_sect()-1] == fUTF16Section ) { - utf16Addreses.push_back(sym.n_value()); + // rdar://problem/7429384 don't coalesce UTF16 strings unless label starts with ___utf16_string + if ( strncmp(&fStrings[sym.n_strx()], "___utf16_string", 15) != 0 ) { + symbolMap[sym.n_value()] = &sym; + // if this symbol is a string of just 0x0000, it may not be in utf16Addreses + if ( words[(sym.n_value() - sect->addr())/2] == 0x0000 ) { + for(typename std::vector::iterator sit=utf16Addreses.begin(); sit != utf16Addreses.end(); ++sit) { + if ( *sit == sym.n_value() ) { + // already in utf16Addreses + break; + } + if ( *sit > sym.n_value() ) { + // need to insert + utf16Addreses.insert(sit, sym.n_value()); + break; + } + } + } + } } } } } - utf16Addreses.push_back(fUTF16Section->addr()+fUTF16Section->size()); - std::sort(utf16Addreses.begin(), utf16Addreses.end()); + // make atom for each string for(int i=utf16Addreses.size()-2; i >=0 ; --i) { pint_t size = utf16Addreses[i+1] - utf16Addreses[i]; - AnonymousAtom* strAtom = new AnonymousAtom(*this, fUTF16Section, utf16Addreses[i], size); - fAtoms.push_back(strAtom); - fAddrToAtom[utf16Addreses[i]] = strAtom; + typename std::map* >::iterator pos = symbolMap.find(utf16Addreses[i]); + if ( pos == symbolMap.end() ) { + AnonymousAtom* strAtom = new AnonymousAtom(*this, fUTF16Section, utf16Addreses[i], size); + fAtoms.push_back(strAtom); + fAddrToAtom[utf16Addreses[i]] = strAtom; + } + else { + SymbolAtom* newAtom = new SymbolAtom(*this, pos->second, fUTF16Section); + fAtoms.push_back(newAtom); + fAddrToAtom[utf16Addreses[i]] = newAtom; + newAtom->setSize(size); + } } } } @@ -5322,6 +5367,9 @@ bool Reader::addRelocReference(const macho_section* sect, if ( weakImport ) kind = arm::kPointerWeakImport; if ( reloc->r_extern() ) { + const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; + if ( (targetSymbol->n_desc() & N_ARM_THUMB_DEF) && (pointerValue == 1) ) + pointerValue = 0; makeByNameReference(kind, srcAddr, targetName, pointerValue); } else { @@ -5345,6 +5393,7 @@ bool Reader::addRelocReference(const macho_section* sect, else { const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + int32_t addend; srcAddr = sect->addr() + sreloc->r_address(); dstAddr = sreloc->r_value(); uint32_t betterDstAddr; @@ -5429,13 +5478,28 @@ bool Reader::addRelocReference(const macho_section* sect, AtomAndOffset toao = findAtomAndOffset(dstAddr); // check for addend encoded in the section content pointerValue = LittleEndian::get32(*fixUpPtr); + addend = pointerValue - (dstAddr - nextRelocValue); + if ( toao.atom->isThumb() && (addend & 1) ) + addend &= -2; // remove thumb bit if ( (dstAddr - nextRelocValue) != pointerValue ) { - if ( toao.atom == srcao.atom ) - toao.offset += (pointerValue + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (pointerValue + nextRelocValue) - dstAddr; + if ( fromao.atom == srcao.atom ) { + if ( ((const macho_section

*)(((BaseAtom*)(srcao.atom))->getSectionRecord()))->flags() & S_ATTR_PURE_INSTRUCTIONS ) { + int pcBaseOffset = srcao.atom->isThumb() ? 4 : 8; + if ( addend == -pcBaseOffset ) { + fromao.offset -= addend; + } + else { + toao.offset += addend; + } + } + else { + toao.offset += addend; + } + } + else if ( toao.atom == srcao.atom ) + toao.offset += addend; else - fromao.offset += (dstAddr - pointerValue) - nextRelocValue; + fromao.offset -= addend; } new Reference(arm::kPointerDiff, srcao, fromao, toao); } diff --git a/ld64/src/ld/MachOWriterExecutable.hpp b/ld64/src/ld/MachOWriterExecutable.hpp index 9596733..5313669 100644 --- a/ld64/src/ld/MachOWriterExecutable.hpp +++ b/ld64/src/ld/MachOWriterExecutable.hpp @@ -1706,14 +1706,15 @@ class ObjCInfoAtom : public WriterAtom { public: ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses); + bool objcReplacementClasses, bool abi2override); virtual const char* getName() const { return "objc$info"; } virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } virtual uint64_t getSize() const { return 8; } virtual const char* getSectionName() const; virtual void copyRawContent(uint8_t buffer[]) const; private: - Segment& getInfoSegment() const; + Segment& getInfoSegment(bool abi2override) const; + bool fAbi2override; uint32_t fContent[2]; }; @@ -3280,7 +3281,8 @@ int Writer::compressedOrdinalForImortedAtom(ObjectFile::Atom* target) template ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) { - return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses)); + + return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses, fOptions.objCABIVersion2POverride())); } template @@ -4350,14 +4352,23 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere else sreloc1->set_r_type(ARM_RELOC_SECTDIFF); sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); + if ( ref->getTargetOffset() >= target.getSize() ) + sreloc1->set_r_value(target.getAddress()); + else + sreloc1->set_r_value(target.getAddress()+ref->getTargetOffset()); sreloc2->set_r_scattered(true); sreloc2->set_r_pcrel(false); sreloc2->set_r_length(2); sreloc2->set_r_type(ARM_RELOC_PAIR); sreloc2->set_r_address(0); - if ( &ref->getFromTarget() == atom ) - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + if ( &ref->getFromTarget() == atom ) { + unsigned int pcBaseOffset = atom->isThumb() ? 4 : 8; + if ( (ref->getFromTargetOffset() > pcBaseOffset) && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()-pcBaseOffset); + } + else + sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); + } else sreloc2->set_r_value(ref->getFromTarget().getAddress()); fSectionRelocs.push_back(reloc2); @@ -6250,6 +6261,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob uint32_t firstDisp; uint32_t nextDisp; uint32_t opcode = 0; + int32_t diff; bool relocateableExternal = false; bool is_bl; bool is_blx; @@ -6330,8 +6342,10 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob } break; case arm::kPointerDiff: - LittleEndian::set32(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + diff = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) + diff |= 1; + LittleEndian::set32(*fixUp, diff); break; case arm::kReadOnlyPointer: if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) @@ -6552,6 +6566,7 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co uint32_t firstDisp; uint32_t nextDisp; uint32_t opcode = 0; + int32_t diff; bool relocateableExternal = false; bool is_bl; bool is_blx; @@ -6572,7 +6587,6 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co case arm::kPointer: case arm::kReadOnlyPointer: case arm::kPointerWeakImport: - { if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content if ( this->indirectSymbolInRelocatableIsLocal(ref) ) @@ -6600,23 +6614,17 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co } } else { - // internal relocation - if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { - // pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) + // internal relocation => pointer contains target address + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - else { - // pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } + LittleEndian::set32(*fixUp, targetAddr); } break; case arm::kPointerDiff: - LittleEndian::set32(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + diff = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); + if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) + diff |= 1; + LittleEndian::set32(*fixUp, diff); break; case arm::kDtraceProbeSite: case arm::kDtraceIsEnabledSite: @@ -9624,7 +9632,10 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const cmd->set_vmaddr(sectInfo->getBaseAddress()); cmd->set_fileoff(sectInfo->fFileOffset); } - cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); + // if last section is zero-fill don't add size to filesize total + if ( !sectInfo->fAllZeroFill ) { + cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); + } cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); } sect->set_sectname(sectInfo->fSectionName); @@ -11269,8 +11280,9 @@ void SegmentSplitInfoContentAtom::encode() template -ObjCInfoAtom::ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, bool objcReplacementClasses) - : WriterAtom(writer, getInfoSegment()) +ObjCInfoAtom::ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, + bool objcReplacementClasses, bool abi2override) + : WriterAtom(writer, getInfoSegment(abi2override)), fAbi2override(abi2override) { fContent[0] = 0; uint32_t value = 0; @@ -11306,16 +11318,16 @@ void ObjCInfoAtom::copyRawContent(uint8_t buffer[]) const // objc info section is in a different segment and section for 32 vs 64 bit runtimes template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } +template <> const char* ObjCInfoAtom::getSectionName() const { return fAbi2override ? "__objc_imageinfo" : "__image_info"; } template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment() const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgObjCSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return abi2override ? Segment::fgDataSegment : Segment::fgObjCSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } +template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } diff --git a/ld64/src/ld/ObjectFile.h b/ld64/src/ld/ObjectFile.h index e3ad601..2710bfb 100644 --- a/ld64/src/ld/ObjectFile.h +++ b/ld64/src/ld/ObjectFile.h @@ -76,7 +76,7 @@ class ReaderOptions fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; - enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; + enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6, k10_7 }; enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0, k3_1, k3_2, k4_0 }; struct AliasPair { diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index f63a123..01ce10f 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -99,12 +99,13 @@ Options::Options(int argc, const char* argv[]) fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), fSharedRegionEligible(false), fPrintOrderFileStatistics(false), - fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), + fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), - fUseSimplifiedDylibReExports(false), fSaveTempFiles(false) + fUseSimplifiedDylibReExports(false), fObjCABIVersion2POverride(false), fSaveTempFiles(false) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -1248,8 +1249,11 @@ void Options::setMacOSXVersionMin(const char* version) case 6: fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; break; + case 7: + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_7; + break; default: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; + fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_7; break; } } @@ -2430,6 +2434,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-pie") == 0 ) { fPositionIndependentExecutable = true; } + else if ( strcmp(arg, "-no_pie") == 0 ) { + fDisablePositionIndependentExecutable = true; + } else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { FileInfo info = findLibrary(&arg[11], true); info.options.fReExport = true; @@ -2504,6 +2511,15 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { fReaderOptions.fOptimizeZeroFill = false; } + else if ( strcmp(arg, "-objc_abi_version") == 0 ) { + const char* version = argv[++i]; + if ( version == NULL ) + throw "-objc_abi_version missing version number"; + if ( strcmp(version, "2") == 0 ) + fObjCABIVersion2POverride = true; + else + warning("ignoring unrecognized argument (%s) to -objc_abi_version", version); + } else { throwf("unknown option: %s", arg); } @@ -2752,6 +2768,11 @@ void Options::parsePreCommandLineEnvironmentSettings() fMakeCompressedDyldInfo = false; fMakeClassicDyldInfo = true; } + // temporary until projects adopt -no_pie + if ( getenv("LD_NO_PIE") != NULL ) { + warning("LD_NO_PIE being used to disble building a position independent executable"); + fDisablePositionIndependentExecutable = true; + } sWarningsSideFilePath = getenv("LD_WARN_FILE"); } @@ -3179,6 +3200,12 @@ void Options::reconfigureDefaults() // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) fUseSimplifiedDylibReExports = true; + + // x86_64 for MacOSX 10.7 defaults to PIE + if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) + && (fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_7) ) { + fPositionIndependentExecutable = true; + } } void Options::checkIllegalOptionCombinations() @@ -3375,6 +3402,11 @@ void Options::checkIllegalOptionCombinations() if ( strncmp(name, ".objc_class_name_", 17) == 0 ) { // rdar://problem/4718189 map ObjC class names to new runtime names switch (fArchitecture) { + case CPU_TYPE_I386: + // i386 only uses new symbols when using objc2 ABI + if ( !fObjCABIVersion2POverride ) + break; + // when using objc2 ABI to same as archs below case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: @@ -3486,6 +3518,10 @@ void Options::checkIllegalOptionCombinations() if ( fPositionIndependentExecutable ) { switch ( fOutputKind ) { case Options::kDynamicExecutable: + // -no_pie anywhere on command line disable PIE + if ( fDisablePositionIndependentExecutable ) + fPositionIndependentExecutable = false; + break; case Options::kPreload: break; case Options::kDynamicLibrary: diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 2fa364e..4a06a5e 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -232,6 +232,7 @@ class Options bool markAutoDeadStripDylib() { return fMarkDeadStrippableDylib; } bool removeEHLabels() { return fReaderOptions.fNoEHLabels; } bool useSimplifiedDylibReExports() { return fUseSimplifiedDylibReExports; } + bool objCABIVersion2POverride() { return fObjCABIVersion2POverride; } private: class CStringEquals @@ -369,6 +370,7 @@ class Options bool fPrintOrderFileStatistics; bool fReadOnlyx86Stubs; bool fPositionIndependentExecutable; + bool fDisablePositionIndependentExecutable; bool fMaxMinimumHeaderPad; bool fDeadStripDylibs; bool fAllowTextRelocs; @@ -382,6 +384,7 @@ class Options bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; bool fUseSimplifiedDylibReExports; + bool fObjCABIVersion2POverride; std::vector fInitialUndefines; NameSet fAllowedUndefined; NameSet fWhyLive; diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 6b6d114..48e1e88 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -87,7 +87,7 @@ class Section : public ObjectFile::Section { public: static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded=true); - static void assignIndexes(); + static void assignIndexes(bool objfile); const char* getName() { return fSectionName; } private: Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill); @@ -109,11 +109,13 @@ class Section : public ObjectFile::Section static NameToSection fgMapping; static std::vector fgSections; static NameToOrdinal fgSegmentDiscoverOrder; + static bool fgMakingObjectFile; }; Section::NameToSection Section::fgMapping; std::vector Section::fgSections; Section::NameToOrdinal Section::fgSegmentDiscoverOrder; +bool Section::fgMakingObjectFile; Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill) : fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill) @@ -254,10 +256,10 @@ int Section::Sorter::segmentOrdinal(const char* segName) if ( strcmp(segName, "__TEXT") == 0 ) return 2; if ( strcmp(segName, "__DATA") == 0 ) - return 3; + return (fgMakingObjectFile ? 6 : 3); // __DATA is last in .o files and here in FLI if ( strcmp(segName, "__OBJC") == 0 ) return 4; - if ( strcmp(segName, "__OBJC2") == 0 ) + if ( strcmp(segName, "__IMPORT") == 0 ) return 5; if ( strcmp(segName, "__LINKEDIT") == 0 ) return INT_MAX; // linkedit segment should always sort last @@ -286,7 +288,7 @@ bool Section::Sorter::operator()(Section* left, Section* right) return left->fIndex < right->fIndex; } -void Section::assignIndexes() +void Section::assignIndexes(bool objfile) { //printf("unsorted sections:\n"); //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { @@ -294,6 +296,7 @@ void Section::assignIndexes() //} // sort it + Section::fgMakingObjectFile = objfile; std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); // assign correct section ordering to each Section object @@ -793,8 +796,10 @@ void Linker::adjustScope() //fprintf(stderr, "demote %s to hidden\n", name); } else if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - // we do have an exported weak symbol, turn WEAK_DEFINES back on - fGlobalSymbolTable.setHasExternalWeakDefinitions(true); + if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn ) { + // we do have an exported weak symbol, turn WEAK_DEFINES back on + fGlobalSymbolTable.setHasExternalWeakDefinitions(true); + } } } else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { @@ -2152,7 +2157,7 @@ ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) void Linker::sortSections() { - Section::assignIndexes(); + Section::assignIndexes(fOptions.outputKind() == Options::kObjectFile); } @@ -4124,7 +4129,8 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) ++fRequireCount; // added a tentative definition means loadUndefines() needs to continue break; case ObjectFile::Atom::kWeakDefinition: - fHasExternalWeakDefinitions = true; + if ( newAtom.getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn ) + fHasExternalWeakDefinitions = true; break; case ObjectFile::Atom::kExternalDefinition: case ObjectFile::Atom::kExternalWeakDefinition: diff --git a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s index 73c31ea..4d38f2d 100644 --- a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -30,31 +30,31 @@ .globl _test_loads _test_loads: @ PIC load of a - ldr r0, L6 + ldr r0, L100 L0: ldr r0, [pc, r0] @ PIC load of c - ldr r0, L6+4 + ldr r0, L100+4 L1: ldr r0, [pc, r0] @ sorta-absolute load of a - ldr r0, L6+8 + ldr r0, L100+8 ldr r0, [r0, #0] @ sorta-absolute load of c - ldr r0, L6+12 + ldr r0, L100+12 ldr r0, [r0, #0] @ sorta-absolute load of external - ldr r0, L6+16 + ldr r0, L100+16 ldr r0, [r0, #0] @ PIC load of a + addend ?? bx lr -L6: +L100: .long _a-(L0+8) .long _c-(L1+8) .long _a @@ -88,10 +88,12 @@ _test_branches: @ call external + addend bne _external+16 -_pointer_diffs: nop bl 1f 1: nop + + .text +_pointer_diffs: .long _foo-1b .long _foo+10-1b .long _test_branches-1b @@ -99,6 +101,98 @@ _pointer_diffs: .long (_test_branches - _test_loads) + -2097152 .long (_test_calls - _test_loads) + -2097152 + .text + .code 32 +_arm1: + bx lr +_arm2: + bx lr + .weak_definition _arm3 + .globl _arm3 + .private_extern _arm3 +_arm3: + bx lr + .weak_definition _arm4 + .globl _arm4 + .private_extern _arm4 +_arm4: + bx lr + + .code 16 + .thumb_func _thumb1 +_thumb1: + bx lr + .thumb_func _thumb2 +_thumb2: + bx lr + .weak_definition _thumb3 + .globl _thumb3 + .private_extern _thumb3 + .thumb_func _thumb3 +_thumb3: + bx lr + .weak_definition _thumb4 + .globl _thumb4 + .private_extern _thumb4 + .thumb_func _thumb4 +_thumb4: + bx lr + + .thumb_func _thumb_func_ref_test +_thumb_func_ref_test: + push {r7, lr} + add r7, sp, #0 + ldr r3, L6 +L2: add r3, pc + ldr r3, L7 +L3: add r3, pc + ldr r3, L8 +L4: add r3, pc + ldr r3, L9 +L5: add r3, pc + pop {r7, pc} + .align 2 +L6: .long _thumb1-(L2+4) +L7: .long _thumb2-(L3+4) +L7a:.long _thumb3-(L3+4) +L7b:.long _thumb4-(L3+4) +L8: .long _arm1-(L4+4) +L9: .long _arm2-(L5+4) +L9a:.long _arm3-(L5+4) +L9b:.long _arm4-(L5+4) + + .code 32 + .align 2 +_arm_func_ref_test: + push {r7, lr} + add r7, sp, #0 + ldr r3, L16 +L12:add r3, pc + ldr r3, L17 +L13:add r3, pc + ldr r3, L18 +L14:add r3, pc + ldr r3, L19 +L15:add r3, pc + pop {r7, pc} + .align 2 +L16: .long _thumb1-(L12+8) +L17: .long _thumb2-(L13+8) +L17a: .long _thumb3-(L3+8) +L17b: .long _thumb4-(L3+8) +L18: .long _arm1-(L14+8) +L19: .long _arm2-(L15+8) +L19a: .long _arm3-(L15+8) +L19b: .long _arm4-(L15+8) + + .section __DATA,__const +_myVTable: + .long _thumb1 + .long _thumb2 + .long _thumb3 + .long _arm1 + .long _arm2 + #endif #if __ppc__ || __ppc64__ @@ -410,6 +504,8 @@ Llocal2: .long _test_branches - . .long _test_branches - . + 8 .long _test_branches - . - 8 + .long 0 + .long 0 #if __ppc64__ .quad Llocal2-_test_branches #endif diff --git a/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile b/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile new file mode 100644 index 0000000..df0ae07 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile @@ -0,0 +1,45 @@ +## +# 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 + + +# +# Test that references to commons survive -r -d +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o + ${OBJECTDUMP} -no_content test.${ARCH}.o | grep -v kind: | grep -v section: > test.${ARCH}.o.dump + + ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o + ${OBJECTDUMP} -no_content test-r-d.${ARCH}.o | grep -v kind: | grep -v section: > test-r-d.${ARCH}.o.dump + + ${PASS_IFF} diff test.${ARCH}.o.dump test-r-d.${ARCH}.o.dump + + +clean: + rm -rf *.o *.dump + diff --git a/ld64/unit-tests/test-cases/tentative-to-real-r/test.c b/ld64/unit-tests/test-cases/tentative-to-real-r/test.c new file mode 100644 index 0000000..27d22e7 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-to-real-r/test.c @@ -0,0 +1,13 @@ + +void foo() {} + +int a; +int b; +int c; + + + +int* pa = &a; +int* pb = &b; +int* pc = &c; + diff --git a/ld64/unit-tests/test-cases/weak-def-flag/Makefile b/ld64/unit-tests/test-cases/weak-def-flag/Makefile index 71d5db6..e813f49 100644 --- a/ld64/unit-tests/test-cases/weak-def-flag/Makefile +++ b/ld64/unit-tests/test-cases/weak-def-flag/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2008-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -40,7 +40,9 @@ all: otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_STDIN} ${CC} ${CCFLAGS} main.c -o main -Wl,-exported_symbol,_my_weak otool -hv main | grep WEAK_DEFINES | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main-strip-weak.c -o main-strip-weak + otool -hv main-strip-weak | grep WEAK_DEFINES | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} main clean: - rm main + rm main main-strip-weak \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/weak-def-flag/main-strip-weak.c b/ld64/unit-tests/test-cases/weak-def-flag/main-strip-weak.c new file mode 100644 index 0000000..cb40513 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-flag/main-strip-weak.c @@ -0,0 +1,16 @@ +#include + +// the 'l' prefix makes this an auto-strip symbol +void my_auto_strip_weak() __asm ( "lautostrip" ); + +void __attribute__((weak)) my_auto_strip_weak() +{ + +} + +int main() +{ + my_auto_strip_weak(); + return 0; +} + From a1080760aae69e71c3a957685e779377bd5d7677 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:35:08 +0100 Subject: [PATCH 07/48] 97-17 --- ld64/ChangeLog | 23 +++++++++++ ld64/src/ld/LTOReader.hpp | 2 +- ld64/src/ld/MachOReaderDylib.hpp | 2 +- ld64/src/ld/Options.cpp | 3 ++ ld64/src/ld/ld.cpp | 71 +------------------------------- 5 files changed, 29 insertions(+), 72 deletions(-) diff --git a/ld64/ChangeLog b/ld64/ChangeLog index 38543bc..ebd5628 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,4 +1,27 @@ +----- Tagged ld64-97.17 + +2010-09-07 Nick Kledzik + + ld mis-handling std::tr1::anonymous symbols + Remove support for ordering gcc-4.0 compiled anonymous namespace symbols + +2010-09-07 Nick Kledzik + + RedGarnet's linker does not honor $ld$hide for umbrella libraries + +----- Tagged ld64-97.16 + +2010-08-10 Nick Kledzik + + add -demangle noop to ld64-97 + +----- Tagged ld64-97.15 + +2010-08-10 Nick Kledzik + + linker crash when using LTO in lto::Atom::getSymbolTableInclusion() + ----- Tagged ld64-97.14 2010-04-20 Nick Kledzik diff --git a/ld64/src/ld/LTOReader.hpp b/ld64/src/ld/LTOReader.hpp index 9d00b00..2e560cc 100644 --- a/ld64/src/ld/LTOReader.hpp +++ b/ld64/src/ld/LTOReader.hpp @@ -145,7 +145,7 @@ class Atom : public ObjectFile::Atom Scope getScope() const { return (fRealAtom ? fRealAtom->getScope() : fScope); } DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } SymbolTableInclusion getSymbolTableInclusion() const - { return fRealAtom->getSymbolTableInclusion(); } + { return (fRealAtom ? fRealAtom->getSymbolTableInclusion() : ObjectFile::Atom::kSymbolTableIn); } bool dontDeadStrip() const { return false; } bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); } bool isThumb() const { return false; } diff --git a/ld64/src/ld/MachOReaderDylib.hpp b/ld64/src/ld/MachOReaderDylib.hpp index 1d8f89b..5a6c553 100644 --- a/ld64/src/ld/MachOReaderDylib.hpp +++ b/ld64/src/ld/MachOReaderDylib.hpp @@ -832,7 +832,7 @@ void Reader::processIndirectLibraries(DylibHander* handler) ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); if ( isPublicLocation(child->getInstallPath()) ) { // promote this child to be automatically added as a direct dependent if this already is - if ( this->explicitlyLinked() || this->implicitlyLinked() ) { + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,child->getInstallPath()) == 0) ) { //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); ((Reader*)child)->setImplicitlyLinked(); } diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 01ce10f..492d140 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -2520,6 +2520,9 @@ void Options::parse(int argc, const char* argv[]) else warning("ignoring unrecognized argument (%s) to -objc_abi_version", version); } + else if ( strcmp(arg, "-demangle") == 0 ) { + // add -demangle noop to ld64-97 + } else { throwf("unknown option: %s", arg); } diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 48e1e88..115240e 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -2025,32 +2025,6 @@ static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeaf } -static bool usesAnonymousNamespace(const char* symbol) -{ - return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) ); -} - - -// -// convert: -// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv -// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv -// -static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[]) -{ - const char* globPtr = strstr(inSymbol, "_GLOBAL__N_"); - while ( isdigit(*(--globPtr)) ) - ; // loop - char* endptr; - unsigned long length = strtoul(globPtr+1, &endptr, 10); - const char* globEndPtr = endptr + length; - int startLen = globPtr-inSymbol+1; - memcpy(outSymbol, inSymbol, startLen); - outSymbol[startLen] = '-'; - strcpy(&outSymbol[startLen+1], globEndPtr); -} - - ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) { ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); @@ -2061,7 +2035,6 @@ ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) else { // slow case. The requested symbol is not in symbol table, so might be static function static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; - static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace; static bool built = false; // build a hash_map the first time if ( !built ) { @@ -2069,18 +2042,7 @@ ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) atom = *it; const char* name = atom->getName(); if ( name != NULL) { - if ( usesAnonymousNamespace(name) ) { - // symbol that uses anonymous namespace - char canonicalName[strlen(name)+2]; - canonicalizeAnonymousName(name, canonicalName); - const char* hashName = strdup(canonicalName); - SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName); - if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() ) - hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom; - else - hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL - } - else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { + if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { // static function or data SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) @@ -2119,37 +2081,6 @@ ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) } } - // look for name in hashTableOfSymbolsWithAnonymousNamespace - if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) { - // symbol that uses anonymous namespace - char canonicalName[strlen(orderedSymbol.symbolName)+2]; - canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName); - SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName); - if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) { - if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { - //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName); - return pos->second; - } - if ( pos->second == NULL ) - // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - const char* name = atom->getName(); - if ( (name != NULL) && usesAnonymousNamespace(name) ) { - char canonicalAtomName[strlen(name)+2]; - canonicalizeAnonymousName(name, canonicalAtomName); - if ( strcmp(canonicalAtomName, canonicalName) == 0 ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { - if ( fOptions.printOrderFileStatistics() ) - warning("%s specified in order_file but it exists in multiple .o files. " - "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); - return atom; - } - } - } - } - } - } } return NULL; } From 07406bee3ce062182a01e0f5b4ce349a991e10fd Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:36:23 +0100 Subject: [PATCH 08/48] 123.2 --- ld64/ChangeLog | 5227 +------ ld64/doc/man/man1/dyldinfo.1 | 10 +- ld64/doc/man/man1/ld.1 | 105 +- ld64/ld64.xcodeproj/project.pbxproj | 507 +- ld64/src/abstraction/FileAbstraction.hpp | 4 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 104 +- ld64/src/abstraction/MachOTrie.hpp | 126 +- ld64/src/ld/Architectures.hpp | 34 +- ld64/src/ld/ArchiveReader.hpp | 467 - ld64/src/ld/ExecutableFile.h | 75 - ld64/src/ld/HeaderAndLoadCommands.hpp | 1391 ++ ld64/src/ld/InputFiles.cpp | 943 ++ ld64/src/ld/InputFiles.h | 115 + ld64/src/ld/LTOReader.hpp | 742 - ld64/src/ld/LinkEdit.hpp | 1306 ++ ld64/src/ld/LinkEditClassic.hpp | 2542 ++++ ld64/src/ld/MachOReaderDylib.hpp | 1048 -- ld64/src/ld/MachOReaderRelocatable.hpp | 6125 -------- ld64/src/ld/MachOWriterExecutable.hpp | 12114 ---------------- ld64/src/ld/ObjectFile.h | 385 - ld64/src/ld/OpaqueSection.hpp | 199 - ld64/src/ld/Options.cpp | 1297 +- ld64/src/ld/Options.h | 323 +- ld64/src/ld/OutputFile.cpp | 3503 +++++ ld64/src/ld/OutputFile.h | 289 + ld64/src/ld/Resolver.cpp | 1402 ++ ld64/src/ld/Resolver.h | 148 + ld64/src/ld/SymbolTable.cpp | 721 + ld64/src/ld/SymbolTable.h | 165 + ld64/src/ld/debugline.c | 44 +- ld64/src/ld/debugline.h | 7 +- ld64/src/ld/ld.cpp | 4612 +----- ld64/src/ld/ld.hpp | 710 + ld64/src/ld/lto_file.hpp | 642 + ld64/src/ld/parsers/archive_file.cpp | 522 + ld64/src/ld/parsers/archive_file.h | 48 + ld64/src/ld/parsers/lto_file.cpp | 870 ++ ld64/src/ld/parsers/lto_file.h | 73 + ld64/src/ld/parsers/macho_dylib_file.cpp | 996 ++ ld64/src/ld/parsers/macho_dylib_file.h | 41 + .../src/ld/parsers/macho_relocatable_file.cpp | 6552 +++++++++ ld64/src/ld/parsers/macho_relocatable_file.h | 58 + ld64/src/ld/parsers/opaque_section_file.cpp | 106 + ld64/src/ld/parsers/opaque_section_file.h | 46 + ld64/src/ld/passes/branch_island.cpp | 623 + ld64/src/ld/passes/branch_island.h | 45 + ld64/src/ld/passes/branch_shim.cpp | 314 + ld64/src/ld/passes/branch_shim.h | 45 + ld64/src/ld/passes/compact_unwind.cpp | 794 + ld64/src/ld/passes/compact_unwind.h | 45 + ld64/src/ld/passes/dtrace_dof.cpp | 339 + ld64/src/ld/passes/dtrace_dof.h | 45 + ld64/src/ld/passes/dylibs.cpp | 86 + ld64/src/ld/passes/dylibs.h | 45 + ld64/src/ld/passes/got.cpp | 276 + ld64/src/ld/passes/got.h | 45 + ld64/src/ld/passes/huge.cpp | 113 + ld64/src/ld/passes/huge.h | 45 + ld64/src/ld/passes/objc.cpp | 1174 ++ ld64/src/ld/passes/objc.h | 45 + ld64/src/ld/passes/order_file.cpp | 531 + ld64/src/ld/passes/order_file.h | 45 + ld64/src/ld/passes/stubs/make_stubs.h | 39 + ld64/src/ld/passes/stubs/stub_arm.hpp | 416 + ld64/src/ld/passes/stubs/stub_arm_classic.hpp | 155 + ld64/src/ld/passes/stubs/stub_ppc_classic.hpp | 190 + ld64/src/ld/passes/stubs/stub_x86.hpp | 347 + ld64/src/ld/passes/stubs/stub_x86_64.hpp | 366 + .../ld/passes/stubs/stub_x86_64_classic.hpp | 165 + ld64/src/ld/passes/stubs/stub_x86_classic.hpp | 163 + ld64/src/ld/passes/stubs/stubs.cpp | 385 + ld64/src/ld/passes/tlvp.cpp | 303 + ld64/src/ld/passes/tlvp.h | 45 + ld64/src/other/ObjectDump.cpp | 888 +- ld64/src/other/dyldinfo.cpp | 390 +- ld64/src/other/machochecker.cpp | 187 +- ld64/src/other/rebase.cpp | 28 +- ld64/src/other/unwinddump.cpp | 54 +- ld64/unit-tests/bin/result-filter.pl | 18 + ld64/unit-tests/include/common.makefile | 28 +- ld64/unit-tests/run-all-unit-tests | 1 + .../test-cases/16-byte-alignment/Makefile | 2 +- ld64/unit-tests/test-cases/Lpath/Makefile | 43 + ld64/unit-tests/test-cases/Lpath/foo.c | 2 + ld64/unit-tests/test-cases/Lpath/main.c | 8 + .../test-cases/absolute-symbol/abs.s | 6 +- .../test-cases/alias-command-line/Makefile | 4 +- .../test-cases/alias-command-line/aliases.s | 7 + .../test-cases/alias-command-line/aliases.txt | 1 + .../test-cases/allow_heap_execute/Makefile | 35 + .../test-cases/allow_heap_execute/main.c | 4 + .../archive-ObjC-unexported/Makefile | 44 + .../test-cases/archive-ObjC-unexported/bar.m | 14 + .../test-cases/archive-ObjC-unexported/foo.m | 8 + .../test-cases/archive-ObjC-unexported/main.m | 9 + .../archive-ObjC-unexported/main.nexp | 1 + .../test-cases/archive-image_info/Makefile | 50 + .../test-cases/archive-image_info/main.m | 36 + .../test-cases/bind_at_load/Makefile | 41 + ld64/unit-tests/test-cases/bind_at_load/bar.c | 2 + ld64/unit-tests/test-cases/bind_at_load/foo.c | 3 + .../unit-tests/test-cases/bind_at_load/main.c | 14 + .../test-cases/blank-stubs/Makefile | 4 +- .../test-cases/branch-distance/Makefile | 4 +- .../test-cases/branch-interworking/Makefile | 42 + .../test-cases/branch-interworking/myarm.s | 19 + .../test-cases/branch-interworking/mythumb.s | 20 + .../test-cases/cfstring-and-cstring/Makefile | 14 + .../test-cases/cfstring-and-cstring/bar.c | 14 + .../test-cases/cfstring-and-cstring/foo.c | 12 + .../test-cases/cfstring-utf16/Makefile | 4 +- .../coalesce_weak_def_in_dylib/Makefile | 2 +- .../test-cases/commons-order/Makefile | 2 +- .../test-cases/cstring-alt-segment/Makefile | 1 + .../test-cases/cstring-alt-segment/custom.s | 4 +- .../test-cases/cstring-empty-labeled/Makefile | 43 + .../test-cases/cstring-empty-labeled/foo.s | 21 + .../test-cases/custom-segment-layout/Makefile | 16 + .../test-cases/custom-segment-layout/main.c | 8 + .../test-cases/custom-segment-layout/zero.s | 11 + .../dead_strip-archive-duplicate-def/Makefile | 43 + .../dead_strip-archive-duplicate-def/bar.c | 8 + .../dead_strip-archive-duplicate-def/foo.c | 10 + .../dead_strip-archive-duplicate-def/main.c | 14 + .../dead_strip-archive-weak-override/Makefile | 43 + .../dead_strip-archive-weak-override/bar.c | 6 + .../dead_strip-archive-weak-override/foo.c | 15 + .../dead_strip-archive-weak-override/main.c | 25 + .../unit-tests/test-cases/dead_strip/Makefile | 3 +- ld64/unit-tests/test-cases/demangle/Makefile | 45 + ld64/unit-tests/test-cases/demangle/main.cxx | 42 + .../test-cases/dependency-logging/Makefile | 46 + .../test-cases/dependency-logging/foo.c | 1 + .../test-cases/dependency-logging/main.c | 7 + .../test-cases/dtrace-old-probes/Makefile | 36 + .../test-cases/dtrace-old-probes/main.c | 49 + .../dtrace-static-probes-coalescing/x.cxx | 2 +- .../test-cases/dtrace-static-probes/Makefile | 2 +- .../test-cases/dwarf-debug-notes-r/Makefile | 33 +- .../dwarf-debug-notes-uuid/Makefile | 45 + .../test-cases/dwarf-debug-notes-uuid/main.c | 23 + .../test-cases/dwarf-debug-notes/Makefile | 24 +- .../dwarf-debug-notes/expected-stabs | 6 +- .../test-cases/dwarf-strip-objc/Makefile | 29 + .../test-cases/dwarf-strip-objc/hello.m | 8 + .../test-cases/dwarf-strip/Makefile | 10 +- .../test-cases/dylib-aliases/Makefile | 2 +- .../test-cases/dylib-re-export-cycle/Makefile | 17 +- .../test-cases/dylib-upward/Makefile | 43 + ld64/unit-tests/test-cases/dylib-upward/bar.c | 1 + ld64/unit-tests/test-cases/dylib-upward/foo.c | 6 + .../unit-tests/test-cases/efi-basic/LibTest.c | 13 + ld64/unit-tests/test-cases/efi-basic/Makefile | 73 + .../test-cases/efi-basic/MtocTest.c | 21 + .../test-cases/efi-basic/mtoctest.py | 135 + .../test-cases/eh-coalescing/bar.cxx | 2 +- .../test-cases/eh-stripped-symbols/Makefile | 8 +- .../test-cases/eh-stripped-symbols/main.cxx | 51 +- .../test-cases/empty-dylib/Makefile | 14 +- .../exported-symbols-dead_strip/Makefile | 39 + .../exported-symbols-dead_strip/foo.c | 34 + .../exported-symbols-dead_strip/foo.exp | 3 + .../test-cases/function-starts/Makefile | 29 + .../test-cases/function-starts/main.c | 10 + ld64/unit-tests/test-cases/hidden-r/Makefile | 41 + ld64/unit-tests/test-cases/hidden-r/foo.c | 5 + ld64/unit-tests/test-cases/hidden-r/main.c | 12 + .../unit-tests/test-cases/kext-basic/mykext.c | 2 + .../label-on-end-of-section/Makefile | 2 +- ld64/unit-tests/test-cases/large-bss/Makefile | 38 + ld64/unit-tests/test-cases/large-bss/test.s | 33 + .../test-cases/lazy-dylib-objc/Makefile | 2 +- .../unit-tests/test-cases/lazy-dylib/Makefile | 4 +- .../test-cases/literals-labels/Makefile | 21 + .../test-cases/literals-labels/literals.s | 77 + .../test-cases/llvm-integration/Makefile | 10 +- .../test-cases/llvm-integration/a17.c | 8 + .../test-cases/lto-dead_strip-objc/Makefile | 41 + .../test-cases/lto-dead_strip-objc/foo.m | 27 + .../lto-dead_strip-some-hidden/Makefile | 41 + .../lto-dead_strip-some-hidden/bar.c | 19 + .../lto-dead_strip-tentative/Makefile | 44 + .../test-cases/lto-dead_strip-tentative/bar.c | 4 + .../test-cases/lto-dead_strip-tentative/baz.c | 5 + .../test-cases/lto-dead_strip-tentative/foo.c | 4 + .../lto-dead_strip-tentative/main.c | 12 + .../test-cases/lto-dead_strip-unused/Makefile | 43 + .../test-cases/lto-dead_strip-unused/bar.c | 15 + .../test-cases/lto-dead_strip-unused/main.c | 8 + .../test-cases/lto-objc-image-info/Makefile | 53 + .../test-cases/lto-objc-image-info/main.m | 9 + .../test-cases/lto-object_path/Makefile | 42 + .../test-cases/lto-object_path/main.c | 15 + .../test-cases/main-stripped/Makefile | 12 +- .../test-cases/main-stripped/main.c | 5 +- .../test-cases/main-stripped/main.exp | 1 + ld64/unit-tests/test-cases/no-uuid/Makefile | 4 +- .../unit-tests/test-cases/non-lazy-r/Makefile | 19 +- ld64/unit-tests/test-cases/non-lazy-r/foo.c | 3 + ld64/unit-tests/test-cases/non-lazy-r/other.c | 1 + .../test-cases/non-lazy-sections-r/Makefile | 55 + .../test-cases/non-lazy-sections-r/foo.s | 34 + ld64/unit-tests/test-cases/objc-abi/Makefile | 49 + ld64/unit-tests/test-cases/objc-abi/test.m | 17 + .../test-cases/objc-category-archive/Makefile | 27 + .../test-cases/objc-category-archive/main.m | 6 + .../test-cases/objc-category-archive/test.m | 21 + .../objc-category-optimize-load/Makefile | 52 + .../objc-category-optimize-load/cat1.m | 15 + .../objc-category-optimize-load/foo.m | 9 + .../objc-category-optimize/Makefile | 71 + .../test-cases/objc-category-optimize/cat1.m | 29 + .../test-cases/objc-category-optimize/cat2.m | 37 + .../test-cases/objc-category-optimize/foo.m | 17 + .../test-cases/objc-class-alias/Makefile | 44 + .../test-cases/objc-class-alias/test.m | 13 + .../test-cases/objc-gc-checks/Makefile | 52 +- .../test-cases/objc-gc-checks/none.c | 1 + .../test-cases/objc-literal-pointers/Makefile | 4 +- .../test-cases/objc-properties/Makefile | 19 + .../test-cases/objc-properties/test.m | 49 + .../test-cases/objc-visibility/Makefile | 42 + .../test-cases/objc-visibility/bar.h | 8 + .../test-cases/objc-visibility/bar.m | 13 + .../test-cases/objc-visibility/foo.h | 8 + .../test-cases/objc-visibility/foo.m | 14 + .../test-cases/order_file-ans/main.cxx | 6 +- .../unit-tests/test-cases/order_file/Makefile | 16 +- ld64/unit-tests/test-cases/order_file/extra.s | 8 +- .../test-cases/order_file/main4.expected | 6 + .../test-cases/order_file/main4.order | 4 + .../test-cases/re-export-and-use/Makefile | 54 + .../test-cases/re-export-and-use/bar.c | 5 + .../test-cases/re-export-and-use/baz.c | 5 + .../test-cases/re-export-and-use/foo.c | 16 + .../test-cases/re-export-and-use/pub.c | 5 + .../test-cases/re-export-layers/Makefile | 61 + .../test-cases/re-export-layers/bar.c | 5 + .../test-cases/re-export-layers/baz.c | 5 + .../test-cases/re-export-layers/foo.c | 4 + .../test-cases/re-export-layers/main.c | 15 + .../test-cases/re-export-symbol/Makefile | 56 + .../test-cases/re-export-symbol/bar.c | 5 + .../test-cases/re-export-symbol/foo.c | 4 + .../test-cases/re-export-symbol/foo.exp | 2 + .../test-cases/re-export-symbol/foo2.exp | 2 + .../test-cases/re-export-symbol/main1.c | 9 + .../test-cases/re-export-symbol/main2.c | 10 + .../test-cases/read-only-relocs/Makefile | 44 +- .../test-cases/read-only-relocs/main.c | 10 + .../test-cases/read-only-relocs/test_bind.c | 10 + .../test-cases/read-only-relocs/test_rebase.c | 8 + .../test-cases/rebase-basic/Makefile | 6 +- .../test-cases/reexport_symbols_list/Makefile | 61 + .../test-cases/reexport_symbols_list/bar.c | 10 + .../test-cases/reexport_symbols_list/bart.exp | 1 + .../test-cases/reexport_symbols_list/foo.c | 4 + .../test-cases/reexport_symbols_list/foo.exp | 1 + .../test-cases/reexport_symbols_list/junk.exp | 1 + .../test-cases/reexport_symbols_list/main1.c | 9 + .../test-cases/relocs-asm/relocs-asm.s | 171 +- .../test-cases/relocs-neg-from-local/Makefile | 6 +- .../test-cases/sectcreate-dead_strip/Makefile | 40 + .../test-cases/sectcreate-dead_strip/main.c | 5 + .../sectcreate-dead_strip/sect_content | 1 + .../test-cases/segment-labels/Makefile | 40 + .../test-cases/segment-labels/main.c | 50 + .../test-cases/segment-labels/test.c | 12 + .../test-cases/stabs-coalesce/Makefile | 6 +- .../static-executable-weak-defines/Makefile | 3 +- .../static-executable-weak-defines/test.c | 3 + .../test-cases/static-executable/test.c | 7 +- .../symbol-hiding-umbrella/Makefile | 48 + .../test-cases/symbol-hiding-umbrella/bar.c | 1 + .../test-cases/symbol-hiding-umbrella/foo.c | 7 + .../test-cases/symbol-hiding-umbrella/main.c | 11 + .../test-cases/symbol-resolver-basic/Makefile | 29 + .../test-cases/symbol-resolver-basic/foo.c | 38 + .../symbol-resolver-hidden/Makefile | 25 + .../test-cases/symbol-resolver-hidden/foo.c | 42 + .../test-cases/tentative-to-real-r/Makefile | 4 +- ld64/unit-tests/test-cases/tlv-basic/Makefile | 39 + ld64/unit-tests/test-cases/tlv-basic/get.s | 162 + ld64/unit-tests/test-cases/tlv-basic/main.c | 39 + ld64/unit-tests/test-cases/tlv-dylib/Makefile | 46 + ld64/unit-tests/test-cases/tlv-dylib/foo.s | 33 + ld64/unit-tests/test-cases/tlv-dylib/getbar.s | 29 + ld64/unit-tests/test-cases/tlv-dylib/getfoo.s | 29 + .../test-cases/tlv-dylib/main.c} | 27 +- .../test-cases/umbrella-dylib/Makefile | 54 + ld64/unit-tests/test-cases/umbrella-dylib/a.c | 11 + ld64/unit-tests/test-cases/umbrella-dylib/b.c | 7 + ld64/unit-tests/test-cases/umbrella-dylib/c.c | 11 + .../test-cases/umbrella-dylib/main.c | 8 + .../undefined-dynamic-lookup/Makefile | 21 +- .../test-cases/unstrippable-symbols/Makefile | 17 + .../test-cases/unstrippable-symbols/foo.c | 24 + ld64/unit-tests/test-cases/utf16-nul/Makefile | 41 + ld64/unit-tests/test-cases/utf16-nul/other.s | 12 + .../unit-tests/test-cases/utf16-nul/withnul.s | 8 + .../test-cases/visibility-warning/Makefile | 32 +- .../test-cases/weak-def-auto-hide/Makefile | 57 + .../test-cases/weak-def-auto-hide/main.c | 15 + .../test-cases/weak-def-auto-hide/other.s | 15 + .../test-cases/weak-def-flag/Makefile | 2 +- .../unit-tests/test-cases/weak-force/Makefile | 50 + ld64/unit-tests/test-cases/weak-force/foo.c | 17 + .../unit-tests/test-cases/weak-force/foo1.exp | 2 + .../unit-tests/test-cases/weak-force/foo2.exp | 2 + .../test-cases/weak_import-force/main.c | 13 +- .../test-cases/weak_import-local/Makefile | 40 + .../test-cases/weak_import-local/foo.c | 7 + .../test-cases/weak_import-local/foo.h | 6 + .../test-cases/weak_import-local/main.c | 18 + ld64/unit-tests/test-cases/weak_import/main.c | 7 +- ld64/unit-tests/test-cases/zero-fill2/test.c | 7 +- 316 files changed, 39598 insertions(+), 30933 deletions(-) delete mode 100644 ld64/src/ld/ArchiveReader.hpp delete mode 100644 ld64/src/ld/ExecutableFile.h create mode 100644 ld64/src/ld/HeaderAndLoadCommands.hpp create mode 100644 ld64/src/ld/InputFiles.cpp create mode 100644 ld64/src/ld/InputFiles.h delete mode 100644 ld64/src/ld/LTOReader.hpp create mode 100644 ld64/src/ld/LinkEdit.hpp create mode 100644 ld64/src/ld/LinkEditClassic.hpp delete mode 100644 ld64/src/ld/MachOReaderDylib.hpp delete mode 100644 ld64/src/ld/MachOReaderRelocatable.hpp delete mode 100644 ld64/src/ld/MachOWriterExecutable.hpp delete mode 100644 ld64/src/ld/ObjectFile.h delete mode 100644 ld64/src/ld/OpaqueSection.hpp create mode 100644 ld64/src/ld/OutputFile.cpp create mode 100644 ld64/src/ld/OutputFile.h create mode 100644 ld64/src/ld/Resolver.cpp create mode 100644 ld64/src/ld/Resolver.h create mode 100644 ld64/src/ld/SymbolTable.cpp create mode 100644 ld64/src/ld/SymbolTable.h create mode 100644 ld64/src/ld/ld.hpp create mode 100644 ld64/src/ld/lto_file.hpp create mode 100644 ld64/src/ld/parsers/archive_file.cpp create mode 100644 ld64/src/ld/parsers/archive_file.h create mode 100644 ld64/src/ld/parsers/lto_file.cpp create mode 100644 ld64/src/ld/parsers/lto_file.h create mode 100644 ld64/src/ld/parsers/macho_dylib_file.cpp create mode 100644 ld64/src/ld/parsers/macho_dylib_file.h create mode 100644 ld64/src/ld/parsers/macho_relocatable_file.cpp create mode 100644 ld64/src/ld/parsers/macho_relocatable_file.h create mode 100644 ld64/src/ld/parsers/opaque_section_file.cpp create mode 100644 ld64/src/ld/parsers/opaque_section_file.h create mode 100644 ld64/src/ld/passes/branch_island.cpp create mode 100644 ld64/src/ld/passes/branch_island.h create mode 100644 ld64/src/ld/passes/branch_shim.cpp create mode 100644 ld64/src/ld/passes/branch_shim.h create mode 100644 ld64/src/ld/passes/compact_unwind.cpp create mode 100644 ld64/src/ld/passes/compact_unwind.h create mode 100644 ld64/src/ld/passes/dtrace_dof.cpp create mode 100644 ld64/src/ld/passes/dtrace_dof.h create mode 100644 ld64/src/ld/passes/dylibs.cpp create mode 100644 ld64/src/ld/passes/dylibs.h create mode 100644 ld64/src/ld/passes/got.cpp create mode 100644 ld64/src/ld/passes/got.h create mode 100644 ld64/src/ld/passes/huge.cpp create mode 100644 ld64/src/ld/passes/huge.h create mode 100644 ld64/src/ld/passes/objc.cpp create mode 100644 ld64/src/ld/passes/objc.h create mode 100644 ld64/src/ld/passes/order_file.cpp create mode 100644 ld64/src/ld/passes/order_file.h create mode 100644 ld64/src/ld/passes/stubs/make_stubs.h create mode 100644 ld64/src/ld/passes/stubs/stub_arm.hpp create mode 100644 ld64/src/ld/passes/stubs/stub_arm_classic.hpp create mode 100644 ld64/src/ld/passes/stubs/stub_ppc_classic.hpp create mode 100644 ld64/src/ld/passes/stubs/stub_x86.hpp create mode 100644 ld64/src/ld/passes/stubs/stub_x86_64.hpp create mode 100644 ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp create mode 100644 ld64/src/ld/passes/stubs/stub_x86_classic.hpp create mode 100644 ld64/src/ld/passes/stubs/stubs.cpp create mode 100644 ld64/src/ld/passes/tlvp.cpp create mode 100644 ld64/src/ld/passes/tlvp.h create mode 100644 ld64/unit-tests/test-cases/Lpath/Makefile create mode 100644 ld64/unit-tests/test-cases/Lpath/foo.c create mode 100644 ld64/unit-tests/test-cases/Lpath/main.c create mode 100644 ld64/unit-tests/test-cases/allow_heap_execute/Makefile create mode 100644 ld64/unit-tests/test-cases/allow_heap_execute/main.c create mode 100644 ld64/unit-tests/test-cases/archive-ObjC-unexported/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-ObjC-unexported/bar.m create mode 100644 ld64/unit-tests/test-cases/archive-ObjC-unexported/foo.m create mode 100644 ld64/unit-tests/test-cases/archive-ObjC-unexported/main.m create mode 100644 ld64/unit-tests/test-cases/archive-ObjC-unexported/main.nexp create mode 100644 ld64/unit-tests/test-cases/archive-image_info/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-image_info/main.m create mode 100644 ld64/unit-tests/test-cases/bind_at_load/Makefile create mode 100644 ld64/unit-tests/test-cases/bind_at_load/bar.c create mode 100644 ld64/unit-tests/test-cases/bind_at_load/foo.c create mode 100644 ld64/unit-tests/test-cases/bind_at_load/main.c create mode 100644 ld64/unit-tests/test-cases/branch-interworking/Makefile create mode 100644 ld64/unit-tests/test-cases/branch-interworking/myarm.s create mode 100644 ld64/unit-tests/test-cases/branch-interworking/mythumb.s create mode 100644 ld64/unit-tests/test-cases/cfstring-and-cstring/Makefile create mode 100644 ld64/unit-tests/test-cases/cfstring-and-cstring/bar.c create mode 100644 ld64/unit-tests/test-cases/cfstring-and-cstring/foo.c create mode 100644 ld64/unit-tests/test-cases/cstring-empty-labeled/Makefile create mode 100644 ld64/unit-tests/test-cases/cstring-empty-labeled/foo.s create mode 100644 ld64/unit-tests/test-cases/custom-segment-layout/Makefile create mode 100644 ld64/unit-tests/test-cases/custom-segment-layout/main.c create mode 100644 ld64/unit-tests/test-cases/custom-segment-layout/zero.s create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-archive-weak-override/main.c create mode 100644 ld64/unit-tests/test-cases/demangle/Makefile create mode 100644 ld64/unit-tests/test-cases/demangle/main.cxx create mode 100644 ld64/unit-tests/test-cases/dependency-logging/Makefile create mode 100644 ld64/unit-tests/test-cases/dependency-logging/foo.c create mode 100644 ld64/unit-tests/test-cases/dependency-logging/main.c create mode 100644 ld64/unit-tests/test-cases/dtrace-old-probes/Makefile create mode 100644 ld64/unit-tests/test-cases/dtrace-old-probes/main.c create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c create mode 100644 ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-strip-objc/hello.m create mode 100644 ld64/unit-tests/test-cases/dylib-upward/Makefile create mode 100644 ld64/unit-tests/test-cases/dylib-upward/bar.c create mode 100644 ld64/unit-tests/test-cases/dylib-upward/foo.c create mode 100644 ld64/unit-tests/test-cases/efi-basic/LibTest.c create mode 100644 ld64/unit-tests/test-cases/efi-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/efi-basic/MtocTest.c create mode 100755 ld64/unit-tests/test-cases/efi-basic/mtoctest.py create mode 100644 ld64/unit-tests/test-cases/exported-symbols-dead_strip/Makefile create mode 100644 ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.c create mode 100644 ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp create mode 100644 ld64/unit-tests/test-cases/function-starts/Makefile create mode 100644 ld64/unit-tests/test-cases/function-starts/main.c create mode 100644 ld64/unit-tests/test-cases/hidden-r/Makefile create mode 100644 ld64/unit-tests/test-cases/hidden-r/foo.c create mode 100644 ld64/unit-tests/test-cases/hidden-r/main.c create mode 100644 ld64/unit-tests/test-cases/large-bss/Makefile create mode 100644 ld64/unit-tests/test-cases/large-bss/test.s create mode 100644 ld64/unit-tests/test-cases/literals-labels/Makefile create mode 100644 ld64/unit-tests/test-cases/literals-labels/literals.s create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-objc/foo.m create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-tentative/bar.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-tentative/baz.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-tentative/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-tentative/main.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-unused/bar.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-unused/main.c create mode 100644 ld64/unit-tests/test-cases/lto-objc-image-info/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-objc-image-info/main.m create mode 100644 ld64/unit-tests/test-cases/lto-object_path/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-object_path/main.c create mode 100644 ld64/unit-tests/test-cases/non-lazy-sections-r/Makefile create mode 100644 ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s create mode 100644 ld64/unit-tests/test-cases/objc-abi/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-abi/test.m create mode 100644 ld64/unit-tests/test-cases/objc-category-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-category-archive/main.m create mode 100644 ld64/unit-tests/test-cases/objc-category-archive/test.m create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize-load/cat1.m create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize-load/foo.m create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize/cat1.m create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize/cat2.m create mode 100644 ld64/unit-tests/test-cases/objc-category-optimize/foo.m create mode 100644 ld64/unit-tests/test-cases/objc-class-alias/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-class-alias/test.m create mode 100644 ld64/unit-tests/test-cases/objc-gc-checks/none.c create mode 100644 ld64/unit-tests/test-cases/objc-properties/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-properties/test.m create mode 100644 ld64/unit-tests/test-cases/objc-visibility/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-visibility/bar.h create mode 100644 ld64/unit-tests/test-cases/objc-visibility/bar.m create mode 100644 ld64/unit-tests/test-cases/objc-visibility/foo.h create mode 100644 ld64/unit-tests/test-cases/objc-visibility/foo.m create mode 100644 ld64/unit-tests/test-cases/order_file/main4.expected create mode 100644 ld64/unit-tests/test-cases/order_file/main4.order create mode 100644 ld64/unit-tests/test-cases/re-export-and-use/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-and-use/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-and-use/baz.c create mode 100644 ld64/unit-tests/test-cases/re-export-and-use/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-and-use/pub.c create mode 100644 ld64/unit-tests/test-cases/re-export-layers/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-layers/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-layers/baz.c create mode 100644 ld64/unit-tests/test-cases/re-export-layers/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-layers/main.c create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/Makefile create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/bar.c create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/foo.c create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/foo.exp create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/foo2.exp create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/main1.c create mode 100644 ld64/unit-tests/test-cases/re-export-symbol/main2.c create mode 100644 ld64/unit-tests/test-cases/read-only-relocs/main.c create mode 100644 ld64/unit-tests/test-cases/read-only-relocs/test_bind.c create mode 100644 ld64/unit-tests/test-cases/read-only-relocs/test_rebase.c create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/Makefile create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/bar.c create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/bart.exp create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/foo.c create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/foo.exp create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/junk.exp create mode 100644 ld64/unit-tests/test-cases/reexport_symbols_list/main1.c create mode 100644 ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile create mode 100644 ld64/unit-tests/test-cases/sectcreate-dead_strip/main.c create mode 100644 ld64/unit-tests/test-cases/sectcreate-dead_strip/sect_content create mode 100644 ld64/unit-tests/test-cases/segment-labels/Makefile create mode 100644 ld64/unit-tests/test-cases/segment-labels/main.c create mode 100644 ld64/unit-tests/test-cases/segment-labels/test.c create mode 100644 ld64/unit-tests/test-cases/symbol-hiding-umbrella/Makefile create mode 100644 ld64/unit-tests/test-cases/symbol-hiding-umbrella/bar.c create mode 100644 ld64/unit-tests/test-cases/symbol-hiding-umbrella/foo.c create mode 100644 ld64/unit-tests/test-cases/symbol-hiding-umbrella/main.c create mode 100644 ld64/unit-tests/test-cases/symbol-resolver-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/symbol-resolver-basic/foo.c create mode 100644 ld64/unit-tests/test-cases/symbol-resolver-hidden/Makefile create mode 100644 ld64/unit-tests/test-cases/symbol-resolver-hidden/foo.c create mode 100644 ld64/unit-tests/test-cases/tlv-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/tlv-basic/get.s create mode 100644 ld64/unit-tests/test-cases/tlv-basic/main.c create mode 100644 ld64/unit-tests/test-cases/tlv-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/tlv-dylib/foo.s create mode 100644 ld64/unit-tests/test-cases/tlv-dylib/getbar.s create mode 100644 ld64/unit-tests/test-cases/tlv-dylib/getfoo.s rename ld64/{src/ld/SectCreate.h => unit-tests/test-cases/tlv-dylib/main.c} (74%) create mode 100644 ld64/unit-tests/test-cases/umbrella-dylib/Makefile create mode 100644 ld64/unit-tests/test-cases/umbrella-dylib/a.c create mode 100644 ld64/unit-tests/test-cases/umbrella-dylib/b.c create mode 100644 ld64/unit-tests/test-cases/umbrella-dylib/c.c create mode 100644 ld64/unit-tests/test-cases/umbrella-dylib/main.c create mode 100644 ld64/unit-tests/test-cases/unstrippable-symbols/Makefile create mode 100644 ld64/unit-tests/test-cases/unstrippable-symbols/foo.c create mode 100644 ld64/unit-tests/test-cases/utf16-nul/Makefile create mode 100644 ld64/unit-tests/test-cases/utf16-nul/other.s create mode 100644 ld64/unit-tests/test-cases/utf16-nul/withnul.s create mode 100644 ld64/unit-tests/test-cases/weak-def-auto-hide/Makefile create mode 100644 ld64/unit-tests/test-cases/weak-def-auto-hide/main.c create mode 100644 ld64/unit-tests/test-cases/weak-def-auto-hide/other.s create mode 100644 ld64/unit-tests/test-cases/weak-force/Makefile create mode 100644 ld64/unit-tests/test-cases/weak-force/foo.c create mode 100644 ld64/unit-tests/test-cases/weak-force/foo1.exp create mode 100644 ld64/unit-tests/test-cases/weak-force/foo2.exp create mode 100644 ld64/unit-tests/test-cases/weak_import-local/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import-local/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import-local/foo.h create mode 100644 ld64/unit-tests/test-cases/weak_import-local/main.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog index ebd5628..bc918bb 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,5177 +1,1198 @@ ------ Tagged ld64-97.17 +2010-12-10 Nick Kledzik -2010-09-07 Nick Kledzik + Man page typo: "dysmutil" under object_path_lto - ld mis-handling std::tr1::anonymous symbols - Remove support for ordering gcc-4.0 compiled anonymous namespace symbols +2010-12-10 Nick Kledzik + + ld64 crashes when warning about re-exported symbol -2010-09-07 Nick Kledzik +-------- tagged ld64-123.1 - RedGarnet's linker does not honor $ld$hide for umbrella libraries +2010-12-07 Nick Kledzik ------ Tagged ld64-97.16 + assertion if symbol from re-exported dylib is in -exported_symbols_list -2010-08-10 Nick Kledzik +-------- tagged ld64-123 - add -demangle noop to ld64-97 +2010-12-06 Nick Kledzik ------ Tagged ld64-97.15 + Change default search order and add -search_dylibs_first to restore old behavior -2010-08-10 Nick Kledzik +2010-12-06 Nick Kledzik - linker crash when using LTO in lto::Atom::getSymbolTableInclusion() + ld should consistently warn when resolvers are not exported ------ Tagged ld64-97.14 +2010-12-02 Nick Kledzik -2010-04-20 Nick Kledzik + data segment has file offset 0 - if last section is zero-fill don't add size to filesize total in -r mode +-------- tagged ld64-122 ------ Tagged ld64-97.13 +2010-12-01 Nick Kledzik -2010-03-17 Nick Kledzik + Add #define ARM_RELOC_HALF in case trying to build with old mach-o/arm/reloc.h header - Support for iPhoneSimulator with OBJC 2.0 ABI +2010-11-30 Nick Kledzik + Linker should synthesize interworking stubs for tail calls + added test case: unit-tests/test-cases/branch-interworking ------ Tagged ld64-97.12 +2010-11-30 Nick Kledzik -2010-03-17 Nick Kledzik + Link Time Optimization error with tentative defs and -dead_strip + added test case: unit-tests/test-cases/lto-dead_strip-tentative - if last section is zero-fill don't add size to filesize total in -r mode +-------- tagged ld64-121 ------ Tagged ld64-97.11 +2010-11-10 Nick Kledzik -2010-01-26 Nick Kledzik + Add -dylibs option to dyldinfo tool - Libc-624.1 causes latent ld bug - * build linker -no_pie - +2010-11-03 Nick Kledzik ------ Tagged ld64-97.10 + Need support for ARM/thumb upper/lower 16 bit relocation -2010-01-26 Nick Kledzik - - LC_SEGMENT command 0 filesize field greater than vmsize field - * Move __DATA to end in -r mode - +2010-11-03 Nick Kledzik -2010-01-26 Nick Kledzik + Spelling typo in linker warning - symboled __ustring strings of just 0x0000 mis-parsed - * Parse __ustring section based on content - not just labels +2010-11-02 Nick Kledzik + Xcode 4: ld -r doesn't work on files compiled with llvm-gcc -flto ------ Tagged ld64-97.9 +2010-11-01 Nick Kledzik -2010-01-14 Nick Kledzik + ld wrongly complaining about tlv relocs for i386 - LC_SEGMENT command 0 filesize field greater than vmsize field - * for i386 -r mode sort __IMPORT segment before __DATA segment +2010-11-01 Nick Kledzik + Fix -why_live to list all references, not just first -2010-01-11 Nick Kledzik +2010-11-01 Nick Kledzik - * fix ARM -r -d references to tentative definitions + Durango is missing dof sections for armv7 slice ------ Tagged ld64-97.8 +-------- tagged ld64-120.3 -2009-12-08 Nick Kledzik +2010-11-09 Nick Kledzik - many mach-o images in Barolo have MH_WEAK_DEFINES bit incorrectly set - * don't let auto-strip weak symbols set MH_WEAK_DEFINES + revert default search order +-------- tagged ld64-120.2 ------ Tagged ld64-97.7 - -2009-11-30 Nick Kledzik - - llvmgcc now puts const short arrays in __ustring section, linker does not handle that - * Only auto-coalesce UTF16 strings that are labeled with "___utf16_string*" - - ------ Tagged ld64-97.6 - -2009-11-06 Nick Kledzik - - make -pie default for x86_64 for 10.7 and later - - -2009-10-28 Nick Kledzik - - Add a -no_pie flag - * support -no_pie and LD_NO_PIE - +2010-11-09 Nick Kledzik ------ Tagged ld64-97.5 + ld -r corrupts multiple non-lazy sections -2009-10-27 Nick Kledzik +-------- tagged ld64-120.1 - crash when __ustring section has zero length string - * stop trying to suppress tailing 0x0000 from synthesized string used to coalese utf16 strings +2010-10-25 Nick Kledzik + When order file used on data, turn ordered zero fill symbols into zero data ------ Tagged ld64-97.4 - -2009-10-21 Nick Kledzik - - missing thumb bit when thumb function takes address of itself - * move toao.atom == srcao.atom test to after fromao.atom == srcao.atom test - - ------ Tagged ld64-97.3 - -2009-10-06 Nick Kledzik - - * Add missing LittleEndian::set32() in arm::kPointerDiff - -2009-10-05 Nick Kledzik - - ARM: handle pointer-diff to weak thumb that is overridden by non-weak ARM - * When parsing ARM relocations, if target is a thumb function, remove one from addend - * When writing out content for arm::kPointerDiff, add one if target is thumb - * When writing ARM_RELOC_SECTDIFF, use target offsets if they fit in function - - ------ Tagged ld64-97.2 - -2009-09-25 Nick Kledzik - - 'unknown DWARF EH encoding' message logged to console with clang-50 - - ------ Tagged ld64-97.1 - -2009-07-28 Nick Kledzik - - make better no-PIC branch island to thumb1 - * Add kBranchIslandNoPicToThumb1 as 8-byte no-PIC island +-------- tagged ld64-120 +2010-10-25 Nick Kledzik -2009-07-20 Nick Kledzik + ld should use this-image ordinal for symbols from re-exported non-public dylibs + added test case: unit-tests/test-cases/re-export-and-use - section$start$__DATA$__bss can fail - * In SectionBoundaryAtom constructor make __bss and __common be zero-fill - * In AtomSorter, make sure end kSectionEnd sort after kTentativeDefinition - * unit-tests/test-cases/section-labels-zero-fill: add test cases +2010-10-25 Nick Kledzik + Value of N_SO stabs should be address of first atom from translation unit ------ Tagged ld64-97 +2010-10-25 Nick Kledzik -2009-07-13 Nick Kledzik - - empty dylib should have subtype from command line - In Linker::writeOutput() for ARM, set initial fCurrentCpuConstraint to be command line subtype - - -2009-07-09 Nick Kledzik - - crash when using -dead_strip and LTO with unresolved intrinsic - * In Linker::optimize() after final deadStripResolve() call checkUndefines() one last time - - -2009-07-09 Nick Kledzik - - ld64 can not find a -e entry point from an archive - * src/ld/ld.cpp: add searchArchives parameter to entryPoint() for use by deadStripResolve() - * unit-tests/test-cases/dead_strip-entry-archive: added test case + Always print arch name on undefined symbols error +2010-10-25 Nick Kledzik -2009-07-08 Nick Kledzik - - __objc_classrefs section could be coalesced - * In machoReader, chop up __objc_classrefs section into pointer size atoms - and make each weak and hidden with a name based on its target name. + Add ld64 version number to crash logs +2010-10-22 Nick Kledzik -2009-06-26 Nick Kledzik + -objc_abi_version 1 not supported - "ld: symbol dyld_stub_binding_helper not defined" for xnu built with clang for x86_64 - * Fix relocationNeededInFinalLinkedImage() to say weak-defs don't require indirection in static executables - * Added unit-tests/test-cases/static-executable-weak-defines +2010-10-22 Nick Kledzik + Add support to ld64 for N_FUN stabs when used for symbolic constants -2009-06-26 Nick Kledzik +2010-10-22 Nick Kledzik - Error msg for missing -init symbol is misleading/unclear - * In Linker::checkUndefines() check all ways that the command line could add an undefined - and then search for close matches/typos. + Change default search order and add -search_dylibs_first to restore old behavior +2010-10-22 Nick Kledzik -2009-06-25 Nick Kledzik - - We need ld-symbol-moving-symbols for ARM/Embedded - * In mach_o::dylib::Reader::addSymbol() parse iPhoneOS version strings - * Update IPhoneVersionMin - - -2009-06-25 Nick Kledzik - - Linker makes subtype-zero file from empty subtype-nonzero file. - * In Linker::writeOutput() set inittal fCurrentCpuConstraint to be command line subtype - * Fix fArchitectureName to have arm sub-types + -L flag should support a space between it and its argument +2010-10-22 Nick Kledzik -2009-06-24 Nick Kledzik + Hidden resolver functions don't work with DYLD_BIND_AT_LAUNCH - ld should do a better job of reporting attempts to link directories - * In Options::buildSearchPaths() sanity check -L and -F options - +2010-10-22 Nick Kledzik -2009-06-24 Nick Kledzik + Support resolver functions for function pointer use from same linkage unit - better error message when libLTO.dylib not loadable - * in Linker::createReader() provide detail error messages +2010-10-19 Nick Kledzik + section$start$ does not always point to start of built in section types ------ Tagged ld64-96.10 +-------- tagged ld64-119.2 -2009-08-31 Nick Kledzik +2010-10-18 Nick Kledzik - empty dylib has been __mh_dylib_header n_sect - * rework patch to use MinimalTextAtom to ensure there is always a __text section - + make having an ObjC2 class symbol in an export list be a warning instead of an error ------ Tagged ld64-96.9 +2010-10-15 Nick Kledzik -2009-08-31 Nick Kledzik + lazily produce (crt1.o missing) error - empty dylib has been __mh_dylib_header n_sect - * suppress __mh_dylib_header from symbol table if there is no __text section - - Implicitly add dyld_stub_binder to libSystem.dylib so iPhone clients can build - against SnowLeopard libSystem.dylib. +-------- tagged ld64-119.1 +2010-10-05 Nick Kledzik ------ Tagged ld64-96.8 + ld -r can produce output with LC_DYLD_INFO load command -2009-08-30 Nick Kledzik +2010-10-05 Nick Kledzik - SWB: gcc-5577.1 fails to build vecLib - + ld doesn't pass stabs debug info through to the final executable any longer ------ Tagged ld64-96.7 +2010-10-05 Nick Kledzik -2009-08-29 Nick Kledzik + __UNIXSTACK placed incorrectly when -stack_addr < 0x4000000 - Linker makes subtype-zero file from empty subtype-nonzero file. +2010-10-05 Nick Kledzik + ld mishandles -lazy-l option + Updated test case: unit-tests/test-cases/lazy-dylib ------ Tagged ld64-96.6 - -2009-07-30 Nick Kledzik +2010-10-04 Nick Kledzik - ld: ldr 12-bit displacement out of range on SnowLeopard with gcc-5648 - * in Section::Section() set fIndex to ensure __symbolstub1 sorts to end of __TEXT segment + -no_compact_unwind should suppress dwarf->CUE warnings +-------- tagged ld64-119 ------ Tagged ld64-96.5 +2010-10-01 Nick Kledzik -2009-07-28 Nick Kledzik + use ld64 to link iBoot - Thumb mode compilation isn't working on 3.1 beta 2 - * Fix instructions used in kBranchIslandToThumb1 case +2010-10-01 Nick Kledzik + crash when entrypoint is thumb ------ Tagged ld64-96.4 +2010-10-01 Nick Kledzik -2009-06-22 Nick Kledzik - - platform linker should use platform ld_classic - * Fix Options::gotoClassicLinker() to use realpath()s - - -2009-06-22 Nick Kledzik - - * Fix Options::setIPhoneVersionMin() to handle 2.x through 9.x + If -ios_version_min is used with -arch i386, assume simulator +2010-10-01 Nick Kledzik ------ Tagged ld64-96.3 + crash with multiple re-exported dylibs with same install_name -2009-06-17 Nick Kledzik +2010-09-28 Nick Kledzik - * Change section sorting so that arm and ppc stub section is immediately after __text section + Linker complains about resolver functions when architecture is inferred. +2010-09-28 Nick Kledzik -2009-06-17 Nick Kledzik + ARM subtype not set on LTO programs - don't use no-PIC stubs in any dylibs - it might conflict with codesigning - * In StubAtom::StubAtom() don't use kStubNoPIC for OS dylibs - - ------ Tagged ld64-96.2 +2010-09-28 Nick Kledzik -2009-06-09 Nick Kledzik + Link-Time Optimization assertion + Added test cases: + unit-tests/test-cases/lto-dead_strip-objc + unit-tests/test-cases/lto-dead_strip-some-hidden - * Back out page-cross branch work around +2010-09-24 Nick Kledzik + Support -dyld_env NAME=value ------ Tagged ld64-96.1 +2010-09-23 Nick Kledzik -2009-06-05 Nick Kledzik - - * Fix "duplicate symbol cache-line-crossing-stub" error by giving placeholders unique names - * Fix -pie by allowing relocs in x86 stubs and stub helpers - + Previous BranchIsland code changes to make buildable with clang++ were bad. Fix. ------ Tagged ld64-96 +2010-09-23 Nick Kledzik -2009-06-04 Nick Kledzik + ld64 objc category merging asserts on category from old framework - * Darwin x86_64 static codegen is really dynamic code gen, so use LTO_CODEGEN_PIC_MODEL_DYNAMIC +2010-09-23 Nick Kledzik + ASLR Sidebuild: Many Projects Fail checksyms_read_only_relocs Verifier -2009-06-03 Nick Kledzik - - use lto_codegen_set_assembler_path() +2010-09-22 Nick Kledzik + Fix DOF section name bug -2009-06-03 Nick Kledzik +2010-09-22 Nick Kledzik - * In src/ld/LTOReader.hpp, move where -save-temps .bc file is saved to be after code model set + Fixes to build with clang++ +2010-09-21 Nick Kledzik -2009-06-01 Nick Kledzik - - Link Time Optimization error with 'dead code strip' + hidden symbol - * scan newAtoms returned from optimize() looking for ones already in the symbol table - * add test case unit-tests/test-cases/lto-dead_strip-all-hidden + In Resolver::fillInHelpersInInternalState(), dyld never needs stubs +2010-09-21 Ivan Krstic -2009-05-19 Nick Kledzik + ld: support non-executable heap Mach-O header flag - make dyld stubs smaller/faster - * Add new addSynthesizedAtoms() method to Writer, called before atoms are sorted - * Fix throwf() and warning() to check printf types - * Add arm::kPointerDiff12 to support fast arm stubs - * Add new ContentType values for stubs and (non)lazy pointers - * Add new variant of arm stub this is one instruction +2010-09-21 Nick Kledzik + Xcode 4 linker fails with "address not in any section" -2009-05-13 Nick Kledzik +2010-09-20 Nick Kledzik - * ld64.xcodeproj/project.pbxproj: add warnings to dyldinfo target - * src/other/dyldinfo.cpp: support classic LINKEDIT format + assertion failure reading i386 yasm .o (not using scattered reloc) +2010-09-20 Nick Kledzik -2009-05-12 Nick Kledzik + ld crashes when parsing dwarf and all code is not in __text - * src/ld/MachOWriterExecutable.hpp: fix optimization to skip branch islands with ARM bl instructions +2010-09-17 Nick Kledzik + magic segment symbol names don't work with preload executables + Update test case: unit-tests/test-cases/segment-labels -2009-05-12 Nick Kledzik +2010-09-17 Nick Kledzik - creation of __unwind_info section can fail if hundreds of functions cannot be compact encoded - * src/ld/MachOWriterExecutable.hpp: fix when to make regular vs compressed pages + OSO in DebugNotes for LTO should point to generated mach-o not, bitcode .o file +2010-09-16 Nick Kledzik -2009-05-08 Nick Kledzik + FUN in debug map not rebased + Update test case: unit-tests/test-cases/rebase-basic - * src/ld/ld.cpp: enhance -save-temps to also write out optimized bitcode file - +2010-09-16 Nick Kledzik -2009-05-08 Nick Kledzik + Relocation failure with i386 32-bit diff to stub - * src/ld/ld.cpp: fix -order_file_statistics to print each symbol not found and correct total - +2010-09-16 Nick Kledzik -2009-05-08 Nick Kledzik + assert when targeting 10.5 and crt1.o/dylib1.o is not supplied - linker should be able to coalesce UTF16 strings - * src/ld/MachOReaderRelocatable.hpp: parse __ustring section by labels but synthesize an - atom name based on the content. Leverage for __cfstring section - * unit-tests/test-cases/cfstring-utf16: update test case +-------- tagged ld64-118.1 +2010-09-15 Nick Kledzik -2009-05-07 Nick Kledzik + Fix missing rebase commands that broke perl - * src/ld/MachOWriterExecutable.hpp: put branch islands further apart if there is no thumb code +2010-09-15 Nick Kledzik + assert when .objc_class_name_* symbol missing + Add test case: unit-tests/test-cases/archive-ObjC-unexported -2009-05-07 Nick Kledzik +2010-09-13 Nick Kledzik - LINKEDIT optimizations for iPhone - * src/ld/ObjectFile.h: Recognize iPhoneOS 3.1 - * src/ld/Options.cpp: iPhoneOS 3.1 => use compressed LINKEDIT - * src/ld/MachOWriterExecutable.hpp: support generating compressed LINKEDIT for arm + linker does not honor $ld$hide for umbrella libraries + Added test case: unit-tests/test-cases/symbol-hiding-umbrella +2010-09-09 Nick Kledzik -2009-05-04 Nick Kledzik + LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats - Add linker support for ARM branch islands - Add labels to linker synthesized jump islands - * src/ld/MachOWriterExecutable.hpp: reworked BranchIslandAtom and createBranchIslands to support arm/thumb - * src/ld/ObjectFile.h: added kBranchIsland - * unit-tests/test-cases/branch-islands: updated test case to check arm/thumb +2010-09-09 Nick Kledzik + support -bind_at_load -2009-04-30 Nick Kledzik +2010-09-07 Nick Kledzik - * src/ld/Options.cpp: fix custom stack base address for arm + ld mis-handling std::tr1::anonymous symbols + Remove support for ordering gcc-4.0 compiled anonymous namespace symbols -2009-04-30 Nick Kledzik +-------- tagged ld64-118 - likely incorrect warning about common symbols - * src/ld/Options.cpp: ignore LD_WARN_COMMONS in -r mode - +2010-09-02 Nick Kledzik ------ Tagged ld64-95.8.3 + -preload should not have LINKEDIT segment + Added test case: unit-tests/test-cases/efi-basic -2009-03-31 Nick Kledzik +2010-09-02 Nick Kledzik - ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: rescan fRegularDefAtomsThatOverrideADylibsWeakDef and only consider global ones - + trivial Objective-C app fails when using libLTO + Added test case: unit-tests/test-cases/lto-objc-image-info ------ Tagged ld64-95.8.2 +2010-09-02 Nick Kledzik -2009-03-18 Nick Kledzik + Add -reexport_symbols_list option to re-export certain symbols. + Added test case: unit-tests/test-cases/reexport_symbols_list - * src/ld/MachOReaderRelocatable.hpp: back out -force_cpusubtype_ALL changes +2010-09-01 Nick Kledzik + LTO with 'dead code strip' can't ignore unused functions with undefined references + Add test case: unit-tests/test-cases/lto-dead_strip-unused ------ Tagged ld64-95.8.1 +2010-09-01 Nick Kledzik -2009-03-17 Nick Kledzik + Warn if unaligned ARM code is detected - -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: in markDead() remove from fLiveRootAtoms - +2010-09-01 Nick Kledzik -2009-03-17 Nick Kledzik + Mach-O linked by current linker don't load in VIrtualBox any more - libgcc fails to build in with ld64-95.8 - * src/ld/MachOReaderRelocatable.hpp: interpret CPU_SUBTYPE_ARM_ALL as CPU_SUBTYPE_ARM_V4T - * src/ld/Options.cpp: interept -force_cpusubtype_ALL as -arch armv4t +2010-09-01 Nick Kledzik + Linker should pick default visibility instead of warning about conflicts + Updated test case: unit-tests/test-cases/visibility-warning ------ Tagged ld64-95.8 +2010-09-01 Nick Kledzik -2009-02-09 Nick Kledzik + Enable new load commands - ld64-95.7 crashes building Foundation-678.39 in side build - * src/ld/MachOReaderRelocatable.hpp: handle a zero length section with a label before __cstring section +2010-09-01 Nick Kledzik + Do not pass -demangle to ld_classic ------ Tagged ld64-95.7 +2010-09-01 Nick Kledzik -2009-02-06 Nick Kledzik - - * src/ld/ObjectFile.h: make fAddCompactUnwindEncoding false by default - + iOS 4.3 armv7 should be PIE by default + better error message for direct access to external globals when not linking read_only_relocs + linker does not error on direct (static) data references to a dylib symbol -2009-02-06 Nick Kledzik +-------- tagged ld64-117.11 - ER: add linker option to zero fill empty DATA sections on disk - * src/ld/Options.cpp: add support for -no_zero_fill_sections - * src/ld/MachOReaderRelocatable.hpp: isZeroFill() is only true if fOptimizeZeroFill - * doc/man/man1/ld.1: document -no_zero_fill_sections - * unit-tests/test-cases/no_zero_fill_sections: add test case - +2010-09-03 Nick Kledzik -2009-02-05 Nick Kledzik + mask thumb bit off non lazy pointers content when parsing arm .o files - label getting resolved to the wrong address. - * src/ld/MachOWriterExecutable.hpp: add findAtomAndOffsetForSection() and use it to disambiguate - * unit-tests/test-cases/label-on-end-of-section: added test case +-------- tagged ld64-117.10 +2010-08-26 Nick Kledzik -2009-01-27 Nick Kledzik + 8F64 kills installtest devices + Don't clear thumb bit on pointers inside thumb functions if addend is negative - Warn -force_cpusubtype_ALL is not supported - * src/ld/Options.cpp: warn if fForceSubtypeAll and fArchitecture is CPU_TYPE_ARM - +-------- tagged ld64-117.9 ------ Tagged ld64-95.6 +2010-08-25 Nick Kledzik -2009-01-25 Nick Kledzik + no more audio because of broken thunk + Update of thumb22 b.w instruction was not clearing bits before or'ing in new ones - Add support for section start/end labels - * src/ld/ObjectFile.h: add kSectionStart and kSectionEnd - * src/ld/MachOReaderRelocatable.hpp: create SectionBoundaryAtoms as needed - * src/ld/ld.cpp: sort SectionBoundaryAtoms correctly - * src/ld/MachOWriterExecutable.hpp: allow all relocations in preload images +-------- tagged ld64-117.8 +2010-08-25 Nick Kledzik ------ Tagged ld64-95.5 + prefetch abort in kernel mode: fault_addr=0xe58024e4 + Don't set thumb bit on .long pointers in thumb functions that point to some offset in same function -2009-01-15 Nick Kledzik +-------- tagged ld64-117.7 - * src/ld/MachOWriterExecutable.hpp: in hasPageCrossingBranches() ignore branches preceeded by a branch +2010-08-24 Nick Kledzik + dyld-179.4 fails to link, assert in setLoadCommandsPadding() + Fix linker to always put __text first before other code-only sections ------ Tagged ld64-95.4 +-------- tagged ld64-117.6 -2009-01-15 Nick Kledzik +2010-08-23 Nick Kledzik - * src/ld/Options.cpp: handle -kext and -r the same for fPreventPageCrossingBranches + ld no longer output static archive dependencies for dyld for B&I + Add test case unit-tests/test-cases/dependency-logging +-------- tagged ld64-117.5 ------ Tagged ld64-95.3 +2010-08-20 Nick Kledzik -2009-01-14 Nick Kledzik + SWB: ld64-117.1 on Durango8F54: Assertion failed: + UTF16 CFStrings were not coalesced correctly when gcc built the .o files and the + last string in the __ustring section only had a single zero byte at the end. + +-------- tagged ld64-117.4 - linker should alter layout to prevent armv7 page crossing branches - * src/ld/Options.cpp: set fPreventPageCrossingBranches - * src/ld/MachOWriterExecutable.hpp: adjust layout of __text so there are not page crossing branches - * src/ld/MachOReaderRelocatable.hpp: support new ARM_THUMB_32BIT_BRANCH reloce +2010-08-19 Nick Kledzik + DTSJ1J105: SpringBoard crashes on boot + Fix order file to move aliases even when subsections_via_symbols it used + Update test case unit-tests/test-cases/order_file ------ Tagged ld64-95.2.10 +2010-08-17 Nick Kledzik -2009-04-02 Nick Kledzik + Fix resolver functions to survive ld -r. + Warn if resolver functions are made non-external. - corrupt metaclass entry in dynamic library - * src/ld/ld.cpp: change Section constructor to copy segment and section names +2010-08-17 Nick Kledzik + Make it an error for resolver functions to be used in anything but a dylib. ------ Tagged ld64-95.2.9 +-------- tagged ld64-117.3 -2009-04-02 Nick Kledzik +2010-08-17 Nick Kledzik - Update ld64 for new triples introduced in 6654669 to support ARM LLVM - * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples + Fix thumb resolver functions + Enable updward dylibs and symbol re-exports for iOS 4.2 +2010-08-16 Nick Kledzik ------ Tagged ld64-95.2.8 + Latest ld no longer supports EFI -preload + Rearrange LINKEDIT chunks in -preload mode -2009-03-24 Nick Kledzik +-------- tagged ld64-117.2 - anonymous functions have the compact unwind info computed wrong - * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom +2010-08-14 Nick Kledzik + Latest ld no longer supports EFI -preload + Add LC_UNIXTHREAD to -preload ------ Tagged ld64-95.2.7 +2010-08-14 Nick Kledzik -2009-03-11 Nick Kledzik + SWB: ld64-117.1 on Durango8F54: Assertion failed: (categoryAtom->size() == Category::size()) + gcc-4.0 uses 'L' labels on categories. This merges them onto previous data and disable category optimzation - AddressBook incorrectly gets _objc_msgSend from WebKit - * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib - that is already explictly or implicitly linked. - * unit-tests/test-cases/re-export-optimizations-indirect: add test case - +2010-08-14 Nick Kledzik -2009-03-10 Nick Kledzik + SWB: ld64-117.1 on Durango8F54: bad category optimization + Disable category optimization for i386 and arm until further testing - dyld weak linking optimization leaves some symbols unbound - * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference - to a symbol in a dylib that is a weak definition - * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case +2010-08-14 Nick Kledzik + SWB: ld64-117.1 on Durango8F54: address not in any section + Handle pointer diff to stub for weak hidden function -2009-03-10 Nick Kledzik +2010-08-13 Nick Kledzik - many OS i386 OS dylibs still have __IMPORT segment - * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr - * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem + Latest ld no longer supports EFI -preload +-------- tagged ld64-117.1 ------ Tagged ld64-95.2.6 +2010-08-11 Nick Kledzik -2009-02-27 Nick Kledzik + Make missing exported symbols a warning to help adoption of new linker - ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef - that will be exported when computing MH_WEAK_DEFINES - * unit-tests/test-cases/operator-new: updated to reproduce issue - +2010-08-11 Nick Kledzik ------ Tagged ld64-95.2.5 + Add ExternalRelocationsAtom<>::pointerReloc() to more easily support kext bundles -2009-02-24 Nick Kledzik +2010-08-09 Nick Kledzik - x86_64 obj-c runtime confused when static lib is stripped - * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings - * unit-tests/test-cases/objc-literal-pointers-strip: added test case - + SWB: ld64-116.2 fix branch to label-4 ------ Tagged ld64-95.2.4 +2010-08-09 Nick Kledzik -2009-02-23 Nick Kledzik + Error with empty non_lazy_symbol_pointers section - * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs +2010-08-06 Nick Kledzik + Add command line options to control symbol weak-def-bit on exported symbols -2009-02-18 Nick Kledzik +-------- tagged ld64-117 - Writer::symbolIndex() uses a linear search and does not scale - * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better - +2010-07-28 Nick Kledzik -2009-02-18 Nick Kledzik + split seg info wrong for x86_64 stub helpers - Use new compact encodings that handle all register permutations - * src/ld/Architectures.hpp: add kSectionOffset24 - * src/ld/ObjectFile.h: add getFDE() - * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding - * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed - * src/other/unwinddump.cpp: update unwinddump output to display register save set - +2010-07-26 Nick Kledzik -2009-02-16 Nick Kledzik + __nlcatlist categories should not be optimized - runtime error with bundle for 10.5 that has weak external symols - * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions - +2010-07-23 Nick Kledzik -2009-02-15 Nick Kledzik + ld64 assertion on object file - i386 relocation error with negative offsets from local labels - * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label - * unit-tests/test-cases/relocs-neg-from-local: add test case - +2010-07-21 Nick Kledzik -2009-02-12 Nick Kledzik + Reorder sections to reduce page faults in object files - -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms - * unit-tests/test-cases/dead_strip-weak-coalesce: added test case +2010-06-30 Nick Kledzik + Support resolver functions in iOS dylibs -2009-02-12 Nick Kledzik +-------- tagged ld64-116.2 - x86_64 weak_import broken for initialized data - * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader::addRelocReference() - * src/other/dyldinfo.cpp: update to display weak_import attribute - * unit-tests/test-cases/weak_import: updated test case +2010-06-30 Nick Kledzik + C programs get objc GCness from dylibs + Update: unit-tests/test-cases/objc-gc-checks -2009-02-06 Nick Kledzik - - ld parsing of __eh_frame unwind information is slow - * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 - +-------- tagged ld64-116.1 ------ Tagged ld64-95.2.3 +2010-06-22 Nick Kledzik -2009-02-04 Nick Kledzik + address range check should not apply to preload executables - ld: warning: can't add line info to anonymous symbol - * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs +2010-06-22 Nick Kledzik + Warn instead of error when CPU_SUBTYPE_ARM_ALL .o files used. ------ Tagged ld64-95.2.2 +2010-06-22 Nick Kledzik -2009-02-02 Nick Kledzik + Fix assert in objc category optimzation. Metaclass also has copy of propery list to update. - ld -r does not preserve the N_NO_DEAD_STRIP bit - * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() - * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case +2010-06-22 Nick Kledzik + Fix crash in -r mode with -alias. ------ Tagged ld64-95.2.1 +-------- tagged ld64-116 ------ Tagged ld64-95.2.10 +2010-06-21 Nick Kledzik -2009-04-02 Nick Kledzik + Add support for -ios_version_min as an alias for -iphoneos_version_min - corrupt metaclass entry in dynamic library - * src/ld/ld.cpp: change Section constructor to copy segment and section names +2010-06-21 Nick Kledzik + linker could merge method lists from class and its categories + Added test case: unit-tests/test-cases/objc-category-optimize + Added option: -no_objc_category_merging to disable ------ Tagged ld64-95.2.9 +2010-06-21 Nick Kledzik -2009-04-02 Nick Kledzik + i386 TLV PIC reloc content is negated - Update ld64 for new triples introduced in 6654669 to support ARM LLVM - * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples +2010-06-15 Nick Kledzik + Added better error messages and asserts for bad thread local object files ------ Tagged ld64-95.2.8 +2010-06-09 Nick Kledzik -2009-03-24 Nick Kledzik + Barolo: 'rebase' makes timestamps invalid/unreadable for GDB - anonymous functions have the compact unwind info computed wrong - * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom +2010-06-09 Nick Kledzik + executable has no debug symbols when compiled with LTO + Added test case: unit-tests/test-cases/lto-object_path ------ Tagged ld64-95.2.7 +2010-06-09 Nick Kledzik -2009-03-11 Nick Kledzik + stop promoting hidden referenced dynamically symbols to global + Updated test case: unit-tests/test-cases/main-stripped - AddressBook incorrectly gets _objc_msgSend from WebKit - * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib - that is already explictly or implicitly linked. - * unit-tests/test-cases/re-export-optimizations-indirect: add test case - - -2009-03-10 Nick Kledzik - - dyld weak linking optimization leaves some symbols unbound - * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference - to a symbol in a dylib that is a weak definition - * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case - - -2009-03-10 Nick Kledzik - - many OS i386 OS dylibs still have __IMPORT segment - * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr - * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem - - ------ Tagged ld64-95.2.6 +2010-06-04 Nick Kledzik -2009-02-27 Nick Kledzik + ER: individual symbol re-exports + Added test case: unit-tests/test-cases/re-export-symbo - ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef - that will be exported when computing MH_WEAK_DEFINES - * unit-tests/test-cases/operator-new: updated to reproduce issue - - ------ Tagged ld64-95.2.5 - -2009-02-24 Nick Kledzik - - x86_64 obj-c runtime confused when static lib is stripped - * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings - * unit-tests/test-cases/objc-literal-pointers-strip: added test case - +2010-06-03 Nick Kledzik ------ Tagged ld64-95.2.4 + add functions start info to LINKEDIT + * Support added but not on by default. Use -function_starts to enable. + * Added test case: unit-tests/test-cases/function-start -2009-02-23 Nick Kledzik +2010-06-02 Nick Kledzik - * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs + ER: add load command for min OS version + * Support added but not on by default. Use -version_load_command to enable. +2010-06-02 Nick Kledzik -2009-02-18 Nick Kledzik + provide better undefined symbol error message - Writer::symbolIndex() uses a linear search and does not scale - * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better - +2010-05-28 Nick Kledzik -2009-02-18 Nick Kledzik + * ld should also merge file attributes from lazy loaded archives + * Move attribute gathering from InputFiles to Resolver - Use new compact encodings that handle all register permutations - * src/ld/Architectures.hpp: add kSectionOffset24 - * src/ld/ObjectFile.h: add getFDE() - * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding - * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed - * src/other/unwinddump.cpp: update unwinddump output to display register save set - +2010-05-28 Nick Kledzik -2009-02-16 Nick Kledzik + * SWB: ld64-115.3: dylib on kext link line causes malformed kext + * allow -static after -kext on command line - runtime error with bundle for 10.5 that has weak external symols - * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions - +-------- tagged ld64-115.3 -2009-02-15 Nick Kledzik +2010-05-26 Nick Kledzik - i386 relocation error with negative offsets from local labels - * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label - * unit-tests/test-cases/relocs-neg-from-local: add test case - + * strip of .o files removes __objc_imageinfo section + * Added test case: unit-tests/test-cases/dwarf-strip-objc -2009-02-12 Nick Kledzik +2010-05-25 Nick Kledzik - -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms - * unit-tests/test-cases/dead_strip-weak-coalesce: added test case + * crash when parsing local vanilla reloc to weak def +-------- tagged ld64-115.2 -2009-02-12 Nick Kledzik +2010-05-21 Nick Kledzik - x86_64 weak_import broken for initialized data - * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader::addRelocReference() - * src/other/dyldinfo.cpp: update to display weak_import attribute - * unit-tests/test-cases/weak_import: updated test case + * switch back to using ld_classic for -static arm code -2009-02-06 Nick Kledzik - - ld parsing of __eh_frame unwind information is slow - * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 - - ------ Tagged ld64-95.2.3 - -2009-02-04 Nick Kledzik - - ld: warning: can't add line info to anonymous symbol - * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs - - ------ Tagged ld64-95.2.2 +2010-05-21 Nick Kledzik -2009-02-02 Nick Kledzik + * warn instead of error when seg1addr is out of range for ARM - ld -r does not preserve the N_NO_DEAD_STRIP bit - * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() - * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case +2010-05-21 Nick Kledzik ------ Tagged ld64-95.2.1 + * fix -undefined dynamic_lookup -nodefaults to not error about missing dyld_stub_binder -2009-01-29 Nick Kledzik +-------- tagged ld64-115.1 - gcc DejaGnu failure: building longcall/dylib library - * src/ld/MachOWriterExecutable.hpp: if no __DATA sections insert non-lazy pointers at end of __TEXT segment - * unit-tests/test-cases/no-data-bundle: added test case +2010-05-19 Nick Kledzik + * Fix trie nodes for resolver functions to have second address be stub not helper ------ Tagged ld64-95.2 - -2009-01-06 Nick Kledzik - - strip -S fails with "new trie is larger than original" - * src/other/PruneTrie.cpp: don't align trie more than original trie was aligned - - ------ Tagged ld64-95.1 - -2008-12-21 Nick Kledzik - - * src/ld/MachOWriterExecutable.hpp: in new linkedit format, make sure only exported symbols - make it into weak binding info - - ------ Tagged ld64-95 +2010-05-19 Nick Kledzik -2008-12-18 Nick Kledzik + * work around for old checksyms tools + * Make i386 stub section named "__symbol_stub" instead of "__stubs" - * src/ld/Options.cpp: move check for fSharedRegionEligible until fPrebind has stabilized - +2010-05-10 Nick Kledzik -2008-12-18 Nick Kledzik + * linking with LTO prints "/tmp/lto.o" - Generate new compressed LINKEDIT when targeting 10.6 - * src/ld/Options.cpp: turn on compressed LINKEDIT by default - +-------- tagged ld64-115 ------ Tagged ld64-94.1 +2010-05-06 Nick Kledzik -2008-12-16 Nick Kledzik + * linker loses x86_64 addend to 'L' symbols + * properly handle addend to 'L' symbols that are ignored - * src/ld/Options.cpp: Fix -F handling in buildSearchPaths() +2010-05-05 Nick Kledzik + * rework min OS version parsing to enable the linker to handle unknown OS versions ------ Tagged ld64-94 -2008-12-15 Nick Kledzik +2010-05-05 Nick Kledzik - * doc/man/man1/ld.1: document new options + * Implement magic section$start$xxx$yyyy and section$end$xxx$yyyy symbols + * Implement magic segment$start$xxx and segment$end$xxx symbols + * Add test case: unit-tests/test-cases/segment-labels -2008-12-15 Nick Kledzik +2010-05-03 Nick Kledzik - linker should enforce all .o files have same sub-type, and ignore sub-type of dylibs - * doc/man/man1/ld.1: update man page about -allow_sub_type_mismatches - * src/ld/ld.cpp: call validFile() with new arguments - * src/ld/MachOReaderRelocatable.hpp: add new arguments to validFile() - * src/ld/Options.cpp: Support LD_ALLOW_CPU_SUBTYPE_MISMATCHES and -allow_sub_type_mismatches + * implement optional demangling in linker + * Add option: -demangle + * Add test case: unit-tests/test-cases/demangle -2008-12-15 Nick Kledzik - - -syslibroot should skip standard search paths not in the SDK - * src/ld/Options.cpp: in buildSearchPaths() if an SDK is specified don't add - standard search paths not in the SDK. - - -2008-12-15 Nick Kledzik - - ld: remove "can't make compact unwind encoding" warning - * src/ld/ObjectFile.h: add fWarnCompactUnwind - * src/ld/Options.cpp: -warn_compact_unwind --> fWarnCompactUnwind - * src/ld/MachOReaderRelocatable.hpp: test fWarnCompactUnwind before warning - - -2008-12-15 Nick Kledzik +2010-05-03 Nick Kledzik - Add dtrace usdt support for arm to ld64 - * src/ld/MachOWriterExecutable.hpp: handle arm::kDtraceIsEnabledSite - * unit-tests/test-cases/dtrace-static-probes: use is-enabled in test case + * ld64 doesn't grok the modern-objc-ABI-on-i386 + * Add support for -objc_abi_version command line option + * Added test case: unit-tests/test-cases/objc-abi ------ Tagged ld64-93 +2010-05-03 Nick Kledzik -2008-12-11 Nick Kledzik - - * src/ld/ObjectFile.h: add fIPhoneVersionMin to track min iPhoneOS version - * src/ld/Options.cpp: use fIPhoneVersionMin + * -alias does not work with __OBJC sections + * sort contents of sections with aliases + * Added test case: unit-tests/test-cases/objc-class-alias -2008-12-11 Nick Kledzik - - non-lazy pointer to non-global tentative definition encoded wrong - * src/ld/MachOWriterExecutable.hpp: don't use INDIRECT_SYMBOL_LOCAL for tentative definitions - * unit-tests/test-cases/non-lazy-r: updated test case - - -2008-12-11 Nick Kledzik +2010-04-28 Nick Kledzik - kernel fails to boot when ld64 used for intermediate ld -r step - * src/ld/MachOWriterExecutable.hpp: in -r mode when generating a scattered sect-diff reloc for - i386/arm, special case when from target is not the atom - the relocation is in. - * unit-tests/test-cases/relocs-asm: update test case + * Feature: Thread local storage + * Add test case: unit-tests/test-cases/tlv-basic + * Add test case: unit-tests/test-cases/tlv-dylib -2008-12-11 Nick Kledzik - - * src/ld/ld.cpp: handle new __program_vars section - * src/ld/MachOWriterExecutable.hpp: handle inserting synthesized sections when there is no __dyld section - - -2008-12-11 Nick Kledzik - - * src/ld/MachOReaderRelocatable.hpp: Fix getDescription() to work when direct reference is to anonymous atom +2010-04-27 Nick Kledzik + * Accelerate needs way to dispatch based on instruction execution time characteristics. + * Add support for "symbol resolver" functions + * Add test case unit-tests/test-cases/symbol-resolver-basic -2008-12-10 Nick Kledzik - * src/ld/Options.cpp: enable LD_FORCE_NO_PREBIND to be used with arm +2010-04-26 Nick Kledzik + * range check fat archives + * check that fat file slice being used does not extend beyond end of file + * check that member being used does not extend beyond end of slice/file -2008-12-10 Nick Kledzik - - Developer tool to print the new compressed LINKEDIT information - * src/other/dyldinfo.cpp: fix typo in usage() - - -2008-12-05 Nick Kledzik - - SnowLeopard kernel should compile warning free - * src/ld/MachOReaderRelocatable.hpp: correct parse two global labels at end of section and make one an alias - * unit-tests/test-cases/end-label: update test case +2010-04-26 Nick Kledzik + * The documentation for the -allowable_client option doesn't say enough about it -2008-12-04 Nick Kledzik - - Better warning than "PPC_RELOC_JBSR should not be using an external relocation" - * src/ld/MachOReaderRelocatable.hpp: issue warning with .o path if it was compiled with -mlong-branch - - -2008-12-04 Nick Kledzik - - linker should not map __pointers -> __nl_symbol_ptr unless actually making new LINKEDIT - * src/ld/ObjectFile.h: add fMakeCompressedDyldInfo for readers to see - * src/ld/Options.cpp: set fMakeCompressedDyldInfo for readers to see - * src/ld/MachOReaderRelocatable.hpp: check fMakeCompressedDyldInfo +2010-04-26 Nick Kledzik + * back out LD_NO_PIE -2008-12-02 Nick Kledzik +2010-04-22 Nick Kledzik - * src/ld/debugline.c: fix error handling in line_open() + * More ICU make check failures with 0-terminated UTF16 strings + * Change UTF16StringSection to break into atoms just on label boundaries + * Added test case: unit-tests/test-cases/utf16-nul -2008-11-26 Nick Kledzik +-------- tagged ld64-114.12 - vtable with thumb entries broke after ld -r - * src/ld/MachOReaderRelocatable.hpp: if target of reloc is thumb, mask thumb bit off addend - * unit-tests/test-cases/thumb-pointer: added test case +2010-04-14 Nick Kledzik - -2008-11-26 Nick Kledzik - - * src/ld/Option.cpp: Fix how crashreporterBuffer is created to not miss some arguments + * Crash with messed up BNSYM -2008-11-24 Nick Kledzik +2010-04-07 Nick Kledzik - Security.framework has some duplicate FDEs for some functions - * src/ld/ld.cpp: remove fDeadAtoms from fLiveAtoms when there are weak atoms overriden by late loads - * unit-tests/test-cases/dead_strip-archive-eh: added test case + * Fix crash with blank dylib stubs +-------- tagged ld64-114.11 ------ Tagged ld64-92 +2010-04-07 Nick Kledzik -2008-11-21 Nick Kledzik - - * src/ld/MachOReaderDylib.hpp: if export_size is zero, no need to parse trie - * src/abstraction/MachOTrie.hpp: gracefully handle empty trie + * for ppc, add split-seg info for TEXT pointers to DATA -2008-11-21 Nick Kledzik +2010-04-07 Nick Kledzik - strip(1) support for new compressed LINKEDIT information - * ld64.xcodeproj/project.pbxproj: build and install new libprunetrie.a - * src/other/prune_trie.h: added - * src/other/PruneTrie.cpp: implements prune_trie() - + * Cannot build ppc64 target with ObjC code -2008-11-21 Nick Kledzik - * src/ld/ld.cpp: if an export file is used and all weak symbols are masked, don't set WEAK_DEFINES - * unit-tests/test-cases/weak-def-flag: added test case +2010-04-01 Nick Kledzik + * let .exp files override auto-hide so that they can be exported if needed -2008-11-20 Nick Kledzik - Generate new compressed LINKEDIT when targeting 10.6 - * src/ld/MachOWriterExecutable.hpp: support generating new compressed format - * src/ld/MachOReaderRelocatable.hpp: new compress format implies non-lazy pointers in __DATA for i386 - * src/ld/MachOReaderDylib.hpp: support linking aginst new format - * src/ld/Options.cpp: suppport -exported_symbols_order and -no_compact_linkedit - * src/ld/ld.cpp: track which atoms have weak counter parts in dylibs - * src/other/dyldinfo.cpp: added tool to display new LINKEDIT format - * ld64.xcodeproj/project.pbxproj: add dyldinfo tool - * unit-tests/*: lots of fixes to work with new format +2010-04-01 Nick Kledzik + * support auto hidden weak symbols: .weak_def_can_be_hidden + * added test case: unit-tests/test-cases/weak-def-auto-hide -2008-11-20 Nick Kledzik - ld64 should preserve N_WEAK_REF when linking MH_KEXT_BUNDLEs - * src/ld/MachOWriterExecutable.hpp: set up fWeakImportMap in synthesizeKextGOT() +2010-04-01 Nick Kledzik + * 'l' symbols not being automatically removed -2008-11-19 Nick Kledzik - VideoToolbox.framework has bad __TEXT.__eh_frame info - * src/ld/Options.cpp: add -no_eh_labels option for use with -r - * src/ld/MachOWriterExecutable.hpp: generate correct x86_64 labeless relocs in -r mode - * src/ld/MachOReaderRelocatable.hpp: now ignore all labels and relocations in - __TEXT/__eh_frame section and rely on getCFIs() from libunwind - * unit-tests/test-cases/eh-coalescing-no-labels: add test case +2010-03-31 Nick Kledzik + * weak defs should not cause indirection in static executables + * Update test case: unit-tests/test-cases/static-executable-weak-defines -2008-11-19 Nick Kledzik +-------- tagged ld64-114.10 - LTO doesn't like dtrace symbols - * src/ld/LTOReader.hpp: ignore __dtrace_probe undefines in bitcode files +2010-03-31 Nick Kledzik + * assert with .o file with two LSDA sections -2008-11-14 Nick Kledzik - * src/abstraction/MachOFileAbstraction.hpp: fix to work with 10.5 headers - - ------ Tagged ld64-91 +-------- tagged ld64-114.9 -2008-11-07 Nick Kledzik - - Remove COMPACT_UNWIND_SUPPORT conditionalizing +2010-03-30 Nick Kledzik - -2008-11-06 Nick Kledzik - - Reorganize source layout. ld sources are now in "ld", - and other tools are in "other". + * L4 locks up starting a second processor, works fine with old linker + * properly get addend from content in x86_64 substractor when target is direct -2008-11-05 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: start installing unwinddump tool - * src/UnwindDump.cpp: support -arch option - * doc/man/man1/unwinddump.1: create man page - - -2008-11-05 Nick Kledzik - - linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries - * src/ld.cpp: in synthesizeDebugNotes() set other field of OSO to be subtype - - -2008-11-05 Nick Kledzik - - Need a linker option to load all objects from one library - * src/Options.cpp: support -force_load option - * src/ArchiveReader.hpp: Add fForceLoad ivar - * doc/man/man1/ld.1: update man page with -force_load option - * unit-tests/test-cases/archive-force-load: add test case +2010-03-29 Nick Kledzik + * ld should excludes debug notes when computing UUID + * added test case: unit-tests/test-cases/dwarf-debug-notes-uuid -2008-11-05 Nick Kledzik +-------- tagged ld64-114.8 - Dtrace Probe Warnings: SnowLeopard kernel should compile warning free - * src/ld.cpp: don't generate GSYM stabs for old style __dtrace_probe - * src/MachOReaderRelocatable.hpp: fix test for deciding if a symbol is an alias - - -2008-11-04 Nick Kledzik - - ADOBE: XCODE: ld: duplicate typeinfo in executable - * src/ld.cpp: in dead-strip mode, record overriden symbols and later rebind all uses - * unit-tests/test-cases/dead_strip-archive-weak: add test case - +2010-03-26 Nick Kledzik -2008-11-03 Nick Kledzik + * __objc_catlist section loses don't dead strip bit in ld -r mode + * Update test case unit-tests/test-cases/static-executable - support increased branch range in Thumb-2 - * src/MachOReaderRelocatable.hpp: handle full branch range in addRelocReference() - * unit-tests/test-cases/branch-distance: added test case +-------- tagged ld64-114.7 -2008-10-31 Devang Patel - - Sqlite 3.5.4 built with lvm-gcc-4.2 -O4 fails regression test - * src/LTOReader.hpp: Use real atom scope when real atom is available. - Preserve globals while optimizing an executable. - -2008-10-30 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support all encodings in getEncodedP() - +2010-03-25 Nick Kledzik ------ Tagged ld64-90 + * Support LD_NO_PIE again -2008-10-30 Nick Kledzik +2010-03-25 Nick Kledzik - icc has dwarf unwind info that is different than gcc - * src/MachOReaderRelocatable.hpp: support more encodings in getEncodedP() + * Rosetta crashes on launch in 11A133a + * Fix -segaddr __TEXT to cause other floating segments to be contiguous with TEXT +2010-03-24 Nick Kledzik -2008-10-23 Nick Kledzik + * Page Zero segment seems to be getting dead stripped - build ld64 for x86_64 - * ld64.xcodeproj/project.pbxproj: add X86_64 to valid archs - - -2008-10-23 Nick Kledzik +2010-03-24 Nick Kledzik - * ld64.xcodeproj/project.pbxproj: use generated @$(DERIVED_FILE_DIR)/linker_opts for extra - linker options. This allows linker to be built if LTO headers and libs are missing. + * kernel sdt dtrace probes not visible +-------- tagged ld64-114.6 -2008-10-23 Nick Kledzik +2010-03-23 Nick Kledzik - Linker warning not shown in the Xcode build log - * src/Options.cpp: add colon to format string in warning() + * new linker makes dylibs with no __text section, causing codesign_allocate tool to fail + * make sure there is always a __text section in dylibs and bundles +-------- tagged ld64-114.5 ------ Tagged ld64-89.3 +2010-03-22 Nick Kledzik -2008-10-24 Nick Kledzik + * missing __objc_imageinfo section + * Real fix will be in 7780438. For now have Resolver also accumulate objc constraint info - ld64-89 broke TOT OpenGL libProgrammability x86_64 build - * src/MachOReaderRelocatable.hpp: add cast in getEncodedP() - - ------ Tagged ld64-89.2 -2008-10-23 Nick Kledzik +2010-03-19 Nick Kledzik - SnowLeopard: Libsystem built with ld64-89.1 causes crashes - * src/MachOReaderRelocatable.hpp: when FDE information causes __text atom to be split, make the - atoms follow-on pairs. + * ld64-114 does not error on missing exported symbols with -dead_strip - ------ Tagged ld64-89.1 - -2008-10-22 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: for x86_64 __eh_frame force direct references +-------- tagged ld64-114.4 +2010-03-16 Nick Kledzik -2008-10-21 Nick Kledzik - - * src/ObjectDump.cpp: Use getContentType() to see if content type is a cstring + * dyld missing LC_ID_DYLINKER +-------- tagged ld64-114.3 ------ Tagged ld64-89 +2010-03-15 Nick Kledzik -2008-10-21 Nick Kledzik + * force i386 kexts to be built with ld_classic + * preserve 'l' labels in static executables + * sync section offsets and addresses in segments with command line addresses - 10A180 with QT-1119 roots: iTunes and QuickTime cannot play back purchased videos - linker should not need .eh labels - * src/MachOWriterExecutable.hpp: use kCFIType to set section attributes - * src/MachOReaderRelocatable.hpp: use libunwind's CFITuple to parse __eh_frame content - * src/ld.cpp: Add adjustScope() phase instead of demoting scope within symboltable.add() - * unit-tests/test-cases/eh-stripped-symbols: added test case +-------- tagged ld64-114.2 ------ Tagged ld64-88.1 +2010-03-13 Nick Kledzik -2008-10-16 Nick Kledzik + * ld64-114 generates x86_64 kext external call sites with incorrect addend - * src/MachOReaderRelocatable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT - * src/MachOWriterExecutable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT +-------- tagged ld64-114.1 +2010-03-12 Nick Kledzik -2008-09-30 Nick Kledzik + * Fix dyldinfo tool to correct show ordinal info for classic linkedit - OBJC2: Reorder __DATA,__objc_* sections by writedness - * src/ld.cpp: change sorting order of Sections +2010-03-12 Nick Kledzik + ld64-114 is causing read_only_reloc verification errors for ppc + * Update machochecker to check this. -2008-09-29 Nick Kledzik +-------- tagged ld64-114 - Executable produced by XCode 3.2 on 10.6 crashes on 10.3.9 - * src/MachOWriterExecutable.hpp: set objc_module_info_addr field of module table - - ------ Tagged ld64-88 - -2008-09-25 Nick Kledzik - - kexts need to be built as MH_BUNDLE mach-o files - * src/ld.cpp: use getUndefinedProxyAtom() with kKextBundle - * src/MachOFileAbstraction.hpp: add MH_KEXT_BUNDLE - * src/Options.cpp: support -kext for all architectures - * src/MachOWriterExecutable.hpp: support kKextBundle to make a bundle like kext - * unit-tests/test-cases/kext-basic: added test case - - -2008-09-25 Nick Kledzik +2010-03-11 Nick Kledzik - ld invoking wrong ld_classic - * src/Options.cpp: first look for ld_classic relative to ld itself - + * i386 dylibs built with ld64-112 cause runtime errors when incorporated into the dyld shared cache + * Add -shared_region option to dyldinfo tool -2008-09-25 Nick Kledzik +-------- tagged ld64-113 - ld fails to link references from 32 bit code into 64 bit code - Desired 32-bit absolute relocation - * src/Architectures.hpp: add x86_64::kPointer32 - * src/MachOReaderRelocatable.hpp: support X86_64_RELOC_UNSIGNED with length=2 - * src/MachOWriterExecutable.hpp: support x86_64::kPointer32 - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added 32-bit pointer tests - +2010-03-11 Nick Kledzik -2008-09-25 Nick Kledzik - - Should be able to mark dylibs as auto-dead-dylib-strip - * src/Options.h: add fMarkDeadStrippableDylib - * src/MachOReaderDylib.hpp: check MH_DEAD_STRIPPABLE_DYLIB - * src/ObjectFile.h: add deadStrippable() - * src/MachOFileAbstraction.hpp: add MH_DEAD_STRIPPABLE_DYLIB - * src/Options.cpp: support -mark_dead_strippable_dylib - * src/MachOWriterExecutable.hpp: test reader->deadStrippable(), set MH_DEAD_STRIPPABLE_DYLIB - * doc/man/man1/ld.1: update man page - * unit-tests/test-cases/dead_strippable_dylib: added test case - + * Allow CPU_SUBTYPE_ARM_ALL .o files to be linked into any arm arch linkage -2008-09-25 Nick Kledzik +2010-03-11 Nick Kledzik - ER: Add -seg_page_size option - * src/Options.cpp: add -seg_page_size option - * src/MachOWriterExecutable.hpp: use new page size info when laying out segments - * doc/man/man1/ld.1: update man page - + ld64-112 with -undefined dynamic_lookup marks all symbols as being flat + * update test case at: unit-tests/test-cases/undefined-dynamic-lookup -2008-09-24 Nick Kledzik +2010-03-10 Nick Kledzik - -arch_errors_fatal not working - * src/ld.cpp: check fOptions.errorOnOtherArchFiles() - * src/Options.cpp: turn -arch_errors_fatal into fOptions.errorOnOtherArchFiles() + * prevent possible crash in warning about can't export hidden symbols +2010-03-10 Nick Kledzik -2008-09-24 Nick Kledzik + * make sure split-info data is zero terminated - CrashTracer: [USER] 1 crash in ld at ld: 0x5ce02 - * src/ld.cpp: abort if resolve() finds an unresolved reference, rather than allow a future crash - +-------- tagged ld64-112 -2008-09-24 Nick Kledzik +2010-03-09 Nick Kledzik - linker crashes linking X86-64 with -fwritable-strings - * src/MachOReaderRelocatable.hpp: handle unbound cfstring references - * unit-tests/test-cases/cfstring-coalesce: update test case - + * Never dead strip sections added with -sectcreate + * Added test case: unit-tests/test-cases/sectcreate-dead_strip -2008-09-24 Nick Kledzik - ld64: bl out of range (-17147704 max is +/-16M) on ppc - * src/MachOWriterExecutable.hpp: tweak branch island regions to be every 14MB instead of 15MB - +-------- tagged ld64-111 -2008-09-24 Nick Kledzik +2010-03-03 Nick Kledzik - -filelist fails with comma in path - * src/Options.cpp: in loadFileList() first try without special comma meaning - * unit-tests/test-cases/filelist/Makefile: update test case - + * Add support for "-arch arm -force_cpusubtype_ALL" to keep gcc building -2008-09-23 Nick Kledzik +2010-03-02 Nick Kledzik - nop not used when aligning functions in -r mode - * src/MachOWriterExecutable.hpp: change check for when to pad with nops to not test segment's name - + * Add some checking to the use of upward dylibs -2008-09-23 Nick Kledzik +-------- tagged ld64-110 - "-pie can only be used when linking a main executable" should be a warning, not an error - * src/Options.cpp: make -pie on a dylib or bundle be a warning instead of an error - - -2008-09-23 Nick Kledzik +2010-03-01 Nick Kledzik - * src/MachOReaderRelocatable.hpp: add warning if dwarf cannot be encoded as compact unwind - + * Don't coalesce cstrings across segments -2008-09-18 Nick Kledzik +2010-03-01 Nick Kledzik - * src/LTOReader.hpp: re-enable use of lto_codegen_debug_options() + * Emulate previous linker bug where hidden symbols with dynamically-referenced + bit were promoted to global. + * Added test case: unit-tests/test-cases/unstrippable-symbols +-------- tagged ld64-109.1 -2008-09-16 Nick Kledzik +2010-02-26 Nick Kledzik - ld does not always set S_CSTRING_LITERALS on __TEXT,__cstring - * src/MachOReaderRelocatable.hpp: add getContentType() to SymbolAtom - * src/MachOWriterExecutable.hpp: for x86_64 don't override named cstrings with LC* name + * Make sure building dyld results in a thread load command +-------- tagged ld64-109 -2008-09-10 Nick Kledzik - - * Options.cpp: add __crashreporter_info__ to communicate command line to crash reporter - * ld64.xcodeproj/project.pbxproj: leave local symbols in ld to provide better crash reports - +2010-02-26 Nick Kledzik -2008-09-08 Nick Kledzik + * Better sorting of zero-fill sections to preserve discovery order + * Zero out file offsets in dynamic symbol table entries that are not used - 161569 GCC 4.2 - breakpoints no longer work for a large number of functions - * src/MachOReaderRelocatable.hpp: support DW_FORM_strp out-of-line strings when parsing line table +2010-02-26 Nick Kledzik + * Support pointer-diffs to zero sized atom in zero sized section -2008-09-02 Nick Kledzik +2010-02-25 Nick Kledzik - * src/MachOReaderRelocatable.hpp: fix compact unwind personality for dyld and -slow_stubs - + * Add support for -r mode with ppc64 -2008-08-29 Nick Kledzik +2010-02-25 Nick Kledzik - -weak_library no longer forces uses to be weak_import - * src/MachOWriterExecutable.hpp: use fWeakImport on dylib to force proxy atoms into fWeakImportMap - * unit-tests/test-cases/weak_import-force: added test case + * Handle multiple labels on the same coalesable literal by making an + atom for each label, each with the same content. + * Add test case: unit-tests/test-cases/literals-labels -2008-08-29 Nick Kledzik - - linker should order __DATA segment to reduce dyld dirtied pages - * src/Options.cpp: add fOrderData and support -no_data_order - * src/ld.cpp: modify tweakLayout() to sort atoms with relocations to start of __data section +2010-02-25 Nick Kledzik + * Handle old ppc .o files that have stubs to static functions -2008-08-27 Nick Kledzik +2010-02-25 Nick Kledzik - * src/Options.cpp: back out + * Add basic ppc64 support +2010-02-24 Nick Kledzik ------ Tagged ld64-87.5 + * Range check TOC entries in archives -2008-08-26 Nick Kledzik - some projects show _Unwind_Resume coming from libSystem.B.dylib - * src/Options.cpp: swap any early symlinks to libSystem with libgcc_s +2010-02-23 Nick Kledzik + * Fix spurious dylib re-export warnings that are just regular linkage cycles ------ Tagged ld64-87.4 -2008-08-25 Nick Kledzik +2010-02-23 Nick Kledzik - some projects show _Unwind_Resume coming from libSystem.B.dylib - * src/Options.cpp: swap any early libSystem with libgcc_s - + * re-partition bits in mach_o::relocatable::Atom ivars to allow more functions per file -2008-08-15 Nick Kledzik - Unable to build ppc debug builds (linker out of range error) - * src/MachOWriterExecutable.hpp: in addPPCBranchIslands() look ahead so large atoms don't push out branch islands +2010-02-22 Nick Kledzik + * re-partition bits in mach_o::relocatable::Atom ivars to allow more fixups per function ------ Tagged ld64-87.3.1 - -2008-09-08 Nick Kledzik - i386 dylibs have incorrect personality pointers when put in dyld shared cache - * src/MachOWriterExecutable.hpp: in addCrossSegmentRef() handle kImageOffset32 to __IMPORT segment +2010-02-22 Nick Kledzik + * Handle re-exported dylibs that are re-exported again + * Added test case: unit-tests/test-cases/re-export-layers ------ Tagged ld64-87.3 -2008-08-09 Nick Kledzik +2010-02-22 Nick Kledzik - work around compiler gcc_except_table alignment - * src/ObjectFile.h: change getLSDA() to return a reference instead of an atom - * src/MachOReaderRelocatable.hpp: special case __eh_frame 64-bit pointer diff relocations - * src/MachOWriterExecutable.hpp: track lsda offset when creating __unwind_info section - * src/UnwindDump.cpp: log when LDSA content does not start with 0xFF - ------ Tagged ld64-87.2 - -2008-08-07 Nick Kledzik - - 10A141: libuwind falls back to dwarf and makes whole system super slow - * src/MachOWriterExecutable.hpp: Fix sign extension bug with x86_64::kPointerDiff24 - * src/UnwindDump.cpp: warn about mangled LSDA entries when dumping unwind section + * Properly handle X86_64_RELOC_SUBTRACTOR with non-external target symbol ------ Tagged ld64-87.1 - -2008-08-03 Nick Kledzik +-------- tagged ld64-108 - * src/LTOReader.hpp: Don't use lto_codegen_debug_options until newer libLTO.dylib is available +2010-02-17 Nick Kledzik - ------ Tagged ld64-87 - -2008-07-21 Nick Kledzik - - * src/Options.cpp: Always set fAutoOrderInitializers=false for dyld + * ER: Support upward dylib dependencies + * Add test case: unit-tests/test-cases/dylib-upward -2008-07-21 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix when regular vs compressed __unwind_info pages are generated - * src/UnwindDump.cpp: fix function name decoding in regular pages - - -2008-07-21 Nick Kledzik +2010-02-17 Nick Kledzik - * ld64.xcodeproj/project.pbxproj: don't allow ld to build for x86_64 until libdtrace.dylib is available + * ld(1) man page typo + * Linker (ld) man page typo: "unredable" in -pagezero_size option description + * Typo in ld(1) man page, "-x" option + * man ld: Change "if" -> "is" + * DOC: ld(1) mentions -dynamiclib when it means -dylib -2008-07-18 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't crash if debug_line section has no line table - - -2008-07-18 Nick Kledzik - - Duplicate probe firings in Security.framework - * src/LTOReader.hpp: optimize() now returns atoms optimized away - * src/ObjectFile.h: optimize() should return if it did anything - * src/ArchiveReader.hpp: pass through optimize() result - * src/ld.cpp: rework dtrace probe processing as a new pass to prevent double counting - - -2008-07-15 Nick Kledzik +2010-02-17 Nick Kledzik - automatically order initializers to start of __TEXT - * src/Options.cpp: add -no_order_inits option - * src/MachOReaderRelocatable.hpp: merge __StaticInit into __text - * src/ObjectFile.h: add fAutoOrderInitializers - * src/ld.cpp: sort initializer to start of __text and terminators to end - * doc/man/man1/ld.1: add doc about -no_order_inits - * unit-tests/test-cases/init-order: add test case + * Wordsmith ld warning about missing directories -2008-07-15 Nick Kledzik - - Only add LC_SEGMENT_SPLIT_INFO to dylibs that might be in the shared cache - * src/MachOWriterExecutable.hpp: re-layout load commands after split-seg data computed - * src/Options.cpp: non-public install name will disable split-seg load command - - -2008-07-14 Nick Kledzik - - ld -r for x86_64 is changing visibility of cstring constants - * src/MachOWriterExecutable.hpp: force x86_64 cstring labels to be local in -r mode - * unit-tests/test-cases/cstring-label: added test case - -2008-07-11 Nick Kledzik +2010-02-17 Nick Kledzik - ld not adding updating LC_SEGMENT_SPLIT_INFO with __unwind_info section - * src/MachOWriterExecutable.hpp: run createSplitSegContent() after __unwind_info section is created + * Fix -umbrella to work when umbrella is a dylib instead of a framework + * Add test case: unit-tests/test-cases/umbrella-dylib -2008-07-10 Nick Kledzik - - * src/LTOReader.hpp: improve missing symbol error message +-------- tagged ld64-107 -2008-07-09 Nick Kledzik +2010-02-16 Nick Kledzik - linker should order __DATA segment to reduce dyld dirtied pages - * src/ld.cpp: first phase, order sections + * Fix bugs with -preload -2008-07-08 Nick Kledzik +2010-02-16 Nick Kledzik - * src/MachOReaderRelocatable.hpp: remove "coal" sections when creating a final linked image + * Fix dylib re-export cylce detection -2008-07-08 Nick Kledzik +2010-02-16 Nick Kledzik - ld: add support for mllvm LTO options - * src/Options.cpp: support -mllvm option - * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options - * src/ld.cpp: pass llvmOptions to optimize() - * src/Options.h: add fLLVMOptions - * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() - * src/ObjectFile.h: add llvmOptions parameter to optimize() - * unit-tests/test-cases/lto-llvm-options: add test case + * -ObjC not pulling in members with categories only + * scan for non-zero __objc_catlist section in archive members when -ObjC is used + * Added test case: unit-tests/test-cases/objc-category-archive -2008-07-07 Nick Kledzik - - Linker fails with: 24-bit pointer diff out of range in unwind info in unwind info from... - * src/MachOWriterExecutable.hpp: fix when to fallback to uncompressed unwind info - +2010-02-15 Nick Kledzik -2008-07-03 Nick Kledzik + * ld glibly removes /dev/null - ld crash with gcc-4.0 code that uses a zero sized array - * src/MachOReaderRelocatable.hpp: handle zero size atom in a zero sized section +2010-02-15 Nick Kledzik -2008-07-03 Nick Kledzik + * Linker should be able to validate GC intentions + * Add -objc_gc and -objc_gc_only. Error when used and RR based .o file is linked in + * Update test case: unit-tests/test-cases/objc-gc-checks - ld crashes when bad ppc relocs are found - * src/MachOReaderRelocatable.hpp: change all missing PAIR warnings to errors - -2008-07-02 Nick Kledzik +2010-02-15 Nick Kledzik - when linking a kext the static linker should leave a pad in the headers to allow code signing - * src/MachOWriterExecutable.hpp: add padding for load commands in object files - * unit-tests/test-cases/code-signed-object-file: added test case + * Linker should provide a way to mark binaries that support compaction + * Added -objc_gc_compaction option + * Update test case: unit-tests/test-cases/objc-gc-checks -2008-07-02 Nick Kledzik - - LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype - * src/MachOWriterExecutable.hpp: correctly set segment size info in object files - * unit-tests/test-cases/no-object-symbols: add test case - - -2008-06-26 Nick Kledzik +2010-02-15 Nick Kledzik - * ld64.xcodeproj/project.pbxproj: enable ld and rebase targets to build for x86_64 - * src/rebase.cpp: remove unused fRelocBase field that was not 64-bit clean - * src/MachOReaderRelocatable.hpp: fix getEncodedP() to be 64-bit clean + * ER: Need a way to detect weak exports in dev tools + * implement -warn_weak_exports +2010-02-15 Nick Kledzik ------ Tagged ld64-86.3 - -2008-06-17 Nick Kledzik - - * src/ld.cpp: fix loadUndefines() to double check undefine symbol was not already loaded - - ------ Tagged ld64-86.2 - -2008-06-14 Nick Kledzik + * Add support for LD_DYLD_PATH - * srd/ld.cpp: Add NULL check in getTentativesNames() - - ------ Tagged ld64-86.1 -2008-06-06 Nick Kledzik +2010-02-15 Nick Kledzik - * src/MachOWriterExecutable.hpp: fix header padding calculation for dyld + * cfstring backing store points to global cstring + * Force all by-name references in cfstring to be direct references + * add test case: unit-tests/test-cases/cfstring-and-cstring ------ Tagged ld64-86 +-------- tagged ld64-106 -2008-06-04 Nick Kledzik +2010-02-12 Nick Kledzik - * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + * Assertion failed: when class is translation unit scoped + * added test case unit-tests/test-cases/objc-visibility -2008-06-04 Nick Kledzik - - * src/ObjectFile.h: add deadAtoms parameter to optimize() - * src/ld.cpp: ditto - * src/ArchiveReader.hpp: ditto - * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs - * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away - * unit-tests/test-cases/lto-weak-native-override: add test case +2010-02-12 Nick Kledzik + * crash with missing crt? -2008-06-04 Nick Kledzik - LTO : 176.gcc and 177.mesa build failure at -O4 - * src/LTOReader.hpp: make sure internal is returned by getAtoms() - * unit-tests/test-cases/lto-archive-dylib: update test case +2010-02-12 Nick Kledzik + * Suppress indirect symbol table in static executables + -2008-06-03 Nick Kledzik +2010-02-12 Nick Kledzik - fix for 5613343 need to search for definitions for common symbols is broken - * src/ld.cpp: modify loadUndefines() to check for undefines in all files and tentative definitions but only in archives - * src/machochecker.cpp: check for undefine symbols and external symbols with same name - * unit-tests/test-cases/tentative-and-archive: update test case + * Rework CIE parsing to work with icc generated code -2008-06-03 Nick Kledzik +2010-02-11 Nick Kledzik - linker produces wrong result for 16-bit call relocations - * src/MachOReaderRelocatable.hpp: properly parse i386 scattered relocs for word sized pc-rel vanilla - * src/MachOWriterExecutable.hpp: propery compute displacement for x86::kPCRel16 - * unit-tests/test-cases/relocs-asm: update test case with callw instructions - + * Fix creation of debug notes + * Tweak unit-tests/test-cases/dwarf-debug-notes to match llvm symbol layout -2008-06-03 Nick Kledzik - Building kext x86_64 with unexported symbols file causes linking problems - * src/MachOWriterExecutable.hpp: better check when creating undefined proxy atoms - * unit-tests/test-cases/unexported_symbols_list-r: added test case - +2010-02-11 Nick Kledzik -2008-06-02 Nick Kledzik + * Don't assert when infering ppc subtype that is ALL. + * Fix spurious warning about mismatched subtypes, when subtype is inferred - S_CSTRING_LITERALS section type not preserved in executable - * src/ObjectFile.h: added ContentType - * src/MachOReaderRelocatable.hpp: set ContentType for anonymous string literals - * src/MachOWriterExecutable.hpp: set S_CSTRING_LITERALS if ContentType is kCStringType - * unit-tests/test-cases/cstring-custom-section: added test case - -2008-06-02 Nick Kledzik - - linker should produce __unwind_info section in final linked images - * src/ld.cpp: sort __unwind_info then __eh_frame section to end of __TEXT - * src/Architectures.hpp: add kImageOffset32 and kPointerDiff24 - * src/ObjectFile.h: add compact unwind info support - * src/MachOReaderRelocatable.hpp: add compact unwind info support - * src/MachOFileAbstraction.hpp: add C++ wrappers for unwind section layout - * src/UnwindDump.cpp: new tool for dumping __unwind_info section - * src/MachOWriterExecutable.hpp: create __unwind_info section when needed - * src/ObjectDump.cpp: print unwind info - +-------- tagged ld64-105 -2008-06-02 Nick Kledzik +2010-02-11 Nick Kledzik - * unit-tests/test-cases/llvm-integration: split out some test cases - * unit-tests/test-cases/lto-preload-pie: added - * unit-tests/test-cases/lto-archive-dylib: added + * Use symbolic constants for bit field sizes -2008-05-30 Nick Kledzik +2010-02-10 Nick Kledzik - * unit-tests: fixes to build all tests with with gcc-4.2 on SnowLeopard - + * Handle out of order sections in .o files -2008-05-30 Nick Kledzik - support -preload option to generate MH_PRELOAD binaries compatible with mtoc(1) and EFI - * src/ld.cpp: add entryPoint parameter to optimize() - * src/ArchiveReader.hpp: ditto - * src/ObjectFile.h: ditto - * src/LTOReader.hpp: use entryPoint parameter to optimize() - * src/Options.h: add kPreload and segment alignment - * src/Options.cpp: support -preload and -segalign - * src/MachOWriterExecutable.hpp: support kPreload and non-page aligned segments +-------- tagged ld64-104 +2010-02-10 Nick Kledzik -2008-05-30 Nick Kledzik + * Rename __tentative internal section name to __comm/tent to sort like __common + * Fix test case in unit-tests/test-cases/tentative-to-real-r - ld should warn if passed -r and also dylibs - * src/ld.cpp: check for spurious dylibs in Linker::addDylib() +2010-02-10 Nick Kledzik ------ Tagged ld64-85.6 + * Better warning messages about mismatched architectures -2008-11-01 Nick Kledzik - - support increased branch range in Thumb-2 - * src/MachOWriterExecutable.hpp: in fixUpReferenceFinal() support new longer branch range - -2008-11-01 Nick Kledzik - - ld warning: unknown option to -iphoneos_version_min, not 1.x or 2.x - * src/Options.cpp: In setIPhoneVersionMin() support 3.x +2010-02-10 Nick Kledzik - ------ Tagged ld64-85.5 + * Gracefully ignore if there are >8000 line info per function in debug info -2008-09-17 Nick Kledzik - vtable pointers can be missing thumb bit - * src/MachOWriterExecutable.hpp: Writer::fixUpReferenceFinal() OR in the 1 bit if the target - of a arm::kReadOnlyPointer is thumb. +2010-02-10 Nick Kledzik + * Properly handle when regular definition is weak_imported + * Add unit-tests/test-cases/weak_import-local ------ Tagged ld64-85.4 -2008-08-11 Nick Kledzik +2010-02-10 Nick Kledzik - ld should ignore LD_PREBIND when processing a static archive - * src/MachOWriterExecutable.hpp: in setImportNlist() never use N_PBUD for object files + * Don't try to coalesce zero length cstrings in mach-o parser. ------ Tagged ld64-85.3 -2008-07-14 Nick Kledzik +2010-02-10 Nick Kledzik - Prebinding busted in DTSB - * src/Options.cpp: check for libstdc++.6.0.[49] in seg_addr_table + * Add work around for llvm using L labels for backing string of CFString with -fwritable-strings ------ Tagged ld64-85.2 - -2008-05-06 Nick Kledzik - - ARM ld should take W bit off of maxprot for __TEXT segment - * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments +2010-02-09 Nick Kledzik + * Ignore labels in __gcc_except_tab section + * Properly apply local relocations to __eh_frame section so CFI parser works + * Update unit-tests/test-cases/eh-stripped-symbols to reproduce problem -2008-05-06 Nick Kledzik - encryptable images may not be signable - * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section - - ------ Tagged ld64-85 (Xcode 3.1) - -2008-04-29 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: is moving from /usr/local/include to /Developer/usr/local/include +2010-02-09 Nick Kledzik + * Fix file offset computation with large zero-fill sections + -2008-04-29 Nick Kledzik +2010-02-09 Nick Kledzik - ld doesn't honor "rightmost" -syslibroot argument - * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + * Force global 'l' to be hidden + * Add test case with objc properties: unit-tests/test-cases/objc-properties -2008-04-29 Nick Kledzik - - GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files - * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment - * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment +-------- tagged ld64-103 +2010-02-04 Nick Kledzik -2008-04-17 Nick Kledzik + * Temporarily change assert() to call exit(1) instead of abort() - * src/MachOReaderRelocatable.hpp: better cpu subtype support +2010-02-04 Nick Kledzik -2008-04-14 Nick Kledzik + * Fix another case in -r mode where the vmsize was less that filesize - ld64 has bad ARM branch island check - * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail +2010-02-04 Nick Kledzik -2008-04-10 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs + * Fix assert when generating GSYM stab debug notes ------ Tagged ld64-84.4 - -2008-04-10 Nick Kledzik +2010-02-04 Nick Kledzik - SPEC2000/eon built with -mdynamic-no-pic won't run - * src/Architectures.hpp: added arm::kReadOnlyPointer - * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer - * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer - * src/machochecker.cpp: allow MH_PIE bit - * unit-tests/test-cases/switch-jump-table: added test cases - - ------ Tagged ld64-84.3 + * Add SRCROOT to crash logs -2008-04-09 Nick Kledzik - - -undefined dynamic_lookup busted - * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates - * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup +2010-02-04 Nick Kledzik ------ Tagged ld64-84.2 + * Remove architectureName() from InputFiles -2008-04-04 Nick Kledzik - * src/ld.cpp: don't add .eh symbols to symbol table in -r mode - * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing - +-------- tagged ld64-102 ------ Tagged ld64-84.1 +2010-02-03 Nick Kledzik -2008-03-28 Nick Kledzik + * Add follow-on reference from symbol text atom to non-symboled text atom - ld should prefer architecture-specific variant over generic in fat object file - * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture - * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files - * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc - ------ Tagged ld64-84 +-------- tagged ld64-101 -2008-03-28 Nick Kledzik +2010-01-29 Nick Kledzik - * src/LTOReader.hpp: don't print lto version, if lto is unavailable + * fix -alias symbols to be global by default -2008-03-26 Nick Kledzik +-------- tagged ld64-100 - Add LD_WARN_COMMONS to BigBear builds - * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file +2010-01-28 Nick Kledzik - -2008-03-26 Nick Kledzik - - Need encryption tag in mach-o file - linker should adjust arm final linked images so __text is never on the same page as the load commands - * src/MachOFileAbstraction.hpp: add support for encryption_info_command - * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption - * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom - * src/machochecker.cpp: validate LC_ENCRYPTION_INFO + * Merge new/refactored linker to trunk - -2008-03-25 Nick Kledzik - - ld64 does not recognize LLVM bitcode archive files - * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp - * src/ArchiveReader.hpp: sniff each member and instantiate correct reader - * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader - * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp - * unit-tests/test-cases/llvm-integration: added test case - - -2008-03-25 Nick Kledzik - - ld64 should switch to new libLTO.dylib interface - Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc - * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface - * unit-tests/test-cases/llvm-integration: update and comment - * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib - * src/ld.cpp: rework and simplify Linker::optimize() - * src/ObjectDump.cpp: Add -nm option - - -2008-03-25 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem - * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem - - -2008-03-24 Nick Kledzik - - Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 - * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. - - -2008-03-21 Nick Kledzik - - * src/Options.cpp: warn if -seg1addr value is not page aligned - - -2008-03-21 Nick Kledzik - - Move ARM support outside of __OPEN_SOURCE__ - * src/ld.cpp: remove __OPEN_SOURCE__ around arm support - * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support - * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h - - ------ Tagged ld64-83.2 - -2008-03-15 Nick Kledzik - - ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results - * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files - * unit-tests/test-cases/objc-exported_symbols_list: added test case - - ------ Tagged ld64-83.1 - -2008-03-14 Nick Kledzik - - -iphone_version_min ==> -iphoneos_version_min - * src/Options.cpp: support -iphoneos_version_min as well - - ------ Tagged ld64-83 - -2008-03-10 Nick Kledzik - - ld needs to strip iphone_version_min option if invoking ld_classic - * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic - - -2008-03-04 Nick Kledzik - - ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) - * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs - * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework - * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools - * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() - * src/ld.cpp: pass lazy helper atom to writer - * doc/man/man1/ld.1: document new options - * unit-tests/test-cases/lazy-dylib-objc: add test case - * unit-tests/test-cases/lazy-dylib: add test case - - ------ Tagged ld64-82.7 - -2008-03-07 Nick Kledzik - - duplicate symbol literal-pointer@__OBJC@__message_refs@... - * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak - * unit-tests/test-cases/objc-selector-coalescing: added test case - - ------ Tagged ld64-82.6 - -2008-03-04 Nick Kledzik - - ld crashes building XsanFS for Snow Leopard Builds - * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() - * unit-tests/test-cases/tentative-and-archive: added test case - -2008-03-04 Nick Kledzik - - ld64 should not force building with gcc 4.0 - * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 - - -2008-02-29 Nick Kledzik - - Simulator frameworks are being build split-seg and not prebound - * src/Options.cpp: only splitseg if prebound - - -2008-02-29 Nick Kledzik - - Linker should not make GSYM debug note for .objc_category_* symbols - * src/ld.cpp: suppress GSYM debug notes for absolute symbols - * unit-tests/test-cases/objc-category-debug-notes: added test case - - -2008-02-29 Nick Kledzik - - non-ASCII CFString support is broken - * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring - * unit-tests/test-cases/cfstring-utf16: add test case - - -2008-02-25 Nick Kledzik - - ld -r -x - * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels - - ------ Tagged ld64-82.5 - -2008-02-12 Nick Kledzik - - x86_64: -stack_size failure when large __bss is used - * src/ld.cpp: only move section already in __DATA segment to new __huge section - * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section - - ------ Tagged ld64-82.4 - -2008-02-06 Nick Kledzik - - comdat warnings with ld -r of C++ .o files - * unit-tests/test-cases/eh-coalescing-r: added test case - * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static - - -2008-02-06 Devang Patel - - LTO of Bom framework with -dead_strip causes ld(1) crash - * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. - * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. - * unit-tests/test-cases/llvm-integration/a15.c: New. - * unit-tests/test-cases/llvm-integration/b15.c: New. - * unit-tests/test-cases/llvm-integration/c15.c: New. - -2008-02-05 Nick Kledzik - - * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used - ------ Tagged ld64-82.3 - -2008-02-04 Nick Kledzik - - ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves - * src/ObjectFile.h: add 10.6 - * src/Options.cpp: add 10.6 support - * src/MachOReaderDylib.hpp: recognize $os10.6$ - - ------ Tagged ld64-82.2 - -2008-01-30 Devang Patel - - Can't build 64-bit Intel binaries with LTO - ld64 fails to build with llvm-gcc-4.2 - * src/LLVMReader.hpp: Fix character count typo in strncmp call. - Use const char * to initialize temp. string. - * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction - instead of hard coding /Developer. - ------ Tagged ld64-82.1 - -2008-01-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs - - -2008-01-22 Nick Kledzik - - ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files - * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs - * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs - * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files - - ------ Tagged ld64-82 - -2008-01-18 Nick Kledzik - - Bad grammar used in ld warning: cannot exported hidden symbol - * src/ld.cpp: fix typo in warning string - - -2008-01-16 Nick Kledzik - - Bundle Loader does not work anymore when loader is a bundle - ld warns of incorrect architecture when linking a bundle to a bundle - * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages - * unit-tests/test-cases/bundle_loader: update test case - - -2008-01-16 Nick Kledzik - - ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) - * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S - - -2008-01-16 Nick Kledzik - - if ld crashes while writing output file, it should delete the half written file - * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete - output file on failure. - - -2008-01-16 Devang Patel - - * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. - - -2008-01-16 Nick Kledzik - - GC-supported library can't be linked into GC-required executable - * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and - allow gc-compatible code to be linked into anything. - * unit-tests/test-cases/objc-gc-checks: update test case - - -2008-01-15 Nick Kledzik - - no debug notes for custom named data - * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore - * unit-tests/test-cases/dwarf-debug-notes: update test case - ------ Tagged ld64-81.5 - -2008-01-14 Devang Patel - - llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 - * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references - after optimization. - * src/ld.cpp: Resolve additional unbounded references after optimization. - - -2008-01-14 Nick Kledzik - - PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes - * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs - * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs - - -2008-01-11 Nick Kledzik - - PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" - * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions - - -2008-01-11 Nick Kledzik - - * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list - - -2008-01-11 Nick Kledzik - - ld64(1) man page uses ambiguous term "suffix" - * doc/man/man1/ld.1: make meaning of "suffix" more explicit - - -2008-01-11 Nick Kledzik - - Obj-C Symbols in Leopard Can't Be Weak Linked - * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines - to dylibs to support Mac OS X 10.3.x dyld - - -2008-01-11 Nick Kledzik - - Unknown error with linker (dyld: unknown external relocation type) - * src/ld.cpp: fix crash when SO stabs are not balanced - - -2008-01-11 Devang Patel - - LTO does not work if expected output is a dynamic library - * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate - visibility info. - -2000-01-10 Nick Kledzik - - __cls_refs section is losing S_LITERAL_POINTERS section type - * src/MachOWriterExecutable.hpp: special case __cls_refs section - * unit-tests/test-cases/objc-literal-pointers: add test case - - -2008-01-03 Nick Kledzik - - wrong EH information might be used - Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom - has kGroupSubordinate references to the other atoms in the group. If the signature atom - is coalesced away, the linker follows kGroupSubordinate references and throws away the - other members of the group. - * unit-tests/test-cases/eh-coalescing: added test case - * src/ld.cpp: added markDead() and use propagate to subordinates - * src/Architectures.hpp: added kGroupSubordinate - * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom - and if used, from .eh atom to its LSDA atom. - * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp - ------ Tagged ld64-81.4.1 - -2007-12-19 Devang Patel - - * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. - -2007-12-19 Devang Patel - - * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). - -2007-12-19 Devang Patel - - print LLVM LTO version number in verbose mode - * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. - * src/Options.cpp: Use printLLVMVersion() in verbose mode. - -2007-12-19 Devang Patel - - print LLVM LTO version number in verbose mode - * src/Options.h: Add verbose() method to check fVerbose flag. - * src/LLVMReader.hpp: Print LLVM version string in verbose mode. - ------ Tagged ld64-81.4 - -2007-12-18 Devang Patel - - * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. - ------ Tagged ld64-81.3 - -2007-12-17 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths - - -2007-12-17 Devang Patel - - * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to - dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. - - -2007-12-14 Nick Kledzik - - gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) - * src/MachOWriterExecutable.hpp: fix Writer::generatesExternalTextReloc() to allow text relocs - * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static - - -2007-12-14 Devang Patel - - Enable Link Time Optimization in Opal - * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. - * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. - * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. - * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. - - -2007-12-13 Nick Kledzik - - SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... - * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly - ------ Tagged ld64-81.2 - - - -2007-12-07 Nick Kledzik - - support 8-bit relocations for i386 - * src/Architectures.hpp: add kPCRel8 - * src/MachOReaderRelocatable.hpp: support 8-bit pc-rel relocations for intel - * src/MachOWriterExecutable.hpp: support 8-bit pc-rel relocations for intel - * unit-tests/test-cases/relocs-asm: add test cases - - ------ Tagged ld64-81.1 - -2007-12-06 Nick Kledzik - - * src/MachOReaderDylib.hpp: rework cycle detection to remove some false positives - - -2007-12-05 Nick Kledzik - - Duplicate probe firings in Security.framework - * src/ld.cpp: check dtrace probe sites are not in fDeadAtoms before using - * unit-tests/test-cases/dtrace-static-probes-coalescing: add test case - - -2007-12-05 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix CFString coalescing to work with -fwritable-strings - * unit-tests/test-cases/cfstring-coalesce: add -fwritable-strings to test case - - ------ Tagged ld64-81 - -2007-11-15 Nick Kledzik - - ld64 should support runtime text relocations - * src/MachOWriterExecutable.hpp: add generatesLocalTextReloc() and generatesExternalTextReloc() - * src/Options.cpp: process -read_only_relocs option - * src/Options.h: add allowTextRelocs() and warnAboutTextRelocs() - * src/MachOReaderRelocatable.hpp: add hasLongBranchStubs() - * src/machochecker.cpp: allow relocs in read only segments, if section flags are set - * unit-tests/test-cases/read-only-relocs: update test case - - -2007-11-08 Devang Patel - - * ld64.xcodeproj/project.pbxproj: add new build phase "build configure.h" for - ld target. - * src/ld.cpp: Include "configure.h" - - ------ Tagged ld64-80.11 - -2008-02-12 Nick Kledzik - - Wrong section name for objc info for ARM when OBJC2 is used - * src/MachOWriterExecutable.hpp: switch segment/section name for ARM objc2 image info - ------ Tagged ld64-80.10 - -2008-02-11 Nick Kledzik - - ld64 does not support -aspen_version_min 2.0 - * src/Options.cpp: allow 2.x for -aspen_version_min - - -2008-02-11 Nick Kledzik - - ld_classic: unknown flag: -aspen_version_min - * src/Options.cpp: change -aspen_version_min x.x to -macosx_version_min 10.5 when invoking ld_classic - - ------ Tagged ld64-80.9 - -2008-01-29 Nick Kledzik - - -iphone_version_min ==> -aspen_version_min - * src/Options.cpp: support -aspen_version_min - - ------ Tagged ld64-80.8 - -2008-01-10 Nick Kledzik - - * src/Options.cpp: support transition to new objc ABI for ARM by allowing old .objc_class_name_* - style names in export files and map them to new _OBJC_CLASS_$_ style names. - - ------ Tagged ld64-80.7 - -2008-01-02 Nick Kledzik - - BigBear5A18 isn't fully prebound - * src/Options.cpp: make fNeedsModuleTable true for arm - ------ Tagged ld64-80.6 - -2007-11-30 Nick Kledzik - - -iphone_version_min - * src/Options.cpp: handle -iphone_version_min option - - ------ Tagged ld64-80.5 - -2007-11-26 Nick Kledzik - - need to special case some dylibs in seg_addr_table - * src/Options.cpp: retry seg_add_table lookup for a couple of unusual dylibs - - ------ Tagged ld64-80.4 - -2007-11-06 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix parsing of external and scattered thumb branch22 relocs - * unit-tests/test-cases/thumb-blx: add test case to keep blx issues from coming back - ------ Tagged ld64-80.3 - -2007-11-03 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: remove recalc of dstAddr which could cause thumb branches to be +2 - * src/MachOWriterExecutable.hpp: remove incorrect test for relocateableExternal - ------ Tagged ld64-80.2 - -2007-11-01 Nick Kledzik - - * src/ld.cpp: hack my own prototype for log2() until math.h is cleaned up - - ------ Tagged ld64-80.1 - -2007-11-01 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: add HEADER_SEARCH_PATHS for cross builds - * src/ld.cpp: temporarily disable LLVM_SUPPORT - * src/MachOWriterExecutable.hpp: Don't use CC_MD5() directly - - -2007-10-26 Nick Kledzik - - Cannot build with libm_static.a statically linked - * src/MachOWriterExecutable.hpp: Fix makesExternalRelocatableReference() for -r -d case - * unit-tests/test-cases/tentative-to-real-hidden: add test case - - ------ Tagged ld64-80 - -2007-10-24 Nick Kledzik - - linker should probably warn about trying to export a hidden symbol - * src/ld.cpp: if using -exported_symbols_list check each hidden atom as it is added to symbol table - * src/Options.h,.cpp: add hasExportMaskList() - * unit-tests/test-cases/exported_symbols_list-hidden: added test case - - -2007-10-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: keep old style dtrace probes externel for kernel builds - - -2007-10-23 Nick Kledzik - - unify error and warning messages - -w should suppress warnings - * src/ld.cpp: use warning() function - * src/Options.h: remove emitWarnings() - * src/MachOReaderDylib.hpp: use warning() function - * src/MachOReaderRelocatable.hpp: use warning() function - * src/Options.cpp: use and implement warning() - * src/MachOWriterExecutable.hpp: use warning() function - * unit-tests/test-cases/visibility-warning: verify -w suppresses warnings - - -2007-10-23 Devang Patel - - * src/ld.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/LLVMReader.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/MachOReaderDylib.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/ObjectFile.h: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/MachOReaderRelocatable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/ObjectDump.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. - - -2007-10-22 Nick Kledzik - - * src/Options.cpp: add support for LD_DEAD_STRIP and LD_WARN_COMMONS - * src/MachOReaderRelocatable.hpp: fix problem with -dead_strip of ObjC literal pointers - - -2007-10-22 Nick Kledzik - - * src/Options.cpp: have -static arm code link with ld_classic (for now) - - -2007-10-22 Nick Kledzik - - Recognize all arm architectures - * src/MachOReaderRelocatable.hpp: add support for all ARM sub-types - * unit-tests/test-cases/cpu-sub-types: add test cases for all combinations of ARM sub-types - - -2007-10-19 Nick Kledzik - - * src/*: merge in arm support - * unit-tests/test-cases/*: fix to work for arm and thumb - ------ Tagged ld64-79 - -2007-10-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: if -r mode, always set custom alignment (SET_COMM_ALIGN) on common symbols - * unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile: fix warning - * unit-tests/test-cases/static-executable/Makefile: fix spurious failure - - -2007-10-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix edge case in branch island generation - - -2007-10-12 Nick Kledzik - - Add option to create old, slow stubs for i386 - * src/ObjectFile.h/.cpp: support -read_only_stubs - * src/MachOWriterExecutable.hpp: enhance StubAtom to support old style __symbol_stub/__la_symbol_ptr stubs - * unit-tests/test-cases/slow-x86-stubs: add test case - - -2007-10-12 Nick Kledzik - - ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - * src/Options.cpp: in findFileUsingPaths() don't search for embedded dylibs - * unit-tests/test-cases/indirect-path-search/Makefile: added case for a dylib embedded in a framework - - -2007-10-11 Nick Kledzik - - add option to disable implicit load commands for indirectly used public dylibs - * src/Options.cpp: add support for -no_implicit_dylibs - * src/ObjectFile.h: add fImplicitlyLinkPublicDylibs - * src/MachOReaderDylib.hpp: test fImplicitlyLinkPublicDylibs before hoisting an implicitly linked dylib - * unit-tests/test-cases/implicit_dylib: add test case - - -2007-10-11 Nick Kledzik - - -interposable_list - * src/Options.h/cpp: Add fInterposeList and fInterposeMode to support -interposable_list - * src/MachOWriterExecutable.hpp: pass symbol name to fOptions.interposable() - * unit-tests/test-cases/interposable_list: add test case - - -2007-10-10 Nick Kledzik - - If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - * src/MachOWriterExecutable.hpp: automatically use LC_LOAD_WEAK_DYLIB if all symbols used from a dylib are weak_import - * unit-tests/test-cases/weak_dylib: added test case - - -2007-10-10 Nick Kledzik - - linker does not error when dylib ordinal exceeds 250 - * src/MachOWriterExecutable.hpp: error out if ordinals exceed max allowed - - -2007-10-10 Nick Kledzik - - overriding 'operator new' or 'operator delete' fails if no weak symbols are present - * src/ld.cpp: at end of checkUndefines() search dylibs for weak versions of any global external symbols - * src/ObjectFile.h: add hasWeakExternals() method to Reader - * src/MachOReaderDylib.hpp: implement hasWeakExternals() method in Reader - * src/ExecutableFile.h: add overridesDylibWeakDefines parameter to write() - * src/MachOWriterExecutable.hpp: use overridesDylibWeakDefines parameter to write() - * unit-tests/test-cases/operator-new: add test case - - -2007-10-05 Nick Kledzik - - No warning about tentative definition conflicting with dylib definition - .comm variables in shared library, worked with XCode 2.4.1, broken with XCode 3? - * src/ld.cpp: at end of checkUndefines() verify if any remaining commons conflict with dylibs - * doc/man/man1/ld.1: document -commons and -warn_commons options - * unit-tests/test-cases/tentative-and-dylib: added test case - - -2007-10-05 Nick Kledzik - - NS/CFString constants are not dead strippable - * src/MachOReaderRelocatable.hpp: break up __cfstring section into one atom per cfstring, make them coalesable - * unit-tests/test-cases/cfstring-coalesce: added test case - - -2007-10-05 Nick Kledzik - - Dead stripping + exported symbols list using wildcards doesn't seem to do the right thing - * src/Options.cpp/h: add hasWildCardExportRestrictList() - * src/ld.cpp: if dead stripping code and have wildcard exports, add all global atoms matching wildcards as roots - * unit-tests/test-cases/exported-symbols-wildcards-dead_strip: added test case - - -2007-10-04 Nick Kledzik - - ld shouldn't search /Network/Library/Frameworks by default - * src/Options.cpp: remove /Network/Library/Frameworks/ from default search path - * doc/man/man1/ld.1: document the change - - -2007-10-04 Nick Kledzik - - all binaries should get LD_UUID load commands, not just those with DWARF symbols - * src/ld.cpp: default fCreateUUID to be true for non object file output types - * unit-tests/test-cases/no-uuid/Makefile: update test case to match new rules - - ------ Tagged ld64-78 - -2007-09-27 Nick Kledzik - - range check load commands - * src/MachOReaderDylib.hpp: check that load commands all fit in load command size from header - * src/MachOReaderRelocatable.hpp: check that load commands all fit in load command size from header - - -2007-09-27 Nick Kledzik - - Xc8M2540a: ld64 crashes when linking Pascal program - * src/ld.cpp: fix findAtomAndOffset() to handle where there are no function atoms - - -2007-09-27 Nick Kledzik - - ADOBE Xcode 3: ld -dead_strip does not work with -init from an archive - * src/ld.cpp: add bool parameter to entryPoint() so -init atom not looked for too soon - * unit-tests/test-cases/dead_strip-init-archive: added test case - - -2007-09-26 Nick Kledzik - - Spurious link warnings for inline members of C++ template classes - * src/ld.cpp: check definition kinds before warning about visibility mismatches - * unit-tests/test-cases/visibility-warning: added test case - - -2007-09-26 Nick Kledzik - - an empty .o file with zero load commands will crash linker - * src/MachOReaderRelocatable.hpp: have Reader constructor return early of no load commands - * unit-tests/test-cases/empty-object: added test case - - -2007-09-26 Nick Kledzik - - 9a527: ppc64 branch islands fail with 4GB pagezeo - * src/MachOWriterExecutable.hpp: start range calculations at start of __text not at zero. - - ------ Tagged ld64-77 (Xcode 3.0) - -2007-07-23 Nick Kledzik - - Kernel is linked with some global symbols unsorted - * src/MachOWriterExecutable.hpp: Add NListNameSorter to allow global atoms and extra labels to be sorted - - -2007-07-20 Nick Kledzik - - Can't do objc_msgSendSuper dispatches after loading a Fix&Continue bundle - * src/MachOWriterExecutable.hpp: when calculating what kind of reloc to use, never use an - external reloc to reference 32-bit ObjC symbols. - - -2007-07-20 Nick Kledzik - - Runtime crash with ICC math library on Leopard - * src/MachOReaderRelocatable.hpp: detect if section starts with a symbol that is not - aligned to section and correct it. - - ------ Tagged ld64-76 - -2007-06-29 Nick Kledzik - - export hiding does not work for frameworks - * src/MachOReaderDylib.hpp: fix checks in isPublicLocation() - * unit-tests/test-cases/symbol-moving: update to test frameworks as well as dylibs - - -2007-06-27 Nick Kledzik - - linker should use undefines from flat dylibs when linking a main flat - * src/ObjectFile.h: added fLinkingMainExecutable - * src/Options.cpp: set up fLinkingMainExecutable - * src/MachOReaderDylib.hpp: when linking a main executable for flat namespace, the reader for - any loaded flat namespace dylib will have a new atoms that has references to all undefined - symbols in the dylib - * unit-tests/test-cases/flat-indirect-undefines: added test case - * doc/man/man1/ld.1: update man page to describe when dylib undefines are used - - -2007-06-27 Nick Kledzik - - OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found - * src/MachOReaderDylib.hpp: add assertNoReExportCycles() method - * unit-tests/test-cases/dylib-re-export-cycle: added test case - - -2007-06-27 Nick Kledzik - - ld64 has slightly different warning message formats than the old ld - * src/ld.cpp: standardize all warning messages to start with "ld: warning" - * src/MachOWriterExecutable.hpp: ditto - * src/MachOReaderRelocatable.hpp: ditto - * src/MachOReaderDylib.hpp:ditto - - -2007-06-26 Nick Kledzik - - -dead_strip can cause duplicate external commons - * src/ld.cpp: don't use discarded coalesced global atoms as dead strip roots - * src/machochecker.cpp: error if duplicate external symbols - * unit-tests/test-cases/commons-coalesced-dead_strip: added test case - - -2007-06-26 Nick Kledzik - - update man page that linker does not search indirect libraries with two-level namespace - * doc/man/man1/ld.1: add new "Indirect dynamic libraries" section to man page - - -2007-06-26 Nick Kledzik - - Xc9A466: Exports file cannot use Mac line ends - * src/Options.cpp: check for \r or \n when parsing .exp files - * unit-tests/test-cases/exported_symbols_list-eol: added test case - - ------ Tagged ld64-75 - -2007-05-31 Nick Kledzik - - Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB - * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 - - ------ Tagged ld64-74.5 - -2007-05-31 Nick Kledzik - - set OSO timestamp to zero for when building in buildit - * src/ld.cpp: check for RC_RELEASE and if exists set all OSO timestamps to zero - - -2007-05-30 Nick Kledzik - - BUILD_STABS now causes ld of xnu to bus error - * src/ld.cpp: Change || to && in collectStabs() - - ------ Tagged ld64-74.4 - -2007-05-18 Nick Kledzik - - static probes don't work with libraries in dyld shared cache - * src/OpaqueSection.hpp: the __TEXT segment is executable - - ------ Tagged ld64-74.3 - -2007-05-16 Nick Kledzik - - ppc: linker adds stubs to cstring references - * src/MachOWriterExecutable.hpp: update ppc stubableReference() to only allow high/low references - to be stubed if they reference a symbol in some other dylib. - * unit-tests/test-cases/stub-generation: added test case - - -2007-05-16 Nick Kledzik - - ppc64: need to make LOCAL indirect symbol table entry for now local symbol - * src/MachOWriterExecutable.hpp: factored local tests into indirectSymbolIsLocal() - * unit-tests/test-cases/non-lazy-r: added test case - - -2007-05-15 Nick Kledzik - - ld64 drops fix&continue bit in __OBJC, __image_info. - * src/MachOReaderRelocatable.hpp: implement objcReplacementClasses() - - -2007-05-15 Nick Kledzik - - support __image_info in __DATA segment for 64-bits - * src/MachOReaderRelocatable.hpp: use strncmp() for __objc_imageinfo since it is 16 bytes long - * src/MachOWriterExecutable.hpp: specialize segment/section names for synthesized objc image info section - - -2007-05-15 Nick Kledzik - - * unit-tests/include/common.makefile: set COMPILER_PATH so harness works with latest compiler - - ------ Tagged ld64-74.2 - -2007-05-11 Nick Kledzik - - ld64-74.1 breaks libstdc++ DejaGnu test (G5 only) - * src/MachOWriterExecutable.hpp: don't stub a reference if the target offset is non-zero - - ------ Tagged ld64-74.1 - -2007-05-09 Nick Kledzik - - * src/Options.h: add emitWarnings() - * src/Options.cpp: wire up -w to emitWarnings() - - -2007-05-09 Nick Kledzik - - ld64 won't link wine (regression from Tiger) - * src/Architectures.hpp: add x86::kPointerDiff16 and x86::kPCRel16 - * src/MachOReaderRelocatable.hpp: add support to parse new relocs - * src/MachOWriterExecutable.hpp: add support fo new relocs - - -2007-05-08 Nick Kledzik - - need way for ld and dyld to see different exported symbols in a dylib - * src/MachOReaderDylib.hpp: update parse and use $ld$ symbols - * src/Options.h: move VersionMin to ReaderOptions - * src/ObjectFile.h: move VersionMin to ReaderOptions - * src/Options.cpp: move VersionMin to ReaderOptions - * src/MachOWriterExecutable.hpp: move VersionMin to ReaderOptions - * unit-tests/test-cases/symbol-moving: added test case - - -2007-05-03 Nick Kledzik - - typo in error message for linking -pie - * src/MachOWriterExecutable.hpp: fix typo in error messages - - ------ Tagged ld64-74 - -2007-05-03 Nick Kledzik - - ld64 can't find @executable _path relative dylibs from our umbrella frameworks - ld64 should handle linking against dylibs that have @loader_path based dylib load commands - * src/ObjectFile.h: add from parameter to findDylib() - * src/MachOReaderDylib.hpp: supply from parameter to findDylib() - * src/ld.cpp: use from parameter for @loader_path substitution in findDylib() - * unit-tests/test-cases/re-export-relative-paths: added test case - - -2007-05-02 Nick Kledzik - - * src/ObjectFile.h: add fLogObjectFiles and fLogAllFiles - * src/Options.cpp: hook up -t to fLogAllFiles and -whatsloaded to fLogObjectFiles - * src/MachOReaderDylib.hpp: log if fLogAllFiles - * src/MachOReaderRelocatable.hpp: log if fLogObjectFiles or fLogAllFiles - * src/MachOReaderArchive.hpp: log if fLogAllFiles - * doc/man/man1/ld.1: update man page - - -2007-05-02 Nick Kledzik - - typo in message, frameowrk - * src/Options.cpp: fix typo - - -2007-05-01 Nick Kledzik - - "ld" man page is missing the description for many options - * doc/man/man1/ld.1: add documentation on all obsolete options - - -2007-05-01 Nick Kledzik - - ld doesn't handle -mlong-branch .o files that have had local symbols stripped - warning about dwarf line info with -mlong-branch - * src/MachOReaderRelocatable.hpp: don't lop -mlong-branch stubs off end of functions - * src/MachOWriterExecutable.hpp: allow code references besides BR24 to be stubable - - -2007-04-30 Nick Kledzik - - unable to link VTK because __textcoal_nt too large - * src/MachOReaderRelocatable.hpp: when doing a final link map __textcoal_nt to __text - - -2007-04-30 Nick Kledzik - - ld does not report error when -r is used and exported symbols are not defined. - ld leaves global common symbols not in exported symbols list. - * src/ld.cpp: stop special casing -r mode in checkUndefines() - * src/MachOWriterExecutable.hpp: don't create proxy atom in -r mode if it is supposed to be exported. - mark tentative definitions are private extern in -r mode even without -keep_private_externs - * unit-tests/test-cases/exported_symbols_list-r: added test case - - -2007-04-27 Nick Kledzik - - ld should keep looking when it finds a weak definition in a dylib - * src/ld.cpp: modified addJustInTimeAtoms() to keep looking when a weak defintion is found - * unit-tests/test-cases/weak-def-ordinal: added test case - - -2007-04-27 Nick Kledzik - - better error message for indirect dylibs missing required architecture - * src/ld.cpp: when loading indirect dylib add path to error messages - - -2007-04-25 Nick Kledzik - - the i386 slice of dyld does not need __IMPORT segment - * src/ObjectFile.h: add fForDyld - * src/Options.cpp: set up fForDyld - * src/MachOReaderRelocatable.hpp: if fForDyld, change __IMPORT segment to __DATA - * src/MachOWriterExecutable.hpp: recognize __DATA/__pointers in dyld as a non-lazy section - - -2007-04-24 Nick Kledzik - - ppc64: need to make LOCAL indirect symbol table entry for now local symbol - * src/MachOWriterExecutable.hpp: use INDIRECT_SYMBOL_LOCAL for any non-global symbol - * unit-tests/test-cases/strip_local: update test case - - -2007-04-24 Nick Kledzik - - ld64 -sectorder and -order_file files don't accept white space following the : - * src/Options.cpp: prune white space after colon and before symbol name - * unit-tests/test-cases/order_file: update test case to have a space after the colon - - -2007-04-24 Nick Kledzik - - ld64 corrupts debug symbol table entries, nm doesn't print them - * src/MachOWriterExecutable.hpp: properly set ilocalsym in module table - - -2007-04-24 Nick Kledzik - - support __image_info in __DATA segment for 64-bits - * src/MachOReaderRelocatable.hpp: look for new objc info section name too - - -2007-04-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix -non_global_symbols_strip_list to work with -r - * unit-tests/test-cases/local-symbol-partial-stripping: update test case - - - ------ Tagged ld64-73.7 - -2007-05-10 Nick Kledzik - - can't use dtrace static probes in x86_64 dylib - * src/MachOWriterExecutable.hpp: x86_64:kPointerDiff32 is ok in shared region - * unit-tests/test-cases/dtrace-static-probes: update to build dylib too - - -2007-05-09 Nick Kledzik - - 9A430: using -dead_strip with static dtrace probes causes ld to crash - * src/ld.cpp: fix markLive() to look at right name in dtrace probe refernce - * unit-tests/test-cases/dtrace-static-probes: added -dead_strip case - - ------ Tagged ld64-73.6 - -2007-04-17 Nick Kledzik - - Add options to do partial stripping of local symbols - * src/MachOWriterExecutable.hpp: use fOptions.keepLocalSymbol() - * src/Options.cpp: implement -non_global_symbols_no_strip_list and -non_global_symbols_strip_list - * src/Options.h: replace stripLocalSymbols() with localSymbolHandling() and keepLocalSymbol() - * doc/man/man1/ld.1: document -non_global_symbols_no_strip_list and -non_global_symbols_strip_list - * unit-tests/test-cases/local-symbol-partial-stripping: added test case - - ------ Tagged ld64-73.5 - -2007-04-17 Nick Kledzik - - ld64-73.3 XBS logging incorrectly reporting "direct" dynamic libraries - * src/ld.cpp: restore direct vs indirect library for LD_TRACE_DYLIBS logging - - -2007-04-16 Nick Kledzik - - data initialized to a weak imported symbol is missing relocation - * src/MachOWriterExecutable.hpp: check for A::kPointerWeakImport in buildExecutableFixups() - * unit-tests/test-cases/weak_import: updated test case to catch this problem - - -2007-04-13 Nick Kledzik - - Support -U - * src/MachOWriterExecutable.hpp: create proxies for -U symbols - * src/Options.cpp: process -U - * src/Options.h: add allowedUndefined() and someAllowedUndefines() - * src/ld.cpp: create proxies for -U symbols - * doc/man/man1/ld.1: document -U and -undefined options - * unit-tests/test-cases/undefined-dynamic-lookup: added test case - - ------ Tagged ld64-73.4 - -2007-04-12 Nick Kledzik - - ld changes needed to support read-only DOF - * src/Options.cpp: remove -read_only_dof - * src/Options.h: remove fReadOnlyDOFs - * src/ld.cpp: only generate read-only DOF sections - - ------ Tagged ld64-73.3.1 - -2007-04-13 Nick Kledzik - - -framework vecLib -framework Accelerate causes bad ordinals - * src/MachOWriterExecutable.hpp: fix bug optimizeDylibReferences() when there are two readers with same install name - - ------ Tagged ld64-73.3 - -2007-04-03 Nick Kledzik - - * src/ld.cpp: read-only-dofs should use 32-bit offsets for x86_64 - * src/MachOReaderDylib.hpp: if "public" re-export is not marked implict, still mark it as re-exported - - -2007-04-02 Nick Kledzik - - if replacement file for -dylib_file is missing, warn instead of error - * src/ld.cpp: a try/catch to turn -dylib_file error into a warning. - * unit-tests/test-cases/dylib_file-missing: add test case - * doc/man/man1/ld.1: update man page about -dead_strip_dylibs - - ------ Tagged ld64-73.2 - -2007-03-31 Nick Kledzik - - ld64-73: atom sorting error with duplicate zero sized bss symbols - * src/MachOReaderRelocatable.hpp: suppress warning on sorting zero size zero fill atoms - -2007-03-31 Nick Kledzik - - ld64-73 fails anything linking with -lm - * src/ld.cpp: when processing dylbs that are sylinks ensure that fDylibMap contains all paths - * src/MachOWriterExecutable.hpp: when dead stripping dylibs and renumbering ordinals make sure - aliases dylib get renumbered too - * unit-tests/test-cases/dylib-aliases: added - - ------ Tagged ld64-73.1 - -2007-03-30 Nick Kledzik - - * src/MachOWriterExecutable.hpp: back out use of LC_REEXPORT_DYLIB until rdar://problem/5009909 is in build fleet - - ------ Tagged ld64-73 - -2007-03-30 Nick Kledzik - - ER: -dead_strip_dylibs - linker should add implicit load commands for indirectly used public dylibs - * src/ObjectFile.h: change dylib reader interface to implictly/explicitlyLinked - * src/ld.cpp: use new dylib reader interface - * src/Options.h: add deadStripDylibs() - * src/Options.cpp: support -dead_strip_dylibs - * src/MachOReaderDylib.hpp: use new dylib reader interface - * src/MachOWriterExecutable.hpp: remove dylib load commands for unused dylibs and alter ordinals - * unit-tests/test-cases/re-export-optimizations: added - * unit-tests/test-cases/dead_strip_dylibs: added - - -2007-03-30 Nick Kledzik - - * src/Options.cpp: enable -lfoo to search for libfoo.so as well as libfoo.dylib, - remove seg addr table hack for transitioning to new linker - -2007-03-30 Nick Kledzik - - ADOBE XCODE3: Linker is slow with large C++ .o files - * src/MachOReaderRelocatable.hpp: the compiler generates stubs to weak functions in the - same translation unit. Don't treat those like the spurios stubs to static functions. - - -2007-03-29 Nick Kledzik - - ld64 should link mach_kernel during xnu builds to support dtrace - * src/MachOReaderRelocatable.hpp: To handle duplicate labels properly, rework how atoms sizes are set - by iterating through sorted fAtoms rather than fAddrToAtom, . Change default alignment of commons - to be the natural alignment of the size rounded up to the closest power of two and max it at 12. - Build atoms in reverse symbol table order so that global atoms are constructed before locals. - This assures that if there is a global and local label at the same location, the global label - will become the atom's name and the local will be an alias. Properly handle a label - at the end of a section. Handle R_ABS in relocations. Handle sect-diff relocs with addends. - Don't auto-strip 'l' symbols in static executables (mach_kernel). - * src/OpaqueSection.hpp: opaque_section now has an ordinal - * src/ld.cpp: opaque_section now requires an ordinal - * src/ObjectFile.h: add ReaderOptions.fForStatic - * src/Options.cpp: set fForStatic when building a static executable - * src/MachOWriterExecutable.hpp: add from atom to StubAtom. Properly write out i386 - sect-diff relocs with addends. properly write out ppc PICbase relocs where pic base - is not in the atom. - - -2007-03-27 Nick Kledzik - - Typo in ld man page (-exported_symbols_list) - * doc/man/man1/ld.1: fix typo - - -2007-03-26 Nick Kledzik - - consider generating LC_UUID from a checksum of the file - * src/Options.h: change emitUUID() to getUUIDMode() - * src/Options.cpp: support -random_uuid - * src/MachOWriterExecutable.hpp: set uuid to be md5 hash of entire output file - - -2007-03-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: restructure writeAtoms() to copy all atoms in memory if possible - - -2007-03-24 Nick Kledzik - - ld -r of stripped .o file can incorrectly merge non-lazy pointers - * src/MachOWriterExecutable.hpp: when generating a .o file, non-lazy pointer with target offsets should be - encoded as LOCAL in the indirect symbol table - * unit-tests/test-cases/stripped-indirect-symbol-table: added test case - - -2007-03-23 Nick Kledzik - - SWB: ld64-72 errors building with gcc-4.2 - * src/MachOReaderDylib.hpp: add curly brackets in switch cases - * src/MachOWriterExecutable.hpp: rearrange classes so there are no template specialization forward references - - -2007-03-23 Nick Kledzik - - * src/ld.cpp: fix -print_statistics when using -dead_strip - - -2007-03-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: generate better names for non-lazy pointers to the interior of atoms - - -2007-03-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: speed up ld -r a little by reversing relocs en mas - - -2007-03-16 Nick Kledzik - - ld Bus Error on missing command line arguments - * src/Options.cpp: check next argv[] is not NULL - - -2007-03-16 Nick Kledzik - - need to be able to order symbols in anonymous namespaces - * src/ld.cpp: add logic to do fuzzy matching of symbols with anonymous namespace usage - * unit-tests/test-cases/order_file-ans: added test case - - -2007-03-16 Nick Kledzik - - headerpad_max_install_names deprecated for 64-bit - * src/ld.cpp: make sure dylib load command order matches command line order - * src/Options.h: add maxMminimumHeaderPad() - * src/Options.cpp: add maxMminimumHeaderPad() set by -headerpad_max_install_names - * src/src/MachOWriterExecutable.hpp: check maxMminimumHeaderPad() - * doc/man/man1/ld.1: update man page about -headerpad_max_install_names - - -2007-03-16 Nick Kledzik - - Linker returns success although exported symbols are undefined. - * src/ld.cpp: turn missing symbols back into an error - - -2007-03-16 Nick Kledzik - - ld64 should handle linking against dylibs that have @loader_path based dylib load commands - * unit-tests/test-cases/loader_path: added test case - - -2007-03-16 Nick Kledzik - - linker should add implicit load commands for indirectly used public dylibs - Indirect libraries should be found using -F and -L options - Simplier, generalized way to re-export dylibs: LC_REEXPORT_DYLIB - * src/ld.cpp: reworked all dylib processing. Readers can now add the dylib list. - * src/Options.h: add findFileUsingPaths() - * src/MachOReaderDylib.hpp: look in re-exported children instead of requring linker to do that - * src/ObjectFile.h: add processIndirectLibraries(), remove getDependentLibraryPaths() - * src/machochecker.cpp: support LC_REEXPORT_DYLIB - * src/ExecutableFile.h: simplify DyLibUsed - * src/Options.cpp: add findFileUsingPaths(). add new re-export options - * src/MachOWriterExecutable.hpp: Use LC_REEXPORT_DYLIB when targetting 10.5 - * doc/man/man1/ld.1: updated with new re-export options - * unit-tests/test-cases/indirect-path-search: added tests that -F and -L work with indirect dylibs - * unit-tests/test-cases/re-export-cases: added tests for all combinations of re-exporting - - -2007-03-14 Nick Kledzik - - sort external relocations to optimize dyld performance - * src/MachOWriterExecutable.hpp: added ExternalRelocSorter - * src/machochecker.cpp: verify external relocations are grouped by symbol number - * unit-tests/test-cases/external-reloc-sorting: added test case - - ------ Tagged ld64-72 - -2007-03-06 Nick Kledzik - - * src/Options.cpp: ignore .objc_category_name_* symbols in .exp files - - -2007-03-06 Nick Kledzik - - * src/Options.cpp: stop special casing mach_kernel and instead requre kernel to be built with -new_linker - - -2007-03-06 Nick Kledzik - - ld64-72 (experimental) is causing DejaGnu test failures - * src/MachOWriterExecutable.hpp: add optimizableGOTReferenceKind() to track GOT uses that cannot be optimized - - -2007-03-06 Nick Kledzik - - minimum header padding should be 32 to allow code signing - * src/Options.cpp: initialize fMinimumHeaderPad to 32 - * src/MachOWriterExecutable.hpp: better calculation of header padding - - -2007-03-06 Nick Kledzik - - Linker crashes with -flat_namespace against two-level dylibs that might have re-exports - * src/ld.cpp: flat namespace should not allow NULL indirect readers - - -2007-03-06 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't error on S_COALESCED sections with anonymous atoms - * src/MachOWriterExecutable.hpp: set MH_PIE bit when linking -pie - * ld64.xcodeproj/project.pbxproj: don't echo environment when running unit test - - -2007-03-01 Nick Kledzik - - * doc/man/man1/ld.1: Add descriptions to all "rarely used options" - - -2007-03-01 Nick Kledzik - - Remove support for Essential Symbols: Warn about use of -Sp option; remove man page entry - * src/Options.cpp: make -Sp obsolete - * doc/man/man1/ld.1: make -Sp obsolete - - -2007-03-01 Nick Kledzik - - Support -pie - * src/Options.h: Add positionIndependentExecutable() - * src/Options.cpp: Support -pie option to set positionIndependentExecutable() - * src/MachOWriterExecutable: if -pie is used, add extra local relocations and error if any - absolute addressing is used - - -2007-03-01 Nick Kledzik - - ld64 should link mach_kernel during xnu builds to support dtrace - * src/ld.cpp: Ensure segments are laid out in discovery order. Add support for kAbsoluteSymbol. - Warn when merging symbols of different visiblity. Warn when a tentative definition - is replaced by one a real definition with a smaller size. Lay out __common section - so that ones built with -fno-commons come before regular commons. - * src/ObjectFile.h: remove SegmentOffset ivar and getter/setters - * src/machochecker.cpp: allow images with no r/w segments - * src/MachOReaderRelocatable: Add AbsoluteAtom. Sort tentative definitions by name instead of by size - Add support for custom commons alignment. - * src/Options.cpp: Fix spurious -sectalign warnings. Don't use ld_classic when linking mach_kernel - * src/MachOWriterExecutable.hpp: Support kAbsoluteSymbol atoms. In -r mode, set custom alignment - for commons if alignment is not its size. Support global __dtrace_probe labels. - * src/ObjectDump.cpp: add support for kAbsoluteSymbol atoms. - * unit-tests/test-cases/commons-alignment: Added test case for custom commons alignment - * unit-tests/test-cases/absolute-symbol: Added test case for basic absolute symbols - * unit-tests/test-cases/segment-order: Added test case that segments lay out in discovery order - * unit-tests/test-cases/commons-order: Added test case that commons lay out correctly - * unit-tests/test-cases/end-label: Added test case that a label used to mark the end of a section does not - get associcated with the next section. - - -2007-02-23 Nick Kledzik - - gcc-5005: DejaGnu failures due to -frepo - * src/ld.cpp: Add quotes to referenced from name to make collect2 and -frepo happy - - -2007-02-22 Nick Kledzik - - * src/MachOWriterExecutable.hpp: rework how padding after load commands is calculated - - -2007-02-21 Nick Kledzik - - * src/MachOWriterExecutable.hpp: extend special case of __mh_execute_header to static executables too - - -2007-02-21 Nick Kledzik - - gcc link map option ( "-M" ) should be redirectable to file - * doc/man/man1/ld.1: added -map option description - * src/Options.h: added generatedMapPath() - * src/Options.cpp: set up generatedMapPath() if -map option is used - * src/MachOWriterExecutable.hpp: add writeMap() method to generate map file - - -2007-02-19 Nick Kledzik - - Implement GOT Load elimination optimization - * src/ld.cpp: track size of all atoms and if > 2GB sort large zero-fill atoms to end - * src/MachOWriterExecutable.hpp: If image size < 2GB, only generate GOT entries if value must be - updatable by dyld. If > 2GB, only eliminate GOT entries to non-zero-fill atoms. Any use - of an eliminated GOT entry has its code changed from MOVQ _foo@GOT(%rip) to LEAQ _foo(%rip). - * unit-tests/test-cases/large-data: added - * unit-tests/test-cases/got-elimination: added - - ------ Tagged ld64-71.2 - -2007-02-13 Nick Kledzik - - new ld ignores -segprot option - * src/Options.h: expose customSegmentProtections() - * src/Options.cpp: parse -segprot option and populate customSegmentProtections() - * src/MachOWriterExecutable.hpp: use customSegmentProtections() - - -2007-02-13 Nick Kledzik - - i386 -stack_addr doesn't work - * src/MachOWriterExecutable.hpp: use correct offset into thread state record - - ------ Tagged ld64-71.1 - -2007-02-07 Nick Kledzik - - * src/ld.cpp: sort __OBJC2 segment to be next to __OBJC segment - - -2007-02-07 Nick Kledzik - - * src/Options.cpp: change missing -seg_addr_table from an error to a warning - - -2007-02-06 Nick Kledzik - - Leopard 9A357: -dylib_file broken? - * src/MachOWriterExecutable.hpp: remove use of fInstallPathOverride - * src/Options.cpp: wire up -dylib_file option - * src/Options.h: remove fInstallPathOverride. add fDylibOverrides - * src/ld.cpp: check dylibOverrides() for indirect libraries - * unit-tests/test-cases/dylib_file: add test case - - -2007-02-05 Nick Kledzik - - * src/MachOReaderDylib.hpp: don't warn about zero size __image_info sections - - -2007-02-04 Rick Balocca - Enable the failing cases for missing command line arguments - -2007-02-04 Rick Balocca - Make sure that all .o's are checked by ObjectDump - and all macho are checked by machochecker - -2007-02-04 Rick Balocca - Fix an endian problem with machochecker - Fix blank-stubs Makefile - ------ Tagged ld64-71 - -2007-02-02 Rick Balocca - blank-stubs test case: handle the case of a native ppc compile--this - sets the subtype, which must be passed to lipo - -2007-02-01 Rick Balocca - make cpu-sub-types test more robust - -2007-02-01 Rick Balocca - auto-arch tests were resulting in a false FAILs - -2007-02-01 Rick Balocca - test cpu-sub-types was resulting in a false FAIL - -2007-02-01 Nick Kledzik - - STD:VSC: c99 -o writes to file that does not have write permission - * src/MachOWriterExecutable.hpp: check file is writable before using it - -2007-02-01 Nick Kledzik - - debug map (N_OSO) timestamps for object files in ranlib archive are incorrect - * src/MachOReaderArchive.hpp: parse modTime for .o files out of archive header - -2007-01-31 Nick Kledzik - - 9A354: ld -all_load does *NOT* produce the same dSYM as *.o or -u - * src/ld.cpp: when using -all_load don't assume that all atoms have same reader - * unit-tests/test-cases/dwarf-archive-all_load: added - ------ Tagged ld64-70.1 - -2007-01-31 Nick Kledzik - - * src/MachOWriterExecutable.hpp: in addObjectRelocs_powerpc() mask scattered r_address to 16-bits - ------ Tagged ld64-70 - - -2007-01-30 Nick Kledzik - - linker should verify GC consistency of modules being linked into library - Support cpu-sub-types for ppc - * src/ObjectFile.h: Add getObjCConstraint() and getCpuConstraint() - * src/MachOReaderRelocatable.hpp: don't make atom for __image_info section, instead parse constaints - * src/MachOReaderDylib.hpp: look at __image_info content to get constaints - * src/ld.cpp: add updateContraints() and checkObjc() - * src/MachOWriterExecutable.hpp: add ObjCInfoAtom to sythesize __image_info content - - -2007-01-28 Nick Kledzik - - src/*: remove ObjectFile::requiresFollowOnAtom() method - - -2007-01-28 Nick Kledzik - - src/ld.cpp: enable LLVM_SUPPORT by default - src/LLVMReader.hpp: don't use absolute paths for llvm headers and libraries - - -2007-01-26 Rick Balocca - * src/ObjectDump.cpp: The usage() message was incorrect. - - -2007-01-25 Rick Balocca - * unit-tests/test-cases/zero-fill3: It was reporting FAIL on ld64 error return. - It should have been checking for non-error return. - - -2007-01-24 Nick Kledzik - - x86 fast stubs should not cross 64-byte boundries - * src/MachOWriterExecutable.hpp: for x86, 64-byte align __jump_table section - and make 64-btye crossing stubs be empty entries with indirect symbol table - entry of INDIRECT_SYMBOL_ABS - - -2007-01-19 Nick Kledzik - - * src/Options.h: add readOnlyx86Stubs() - * src/Options.cpp: support -read_only_stubs - * src/MachOWriterExecutable.hpp: make __IMPORT segment not writable if -read_only_stubs is used - - -2007-01-16 Eric Christopher - - ld64 --help isn't recognized - * src/Options.cpp (Options::parse): Support --help and -help. - - -2007-01-15 Nick Kledzik - - * src/MachOFileAbstraction.hpp: add range checking on macho_scattered_relocation_info::set_r_address() - - -2007-01-14 Nick Kledzik - - Support wildcards in contents of -exported_symbols_list - * src/Options.h: add SetWithWildcards class - * src/Options.cpp: add -exported_symbol and -unexported_symbol and use SetWithWildcards - * doc/man/man1/ld.1: add -exported_symbol and wildcard explanation - * unit-tests/test-cases/exported-symbols-wildcards: added test case - - -2007-01-10 Nick Kledzik - - [U]SDT probes should use C calling convention - * src/Options.cpp: Add -read_only_dof - * src/ld.cpp: create __dof section(s) based on probe and isenabled sites - * src/MachOReaderRelocatable.hpp: parse new sdt 2.0 probes encoded in .o files - * src/MachOWriterExecutable.hpp: handle regenerating dtrace probes into .o files - * unit-tests/test-cases/dtrace-static-probes: added test case - - ------ Tagged ld64-69.8 - -2007-01-30 Nick Kledzik - - Support LD_FORCE_NO_SEG_ADDR_TABLE - * src/Options.cpp: Support LD_FORCE_NO_SEG_ADDR_TABLE - - ------ Tagged ld64-69.7 - -2007-01-25 Nick Kledzik - - Leopard9A351: CFM Apps Are Broken because CFM glue is missing - * src/MachOReaderRelocatable.hpp: check S_ATTR_NO_DEAD_STRIP in dontDeadStrip() - - ------ Tagged ld64-69.6 - -2007-01-24 Nick Kledzik - - LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive - * src/ld.cpp: create and use logArchive() - - ------ Tagged ld64-69.5 - -2007-01-22 Nick Kledzik - - 9A350: can't link ppc programs with ld_classic - * src/Options.cpp: Remove support for LD_NO_CLASSIC_LINKER. Add support for -classic_linker - - ------ Tagged ld64-69.4 - -2007-01-17 Nick Kledzik - - QTComponents does not link with ld64 - * src/MachOReaderRelocatable.hpp: handle N_RSYM and N_PSYM stabs - - ------ Tagged ld64-69.3 - -2007-01-03 Nick Kledzik - - * src/Options.cpp: If the same dylib is specified twice and the second is specified weak, make it weak - - ------ Tagged ld64-69.2 - -2006-12-18 Nick Kledzik - - -dead_strip without -exported_symbols_list should not strip global functions from archives - * src/ld.cpp: when adding a .o file from an archive, add all its global symbols to live roots - * unit-tests/test-cases/dead_strip-archive: added - - -2006-12-18 Nick Kledzik - - flat_namespace main executables do not need to indirect interior references - * src/MachOWriterExecutable.hpp: don't indirect references to global symbols in main executables - * unit-tests/test-cases/flat-main: updated to test for indirection - * unit-tests/test-cases/flat-dylib: added - - ------ Tagged ld64-69.1 - -2006-12-15 Nick Kledzik - - -flat_namespace does not work with -mdynamic-no-pic - * src/MachOWriterExecutable.hpp: rework checking for use of ppc absolute addressing to allow them as long as - the target is within the same linkage unit. - - -2006-12-15 Nick Kledzik - - -ObjC should only load .o with .objc_ symbols - * src/Options.cpp: remove warning from -ObjC and have it instead set fLoadAllObjcObjectsFromArchives - * src/MachOReaderArchive.hpp: when -ObjC is used, preload all .o files from archives that contain .objc_ symbols - - ------ Tagged ld64-69 - -2006-12-13 Nick Kledzik - - prebound interior pointers must be non-zero - * src/MachOWriterExecutable.hpp: in fixUpReference_powerpc() set lazy pointers bound to with the dylib to - their target value. Properly set REFERENCE_FLAG_UNDEFINED_* flags in reference table and n_desc - - -2006-12-09 Nick Kledzik - - ld64 fails to detect error that ld_classic does - * src/MachOWriterExecutable.hpp: check for absolute reloc to an external symbol - * src/MachOReaderRelocatable.hpp: ignore -mlong-branch stubs in .o files - - -2006-12-09 Nick Kledzik - - symbols with REFERENCED_DYNAMICALLY should never be stripped - * src/MachOWriterExecutable.hpp: update Writer::shouldExport() to check for kSymbolTableInAndNeverStrip - * unit-tests/test-cases/main-stripped: add test that dynamically referenced symbol cannot be stripped - - -2006-12-08 Nick Kledzik - - * unit-tests/test-cases/allowable-client: add variant test cases (e.g. CoreServices_profile) - * src/ld.cpp: allow frameworks with variant install names (e.g. CoreServices_profile) to be private clients - - -2006-12-08 Nick Kledzik - - * doc/man/man1/ld.1: rewrite man page - * src/Options.h: add warnObsolete() - * src/Options.cpp: use warnObsolete() on many options. Make nonWeak the weak-mis-match default. - Make -ObjC mean -all_load. - ------ Tagged ld64-68.3 - -2006-12-05 Nick Kledzik - - * src/ld.cpp: allow umbrella frameworks to have variant install names (e.g. CoreServices_profile) and still link - - ------ Tagged ld64-68.2 - -2006-12-05 Nick Kledzik - - * src/MachOWriterExecutable.cpp: Use N_PBUD in the symbol table for undefined symbols in prebound dylibs - - ------ Tagged ld64-68.1 - -2006-12-01 Nick Kledzik - - * src/Options.cpp: always generate module tables for 32-bit architectures so that ld_classic - can link against them - - ------ Tagged ld64-68 - -2006-12-01 Nick Kledzik - - seg_addr_table needs matching fuzziness - * src/Options.cpp: special case a how a dozen dylib are looked up in the seg_addr_table - - -2006-12-01 Nick Kledzik - - * src/Options.cpp: have all -static links for 32-bit archs roll over to ld_classic unless - LD_NO_CLASSIC_LINKER_STATIC is set. - * unit-tests/bin/make-recursive.pl: set LD_NO_CLASSIC_LINKER_STATIC for unit tests - - -2006-11-29 Nick Kledzik - - ld64-67: QTComponents fails to build - * src/MachOReaderRelocatable.hpp: don't error out when a local non-lazy pointer does not point to a symbol - * unit-tests/test-cases/strip_local: added test case - - -2006-11-28 Nick Kledzik - - Need a way to mark libraries usable by dynamic linker but unusable by static linker - * src/Options.cpp: allow -client_name to be used with main executables - * src/ld.cpp: generalize -allowable_client. Any dylib can now restrict who can link against it. As a convention - linking with -allowable_client '!' will mean no one can statically link with the dylib. It can still be loaded - dynamically, or by any existing clients, but no new clients can link with it. - * unit-tests/test-cases/allowable-client/Makefile: enable previously commented out test cases. Add test cases - of a dylib that allows no clients and just one client - -2006-11-27 Nick Kledzik - - -final_output should be used if -install_name not used - * src/Options.cpp: fall back to using -final_output for install name - - ------ Tagged ld64-67 - -2006-11-17 Nick Kledzik - - * src/MachOWriterExecutable.hpp: support __IMPORT segment being slide independently of __DATA segment in shared cache - - -2006-11-16 Nick Kledzik - - 9a303: ld -filelist Bus Error - * src/Options.cpp: add check that -filelist is followed by an argument - - -2006-11-16 Nick Kledzik - - * src/MachOWriterExecutable.hpp: when building split-seg dylibs, LINKEDIT goes in read-only side - - -2006-11-15 Nick Kledzik - - * src/MachOWriterExecutable.hpp: set proper attributes for __eh_frame in ld -r mode - * unit-tests/test-cases/eh_frame: added test case - - -2006-11-10 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: redirect references to static weak stubs to the real target - - -2006-11-09 Nick Kledzik - - * src/MachOWriterExecutable.hpp: r_address is offset from first LC_SEGMENT vmaddr - not from segment with lowest address - - ------ Tagged ld64-66.1 - -2006-11-09 Nick Kledzik - - * src/MachOWriterExecutable.hpp: initialize fModuleInfoAtom to zero - - -2006-11-08 Nick Kledzik - - FSF GCC's libjava doesn't link with Ochre ld64 - * src/MachOReaderRelocatable.hpp: ignore debug_line section if debug_info section is missing or empty - ------ Tagged ld64-66 - -2006-11-07 Nick Kledzik - - SWB: d64-65 does not built usage split-seg dylibs - * src/MachOWriterExecutable.hpp: when prebinding split-seg correctly set r_address fields and on - disk values for external relocations - * unit-tests/test-cases/prebound-split-seg: added test case - - -2006-11-03 Nick Kledzik - - * src/MachOReaderDylib.hpp: don't report dependent libraries if MH_NO_REEXPORTED_DYLIBS bit is set - * src/MachOWriterExecutable.hpp: set MH_NO_REEXPORTED_DYLIBS bit if dylib does not logically re-export any other dylibs - * unit-tests/test-cases/re-export-flag: added test case - * src/machochecker.cpp: validate use of MH_NO_REEXPORTED_DYLIBS - - -2006-11-02 Nick Kledzik - - Mysterious messages from ld64 with MACOSX_DEPLOYMENT_TARGET = 10.5 - * src/MachOWriterExecutable.hpp: kPointerWeakImport is a valid reference type to cross segments - - -2006-11-02 Nick Kledzik - - * src/Options.cpp,h: Add support for -rpath - * src/MachOFileAbstraction.hpp: add macho_rpath_command - * src/MachOWriterExecutable.hpp: add RPathLoadCommandsAtom to create LC_RPATH for each -rpath - - ------ Tagged ld64-65 - -2006-10-30 Nick Kledzik - - x86_64 default stack_addr is wrong - * src/Options.cpp: change default 64-bit stack location when using -stack_size - - -2006-10-30 Nick Kledzik - - dylibs need modules for 10.3 and for ld_classic in Salt - * src/MachOWriterExecutable.hpp: add ModuleInfoLinkEditAtom to create module table stuff - * src/Options.cpp,h: Add needsModuleTable() - * src/MachOFileAbstraction.hpp: Add macho_dylib_module, macho_dylib_reference, and macho_dylib_table_of_contents - - -2006-10-27 Nick Kledzik - - * unit-tests/test-cases/no-uuid/Makefile: add -gstabs+ to be compatible with latest compiler - * unit-tests/test-cases/stabs-coalesce/Makefile: add -gstabs+ to be compatible with latest compiler - - -2006-10-26 Nick Kledzik - - i386 -mdynamic-no-pic switch statement jump table is out of line - * src/MachOWriterExecutable.hpp: for i386 don't check for direct references to weak symbols - - -2006-10-26 Devang Patel - - * src/LLVMReader.hpp: Supply final output file path to optimizer. - -2006-10-26 Devang Patel - - * src/ObjectFile.h: Make setSection* methods virtual. - * src/LLVMReader.hpp: Override setSection* methods. - -2006-10-26 Devang Patel - - * unit-tests/test-case/llvm-integration/a13.h: New. - * unit-tests/test-case/llvm-integration/a13.cc: New. - * unit-tests/test-case/llvm-integration/main13.cc: New. - -2006-10-26 Devang Patel - - * src/options.h, src/options.cpp: Add -save-temps command line option. - * src/LLVMReader.hpp: Use saveTemps option. - - -2006-10-26 Devang Patel - - * src/LLVMReader.hpp: Remove invalid module from memory. - -2006-10-26 Devang Patel - - * src/LLVMReader.hpp: Collect symbol alignment info from LLVM optimizer. - -2006-10-21 Eric Christopher - - * src/ld.cpp (Linker::Linker): Check for LD_NO_CLASSIC_LINKER before - invoking ld_classic. - * unit-tests/test-cases/relocs-literals/Makefile: Run for -mdynamic-no-pic - and pic. - * unit-tests/test-cases/static-executable/Makefile: Skip for 64-bit. Add - -dead_strip to command line. - ------ Tagged ld64-64.2 - -2006-10-19 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: stop copying LLVMReader.hpp into man1 directory - ------ Tagged ld64-64.1 - -2006-10-19 Nick Kledzik - - ld64-63.1 erroneously coalesces an empty string with a non-empty string - * src/MachOReaderRelocatable.hpp: rework cstring parsing to not assume all strings are start - at section alignment boundaries, and when coalescing empty strings always use one with greatest - alignment requirement - * src/MachOWriterExecutable.hpp: in -r mode, don't pad end of cstring section - * src/ObjectFile.h: correctly name leadingZeros() as trailingZeros() - * src/ld.cpp: leadingZeros() --> trailingZeros() - - -2006-10-18 Eric Christopher - - * unit-tests/test-cases/read-only-relocs/Makefile: Skip for x86_64. - * unit-tests/test-cases/llvm-integration/Makefile: Skip if llvm isn't - present. - -2006-10-18 Nick Kledzik - - ld64 change required to go with assembler cstring change - ld64 should error when a local relocation references an address outside its section - * src/MachOReaderRelocatable.hpp: for x86_64 in order to work with local or external relocations to cstrings - change parser to allow atoms with a pending name that is resolved after references are instantiated. - Make direct references to kRegularDefinition atoms. - * src/MachOWriterExecutable.hpp: in -r mode for x86_64 generate L* labels for cstrings and use external relocations - * unit-tests/test-cases/relocs-literals/test.c: add two cases of cstring literal plus addend - - -2006-10-06 Nick Kledzik - - check MACOSX_DEPLOYMENT_TARGET if -macosx_version_min is not used - * src/Options.cpp: if -macosx_version_min is not used, check MACOSX_DEPLOYMENT_TARGET, if - that is unused, default to 10.5 - ------ Tagged ld64-64 - -2006-10-06 Nick Kledzik - - crash in ppc64 program - bl to saveFP, but saveFP is too far away? - * src/MachOWriterExecutable.hpp: in addPPCBranchIslands(), properly account for growth of __text - - -2006-10-06 Nick Kledzik - - Linker-defined alias converts reference into definition and generates error. - * src/MachOReaderRelocatable.hpp: only alias symbols actually in the symbol table - - -2006-10-06 Nick Kledzik - - * unit-tests/test-cases/dwarf-debug-notes/Makefile: crt1.o no longer has stabs, so don't need to strip it - * unit-tests/test-cases/dwarf-debug-notes-r/Makefile: crt1.o no longer has stabs, so don't need to strip it - - -2006-10-06 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: rework dwarf line parsing to fix warnings that starting - showing up with gcc-5421 - - -2006-10-05 Eric Christopher - - ld64 needs to support libtool options - * src/Options.cpp (Options::parse): Add -noall_load, -install_name, - -current_version and -compatibility_version. - -2006-10-03 Eric Christopher - - * src/Options.cpp (Options::gotoClassicLinker): Use execvp - to call ld_classic. - -2006-10-03 Eric Christopher - - * unit-tests/test-cases/tentative-to-real/Makefile: Clean up after tests. - -2006-10-03 Eric Christopher - - * unit-tests/include/common.makefile (VALID_ARCHS): Add x86_64. - (OTOOL): Remove munging based on ARCH. - -2006-09-29 Nick Kledzik - - problem merging .o files built with and without -fno-common - src/Options.*: make MakeTentativeDefinitionsReal a reader option - src/ObjectFile.h: make MakeTentativeDefinitionsReal a reader option - src/MachOWriterExecutable.hpp: make MakeTentativeDefinitionsReal a reader option - src/MachOReaderRelocatable.hpp: only assign a section name of __common to - tentative defintions when making a final linked image - - -2006-09-28 Nick Kledzik - - src/Options.h/.cpp: add support for -segaddr option - src/MachOWriterExecutable.hpp: In Writer::assignFileOffsets(), use -segaddr info - - -2006-09-28 Nick Kledzik - - Emit new CPU subtypes for ppc64 and x86-64 when targeting 10.5 or later - src/MachOWriterExecutable.hpp: set high bit of cpusubtype of 64-bit main executables when targeting 10.5 or later - - -2006-09-28 Devang Patel - - Add LLVM LTO support - src/LLVMReader.hpp: New file. - src/ld.cpp: Add optimization phase. Use LLVM LTO. - unit-tests/test-cases/llvm-integration: New tests. - -2006-09-27 Nick Kledzik - - ld64.xcodeproj/project.pbxproj: remove accidental install of source file into man1 - - -2006-09-25 Nick Kledzik - - src/Architectures.hpp: add kPointerDiff16 for ppc and ppc64 - src/MachOReaderRelocatable.hpp: support kPointerDiff16 - src/MachOWriterExecutable.hpp: support kPointerDiff16 - ------ Tagged ld64-63.1 - -2006-09-22 Nick Kledzik - - src/MachOWriterExecutable.hpp: include stubs in LC_SEGMENT_SPLIT_INFO - - -2006-09-21 Nick Kledzik - - src/Options.cpp: disable split-seg dylibs for 64-bit architectures - - -2006-09-19 Nick Kledzik - - src/MachOReaderRelocatable.hpp: rework __cstring parsing to better handle mixed alignment cstrings - src/MachOWriterExecutable.hpp: in -r mode, make all __cstrings aligned to section alignment - - -2006-09-19 Nick Kledzik - - src/MachOWriterExecutable.hpp: rework encoding of LC_SEGMENT_SPLIT_INFO - - -2006-09-19 Nick Kledzik - - src/Options.cpp: check for -search_paths_first in first pass - - ------ Tagged ld64-63 - -2006-09-15 Nick Kledzik - - src/Options.cpp: since the ld64 will repeatedly search an archive, and some project list archives - multiple times on command line to work with traditional linkers, automatically ignore duplicate libraries - unit-tests/test-cases/archive-duplicate: added test case - - -2006-09-15 Nick Kledzik - - src/Options.cpp: support -r -static - src/MachOWriterExecutable.hpp: support -r -static an don't generate LC_DYSYMTAB - - -2006-09-14 Nick Kledzik - - src/MachOWriterExecutable.hpp: in -r mode references to weak symbols should not create external relocations - as that can cause nmedit to errror later. - - -2006-09-13 Nick Kledzik - - ld64: Handle .objc_class_name exports specially - src/Options.cpp: add hack so that .objc_class_name_XXX in -exported_symbols_list imples _OBJC_CLASS_$_XXX - src/ld.cpp: add hack to supporess errors about .objc_class_name_XXX or _OBJC_CLASS_$_XXX being undefined - - -2006-09-12 Nick Kledzik - - Support -prebind when targeting ppc and OS < 10.4 - src/Options.h: add splitSeg() and baseWritableAddress() - src/Options.cpp: Add support for -seg_addr_table and LD_SEG_ADDR_TABLE, and -prebind and LD_PREBIND. - src/src/MachOWriterExecutable.hpp: support split-seg and canonical prebound files to be generated - - -2006-09-11 Nick Kledzik - - Linking a dylib or binary from identical binaries should produce the same output - src/MachOWriterExecutable.hpp: set the timestamps to be constant - - -2006-09-11 Nick Kledzik - - Linker support for ordering all sections and symbols - src/Options.cpp: Add -order_file_statistics. Allow architecture prefixes in order files - src/ld.cpp: Use fOptions.printOrderFileStatistics() - - -2006-09-11 Nick Kledzik - - Support -sectorder - unit-tests/test-cases/order_file: added test case - src/ld.cpp: Implement order file support in Linker::sortAtoms() - src/Options.h: add Options.orderedSymbols() - src/Options.cpp: add parseOrderFile(), implement -order_file - - -2006-09-07 Nick Kledzik - - need -i for 64-bit (or equivalent) - Support -i for aliasing exported symbols - unit-tests/test-cases/alias-objects: added - unit-tests/test-cases/alias-command-line: added - src/ObjectFile.h: Added Atom::getOrdinal() as new way to sort atoms. Added ReaderOptions.fAliases - src/MachOReaderRelocatable.hpp: Added SymbolAliasAtom to handle multiple symbols to same address - src/MachOReaderArchive.hpp: implement Atom::getOrdinal() to space out atom ordinals across member objects - src/Options.cpp: support -i, -alias, -alias_list. Move search of /Network/Library/Frameworks to after /System/Library/Frameworks - src/MachOWriterExecutable.hpp: pad out seg_info data. Implement getOrdinal(). - src/ObjectDump.cpp: call constructors directly instead of using make() wrapper - - -2006-09-01 Nick Kledzik - - Need the ability to tag libraries/plug-ins with security attributes - src/MachOReaderDylib.hpp: add warning if using -root_safe or -setuid_safe and link against dylib that is not - src/ObjectFile.h: add ReaderOption fRootSafe and fSetuidSafe - src/Options.cpp: handle -root_safe or -setuid_safe command line options - src/MachOWriterExecutable.hpp: set MH_ROOT_SAFE and MH_SETUID_SAFE flags - - -2006-08-31 Nick Kledzik - - src/ld.cpp: Add Linker::processDTrace() for processing dtrace static probes - src/OpaqueSection.hpp: renamed, add symbol name, add ability to add references - ld64.xcodeproj/project.pbxproj: remove SectCreate.cpp, add OpaqueSection.hpp - - -2006-08-28 Nick Kledzik - - Add convention for removing symbols at link time - Assembler -L option causes ld64 to split stubs - unit-tests/test-cases/special-labels: added test case - src/MachOReaderRelocatable.hpp: ignore L* labels, make l* labels as kSymbolTableNotIn - - -2006-08-28 Nick Kledzik - - src/lObjectFile.h: refactor isTargetUnbound() into getTargetBinding() - src/ld.cpp: create __dof section in final linked images from dtrace static probes - src/Architectures.hpp: add kDtraceProbe - src/Options.h/cpp: Add support for -dtrace - src/machochecker.cpp: support LC_SEGMENT_SPLIT_INFO - src/MachOWriterExecutable.hpp: support kDtraceProbe - src/MachOReaderRelocatable.hpp: suppport kDtraceProbe - - -2006-08-25 Nick Kledzik - - generate LC_SEGMENT_SPLIT_INFO for 10.5 or later dylibs - src/Options.h&.cpp: implement sharedRegionEligible() to control when LC_SEGMENT_SPLIT_INFO is added - src/MachOFileAbstraction.hpp: add macho_linkedit_data_command - src/MachOWriterExecutable.hpp: generate LC_SEGMENT_SPLIT_INFO load command and linkedit content - ------ Tagged ld64-62 - -2006-08-15 Nick Kledzik - - wrong error message when symbol is found in unused indirect library - src/ld.cpp: remove indirect libraries if they are not re-exported - unit-tests/test-cases/indirect-dylib: added test case - - -2006-08-15 Nick Kledzik - - alignment needs to be richer - src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info - src/ld.cpp: modify SymbolTable::add() to work with new Alignment type - src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers() - src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address - src/ObjectDump.cpp: print richer Alignment info - unit-tests/test-cases/align-modulus: added test case - - -2006-08-11 Nick Kledzik - - remove OPEN_SOURCE conditionals around x86_64 support - - -2006-07-31 Nick Kledzik - - ld64 while linking cc1 [ when dead_strip is ON] - src/ld.cpp: Add ivar fAtomsWithUnresolvedReferences to track atoms not initially resolvable - unit-tests/test-cases/dead_strip-archive: added test case - - -2006-07-31 Nick Kledzik - - x86_64: instructions with immediate and rip-relative operands need to use new relocation types - src/MachOWriterExecutable.hpp: generate new reloc types in -r mode - src/MachOReaderRelocatable.hpp: parse new reloc types - unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type - - -2006-07-18 Nick Kledzik - - src/MachOReaderRelocatable.hpp: suppress warning about dwarf info parsing for one benign no-op case - the compiler emits when there are not functions in the __text section - - -2006-07-17 Nick Kledzik - - faster debug note generation - src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a - pass per .o file. Added timing info for collectDebugInfo() to -print_statistics - unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r - unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order - - -2006-07-17 Nick Kledzik - - ld64 VSIZE is 1.18GB when building Finder ppc64 - src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped - ------ Tagged ld64-61.1 - -2006-07-11 Nick Kledzik - - ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name - src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message - -2006-07-11 Nick Kledzik - - If -arch is missing, rollover to ld_classic does not happen - src/Options.h: make gotoClassicLinker() public - src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386 - ------ Tagged ld64-61 - -2006-06-29 Nick Kledzik - - ld64 should be renamed to ld - src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen - src/ld.cpp: alter version string - ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64 - doc/man/man1/ld.1: added - ------ Tagged ld64-60 - -2006-06-28 Nick Kledzik - - Can't link large ppc64 program: ld64 says "bl out of range" - MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions - and properly chain together branch islands - MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references - only when done - -2006-06-28 Nick Kledzik - - MySQL-36 fails to build with ld64-59 - src/MachOReaderRelocatable.hpp: back out fix for 4585335 - src/MachOWriterExecutable.hpp: back out fix for 4585335 - -2006-06-27 Nick Kledzik - - src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how - dwarf debug notes are formed. - -2006-06-23 Nick Kledzik - - - - ld64 doesn't support variant linking -framework fw,_debug - src/Options.cpp: enhance findFramework() to support suffixes - ------ Tagged ld64-59 - -2006-06-22 Nick Kledzik - - ld64 lost DWARF debug notes - src/MachOReaderRelocatable.hpp: add fHasUUID so kDebugInfoStabsUUID can be set later - unit-tests/test-cases/dwarf-debug-notes-r: added test case - -2006-06-21 Nick Kledzik - - python 64-bit address miscalculation - src/MachOReaderRelocatable.hpp: change getTargetOffset() to sign extend the 32-bit value to 64-bits - -2006-06-21 Nick Kledzik - - ld64 seems to offset things incorrectly when using -r - src/MachOWriterExecutable.hpp: in -r mode, virtual sections should not increment address - - ------ Tagged ld64-58 - -2006-06-16 Nick Kledzik - - src/rebase.cpp: fix page alignment problem - src/rebase.cpp: fix endianess problem with local non-lazy pointers - -2006-06-15 Nick Kledzik - - src/rebase.cpp: fix to build in CurryWeed - ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed - -2006-06-15 Nick Kledzik - - Support .objc_class_name_* symbols - src/ObjectFile.h: Add kSymbolTableInAsAbsolute - src/MachOReaderRelocatable.hpp: synthesize references to required objc classes - src/MachOWriterExecutable.hpp: write objc_class_name as absolute symbol - unit-tests/test-cases/objc-references: added - -2006-06-15 Nick Kledzik - - SECTION_ATTRIBUTES unset in ppc64 mach-o header - src/MachOWriterExecutable.hpp: add section attribute for sections with code - -2006-06-15 Nick Kledzik - - ld64 bogus duplicate symbol name linking GNU libobjc - src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes - -2006-06-15 Nick Kledzik - - x86_64: ".align" directive not honored - src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size - -2006-06-14 Nick Kledzik - - jump table into middle of weak symbol causes error - src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols - src/MachOWriterExecutable.hpp: do not error on absolute references to interior of weak symbols - -2006-06-13 Nick Kledzik - - src/Options.cpp: allow -image_base as an alias for -seg1addr - -2006-06-13 Nick Kledzik - - implement -d - src/Options.h: add fMakeTentativeDefinitionsReal - src/Options.cpp: set fMakeTentativeDefinitionsReal if -d option is found - src/MachOWriterExecutable.hpp: turn tentative into real definition if makeTentativeDefinitionsReal - unit-tests/test-cases/btentative-to-real: added test case - -2006-06-13 Nick Kledzik - - implement -bundle_loader - src/Options.h: add fBundleLoader bit to DynamicLibraryOptions - src/Options.cpp: handle -bundle_loader - src/ld.cpp: pass fBundleLoader bit to MachOReaderDylib - src/MachOReaderDylib.hpp: support reading MH_EXECUTE files if fBundleLoader is set - src/MachOWriterExecutable.hpp: set bundle loader ordinal as EXECUTABLE_ORDINAL - unit-tests/test-cases/bundle_loader: added test case - -2006-06-12 Nick Kledzik - - -syslibroot can cause "can't find ordinal for imported" error - src/MachOReaderDylib.hpp: in Reader::reExports() compare install path in addition to load path - - -2006-06-10 Nick Kledzik - - Need rebasing tool - src/rebase.cpp: added - unit-tests/test-cases/rebase-basic: added - doc/man/man1/rebase.1: added - ld64.xcodeproj/project.pbxproj: added rebase target. changed all targets to build with dwarf - - -2006-06-10 Nick Kledzik - - src/machochecker.cpp: add some ppc reloc sanity checking - ------ Tagged ld64-57 - -2006-06-06 Nick Kledzik - - ld64 is not adding a final '/' char on the initial directory-name SO stab debug map entry - ld.cpp: Change Linker::synthesizeStabs() to assure directory SO always has a trailing slash - unit-tests/test-cases/dwarf-debug-notes/expected-stabs: update with trailing / - -2006-06-06 Nick Kledzik - - -sectcreate of a 0-byte section fails - MachOWriterExecutable.cpp: Don't error out on zero length segments - MachOWriterExecutable.cpp: For ppc64 reloc base address is the first writable segment iff - there is a writable segment >4GB from base address - -2006-06-04 Eric Christopher - - Radar 4560240 - Radar 3964999 - * src/ld.cpp (createReader): Fixed error message. - (resolve): Ditto. - (resolveFrom): Ditto. - (checkUndefines): Ditto. - ------ Tagged ld64-56 - -2006-05-23 Nick Kledzik - - No debug notes for ObjC methods when linking with ld64 - ld.cpp: don't limit debug notes to functions starting with underscore - -2006-05-22 Nick Kledzik - - ld64 spends much time in mach_o::relocatable::Reader::findAtomByName - * src/MachOReaderRelocatable.hpp: add makeReferenceToSymbol() so that x86_64 does not need to do by-name lookups - -2006-05-22 Nick Kledzik - - remove inferring warning - * ld.cpp: Remove "inferring" warning. If a link failed and now arch was specifed add which arch was - inferred to error message - -2006-05-19 Nick Kledzik - - ld64 does not honor -arch_multiple - * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message - -2006-05-19 Nick Kledzik - - Support S_16BYTE_LITERALS section types - * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS - * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS - -2006-05-19 Nick Kledzik - - "warning can't parse dwarf compilation unit info" warnings building debug - * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing - ------ Tagged ld64-55 - -2006-05-18 Nick Kledzik - - Default the pagezero size to 4GB for x86-64 - * src/Options.cpp: Chnage default the pagezero size to 4GB for x86-64 - -2006-05-18 Nick Kledzik - - x86_64 CarbonCore fails to link with "atom not found in symbolIndex" - * src/MachOWriterExecutable.hpp: in buildObjectFileFixups() don't call addObjectRelocs() on kNoFixUp references - -2006-05-18 Nick Kledzik - - ld64: .section defaults to read-only - * src/MachOReaderRelocatable.hpp: default unknown segments to r/w - -2006-05-18 Nick Kledzik - - -fvisibility=hidden causes crashes for x86_64 - * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions - -2006-05-12 Nick Kledzik - - * src/Architectures.hpp: add x86::kAbsolute32 - * src/MachOReaderRelocatable.hpp: generate x86::kAbsolute32 for mdynamic-no-pic instructions - * src/MachOWriterExecutable.hpp: process x86::kAbsolute32 reference kind - ------ Tagged ld64-54 - -2006-05-11 Nick Kledzik - - CF-393 failes to link for x86_64 - * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer::fixUpReferenceRelocatable - -2006-05-11 Nick Kledzik - - warning arch x86_64 not found using i386 - * src/ld.cpp: remove hack to allow x86_64 to link against i386 dylibs - - -2006-05-10 Nick Kledzik - - x86_64: .objc_class_name symbol names scrambled - * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections - - -2006-05-08 Nick Kledzik - - Support -dead_strip - * src/Options.h/cpp: implement -why_load and -why_live. Enable -dead_strip. - * src/MachOReaderArchive.hpp: implement -why_load - * src/MachOReaderRelocatable.hpp: suppress GCC_except_table* symbols in final output - * src/ld.cpp: implement dead code stripping - * unit-tests/test-cases/dead_strip: added - ------ Tagged ld64-53 - -2006-05-05 Nick Kledzik - - * src/Options.cpp: make 10.4 be minimum OS version for newer architectures - -2006-05-05 Nick Kledzik - - N_SO symbols in 64-bit builds have a zero address for n.n_value - * src/ld.cpp: for SO stabs, associate first and last atom in the SO range - * src/MachOWriterExecutable.hpp: use atom associated with SO stab to set ins n_value - -2006-05-05 Nick Kledzik - - * MachOWriterExecutable.hpp: fix end FUN stab to have length of function - - -2006-05-02 Nick Kledzik - - 64-bit main executables should have 4GB zero page by default - * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64 - 64 bit: apps with -mdynamic-no-pic seg fault when page zero > 4GB - * src/MachOWriterExecutable.cpp: rework pagezero for ppc64 so that if any mdynamic-no-pic code - is found, the code is kept in the low 2GB, and a new segment is create to map away up to 4GB. - -2006-05-02 Nick Kledzik - - * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack - overlaps shared region - ------ Tagged ld64-52.1 - -2006-05-01 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle - the __data section too. - ------ Tagged ld64-52 - -2006-04-28 Nick Kledzik - - 64-bit: 9A152 TextEdit crashes in dlopen on bring-up - * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection - -2006-04-28 Nick Kledzik - - 64 Bit: Development build of ppc64 TextEdit gets confused about static variables - * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol - - - -2006-04-21 Nick Kledzik - - * src/Options.cpp: fix default address for ppc64 custom stack - * src/MachOWriterExecutable.cpp: fix set up of ppc64 custom stack - - -2006-04-14 Nick Kledzik - - * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name - ------ Tagged ld64-51.1 - -2006-04-13 Nick Kledzik - - 64-bit: 9A152 TextEdit crashes in dlopen on bring-up - * src/MachOReaderRelocatable.hpp: when detecting anonymous non-lazy-pointers disqualify data - that points to static or global symbols - * src/ld.cpp: print version of ld64 in error messages - - ------ Tagged ld64-51 - -2006-04-11 Nick Kledzik - - exported symbols not properly stripped - * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope() - -2006-03-31 Nick Kledzik - - ld64 fails when linking debug ppc64 HIToolbox - * src/MachOReaderRelocatable.hpp: handle anonymous non-lazy pointers encoded with local relocations - * src/MachOWriterExecutable.hpp: in -r mode, only generated INDIRECT_SYMBOL_LOCAL for non-lazy targets that - - -2006-03-31 Nick Kledzik - - ld64 should remove generated file if link errors out - * src/MachOWriterExecutable.hpp: catch exceptions in Writer::write(), delete output file, and rethrow - - ------ Tagged ld64-50 - - -2006-03-29 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols - * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space - -2006-03-28 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info - ------ Tagged ld64-49.1 - -2006-03-25 Nick Kledzik - - * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB - ------ Tagged ld64-49 - -2006-03-24 Nick Kledzik - - * src/MachOWriterExecutable.hpp: dyld is allowed to have synthesized non-lazy pointers - ld64 is after processing bad GSYM stabs - * src/MachOReaderRelocatable.hpp: if a GSYM is found that does not match any data symbol, suppress it - -2006-03-23 Nick Kledzik - - * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceFinal() fix when x86::kPointer is for an - external relocation - -2006-03-23 Nick Kledzik - - * src/Options.cpp: change macosx-min-version to default to a per-architecture setting - add warning if -pagezero_size is not page aligned - * src/MachOWriterExecutable.hpp: properly handle external relocations for ppc64 with 4GB pagezero - * src/machochecker.cpp: sanity check relocation records - ------ Tagged ld64-48 - -2006-03-21 Nick Kledzik - - 64bit: passing function pointer to another function passes the wrong function address - * src/MachOReaderRelocatable.hpp: when processing a non-lazy pointer to a static function, don't accidentally - match it to a STAB symbol. - -2006-03-21 Nick Kledzik - - .eh symbols make up 13% of libstdc++'s stripped binary size - * src/ObjectFile.h: add ReaderOptions.fForFinalLinkedImage - * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage - * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image - -2006-03-21 Nick Kledzik - - ld64 does not parse optional second argument to -filelist - * unit-tests/test-cases/filelist: added - * src/Options.cpp: in Options::loadFileList() handle comma option - - ------ Tagged ld64-47.1 - - ------ Tagged ld64-47 - - ------ Tagged ld64-46 - -2006-03-10 Nick Kledzik - - ld64 should figure out architecture from .o files - * unit-tests/test-cases/auto-arch: added - * src/ld.cpp: added Linker::inferArchitecture() to scan .o files are infer architecture to link - * src/MachOReaderArchive.hpp: enhanced validFile() to look deeper into archive and really valdate - * src/MachOWriterExecutable.hpp: stop using fOptions.architecture() - * src/Options.cpp: stop defaulting to ppc64 - - -2006-03-09 Nick Kledzik - - Need "intentionally left blank" dylib stubs - * unit-tests/include/common.makefile: add VALID_ARCHS - * unit-tests/run-all-unit-tests: set up VALID_ARCHS - * unit-tests/test-cases/blank-stubs: add test case - * src/ld.cpp: in addDylib(), detect and ignore blank stubs - * src/MachOReaderDylib.hpp: in constructor, handle blank stubs - -2006-03-09 Nick Kledzik - - crash in stub with 2GB pagezero - * src/MachOWriterExecutable.hpp: StubAtom can't be no-pic if a large zero-page is used - -2006-03-06 Nick Kledzik - - * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two - ------ Tagged ld64-45 - - -2006-03-06 Nick Kledzik - - LP64/9A122: ld64: hang when trying to link DiscRecording framework - * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion - - ------ Tagged ld64-44 - -2006-03-04 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix again test for detection of anonymous non-lazy-pointer. - Error out if .o file contains old __DWARFA style dwarf. - -2006-03-02 Nick Kledzik - - * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. - ------ Tagged ld64-43 - - -2006-03-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: tighten detection of anonymous non-lazy-pointer - ------ Tagged ld64-42 - -2006-02-28 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment - -2006-02-28 Nick Kledzik - - SWB: ld64-37 (can't resolve symbol ___dso_handle) - * src/MachOWriterExecutable.hpp: add class DsoHandleAtom - -2006-02-28 Nick Kledzik - - * unit-tests/test-cases/literals-coalesce-alignment: added test case - * src/ld.cpp: when coalescing strings pick one with greater alignment - ld64: CG link failed because lo14 reference to anonymous non-lazy-pointer not aligned - * unit-tests/test-cases/relocs-c/test.c: tweak to fail like 4458660 - * src/MachOReaderRelocatable.hpp: detect anonymous non-lazy-pointer and transform into real non-lazy-pointers - ------ Tagged ld64-41 - -2006-02-24 Nick Kledzik - - * src/Options.cpp: Warning about -no_dead_strip_inits_and_terms and -i options. - Fix -weak-l option. - ------ Tagged ld64-40 - -2006-02-24 Nick Kledzik - - Leopard9A113: ppc64 libstdc++.dylib initializer crashes in pthread_once - * unit-tests/test-cases/multiple-entry-points: added - * src/MachOReaderRelocatable.hpp: make sure that if there are multiple symbols with the same - address, that we properly make zero length atoms for all but last symbol - -2006-02-24 Nick Kledzik - - * src/Options.cpp: ld64 doesn't realpath(3) B&I tracing paths - -2006-02-24 Nick Kledzik - - * src/Options.cpp: 9A110: ld64 can't deal with section names >16 chars - -2006-02-23 Nick Kledzik - - * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations - * src/Options.cpp: use vector.reserve() to minimize re-allocations - * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations - * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations - * src/ld.cpp: use vector.reserve() to minimize re-allocations - -2006-02-23 Nick Kledzik - - ld64 creates corrupt executables (and has malloc errors) with -headerpad option - * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom::setSize() to update fLargestAtomSize - * unit-tests/test-cases/header-pad: added - -2006-02-23 Nick Kledzik - - ld64 creates invalid static executables - * src/MachOWriterExecutable.hpp: Change MachHeaderAtom::copyRawContent() to create correct header - for static executables. Change SymbolTableLoadCommandsAtom to skip LC_DYSYMTAB for static executables - * src/machochecker.cpp: Add tests that static executables are well formed - * unit-tests/test-cases/static-executable: added - -2006-02-22 Nick Kledzik - - * src/Options.cpp: chnage printf on unknown arg to a throw - ------ Tagged ld64-39 - -2006-02-20 Nick Kledzik - - * unit-tests/test-cases/read-only-relocs: added new test case - * src/MachOWriterExecutable.hpp: detect and error on relocs in read-only sections - * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs - -2006-02-20 Nick Kledzik - - * unit-tests/test-cases/stabs-coalesce: added new test case - * src/ld.cpp.hpp: in collectStabs removed unused stabs - ------ Tagged ld64-38 - -2006-02-17 Nick Kledzik - - * src/MachOWriterExecutable.hpp: set correct n_sect field of stabs - -2006-02-15 Nick Kledzik - - * src/MachOReaderArchive.hpp: with -all_load skip over both kinds of SYMDEFs - * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case - ------ Tagged ld64-37 - -2006-02-13 Eric Christopher - - * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. - Adjust whitespace. - -2006-02-13 Nick Kledzik - - * src/MachOWriterExecutable.hpp: in Writer::fixUpReferenceRelocatable() fix kPCRel32 for external case - -2006-02-13 Nick Kledzik - - * unit-tests/test-cases/zero-fill: added - * src/machochecker.cpp: check that S_ZEROFILL have no file offset - * src/MachOWriterExecutable.hpp: rework assignFileOffsets() to fix rdar://problem/4441145 - -2006-02-12 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix use of first zero-length c-string in .o file - -2006-02-12 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix uninitialized fAlignment - -2006-02-12 Nick Kledzik - - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add pointer-diff cases - * src/Architectures.hpp: make size explicit in ppc/ppc64 kPointerDiff - * src/MachOReaderRelocatable.hpp: don't allow kPointerDiff64 for ppc (just ppc64) - * src/MachOWriterExecutable.cpp: set proper r_length for ld -r of kPointerDiff - ------ Tagged ld64-36 - -2006-02-08 Nick Kledzik - - * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions - -2006-02-08 Nick Kledzik - - * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section - Keep S_COALESCED attribute for __eh_frame - -2006-02-08 Nick Kledzik - - * src/ld.cpp: Temporarily turn allowable client errors into warnings - * unit-tests/test-cases/allowable-clientMakefile: Temporarily let warnings be ok for above - * src/MachOWriterExecutable.hpp: fix ld -r to not use external relocations for symbols make static - -2006-02-08 Nick Kledzik - - * src/ld.cpp: A sibling in an umbrella can always link with its other siblings - * unit-tests/test-cases/allowable-client: add test case for above - -2006-02-08 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support LOCAL non-lazy pointers to hidden symbols - * src/machochecker.cpp: verify indirect symbol table - * unit-tests/test-cases/private-non-lazy: added test case - -2006-02-07 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix calculation of file offsets in ld -r mode - * src/machochecker.cpp: verify segment file offsets are within file - ------ Tagged ld64-35 - -2006-02-06 Nick Kledzik - - * ld.cpp: allow parent of sub-framework to link - * unit-tests/test-cases/allowable-client/Makefile: added cases for parent and clients of parent - -2006-02-04 Nick Kledzik - - * unit-tests/test-cases/relocs-c/test.c: added some array cases - * src/MachOReaderRelocatable.hpp: factor out makeReferenceToEH() - * src/MachOWriterExecutable.hpp: add initial support for non-lazy pointer synthesis - ------ Tagged ld64-34 - -2006-02-04 Nick Kledzik - - * src/ld.cpp: fix -no_arch_warnings - fix -undefined warning - Do BINCL/EINCL optimization for gfull stabs - Implement "essential symbols" for stabs (-Sp) - Fix allowable clients to only test on direct libraries - * src/MachOReaderRelocatable.hpp: support BINCL/EINCL stabs - -2006-02-03 Nick Kledzik - - * src/machochecker.cpp: add code to check load command alignment - * src/MachOWriterExecutable.hpp: make load command alignment depend on architecture - -2006-02-03 Nick Kledzik - - * unit-tests/test-cases/literals-coalesce: added - * src/MachOReaderRelocatable.hpp: assure all targets of low14 ppc relocs are at least 4-byte alignmented - ------ Tagged ld64-33 - -2006-02-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals - * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 - ------ Tagged ld64-32 - -2006-02-02 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms - -2006-02-02 Nick Kledzik - - * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one - * unit-tests/test-cases/archive-weak: add test case for above - * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself - * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list - -2006-02-01 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 - -2006-02-01 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms - ------ Tagged ld64-31 - -2006-02-01 Eric Christopher - - * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling... - * unit-tests/include/common.makefile: ... here. - * unit-tests/bin/fail-if-stdin.pl: New. - * unit-tests/test-cases/no-uuid: Ditto. - * src/ld.cpp (Linker::) Add fCreateUUID. - (::Linker): Initialize. - (::collectStabs): Use. Set if dwarf or we have a UUID already. - (::writeOutput): Pass as argument to Writer::write along with option. - * src/Options.h (Option::emitUUID): Declare. - (Option::fEmitUUID): Ditto. - * src/Options.cpp (Option::emitUUID): New. - (parse): Handle -no_uuid. - * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID. - * src/ExecutableFile.h (Writer::Write): Add createUUID boolean. - * src/MachOWriterExecutable: Add UUID forward declaration. - (fUUIDAtom): New. - (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size - to zero at start. - (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID. - (MachHeaderAtom::copyRawContent): Don't count a load command if its size is - 0. - (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit. - - -2006-01-31 Nick Kledzik - - * unit-tests/test-cases/dwarf-debug-notes : Added - * src/ld.cpp: don't generate debug note for .eh symbols - * src/MachOReaderRelocatable.hpp: make dwarf line info to atom matching faster and better - -2006-01-31 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard - * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers - -2006-01-31 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: better error message for bad relocs - * src/ObjectDump.cpp: add emacs tab settings - * src/SectCreate.h: ditto - * src/SectCreate.cpp: ditto - * src/machochecker.cpp: ditto - * src/ExecutableFile.h: ditto - -2006-01-30 Eric Christopher - - * src/ExecutableFile.h: Indent. - -2006-01-30 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: performance improvements - * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore - -2006-01-30 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above - -2006-01-29 Nick Kledzik - - * unit-tests/test-cases/weak_import: added test case - * src/ld.cpp: move code for weak_import mismatch to writer - * src/ObjectFile.h: remove ImportWeakness methods - * src/MachOReaderDylib.hpp: ditto - * src/SectCreate.cpp: ditto - * src/Architectures.hpp: add new ReferenceKinds for weak_imports - * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds - * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches - -2006-01-29 Nick Kledzik - - * src/Options.cpp: verify -allow_stack_execute is only used on main executables - -2006-01-29 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff - * src/debugline.c: sync with latest dwarf reader from Geoff - -2006-01-27 Eric Christopher - - * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. - -2006-01-27 Eric Christopher - - * src/Options.h (Options): Add hasExecutableStack, fExecutableStack. - * src/Options.cpp (Options::hasExecutableStack): New. - (Options::parse): Parse -allow_stack_execute. - * src/MachOWriterExecutable.hpp (MachHeaderAtom::copyRawContent): - Implement MH_ALLOW_STACK_EXECUTION. - * unit-tests/include/common.makefile (FAIL_IF_EMPTY): New. - * unit-tests/bin/fail-if-no-stdin.pl: New file. - * unit-tests/test-cases/allow-stack-execute: New directory. - -2006-01-27 Nick Kledzik - - * src/MachOFileAbstraction.hpp: rely on latest system headers - * src/MachOWriterExecutable.hpp: fix ppc stubs. - wrote new relocationNeededInFinalLinkedImage() to replace common code - -2006-01-27 Eric Christopher - - * src/ld.cpp (logTraceInfo): New. - (Linker::addArchive): Use. - (Linker::addDylib): Ditto. - * src/ObjectFile (ReaderOptions::fTraceOutputFile): New. - * src/MachOReaderArchive.hpp (Reader::Reader): Move trace - logging to Linker::addArchive. - * src/Options.cpp (parsePreCommandLineEnvironment): Check - LD_PRINT_FILE if tracing dylibs or archives. - -2006-01-26 Nick Kledzik - - * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes - -2006-01-26 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state - -2006-01-26 Nick Kledzik - - Rewrite all stabs processing. - Move sythesize of debug notes into ld.cpp - -2006-01-26 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs - -2006-01-25 Nick Kledzik - - * ld64.xcodeproj/project.pbxproj: special case building in Curry - -2006-01-25 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis - -2006-01-24 Eric Christopher - - * src/ld.cpp (Linker::createReaders): Change logging title to XBS. - (Linker::addDylib): Ditto. - * src/MachOReaderArchive.hpp (Reader::Reader): Ditto. - * src/Options.h (fPrintOptions): New. - * src/Options.cpp (Options::Options): Initialize above. - (Options::checkForFile): Change logging title to XBS. - (Options::findFramework): Ditto. - (Options::parse): Add log for options. - (Options::parsePreCommandLineEnvironmentSettings): Add LD_TRACE_ARCHIVES, - LD_TRACE_DYLIBS, and LD_PRINT_OPTIONS. - -2006-01-24 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: better C++ eh parsing - -2006-01-23 Eric Christopher - - * unit-tests/bin/fail-if-exit-zero.pl: New. - * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use. - * unit-tests/allowable-client: New test. - * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib. - * src/Options.h (allowableClients): New. - (clientName): Ditto. - (fAllowableClients): Ditto. - (fClientName): Ditto. - * src/Options.cpp: Implement above. - (parse): Handle -allowable_client and -client_name. - * src/MachOReaderDylib.hpp (getAllowableClients): New. - (fAllowableClients): Ditto. - (Reader): Process LC_SUB_CLIENT load command. - * src/ObjectFile.h (parentUmbrella): New. - (getAllowableClients): New. - * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New. - -2006-01-23 Nick Kledzik - - * unit-tests/test-cases/archive-basic: added - * src/ld.cpp: fix shadowed local variable - * src/FileAbstraction.hpp: ld64 shouldn't inline when building debug - -2006-01-23 Nick Kledzik - - * src/ld.cpp: fix symbol not found error message - * src/MachOReaderDylib.hpp: add logging to hash table - * src/MachOReaderRelocatable.hpp: enable stabs processing. Handle static functions with stubs - handle labeled cstrings. - * src/MachOWriterExecutable.hpp: properly suppress atoms not in symbol table. fix low14 error check. - add StubAtomHelper. - * unit-tests/test-cases/relocs-literals/test.c: add more interesting edge cases - -2006-01-17 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes - -2006-01-16 Nick Kledzik - - * src/debugline.{sh}: added - * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf - * src/MachOWriterExecutable.hpp: fix lazy pointer section - * src/ObjectDump.hpp: Fix conditionalization - * unit-tests/test-cases/dwarf-strip: added - -2006-01-11 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 - * src/ObjectDump.hpp: Support -arch option - -2006-01-10 Nick Kledzik - - * src/MachOWriterExecutable.hpp: fix stubs for ppc64 - * src/MachOFileAbstraction.hpp: fix typo for macho_routines - * ld64.xcodeproj/project.pbxproj: add machochecker target - * src/machochecker.cpp: new skeleton for checking mach-o file bit - * unit-tests/: Add support for running machochecker - -2006-01-10 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed - * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime - -2006-01-09 Nick Kledzik - - * track modification time of .o files so that sythesized OSO stab will have it - -2006-01-09 Nick Kledzik - - * src/MachOFileAbstraction.hpp: add macho_uuid_command - * src/MachOWriterExecutable.cpp: add UUID load command to generated files - -2006-01-09 Nick Kledzik - - * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped - * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped - -2006-01-05 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: support new relocations - -2006-01-05 Nick Kledzik - - * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB - * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor - -2006-01-05 Nick Kledzik - - refactor: transform Atom::dontStripName() to getSymbolTableInclusion() - * src/ld.cpp: pass dyld_stub_binding_helper to writer - * src/MachOReaderRelocatable.hpp: update synthesized stabs - Ignore stubs and lazy pointers in .o files - Support initializers and terminators - * src/MachOWriterExecutable.hpp: synthesize stubs and lazy pointers as needed - * ld64.xcodeproj/project.pbxproj: change Release target to build with dwarf - -2006-01-03 Eric Christopher - - * src/Options.h (multipleDefinitionsInDylibs): Declare. - (overridingDefinitionInDependentDylib): Ditto. - (warnOnMultipleDefinitionsInObjectFiles): Ditto. - (multiplyDefined): Remove. - (multiplyDefinedUnused): Ditto. - (fMultiplyDefined): Ditto. - (fWarnOnMultiplyDefined): New. - (fMultiplyDefinedDynamic): Ditto. - * src/Options.cpp (Options::Options): Initialize above. - (overridingDefinitionInDependentDylib): New. - (multipleDefinitionsInDylibs): Ditto. - (warnOnMultipleDefinitionsInObjectFiles): Ditto. - (parse): Update comments. Fix parsing of -y option. - Update error message for -dead_strip. Parse above - options. - -2006-01-02 Nick Kledzik - - * Refactor: move Atom::writeContent() to Writer - -2005-12-23 Nick Kledzik - - * Reworked, simplify, and document test harness - * unit-tests/README: Added - -2005-12-23 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fixes for Objective-C - * unit-tests/test-cases/relocs-objc: Added - -2005-12-22 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair - * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf - -2005-12-21 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Fix parsing of literal sections - * src/MachOWriterExecutable.hpp: Fix writing of literal sections - * unit-tests/test-cases/relocs-literals: Added - -2005-12-15 Eric Christopher - - * src/Options.h (enum Treatment): New. - (enum PICTreatment): Delete. - (enum VersionMin): New. - (prebind): Declare. - (macosxVersionMin): Ditto. - (multiplyDefined): Ditto. - (multiplyDefinedUnused): Ditto. - (setVersionMin): Ditto. - (setPICTreatment): Delete. - (setReadOnlyRelocTreatment): Ditto. - (picTreatment): Adjust return type. - (parseTreatment): New. - (fPrebind): Ditto. - (fVersionMin): Ditto. - (fPICTreatment): Change type. - (fMultiplyDefined): New. - (fMultiplyDefinedUnused): Ditto. - (fLimitUndefinedSymbols): Ditto. - - * src/Options.cpp: Fix whitespace. Add comments on options. - (Options::Options): Add initializers for new variables. - (Options::prebind): New. - (Options::macosxVersionMin): Ditto. - (Options::parseTreatment): Ditto. - (Options::setVersionMin): Ditto. - (Options::setReadOnlyRelocTreatment): Delete. - (Options::setPICTreatment): Ditto. - (Options::Parse): Update for above. Add comments. - -2005-12-15 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Add comments about dwarf - -2005-12-14 Nick Kledzik - - * src/ELFFileAbstraction.hpp: Added - * src/ELFReaderRelocatable.hpp: Added - * Lot of fixes for new architecture - * Added __OPEN_SOURCE__ to "Preprocessor Macros" to disable new architecture support by default - -2005-12-13 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections - * unit-tests/test-cases/dwarf-ignore: added - -2005-12-12 Nick Kledzik - - * Added test harness and three initial tests: - relocs-asm, relocs-c, and hello-world - -2005-12-12 Nick Kledzik - - * src/MachOReaderRelocatable.hpp: Massive refactoring: - Now there are three Atom classes, Chopping into Atoms - is done on label boundaries or by knowledge of special - sections, Share lots of ppc/ppc64 code. - Stabs process code is temporarily disabled. - -2005-12-12 Nick Kledzik - - * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort - -2005-12-11 Eric Christopher - - * src/Options.cpp: Reformat. - * src/Options.h: Ditto. - -2005-12-07 Eric Christopher - - * src/MachOReaderRelocatable.hpp (Atom::getAlignment): - When calculating alignment of an Atom, take into account - the alignment from which we pulled the Atom. - -2005-12-06 Nick Kledzik - - * src/Options.cpp src/Options.h: Add design comments - -2005-12-05 Eric Christopher - - * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and - i386 linkers. - -2005-12-05 Eric Christopher - - * ChangeLog: New file. - -2005-12-02 Nick Kledzik - - * src/ObjectFile.h: Add design comments - -2005-11-30 Nick Kledzik - - * Fix uses of __OPEN_SOURCE__ - -2005-11-28 Nick Kledzik - - * Refactor Atom to use getDefinitionKind() - -2005-11-21 Nick Kledzik - - * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode - -2005-11-18 Nick Kledzik - - * x86 tweaks - -2005-11-18 Nick Kledzik - - * src/ObjectDump.cpp: make work with command line arguments - -2005-11-18 Nick Kledzik - - * Massive rework to remove preprocessor conditionals and use templates - -2005-11-14 Nick Kledzik - - * Created new Subversion repository for ld64 from cvs tag ld64-27.2 diff --git a/ld64/doc/man/man1/dyldinfo.1 b/ld64/doc/man/man1/dyldinfo.1 index 3dafedc..92deb13 100644 --- a/ld64/doc/man/man1/dyldinfo.1 +++ b/ld64/doc/man/man1/dyldinfo.1 @@ -1,4 +1,4 @@ -.Dd November 20, 2008 +.Dd November 10, 2010 .Dt dyldinfo 1 .Os Darwin .Sh NAME @@ -7,12 +7,14 @@ .Sh SYNOPSIS .Nm .Op Fl arch Ar arch-name +.Op Fl dylibs .Op Fl rebase .Op Fl bind .Op Fl weak_bind .Op Fl lazy_bind .Op Fl export .Op Fl opcodes +.Op Fl function_starts .Ar file(s) .Sh DESCRIPTION Executables built for Mac OS X 10.6 and later have a new format for the @@ -23,10 +25,12 @@ The options are as follows: .Bl -tag -width indent .It Fl arch Ar arch Only display the specified architecture. Other architectures in a universal image are ignored. +.It Fl dylibs +Display the table of dylibs on which this image depends. .It Fl rebase Display the table of rebasing information. Rebasing is what dyld does when an image is not loaded at its preferred address. Typically, this involves updating pointers in the __DATA -segment which are point within the image. +segment which point within the image. .It Fl bind Display the table of binding information. These are the symbolic fix ups that dyld must do when an image is loaded. @@ -41,6 +45,8 @@ functions in some external dylib. Display the table symbols which this image exports. .It Fl opcodes Display the low level opcodes used to encode all rebase and binding information. +.It Fl function_starts +Decodes the list of function start addresses. .El .Sh SEE ALSO .Xr otool 1 diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index b6363fc..d4d1329 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -1,4 +1,4 @@ -.Dd December 15, 2008 +.Dd Sept 2, 2010 .Dt ld 1 .Os Darwin .Sh NAME @@ -78,7 +78,7 @@ paths. .Ss Two-level namespace By default all references resolved to a dynamic library record the library to which they were resolved. At runtime, dyld uses that information to directly resolve -symobls. The alternative is to use the -flat_namespace option. With flat namespace, +symbols. The alternative is to use the -flat_namespace option. With flat namespace, the library is not recorded. At runtime, dyld will search each dynamic library in load order when resolving symbols. This is slower, but more like how other operating systems resolve symbols. @@ -114,7 +114,7 @@ Merges object files to produce another mach-o object file with file type MH_OBJE .It Fl dylinker Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when building dyld. .It Fl dynamic -The default. Implied by -dynamiclib, -bundle, or -execute +The default. Implied by -dylib, -bundle, or -execute .It Fl static Produces a mach-o file that does not use the dyld. Only used building the kernel. .It Fl arch Ar arch_name @@ -149,12 +149,18 @@ the first function in it is called. This is the same as listing a file name path to a shared library on the link line except that the linker will construct glue code so that the shared library is not loaded until the first function in it is called. +.It Fl upward-l Ns Ar x +This is the same as the -lx but specifies that the dylib is an upward dependency. +.It Fl upward_library Ar path_to_library +This is the same as listing a file name path to a library on the link line but also marks +the dylib as an upward dependency. .It Fl L Ns dir Add .Ar dir to the list of directories in which to search for libraries. Directories specified with -L are searched in the order they appear on the command line -and before the default search path. +and before the default search path. In Xcode4 and later, there can be a space between +the -L and directory. .It Fl Z Do not search the standard directories when searching for libraries and frameworks. .It Fl syslibroot Ar rootdir @@ -162,10 +168,15 @@ Prepend .Ar rootdir to all search paths when searching for libraries or frameworks. .It Fl search_paths_first -By default the -lx and -weak-lx options first search for a file of the form `libx.dylib' in each directory +This is now the default (in Xcode4 tools). When processing -lx the linker now searches each directory +in its library search paths for `libx.dylib' then `libx.a' before the moving on to the next path +in the library search path. +.It Fl search_dylibs_first +Changes the searching behavior for libraries. The default is that when processing -lx the linker +searches each directory in its library search paths for `libx.dylib' then `libx.a'. +This option changes the behavior to first search for a file of the form `libx.dylib' in each directory in the library search path, then a file of the form `libx.a' is searched for in the library search paths. -This option changes it so that in each path `libx.dylib' is searched for then `libx.a' before the -next path in the library search path is searched. +This option restores the search behavior of the linker prior to Xcode4. .It Fl framework Ar name[,suffix] This option tells the linker to search for `name.framework/name' the framework search path. If the optional suffix is specified the framework is first searched for the name with the suffix and then without @@ -181,13 +192,17 @@ This was previously done with a separate -sub_umbrella option. This is the same as the -framework name[,suffix] except that the linker will construct glue code so that the framework is not loaded until the first function in it is called. You cannot directly access -data or Objective-C classes in a frameworked linked this way. +data or Objective-C classes in a framework linked this way. +.It Fl upward_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] but also specifies that the +framework is an upward dependency. .It Fl F Ns dir Add .Ar dir to the list of directories in which to search for frameworks. Directories specified with -F are searched in the order they appear on the command line -and before the default search path. +and before the default search path. In Xcode4 and later, there can be a space between +the -F and directory. .It Fl all_load Loads all members of static archive libraries. .It Fl ObjC @@ -231,7 +246,7 @@ any symbol in that section that are specified in the order file is moved to the start of its section and laid out in the same order as in the order file .Ar file . Order files are text files with one symbol name per line. Lines starting with a # are comments. -A symbol name may be optionally preceded with its object file leafname and a colon (e.g. foo.o:_foo). +A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo). This is useful for static functions/data that occur in multiple files. A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). This enables you to have one order file that works for multiple architectures. @@ -249,6 +264,11 @@ This is set to indicate the oldest Mac OS X version that that the output is to b a later version enables the linker to assumes features of that OS in the output file. The format of .Ar version is a Mac OS X version number such as 10.4 or 10.5 +.It Fl ios_version_min Ar version +This is set to indicate the oldest iOS version that that the output is to be used on. Specifying +a later version enables the linker to assumes features of that OS in the output file. The format of +.Ar version +is an iOS version number such as 3.1 or 4.0 .It Fl image_base Ar address Specifies the perferred load address for a dylib or bundle. The argument .Ar address @@ -321,11 +341,11 @@ will cause a bus error if a NULL pointer is dereferenced. The argument is a hexadecimal number with an optional leading 0x. If .Ar size is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size -is 4KB. On 64-bit architectures, the default size if 4GB. The ppc64 architecture has some special cases. Since Mac +is 4KB. On 64-bit architectures, the default size is 4GB. The ppc64 architecture has some special cases. Since Mac OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless -macosx_version_min is 10.5 or later. Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero -size is set to 4KB and then a new unredable trailing segment is created after the code, filling up the lower 4GB. +size is set to 4KB and then a new unreadable trailing segment is created after the code, filling up the lower 4GB. .It Fl stack_size Ar size Specifies the maximum stack size for the main thread in a program. Without this option a program has a 8MB stack. The argument @@ -353,7 +373,7 @@ dynamic libraries the bundle was linked with. Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d -Force definition of common symbols. That is, transform tentative defintions into real definitions. +Force definition of common symbols. That is, transform tentative definitions into real definitions. .El .Ss Options that control symbol resolution .Bl -tag @@ -391,6 +411,11 @@ The specified is added to the list of global symbols names that will not remain as global symbols in the output file. This option can be used multiple times. For short lists, this can be more convenient than creating a file and using -unexported_symbols_list. +.It Fl reexported_symbols_list Ar file +The specified +.Ar filename +contains a list of symbol names that are implemented in a dependent dylib and should be re-exported +through the dylib being created. .It Fl alias Ar symbol_name Ar alternate_symbol_name Create an alias named .Ar alternate_symbol_name @@ -432,7 +457,7 @@ Specifies how commons (aka tentative definitions) are resolved with respect to d ignore_dylibs, use_dylibs, error. The default is ignore_dylibs which means the linker will turn a tentative definition in an object file into a real definition and not even check dylibs for conflicts. The dylibs option means the linker should check linked dylibs for definitions and use them to replace tentative definitions -from object files. The error option means the linker should issu an error whenever a tentative definition in an +from object files. The error option means the linker should issue an error whenever a tentative definition in an object file conflicts with an external symbol in a linked dylib. See also -warn_commons. .El .Ss Options for introspecting the linker @@ -463,7 +488,7 @@ Do not put debug information (STABS or DWARF) in the output file. .It Fl x Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and getting symbol names in back traces, but are not used at runtime. If -x is used with -r -non-global symbol names are not removed, but instead replaced with a unique, duumy name +non-global symbol names are not removed, but instead replaced with a unique, dummy name that will be automatically removed when linked into a final linked image. This allows dead code stripping, which uses symbols to break up code and data, to work properly and provides the security of having source symbol names removed. @@ -488,6 +513,10 @@ Prints the version of the linker. 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 +behavior and allows instructions on any page to be run. .It Fl no_eh_labels Normally in -r mode, the linker produces .eh labels on all FDEs in the __eh_frame section. This option suppresses those labels. Those labels are not needed by the Mac OS X 10.6 @@ -497,13 +526,23 @@ When producing a final linked image, the linker processes the __eh_frame section produces an __unwind_info section. Most FDE entries in the __eh_frame can be represented by a 32-bit value in the __unwind_info section. The option issues a warning for any function whose FDE cannot be expressed in the compact unwind format. +.It Fl warn_weak_exports +Issue a warning if the resulting final linked image contains weak external symbols. Such +symbols require dyld to do extra work at launch time to coalesce those symbols. +.It Fl objc_gc_compaction +Marks the Objective-C image info in the final linked image with the bit that says that the +code was built to work the compacting garbage collection. +.It Fl objc_gc +Verifies all code was compiled with -fobjc-gc or -fobjc-gc-only. +.It Fl objc_gc_only +Verifies all code was compiled with -fobjc-gc-only. .It Fl dead_strip_dylibs Remove dylibs that are unreachable by the entry point or exported symbols. That is, suppresses the generation of load command commands for dylibs which supplied no symbols during the link. This option should not be used when linking against a dylib which is required at runtime for some indirect reason such as the dylib has an important initializer. .It Fl allow_sub_type_mismatches -Normally the linker consisders different cpu-subtype for ARM (e.g. armv4t and armv6) to be different +Normally the linker considers different cpu-subtype for ARM (e.g. armv4t and armv6) to be different different architectures that cannot be mixed at build time. This option relaxes that requirement, allowing you to mix object files compiled for different ARM subtypes. .It Fl no_uuid @@ -522,7 +561,13 @@ Only used when creating a dynamic library. .It Fl sub_umbrella Ar framework_name The specified framework will be re-exported. Only used when creating a dynamic library. .It Fl allowable_client Ar name -Restricts what can link against the dynamic library being created. +Restricts what can link against the dynamic library being created. By default any code +can link against any dylib. But if a dylib is supposed to be private to a small +set of clients, you can formalize that by adding a -allowable_client for each client. +If a client is libfoo.1.dylib its -allowable_client name would be "foo". If a +client is Foo.framework its -allowable_client name would be "Foo". For the degenerate +case where you want no one to ever link against a dylib, you can set the +-allowable_client to "!". .It Fl client_name Ar name Enables a bundle to link against a dylib that was built with -allowable_client. The name specified must match one of the -allowable_client names specified when the dylib was created. @@ -601,11 +646,11 @@ driver when it is invoked with multiple -arch arguments. Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the libraries being linked against have not changed. .It Fl dot Ar path -Create a file a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. +Create a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. .It Fl keep_relocs Add section based relocation records to a final linked image. These relocations are ignored at runtime by dyld. .It Fl warn_stabs -Print a warning when the linker cannot do a BINCL/EINCL optimzation because the compiler put a bad stab symbol inside +Print a warning when the linker cannot do a BINCL/EINCL optimization because the compiler put a bad stab symbol inside a BINCL/EINCL range. .It Fl warn_commons Print a warning whenever the a tentative definition in an object file is found and a external symbol by the same name @@ -614,7 +659,7 @@ in a header file. .It Fl read_only_stubs [i386 only] Makes the __IMPORT segment of a final linked images read-only. This option makes a program slightly more secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code. The downside -is the dyld must use mprotect() to temporily make the segment writable while it is binding the stubs. +is the dyld must use mprotect() to temporarily make the segment writable while it is binding the stubs. .It Fl slow_stubs [i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which calls through a lazy pointer in the __DATA segment. @@ -626,6 +671,22 @@ is linked such that _malloc is interposable, then calls to malloc() from within stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed (not interposed) because they would be direct calls. +.It Fl no_function_starts +By default the linker creates a compress table of function start addresses in the LINKEDIT of +final linked image. This option disables that behavior. +.It Fl no_version_load_command +By default the linker creates a load command in final linked images that contains the -macosx_version_min. +This option disables that behavior. +.It Fl no_objc_category_merging +By default when producing final linked image, the linker will optimize Objective-C classes by merging +any categories on a class into the class. Both the class and its categories must be defined in the image +being linked for the optimization to occur. Using this option disables that behavior. +.It Fl object_path_lto Ar filename +When performing Link Time Optimization (LTO) and a temporary mach-o object file is needed, if this +option is used, the temporary file will be stored at the specified path and remain after the link +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. .El .Ss Obsolete Options .Bl -tag @@ -663,7 +724,7 @@ This is now the default so does not need to be specified. .It Fl multi_module Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. .It Fl no_dead_strip_inits_and_terms -The linker never dead strips initialzation and termination routines. They are considered "roots" of the dead strip graph. +The linker never dead strips initialization and termination routines. They are considered "roots" of the dead strip graph. .It Fl A Ar basefile Obsolete incremental load format. This option is obsolete. .It Fl b @@ -691,7 +752,7 @@ is used. This was previously used to debug where an undefined symbol was used, automatically prints out all usages. The -why_live option can also be used to display what kept a symbol from being dead striped. This option is obsolete. .It Fl Y Ar number -Used to control how many occurances of each symbol specifed with -y would be shown. This option is obsolete. +Used to control how many occurrences of each symbol specified with -y would be shown. This option is obsolete. .It Fl nomultidefs Only used when linking an umbrella framework. Sets the MH_NOMULTIDEFS bit in the mach_header. The MH_NOMULTIDEFS bit has been obsolete since Mac OS X 10.4. This option is obsolete. diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 9c5493c..cfb54eb 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ F96904890A4333AC00B77D2A /* PBXTargetDependency */, F9EA73970974999B008B4F1D /* PBXTargetDependency */, F9B693890EC4D28C00076912 /* PBXTargetDependency */, + F9F9AD68116D58AF0028EFAB /* PBXTargetDependency */, ); name = "unit-tests"; productName = "unit-tests"; @@ -43,13 +44,35 @@ /* Begin PBXBuildFile section */ F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; + F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93CB246116E69EB003233B8 /* tlvp.cpp */; }; F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; + F98498A310AE2159009E9878 /* compact_unwind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA963310A2545C0097A440 /* compact_unwind.cpp */; }; + F98498A410AE2159009E9878 /* got.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AB1063107D380700E54C9E /* got.cpp */; }; + F9849E3610B38EF5009E9878 /* order_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9849E3410B38EF5009E9878 /* order_file.cpp */; }; + F984A38210BB4B0D009E9878 /* branch_island.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F984A38010BB4B0D009E9878 /* branch_island.cpp */; }; + F989D30D106826020014B60C /* OutputFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F989D30B106826020014B60C /* OutputFile.cpp */; }; F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */; }; F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */; }; + F9A4DB9110F816FF00BD8423 /* objc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A4DB8F10F816FF00BD8423 /* objc.cpp */; }; + F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA44DA1294885F00CB8390 /* branch_shim.cpp */; }; + F9AA65111051BD2B003E3539 /* stubs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65101051BD2B003E3539 /* stubs.cpp */; }; + F9AA65891051E750003E3539 /* macho_relocatable_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */; }; + F9AA65DD1051EC4A003E3539 /* archive_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65D71051EC4A003E3539 /* archive_file.cpp */; }; + F9AA65DE1051EC4A003E3539 /* lto_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65D91051EC4A003E3539 /* lto_file.cpp */; }; + F9AA65DF1051EC4A003E3539 /* macho_dylib_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */; }; + F9AA6786105700C2003E3539 /* opaque_section_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA6784105700C2003E3539 /* opaque_section_file.cpp */; }; + F9AA67B610570C41003E3539 /* dtrace_dof.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA67B510570C41003E3539 /* dtrace_dof.cpp */; }; + F9AA687C10572E27003E3539 /* InputFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA687A10572E27003E3539 /* InputFiles.cpp */; }; + F9AA69B610583C0C003E3539 /* SymbolTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA69B410583C0C003E3539 /* SymbolTable.cpp */; }; + F9AA69C110583E19003E3539 /* Resolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA69BF10583E19003E3539 /* Resolver.cpp */; }; + F9AA6FF910618CD2003E3539 /* macho_relocatable_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */; }; + F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AE20FD1107D1440007ED5D /* dylibs.cpp */; }; + F9AE23291109015E0007ED5D /* lto_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65D91051EC4A003E3539 /* lto_file.cpp */; }; F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; F9B670120DDA17E800E6D0DA /* UnwindDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */; }; F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B813810EC2653000F94C13 /* unwinddump.1 */; }; F9BA51650ECE58C800D1D62E /* dyldinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */; }; + F9BA955E10A233000097A440 /* huge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA955C10A233000097A440 /* huge.cpp */; }; F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */; }; F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; @@ -61,7 +84,7 @@ /* Begin PBXBuildRule section */ F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; + compilerSpec = com.apple.compilers.llvm.clang.1_0; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -69,7 +92,7 @@ }; F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; + compilerSpec = com.apple.compilers.llvm.clang.1_0; fileType = sourcecode.cpp; isEditable = 1; outputFiles = ( @@ -148,6 +171,13 @@ remoteGlobalIDString = F9EA72CA097454A6008B4F1D; remoteInfo = machocheck; }; + F9F9AD67116D58AF0028EFAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9BA51600ECE58BE00D1D62E; + remoteInfo = dyldinfo; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -208,26 +238,63 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/ld/LTOReader.hpp; sourceTree = ""; }; C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ld/ExecutableFile.h; sourceTree = ""; }; F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; }; - F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ld/ObjectFile.h; sourceTree = ""; }; + F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; }; F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; }; F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; }; - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/ld/MachOReaderDylib.hpp; sourceTree = ""; }; - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/ld/MachOReaderRelocatable.hpp; sourceTree = ""; }; - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/ld/MachOWriterExecutable.hpp; sourceTree = ""; }; + F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = ""; }; + F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = ""; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = ""; }; F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; - F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/ld/OpaqueSection.hpp; sourceTree = ""; }; - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ld/ArchiveReader.hpp; sourceTree = ""; }; + F984963310AB9318009E9878 /* stub_ppc_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_ppc_classic.hpp; sourceTree = ""; }; + F9849E3410B38EF5009E9878 /* order_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = order_file.cpp; sourceTree = ""; }; + F9849E3510B38EF5009E9878 /* order_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = order_file.h; sourceTree = ""; }; + F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm_classic.hpp; sourceTree = ""; }; + F984A38010BB4B0D009E9878 /* branch_island.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_island.cpp; sourceTree = ""; }; + F984A38110BB4B0D009E9878 /* branch_island.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_island.h; sourceTree = ""; }; + F989D0391062E6350014B60C /* stub_x86_64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64.hpp; sourceTree = ""; }; + F989D30B106826020014B60C /* OutputFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OutputFile.cpp; path = src/ld/OutputFile.cpp; sourceTree = ""; }; + F989D30C106826020014B60C /* OutputFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OutputFile.h; path = src/ld/OutputFile.h; sourceTree = ""; }; + F989D3AA10684F5B0014B60C /* LinkEdit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEdit.hpp; path = src/ld/LinkEdit.hpp; sourceTree = ""; }; + F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEditClassic.hpp; path = src/ld/LinkEditClassic.hpp; sourceTree = ""; }; + F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = HeaderAndLoadCommands.hpp; path = src/ld/HeaderAndLoadCommands.hpp; sourceTree = ""; }; F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprunetrie.a; sourceTree = BUILT_PRODUCTS_DIR; }; F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PruneTrie.cpp; path = src/other/PruneTrie.cpp; sourceTree = ""; }; F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prune_trie.h; path = src/other/prune_trie.h; sourceTree = ""; }; + F9A4DB8F10F816FF00BD8423 /* objc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = objc.cpp; sourceTree = ""; }; + F9A4DB9010F816FF00BD8423 /* objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = objc.h; sourceTree = ""; }; + F9AA44DA1294885F00CB8390 /* branch_shim.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_shim.cpp; sourceTree = ""; }; + F9AA44DB1294885F00CB8390 /* branch_shim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_shim.h; sourceTree = ""; }; + F9AA5FCC103F5CD1003E3539 /* ld.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ld.hpp; path = src/ld/ld.hpp; sourceTree = ""; }; + F9AA650D1051BD2B003E3539 /* make_stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = make_stubs.h; sourceTree = ""; }; + F9AA650F1051BD2B003E3539 /* stub_arm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm.hpp; sourceTree = ""; }; + F9AA65101051BD2B003E3539 /* stubs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stubs.cpp; sourceTree = ""; }; + F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_relocatable_file.cpp; sourceTree = ""; }; + F9AA65881051E750003E3539 /* macho_relocatable_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_relocatable_file.h; sourceTree = ""; }; + F9AA65D71051EC4A003E3539 /* archive_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = archive_file.cpp; sourceTree = ""; }; + F9AA65D81051EC4A003E3539 /* archive_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_file.h; sourceTree = ""; }; + F9AA65D91051EC4A003E3539 /* lto_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lto_file.cpp; sourceTree = ""; }; + F9AA65DA1051EC4A003E3539 /* lto_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lto_file.h; sourceTree = ""; }; + F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_dylib_file.cpp; sourceTree = ""; }; + F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_dylib_file.h; sourceTree = ""; }; + F9AA6784105700C2003E3539 /* opaque_section_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_section_file.cpp; sourceTree = ""; }; + F9AA6785105700C2003E3539 /* opaque_section_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opaque_section_file.h; sourceTree = ""; }; + F9AA67B410570C41003E3539 /* dtrace_dof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dtrace_dof.h; sourceTree = ""; }; + F9AA67B510570C41003E3539 /* dtrace_dof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtrace_dof.cpp; sourceTree = ""; }; + F9AA687A10572E27003E3539 /* InputFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InputFiles.cpp; path = src/ld/InputFiles.cpp; sourceTree = ""; }; + F9AA687B10572E27003E3539 /* InputFiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InputFiles.h; path = src/ld/InputFiles.h; sourceTree = ""; }; + F9AA69B410583C0C003E3539 /* SymbolTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolTable.cpp; path = src/ld/SymbolTable.cpp; sourceTree = ""; }; + F9AA69B510583C0C003E3539 /* SymbolTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolTable.h; path = src/ld/SymbolTable.h; sourceTree = ""; }; + F9AA69BF10583E19003E3539 /* Resolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resolver.cpp; path = src/ld/Resolver.cpp; sourceTree = ""; }; + F9AA69C010583E19003E3539 /* Resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Resolver.h; path = src/ld/Resolver.h; sourceTree = ""; }; + F9AB1063107D380700E54C9E /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = got.cpp; sourceTree = ""; }; + F9AB1064107D380700E54C9E /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = got.h; sourceTree = ""; }; + F9AE20FD1107D1440007ED5D /* dylibs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dylibs.cpp; sourceTree = ""; }; + F9AE20FE1107D1440007ED5D /* dylibs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dylibs.h; sourceTree = ""; }; F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; F9B670080DDA176100E6D0DA /* unwinddump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unwinddump; sourceTree = BUILT_PRODUCTS_DIR; }; F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindDump.cpp; path = src/other/unwinddump.cpp; sourceTree = ""; }; @@ -235,6 +302,12 @@ F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOTrie.hpp; path = src/abstraction/MachOTrie.hpp; sourceTree = ""; }; F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = src/other/dyldinfo.cpp; sourceTree = ""; }; F9BA51610ECE58BE00D1D62E /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; }; + F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_classic.hpp; sourceTree = ""; }; + F9BA8A7F1096150F0097A440 /* stub_x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86.hpp; sourceTree = ""; }; + F9BA955C10A233000097A440 /* huge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = huge.cpp; sourceTree = ""; }; + F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = ""; }; + F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = ""; }; + F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = ""; }; F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; }; F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; }; @@ -318,6 +391,69 @@ name = Products; sourceTree = ""; }; + F9AA650B1051BD2B003E3539 /* passes */ = { + isa = PBXGroup; + children = ( + F984A38010BB4B0D009E9878 /* branch_island.cpp */, + F984A38110BB4B0D009E9878 /* branch_island.h */, + F9AA44DA1294885F00CB8390 /* branch_shim.cpp */, + F9AA44DB1294885F00CB8390 /* branch_shim.h */, + F9849E3410B38EF5009E9878 /* order_file.cpp */, + F9849E3510B38EF5009E9878 /* order_file.h */, + F9BA963310A2545C0097A440 /* compact_unwind.cpp */, + F9BA963410A2545C0097A440 /* compact_unwind.h */, + F9AA67B410570C41003E3539 /* dtrace_dof.h */, + F9AA67B510570C41003E3539 /* dtrace_dof.cpp */, + F9BA955C10A233000097A440 /* huge.cpp */, + F9BA955D10A233000097A440 /* huge.h */, + F9AB1063107D380700E54C9E /* got.cpp */, + F9AB1064107D380700E54C9E /* got.h */, + F93CB246116E69EB003233B8 /* tlvp.cpp */, + F93CB247116E69EB003233B8 /* tlvp.h */, + F9AE20FD1107D1440007ED5D /* dylibs.cpp */, + F9AE20FE1107D1440007ED5D /* dylibs.h */, + F9A4DB8F10F816FF00BD8423 /* objc.cpp */, + F9A4DB9010F816FF00BD8423 /* objc.h */, + F9AA650C1051BD2B003E3539 /* stubs */, + ); + name = passes; + path = src/ld/passes; + sourceTree = ""; + }; + F9AA650C1051BD2B003E3539 /* stubs */ = { + isa = PBXGroup; + children = ( + F9AA650D1051BD2B003E3539 /* make_stubs.h */, + F9AA65101051BD2B003E3539 /* stubs.cpp */, + F9AA650F1051BD2B003E3539 /* stub_arm.hpp */, + F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */, + F9BA8A7F1096150F0097A440 /* stub_x86.hpp */, + F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */, + F989D0391062E6350014B60C /* stub_x86_64.hpp */, + F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */, + F984963310AB9318009E9878 /* stub_ppc_classic.hpp */, + ); + path = stubs; + sourceTree = ""; + }; + F9AA65861051E750003E3539 /* parsers */ = { + isa = PBXGroup; + children = ( + F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, + F9AA6785105700C2003E3539 /* opaque_section_file.h */, + F9AA65D71051EC4A003E3539 /* archive_file.cpp */, + F9AA65D81051EC4A003E3539 /* archive_file.h */, + F9AA65D91051EC4A003E3539 /* lto_file.cpp */, + F9AA65DA1051EC4A003E3539 /* lto_file.h */, + F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */, + F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */, + F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */, + F9AA65881051E750003E3539 /* macho_relocatable_file.h */, + ); + name = parsers; + path = src/ld/parsers; + sourceTree = ""; + }; F9B8137E0EC2651200F94C13 /* doc */ = { isa = PBXGroup; children = ( @@ -342,18 +478,24 @@ F9B813AD0EC27B8500F94C13 /* ld */ = { isa = PBXGroup; children = ( + F9AA69BF10583E19003E3539 /* Resolver.cpp */, + F9AA69C010583E19003E3539 /* Resolver.h */, + F9AA69B410583C0C003E3539 /* SymbolTable.cpp */, + F9AA69B510583C0C003E3539 /* SymbolTable.h */, + F9AA687A10572E27003E3539 /* InputFiles.cpp */, + F9AA687B10572E27003E3539 /* InputFiles.h */, + F9AA5FCC103F5CD1003E3539 /* ld.hpp */, F9023C3F06D5A254001BBF46 /* ld.cpp */, - F933DC37092A82480083EAC8 /* Architectures.hpp */, - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */, - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */, - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, - F9023C4106D5A254001BBF46 /* ObjectFile.h */, - F98D26850AA779BD00416316 /* OpaqueSection.hpp */, F9C0D48A06DD1E1B001C7193 /* Options.cpp */, F9C0D48B06DD1E1B001C7193 /* Options.h */, + F989D30B106826020014B60C /* OutputFile.cpp */, + F989D30C106826020014B60C /* OutputFile.h */, + F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */, + F989D3AA10684F5B0014B60C /* LinkEdit.hpp */, + F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */, + F9AA650B1051BD2B003E3539 /* passes */, + F9AA65861051E750003E3539 /* parsers */, + F933DC37092A82480083EAC8 /* Architectures.hpp */, F9EA7582097882F3008B4F1D /* debugline.c */, F9EA7583097882F3008B4F1D /* debugline.h */, ); @@ -381,7 +523,7 @@ isa = PBXNativeTarget; buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; buildPhases = ( - 0B12F6A50CE39466008ABCAE /* build configure.h */, + F9E8DB4D11921594007B4D6A /* make config.h */, F9023C3606D5A23E001BBF46 /* Sources */, F9023C3706D5A23E001BBF46 /* Frameworks */, F97F5025070D0B6300B9FCD7 /* copy man page */, @@ -501,9 +643,19 @@ /* Begin PBXProject section */ F9023C3006D5A227001BBF46 /* Project object */ = { isa = PBXProject; + attributes = { + ORGANIZATIONNAME = "Apple Inc."; + }; buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 0; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = F9023C2C06D5A227001BBF46; productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; projectDirPath = ""; @@ -523,34 +675,34 @@ /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - 0B12F6A50CE39466008ABCAE /* build configure.h */ = { + F96D5367094A2754008E9EE8 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "build configure.h"; outputPaths = ( - "$(DERIVED_FILE_DIR)/configure.h", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib\" > ${DERIVED_FILE_DIR}/linker_opts\nelse\n\techo \"#undef LTO_SUPPORT\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"\" > ${DERIVED_FILE_DIR}/linker_opts\nfi\n"; + 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"; showEnvVarsInLog = 0; }; - F96D5367094A2754008E9EE8 /* ShellScript */ = { + F9E8DB4D11921594007B4D6A /* make config.h */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "make config.h"; outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", ); 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"; + shellPath = /bin/sh; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/configure.h\n\nif [ -n \"${IPHONEOS_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_IPHONEOS_MIN_VERSION \\\"${IPHONEOS_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\nelse\n if [ -n \"${MACOSX_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_MACOSX_MIN_VERSION \\\"${MACOSX_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\n fi\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -560,9 +712,29 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, + F9AA65891051E750003E3539 /* macho_relocatable_file.cpp in Sources */, + F9AA65DD1051EC4A003E3539 /* archive_file.cpp in Sources */, + F9AA65DE1051EC4A003E3539 /* lto_file.cpp in Sources */, + F9AA65DF1051EC4A003E3539 /* macho_dylib_file.cpp in Sources */, F9EA7584097882F3008B4F1D /* debugline.c in Sources */, + F9AA687C10572E27003E3539 /* InputFiles.cpp in Sources */, + F9AA69B610583C0C003E3539 /* SymbolTable.cpp in Sources */, + F9AA69C110583E19003E3539 /* Resolver.cpp in Sources */, + F989D30D106826020014B60C /* OutputFile.cpp in Sources */, + F9AA65111051BD2B003E3539 /* stubs.cpp in Sources */, + F9AA6786105700C2003E3539 /* opaque_section_file.cpp in Sources */, + F9AA67B610570C41003E3539 /* dtrace_dof.cpp in Sources */, + F98498A310AE2159009E9878 /* compact_unwind.cpp in Sources */, + F98498A410AE2159009E9878 /* got.cpp in Sources */, + F9BA955E10A233000097A440 /* huge.cpp in Sources */, + F9849E3610B38EF5009E9878 /* order_file.cpp in Sources */, + F984A38210BB4B0D009E9878 /* branch_island.cpp in Sources */, + F9A4DB9110F816FF00BD8423 /* objc.cpp in Sources */, + F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */, + F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */, + F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -570,6 +742,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F9AA6FF910618CD2003E3539 /* macho_relocatable_file.cpp in Sources */, + F9AE23291109015E0007ED5D /* lto_file.cpp in Sources */, F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */, F9EA75BC09788857008B4F1D /* debugline.c in Sources */, ); @@ -668,12 +842,19 @@ target = F9EA72CA097454A6008B4F1D /* machocheck */; targetProxy = F9EA73960974999B008B4F1D /* PBXContainerItemProxy */; }; + F9F9AD68116D58AF0028EFAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9BA51600ECE58BE00D1D62E /* dyldinfo */; + targetProxy = F9F9AD67116D58AF0028EFAB /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = NO; @@ -682,6 +863,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -696,7 +878,7 @@ GCC_WARN_MISSING_PARENTHESES = YES; GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; GCC_WARN_PEDANTIC = NO; - GCC_WARN_SHADOW = NO; + GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = NO; @@ -714,7 +896,7 @@ LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "@$(DERIVED_FILE_DIR)/linker_opts"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; PREBINDING = NO; PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; @@ -726,14 +908,20 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 3; - GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", + NDEBUG, + "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", + ); + GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'"; GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -765,8 +953,7 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ( - "-Wl,-no_pie", - "@$(DERIVED_FILE_DIR)/linker_opts", + "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -783,12 +970,25 @@ F933D92009291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include"; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + ); INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; OTHER_REZFLAGS = ""; @@ -811,7 +1011,11 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = s; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include"; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + ); INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; OTHER_REZFLAGS = ""; @@ -859,6 +1063,205 @@ }; name = Release; }; + F9849FF810B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = "Release-assert"; + }; + F9849FF910B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = all; + ZERO_LINK = NO; + }; + name = "Release-assert"; + }; + F9849FFA10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", + "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", + ); + GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DEVELOPER_DIR)/usr/local/include", + "$(DEVELOPER_DIR)/usr/include", + ); + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = ( + "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib", + "-Wl,-exported_symbol,__mh_execute_header", + ); + PREBINDING = NO; + PRODUCT_NAME = ld; + SECTORDER_FLAGS = ""; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "x86_64 i386 ppc"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = "Release-assert"; + }; + F9849FFB10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = rebase; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "i386 ppc x86_64"; + }; + name = "Release-assert"; + }; + F9849FFC10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = unwinddump; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + }; + name = "Release-assert"; + }; + F9849FFD10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + ); + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = "Release-assert"; + }; + F9849FFE10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = "Release-assert"; + }; + F9849FFF10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = dyldinfo; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + ZERO_LINK = NO; + }; + name = "Release-assert"; + }; + F984A00010B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = prunetrie; + }; + name = "Release-assert"; + }; + F984A00110B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = "unit-tests"; + }; + name = "Release-assert"; + }; F9A3DDCB0ED762B800C590B9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1059,90 +1462,100 @@ buildConfigurations = ( F933D91C09291AC90083EAC8 /* Debug */, F933D91D09291AC90083EAC8 /* Release */, + F9849FFA10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */ = { isa = XCConfigurationList; buildConfigurations = ( F933D92009291AC90083EAC8 /* Debug */, F933D92109291AC90083EAC8 /* Release */, + F9849FFD10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */ = { isa = XCConfigurationList; buildConfigurations = ( F933D92409291AC90083EAC8 /* Debug */, F933D92509291AC90083EAC8 /* Release */, + F9849FF810B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */ = { isa = XCConfigurationList; buildConfigurations = ( F96D536E094A2773008E9EE8 /* Debug */, F96D536F094A2773008E9EE8 /* Release */, + F984A00110B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */ = { isa = XCConfigurationList; buildConfigurations = ( F9A3DDCB0ED762B800C590B9 /* Debug */, F9A3DDCC0ED762B800C590B9 /* Release */, + F984A00010B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { isa = XCConfigurationList; buildConfigurations = ( F9B1A26D0A3A568700DA8FAB /* Debug */, F9B1A26E0A3A568700DA8FAB /* Release */, + F9849FF910B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */ = { isa = XCConfigurationList; buildConfigurations = ( F9B670060DDA176100E6D0DA /* Debug */, F9B670070DDA176100E6D0DA /* Release */, + F9849FFC10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */ = { isa = XCConfigurationList; buildConfigurations = ( F9BA51630ECE58BF00D1D62E /* Debug */, F9BA51640ECE58BF00D1D62E /* Release */, + F9849FFF10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { isa = XCConfigurationList; buildConfigurations = ( F9EA72D0097454D5008B4F1D /* Debug */, F9EA72D1097454D5008B4F1D /* Release */, + F9849FFE10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */ = { isa = XCConfigurationList; buildConfigurations = ( F9EC77F10A2F8616002A3E39 /* Debug */, F9EC77F20A2F8616002A3E39 /* Release */, + F9849FFB10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; /* End XCConfigurationList section */ }; diff --git a/ld64/src/abstraction/FileAbstraction.hpp b/ld64/src/abstraction/FileAbstraction.hpp index 1f7a629..8517e87 100644 --- a/ld64/src/abstraction/FileAbstraction.hpp +++ b/ld64/src/abstraction/FileAbstraction.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -118,6 +118,7 @@ class Pointer32 { public: typedef uint32_t uint_t; + typedef int32_t sint_t; typedef _E E; static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } @@ -130,6 +131,7 @@ class Pointer64 { public: typedef uint64_t uint_t; + typedef int64_t sint_t; typedef _E E; static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 50982a1..a9a69a7 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,6 +40,10 @@ // stuff that will eventually go away once newer cctools headers are widespread +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + #ifndef CPU_SUBTYPE_ARM_V5TEJ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) #endif @@ -132,6 +136,79 @@ #endif +#ifndef S_THREAD_LOCAL_REGULAR + #define S_THREAD_LOCAL_REGULAR 0x11 +#endif + +#ifndef S_THREAD_LOCAL_ZEROFILL + #define S_THREAD_LOCAL_ZEROFILL 0x12 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLES + #define S_THREAD_LOCAL_VARIABLES 0x13 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLE_POINTERS + #define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#endif + +#ifndef S_THREAD_LOCAL_INIT_FUNCTION_POINTERS + #define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 +#endif + +#ifndef MH_HAS_TLV_DESCRIPTORS + #define MH_HAS_TLV_DESCRIPTORS 0x800000 +#endif + +#ifndef X86_64_RELOC_TLV + #define X86_64_RELOC_TLV 9 +#endif + +#define GENERIC_RLEOC_TLV 5 + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + +// type internal to linker +#define BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB 0 + +#ifndef LC_VERSION_MIN_MACOSX + #define LC_VERSION_MIN_MACOSX 0x24 + #define LC_VERSION_MIN_IPHONEOS 0x25 + + struct version_min_command { + uint32_t cmd; /* LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS */ + uint32_t cmdsize; /* sizeof(struct min_version_command) */ + uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t reserved; /* zero */ + }; +#endif + +#ifndef N_SYMBOL_RESOLVER + #define N_SYMBOL_RESOLVER 0x100 +#endif + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif + +#ifndef MH_NO_HEAP_EXECUTION + #define MH_NO_HEAP_EXECUTION 0x1000000 +#endif + +#ifndef LC_DYLD_ENVIRONMENT + #define LC_DYLD_ENVIRONMENT 0x27 +#endif + +// hack until newer everywhere +#define ARM_RELOC_HALF 8 +#define ARM_RELOC_HALF_SECTDIFF 9 + // // This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness @@ -471,7 +548,7 @@ class macho_uuid_command { void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(uint8_t u[16]) INLINE { memcpy(&fields.uuid, u, 16); } + void set_uuid(const uint8_t u[16]) INLINE { memcpy(&fields.uuid, u, 16); } typedef typename P::E E; private: @@ -1159,6 +1236,29 @@ class macho_dyld_info_command { }; +// +// mach-o version load command +// +template +class macho_version_min_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t version() const INLINE { return fields.version; } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t reserved() const INLINE { return fields.reserved; } + void set_reserved(uint32_t value) INLINE { E::set32(fields.reserved, value); } + + typedef typename P::E E; +private: + version_min_command fields; +}; + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp index 43284c4..7b30cad 100644 --- a/ld64/src/abstraction/MachOTrie.hpp +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,9 +25,11 @@ #define __MACH_O_TRIE__ #include +#include #include "MachOFileAbstraction.hpp" + namespace mach_o { namespace trie { @@ -42,25 +44,28 @@ struct Edge struct Node { - Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), + fOther(0), fImportedName(NULL), fOrdered(false), fHaveExportInfo(false), fTrieOffset(0) {} ~Node() { } const char* fCummulativeString; std::vector fChildren; uint64_t fAddress; - uint32_t fFlags; + uint64_t fFlags; + uint64_t fOther; + const char* fImportedName; bool fOrdered; bool fHaveExportInfo; uint32_t fTrieOffset; - void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { + void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) { const char* partialStr = &fullStr[strlen(fCummulativeString)]; for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { Edge& e = *it; int subStringLen = strlen(e.fSubString); if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { // already have matching edge, go down that path - e.fChild->addSymbol(fullStr, address, flags); + e.fChild->addSymbol(fullStr, address, flags, other, importName); return; } else { @@ -81,18 +86,30 @@ struct Node abEdge.fChild = bNode; Edge bcEdge(bcEdgeStr, cNode); bNode->fChildren.push_back(bcEdge); - bNode->addSymbol(fullStr, address, flags); + bNode->addSymbol(fullStr, address, flags, other, importName); return; } } } } + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + assert(importName != NULL); + assert(other != 0); + } + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + assert(other != 0); + } // no commonality with any existing child, make a new edge that is this whole string Node* newNode = new Node(strdup(fullStr)); Edge newEdge(strdup(partialStr), newNode); fChildren.push_back(newEdge); newNode->fAddress = address; newNode->fFlags = flags; + newNode->fOther = other; + if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) ) + newNode->fImportedName = importName; + else + newNode->fImportedName = NULL; newNode->fHaveExportInfo = true; } @@ -115,14 +132,26 @@ struct Node } // byte for terminal node size in bytes, or 0x00 if not terminal node - // teminal node (uleb128 flags, uleb128 addr) + // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) // byte for child node count // each child: zero terminated substring, uleb128 node offset bool updateOffset(uint32_t& offset) { - uint32_t nodeSize = 1; // byte for length of export info - if ( fHaveExportInfo ) - nodeSize += uleb128_size(fFlags) + uleb128_size(fAddress); - + uint32_t nodeSize = 1; // length of export info when no export info + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal + if ( fImportedName != NULL ) + nodeSize += strlen(fImportedName); + ++nodeSize; // trailing zero in imported name + } + else { + nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + nodeSize += uleb128_size(fOther); + } + // do have export info, overall node size so far is uleb128 of export info + export info + nodeSize += uleb128_size(nodeSize); + } // add children ++nodeSize; // byte for count of chidren for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { @@ -139,13 +168,42 @@ struct Node void appendToStream(std::vector& out) { if ( fHaveExportInfo ) { - // nodes with export info: size, flags, address - out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress)); - append_uleb128(fFlags, out); - append_uleb128(fAddress, out); + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( fImportedName != NULL ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1; + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + append_string(fImportedName, out); + } + else { + // nodes with re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1; + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + out.push_back(0); + } + } + else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // nodes with export info: size, flags, address, other + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + append_uleb128(fOther, out); + } + else { + // nodes with export info: size, flags, address + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + } } else { - // no export info + // no export info uleb128 of zero is one byte of zero out.push_back(0); } // write number of children @@ -216,22 +274,25 @@ struct Entry const char* name; uint64_t address; uint64_t flags; + uint64_t other; + const char* importName; }; -inline void makeTrie(const std::vector& input, std::vector& output) + +inline void makeTrie(const std::vector& entries, std::vector& output) { Node start(strdup("")); // make nodes for all exported symbols - for (std::vector::const_iterator it = input.begin(); it != input.end(); ++it) { - start.addSymbol(it->name, it->address, it->flags); + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addSymbol(it->name, it->address, it->flags, it->other, it->importName); } // create vector of nodes std::vector orderedNodes; - orderedNodes.reserve(input.size()*2); - for (std::vector::const_iterator it = input.begin(); it != input.end(); ++it) { + orderedNodes.reserve(entries.size()*2); + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { start.addOrderedNodes(it->name, orderedNodes); } @@ -263,18 +324,31 @@ struct EntryWithOffset static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, - char* cummulativeString, int curStrOffset, std::vector& output) + char* cummulativeString, int curStrOffset, + std::vector& output) { if ( p >= end ) throw "malformed trie, node past end"; - const uint8_t terminalSize = *p++; + const uint8_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { EntryWithOffset e; e.nodeOffset = p-start; e.entry.name = strdup(cummulativeString); e.entry.flags = read_uleb128(p, end); - e.entry.address = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + e.entry.address = 0; + e.entry.other = read_uleb128(p, end); // dylib ordinal + e.entry.importName = (char*)p; + } + else { + e.entry.address = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + e.entry.other = read_uleb128(p, end); + else + e.entry.other = 0; + e.entry.importName = NULL; + } output.push_back(e); } const uint8_t childrenCount = *children++; @@ -293,8 +367,8 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) -{ - // empty tree has no entries +{ + // empty trie has no entries if ( start == end ) return; char cummulativeString[4000]; diff --git a/ld64/src/ld/Architectures.hpp b/ld64/src/ld/Architectures.hpp index 2a449f1..1145550 100644 --- a/ld64/src/ld/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -34,58 +34,26 @@ struct ppc { typedef Pointer32 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, - kPointerDiff16, kPointerDiff32, kPointerDiff=kPointerDiff32, kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct ppc64 { typedef Pointer64 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, - kPointerDiff16, kPointerDiff32, kPointerDiff64, kPointerDiff=kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct x86 { typedef Pointer32 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kPointerDiff16, - kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, - kImageOffset32, kPointerDiff24, kSectionOffset24, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct x86_64 { typedef Pointer64 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointer32, kPointerWeakImport, kPointerDiff, kPointerDiff32, - kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, - kBranchPCRel32, kBranchPCRel32WeakImport, - kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, - kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, kGOTNoFixUp, - kImageOffset32, kPointerDiff24, kSectionOffset24, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct arm { typedef Pointer32 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, - kPointerDiff32=kPointerDiff, kReadOnlyPointer, kPointerDiff12, - kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; #endif // __ARCHITECTURES__ diff --git a/ld64/src/ld/ArchiveReader.hpp b/ld64/src/ld/ArchiveReader.hpp deleted file mode 100644 index b8f1a5c..0000000 --- a/ld64/src/ld/ArchiveReader.hpp +++ /dev/null @@ -1,467 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_ARCHIVE__ -#define __OBJECT_FILE_ARCHIVE__ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "ObjectFile.h" -#include "MachOReaderRelocatable.hpp" -#if LTO_SUPPORT - #include "LTOReader.hpp" -#endif - -namespace archive { - -typedef const struct ranlib* ConstRanLibPtr; - -template -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength); - Reader(const uint8_t fileContent[], uint64_t fileLength, - const char* path, time_t modTime, - const LibraryOptions& archiveOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime(){ return fModTime; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabs() { return NULL; } - virtual bool optimize(const std::vector&, std::vector&, - std::vector&, const std::set&, - std::vector& newDeadAtoms, - uint32_t, ObjectFile::Reader* writer, - ObjectFile::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs); - -private: - static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength); - static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength); - static cpu_type_t architecture(); - - - class Entry : ar_hdr - { - public: - const char* getName() const; - time_t getModTime() const; - const uint8_t* getContent() const; - uint32_t getContentSize() const; - const Entry* getNext() const; - private: - bool hasLongName() const; - unsigned int getLongNameSpace() const; - - }; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; - - typedef typename A::P P; - typedef typename A::P::E E; - - const struct ranlib* ranlibHashSearch(const char* name); - ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); - void dumpTableOfContents(); - void buildHashTable(); - - const char* fPath; - time_t fModTime; - const ObjectFile::ReaderOptions& fOptions; - uint32_t fOrdinalBase; - const uint8_t* fFileContent; - uint64_t fFileLength; - const struct ranlib* fTableOfContents; - uint32_t fTableOfContentCount; - const char* fStringPool; - std::vector fAllAtoms; - std::vector fInstantiatedReaders; - std::set fInstantiatedEntries; - std::set fPossibleEntries; - NameToEntryMap fHashTable; - bool fForceLoad; - - static std::vector fgEmptyList; -}; - -template -std::vector Reader::fgEmptyList; - - -template -bool Reader::Entry::hasLongName() const -{ - return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); -} - -template -unsigned int Reader::Entry::getLongNameSpace() const -{ - char* endptr; - long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); - return result; -} - -template -const char* Reader::Entry::getName() const -{ - if ( this->hasLongName() ) { - int len = this->getLongNameSpace(); - static char longName[256]; - strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); - longName[len] = '\0'; - return longName; - } - else { - static char shortName[20]; - strncpy(shortName, this->ar_name, 16); - shortName[16] = '\0'; - char* space = strchr(shortName, ' '); - if ( space != NULL ) - *space = '\0'; - return shortName; - } -} - -template -time_t Reader::Entry::getModTime() const -{ - char temp[14]; - strncpy(temp, this->ar_date, 12); - temp[12] = '\0'; - char* endptr; - return (time_t)strtol(temp, &endptr, 10); -} - - -template -const uint8_t* Reader::Entry::getContent() const -{ - if ( this->hasLongName() ) - return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); - else - return ((uint8_t*)this) + sizeof(ar_hdr); -} - - -template -uint32_t Reader::Entry::getContentSize() const -{ - char temp[12]; - strncpy(temp, this->ar_size, 10); - temp[10] = '\0'; - char* endptr; - long size = strtol(temp, &endptr, 10); - // long name is included in ar_size - if ( this->hasLongName() ) - size -= this->getLongNameSpace(); - return size; -} - - -template -const class Reader::Entry* Reader::Entry::getNext() const -{ - const uint8_t* p = this->getContent() + getContentSize(); - p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align - return (class Reader::Entry*)p; -} - - -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_POWERPC64; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_I386; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_X86_64; } -template <> cpu_type_t Reader::architecture() { return CPU_TYPE_ARM; } - - -template -bool Reader::validMachOFile(const uint8_t* fileContent, uint64_t fileLength) -{ - return mach_o::relocatable::Reader::validFile(fileContent); -} - -template -bool Reader::validLTOFile(const uint8_t* fileContent, uint64_t fileLength) -{ -#if LTO_SUPPORT - return lto::Reader::validFile(fileContent, fileLength, architecture()); -#else - return false; -#endif -} - - - -template -bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength) -{ - // must have valid archive header - if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) - return false; - - // peak at first .o file and verify it is correct architecture - const Entry* const start = (Entry*)&fileContent[8]; - const Entry* const end = (Entry*)&fileContent[fileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - // skip option table-of-content member - if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) - continue; - // archive is valid if first .o file is valid - return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize())); - } - // empty archive - return true; -} - -template -Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, - const LibraryOptions& archiveOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL), - fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL), fForceLoad(archiveOptions.fForceLoad) -{ - fPath = strdup(path); - fFileContent = fileContent; - fFileLength = fileLength; - - if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) - throw "not an archive"; - - // write out path for -whatsloaded option - if ( options.fLogAllFiles ) - printf("%s\n", path); - - if ( !options.fFullyLoadArchives && !fForceLoad ) { - const Entry* const firstMember = (Entry*)&fFileContent[8]; - if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { - const uint8_t* contents = firstMember->getContent(); - uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); - fTableOfContents = (const struct ranlib*)&contents[4]; - fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); - fStringPool = (const char*)&contents[ranlibArrayLen+8]; - if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) - || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) - throw "malformed archive, perhaps wrong architecture"; - this->buildHashTable(); - } - else - throw "archive has no table of contents"; - } -} - - -template -ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) -{ - const char* memberName = member->getName(); - char memberPath[strlen(fPath) + strlen(memberName)+4]; - strcpy(memberPath, fPath); - strcat(memberPath, "("); - strcat(memberPath, memberName); - strcat(memberPath, ")"); - //fprintf(stderr, "using %s from %s\n", memberName, fPath); - try { - // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive - uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent; - if ( validMachOFile(member->getContent(), member->getContentSize()) ) { - return new typename mach_o::relocatable::Reader::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase); - } -#if LTO_SUPPORT - else if ( validLTOFile(member->getContent(), member->getContentSize()) ) { - return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture()); - } -#endif - throwf("archive member '%s' with length %d is not mach-o or bitcode", memberName, member->getContentSize()); - } - catch (const char* msg) { - throwf("in %s, %s", memberPath, msg); - } -} - - -template -std::vector& Reader::getAtoms() -{ - if ( fOptions.fFullyLoadArchives || fForceLoad ) { - // build vector of all atoms from all .o files in this archive - const Entry* const start = (Entry*)&fFileContent[8]; - const Entry* const end = (Entry*)&fFileContent[fFileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) - continue; - if ( fOptions.fWhyLoad ) { - if ( fForceLoad ) - printf("-force_load forced load of %s(%s)\n", this->getPath(), memberName); - else - printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); - } - ObjectFile::Reader* r = this->makeObjectReaderForMember(p); - std::vector& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - fInstantiatedReaders.push_back(r); - } - return fAllAtoms; - } - else if ( fOptions.fLoadAllObjcObjectsFromArchives ) { - // build vector of all atoms from all .o files containing objc classes in this archive - for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) { - if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { - const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) - printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName()); - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - std::vector& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - fInstantiatedReaders.push_back(r); - } - } - } - return fAllAtoms; - } - else { - // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed - return fgEmptyList; - } -} - -template -bool Reader::optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, const std::set& deadAtoms, - std::vector& newDeadAtoms, - uint32_t nextOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) -{ - bool result = false; - for(std::vector::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) { - result |= (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, newDeadAtoms, nextOrdinal, - writer, entryPointAtom, llvmOptions, allGlobalsAReDeadStripRoots, okind, - verbose, saveTemps, outputFilePath, pie, allowTextRelocs); - } - return result; -} - - - -template -ConstRanLibPtr Reader::ranlibHashSearch(const char* name) -{ - class NameToEntryMap::iterator pos = fHashTable.find(name); - if ( pos != fHashTable.end() ) - return pos->second; - else - return NULL; -} - -template -void Reader::buildHashTable() -{ - // walk through list backwards, adding/overwriting entries - // this assures that with duplicates those earliest in the list will be found - for (int i = fTableOfContentCount-1; i >= 0; --i) { - const struct ranlib* entry = &fTableOfContents[i]; - const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; - const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; - //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); - fHashTable[entryName] = entry; - fPossibleEntries.insert(member); - } -} - -template -void Reader::dumpTableOfContents() -{ - for (unsigned int i=0; i < fTableOfContentCount; ++i) { - const struct ranlib* e = &fTableOfContents[i]; - printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); - } -} - -template -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - if ( fOptions.fFullyLoadArchives || fForceLoad ) { - return NULL; - } - else { - const struct ranlib* result = NULL; - // do a hash search of table of contents looking for requested symbol - result = ranlibHashSearch(name); - if ( result != NULL ) { - const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) - printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - fInstantiatedReaders.push_back(r); - return new std::vector(r->getAtoms()); - } - } - //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); - return NULL; - } -} - - - - - -}; // namespace archive - - -#endif // __OBJECT_FILE_ARCHIVE__ diff --git a/ld64/src/ld/ExecutableFile.h b/ld64/src/ld/ExecutableFile.h deleted file mode 100644 index dca22f7..0000000 --- a/ld64/src/ld/ExecutableFile.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __EXECUTABLEFILE__ -#define __EXECUTABLEFILE__ - -#include -#include - -#include "ObjectFile.h" -#include "Options.h" - - -namespace ExecutableFile { - - struct DyLibUsed - { - ObjectFile::Reader* reader; - LibraryOptions options; - }; - - class Writer : public ObjectFile::Reader - { - public: - virtual ~Writer() {}; - - virtual const char* getPath() = 0; - virtual std::vector& getAtoms() = 0; - virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; - virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses) = 0; - virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; - virtual void addSynthesizedAtoms(const std::vector& existingAtoms, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool biggerThanTwoGigs, - uint32_t dylibSymbolCount, - std::vector& newAtoms) = 0; - virtual uint64_t write(std::vector& atoms, - std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, - bool createUUID, bool canScatter, - ObjectFile::Reader::CpuConstraint cpuConstraint, - std::set& atomsThatOverrideWeak, - bool hasExternalWeakDefinitions) = 0; - - protected: - Writer(std::vector&) {}; - }; - -}; - -#endif // __EXECUTABLEFILE__ diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp new file mode 100644 index 0000000..2c97dba --- /dev/null +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -0,0 +1,1391 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __HEADER_LOAD_COMMANDS_HPP__ +#define __HEADER_LOAD_COMMANDS_HPP__ + +#include +#include +#include +#include + +#include + +#include "MachOFileAbstraction.hpp" +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + +class HeaderAndLoadCommandsAbtract : public ld::Atom +{ +public: + HeaderAndLoadCommandsAbtract(const ld::Section& sect, ld::Atom::Definition d, + ld::Atom::Combine c, ld::Atom::Scope s, ld::Atom::ContentType ct, + ld::Atom::SymbolTableInclusion i, bool dds, bool thumb, bool al, + ld::Atom::Alignment a) : ld::Atom(sect, d, c, s, ct, i, dds, thumb, al, a) { } + + virtual void setUUID(const uint8_t digest[16]) = 0; + virtual void recopyUUIDCommand() = 0; +}; + +template +class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract +{ +public: + HeaderAndLoadCommandsAtom(const Options& opts, ld::Internal& state, + OutputFile& writer); + + // overrides of ld::Atom + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "mach-o header and load commands"; } + virtual uint64_t size() const; + virtual uint64_t objectAddress() const { return _address; } + virtual void copyRawContent(uint8_t buffer[]) const; + + // overrides of HeaderAndLoadCommandsAbtract + virtual void setUUID(const uint8_t digest[16]) { memcpy(_uuid, digest, 16); } + virtual void recopyUUIDCommand(); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + unsigned int nonHiddenSectionCount() const; + unsigned int segmentCount() const; + static uint32_t alignedSize(uint32_t x); + uint32_t magic() const; + uint32_t cpuType() const; + uint32_t cpuSubType() const; + uint32_t flags() const; + uint32_t fileType() const; + uint32_t commandsCount() const; + uint32_t threadLoadCommandSize() const; + uint8_t* copySingleSegmentLoadCommand(uint8_t* p) const; + uint8_t* copySegmentLoadCommands(uint8_t* p) const; + uint8_t* copyDyldInfoLoadCommand(uint8_t* p) const; + uint8_t* copySymbolTableLoadCommand(uint8_t* p) const; + uint8_t* copyDynamicSymbolTableLoadCommand(uint8_t* p) const; + uint8_t* copyDyldLoadCommand(uint8_t* p) const; + uint8_t* copyDylibIDLoadCommand(uint8_t* p) const; + uint8_t* copyRoutinesLoadCommand(uint8_t* p) const; + uint8_t* copyUUIDLoadCommand(uint8_t* p) const; + uint8_t* copyVersionLoadCommand(uint8_t* p) const; + uint8_t* copyThreadsLoadCommand(uint8_t* p) const; + uint8_t* copyEncryptionLoadCommand(uint8_t* p) const; + uint8_t* copySplitSegInfoLoadCommand(uint8_t* p) const; + uint8_t* copyDylibLoadCommand(uint8_t* p, const ld::dylib::File*) const; + uint8_t* copyRPathLoadCommand(uint8_t* p, const char*) const; + uint8_t* copySubFrameworkLoadCommand(uint8_t* p) const; + uint8_t* copyAllowableClientLoadCommand(uint8_t* p, const char* client) const; + uint8_t* copySubLibraryLoadCommand(uint8_t* p, const char* name) const; + uint8_t* copySubUmbrellaLoadCommand(uint8_t* p, const char* name) const; + uint8_t* copyFunctionStartsLoadCommand(uint8_t* p) const; + uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; + + uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; + bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const; + + + const Options& _options; + ld::Internal& _state; + OutputFile& _writer; + pint_t _address; + bool _hasDyldInfoLoadCommand; + bool _hasDyldLoadCommand; + bool _hasDylibIDLoadCommand; + bool _hasThreadLoadCommand; + bool _hasEncryptionLoadCommand; + bool _hasSplitSegInfoLoadCommand; + bool _hasRoutinesLoadCommand; + bool _hasUUIDLoadCommand; + bool _hasSymbolTableLoadCommand; + bool _hasDynamicSymbolTableLoadCommand; + bool _hasRPathLoadCommands; + bool _hasSubFrameworkLoadCommand; + bool _hasVersionLoadCommand; + bool _hasFunctionStartsLoadCommand; + uint32_t _dylibLoadCommmandsCount; + uint32_t _allowableClientLoadCommmandsCount; + uint32_t _dyldEnvironExrasCount; + std::vector _subLibraryNames; + std::vector _subUmbrellaNames; + uint8_t _uuid[16]; + mutable macho_uuid_command

* _uuidCmdInOutputBuffer; + + static ld::Section _s_section; + static ld::Section _s_preload_section; +}; + +template +ld::Section HeaderAndLoadCommandsAtom::_s_section("__TEXT", "__mach_header", ld::Section::typeMachHeader, true); +template +ld::Section HeaderAndLoadCommandsAtom::_s_preload_section("__HEADER", "__mach_header", ld::Section::typeMachHeader, true); + + +template +HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : HeaderAndLoadCommandsAbtract((opts.outputKind() == Options::kPreload) ? _s_preload_section : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableNotIn, false, false, false, + (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(12) ), + _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL) +{ + bzero(_uuid, 16); + _hasDyldInfoLoadCommand = opts.makeCompressedDyldInfo(); + _hasDyldLoadCommand = ((opts.outputKind() == Options::kDynamicExecutable) || (_options.outputKind() == Options::kDyld)); + _hasDylibIDLoadCommand = (opts.outputKind() == Options::kDynamicLibrary); + _hasThreadLoadCommand = _hasDyldLoadCommand || (opts.outputKind() == Options::kStaticExecutable) + || (opts.outputKind() == Options::kPreload) + || (opts.outputKind() == Options::kDyld); + _hasEncryptionLoadCommand = opts.makeEncryptable(); + _hasSplitSegInfoLoadCommand = opts.sharedRegionEligible(); + _hasRoutinesLoadCommand = (opts.initFunctionName() != NULL); + _hasSymbolTableLoadCommand = true; + _hasUUIDLoadCommand = (opts.UUIDMode() != Options::kUUIDNone); + switch ( opts.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kKextBundle: + _hasDynamicSymbolTableLoadCommand = true; + break; + case Options::kObjectFile: + if ( ! state.someObjectFileHasDwarf ) + _hasUUIDLoadCommand = false; + _hasDynamicSymbolTableLoadCommand = false; + for (std::vector::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( (*it)->type() == ld::Section::typeNonLazyPointer ) { + _hasDynamicSymbolTableLoadCommand = true; + break; + } + } + break; + case Options::kStaticExecutable: + _hasDynamicSymbolTableLoadCommand = false; + break; + case Options::kPreload: + _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); + break; + } + _hasRPathLoadCommands = (_options.rpaths().size() != 0); + _hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL); + _hasVersionLoadCommand = _options.addVersionLoadCommand(); + _hasFunctionStartsLoadCommand = _options.addFunctionStarts(); + _dylibLoadCommmandsCount = _writer.dylibCount(); + _allowableClientLoadCommmandsCount = _options.allowableClients().size(); + _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); + if ( ! _options.useSimplifiedDylibReExports() ) { + // target OS does not support LC_REEXPORT_DYLIB, so use old complicated load commands + for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + const ld::dylib::File* dylib = _writer.dylibByOrdinal(ord); + if ( dylib->willBeReExported() ) { + // if child says it is an sub-framework of the image being created, then nothing to do here + bool isSubFramework = false; + const char* childInUmbrella = dylib->parentUmbrella(); + if ( childInUmbrella != NULL ) { + const char* myLeaf = strrchr(_options.installPath(), '/'); + if ( myLeaf != NULL ) { + if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) + isSubFramework = true; + } + } + // LC_SUB_FRAMEWORK is in child, so do nothing in parent + if ( ! isSubFramework ) { + // this dylib also needs a sub_x load command + bool isFrameworkReExport = false; + const char* lastSlash = strrchr(dylib->installPath(), '/'); + if ( lastSlash != NULL ) { + char frameworkName[strlen(lastSlash)+20]; + sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); + isFrameworkReExport = (strstr(dylib->installPath(), frameworkName) != NULL); + } + if ( isFrameworkReExport ) { + // needs a LC_SUB_UMBRELLA command + _subUmbrellaNames.push_back(&lastSlash[1]); + } + else { + // needs a LC_SUB_LIBRARY command + const char* nameStart = &lastSlash[1]; + if ( lastSlash == NULL ) + nameStart = dylib->installPath(); + int len = strlen(nameStart); + const char* dot = strchr(nameStart, '.'); + if ( dot != NULL ) + len = dot - nameStart; + char* subLibName = new char[len+1]; + strlcpy(subLibName, nameStart, len+1); + _subLibraryNames.push_back(subLibName); + } + } + } + } + } +} + +template +uint32_t HeaderAndLoadCommandsAtom::alignedSize(uint32_t size) +{ + if ( sizeof(pint_t) == 4 ) + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o + else + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + + +template +unsigned int HeaderAndLoadCommandsAtom::nonHiddenSectionCount() const +{ + unsigned int count = 0; + for (std::vector::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( ! (*it)->isSectionHidden() && ((*it)->type() != ld::Section::typeTentativeDefs) ) + ++count; + } + return count; +} + +template +unsigned int HeaderAndLoadCommandsAtom::segmentCount() const +{ + if ( _options.outputKind() == Options::kObjectFile ) { + // .o files have one anonymous segment that contains all sections + return 1; + } + + unsigned int count = 0; + const char* lastSegName = ""; + for (std::vector::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( _options.outputKind() == Options::kPreload ) { + if ( (*it)->type() == ld::Section::typeMachHeader ) + continue; // for -preload, don't put hidden __HEADER segment into output + if ( (*it)->type() == ld::Section::typeLinkEdit ) + continue; // for -preload, don't put hidden __LINKEDIT segment into output + } + if ( strcmp(lastSegName, (*it)->segmentName()) != 0 ) { + lastSegName = (*it)->segmentName(); + ++count; + } + } + return count; +} + + +template +uint64_t HeaderAndLoadCommandsAtom::size() const +{ + uint32_t sz = sizeof(macho_header

); + + sz += sizeof(macho_segment_command

) * this->segmentCount(); + sz += sizeof(macho_section

) * this->nonHiddenSectionCount(); + + if ( _hasDylibIDLoadCommand ) + sz += alignedSize(sizeof(macho_dylib_command

) + strlen(_options.installPath()) + 1); + + if ( _hasDyldInfoLoadCommand ) + sz += sizeof(macho_dyld_info_command

); + + if ( _hasSymbolTableLoadCommand ) + sz += sizeof(macho_symtab_command

); + + if ( _hasDynamicSymbolTableLoadCommand ) + sz += sizeof(macho_dysymtab_command

); + + if ( _hasDyldLoadCommand ) + sz += alignedSize(sizeof(macho_dylinker_command

) + strlen(_options.dyldInstallPath()) + 1); + + if ( _hasRoutinesLoadCommand ) + sz += sizeof(macho_routines_command

); + + if ( _hasUUIDLoadCommand ) + sz += sizeof(macho_uuid_command

); + + if ( _hasVersionLoadCommand ) + sz += sizeof(macho_version_min_command

); + + if ( _hasThreadLoadCommand ) + sz += this->threadLoadCommandSize(); + + if ( _hasEncryptionLoadCommand ) + sz += sizeof(macho_encryption_info_command

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

); + + for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + sz += alignedSize(sizeof(macho_dylib_command

) + strlen(_writer.dylibByOrdinal(ord)->installPath()) + 1); + } + + if ( _hasRPathLoadCommands ) { + const std::vector& rpaths = _options.rpaths(); + for (std::vector::const_iterator it = rpaths.begin(); it != rpaths.end(); ++it) { + sz += alignedSize(sizeof(macho_rpath_command

) + strlen(*it) + 1); + } + } + + if ( _hasSubFrameworkLoadCommand ) + sz += alignedSize(sizeof(macho_sub_framework_command

) + strlen(_options.umbrellaName()) + 1); + + for (std::vector::const_iterator it = _subLibraryNames.begin(); it != _subLibraryNames.end(); ++it) { + sz += alignedSize(sizeof(macho_sub_library_command

) + strlen(*it) + 1); + } + + for (std::vector::const_iterator it = _subUmbrellaNames.begin(); it != _subUmbrellaNames.end(); ++it) { + sz += alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(*it) + 1); + } + + if ( _allowableClientLoadCommmandsCount != 0 ) { + const std::vector& clients = _options.allowableClients(); + for (std::vector::const_iterator it = clients.begin(); it != clients.end(); ++it) { + sz += alignedSize(sizeof(macho_sub_client_command

) + strlen(*it) + 1); + } + } + + if ( _dyldEnvironExrasCount != 0 ) { + const std::vector& extras = _options.dyldEnvironExtras(); + for (std::vector::const_iterator it = extras.begin(); it != extras.end(); ++it) { + sz += alignedSize(sizeof(macho_dylinker_command

) + strlen(*it) + 1); + } + } + + if ( _hasFunctionStartsLoadCommand ) + sz += sizeof(macho_linkedit_data_command

); + + return sz; +} + +template +uint32_t HeaderAndLoadCommandsAtom::commandsCount() const +{ + uint32_t count = this->segmentCount(); + + if ( _hasDylibIDLoadCommand ) + ++count; + + if ( _hasDyldInfoLoadCommand ) + ++count; + + if ( _hasSymbolTableLoadCommand ) + ++count; + + if ( _hasDynamicSymbolTableLoadCommand ) + ++count; + + if ( _hasDyldLoadCommand ) + ++count; + + if ( _hasRoutinesLoadCommand ) + ++count; + + if ( _hasUUIDLoadCommand ) + ++count; + + if ( _hasVersionLoadCommand ) + ++count; + + if ( _hasThreadLoadCommand ) + ++count; + + if ( _hasEncryptionLoadCommand ) + ++count; + + if ( _hasSplitSegInfoLoadCommand ) + ++count; + + count += _dylibLoadCommmandsCount; + + count += _options.rpaths().size(); + + if ( _hasSubFrameworkLoadCommand ) + ++count; + + count += _subLibraryNames.size(); + + count += _subUmbrellaNames.size(); + + count += _allowableClientLoadCommmandsCount; + + count += _dyldEnvironExrasCount; + + if ( _hasFunctionStartsLoadCommand ) + ++count; + + return count; +} + +template +uint32_t HeaderAndLoadCommandsAtom::fileType() const +{ + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return MH_EXECUTE; + case Options::kDynamicLibrary: + return MH_DYLIB; + case Options::kDynamicBundle: + return MH_BUNDLE; + case Options::kObjectFile: + return MH_OBJECT; + case Options::kDyld: + return MH_DYLINKER; + case Options::kPreload: + return MH_PRELOAD; + case Options::kKextBundle: + return MH_KEXT_BUNDLE; + } + throw "unknonwn mach-o file type"; +} + +template +uint32_t HeaderAndLoadCommandsAtom::flags() const +{ + uint32_t bits = 0; + if ( _options.outputKind() == Options::kObjectFile ) { + if ( _state.allObjectFilesScatterable ) + bits = MH_SUBSECTIONS_VIA_SYMBOLS; + } + else { + if ( _options.outputKind() == Options::kStaticExecutable ) { + bits |= MH_NOUNDEFS; + } + else if ( _options.outputKind() == Options::kPreload ) { + bits |= MH_NOUNDEFS; + if ( _options.positionIndependentExecutable() ) + bits |= MH_PIE; + } + else { + bits = MH_DYLDLINK; + switch ( _options.nameSpace() ) { + case Options::kTwoLevelNameSpace: + bits |= MH_TWOLEVEL | MH_NOUNDEFS; + break; + case Options::kFlatNameSpace: + break; + case Options::kForceFlatNameSpace: + bits |= MH_FORCE_FLAT; + break; + } + if ( _writer.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols ) + bits |= MH_WEAK_DEFINES; + if ( _writer.usesWeakExternalSymbols || _writer.hasWeakExternalSymbols ) + bits |= MH_BINDS_TO_WEAK; + if ( _options.prebind() ) + bits |= MH_PREBOUND; + if ( _options.splitSeg() ) + bits |= MH_SPLIT_SEGS; + if ( (_options.outputKind() == Options::kDynamicLibrary) + && _writer._noReExportedDylibs + && _options.useSimplifiedDylibReExports() ) { + bits |= MH_NO_REEXPORTED_DYLIBS; + } + if ( _options.positionIndependentExecutable() && ! _writer.pieDisabled ) + bits |= MH_PIE; + if ( _options.markAutoDeadStripDylib() ) + bits |= MH_DEAD_STRIPPABLE_DYLIB; + if ( _writer.hasThreadLocalVariableDefinitions ) + bits |= MH_HAS_TLV_DESCRIPTORS; + if ( _options.hasNonExecutableHeap() ) + bits |= MH_NO_HEAP_EXECUTION; + } + if ( _options.hasExecutableStack() ) + bits |= MH_ALLOW_STACK_EXECUTION; + } + return bits; +} + +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } + +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_POWERPC; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_POWERPC64; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_I386; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_X86_64; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM; } + + +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return _state.cpuSubType; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (CPU_SUBTYPE_POWERPC_ALL | 0x80000000); + else + return CPU_SUBTYPE_POWERPC_ALL; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return CPU_SUBTYPE_I386_ALL; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (CPU_SUBTYPE_X86_64_ALL | 0x80000000); + else + return CPU_SUBTYPE_X86_64_ALL; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return _state.cpuSubType; +} + + + +template +uint8_t* HeaderAndLoadCommandsAtom::copySingleSegmentLoadCommand(uint8_t* p) const +{ + // in .o files there is just one segment load command with a blank name + // and all sections under it + macho_segment_command

* cmd = (macho_segment_command

*)p; + cmd->set_cmd(macho_segment_command

::CMD); + cmd->set_segname(""); + cmd->set_vmaddr(_options.baseAddress()); + cmd->set_vmsize(0); // updated after sections set + cmd->set_fileoff(0); // updated after sections set + cmd->set_filesize(0); // updated after sections set + cmd->set_maxprot(VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + cmd->set_initprot(VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + cmd->set_nsects(this->nonHiddenSectionCount()); + cmd->set_flags(0); + // add sections array + macho_section

* msect = (macho_section

*)&p[sizeof(macho_segment_command

)]; + for (std::vector::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* fsect = *sit; + if ( fsect->isSectionHidden() ) + continue; + if ( fsect->type() == ld::Section::typeTentativeDefs ) + continue; + msect->set_sectname(fsect->sectionName()); + msect->set_segname(fsect->segmentName()); + msect->set_addr(fsect->address); + msect->set_size(fsect->size); + msect->set_offset(fsect->fileOffset); + msect->set_align(fsect->alignment); + msect->set_reloff((fsect->relocCount == 0) ? 0 : _writer.sectionRelocationsSection->fileOffset + fsect->relocStart * sizeof(macho_relocation_info

)); + msect->set_nreloc(fsect->relocCount); + msect->set_flags(sectionFlags(fsect)); + msect->set_reserved1(fsect->indirectSymTabStartIndex); + msect->set_reserved2(fsect->indirectSymTabElementSize); + // update segment info + if ( cmd->fileoff() == 0 ) + cmd->set_fileoff(fsect->fileOffset); + cmd->set_vmsize(fsect->address + fsect->size - cmd->vmaddr()); + if ( (fsect->type() != ld::Section::typeZeroFill) && (fsect->type() != ld::Section::typeTentativeDefs) ) + cmd->set_filesize(fsect->fileOffset + fsect->size - cmd->fileoff()); + ++msect; + } + cmd->set_cmdsize(sizeof(macho_segment_command

) + cmd->nsects()*sizeof(macho_section

)); + return p + cmd->cmdsize(); +} + +struct SegInfo { + SegInfo(const char* n, const Options&); + const char* segName; + uint32_t nonHiddenSectionCount; + uint32_t maxProt; + uint32_t initProt; + std::vector sections; +}; + + +SegInfo::SegInfo(const char* n, const Options& opts) + : segName(n), nonHiddenSectionCount(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n)) +{ +} + + +template +uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* sect) const +{ + uint32_t bits; + switch ( sect->type() ) { + case ld::Section::typeUnclassified: + if ( strcmp(sect->segmentName(), "__OBJC") == 0 ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (strcmp(sect->sectionName(), "__objc_classlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (strcmp(sect->sectionName(), "__objc_catlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + 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 + return S_REGULAR; + case ld::Section::typeCode: + bits = S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + if ( sect->hasLocalRelocs && ! _writer.pieDisabled ) + bits |= S_ATTR_LOC_RELOC; + if ( sect->hasExternalRelocs ) + bits |= S_ATTR_EXT_RELOC; + return bits; + case ld::Section::typePageZero: + return S_REGULAR; + case ld::Section::typeImportProxies: + return S_REGULAR; + case ld::Section::typeLinkEdit: + return S_REGULAR; + case ld::Section::typeMachHeader: + return S_REGULAR; + case ld::Section::typeStack: + return S_REGULAR; + case ld::Section::typeLiteral4: + return S_4BYTE_LITERALS; + case ld::Section::typeLiteral8: + return S_8BYTE_LITERALS; + case ld::Section::typeLiteral16: + return S_16BYTE_LITERALS; + case ld::Section::typeConstants: + return S_REGULAR; + case ld::Section::typeTempLTO: + assert(0 && "typeTempLTO should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeAbsoluteSymbols: + assert(0 && "typeAbsoluteSymbols should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeCString: + case ld::Section::typeNonStdCString: + return S_CSTRING_LITERALS; + case ld::Section::typeCStringPointer: + return S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP; + case ld::Section::typeUTF16Strings: + return S_REGULAR; + case ld::Section::typeCFString: + return S_REGULAR; + case ld::Section::typeObjC1Classes: + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + case ld::Section::typeCFI: + return S_REGULAR; + case ld::Section::typeLSDA: + return S_REGULAR; + case ld::Section::typeDtraceDOF: + return S_DTRACE_DOF; + case ld::Section::typeUnwindInfo: + return S_REGULAR; + case ld::Section::typeObjCClassRefs: + case ld::Section::typeObjC2CategoryList: + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + case ld::Section::typeZeroFill: + if ( _options.optimizeZeroFill() ) + return S_ZEROFILL; + else + return S_REGULAR; + case ld::Section::typeTentativeDefs: + assert(0 && "typeTentativeDefs should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyPointerClose: + return S_LAZY_SYMBOL_POINTERS; + case ld::Section::typeStubClose: + case ld::Section::typeStub: + if ( sect->hasLocalRelocs ) + return S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_LOC_RELOC; + else + return S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + case ld::Section::typeNonLazyPointer: + return S_NON_LAZY_SYMBOL_POINTERS; + case ld::Section::typeDyldInfo: + return S_REGULAR; + case ld::Section::typeLazyDylibPointer: + return S_LAZY_DYLIB_SYMBOL_POINTERS; + case ld::Section::typeStubHelper: + if ( sect->hasLocalRelocs ) + return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_LOC_RELOC; + else + return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + case ld::Section::typeInitializerPointers: + return S_MOD_INIT_FUNC_POINTERS; + case ld::Section::typeTerminatorPointers: + return S_MOD_TERM_FUNC_POINTERS; + case ld::Section::typeTLVInitialValues: + return S_THREAD_LOCAL_REGULAR; + case ld::Section::typeTLVZeroFill: + return S_THREAD_LOCAL_ZEROFILL; + case ld::Section::typeTLVDefs: + return S_THREAD_LOCAL_VARIABLES; + case ld::Section::typeTLVInitializerPointers: + return S_THREAD_LOCAL_INIT_FUNCTION_POINTERS; + case ld::Section::typeTLVPointers: + return S_THREAD_LOCAL_VARIABLE_POINTERS; + case ld::Section::typeFirstSection: + assert(0 && "typeFirstSection should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeLastSection: + assert(0 && "typeLastSection should not make it to final linked image"); + return S_REGULAR; + } + return S_REGULAR; +} + + +template +bool HeaderAndLoadCommandsAtom::sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typeAbsoluteSymbols: + case ld::Section::typeTentativeDefs: + case ld::Section::typeLastSection: + return true; + default: + break; + } + return false; +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const +{ + // group sections into segments + std::vector segs; + const char* lastSegName = ""; + for (std::vector::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( _options.outputKind() == Options::kPreload ) { + if ( (*it)->type() == ld::Section::typeMachHeader ) + continue; // for -preload, don't put hidden __HEADER segment into output + if ( (*it)->type() == ld::Section::typeLinkEdit ) + continue; // for -preload, don't put hidden __LINKEDIT segment into output + } + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + SegInfo si(sect->segmentName(), _options); + segs.push_back(si); + lastSegName = sect->segmentName(); + } + if ( ! sect->isSectionHidden() ) + segs.back().nonHiddenSectionCount++; + segs.back().sections.push_back(sect); + } + // write out segment load commands for each section with trailing sections + for (std::vector::iterator it = segs.begin(); it != segs.end(); ++it) { + SegInfo& si = *it; + ld::Internal::FinalSection* lastNonZeroFillSection = NULL; + for (int i=si.sections.size()-1; i >= 0; --i) { + if ( !sectionTakesNoDiskSpace(si.sections[i]) ) { + lastNonZeroFillSection = si.sections[i]; + break; + } + } + uint64_t vmsize = si.sections.back()->address + si.sections.back()->size - si.sections.front()->address; + vmsize = ((vmsize+_options.segmentAlignment()-1) & (-_options.segmentAlignment())); + uint64_t filesize = 0; + if ( lastNonZeroFillSection != NULL ) { + filesize = lastNonZeroFillSection->address + lastNonZeroFillSection->size - si.sections.front()->address; + // round up all segments to page aligned, except __LINKEDIT + if ( (si.sections[0]->type() != ld::Section::typeLinkEdit) && (si.sections[0]->type() != ld::Section::typeImportProxies) ) + filesize = (filesize + _options.segmentAlignment()-1) & (-_options.segmentAlignment()); + } + if ( si.sections.front()->type() == ld::Section::typePageZero ) + filesize = 0; + else if ( si.sections.front()->type() == ld::Section::typeStack ) + filesize = 0; + macho_segment_command

* segCmd = (macho_segment_command

*)p; + segCmd->set_cmd(macho_segment_command

::CMD); + segCmd->set_cmdsize(sizeof(macho_segment_command

) + si.nonHiddenSectionCount*sizeof(macho_section

)); + segCmd->set_segname(si.sections.front()->segmentName()); + segCmd->set_vmaddr(si.sections.front()->address); + segCmd->set_vmsize(vmsize); + segCmd->set_fileoff(si.sections.front()->fileOffset); + segCmd->set_filesize(filesize); + segCmd->set_maxprot(si.maxProt); + segCmd->set_initprot(si.initProt); + segCmd->set_nsects(si.nonHiddenSectionCount); + segCmd->set_flags(0); + p += sizeof(macho_segment_command

); + macho_section

* msect = (macho_section

*)p; + for (std::vector::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) { + ld::Internal::FinalSection* fsect = *sit; + if ( ! fsect->isSectionHidden() ) { + msect->set_sectname(fsect->sectionName()); + msect->set_segname(fsect->segmentName()); + msect->set_addr(fsect->address); + msect->set_size(fsect->size); + msect->set_offset(sectionTakesNoDiskSpace(fsect) ? 0 : fsect->fileOffset); + msect->set_align(fsect->alignment); + msect->set_reloff(0); + msect->set_nreloc(0); + msect->set_flags(sectionFlags(fsect)); + msect->set_reserved1(fsect->indirectSymTabStartIndex); + msect->set_reserved2(fsect->indirectSymTabElementSize); + p += sizeof(macho_section

); + ++msect; + } + } + } + + return p; +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copySymbolTableLoadCommand(uint8_t* p) const +{ + // build LC_SYMTAB command + macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)p; + symbolTableCmd->set_cmd(LC_SYMTAB); + symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); + symbolTableCmd->set_nsyms(_writer.symbolTableSection->size/sizeof(macho_nlist

)); + symbolTableCmd->set_symoff(_writer.symbolTableSection->size == 0 ? 0 : _writer.symbolTableSection->fileOffset); + symbolTableCmd->set_stroff(_writer.stringPoolSection->size == 0 ? 0 : _writer.stringPoolSection->fileOffset ); + symbolTableCmd->set_strsize(_writer.stringPoolSection->size); + return p + sizeof(macho_symtab_command

); +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDynamicSymbolTableLoadCommand(uint8_t* p) const +{ + // build LC_SYMTAB command + macho_dysymtab_command

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)p; + dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); + dynamicSymbolTableCmd->set_ilocalsym(0); + dynamicSymbolTableCmd->set_nlocalsym(_writer._localSymbolsCount); + dynamicSymbolTableCmd->set_iextdefsym(dynamicSymbolTableCmd->ilocalsym()+dynamicSymbolTableCmd->nlocalsym()); + dynamicSymbolTableCmd->set_nextdefsym(_writer._globalSymbolsCount); + dynamicSymbolTableCmd->set_iundefsym(dynamicSymbolTableCmd->iextdefsym()+dynamicSymbolTableCmd->nextdefsym()); + dynamicSymbolTableCmd->set_nundefsym(_writer._importSymbolsCount); + + // FIX ME: support for 10.3 dylibs which need modules + //if ( fWriter.fModuleInfoAtom != NULL ) { + // dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); + // dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); + // dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); + // dynamicSymbolTableCmd->set_nmodtab(1); + // dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); + // dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); + //} + + bool hasIndirectSymbols = ( (_writer.indirectSymbolTableSection != NULL) && (_writer.indirectSymbolTableSection->size != 0) ); + dynamicSymbolTableCmd->set_indirectsymoff(hasIndirectSymbols ? _writer.indirectSymbolTableSection->fileOffset : 0); + dynamicSymbolTableCmd->set_nindirectsyms( hasIndirectSymbols ? _writer.indirectSymbolTableSection->size/sizeof(uint32_t) : 0); + + // FIX ME: support for classic relocations + if ( _options.outputKind() != Options::kObjectFile ) { + bool hasExternalRelocs = ( (_writer.externalRelocationsSection != NULL) && (_writer.externalRelocationsSection->size != 0) ); + dynamicSymbolTableCmd->set_extreloff(hasExternalRelocs ? _writer.externalRelocationsSection->fileOffset : 0); + dynamicSymbolTableCmd->set_nextrel( hasExternalRelocs ? _writer.externalRelocationsSection->size/8 : 0); + bool hasLocalRelocs = ( (_writer.localRelocationsSection != NULL) && (_writer.localRelocationsSection->size != 0) ); + dynamicSymbolTableCmd->set_locreloff(hasLocalRelocs ? _writer.localRelocationsSection->fileOffset : 0); + dynamicSymbolTableCmd->set_nlocrel (hasLocalRelocs ? _writer.localRelocationsSection->size/8 : 0); + } + return p + sizeof(macho_dysymtab_command

); +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDyldInfoLoadCommand(uint8_t* p) const +{ + // build LC_DYLD_INFO command + macho_dyld_info_command

* cmd = (macho_dyld_info_command

*)p; + + cmd->set_cmd(LC_DYLD_INFO_ONLY); + cmd->set_cmdsize(sizeof(macho_dyld_info_command

)); + if ( _writer.rebaseSection->size != 0 ) { + cmd->set_rebase_off(_writer.rebaseSection->fileOffset); + cmd->set_rebase_size(_writer.rebaseSection->size); + } + if ( _writer.bindingSection->size != 0 ) { + cmd->set_bind_off(_writer.bindingSection->fileOffset); + cmd->set_bind_size(_writer.bindingSection->size); + } + if ( _writer.weakBindingSection->size != 0 ) { + cmd->set_weak_bind_off(_writer.weakBindingSection->fileOffset); + cmd->set_weak_bind_size(_writer.weakBindingSection->size); + } + if ( _writer.lazyBindingSection->size != 0 ) { + cmd->set_lazy_bind_off(_writer.lazyBindingSection->fileOffset); + cmd->set_lazy_bind_size(_writer.lazyBindingSection->size); + } + if ( _writer.exportSection->size != 0 ) { + cmd->set_export_off(_writer.exportSection->fileOffset); + cmd->set_export_size(_writer.exportSection->size); + } + return p + sizeof(macho_dyld_info_command

); +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDyldLoadCommand(uint8_t* p) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylinker_command

) + strlen(_options.dyldInstallPath()) + 1); + macho_dylinker_command

* cmd = (macho_dylinker_command

*)p; + if ( _options.outputKind() == Options::kDyld ) + cmd->set_cmd(LC_ID_DYLINKER); + else + cmd->set_cmd(LC_LOAD_DYLINKER); + cmd->set_cmdsize(sz); + cmd->set_name_offset(); + strcpy((char*)&p[sizeof(macho_dylinker_command

)], _options.dyldInstallPath()); + return p + sz; +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDylibIDLoadCommand(uint8_t* p) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylib_command

) + strlen(_options.installPath()) + 1); + macho_dylib_command

* cmd = (macho_dylib_command

*)p; + cmd->set_cmd(LC_ID_DYLIB); + cmd->set_cmdsize(sz); + cmd->set_name_offset(); + cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses + cmd->set_current_version(_options.currentVersion()); + cmd->set_compatibility_version(_options.compatibilityVersion()); + strcpy((char*)&p[sizeof(macho_dylib_command

)], _options.installPath()); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copyRoutinesLoadCommand(uint8_t* p) const +{ + pint_t initAddr = _state.entryPoint->finalAddress(); + if ( _state.entryPoint->isThumb() ) + initAddr |= 1ULL; + macho_routines_command

* cmd = (macho_routines_command

*)p; + cmd->set_cmd(macho_routines_command

::CMD); + cmd->set_cmdsize(sizeof(macho_routines_command

)); + cmd->set_init_address(initAddr); + return p + sizeof(macho_routines_command

); +} + + +template +void HeaderAndLoadCommandsAtom::recopyUUIDCommand() +{ + assert(_uuidCmdInOutputBuffer != NULL); + _uuidCmdInOutputBuffer->set_uuid(_uuid); +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyUUIDLoadCommand(uint8_t* p) const +{ + macho_uuid_command

* cmd = (macho_uuid_command

*)p; + cmd->set_cmd(LC_UUID); + cmd->set_cmdsize(sizeof(macho_uuid_command

)); + cmd->set_uuid(_uuid); + _uuidCmdInOutputBuffer = cmd; // save for later re-write by recopyUUIDCommand() + return p + sizeof(macho_uuid_command

); +} + + +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) ); + if ( macVersion != ld::macVersionUnset ) { + cmd->set_cmd(LC_VERSION_MIN_MACOSX); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version((uint32_t)macVersion); + cmd->set_reserved(0); + } + else { + cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

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

); +} + + +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command* cmd = (macho_thread_command*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_thread_register(0, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(3, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command* cmd = (macho_thread_command*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_thread_register(0, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(3, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command

* cmd = (macho_thread_command

*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); // i386_THREAD_STATE + cmd->set_count(16); // i386_THREAD_STATE_COUNT; + cmd->set_thread_register(10, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(7, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command

* cmd = (macho_thread_command

*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(x86_THREAD_STATE64); + cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_thread_register(16, start); // rip + if ( _options.hasCustomStack() ) + cmd->set_thread_register(7, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + if ( _state.entryPoint->isThumb() ) + start |= 1ULL; + macho_thread_command

* cmd = (macho_thread_command

*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); + cmd->set_count(17); + cmd->set_thread_register(15, start); // pc + if ( _options.hasCustomStack() ) + cmd->set_thread_register(13, _options.customStackAddr()); // sp + return p + threadLoadCommandSize(); +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copyEncryptionLoadCommand(uint8_t* p) const +{ + macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)p; + cmd->set_cmd(LC_ENCRYPTION_INFO); + cmd->set_cmdsize(sizeof(macho_encryption_info_command

)); + assert(_writer.encryptedTextStartOffset() != 0); + assert(_writer.encryptedTextEndOffset() != 0); + cmd->set_cryptoff(_writer.encryptedTextStartOffset()); + cmd->set_cryptsize(_writer.encryptedTextEndOffset()-_writer.encryptedTextStartOffset()); + cmd->set_cryptid(0); + return p + sizeof(macho_encryption_info_command

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

* cmd = (macho_linkedit_data_command

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

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

); +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDylibLoadCommand(uint8_t* p, const ld::dylib::File* dylib) const +{ + 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 ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); + else if ( dylib->willBeReExported() && _options.useSimplifiedDylibReExports() ) + cmd->set_cmd(LC_REEXPORT_DYLIB); + else if ( dylib->willBeUpwardDylib() && _options.useUpwardDylibs() ) + cmd->set_cmd(LC_LOAD_UPWARD_DYLIB); + else + cmd->set_cmd(LC_LOAD_DYLIB); + cmd->set_cmdsize(sz); + cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses + cmd->set_current_version(dylib->currentVersion()); + cmd->set_compatibility_version(dylib->compatibilityVersion()); + cmd->set_name_offset(); + strcpy((char*)&p[sizeof(macho_dylib_command

)], dylib->installPath()); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copyRPathLoadCommand(uint8_t* p, const char* path) const +{ + uint32_t sz = alignedSize(sizeof(macho_rpath_command

) + strlen(path) + 1); + macho_rpath_command

* cmd = (macho_rpath_command

*)p; + cmd->set_cmd(LC_RPATH); + cmd->set_cmdsize(sz); + cmd->set_path_offset(); + strcpy((char*)&p[sizeof(macho_rpath_command

)], path); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copySubFrameworkLoadCommand(uint8_t* p) const +{ + const char* umbrellaName = _options.umbrellaName(); + uint32_t sz = alignedSize(sizeof(macho_sub_framework_command

) + strlen(umbrellaName) + 1); + macho_sub_framework_command

* cmd = (macho_sub_framework_command

*)p; + cmd->set_cmd(LC_SUB_FRAMEWORK); + cmd->set_cmdsize(sz); + cmd->set_umbrella_offset(); + strcpy((char*)&p[sizeof(macho_sub_framework_command

)], umbrellaName); + return p + sz; +} + + +template +uint8_t* HeaderAndLoadCommandsAtom::copyAllowableClientLoadCommand(uint8_t* p, const char* client) const +{ + uint32_t sz = alignedSize(sizeof(macho_sub_client_command

) + strlen(client) + 1); + macho_sub_client_command

* cmd = (macho_sub_client_command

*)p; + cmd->set_cmd(LC_SUB_CLIENT); + cmd->set_cmdsize(sz); + cmd->set_client_offset(); + strcpy((char*)&p[sizeof(macho_sub_client_command

)], client); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copyDyldEnvLoadCommand(uint8_t* p, const char* env) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylinker_command

) + strlen(env) + 1); + macho_dylinker_command

* cmd = (macho_dylinker_command

*)p; + cmd->set_cmd(LC_DYLD_ENVIRONMENT); + cmd->set_cmdsize(sz); + cmd->set_name_offset(); + strcpy((char*)&p[sizeof(macho_dylinker_command

)], env); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copySubUmbrellaLoadCommand(uint8_t* p, const char* nm) const +{ + uint32_t sz = alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(nm) + 1); + macho_sub_umbrella_command

* cmd = (macho_sub_umbrella_command

*)p; + cmd->set_cmd(LC_SUB_UMBRELLA); + cmd->set_cmdsize(sz); + cmd->set_sub_umbrella_offset(); + strcpy((char*)&p[sizeof(macho_sub_umbrella_command

)], nm); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copySubLibraryLoadCommand(uint8_t* p, const char* nm) const +{ + uint32_t sz = alignedSize(sizeof(macho_sub_library_command

) + strlen(nm) + 1); + macho_sub_library_command

* cmd = (macho_sub_library_command

*)p; + cmd->set_cmd(LC_SUB_LIBRARY); + cmd->set_cmdsize(sz); + cmd->set_sub_library_offset(); + strcpy((char*)&p[sizeof(macho_sub_library_command

)], nm); + return p + sz; +} + +template +uint8_t* HeaderAndLoadCommandsAtom::copyFunctionStartsLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

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

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

); +} + + +template +void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const +{ + macho_header

* mh = (macho_header

*)buffer; + bzero(buffer, this->size()); + + // copy mach_header + mh->set_magic(this->magic()); + mh->set_cputype(this->cpuType()); + mh->set_cpusubtype(this->cpuSubType()); + mh->set_filetype(this->fileType()); + mh->set_ncmds(this->commandsCount()); + mh->set_sizeofcmds(this->size()-sizeof(macho_header

)); + mh->set_flags(this->flags()); + + // copy load commands + uint8_t* p = &buffer[sizeof(macho_header

)]; + + if ( _options.outputKind() == Options::kObjectFile ) + p = this->copySingleSegmentLoadCommand(p); + else + p = this->copySegmentLoadCommands(p); + + if ( _hasDylibIDLoadCommand ) + p = this->copyDylibIDLoadCommand(p); + + if ( _hasDyldInfoLoadCommand ) + p = this->copyDyldInfoLoadCommand(p); + + if ( _hasSymbolTableLoadCommand ) + p = this->copySymbolTableLoadCommand(p); + + if ( _hasDynamicSymbolTableLoadCommand ) + p = this->copyDynamicSymbolTableLoadCommand(p); + + if ( _hasDyldLoadCommand ) + p = this->copyDyldLoadCommand(p); + + if ( _hasRoutinesLoadCommand ) + p = this->copyRoutinesLoadCommand(p); + + if ( _hasUUIDLoadCommand ) + p = this->copyUUIDLoadCommand(p); + + if ( _hasVersionLoadCommand ) + p = this->copyVersionLoadCommand(p); + + if ( _hasThreadLoadCommand ) + p = this->copyThreadsLoadCommand(p); + + if ( _hasEncryptionLoadCommand ) + p = this->copyEncryptionLoadCommand(p); + + if ( _hasSplitSegInfoLoadCommand ) + p = this->copySplitSegInfoLoadCommand(p); + + for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + p = this->copyDylibLoadCommand(p, _writer.dylibByOrdinal(ord)); + } + + if ( _hasRPathLoadCommands ) { + const std::vector& rpaths = _options.rpaths(); + for (std::vector::const_iterator it = rpaths.begin(); it != rpaths.end(); ++it) { + p = this->copyRPathLoadCommand(p, *it); + } + } + + if ( _hasSubFrameworkLoadCommand ) + p = this->copySubFrameworkLoadCommand(p); + + for (std::vector::const_iterator it = _subLibraryNames.begin(); it != _subLibraryNames.end(); ++it) { + p = this->copySubLibraryLoadCommand(p, *it); + } + + for (std::vector::const_iterator it = _subUmbrellaNames.begin(); it != _subUmbrellaNames.end(); ++it) { + p = this->copySubUmbrellaLoadCommand(p, *it); + } + + if ( _allowableClientLoadCommmandsCount != 0 ) { + const std::vector& clients = _options.allowableClients(); + for (std::vector::const_iterator it = clients.begin(); it != clients.end(); ++it) { + p = this->copyAllowableClientLoadCommand(p, *it); + } + } + + if ( _dyldEnvironExrasCount != 0 ) { + const std::vector& extras = _options.dyldEnvironExtras(); + for (std::vector::const_iterator it = extras.begin(); it != extras.end(); ++it) { + p = this->copyDyldEnvLoadCommand(p, *it); + } + } + + if ( _hasFunctionStartsLoadCommand ) + p = this->copyFunctionStartsLoadCommand(p); + +} + + + +} // namespace tool +} // namespace ld + +#endif // __HEADER_LOAD_COMMANDS_HPP__ diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp new file mode 100644 index 0000000..bc292bb --- /dev/null +++ b/ld64/src/ld/InputFiles.cpp @@ -0,0 +1,943 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-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@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Options.h" + +#include "InputFiles.h" +#include "macho_relocatable_file.h" +#include "macho_dylib_file.h" +#include "archive_file.h" +#include "lto_file.h" +#include "opaque_section_file.h" + + +namespace ld { +namespace tool { + + + + +class DSOHandleAtom : public ld::Atom { +public: + DSOHandleAtom(const char* nm, ld::Atom::Scope sc, + ld::Atom::SymbolTableInclusion inc, bool preload=false) + : ld::Atom(preload ? _s_section_preload : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + sc, ld::Atom::typeUnclassified, inc, true, false, false, + ld::Atom::Alignment(1)), _name(nm) {} + + virtual 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 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { } + virtual void setScope(Scope) { } + + virtual ~DSOHandleAtom() {} + + static ld::Section _s_section; + static ld::Section _s_section_preload; + static DSOHandleAtom _s_atomAll; + static DSOHandleAtom _s_atomExecutable; + static DSOHandleAtom _s_atomDylib; + static DSOHandleAtom _s_atomBundle; + static DSOHandleAtom _s_atomDyld; + static DSOHandleAtom _s_atomObjectFile; + static DSOHandleAtom _s_atomPreload; +private: + const char* _name; +}; +ld::Section DSOHandleAtom::_s_section("__TEXT", "__mach_header", ld::Section::typeMachHeader, true); +ld::Section DSOHandleAtom::_s_section_preload("__HEADER", "__mach_header", ld::Section::typeMachHeader, true); +DSOHandleAtom DSOHandleAtom::_s_atomAll("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomExecutable("__mh_execute_header", ld::Atom::scopeGlobal, ld::Atom::symbolTableInAndNeverStrip); +DSOHandleAtom DSOHandleAtom::_s_atomDylib("__mh_dylib_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomBundle("__mh_bundle_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomDyld("__mh_dylinker_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomObjectFile("__mh_object_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomPreload("__mh_preload_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, true); + + + +class PageZeroAtom : public ld::Atom { +public: + PageZeroAtom(uint64_t sz) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeZeroFill, + symbolTableNotIn, true, false, false, ld::Atom::Alignment(12)), + _size(sz) {} + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "page zero"; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { } + virtual void setScope(Scope) { } + + virtual ~PageZeroAtom() {} + + static ld::Section _s_section; + static DSOHandleAtom _s_atomAll; +private: + uint64_t _size; +}; +ld::Section PageZeroAtom::_s_section("__PAGEZERO", "__pagezero", ld::Section::typePageZero, true); + + +class CustomStackAtom : public ld::Atom { +public: + CustomStackAtom(uint64_t sz) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeZeroFill, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(12)), + _size(sz) {} + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "custom stack"; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { } + virtual void setScope(Scope) { } + + virtual ~CustomStackAtom() {} + +private: + uint64_t _size; + static ld::Section _s_section; +}; +ld::Section CustomStackAtom::_s_section("__UNIXSTACK", "__stack", ld::Section::typeStack, true); + + + +const char* InputFiles::fileArch(const uint8_t* p, unsigned len) +{ + const char* result = mach_o::relocatable::archName(p); + if ( result != NULL ) + return result; + + result = lto::archName(p, len); + if ( result != NULL ) + return result; + + if ( strncmp((const char*)p, "!\n", 8) == 0 ) + return "archive"; + + return "unsupported file format"; +} + + +ld::File* InputFiles::makeFile(const Options::FileInfo& info) +{ + // map in whole file + uint64_t len = info.fileLen; + int fd = ::open(info.path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + if ( info.fileLen < 20 ) + throw "file too small"; + + uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file, errno=%d", errno); + + // if fat file, skip to architecture we want + // Note: fat header is always big-endian + bool isFatFile = false; + const fat_header* fh = (fat_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + isFatFile = true; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + uint32_t sliceToUse; + bool sliceFound = false; + if ( _options.preferSubArchitecture() ) { + // first try to find a slice that match cpu-type and cpu-sub-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture()) + && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( !sliceFound ) { + // look for any slice that matches just cpu-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( sliceFound ) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); + len = OSSwapBigToHostInt32(archs[sliceToUse].size); + if ( fileOffset+len > info.fileLen ) { + throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", + fileOffset, fileOffset+len, info.fileLen); + } + // if requested architecture is page aligned within fat file, then remap just that portion of file + if ( (fileOffset & 0x00000FFF) == 0 ) { + // unmap whole file + munmap((caddr_t)p, info.fileLen); + // re-map just part we need + p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); + if ( p == (uint8_t*)(-1) ) + throwf("can't re-map file, errno=%d", errno); + } + else { + p = &p[fileOffset]; + } + } + } + ::close(fd); + + // see if it is an object file + mach_o::relocatable::ParserOptions objOpts; + objOpts.architecture = _options.architecture(); + objOpts.objSubtypeMustMatch = _options.preferSubArchitecture(); + objOpts.logAllFiles = _options.logAllFiles(); + objOpts.convertUnwindInfo = _options.needsUnwindInfoSection(); + objOpts.subType = _options.subArchitecture(); + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, _nextInputOrdinal, objOpts); + if ( objResult != NULL ) + return this->addObject(objResult, info, len); + + // see if it is an llvm object file + objResult = lto::parse(p, len, info.path, info.modTime, _nextInputOrdinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles()); + if ( objResult != NULL ) + return this->addObject(objResult, info, len); + + // 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); + if ( dylibResult != NULL ) + return this->addDylib(dylibResult, info, len); + + // see if it is a static library + archive::ParserOptions archOpts; + archOpts.objOpts = objOpts; + archOpts.forceLoadThisArchive = info.options.fForceLoad; + archOpts.forceLoadAll = _options.fullyLoadArchives(); + archOpts.forceLoadObjC = _options.loadAllObjcObjectsFromArchives(); + archOpts.verboseLoad = _options.whyLoad(); + archOpts.logAllFiles = _options.logAllFiles(); + ld::File* archiveResult = archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts); + if ( archiveResult != NULL ) + 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() ) { + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + } + else { + const char* libLTO = "libLTO.dylib"; + char ldPath[PATH_MAX]; + char tmpPath[PATH_MAX]; + char libLTOPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tmpPath) != NULL ) { + char* lastSlash = strrchr(tmpPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib/libLTO.dylib"); + libLTO = tmpPath; + if ( realpath(tmpPath, libLTOPath) != NULL ) + libLTO = libLTOPath; + } + } + throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); + } + } + + // error handling + if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + throwf("missing required architecture %s in file", _options.architectureName()); + } + else { + if ( isFatFile ) + throwf("file is universal but does not contain a(n) %s slice", _options.architectureName()); + else + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + } +} + +void InputFiles::logDylib(ld::File* file, bool indirect) +{ + if ( _options.traceDylibs() ) { + const char* fullPath = file->path(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + const ld::dylib::File* dylib = dynamic_cast(file); + if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) { + // don't log upward dylibs when XBS is computing dependencies + logTraceInfo("[Logging for XBS] Used upward dynamic library: %s\n", fullPath); + } + else { + if ( indirect ) + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + else + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } + } +} + +void InputFiles::logArchive(ld::File* file) const +{ + if ( _options.traceArchives() && (_archiveFilesLogged.count(file) == 0) ) { + // LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive + _archiveFilesLogged.insert(file); + const char* fullPath = file->path(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + } +} + + +void InputFiles::logTraceInfo(const char* format, ...) const +{ + // one time open() of custom LD_TRACE_FILE + static int trace_file = -1; + if ( trace_file == -1 ) { + const char *trace_file_path = _options.traceOutputFile(); + if ( trace_file_path != NULL ) { + trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); + if ( trace_file == -1 ) + throwf("Could not open or create trace file: %s", trace_file_path); + } + else { + trace_file = fileno(stderr); + } + } + + char trace_buffer[MAXPATHLEN * 2]; + va_list ap; + va_start(ap, format); + int length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); + va_end(ap); + char* buffer_ptr = trace_buffer; + + while (length > 0) { + ssize_t amount_written = write(trace_file, buffer_ptr, length); + if(amount_written == -1) + /* Failure to write shouldn't fail the build. */ + return; + buffer_ptr += amount_written; + length -= amount_written; + } +} + +ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath) +{ + //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); + InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath); + if ( pos != _installPathToDylibs.end() ) { + return pos->second; + } + else { + // allow -dylib_path option to override indirect library to use + for (std::vector::const_iterator dit = _options.dylibOverrides().begin(); dit != _options.dylibOverrides().end(); ++dit) { + if ( strcmp(dit->installName,installPath) == 0 ) { + try { + Options::FileInfo info = _options.findFile(dit->useInstead); + ld::File* reader = this->makeFile(info); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + //_installPathToDylibs[strdup(installPath)] = dylibReader; + this->logDylib(dylibReader, true); + return dylibReader; + } + else + throwf("indirect dylib at %s is not a dylib", dit->useInstead); + } + catch (const char* msg) { + warning("ignoring -dylib_file option, %s", msg); + } + } + } + char newPath[MAXPATHLEN]; + // handle @loader_path + if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { + strcpy(newPath, fromPath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &installPath[13]); + else + strcpy(newPath, &installPath[13]); + installPath = newPath; + } + // note: @executable_path case is handled inside findFileUsingPaths() + // search for dylib using -F and -L paths + Options::FileInfo info = _options.findFileUsingPaths(installPath); + try { + ld::File* reader = this->makeFile(info); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); + //_installPathToDylibs[strdup(installPath)] = dylibReader; + this->logDylib(dylibReader, true); + return dylibReader; + } + else + throwf("indirect dylib at %s is not a dylib", info.path); + } + catch (const char* msg) { + throwf("in %s, %s", info.path, msg); + } + } +} + + + +void InputFiles::createIndirectDylibs() +{ + _allDirectDylibsLoaded = true; + + // mark all dylibs initially specified as required and check if they can be used + for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) { + it->second->setExplicitlyLinked(); + this->checkDylibClientRestrictions(it->second); + } + + // keep processing dylibs until no more dylibs are added + unsigned long lastMapSize = 0; + std::set dylibsProcessed; + while ( lastMapSize != _allDylibs.size() ) { + lastMapSize = _allDylibs.size(); + // can't iterator _installPathToDylibs while modifying it, so use temp buffer + std::vector unprocessedDylibs; + for (std::set::iterator it=_allDylibs.begin(); it != _allDylibs.end(); it++) { + if ( dylibsProcessed.count(*it) == 0 ) + unprocessedDylibs.push_back(*it); + } + for (std::vector::iterator it=unprocessedDylibs.begin(); it != unprocessedDylibs.end(); it++) { + dylibsProcessed.insert(*it); + (*it)->processIndirectLibraries(this, _options.implicitlyLinkIndirectPublicDylibs()); + } + } + + // go back over original dylibs and mark sub frameworks as re-exported + if ( _options.outputKind() == Options::kDynamicLibrary ) { + const char* myLeaf = strrchr(_options.installPath(), '/'); + if ( myLeaf != NULL ) { + for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); it++) { + ld::dylib::File* dylibReader = dynamic_cast(*it); + if ( dylibReader != NULL ) { + const char* childParent = dylibReader->parentUmbrella(); + if ( childParent != NULL ) { + if ( strcmp(childParent, &myLeaf[1]) == 0 ) { + // mark that this dylib will be re-exported + dylibReader->setWillBeReExported(); + } + } + } + } + } + } + +} + +void InputFiles::createOpaqueFileSections() +{ + // extra command line section always at end + for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { + _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, + it->dataLen, _nextInputOrdinal)); + // bump ordinal + _nextInputOrdinal++; + } + +} + + +void InputFiles::checkDylibClientRestrictions(ld::dylib::File* dylib) +{ + // Check for any restrictions on who can link with this dylib + const char* dylibParentName = dylib->parentUmbrella() ; + const std::vector* clients = dylib->allowableClients(); + if ( (dylibParentName != NULL) || (clients != NULL) ) { + // only dylibs that are in an umbrella or have a client list need verification + const char* installName = _options.installPath(); + const char* installNameLastSlash = strrchr(installName, '/'); + bool isParent = false; + bool isSibling = false; + bool isAllowableClient = false; + // There are three cases: + if ( (dylibParentName != NULL) && (installNameLastSlash != NULL) ) { + // starts after last slash + const char* myName = &installNameLastSlash[1]; + unsigned int myNameLen = strlen(myName); + if ( strncmp(myName, "lib", 3) == 0 ) + myName = &myName[3]; + // up to first dot + const char* firstDot = strchr(myName, '.'); + if ( firstDot != NULL ) + myNameLen = firstDot - myName; + // up to first underscore + const char* firstUnderscore = strchr(myName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - myName) < (int)myNameLen) ) + myNameLen = firstUnderscore - myName; + + // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella + isParent = ( (strlen(dylibParentName) == myNameLen) && (strncmp(myName, dylibParentName, myNameLen) == 0) ); + + // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent + isSibling = ( (_options.umbrellaName() != NULL) && (strcmp(_options.umbrellaName(), dylibParentName) == 0) ); + } + + if ( !isParent && !isSibling && (clients != NULL) ) { + // case 3) the dylib has a list of allowable clients, and we are creating one of them + const char* clientName = _options.clientName(); + int clientNameLen = 0; + if ( clientName != NULL ) { + // use client name as specified on command line + clientNameLen = strlen(clientName); + } + else { + // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) + clientName = installName; + clientNameLen = strlen(clientName); + // starts after last slash + if ( installNameLastSlash != NULL ) + clientName = &installNameLastSlash[1]; + if ( strncmp(clientName, "lib", 3) == 0 ) + clientName = &clientName[3]; + // up to first dot + const char* firstDot = strchr(clientName, '.'); + if ( firstDot != NULL ) + clientNameLen = firstDot - clientName; + // up to first underscore + const char* firstUnderscore = strchr(clientName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) + clientNameLen = firstUnderscore - clientName; + } + + // Use clientName to check if this dylib is able to link against the allowable clients. + for (std::vector::const_iterator it = clients->begin(); it != clients->end(); it++) { + if ( strncmp(*it, clientName, clientNameLen) == 0 ) + isAllowableClient = true; + } + } + + if ( !isParent && !isSibling && !isAllowableClient ) { + if ( dylibParentName != NULL ) { + throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", + dylib->path(), dylibParentName); + } + else { + throwf("cannot link directly with %s", dylib->path()); + } + } + } +} + + +void InputFiles::inferArchitecture(Options& opts, const char** archName) +{ + _inferredArch = true; + // scan all input files, looking for a thin .o file. + // the first one found is presumably the architecture to link + uint8_t buffer[sizeof(mach_header_64)]; + const std::vector& files = opts.getInputFiles(); + for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { + int fd = ::open(it->path, O_RDONLY, 0); + if ( fd != -1 ) { + ssize_t amount = read(fd, buffer, sizeof(buffer)); + ::close(fd); + if ( amount >= (ssize_t)sizeof(buffer) ) { + cpu_type_t type; + cpu_subtype_t subtype; + if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype) ) { + opts.setArchitecture(type, subtype); + *archName = opts.architectureName(); + return; + } + } + } + } + + // no thin .o files found, so default to same architecture this tool was built as + warning("-arch not specified"); +#if __ppc__ + opts.setArchitecture(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); +#elif __i386__ + opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL); +#elif __ppc64__ + opts.setArchitecture(CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL); +#elif __x86_64__ + opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL); +#elif __arm__ + opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6); +#else + #error unknown default architecture +#endif + *archName = opts.architectureName(); +} + + +InputFiles::InputFiles(Options& opts, const char** archName) + : _totalObjectSize(0), _totalArchiveSize(0), + _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), + _options(opts), _bundleLoader(NULL), _nextInputOrdinal(1), + _allDirectDylibsLoaded(false), _inferredArch(false) +{ +// fStartCreateReadersTime = mach_absolute_time(); + if ( opts.architecture() == 0 ) { + // command line missing -arch, so guess arch + inferArchitecture(opts, archName); + } + + const std::vector& files = opts.getInputFiles(); + if ( files.size() == 0 ) + throw "no object files specified"; + // add all direct object, archives, and dylibs + _inputFiles.reserve(files.size()); + for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { + const Options::FileInfo& entry = *it; + try { + _inputFiles.push_back(this->makeFile(entry)); + } + catch (const char* msg) { + if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { + if ( opts.ignoreOtherArchInputFiles() ) { + // ignore, because this is about an architecture not in use + } + else { + warning("ignoring file %s, %s", entry.path, msg); + } + } + else { + throwf("in %s, %s", entry.path, msg); + } + } + } + + this->createIndirectDylibs(); + this->createOpaqueFileSections(); +} + + + +ld::File* InputFiles::addArchive(ld::File* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + // bump ordinal + _nextInputOrdinal += reader->subFileCount(); + + // update stats + _totalArchiveSize += mappedLen; + _totalArchivesLoaded++; + return reader; +} + + +ld::File* InputFiles::addObject(ld::relocatable::File* file, const Options::FileInfo& info, uint64_t mappedLen) +{ + // bump ordinal + _nextInputOrdinal++; + + // update stats + _totalObjectSize += mappedLen; + _totalObjectLoaded++; + return file; +} + + +ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + _allDylibs.insert(reader); + + if ( (reader->installPath() == NULL) && !info.options.fBundleLoader ) { + // this is a "blank" stub + // silently ignore it + return reader; + } + // store options about how dylib will be used in dylib itself + if ( info.options.fWeakImport ) + reader->setWillBeWeakLinked(); + if ( info.options.fReExport ) + reader->setWillBeReExported(); + if ( info.options.fUpward ) { + if ( _options.outputKind() == Options::kDynamicLibrary ) + reader->setWillBeUpwardDylib(); + else + warning("ignoring upward dylib option for %s\n", info.path); + } + if ( info.options.fLazyLoad ) + reader->setWillBeLazyLoadedDylb(); + + // add to map of loaded dylibs + const char* installPath = reader->installPath(); + if ( installPath != NULL ) { + InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath); + if ( pos == _installPathToDylibs.end() ) { + _installPathToDylibs[strdup(installPath)] = reader; + } + else { + bool dylibOnCommandLineTwice = ( strcmp(pos->second->path(), reader->path()) == 0 ); + bool isSymlink = false; + // ignore if this is a symlink to a dylib we've already loaded + if ( !dylibOnCommandLineTwice ) { + char existingDylibPath[PATH_MAX]; + if ( realpath(pos->second->path(), existingDylibPath) != NULL ) { + char newDylibPath[PATH_MAX]; + if ( realpath(reader->path(), newDylibPath) != NULL ) { + isSymlink = ( strcmp(existingDylibPath, newDylibPath) == 0 ); + } + } + } + if ( !dylibOnCommandLineTwice && !isSymlink ) + warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + } + } + else if ( info.options.fBundleLoader ) + _bundleLoader = reader; + + // log direct readers + if ( !_allDirectDylibsLoaded ) + this->logDylib(reader, false); + + // bump ordinal + _nextInputOrdinal++; + + // update stats + _totalDylibsLoaded++; + + return reader; +} + + +bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const +{ + bool didSomething = false; + for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + didSomething |= (*it)->forEachAtom(handler); + } + if ( didSomething || true ) { + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomExecutable); + handler.doAtom(DSOHandleAtom::_s_atomAll); + if ( _options.pageZeroSize() != 0 ) + handler.doAtom(*new PageZeroAtom(_options.pageZeroSize())); + if ( _options.hasCustomStack() ) + handler.doAtom(*new CustomStackAtom(_options.customStackSize())); + break; + case Options::kDynamicLibrary: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomDylib); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kDynamicBundle: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomBundle); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kDyld: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomDyld); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::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); + break; + case Options::kObjectFile: + handler.doAtom(DSOHandleAtom::_s_atomObjectFile); + break; + case Options::kKextBundle: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + } + } + return didSomething; +} + + +bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler& handler) const +{ + // check each input file + for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + ld::File* file = *it; + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + ld::dylib::File* dylibFile = dynamic_cast(file); + if ( searchDylibs && (dylibFile != NULL) ) { + //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() ); + if ( dylibFile->justInTimeforEachAtom(name, handler) ) { + // we found a definition in this dylib + // done, unless it is a weak definition in which case we keep searching + if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) + return true; + // else continue search for a non-weak definition + } + } + else if ( searchArchives && (dylibFile == NULL) ) { + if ( file->justInTimeforEachAtom(name, handler) ) { + if ( _options.traceArchives() ) + logArchive(file); + // found definition in static library, done + return true; + } + } + } + + // search indirect dylibs + if ( searchDylibs ) { + for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { + ld::dylib::File* dylibFile = it->second; + bool searchThisDylib = false; + if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { + // for two level namesapce, just check all implicitly linked dylibs + searchThisDylib = dylibFile->implicitlyLinked() && !dylibFile->explicitlyLinked(); + } + else { + // for flat namespace, check all indirect dylibs + searchThisDylib = ! dylibFile->explicitlyLinked(); + } + if ( searchThisDylib ) { + //fprintf(stderr, "searchLibraries(%s), looking in implicitly linked %s\n", name, dylibFile->path() ); + if ( dylibFile->justInTimeforEachAtom(name, handler) ) { + // we found a definition in this dylib + // done, unless it is a weak definition in which case we keep searching + if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) + return true; + // else continue search for a non-weak definition + } + } + } + } + + return false; +} + + +bool InputFiles::searchWeakDefInDylib(const char* name) const +{ + // search all relevant dylibs to see if any of a weak-def with this name + for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { + ld::dylib::File* dylibFile = it->second; + if ( dylibFile->implicitlyLinked() || dylibFile->explicitlyLinked() ) { + if ( dylibFile->hasWeakExternals() && dylibFile->hasWeakDefinition(name) ) { + return true; + } + } + } + return false; +} + +void InputFiles::dylibs(ld::Internal& state) +{ + bool dylibsOK; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + dylibsOK = true; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + case Options::kKextBundle: + dylibsOK = false; + break; + } + + // add command line dylibs in order + for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + ld::dylib::File* dylibFile = dynamic_cast(*it); + // only add dylibs that are not "blank" dylib stubs + if ( (dylibFile != NULL) && ((dylibFile->installPath() != NULL) || (dylibFile == _bundleLoader)) ) { + if ( dylibsOK ) + state.dylibs.push_back(dylibFile); + else + warning("unexpected dylib (%s) on link line", dylibFile->path()); + } + } + // add implicitly linked dylibs + if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { + for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { + ld::dylib::File* dylibFile = it->second; + if ( dylibFile->implicitlyLinked() && dylibsOK ) + state.dylibs.push_back(dylibFile); + } + } + // and -bundle_loader + state.bundleLoader = _bundleLoader; +} + + +} // namespace tool +} // namespace ld + diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h new file mode 100644 index 0000000..1565515 --- /dev/null +++ b/ld64/src/ld/InputFiles.h @@ -0,0 +1,115 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __INPUT_FILES_H__ +#define __INPUT_FILES_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + +class InputFiles : public ld::dylib::File::DylibHandler +{ +public: + InputFiles(Options& opts, const char** archName); + + // implementation from ld::dylib::File::DylibHandler + virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath); + + // 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; + // 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 + void dylibs(ld::Internal& state); + + bool inferredArch() const { return _inferredArch; } + + uint32_t nextInputOrdinal() const { return _nextInputOrdinal++; } + + // for -print_statistics + uint64_t _totalObjectSize; + uint64_t _totalArchiveSize; + uint32_t _totalObjectLoaded; + uint32_t _totalArchivesLoaded; + uint32_t _totalDylibsLoaded; + + +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* 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); + void logTraceInfo (const char* format, ...) const; + void logDylib(ld::File*, bool indirect); + void logArchive(ld::File*) const; + void createIndirectDylibs(); + void checkDylibClientRestrictions(ld::dylib::File*); + void createOpaqueFileSections(); + + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToDylib; + + const Options& _options; + std::vector _inputFiles; + mutable std::set _archiveFilesLogged; + InstallNameToDylib _installPathToDylibs; + std::set _allDylibs; + ld::dylib::File* _bundleLoader; + mutable uint32_t _nextInputOrdinal; + bool _allDirectDylibsLoaded; + bool _inferredArch; +}; + +} // namespace tool +} // namespace ld + +#endif // __INPUT_FILES_H__ diff --git a/ld64/src/ld/LTOReader.hpp b/ld64/src/ld/LTOReader.hpp deleted file mode 100644 index 2e560cc..0000000 --- a/ld64/src/ld/LTOReader.hpp +++ /dev/null @@ -1,742 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __LTO_READER_H__ -#define __LTO_READER_H__ - -#include -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ObjectFile.h" -#include "Options.h" - -#include "llvm-c/lto.h" - - -namespace lto { - - -// -// Reference handles Atom references. These references facilitate -// symbol resolution. -// - -class Reference : public ObjectFile::Reference -{ -public: - Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { } - Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { } - - bool isTargetUnbound() const { return fTargetAtom == NULL; } - bool isFromTargetUnbound() const { return true; } - uint8_t getKind() const { return 0; } - uint64_t getFixUpOffset() const { return 0; } - const char * getTargetName() const { return fTargetName; } - ObjectFile::Atom& getTarget() const { return *fTargetAtom; } - uint64_t getTargetOffset() const { return 0; } - bool hasFromTarget() const { return false; } - ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } - const char * getFromTargetName() const { return NULL; } - uint64_t getFromTargetOffset() const { return 0; } - TargetBinding getTargetBinding() const; - TargetBinding getFromTargetBinding() const { return kDontBind; } - void setTarget (ObjectFile::Atom& a, uint64_t offset) - { fTargetAtom = &a; } - void setFromTarget(ObjectFile::Atom &a) { } - const char * getDescription() const; - -private: - const char * fTargetName; - ObjectFile::Atom * fTargetAtom; -}; - - -ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const -{ - if ( fTargetAtom == NULL ) - return kUnboundByName; - else if ( fTargetName == NULL ) - return kBoundDirectly; - else - return kBoundByName; -} - -const char* Reference::getDescription() const -{ - static char temp[256]; - strcpy(temp, "reference to "); - if ( fTargetName != NULL ) - strcat(temp, fTargetName); - else - strcat(temp, fTargetAtom->getDisplayName()); - return temp; -} - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - - static Segment fgBootstrapSegment; - -private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; -}; - -Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false); - - - - -// -// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, -// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After -// optimization is performed, real Atoms are created for these symobls. However these real Atoms -// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate -// methods to real atom. -// -class Atom : public ObjectFile::Atom -{ -public: - Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom); - - ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; } - bool getTranslationUnitSource (const char **dir, const char **name) const - { return fRealAtom->getTranslationUnitSource(dir, name); } - const char * getName () const { return fName; } - const char * getDisplayName() const { return this->getName(); } - Scope getScope() const { return (fRealAtom ? fRealAtom->getScope() : fScope); } - DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } - SymbolTableInclusion getSymbolTableInclusion() const - { return (fRealAtom ? fRealAtom->getSymbolTableInclusion() : ObjectFile::Atom::kSymbolTableIn); } - bool dontDeadStrip() const { return false; } - bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); } - bool isThumb() const { return false; } - uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); } - std::vector& getReferences() const - { return (fRealAtom ? fRealAtom->getReferences() : (std::vector&)fReferences); } - bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); } - const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); } - // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection. - class ObjectFile::Section * getSection() const { return fSection; } - ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); } - uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); } - ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); } - std::vector* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); } - ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); } - void copyRawContent(uint8_t buffer[]) const - { if (fRealAtom) fRealAtom->copyRawContent(buffer); } - void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; } - - void setRealAtom (ObjectFile::Atom *atom) - { fRealAtom = atom; } - ObjectFile::Atom * getRealAtom() { return fRealAtom; } - void addReference(ObjectFile::Reference *ref) - { fReferences.push_back(ref); } - - void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); } - void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); } - -private: - class Reader& fOwner; - const char* fName; - ObjectFile::Atom::Scope fScope; - ObjectFile::Atom::DefinitionKind fKind; - uint8_t fAlignment; - ObjectFile::Atom* fRealAtom; - std::vector fReferences; -}; - - -Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom) -: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL) -{ - // every Atom references the InternalAtom for its reader - fReferences.push_back(new Reference(internalAtom)); -} - - -// -// ld64 only tracks non-internal symbols from an llvm bitcode file. -// We model this by having an InternalAtom which represent all internal functions and data. -// All non-interal symbols from a bitcode file are represented by a Atom -// and each Atom has a reference to the InternalAtom. The InternalAtom -// also has references to each symbol external to the bitcode file. -// -class InternalAtom : public ObjectFile::Atom -{ -public: - InternalAtom(class Reader& owner) : fOwner(owner) {} - - ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; } - bool getTranslationUnitSource (const char **dir, const char **name) const - { return false; } - const char * getName () const { return "__llvm-internal-atom"; } - const char * getDisplayName() const { return "llvm bitcode"; } - Scope getScope() const { return scopeTranslationUnit; } - DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; } - bool dontDeadStrip() const { return false; } - bool isZeroFill() const { return false; } - bool isThumb() const { return false; } - uint64_t getSize() const { return 0; } - std::vector& getReferences() const { return (std::vector&)fReferences; } - bool mustRemainInSection() const { return false; } - const char * getSectionName() const { return NULL; } - class ObjectFile::Section * getSection() const { return NULL; } - ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; } - uint32_t getOrdinal() const { return 0; } - ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - std::vector* getLineInfo() const { return NULL; } - ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - void copyRawContent(uint8_t buffer[]) const { } - void setScope(Scope s) { } - - void addReference(const char* targetName); - -private: - class Reader& fOwner; - std::vector fReferences; -}; - - -void InternalAtom::addReference(const char* name) -{ - fReferences.push_back(new Reference(name)); -} - - - - -class RemovableAtoms -{ -public: - RemovableAtoms(std::set& iAtoms) : fAtoms(iAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fAtoms.count(atom) != 0 ); - } - -private: - std::set& fAtoms; -}; - - - -// -// LLVM bitcode file reader -// -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); - static const char* fileKind(const uint8_t* fileContent); - static bool loaded() { return (::lto_get_version() != NULL); } - Reader(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, - const ObjectFile::ReaderOptions&, cpu_type_t arch); - virtual ~Reader(); - - virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return fModTime; } - virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; } - virtual std::vector* getStabs() { return NULL; } - virtual bool optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, const std::set&, - std::vector& newDeadAtoms, - uint32_t nextInputOrdinal, - ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - int outputKind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs); - -private: - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; - - ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal); - static const char* tripletPrefixForArch(cpu_type_t); - - cpu_type_t fArchitecture; - const char* fPath; - time_t fModTime; - lto_module_t fModule; - std::vector fAtoms; - InternalAtom fInternalAtom; - const ObjectFile::ReaderOptions& fReaderOptions; - static std::set fgReaders; - static bool fgOptimized; -}; - -bool Reader::fgOptimized = false; -std::set Reader::fgReaders; - - -Reader::~Reader() -{ - if ( fModule != NULL ) - ::lto_module_dispose(fModule); -} - -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, cpu_type_t arch) - : fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options) -{ - fgReaders.insert(this); - - fModule = ::lto_module_create_from_memory(fileContent, fileLength); - if ( fModule == NULL ) - throwf("could not parse object file %s: %s", path, lto_get_error_message()); - - fAtoms.push_back(&fInternalAtom); - - uint32_t count = ::lto_module_get_num_symbols(fModule); - for (uint32_t i=0; i < count; ++i) { - const char* name = ::lto_module_get_symbol_name(fModule, i); - lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i); - - // LTO doesn't like dtrace symbols - // ignore dtrace static probes for now - // later when codegen is done and a mach-o file is produces the probes will be processed - if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) - continue; - - ObjectFile::Atom::DefinitionKind kind; - switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { - case LTO_SYMBOL_DEFINITION_REGULAR: - kind = ObjectFile::Atom::kRegularDefinition; - break; - case LTO_SYMBOL_DEFINITION_TENTATIVE: - kind = ObjectFile::Atom::kTentativeDefinition; - break; - case LTO_SYMBOL_DEFINITION_WEAK: - kind = ObjectFile::Atom::kWeakDefinition; - break; - case LTO_SYMBOL_DEFINITION_UNDEFINED: - case LTO_SYMBOL_DEFINITION_WEAKUNDEF: - kind = ObjectFile::Atom::kExternalDefinition; - break; - default: - throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); - } - - // make LLVM atoms for definitions and a reference for undefines - if ( kind != ObjectFile::Atom::kExternalDefinition ) { - ObjectFile::Atom::Scope scope; - switch ( attr & LTO_SYMBOL_SCOPE_MASK) { - case LTO_SYMBOL_SCOPE_INTERNAL: - scope = ObjectFile::Atom::scopeTranslationUnit; - break; - case LTO_SYMBOL_SCOPE_HIDDEN: - scope = ObjectFile::Atom::scopeLinkageUnit; - break; - case LTO_SYMBOL_SCOPE_DEFAULT: - scope = ObjectFile::Atom::scopeGlobal; - break; - default: - throwf("unknown scope for symbol %s in bitcode file %s", name, path); - } - // only make atoms for non-internal symbols - if ( scope == ObjectFile::Atom::scopeTranslationUnit ) - continue; - uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); - // make Atom - fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom)); - } - else { - // add to list of external references - fInternalAtom.addReference(name); - } - } -} - -const char* Reader::tripletPrefixForArch(cpu_type_t arch) -{ - switch (arch) { - case CPU_TYPE_POWERPC: - return "powerpc-"; - case CPU_TYPE_POWERPC64: - return "powerpc64-"; - case CPU_TYPE_I386: - return "i386-"; - case CPU_TYPE_X86_64: - return "x86_64-"; - case CPU_TYPE_ARM: - return "arm"; - } - return ""; -} - -bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) -{ - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); -} - -const char* Reader::fileKind(const uint8_t* p) -{ - if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { - uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); - switch (arch) { - case CPU_TYPE_POWERPC: - return "ppc"; - case CPU_TYPE_I386: - return "i386"; - case CPU_TYPE_X86_64: - return "x86_64"; - case CPU_TYPE_ARM: - return "arm"; - } - return "unknown bitcode architecture"; - } - return NULL; -} - -bool Reader::optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, const std::set& deadAtoms, - std::vector& newlyDeadAtoms, - uint32_t nextInputOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - int okind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) -{ - // this method is call on all Readers. We want the first call to trigger optimization - // across all Readers and the subsequent calls to do nothing. - if ( fgOptimized ) - return false; - fgOptimized = true; - - Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency - - // print out LTO version string if -v was used - if ( verbose ) - fprintf(stderr, "%s\n", lto_get_version()); - - // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); - for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - if ( ::lto_codegen_add_module(generator, (*it)->fModule) ) - throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message()); - } - - // add any -mllvm command line options - for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { - ::lto_codegen_debug_options(generator, *it); - } - - // The atom graph uses directed edges (references). Collect all references where - // originating atom is not part of any LTO Reader. This allows optimizer to optimize an - // external (i.e. not originated from same .o file) reference if all originating atoms are also - // defined in llvm bitcode file. - CStringSet nonLLVMRefs; - CStringToAtom llvmAtoms; - bool hasNonllvmAtoms = false; - for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - // only look at references come from an atom that is not an llvm atom - if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) { - // remember if we've seen any atoms not from an llvm reader and not from the writer - if ( atom->getFile() != writer ) - hasNonllvmAtoms = true; - std::vector& refs = atom->getReferences(); - for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { - ObjectFile::Reference* ref = *ri; - // add target name to set if target is an llvm atom - if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) { - nonLLVMRefs.insert(ref->getTargetName()); - } - } - } - else { - const char* name = atom->getName(); - if ( name != NULL ) - llvmAtoms[name] = (Atom*)atom; - } - } - // if entry point is in a llvm bitcode file, it must be preserved by LTO - if ( entryPointAtom != NULL ) { - if ( fgReaders.count((Reader*)(entryPointAtom->getFile())) != 0 ) - nonLLVMRefs.insert(entryPointAtom->getName()); - } - - // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions - // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced - // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead - // atom so that the linker can replace it with the mach-o one later. - CStringToAtom deadllvmAtoms; - for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) { - const char* name = atom->getName(); - ::lto_codegen_add_must_preserve_symbol(generator, name); - deadllvmAtoms[name] = (Atom*)atom; - } - } - - - // tell code generator about symbols that must be preserved - for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { - const char* name = it->first; - Atom* atom = it->second; - // Include llvm Symbol in export list if it meets one of following two conditions - // 1 - atom scope is global (and not linkage unit). - // 2 - included in nonLLVMRefs set. - // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - } - - // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) - if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) { - if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { - // HACK, no good way to tell linker we are all done, so just quit - exit(0); - } - warning("could not produce merged bitcode file"); - } - - // set code-gen model - lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - switch ( outputKind ) { - case Options::kDynamicExecutable: - case Options::kPreload: - if ( pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: // ?? Is this appropriate ? - case Options::kDyld: - case Options::kKextBundle: - if ( allowTextRelocs ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - break; - case Options::kStaticExecutable: - // darwin x86_64 "static" code model is really dynamic code model - if ( fArchitecture == CPU_TYPE_X86_64 ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_STATIC; - break; - } - if ( ::lto_codegen_set_pic_model(generator, model) ) - throwf("could not create set codegen model: %s", lto_get_error_message()); - - // if requested, save off merged bitcode file - if ( saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } - -#if LTO_API_VERSION >= 3 - // find assembler next to linker - char path[PATH_MAX]; - uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { - char* lastSlash = strrchr(path, '/'); - if ( lastSlash != NULL ) { - strcpy(lastSlash+1, "as"); - struct stat statInfo; - if ( stat(path, &statInfo) == 0 ) - ::lto_codegen_set_assembler_path(generator, path); - } - } -#endif - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); - - // if requested, save off temp mach-o file - if ( saveTemps ) { - char tempMachoPath[MAXPATHLEN]; - strcpy(tempMachoPath, outputFilePath); - strcat(tempMachoPath, ".lto.o"); - int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if ( fd != -1) { - ::write(fd, machOFile, machOFileLen); - ::close(fd); - } - // save off merged bitcode file - char tempOptBitcodePath[MAXPATHLEN]; - strcpy(tempOptBitcodePath, outputFilePath); - strcat(tempOptBitcodePath, ".lto.opt.bc"); - ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); - } - - // parse generated mach-o file into a MachOReader - ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal); - - // sync generated mach-o atoms with existing atoms ld knows about - std::vector machoAtoms = machoReader->getAtoms(); - for (std::vector::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - const char* name = atom->getName(); - if ( name != NULL ) { - CStringToAtom::iterator pos = llvmAtoms.find(name); - if ( pos != llvmAtoms.end() ) { - // turn Atom into a proxy for this mach-o atom - pos->second->setRealAtom(atom); - } - else { - // an atom of this name was not in the allAtoms list the linker gave us - if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { - // this corresponding to an atom that the linker coalesced away. Ignore it - // Make sure there any dependent atoms are also marked dead - std::vector& refs = atom->getReferences(); - for (std::vector::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { - ObjectFile::Reference* ref = *ri; - if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX - ObjectFile::Atom* targ = &ref->getTarget(); - deadllvmAtoms[targ->getName()] = (Atom*)atom; - } - } - } - else - { - // this is something new that lto conjured up, tell ld its new - newAtoms.push_back(atom); - } - } - } - else { - // ld only knew about named atoms, so this one must be new - newAtoms.push_back(atom); - } - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* ref = *rit; - const char* targetName = ref->getTargetName(); - CStringToAtom::iterator pos; - if (targetName != NULL) { - switch ( ref->getTargetBinding() ) { - case ObjectFile::Reference::kUnboundByName: - // accumulate unbounded references so that ld can bound them. - additionalUndefines.push_back(targetName); - break; - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - // If mach-o atom is referencing another mach-o atom then - // reference is not going through Atom proxy. Fix it here to ensure that all - // llvm symbol references always go through Atom proxy. - pos = llvmAtoms.find(targetName); - if ( pos != llvmAtoms.end() ) - ref->setTarget(*pos->second, ref->getTargetOffset()); - break; - case ObjectFile::Reference::kDontBind: - break; - } - } - } - } - - // Remove InternalAtoms from ld - for (std::set::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - newlyDeadAtoms.push_back(&((*it)->fInternalAtom)); - } - // Remove Atoms from ld if code generator optimized them away - for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { - // check if setRealAtom() called on this Atom - if ( li->second->getRealAtom() == NULL ) - newlyDeadAtoms.push_back(li->second); - } - - return true; -} - - -ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal) -{ - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - } - throw "LLVM LTO, file is not of required architecture"; -} - -}; // namespace lto - -extern void printLTOVersion(Options& opts); - -void printLTOVersion(Options& opts) { - const char* vers = lto_get_version(); - if ( vers != NULL ) - fprintf(stderr, "%s\n", vers); -} - - -#endif - diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp new file mode 100644 index 0000000..d905a3d --- /dev/null +++ b/ld64/src/ld/LinkEdit.hpp @@ -0,0 +1,1306 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-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@ + */ + +#ifndef __LINKEDIT_HPP__ +#define __LINKEDIT_HPP__ + +#include +#include +#include +#include +#include + +#include + +#include "Options.h" +#include "ld.hpp" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +namespace ld { +namespace tool { + +class ByteStream { +private: + std::vector _data; +public: + std::vector& bytes() { return _data; } + unsigned long size() const { return _data.size(); } + void reserve(unsigned long l) { _data.reserve(l); } + const uint8_t* start() const { return &_data[0]; } + + void append_uleb128(uint64_t value) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + _data.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + void append_sleb128(int64_t value) { + bool isNeg = ( value < 0 ); + uint8_t byte; + bool more; + do { + byte = value & 0x7F; + value = value >> 7; + if ( isNeg ) + more = ( (value != -1) || ((byte & 0x40) == 0) ); + else + more = ( (value != 0) || ((byte & 0x40) != 0) ); + if ( more ) + byte |= 0x80; + _data.push_back(byte); + } + while( more ); + } + + void append_string(const char* str) { + for (const char* s = str; *s != '\0'; ++s) + _data.push_back(*s); + _data.push_back('\0'); + } + + void append_byte(uint8_t byte) { + _data.push_back(byte); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + void pad_to_size(unsigned int alignment) { + while ( (_data.size() % alignment) != 0 ) + _data.push_back(0); + } +}; + + +class LinkEditAtom : public ld::Atom +{ +public: + + // overrides of ld::Atom + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual uint64_t objectAddress() const { return 0; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + + virtual void encode() const = 0; + + LinkEditAtom(const Options& opts, ld::Internal& state, + OutputFile& writer, const ld::Section& sect, + unsigned int pointerSize) + : ld::Atom(sect, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn, + false, false, false, ld::Atom::Alignment(log2(pointerSize))), + _options(opts), _state(state), _writer(writer), + _encoded(false) { } +protected: + const Options& _options; + ld::Internal& _state; + OutputFile& _writer; + mutable ByteStream _encodedData; + mutable bool _encoded; +}; + +uint64_t LinkEditAtom::size() const +{ + assert(_encoded); + return _encodedData.size(); +} + +void LinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + assert(_encoded); + memcpy(buffer, _encodedData.start(), _encodedData.size()); +} + + + + +template +class RebaseInfoAtom : public LinkEditAtom +{ +public: + RebaseInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { _encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "rebase info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + struct rebase_tmp + { + rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + }; + + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; +}; + +template +ld::Section RebaseInfoAtom::_s_section("__LINKEDIT", "__rebase", ld::Section::typeLinkEdit, true); + + +template +void RebaseInfoAtom::encode() const +{ + // omit relocs if this was supposed to be PIE but PIE not possible + if ( _options.positionIndependentExecutable() && this->_writer.pieDisabled ) + return; + + // sort rebase info by type, then address + std::vector& info = this->_writer._rebaseInfo; + std::sort(info.begin(), info.end()); + + // convert to temp encoding that can be more easily optimized + std::vector mid; + uint64_t curSegStart = 0; + uint64_t curSegEnd = 0; + uint32_t curSegIndex = 0; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { + if ( type != it->_type ) { + mid.push_back(rebase_tmp(REBASE_OPCODE_SET_TYPE_IMM, it->_type)); + type = it->_type; + } + if ( address != it->_address ) { + if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) { + if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) ) + throw "binding address outside range of any segment"; + mid.push_back(rebase_tmp(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, it->_address - curSegStart)); + } + else { + mid.push_back(rebase_tmp(REBASE_OPCODE_ADD_ADDR_ULEB, it->_address-address)); + } + address = it->_address; + } + mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); + address += sizeof(pint_t); + } + mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); + + // optimize phase 1, compress packed runs of pointers + rebase_tmp* dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) { + *dst = *src++; + while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) { + dst->operand1 += src->operand1; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 2, combine rebase/add pairs + dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) + && (src->operand1 == 1) + && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) { + dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with + // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) + && (src[2].operand1 == delta) ) { + // found at least three in a row, this is worth compressing + dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 4, use immediate encodings + for (rebase_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) { + p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES; + } + } + + // convert to compressed encoding + const static bool log = false; + this->_encodedData.reserve(info.size()*2); + bool done = false; + for (typename std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case REBASE_OPCODE_DONE: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DONE()\n"); + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + this->_encodedData.append_byte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_IMM_TIMES(%lld)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%lld)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + this->_encodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + this->_encodedData.append_uleb128(it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + } + } + + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + if (log) fprintf(stderr, "total rebase info size = %ld\n", this->_encodedData.size()); +} + + +template +class BindingInfoAtom : public LinkEditAtom +{ +public: + BindingInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "binding info"; } + // overrides of LinkEditAtom + virtual void encode() const; + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct binding_tmp + { + binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) + : opcode(op), operand1(p1), operand2(p2), name(s) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + const char* name; + }; + + static ld::Section _s_section; +}; + +template +ld::Section BindingInfoAtom::_s_section("__LINKEDIT", "__binding", ld::Section::typeLinkEdit, true); + + +template +void BindingInfoAtom::encode() const +{ + // sort by library, symbol, type, then address + std::vector& info = this->_writer._bindingInfo; + std::sort(info.begin(), info.end()); + + // convert to temp encoding that can be more easily optimized + std::vector mid; + uint64_t curSegStart = 0; + uint64_t curSegEnd = 0; + uint32_t curSegIndex = 0; + int ordinal = 0x80000000; + const char* symbolName = NULL; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + int64_t addend = 0; + for (std::vector::const_iterator it = info.begin(); it != info.end(); ++it) { + if ( ordinal != it->_libraryOrdinal ) { + if ( it->_libraryOrdinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->_libraryOrdinal)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->_libraryOrdinal)); + } + ordinal = it->_libraryOrdinal; + } + if ( symbolName != it->_symbolName ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->_flags, 0, it->_symbolName)); + symbolName = it->_symbolName; + } + if ( type != it->_type ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->_type)); + type = it->_type; + } + if ( address != it->_address ) { + if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) { + if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) ) + throw "binding address outside range of any segment"; + mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, it->_address - curSegStart)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->_address-address)); + } + address = it->_address; + } + if ( addend != it->_addend ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->_addend)); + addend = it->_addend; + } + mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); + address += sizeof(pint_t); + } + mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); + + + // optimize phase 1, combine bind/add pairs + binding_tmp* dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { + p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // convert to compressed encoding + const static bool log = false; + this->_encodedData.reserve(info.size()*2); + bool done = false; + for (typename std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); + this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); + this->_encodedData.append_string(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + this->_encodedData.append_sleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + this->_encodedData.append_uleb128(it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + } + } + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + if (log) fprintf(stderr, "total binding info size = %ld\n", this->_encodedData.size()); +} + + + +template +class WeakBindingInfoAtom : public LinkEditAtom +{ +public: + WeakBindingInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { _encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "weak binding info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct WeakBindingSorter + { + bool operator()(const OutputFile::BindingInfo& left, const OutputFile::BindingInfo& right) + { + // sort by symbol, type, address + if ( left._symbolName != right._symbolName ) + return ( strcmp(left._symbolName, right._symbolName) < 0 ); + if ( left._type != right._type ) + return (left._type < right._type); + return (left._address < right._address); + } + }; + + struct binding_tmp + { + binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) + : opcode(op), operand1(p1), operand2(p2), name(s) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + const char* name; + }; + + static ld::Section _s_section; +}; + +template +ld::Section WeakBindingInfoAtom::_s_section("__LINKEDIT", "__weak_binding", ld::Section::typeLinkEdit, true); + + +template +void WeakBindingInfoAtom::encode() const +{ + // sort by symbol, type, address + std::vector& info = this->_writer._weakBindingInfo; + if ( info.size() == 0 ) { + // short circuit if no weak binding needed + this->_encoded = true; + return; + } + std::sort(info.begin(), info.end(), WeakBindingSorter()); + + // convert to temp encoding that can be more easily optimized + std::vector mid; + mid.reserve(info.size()); + uint64_t curSegStart = 0; + uint64_t curSegEnd = 0; + uint32_t curSegIndex = 0; + const char* symbolName = NULL; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + int64_t addend = 0; + for (typename std::vector::const_iterator it = info.begin(); it != info.end(); ++it) { + if ( symbolName != it->_symbolName ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->_flags, 0, it->_symbolName)); + symbolName = it->_symbolName; + } + // non-weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND + if ( it->_type != BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB ) { + if ( type != it->_type ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->_type)); + type = it->_type; + } + if ( address != it->_address ) { + if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) { + if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) ) + throw "binding address outside range of any segment"; + mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, it->_address - curSegStart)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->_address-address)); + } + address = it->_address; + } + if ( addend != it->_addend ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->_addend)); + addend = it->_addend; + } + mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); + address += sizeof(pint_t); + } + } + mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); + + + // optimize phase 1, combine bind/add pairs + binding_tmp* dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + } + dst->opcode = BIND_OPCODE_DONE; + + + // convert to compressed encoding + const static bool log = false; + this->_encodedData.reserve(info.size()*2); + bool done = false; + for (typename std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); + this->_encodedData.append_byte(BIND_OPCODE_DONE); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); + this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); + this->_encodedData.append_string(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + this->_encodedData.append_sleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + this->_encodedData.append_uleb128(it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + } + } + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + if (log) fprintf(stderr, "total weak binding info size = %ld\n", this->_encodedData.size()); + +} + + + +template +class LazyBindingInfoAtom : public LinkEditAtom +{ +public: + LazyBindingInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) {_encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "lazy binding info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; +}; + +template +ld::Section LazyBindingInfoAtom::_s_section("__LINKEDIT", "__lazy_binding", ld::Section::typeLinkEdit, true); + + + +template +void LazyBindingInfoAtom::encode() const +{ + // stream all lazy bindings and record start offsets + std::vector& info = this->_writer._lazyBindingInfo; + for (std::vector::const_iterator it = info.begin(); it != info.end(); ++it) { + // record start offset for use by stub helper + this->_writer.setLazyBindingInfoOffset(it->_address, this->_encodedData.size()); + + // write address to bind + uint64_t segStart = 0; + uint64_t segEnd = 0; + uint32_t segIndex = 0; + if ( ! this->_writer.findSegment(this->_state, it->_address, &segStart, &segEnd, &segIndex) ) + throw "lazy binding address outside range of any segment"; + this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); + this->_encodedData.append_uleb128(it->_address - segStart); + + // write ordinal + if ( it->_libraryOrdinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->_libraryOrdinal & BIND_IMMEDIATE_MASK) ); + } + else if ( it->_libraryOrdinal <= 15 ) { + // small ordinals are encoded in opcode + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->_libraryOrdinal); + } + else { + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->_encodedData.append_uleb128(it->_libraryOrdinal); + } + // write symbol name + this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->_flags); + this->_encodedData.append_string(it->_symbolName); + // write do bind + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND); + this->_encodedData.append_byte(BIND_OPCODE_DONE); + } + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + //fprintf(stderr, "lazy binding info size = %ld, for %ld entries\n", _encodedData.size(), allLazys.size()); +} + + + +template +class ExportInfoAtom : public LinkEditAtom +{ +public: + ExportInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { _encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "export info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + const ld::Atom* stubForResolverFunction(const ld::Atom* resolver) const; + + struct TrieEntriesSorter + { + TrieEntriesSorter(const Options& o) : _options(o) {} + + bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) + { + unsigned int leftOrder; + unsigned int rightOrder; + _options.exportedSymbolOrder(left.name, &leftOrder); + _options.exportedSymbolOrder(right.name, &rightOrder); + if ( leftOrder != rightOrder ) + return (leftOrder < rightOrder); + else + return (left.address < right.address); + } + private: + const Options& _options; + }; + + static ld::Section _s_section; +}; + +template +ld::Section ExportInfoAtom::_s_section("__LINKEDIT", "__export", ld::Section::typeLinkEdit, true); + +template +const ld::Atom* ExportInfoAtom::stubForResolverFunction(const ld::Atom* resolver) const +{ + for (std::vector::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( (sect->type() == ld::Section::typeStub) || (sect->type() == ld::Section::typeStubClose) ) { + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( strcmp(atom->name(), resolver->name()) == 0 ) + return atom; + } + } + } + assert(0 && "no stub for resolver function"); + return NULL; +} + + +template +void ExportInfoAtom::encode() const +{ + // make vector of mach_o::trie::Entry for all exported symbols + std::vector& exports = this->_writer._exportedAtoms; + uint64_t imageBaseAddress = this->_writer.headerAndLoadCommandsSection->address; + std::vector entries; + entries.reserve(exports.size()); + for (std::vector::const_iterator it = exports.begin(); it != exports.end(); ++it) { + const ld::Atom* atom = *it; + mach_o::trie::Entry entry; + uint64_t flags = (atom->contentType() == ld::Atom::typeTLV) ? EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL : EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + uint64_t other = 0; + uint64_t address = atom->finalAddress() - imageBaseAddress; + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) + flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + if ( atom->definition() == ld::Atom::definitionProxy ) { + entry.name = atom->name(); + entry.flags = flags | EXPORT_SYMBOL_FLAGS_REEXPORT; + entry.other = this->_writer.compressedOrdinalForAtom(atom); + if ( entry.other == BIND_SPECIAL_DYLIB_SELF ) { + warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->file()->path()); + continue; + } + if ( atom->isAlias() ) { + // alias proxy means symbol was re-exported with a name change + const ld::Atom* aliasOf = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + aliasOf = fit->u.target; + } + } + assert(aliasOf != NULL); + entry.importName = aliasOf->name(); + } + else { + // symbol name stays same as re-export + entry.importName = atom->name(); + } + entries.push_back(entry); + //fprintf(stderr, "re-export %s from lib %llu as %s\n", entry.importName, entry.other, entry.name); + } + else { + if ( atom->isThumb() ) + address |= 1; + if ( atom->contentType() == ld::Atom::typeResolver ) { + flags |= EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER; + // set normal lookup to return stub address + // and add resolver function in new location that newer dyld's can access + other = address; + const ld::Atom* stub = stubForResolverFunction(atom); + address = stub->finalAddress() - imageBaseAddress; + if ( stub->isThumb() ) + address |= 1; + } + entry.name = atom->name(); + entry.flags = flags; + entry.address = address; + entry.other = other; + entry.importName = NULL; + entries.push_back(entry); + } + } + + // sort vector by -exported_symbols_order, and any others by address + std::sort(entries.begin(), entries.end(), TrieEntriesSorter(_options)); + + // create trie + mach_o::trie::makeTrie(entries, this->_encodedData.bytes()); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; +} + + +template +class SplitSegInfoAtom : public LinkEditAtom +{ +public: + SplitSegInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "split seg info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + void addSplitSegInfo(uint64_t address, ld::Fixup::Kind k) const; + void uleb128EncodeAddresses(const std::vector& locations) const; + + mutable std::vector _32bitPointerLocations; + mutable std::vector _64bitPointerLocations; + mutable std::vector _ppcHi16Locations; + + + static ld::Section _s_section; +}; + +template +ld::Section SplitSegInfoAtom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); + +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + _32bitPointerLocations.push_back(address); + break; + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + _64bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStoreLittleEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStorePPCPicHigh16AddLow: + _ppcHi16Locations.push_back(address); + break; + case ld::Fixup::kindStoreBigEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStorePPCPicHigh16AddLow: + _ppcHi16Locations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template +void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& locations) const +{ + pint_t addr = this->_options.baseAddress(); + for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { + pint_t nextAddr = *it; + //fprintf(stderr, "nextAddr=0x%0llX\n", (uint64_t)nextAddr); + uint64_t delta = nextAddr - addr; + //fprintf(stderr, "delta=0x%0llX\n", delta); + if ( delta == 0 ) + throw "double split seg info for same address"; + // uleb128 encode + uint8_t byte; + do { + byte = delta & 0x7F; + delta &= ~0x7F; + if ( delta != 0 ) + byte |= 0x80; + this->_encodedData.append_byte(byte); + delta = delta >> 7; + } + while( byte >= 0x80 ); + addr = nextAddr; + } +} + + +template +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); + } + + // delta compress runs of addresses + this->_encodedData.reserve(8192); + if ( _32bitPointerLocations.size() != 0 ) { + this->_encodedData.append_byte(1); + //fprintf(stderr, "type 1:\n"); + std::sort(_32bitPointerLocations.begin(), _32bitPointerLocations.end()); + this->uleb128EncodeAddresses(_32bitPointerLocations); + this->_encodedData.append_byte(0); // terminator + } + + if ( _64bitPointerLocations.size() != 0 ) { + this->_encodedData.append_byte(2); + //fprintf(stderr, "type 2:\n"); + std::sort(_64bitPointerLocations.begin(), _64bitPointerLocations.end()); + this->uleb128EncodeAddresses(_64bitPointerLocations); + this->_encodedData.append_byte(0); // terminator + } + + if ( _ppcHi16Locations.size() != 0 ) { + this->_encodedData.append_byte(3); + //fprintf(stderr, "type 3:\n"); + std::sort(_ppcHi16Locations.begin(), _ppcHi16Locations.end()); + this->uleb128EncodeAddresses(_ppcHi16Locations); + this->_encodedData.append_byte(0); // terminator + } + + // always add zero byte to mark end + this->_encodedData.append_byte(0); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + // clean up temporaries + _32bitPointerLocations.clear(); + _64bitPointerLocations.clear(); + _ppcHi16Locations.clear(); +} + + +template +class FunctionStartsAtom : public LinkEditAtom +{ +public: + FunctionStartsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "function starts"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; +}; + +template +ld::Section FunctionStartsAtom::_s_section("__LINKEDIT", "__funcStarts", ld::Section::typeLinkEdit, true); + + +template +void FunctionStartsAtom::encode() const +{ + this->_encodedData.reserve(8192); + const uint64_t badAddress = 1; + uint64_t addr = badAddress; + // delta compress all function addresses + for (std::vector::iterator it = this->_state.sections.begin(); it != this->_state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->type() == ld::Section::typeMachHeader ) { + // start with delta from start of __TEXT + addr = sect->address; + } + else if ( sect->type() == ld::Section::typeCode ) { + assert(addr != badAddress); + std::vector& atoms = sect->atoms; + for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + uint64_t nextAddr = atom->finalAddress(); + if ( atom->isThumb() ) + nextAddr |= 1; + uint64_t delta = nextAddr - addr; + if ( delta != 0 ) + this->_encodedData.append_uleb128(delta); + addr = nextAddr; + } + } + } + + // terminator + this->_encodedData.append_byte(0); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; +} + + + +} // namespace tool +} // namespace ld + +#endif // __LINKEDIT_HPP__ diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp new file mode 100644 index 0000000..d5438ac --- /dev/null +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -0,0 +1,2542 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-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@ + */ + +#ifndef __LINKEDIT_CLASSIC_HPP__ +#define __LINKEDIT_CLASSIC_HPP__ + +#include +#include +#include +#include +#include + +#include + +#include "Options.h" +#include "ld.hpp" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +namespace ld { +namespace tool { + + + +class ClassicLinkEditAtom : public ld::Atom +{ +public: + + // overrides of ld::Atom + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual uint64_t objectAddress() const { return 0; } + + virtual void encode() = 0; + virtual bool hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe) { return false; } + + ClassicLinkEditAtom(const Options& opts, ld::Internal& state, + OutputFile& writer, const ld::Section& sect, + unsigned int pointerSize) + : ld::Atom(sect, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn, + false, false, false, ld::Atom::Alignment(log2(pointerSize))), + _options(opts), _state(state), _writer(writer) { } +protected: + const Options& _options; + ld::Internal& _state; + OutputFile& _writer; +}; + + + +class StringPoolAtom : public ClassicLinkEditAtom +{ +public: + StringPoolAtom(const Options& opts, ld::Internal& state, + OutputFile& writer, int pointerSize); + + // overrides of ld::Atom + virtual const char* name() const { return "string pool"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode() { } + + int32_t add(const char* name); + int32_t addUnique(const char* name); + int32_t emptyString() { return 1; } + const char* stringForIndex(int32_t) const; + uint32_t currentOffset(); + +private: + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + enum { kBufferSize = 0x01000000 }; + typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; + + const uint32_t _pointerSize; + std::vector _fullBuffers; + char* _currentBuffer; + uint32_t _currentBufferUsed; + StringToOffset _uniqueStrings; + + static ld::Section _s_section; +}; + +ld::Section StringPoolAtom::_s_section("__LINKEDIT", "__string_pool", ld::Section::typeLinkEdit, true); + + +StringPoolAtom::StringPoolAtom(const Options& opts, ld::Internal& state, OutputFile& writer, int pointerSize) + : ClassicLinkEditAtom(opts, state, writer, _s_section, pointerSize), + _pointerSize(pointerSize), _currentBuffer(NULL), _currentBufferUsed(0) +{ + _currentBuffer = new char[kBufferSize]; + // burn first byte of string pool (so zero is never a valid string offset) + _currentBuffer[_currentBufferUsed++] = ' '; + // make offset 1 always point to an empty string + _currentBuffer[_currentBufferUsed++] = '\0'; +} + +uint64_t StringPoolAtom::size() const +{ + // pointer size align size + return (kBufferSize * _fullBuffers.size() + _currentBufferUsed + _pointerSize-1) & (-_pointerSize); +} + +void StringPoolAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t offset = 0; + for (unsigned int i=0; i < _fullBuffers.size(); ++i) { + memcpy(&buffer[offset], _fullBuffers[i], kBufferSize); + offset += kBufferSize; + } + memcpy(&buffer[offset], _currentBuffer, _currentBufferUsed); + // zero fill end to align + offset += _currentBufferUsed; + while ( (offset % _pointerSize) != 0 ) + buffer[offset++] = 0; +} + +int32_t StringPoolAtom::add(const char* str) +{ + int32_t offset = kBufferSize * _fullBuffers.size() + _currentBufferUsed; + int lenNeeded = strlcpy(&_currentBuffer[_currentBufferUsed], str, kBufferSize-_currentBufferUsed)+1; + if ( (_currentBufferUsed+lenNeeded) < kBufferSize ) { + _currentBufferUsed += lenNeeded; + } + else { + int copied = kBufferSize-_currentBufferUsed-1; + // change trailing '\0' that strlcpy added to real char + _currentBuffer[kBufferSize-1] = str[copied]; + // alloc next buffer + _fullBuffers.push_back(_currentBuffer); + _currentBuffer = new char[kBufferSize]; + _currentBufferUsed = 0; + // append rest of string + this->add(&str[copied+1]); + } + return offset; +} + +uint32_t StringPoolAtom::currentOffset() +{ + return kBufferSize * _fullBuffers.size() + _currentBufferUsed; +} + + +int32_t StringPoolAtom::addUnique(const char* str) +{ + StringToOffset::iterator pos = _uniqueStrings.find(str); + if ( pos != _uniqueStrings.end() ) { + return pos->second; + } + else { + int32_t offset = this->add(str); + _uniqueStrings[str] = offset; + return offset; + } +} + + +const char* StringPoolAtom::stringForIndex(int32_t index) const +{ + int32_t currentBufferStartIndex = kBufferSize * _fullBuffers.size(); + int32_t maxIndex = currentBufferStartIndex + _currentBufferUsed; + // check for out of bounds + if ( index > maxIndex ) + return ""; + // check for index in _currentBuffer + if ( index > currentBufferStartIndex ) + return &_currentBuffer[index-currentBufferStartIndex]; + // otherwise index is in a full buffer + uint32_t fullBufferIndex = index/kBufferSize; + return &_fullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; +} + + + +template +class SymbolTableAtom : public ClassicLinkEditAtom +{ +public: + SymbolTableAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : ClassicLinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)), + _stabsStringsOffsetStart(0), _stabsStringsOffsetEnd(0), + _stabsIndexStart(0), _stabsIndexEnd(0) { } + + // overrides of ld::Atom + virtual const char* name() const { return "symbol table"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode(); + virtual bool hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + bool addLocal(const ld::Atom* atom, StringPoolAtom* pool); + void addGlobal(const ld::Atom* atom, StringPoolAtom* pool); + void addImport(const ld::Atom* atom, StringPoolAtom* pool); + uint8_t classicOrdinalForProxy(const ld::Atom* atom); + uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool); + uint64_t valueForStab(const ld::relocatable::File::Stab& stab); + uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab); + + + mutable std::vector > _globals; + mutable std::vector > _locals; + mutable std::vector > _imports; + + uint32_t _stabsStringsOffsetStart; + uint32_t _stabsStringsOffsetEnd; + uint32_t _stabsIndexStart; + uint32_t _stabsIndexEnd; + + static ld::Section _s_section; +}; + +template +ld::Section SymbolTableAtom::_s_section("__LINKEDIT", "__symbol_table", ld::Section::typeLinkEdit, true); + + + +template +bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) +{ + macho_nlist

entry; + static int s_anonNameIndex = 1; + assert(atom->symbolTableInclusion() != ld::Atom::symbolTableNotIn); + + // set n_strx + const char* symbolName = atom->name(); + char anonName[32]; + if ( this->_options.outputKind() == Options::kObjectFile ) { + if ( atom->contentType() == ld::Atom::typeCString ) { + if ( atom->combine() == ld::Atom::combineByNameAndContent ) { + // don't use 'l' labels for x86_64 strings + // x86_64 obj-c runtime confused when static lib is stripped + sprintf(anonName, "LC%u", s_anonNameIndex++); + symbolName = anonName; + } + } + else if ( atom->contentType() == ld::Atom::typeCFI ) { + if ( _options.removeEHLabels() ) + return false; + // synthesize .eh name + if ( strcmp(atom->name(), "CIE") == 0 ) + symbolName = "EH_Frame1"; + else + symbolName = "func.eh"; + } + else if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) { + // make auto-strip anonymous name for symbol + sprintf(anonName, "l%03u", s_anonNameIndex++); + symbolName = anonName; + } + } + entry.set_n_strx(pool->add(symbolName)); + + // set n_type + uint8_t type = N_SECT; + if ( atom->definition() == ld::Atom::definitionAbsolute ) { + type = N_ABS; + } + else if ( (atom->section().type() == ld::Section::typeObjC1Classes) + && (this->_options.outputKind() == Options::kObjectFile) ) { + // __OBJC __class has floating abs symbols for each class data structure + type = N_ABS; + } + if ( atom->scope() == ld::Atom::scopeLinkageUnit ) + type |= N_PEXT; + entry.set_n_type(type); + + // set n_sect (section number of implementation ) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_sect(0); + else + entry.set_n_sect(atom->machoSection()); + + // set n_desc + uint16_t desc = 0; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) + desc |= N_WEAK_DEF; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + entry.set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_value(atom->objectAddress()); + else + entry.set_n_value(atom->finalAddress()); + + // add to array + _locals.push_back(entry); + return true; +} + + +template +void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) +{ + macho_nlist

entry; + + // set n_strx + entry.set_n_strx(pool->add(atom->name())); + + // set n_type + if ( atom->definition() == ld::Atom::definitionAbsolute ) { + entry.set_n_type(N_EXT | N_ABS); + } + else if ( (atom->section().type() == ld::Section::typeObjC1Classes) + && (this->_options.outputKind() == Options::kObjectFile) ) { + // __OBJC __class has floating abs symbols for each class data structure + entry.set_n_type(N_EXT | N_ABS); + } + else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) { + entry.set_n_type(N_EXT | N_INDR); + } + else { + entry.set_n_type(N_EXT | N_SECT); + if ( (atom->scope() == ld::Atom::scopeLinkageUnit) && (this->_options.outputKind() == Options::kObjectFile) ) { + if ( this->_options.keepPrivateExterns() ) + entry.set_n_type(N_EXT | N_SECT | N_PEXT); + } + else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) + && (atom->section().type() == ld::Section::typeMachHeader) ) { + // the __mh_execute_header is historical magic and must be an absolute symbol + entry.set_n_type(N_EXT | N_ABS); + } + } + + // set n_sect (section number of implementation) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_sect(0); + else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) + entry.set_n_sect(0); + else + entry.set_n_sect(atom->machoSection()); + + // set n_desc + uint16_t desc = 0; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( (atom->contentType() == ld::Atom::typeResolver) && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_SYMBOL_RESOLVER; + if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) { + desc |= N_WEAK_DEF; + // support auto hidden weak symbols: .weak_def_can_be_hidden + if ( (atom->scope() == ld::Atom::scopeGlobal) && atom->autoHide() && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_WEAK_REF; + } + entry.set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_value(atom->objectAddress()); + else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) { + if ( atom->isAlias() ) { + // this re-export also renames + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + entry.set_n_value(pool->add(fit->u.target->name())); + } + } + } + else + entry.set_n_value(entry.n_strx()); + } + else + entry.set_n_value(atom->finalAddress()); + + // add to array + _globals.push_back(entry); +} + +template +uint8_t SymbolTableAtom::classicOrdinalForProxy(const ld::Atom* atom) +{ + assert(atom->definition() == ld::Atom::definitionProxy); + // when linking for flat-namespace ordinals are always zero + if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) + return 0; + const ld::dylib::File* dylib = dynamic_cast(atom->file()); + // when linking -undefined dynamic_lookup, unbound symbols use DYNAMIC_LOOKUP_ORDINAL + if ( dylib == NULL ) { + if (_options.undefinedTreatment() == Options::kUndefinedDynamicLookup ) + return DYNAMIC_LOOKUP_ORDINAL; + if (_options.allowedUndefined(atom->name()) ) + return DYNAMIC_LOOKUP_ORDINAL; + } + assert(dylib != NULL); + int ord = this->_writer.dylibToOrdinal(dylib); + if ( ord == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) + return EXECUTABLE_ORDINAL; + return ord; +} + + +template +void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) +{ + macho_nlist

entry; + + // set n_strx + entry.set_n_strx(pool->add(atom->name())); + + // set n_type + if ( this->_options.outputKind() == Options::kObjectFile ) { + if ( (atom->scope() == ld::Atom::scopeLinkageUnit) + && (atom->definition() == ld::Atom::definitionTentative) ) + entry.set_n_type(N_UNDF | N_EXT | N_PEXT); + else + entry.set_n_type(N_UNDF | N_EXT); + } + else { + if ( this->_options.prebind() ) + entry.set_n_type(N_PBUD | N_EXT); + else + entry.set_n_type(N_UNDF | N_EXT); + } + + // set n_sect + entry.set_n_sect(0); + + uint16_t desc = 0; + if ( this->_options.outputKind() != Options::kObjectFile ) { + uint8_t ordinal = this->classicOrdinalForProxy(atom); + //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); + SET_LIBRARY_ORDINAL(desc, ordinal); + +#if 0 + // set n_desc ( high byte is library ordinal, low byte is reference type ) + std::map::iterator pos = fStubsMap.find(atom); + if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) + desc |= REFERENCE_FLAG_UNDEFINED_LAZY; + else + desc |= REFERENCE_FLAG_UNDEFINED_NON_LAZY; +#endif + } + else if ( atom->definition() == ld::Atom::definitionTentative ) { + uint8_t align = atom->alignment().powerOf2; + // always record custom alignment of common symbols to match what compiler does + SET_COMM_ALIGN(desc, align); + } + if ( (this->_options.outputKind() != Options::kObjectFile) + && (atom->definition() == ld::Atom::definitionProxy) + && (atom->combine() == ld::Atom::combineByName) ) { + desc |= N_REF_TO_WEAK; + } + if ( atom->weakImported() ) + desc |= N_WEAK_REF; + entry.set_n_desc(desc); + + // set n_value, zero for import proxy and size for tentative definition + if ( atom->definition() == ld::Atom::definitionTentative ) + entry.set_n_value(atom->size()); + else + entry.set_n_value(0); + + // add to array + _imports.push_back(entry); +} + +template +uint8_t SymbolTableAtom::sectionIndexForStab(const ld::relocatable::File::Stab& stab) +{ + // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN + if ( stab.type == N_FUN ) + return stab.other; + else if ( stab.type == N_GSYM ) + return 0; + else if ( stab.atom != NULL ) + return stab.atom->machoSection(); + else + return stab.other; +} + + +template +uint64_t SymbolTableAtom::valueForStab(const ld::relocatable::File::Stab& stab) +{ + switch ( stab.type ) { + case N_FUN: + if ( stab.atom == NULL ) { + // Add support to ld64 for N_FUN stabs when used for symbolic constants + return stab.value; + } + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of function N_FUN has size + return stab.atom->size(); + } + else { + // start of function N_FUN has address + return stab.atom->finalAddress(); + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + if ( stab.atom == NULL ) + // some weird assembly files have slines not associated with a function + return stab.value; + else + // all these stab types need their value changed from an offset in the atom to an address + return stab.atom->finalAddress() + stab.value; + case N_STSYM: + case N_LCSYM: + case N_BNSYM: + // all these need address of atom + if ( stab.atom != NULL ) + return stab.atom->finalAddress(); + else + return 0; // work around for mismatch N_BNSYM + case N_ENSYM: + return stab.atom->size(); + case N_SO: + if ( stab.atom == NULL ) { + return 0; + } + else { + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of translation unit N_SO has address of end of last atom + return stab.atom->finalAddress() + stab.atom->size(); + } + else { + // start of translation unit N_SO has address of end of first atom + return stab.atom->finalAddress(); + } + } + break; + default: + return stab.value; + } +} + +template +uint32_t SymbolTableAtom::stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool) +{ + switch (stab.type) { + case N_SO: + if ( (stab.string == NULL) || stab.string[0] == '\0' ) { + return pool->emptyString(); + break; + } + // fall into uniquing case + case N_SOL: + case N_BINCL: + case N_EXCL: + return pool->addUnique(stab.string); + break; + default: + if ( stab.string == NULL ) + return 0; + else if ( stab.string[0] == '\0' ) + return pool->emptyString(); + else + return pool->add(stab.string); + } + return 0; +} + + + +template +bool SymbolTableAtom::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe) +{ + ssos = _stabsStringsOffsetStart; + ssoe = _stabsStringsOffsetEnd; + sos = _stabsIndexStart * sizeof(macho_nlist

); + soe = _stabsIndexEnd * sizeof(macho_nlist

); + return ( (_stabsIndexStart != _stabsIndexEnd) || (_stabsStringsOffsetStart != _stabsStringsOffsetEnd) ); +} + +template +void SymbolTableAtom::encode() +{ + uint32_t symbolIndex = 0; + + // make nlist entries for all local symbols + std::vector& localAtoms = this->_writer._localAtoms; + _locals.reserve(localAtoms.size()+this->_state.stabs.size()); + this->_writer._localSymbolsStartIndex = 0; + // make nlist entries for all debug notes + _stabsIndexStart = symbolIndex; + _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset(); + for (std::vector::const_iterator sit=this->_state.stabs.begin(); sit != this->_state.stabs.end(); ++sit) { + macho_nlist

entry; + entry.set_n_type(sit->type); + entry.set_n_sect(sectionIndexForStab(*sit)); + entry.set_n_desc(sit->desc); + entry.set_n_value(valueForStab(*sit)); + entry.set_n_strx(stringOffsetForStab(*sit, this->_writer._stringPoolAtom)); + _locals.push_back(entry); + ++symbolIndex; + } + _stabsIndexEnd = symbolIndex; + _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset(); + for (std::vector::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( this->addLocal(atom, this->_writer._stringPoolAtom) ) + this->_writer._atomToSymbolIndex[atom] = symbolIndex++; + } + this->_writer._localSymbolsCount = symbolIndex; + + + // make nlist entries for all global symbols + std::vector& globalAtoms = this->_writer._exportedAtoms; + _globals.reserve(globalAtoms.size()); + this->_writer._globalSymbolsStartIndex = symbolIndex; + for (std::vector::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) { + const ld::Atom* atom = *it; + this->addGlobal(atom, this->_writer._stringPoolAtom); + this->_writer._atomToSymbolIndex[atom] = symbolIndex++; + } + this->_writer._globalSymbolsCount = symbolIndex - this->_writer._globalSymbolsStartIndex; + + // make nlist entries for all undefined (imported) symbols + std::vector& importAtoms = this->_writer._importedAtoms; + _imports.reserve(importAtoms.size()); + this->_writer._importSymbolsStartIndex = symbolIndex; + for (std::vector::const_iterator it=importAtoms.begin(); it != importAtoms.end(); ++it) { + this->addImport(*it, this->_writer._stringPoolAtom); + this->_writer._atomToSymbolIndex[*it] = symbolIndex++; + } + this->_writer._importSymbolsCount = symbolIndex - this->_writer._importSymbolsStartIndex; +} + +template +uint64_t SymbolTableAtom::size() const +{ + return sizeof(macho_nlist

) * (_locals.size() + _globals.size() + _imports.size()); +} + +template +void SymbolTableAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(&buffer[this->_writer._localSymbolsStartIndex*sizeof(macho_nlist

)], &_locals[0], + this->_writer._localSymbolsCount*sizeof(macho_nlist

)); + memcpy(&buffer[this->_writer._globalSymbolsStartIndex*sizeof(macho_nlist

)], &_globals[0], + this->_writer._globalSymbolsCount*sizeof(macho_nlist

)); + memcpy(&buffer[this->_writer._importSymbolsStartIndex *sizeof(macho_nlist

)], &_imports[0], + this->_writer._importSymbolsCount*sizeof(macho_nlist

)); +} + + + + +class RelocationsAtomAbstract : public ClassicLinkEditAtom +{ +public: + RelocationsAtomAbstract(const Options& opts, ld::Internal& state, + OutputFile& writer, const ld::Section& sect, + unsigned int pointerSize) + : ClassicLinkEditAtom(opts, state, writer, sect, pointerSize) { } + + virtual void addPointerReloc(uint64_t addr, uint32_t symNum) = 0; + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) = 0; + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) = 0; + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) = 0; + virtual uint64_t relocBaseAddress(ld::Internal& state) = 0; + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) = 0; +protected: + uint32_t symbolIndex(const ld::Atom* atom) const; + +}; + + + +uint32_t RelocationsAtomAbstract::symbolIndex(const ld::Atom* atom) const +{ + std::map::iterator pos = this->_writer._atomToSymbolIndex.find(atom); + if ( pos != this->_writer._atomToSymbolIndex.end() ) + return pos->second; + fprintf(stderr, "_atomToSymbolIndex content:\n"); + for(std::map::iterator it = this->_writer._atomToSymbolIndex.begin(); it != this->_writer._atomToSymbolIndex.end(); ++it) { + fprintf(stderr, "%p(%s) => %d\n", it->first, it->first->name(), it->second); + } + throwf("internal error: atom not found in symbolIndex(%s)", atom->name()); +} + + +template +class LocalRelocationsAtom : public RelocationsAtomAbstract +{ +public: + LocalRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "local relocations"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode() {} + // overrides of RelocationsAtomAbstract + virtual void addPointerReloc(uint64_t addr, uint32_t symNum); + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) {} + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) {} + virtual uint64_t relocBaseAddress(ld::Internal& state); + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum); + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) { } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + std::vector > _relocs; + + static ld::Section _s_section; +}; + +template +ld::Section LocalRelocationsAtom::_s_section("__LINKEDIT", "__local_relocs", ld::Section::typeLinkEdit, true); + + +template <> +uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) +{ + if ( _options.outputKind() == Options::kKextBundle ) { + // for kext bundles the reloc base address starts at __TEXT segment + return _options.baseAddress(); + } + // for all other kinds, the x86_64 reloc base address starts at __DATA segment + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + return sect->address; + } + throw "__DATA segment not found"; +} + +template +uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) +{ + return _options.baseAddress(); +} + +template +void LocalRelocationsAtom::addPointerReloc(uint64_t addr, uint32_t symNum) +{ + macho_relocation_info

reloc; + reloc.set_r_address(addr); + reloc.set_r_symbolnum(symNum); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(false); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + _relocs.push_back(reloc); +} + +template +void LocalRelocationsAtom::addTextReloc(uint64_t addr, ld::Fixup::Kind kind, uint64_t targetAddr, uint32_t symNum) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + switch ( kind ) { + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( _options.outputSlidable() ) { + reloc1.set_r_address(addr); + reloc1.set_r_symbolnum(symNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + reloc2.set_r_address(targetAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + _relocs.push_back(reloc1); + _relocs.push_back(reloc2); + } + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCAbsHigh16: + if ( _options.outputSlidable() ) { + reloc1.set_r_address(addr); + reloc1.set_r_symbolnum(symNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); + reloc2.set_r_address(targetAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + _relocs.push_back(reloc1); + _relocs.push_back(reloc2); + } + break; + default: + break; + } +} + + +template +uint64_t LocalRelocationsAtom::size() const +{ + return _relocs.size() * sizeof(macho_relocation_info

); +} + +template +void LocalRelocationsAtom::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &_relocs[0], _relocs.size()*sizeof(macho_relocation_info

)); +} + + + + + + +template +class ExternalRelocationsAtom : public RelocationsAtomAbstract +{ +public: + ExternalRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "external relocations"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode() {} + // overrides of RelocationsAtomAbstract + virtual void addPointerReloc(uint64_t addr, uint32_t symNum) {} + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) {} + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*); + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*); + virtual uint64_t relocBaseAddress(ld::Internal& state); + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) { } + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct LocAndAtom { + LocAndAtom(uint64_t l, const ld::Atom* a) : loc(l), atom(a), symbolIndex(0) {} + + uint64_t loc; + const ld::Atom* atom; + uint32_t symbolIndex; + + bool operator<(const LocAndAtom& rhs) const { + // sort first by symbol number + if ( this->symbolIndex != rhs.symbolIndex ) + return (this->symbolIndex < rhs.symbolIndex); + // then sort all uses of the same symbol by address + return (this->loc < rhs.loc); + } + + }; + + static uint32_t pointerReloc(); + static uint32_t callReloc(); + + mutable std::vector _pointerLocations; + mutable std::vector _callSiteLocations; + + static ld::Section _s_section; +}; + +template +ld::Section ExternalRelocationsAtom::_s_section("__LINKEDIT", "__extrn_relocs", ld::Section::typeLinkEdit, true); + +template <> +uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) +{ + // for x86_64 the reloc base address starts at __DATA segment + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + return sect->address; + } + throw "__DATA segment not found"; +} + +template +uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) +{ + return 0; +} + +template +void ExternalRelocationsAtom::addExternalPointerReloc(uint64_t addr, const ld::Atom* target) +{ + _pointerLocations.push_back(LocAndAtom(addr, target)); +} + +template +void ExternalRelocationsAtom::addExternalCallSiteReloc(uint64_t addr, const ld::Atom* target) +{ + _callSiteLocations.push_back(LocAndAtom(addr, target)); +} + + +template +uint64_t ExternalRelocationsAtom::size() const +{ + if ( _options.outputKind() == Options::kStaticExecutable ) { + assert(_pointerLocations.size() == 0); + assert(_callSiteLocations.size() == 0); + } + return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info

); +} + +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return GENERIC_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return PPC_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X86_64_RELOC_UNSIGNED; } +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return PPC_RELOC_VANILLA; } + + +template <> uint32_t ExternalRelocationsAtom::callReloc() { return X86_64_RELOC_BRANCH; } +template <> uint32_t ExternalRelocationsAtom::callReloc() { return GENERIC_RELOC_VANILLA; } +template +uint32_t ExternalRelocationsAtom::callReloc() +{ + assert(0 && "external call relocs not implemented"); + return 0; +} + + +template +void ExternalRelocationsAtom::copyRawContent(uint8_t buffer[]) const +{ + macho_relocation_info

* r = (macho_relocation_info

*)buffer; + + // assign symbol index, now that symbol table is built + for (typename std::vector::iterator it = _pointerLocations.begin(); it != _pointerLocations.end(); ++it) { + it->symbolIndex = symbolIndex(it->atom); + } + std::sort(_pointerLocations.begin(), _pointerLocations.end()); + for (typename std::vector::const_iterator it = _pointerLocations.begin(); it != _pointerLocations.end(); ++it, ++r) { + r->set_r_address(it->loc); + r->set_r_symbolnum(it->symbolIndex); + r->set_r_pcrel(false); + r->set_r_length(); + r->set_r_extern(true); + r->set_r_type(this->pointerReloc()); + } + + for (typename std::vector::iterator it = _callSiteLocations.begin(); it != _callSiteLocations.end(); ++it) { + it->symbolIndex = symbolIndex(it->atom); + } + std::sort(_callSiteLocations.begin(), _callSiteLocations.end()); + for (typename std::vector::const_iterator it = _callSiteLocations.begin(); it != _callSiteLocations.end(); ++it, ++r) { + r->set_r_address(it->loc); + r->set_r_symbolnum(it->symbolIndex); + r->set_r_pcrel(true); + r->set_r_length(2); + r->set_r_extern(true); + r->set_r_type(this->callReloc()); + } +} + + +template +class SectionRelocationsAtom : public RelocationsAtomAbstract +{ +public: + SectionRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "section relocations"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode(); + // overrides of RelocationsAtomAbstract + virtual void addPointerReloc(uint64_t addr, uint32_t symNum) {} + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) {} + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) {} + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) {} + virtual uint64_t relocBaseAddress(ld::Internal& state) { return 0; } + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + + struct Entry { + ld::Fixup::Kind kind; + bool toTargetUsesExternalReloc; + bool fromTargetUsesExternalReloc; + const ld::Atom* inAtom; + uint32_t offsetInAtom; + const ld::Atom* toTarget; + uint64_t toAddend; + const ld::Atom* fromTarget; + uint64_t fromAddend; + }; + uint32_t sectSymNum(bool external, const ld::Atom* target); + void encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs); + + struct SectionAndEntries { + ld::Internal::FinalSection* sect; + std::vector entries; + std::vector > relocs; + }; + + std::vector _entriesBySection; + + static ld::Section _s_section; +}; + +template +ld::Section SectionRelocationsAtom::_s_section("__LINKEDIT", "__sect_relocs", ld::Section::typeLinkEdit, true); + + + + +template +uint64_t SectionRelocationsAtom::size() const +{ + uint32_t count = 0; + for(typename std::vector::const_iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + const SectionAndEntries& se = *it; + count += se.relocs.size(); + } + return count * sizeof(macho_relocation_info

); +} + +template +void SectionRelocationsAtom::copyRawContent(uint8_t buffer[]) const +{ + uint32_t offset = 0; + for(typename std::vector::const_iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + const SectionAndEntries& se = *it; + memcpy(&buffer[offset], &se.relocs[0], se.relocs.size()*sizeof(macho_relocation_info

)); + offset += (se.relocs.size() * sizeof(macho_relocation_info

)); + } +} + + +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86BranchPCRel8: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(0); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32_1: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_1); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32_2: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_2); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32_4: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_4); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32GOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + default: + assert(0 && "need to handle -r reloc"); + + } + +} + + + +template +uint32_t SectionRelocationsAtom::sectSymNum(bool external, const ld::Atom* target) +{ + if ( target->definition() == ld::Atom::definitionAbsolute ) + return R_ABS; + if ( external ) + return this->symbolIndex(target); // in external relocations, r_symbolnum field is symbol index + else + return target->machoSection(); // in non-extern relocations, r_symbolnum is mach-o section index of target +} + +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86BranchPCRel8: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(0); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(0); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(1); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(1); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); + 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); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(GENERIC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + default: + assert(0 && "need to handle -r reloc"); + + } +} + + +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM_THUMB_RELOC_BR22); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); + 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 + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(ARM_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + //unsigned int pcBaseOffset = entry.inAtom->isThumb() ? 4 : 8; + //if ( entry.fromAddend > pcBaseOffset ) + // sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend-pcBaseOffset); + //else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else { + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + } + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreARMLow16: + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreThumbLow16: + case ld::Fixup::kindStoreThumbHigh16: + { + int len = 0; + uint32_t otherHalf = 0; + uint32_t value = entry.toTarget->finalAddress()+entry.toAddend; + if ( entry.fromTarget != NULL ) + value -= (entry.fromTarget->finalAddress()+entry.fromAddend); + switch ( entry.kind ) { + case ld::Fixup::kindStoreARMLow16: + len = 0; + otherHalf = value >> 16; + break; + case ld::Fixup::kindStoreARMHigh16: + len = 1; + otherHalf = value & 0xFFFF; + break; + case ld::Fixup::kindStoreThumbLow16: + len = 2; + otherHalf = value >> 16; + break; + case ld::Fixup::kindStoreThumbHigh16: + len = 3; + otherHalf = value & 0xFFFF; + break; + default: + break; + } + if ( entry.fromTarget != NULL ) { + // this is a sect-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(len); + sreloc1->set_r_type(ARM_RELOC_HALF_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(len); + sreloc2->set_r_type(ARM_RELOC_PAIR); + sreloc2->set_r_address(otherHalf); + if ( entry.fromTarget == entry.inAtom ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // this is absolute address + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(len); + sreloc1->set_r_type(ARM_RELOC_HALF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + reloc2.set_r_address(otherHalf); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(len); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(len); + reloc1.set_r_extern(false); + reloc1.set_r_type(ARM_RELOC_HALF); + reloc2.set_r_address(otherHalf); // other half + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(len); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + } + } + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + + +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); + } + uint32_t toAddr; + uint32_t fromAddr; + + switch ( entry.kind ) { + + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStorePPCBranch14: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR14); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + 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); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + if ( external ) + reloc2.set_r_address(entry.toAddend >> 16); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HI16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HA16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicHigh16AddLow: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); + } + uint32_t toAddr; + uint32_t fromAddr; + + switch ( entry.kind ) { + + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStorePPCBranch14: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR14); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + 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); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreBigEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(3); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + 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(3); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(3); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + if ( external ) + reloc2.set_r_address(entry.toAddend >> 16); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HI16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HA16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicHigh16AddLow: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + +template +void SectionRelocationsAtom::addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) +{ + Entry entry; + entry.kind = kind; + entry.toTargetUsesExternalReloc = toTargetUsesExternalReloc; + entry.fromTargetUsesExternalReloc = fromTargetExternalReloc; + entry.inAtom = inAtom; + entry.offsetInAtom = offsetInAtom; + entry.toTarget = toTarget; + entry.toAddend = toAddend; + entry.fromTarget = fromTarget; + entry.fromAddend = fromAddend; + + static ld::Internal::FinalSection* lastSection = NULL; + static SectionAndEntries* lastSectionAndEntries = NULL; + + if ( sect != lastSection ) { + for(typename std::vector::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + if ( sect == it->sect ) { + lastSection = sect; + lastSectionAndEntries = &*it; + break; + } + } + if ( sect != lastSection ) { + SectionAndEntries tmp; + tmp.sect = sect; + _entriesBySection.push_back(tmp); + lastSection = sect; + lastSectionAndEntries = &_entriesBySection.back(); + } + } + lastSectionAndEntries->entries.push_back(entry); +} + +template +void SectionRelocationsAtom::encode() +{ + // convert each Entry record to one or two reloc records + for(typename std::vector::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + SectionAndEntries& se = *it; + for(typename std::vector::iterator eit=se.entries.begin(); eit != se.entries.end(); ++eit) { + encodeSectionReloc(se.sect, *eit, se.relocs); + } + } + + // update sections with start and count or relocs + uint32_t index = 0; + for(typename std::vector::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + SectionAndEntries& se = *it; + se.sect->relocStart = index; + se.sect->relocCount = se.relocs.size(); + index += se.sect->relocCount; + } + +} + + + +template +class IndirectSymbolTableAtom : public ClassicLinkEditAtom +{ +public: + IndirectSymbolTableAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : ClassicLinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "indirect symbol table"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode(); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + void encodeStubSection(ld::Internal::FinalSection* sect); + void encodeLazyPointerSection(ld::Internal::FinalSection* sect); + void encodeNonLazyPointerSection(ld::Internal::FinalSection* sect); + uint32_t symIndexOfStubAtom(const ld::Atom*); + uint32_t symIndexOfLazyPointerAtom(const ld::Atom*); + uint32_t symIndexOfNonLazyPointerAtom(const ld::Atom*); + uint32_t symbolIndex(const ld::Atom*); + bool kextBundlesDontHaveIndirectSymbolTable(); + + + std::vector _entries; + + static ld::Section _s_section; +}; + +template +ld::Section IndirectSymbolTableAtom::_s_section("__LINKEDIT", "__ind_sym_tab", ld::Section::typeLinkEdit, true); + + + + +template +uint32_t IndirectSymbolTableAtom::symbolIndex(const ld::Atom* atom) +{ + std::map::iterator pos = this->_writer._atomToSymbolIndex.find(atom); + if ( pos != this->_writer._atomToSymbolIndex.end() ) + return pos->second; + //fprintf(stderr, "_atomToSymbolIndex content:\n"); + //for(std::map::iterator it = this->_writer._atomToSymbolIndex.begin(); it != this->_writer._atomToSymbolIndex.end(); ++it) { + // fprintf(stderr, "%p(%s) => %d\n", it->first, it->first->name(), it->second); + //} + throwf("internal error: atom not found in symbolIndex(%s)", atom->name()); +} + +template +uint32_t IndirectSymbolTableAtom::symIndexOfStubAtom(const ld::Atom* stubAtom) +{ + for (ld::Fixup::iterator fit = stubAtom->fixupsBegin(); fit != stubAtom->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + assert((fit->u.target->contentType() == ld::Atom::typeLazyPointer) + || (fit->u.target->contentType() == ld::Atom::typeLazyDylibPointer)); + return symIndexOfLazyPointerAtom(fit->u.target); + } + } + throw "internal error: stub missing fixup to lazy pointer"; +} + + +template +uint32_t IndirectSymbolTableAtom::symIndexOfLazyPointerAtom(const ld::Atom* lpAtom) +{ + for (ld::Fixup::iterator fit = lpAtom->fixupsBegin(); fit != lpAtom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindLazyTarget ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + return symbolIndex(fit->u.target); + } + } + throw "internal error: lazy pointer missing fixupLazyTarget fixup"; +} + +template +uint32_t IndirectSymbolTableAtom::symIndexOfNonLazyPointerAtom(const ld::Atom* nlpAtom) +{ + //fprintf(stderr, "symIndexOfNonLazyPointerAtom(%p) %s\n", nlpAtom, nlpAtom->name()); + for (ld::Fixup::iterator fit = nlpAtom->fixupsBegin(); fit != nlpAtom->fixupsEnd(); ++fit) { + // non-lazy-pointer to a stripped symbol => no symbol index + if ( fit->clusterSize != ld::Fixup::k1of1 ) + return INDIRECT_SYMBOL_LOCAL; + const ld::Atom* target; + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = _state.indirectBindingTable[fit->u.bindingIndex]; + break; + 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: + if ( targetIsGlobal ) { + if ( _options.outputKind() == Options::kObjectFile ) { + // nlpointer to global symbol uses indirect symbol table in .o files + return symbolIndex(target); + } + else if ( target->combine() == ld::Atom::combineByName ) { + // dyld needs to bind nlpointer to global weak def + return symbolIndex(target); + } + else if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) { + // dyld needs to bind nlpointer to global def linked for flat namespace + return symbolIndex(target); + } + } + break; + case ld::Atom::definitionTentative: + case ld::Atom::definitionAbsolute: + if ( _options.outputKind() == Options::kObjectFile ) { + // tentative def in .o file always uses symbol index + return symbolIndex(target); + } + // dyld needs to bind nlpointer to global def linked for flat namespace + if ( targetIsGlobal && _options.nameSpace() != Options::kTwoLevelNameSpace ) + return symbolIndex(target); + break; + case ld::Atom::definitionProxy: + // dyld needs to bind nlpointer to something in another dylib + { + const ld::dylib::File* dylib = dynamic_cast(target->file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + } + return symbolIndex(target); + } + } + if ( nlpAtom->fixupsBegin() == nlpAtom->fixupsEnd() ) { + // no fixups means this is the ImageLoader cache slot + return INDIRECT_SYMBOL_ABS; + } + + // The magic index INDIRECT_SYMBOL_LOCAL tells dyld it should does not need to bind + // this non-lazy pointer. + return INDIRECT_SYMBOL_LOCAL; +} + + + +template +void IndirectSymbolTableAtom::encodeStubSection(ld::Internal::FinalSection* sect) +{ + sect->indirectSymTabStartIndex = _entries.size(); + sect->indirectSymTabElementSize = sect->atoms[0]->size(); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + _entries.push_back(symIndexOfStubAtom(*ait)); + } +} + +template +void IndirectSymbolTableAtom::encodeLazyPointerSection(ld::Internal::FinalSection* sect) +{ + sect->indirectSymTabStartIndex = _entries.size(); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + _entries.push_back(symIndexOfLazyPointerAtom(*ait)); + } +} + +template +void IndirectSymbolTableAtom::encodeNonLazyPointerSection(ld::Internal::FinalSection* sect) +{ + sect->indirectSymTabStartIndex = _entries.size(); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + _entries.push_back(symIndexOfNonLazyPointerAtom(*ait)); + } +} + +template +bool IndirectSymbolTableAtom::kextBundlesDontHaveIndirectSymbolTable() +{ + return true; +} + +template +void IndirectSymbolTableAtom::encode() +{ + // static executables should not have an indirect symbol table + if ( this->_options.outputKind() == Options::kStaticExecutable ) + return; + + // x86_64 kext bundles should not have an indirect symbol table + if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() ) + return; + + // find all special sections that need a range of the indirect symbol table section + for (std::vector::iterator sit = this->_state.sections.begin(); sit != this->_state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeStub: + case ld::Section::typeStubClose: + this->encodeStubSection(sect); + break; + case ld::Section::typeLazyPointerClose: + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyDylibPointer: + this->encodeLazyPointerSection(sect); + break; + case ld::Section::typeNonLazyPointer: + this->encodeNonLazyPointerSection(sect); + break; + default: + break; + } + } +} + +template +uint64_t IndirectSymbolTableAtom::size() const +{ + return _entries.size() * sizeof(uint32_t); +} + +template +void IndirectSymbolTableAtom::copyRawContent(uint8_t buffer[]) const +{ + uint32_t* array = (uint32_t*)buffer; + for(unsigned long i=0; i < _entries.size(); ++i) { + E::set32(array[i], _entries[i]); + } +} + + + + + + + + +} // namespace tool +} // namespace ld + +#endif // __LINKEDIT_CLASSIC_HPP__ diff --git a/ld64/src/ld/MachOReaderDylib.hpp b/ld64/src/ld/MachOReaderDylib.hpp deleted file mode 100644 index 5a6c553..0000000 --- a/ld64/src/ld/MachOReaderDylib.hpp +++ /dev/null @@ -1,1048 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_DYLIB_MACH_O__ -#define __OBJECT_FILE_DYLIB_MACH_O__ - -#include -#include -#include -#include - - -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "MachOTrie.hpp" -#include "ObjectFile.h" - -// -// -// To implement architecture xxx, you must write template specializations for the following method: -// Reader::validFile() -// -// - - - - -namespace mach_o { -namespace dylib { - - -// forward reference -template class Reader; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } -private: - const char* fName; -}; - - -// -// An ExportAtom has no content. It exists so that the linker can track which imported -// symbols came from which dynamic libraries. -// -template -class ExportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const { return fName; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual bool dontDeadStrip() const { return false; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return "._imports"; } - virtual Segment& getSegment() const { return fgImportSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const {} - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - typedef typename A::P P; - - ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal) - : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {} - virtual ~ExportAtom() {} - - ObjectFile::Reader& fOwner; - const char* fName; - uint32_t fOrdinal; - bool fWeakDefinition; - - static std::vector fgEmptyReferenceList; - static Segment fgImportSegment; -}; - -template -Segment ExportAtom::fgImportSegment("__LINKEDIT"); - -template -std::vector ExportAtom::fgEmptyReferenceList; - - - -class ImportReference : public ObjectFile::Reference -{ -public: - ImportReference(const char* name) - : fTarget(NULL), fTargetName(strdup(name)) {} - virtual ~ImportReference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget==NULL) ? ObjectFile::Reference::kUnboundByName : ObjectFile::Reference::kBoundByName; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return 0; } - virtual uint64_t getFixUpOffset() const { return 0; } - virtual const char* getTargetName() const { return fTargetName; } - virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } - virtual uint64_t getTargetOffset() const { return 0; } - virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } - virtual const char* getFromTargetName() const { return NULL; } - virtual uint64_t getFromTargetOffset() const { return 0; } - virtual void setTarget(ObjectFile::Atom& atom, uint64_t offset) { fTarget = &atom; } - virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } - virtual const char* getDescription() const { return "dylib import reference"; } - -private: - const ObjectFile::Atom* fTarget; - const char* fTargetName; -}; - - -// -// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace -// the imports of all flat dylibs are checked -// -template -class ImportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return "flat-imports"; } - virtual const char* getDisplayName() const { return "flat_namespace undefines"; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return false; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return "._imports"; } - virtual Segment& getSegment() const { return fgImportSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const {} - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - typedef typename A::P P; - - ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector& imports) - : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); } - virtual ~ImportAtom() {} - void makeReferences(std::vector& imports) { - for (std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { - fReferences.push_back(new ImportReference(*it)); - } - } - - - ObjectFile::Reader& fOwner; - uint32_t fOrdinal; - std::vector fReferences; - - static Segment fgImportSegment; -}; - -template -Segment ImportAtom::fgImportSegment("__LINKEDIT"); - - - - -// -// The reader for a dylib extracts all exported symbols names from the memory-mapped -// dylib, builds a hash table, then unmaps the file. This is an important memory -// savings for large dylibs. -// -template -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, bool executableOrDylib); - Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const LibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, - uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms(); - virtual std::vector* getJustInTimeAtomsFor(const char* name); - virtual std::vector* getStabs() { return NULL; } - virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; } - virtual const char* getInstallPath() { return fDylibInstallPath; } - virtual uint32_t getTimestamp() { return fDylibTimeStamp; } - virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } - virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } - virtual void processIndirectLibraries(DylibHander* handler); - virtual void setExplicitlyLinked() { fExplicitlyLinked = true; } - virtual bool explicitlyLinked() { return fExplicitlyLinked; } - virtual bool implicitlyLinked() { return fImplicitlyLinked; } - virtual bool providedExportAtom() { return fProvidedAtom; } - virtual const char* parentUmbrella() { return fParentUmbrella; } - virtual std::vector* getAllowableClients(); - virtual bool hasWeakExternals() { return fHasWeakExports; } - virtual bool deadStrippable() { return fDeadStrippable; } - virtual bool isLazyLoadedDylib() { return fLazyLoaded; } - - virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } - -protected: - - struct ReExportChain { ReExportChain* prev; Reader* reader; }; - - void assertNoReExportCycles(ReExportChain*); - -private: - typedef typename A::P P; - typedef typename A::P::E E; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; - typedef __gnu_cxx::hash_set, CStringEquals> NameSet; - typedef typename NameToAtomMap::iterator NameToAtomMapIterator; - - struct PathAndFlag { const char* path; bool reExport; }; - - bool isPublicLocation(const char* path); - void addSymbol(const char* name, bool weak); - void addDyldFastStub(); - void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, - const uint8_t* fileContent); - void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, - const macho_nlist

* symbolTable, const char* strings, - const uint8_t* fileContent); - - const char* fPath; - const char* fParentUmbrella; - std::vector fAllowableClients; - const char* fDylibInstallPath; - uint32_t fDylibTimeStamp; - uint32_t fDylibtCurrentVersion; - uint32_t fDylibCompatibilityVersion; - uint32_t fReExportedOrdinal; - std::vector fDependentLibraryPaths; - NameToAtomMap fAtoms; - NameSet fIgnoreExports; - bool fNoRexports; - bool fHasWeakExports; - bool fDeadStrippable; - const bool fLinkingFlat; - const bool fLinkingMainExecutable; - bool fExplictReExportFound; - bool fExplicitlyLinked; - bool fImplicitlyLinked; - bool fProvidedAtom; - bool fImplicitlyLinkPublicDylibs; - bool fLazyLoaded; - ObjectFile::Reader::ObjcConstraint fObjcContraint; - std::vector fReExportedChildren; - const ObjectFile::ReaderOptions::MacVersionMin fMacDeploymentVersionMin; - const ObjectFile::ReaderOptions::IPhoneVersionMin fIPhoneDeploymentVersionMin; - std::vector fFlatImports; - - static bool fgLogHashtable; - static std::vector fgEmptyAtomList; -}; - -template -std::vector Reader::fgEmptyAtomList; -template -bool Reader::fgLogHashtable = false; - - -template -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const LibraryOptions& dylibOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), - fDylibCompatibilityVersion(0), fReExportedOrdinal(ordinalBase), fLinkingFlat(options.fFlatNamespace), - fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), - fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), - fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), - fObjcContraint(ObjectFile::Reader::kObjcNone), - fMacDeploymentVersionMin(options.fMacVersionMin), - fIPhoneDeploymentVersionMin(options.fIPhoneVersionMin) -{ - // sanity check - if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) - throw "not a valid mach-o object file"; - - fPath = strdup(path); - - const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); - const macho_load_command

* const cmdsEnd = (macho_load_command

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

) + header->sizeofcmds()); - - // write out path for -whatsloaded option - if ( options.fLogAllFiles ) - printf("%s\n", path); - - if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) ) - warning("using -root_safe but linking against %s which is not root safe", path); - - if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) ) - warning("using -setuid_safe but linking against %s which is not setuid safe", path); - - // a "blank" stub has zero load commands - if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { - // no further processing needed - munmap((caddr_t)fileContent, fileLength); - return; - } - - - // optimize the case where we know there is no reason to look at indirect dylibs - fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); - fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); - fDeadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); - bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; - - // pass 1 builds list of all dependent libraries - const macho_load_command

* cmd = cmds; - if ( trackDependentLibraries ) { - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_REEXPORT_DYLIB: - fExplictReExportFound = true; - // fall into next case - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - PathAndFlag entry; - entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); - entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); - fDependentLibraryPaths.push_back(entry); - break; - } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - } - - // pass 2 determines re-export info - const macho_dysymtab_command

* dynamicInfo = NULL; - const macho_dyld_info_command

* dyldInfo = NULL; - const macho_nlist

* symbolTable = NULL; - const char* strings = NULL; - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); - strings = (char*)header + symtab->stroff(); - } - break; - case LC_DYSYMTAB: - dynamicInfo = (macho_dysymtab_command

*)cmd; - break; - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - dyldInfo = (macho_dyld_info_command

*)cmd; - break; - case LC_ID_DYLIB: - { - macho_dylib_command

* dylibID = (macho_dylib_command

*)cmd; - fDylibInstallPath = strdup(dylibID->name()); - fDylibTimeStamp = dylibID->timestamp(); - fDylibtCurrentVersion = dylibID->current_version(); - fDylibCompatibilityVersion = dylibID->compatibility_version(); - } - break; - case LC_SUB_UMBRELLA: - if ( trackDependentLibraries ) { - const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - const char* dylibName = it->path; - const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - it->reExport = true; - } - } - break; - case LC_SUB_LIBRARY: - if ( trackDependentLibraries) { - const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - const char* dylibName = it->path; - const char* lastSlash = strrchr(dylibName, '/'); - const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) - leafStart = dylibName; - const char* firstDot = strchr(leafStart, '.'); - int len = strlen(leafStart); - if ( firstDot != NULL ) - len = firstDot - leafStart; - if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - it->reExport = true; - } - } - break; - case LC_SUB_FRAMEWORK: - fParentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); - break; - case macho_segment_command

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

*)cmd)->segname(), "__OBJC") == 0 ) { - const macho_segment_command

* segment = (macho_segment_command

*)cmd; - const macho_section

* const sectionsStart = (macho_section

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

)); - const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( strcmp(sect->sectname(), "__image_info") == 0 ) { - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); - if ( (sect->size() >= 8) && (contents[0] == 0) ) { - uint32_t flags = E::get32(contents[1]); - if ( (flags & 4) == 4 ) - fObjcContraint = ObjectFile::Reader::kObjcGC; - else if ( (flags & 2) == 2 ) - fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - else - fObjcContraint = ObjectFile::Reader::kObjcRetainRelease; - } - else if ( sect->size() > 0 ) { - warning("can't parse __OBJC/__image_info section in %s", fPath); - } - } - } - } - } - - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - - // Process the rest of the commands here. - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SUB_CLIENT: - const char *temp = strdup(((macho_sub_client_command

*)cmd)->client()); - fAllowableClients.push_back(temp); - break; - } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); - } - - // validate minimal load commands - if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) - throwf("dylib %s missing LC_ID_DYLIB load command", path); - if ( symbolTable == NULL ) - throw "binary missing LC_SYMTAB load command"; - if ( dynamicInfo == NULL ) - throw "binary missing LC_DYSYMTAB load command"; - - // if linking flat and this is a flat dylib, create one atom that references all imported symbols - if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { - std::vector importNames; - importNames.reserve(dynamicInfo->nundefsym()); - const macho_nlist

* start = &symbolTable[dynamicInfo->iundefsym()]; - const macho_nlist

* end = &start[dynamicInfo->nundefsym()]; - for (const macho_nlist

* sym=start; sym < end; ++sym) { - importNames.push_back(&strings[sym->n_strx()]); - } - fFlatImports.push_back(new ImportAtom(*this, fReExportedOrdinal++, importNames)); - } - - // build hash table - if ( dyldInfo != NULL ) - buildExportHashTableFromExportInfo(dyldInfo, fileContent); - else - buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); - - // special case libSystem - if ( (fDylibInstallPath != NULL) && (strcmp(fDylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) - addDyldFastStub(); - - // unmap file - munmap((caddr_t)fileContent, fileLength); -} - - -template -void Reader::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, - const macho_nlist

* symbolTable, const char* strings, - const uint8_t* fileContent) -{ - if ( dynamicInfo->tocoff() == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->getPath()); - const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; - const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; - fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count - for (const macho_nlist

* sym=start; sym < end; ++sym) { - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0); - } - } - else { - int32_t count = dynamicInfo->ntoc(); - fAtoms.resize(count); // set initial bucket count - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->getPath()); - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); - for (int32_t i = 0; i < count; ++i) { - const uint32_t index = E::get32(toc[i].symbol_index); - const macho_nlist

* sym = &symbolTable[index]; - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0); - } - } -} - - -template -void Reader::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, - const uint8_t* fileContent) -{ - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->getPath()); - if ( dyldInfo->export_size() > 0 ) { - const uint8_t* start = fileContent + dyldInfo->export_off(); - const uint8_t* end = &start[dyldInfo->export_size()]; - std::vector list; - parseTrie(start, end, list); - for (std::vector::iterator it=list.begin(); it != list.end(); ++it) - this->addSymbol(it->name, it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); - } -} - - -template <> -void Reader::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false); -} - -template <> -void Reader::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false); -} - -// hack for bring up of iPhoneOS builds on SnowLeopard -template <> -void Reader::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false); -} - -template -void Reader::addDyldFastStub() -{ - // do nothing -} - -template -void Reader::addSymbol(const char* name, bool weakDef) -{ - //fprintf(stderr, "addSymbol() %s\n", name); - // symbols that start with $ld$ are meta-data to the static linker - // need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { - // $ld$ $ $ - const char* symAction = &name[4]; - const char* symCond = strchr(symAction, '$'); - if ( symCond != NULL ) { - if ( fMacDeploymentVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { - ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; - // ex: $ld$add$os10.6$_foo - if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { - switch ( symCond[6] - '0' ) { - case 0: - case 1: - symVersionCondition = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - symVersionCondition = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - symVersionCondition = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - symVersionCondition = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - symVersionCondition = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - symVersionCondition = ObjectFile::ReaderOptions::k10_6; - break; - } - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fMacDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); - } - } - } - else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); - } - } - else { - warning("bad symbol version: %s in dylib %s", name, this->getPath()); - } - } - else if ( fIPhoneDeploymentVersionMin != ObjectFile::ReaderOptions::kMinIPhoneVersionUnset ) { - ObjectFile::ReaderOptions::IPhoneVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinIPhoneVersionUnset; - // ex: $ld$add$os3.1$_foo - if ( (strncmp(symCond, "$os", 3) == 0) && isdigit(symCond[3]) && (symCond[4] == '.') ) { - if ( (symCond[3] == '2') && (symCond[5] == '0') ) - symVersionCondition = ObjectFile::ReaderOptions::k2_0; - else if ( (symCond[3] == '2') && (symCond[5] == '1') ) - symVersionCondition = ObjectFile::ReaderOptions::k2_1; - else if ( (symCond[3] == '2') && (symCond[5] >= '2') ) - symVersionCondition = ObjectFile::ReaderOptions::k2_2; - else if ( (symCond[3] == '3') && (symCond[5] == '0') ) - symVersionCondition = ObjectFile::ReaderOptions::k3_0; - else if ( (symCond[3] == '3') && (symCond[5] == '1') ) - symVersionCondition = ObjectFile::ReaderOptions::k3_1; - else if ( (symCond[3] == '3') && (symCond[5] >= '2') ) - symVersionCondition = ObjectFile::ReaderOptions::k3_2; - else if ( (symCond[3] >= '4') ) - symVersionCondition = ObjectFile::ReaderOptions::k4_0; - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fIPhoneDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); - } - } - } - else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); - } - } - else { - warning("bad symbol version: %s in dylib %s, symCond=%s", name, this->getPath(), symCond); - } - } - } - else { - warning("bad symbol condition: %s in dylib %s", name, this->getPath()); - } - } - - // add symbol as possible export if we are not supposed to ignore it - if ( fIgnoreExports.count(name) == 0 ) { - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = weakDef; - bucket.ordinal = fReExportedOrdinal++; - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[strdup(name)] = bucket; - } -} - - -template -std::vector& Reader::getAtoms() -{ - return fFlatImports; -} - - -template -std::vector* Reader::getJustInTimeAtomsFor(const char* name) -{ - // if supposed to ignore this export, then pretend I don't have it - if ( fIgnoreExports.count(name) != 0 ) - return NULL; - - std::vector* atoms = NULL; - NameToAtomMapIterator pos = fAtoms.find(name); - if ( pos != fAtoms.end() ) { - if ( pos->second.atom == NULL ) { - // instantiate atom and update hash table - pos->second.atom = new ExportAtom(*this, name, pos->second.weak, pos->second.ordinal); - fProvidedAtom = true; - if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); - } - // return a vector of one atom - atoms = new std::vector; - atoms->push_back(pos->second.atom); - } - else { - if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); - // look in children that I re-export - for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { - //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->getPath(), (*it)->getInstallPath()); - std::vector* childAtoms = (*it)->getJustInTimeAtomsFor(name); - if ( childAtoms != NULL ) { - // make a new atom that says this reader is the owner - bool isWeakDef = (childAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition); - // return a vector of one atom - ExportAtom* newAtom = new ExportAtom(*this, name, isWeakDef, fReExportedOrdinal++); - fProvidedAtom = true; - atoms = new std::vector; - atoms->push_back(newAtom); - delete childAtoms; - return atoms; - } - } - } - return atoms; -} - - - -template -bool Reader::isPublicLocation(const char* path) -{ - // -no_implicit_dylibs disables this optimization - if ( ! fImplicitlyLinkPublicDylibs ) - return false; - - // /usr/lib is a public location - if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) ) - return true; - - // /System/Library/Frameworks/ is a public location - if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { - const char* frameworkDot = strchr(&path[27], '.'); - // but only top level framework - // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true - // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false - if ( frameworkDot != NULL ) { - int frameworkNameLen = frameworkDot - &path[27]; - if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) - return true; - } - } - - return false; -} - -template -void Reader::processIndirectLibraries(DylibHander* handler) -{ - if ( fLinkingFlat ) { - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - handler->findDylib(it->path, this->getPath()); - } - } - else if ( fNoRexports ) { - // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do - } - else { - // two-level, might have re-exports - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - if ( it->reExport ) { - //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path); - // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); - if ( isPublicLocation(child->getInstallPath()) ) { - // promote this child to be automatically added as a direct dependent if this already is - if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,child->getInstallPath()) == 0) ) { - //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); - ((Reader*)child)->setImplicitlyLinked(); - } - else if ( child->explicitlyLinked() || child->implicitlyLinked() ) { - //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); - } - else { - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - else { - // add all child's symbols to me - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - else if ( !fExplictReExportFound ) { - // see if child contains LC_SUB_FRAMEWORK with my name - ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); - const char* parentUmbrellaName = ((Reader*)child)->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - const char* parentName = this->getPath(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { - // add all child's symbols to me - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - } - } - } - - // check for re-export cycles - ReExportChain chain; - chain.prev = NULL; - chain.reader = this; - this->assertNoReExportCycles(&chain); -} - -template -void Reader::assertNoReExportCycles(ReExportChain* prev) -{ - // recursively check my re-exported dylibs - ReExportChain chain; - chain.prev = prev; - chain.reader = this; - for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { - ObjectFile::Reader* child = *it; - // check child is not already in chain - for (ReExportChain* p = prev; p != NULL; p = p->prev) { - if ( p->reader == child ) { - throwf("cycle in dylib re-exports with %s", child->getPath()); - } - } - ((Reader*)(*it))->assertNoReExportCycles(&chain); - } -} - - -template -std::vector* Reader::getAllowableClients() -{ - std::vector* result = new std::vector; - for (typename std::vector::iterator it = fAllowableClients.begin(); - it != fAllowableClients.end(); - it++) { - result->push_back(*it); - } - return (fAllowableClients.size() != 0 ? result : NULL); -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_I386 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -}; // namespace dylib -}; // namespace mach_o - - -#endif // __OBJECT_FILE_DYLIB_MACH_O__ diff --git a/ld64/src/ld/MachOReaderRelocatable.hpp b/ld64/src/ld/MachOReaderRelocatable.hpp deleted file mode 100644 index 2aa7926..0000000 --- a/ld64/src/ld/MachOReaderRelocatable.hpp +++ /dev/null @@ -1,6125 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_MACH_O__ -#define __OBJECT_FILE_MACH_O__ - -#include -#include -#include -#include - -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ObjectFile.h" -#include "dwarf2.h" -#include "debugline.h" - -#include -#include -#include - -// -// -// To implement architecture xxx, you must write template specializations for the following six methods: -// Reader::validFile() -// Reader::validSectionType() -// Reader::addRelocReference() -// Reference::getDescription() -// -// - - - -extern __attribute__((noreturn)) void throwf(const char* format, ...); -extern void warning(const char* format, ...); - -namespace mach_o { -namespace relocatable { - - - -class ReferenceSorter -{ -public: - bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right) - { - return ( left->getFixUpOffset() < right->getFixUpOffset() ); - } -}; - - -// forward reference -template class Reader; - -struct AtomAndOffset -{ - AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {} - AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} - ObjectFile::Atom* atom; - uint32_t offset; -}; - - -template -class Reference : public ObjectFile::Reference -{ -public: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - - Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget); - Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget); - Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset); - - virtual ~Reference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const; - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const; - virtual uint8_t getKind() const { return (uint8_t)fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getName() : fToTargetName; } - virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } - virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); } - virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } - virtual const char* getFromTargetName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getName() : fFromTargetName; } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } - virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } - virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } - virtual void setFromTargetName(const char* name) { fFromTargetName = name; } - virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } - virtual const char* getDescription() const; - virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } - virtual bool isBranch() const; - virtual const char* getTargetDisplayName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName; } - virtual const char* getFromTargetDisplayName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getDisplayName() : fFromTargetName; } - - static bool fgForFinalLinkedImage; - -private: - pint_t fFixUpOffsetInSrc; - AtomAndOffset fToTarget; - AtomAndOffset fFromTarget; - const char* fToTargetName; - const char* fFromTargetName; - Kinds fKind; - -}; - - -template bool Reference::fgForFinalLinkedImage = true; - -template -Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) - : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL), - fKind(kind) -{ - // make reference a by-name unless: - // - the reference type is only used with direct references - // - the target is translation unit scoped - // - the target kind is not regular (is weak or tentative) - if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) - && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && (toTarget.atom != at.atom) ) { - fToTargetName = toTarget.atom->getName(); - //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName()); - fToTarget.atom = NULL; - } - ((class BaseAtom*)at.atom)->addReference(this); - //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset); -} - -template -Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget) - : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget), - fToTargetName(NULL), fFromTargetName(NULL), fKind(kind) -{ - // make reference a by-name where needed - if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) - && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && (toTarget.atom != at.atom) ) { - fToTargetName = toTarget.atom->getName(); - fToTarget.atom = NULL; - } - ((class BaseAtom*)at.atom)->addReference(this); - //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind, - // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset); -} - -template -Reference::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset) - : fFixUpOffsetInSrc(at.offset), - fToTargetName(toName), fFromTargetName(NULL), fKind(kind) -{ - fToTarget.offset = toOffset; - ((class BaseAtom*)at.atom)->addReference(this); -} - -template -ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const -{ - if ( fgForFinalLinkedImage ) { - if ( (fKind == A::kDtraceProbe) || (fKind == A::kDtraceProbeSite) || (fKind == A::kDtraceIsEnabledSite) || (fKind == A::kDtraceTypeReference) ) - return ObjectFile::Reference::kDontBind; - } - if ( fToTarget.atom == NULL ) - return ObjectFile::Reference::kUnboundByName; - if ( fToTargetName == NULL ) - return ObjectFile::Reference::kBoundDirectly; - else - return ObjectFile::Reference::kBoundByName; -} - -template -ObjectFile::Reference::TargetBinding Reference::getFromTargetBinding() const -{ - if ( fFromTarget.atom == NULL ) { - if ( fFromTargetName == NULL ) - return ObjectFile::Reference::kDontBind; - else - return ObjectFile::Reference::kUnboundByName; - } - else { - if ( fFromTargetName == NULL ) - return ObjectFile::Reference::kBoundDirectly; - else - return ObjectFile::Reference::kBoundByName; - } -} - - - -template -class Segment : public ObjectFile::Segment -{ -public: - Segment(const macho_section* sect); - virtual const char* getName() const { return fSection->segname(); } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } -private: - const macho_section* fSection; - bool fWritable; - bool fExecutable; -}; - -template -Segment::Segment(const macho_section* sect) - : fSection(sect), fWritable(true), fExecutable(false) -{ - if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { - fWritable = false; - fExecutable = true; - } - else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { - fWritable = true; - fExecutable = true; - } -} - - -class DataSegment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const { return "__DATA"; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return true; } - virtual bool isContentExecutable() const { return false; } - - static DataSegment fgSingleton; -}; - -DataSegment DataSegment::fgSingleton; - -class LinkEditSegment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const { return "__LINKEDIT"; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } - - static LinkEditSegment fgSingleton; -}; - -LinkEditSegment LinkEditSegment::fgSingleton; - -class BaseAtom : public ObjectFile::Atom -{ -public: - BaseAtom() : fStabsStartIndex(0), fStabsCount(0), fHasCompactUnwindInfo(false) {} - - virtual void setSize(uint64_t size) = 0; - virtual void addReference(ObjectFile::Reference* ref) = 0; - virtual void sortReferences() = 0; - virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; - virtual const ObjectFile::ReaderOptions& getOptions() const = 0; - virtual uint64_t getObjectAddress() const = 0; - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual void setOrdinal(uint32_t value) { fOrdinal = value; } - virtual const void* getSectionRecord() const = 0; - virtual unsigned int getSectionIndex() const = 0; - virtual bool isAlias() const { return false; } - virtual uint8_t getLSDAReferenceKind() const { return 0; } - virtual uint8_t getPersonalityReferenceKind() const { return 0; } - virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress) { return 0; } - virtual ObjectFile::UnwindInfo::iterator beginUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[0] : NULL; } - virtual ObjectFile::UnwindInfo::iterator endUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[1] : NULL; } - virtual ObjectFile::Reference* getLSDA(); - virtual ObjectFile::Reference* getFDE(); - virtual Atom* getPersonalityPointer(); - virtual void setCompactUnwindEncoding(uint64_t ehAtomAddress); - - uint32_t fStabsStartIndex; - uint32_t fStabsCount; - uint32_t fOrdinal; - ObjectFile::UnwindInfo fSingleUnwindInfo[1]; - bool fHasCompactUnwindInfo; -}; - - -ObjectFile::Reference* BaseAtom::getLSDA() -{ - const uint8_t groupKind = this->getLSDAReferenceKind(); - const std::vector& refs = this->getReferences(); - for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kLSDAType) ) { - return ref; - } - } - return NULL; -} - -ObjectFile::Reference* BaseAtom::getFDE() -{ - const uint8_t groupKind = this->getLSDAReferenceKind(); - const std::vector& refs = this->getReferences(); - for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kCFIType) ) { - return ref; - } - } - return NULL; -} - -ObjectFile::Atom* BaseAtom::getPersonalityPointer() -{ - const uint8_t personalityKind = this->getPersonalityReferenceKind(); - const std::vector& refs = this->getReferences(); - for (std::vector::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( ref->getKind() == personalityKind ) { - if ( strcmp(ref->getTarget().getSectionName(), "__nl_symbol_ptr") == 0 ) - return &ref->getTarget(); - if ( strcmp(ref->getTarget().getSectionName(), "__pointers") == 0 ) - return &ref->getTarget(); - } - } - return NULL; -} - - -void BaseAtom::setCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - fSingleUnwindInfo[0].unwindInfo = this->getCompactUnwindEncoding(ehAtomAddress); - fHasCompactUnwindInfo = true; -} - - -class BaseAtomSorter -{ -public: - bool operator()(const class BaseAtom* left, const class BaseAtom* right) { - if ( left == right ) - return false; - uint64_t leftAddr = left->getObjectAddress(); - uint64_t rightAddr = right->getObjectAddress(); - if ( leftAddr < rightAddr ) { - return true; - } - else if ( leftAddr > rightAddr ) { - return false; - } - else { - // if they have same address, one might be the end of a section and the other the start of the next section - const void* leftSection = left->getSectionRecord(); - const void* rightSection = right->getSectionRecord(); - if ( leftSection != rightSection ) { - return ( leftSection < rightSection ); - } - // if they have same address and section, one might be an alias - bool leftAlias = left->isAlias(); - bool rightAlias = right->isAlias(); - if ( leftAlias && rightAlias ) { - // sort multiple aliases for same address first by scope - ObjectFile::Atom::Scope leftScope = left->getScope(); - ObjectFile::Atom::Scope rightScope = right->getScope(); - if ( leftScope != rightScope ) { - return ( leftScope < rightScope ); - } - // sort multiple aliases for same address then by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - } - else if ( leftAlias ) { - return true; - } - else if ( rightAlias ) { - return false; - } - // one might be a section start or end label - switch ( left->getContentType() ) { - case ObjectFile::Atom::kSectionStart: - return true; - case ObjectFile::Atom::kSectionEnd: - return false; - default: - break; - } - switch ( right->getContentType() ) { - case ObjectFile::Atom::kSectionStart: - return false; - case ObjectFile::Atom::kSectionEnd: - return true; - default: - break; - } - // they could be tentative defintions - switch ( left->getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - // sort tentative definitions by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - case ObjectFile::Atom::kAbsoluteSymbol: - // sort absolute symbols with same address by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - default: - // hack for rdar://problem/5102873 - if ( !left->isZeroFill() || !right->isZeroFill() ) - warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); - break; - } - } - return false; - } -}; - - -// -// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry -// pointing to it. A C function or global variable is represented by one of these atoms. -// -// -template -class SymbolAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) - ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } - virtual bool dontDeadStrip() const; - virtual bool isZeroFill() const; - virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } - virtual uint64_t getSize() const { return fSize; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual Segment& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector* getLineInfo() const { return (std::vector*)&fLineInfo; } - virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size); - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return fAddress; } - virtual const void* getSectionRecord() const { return (const void*)fSection; } - virtual unsigned int getSectionIndex() const { return 1 + (fSection - fOwner.fSectionsStart); } - virtual uint8_t getLSDAReferenceKind() const; - virtual uint8_t getPersonalityReferenceKind() const; - virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - typedef typename std::vector*> ReferenceVector; - typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser - typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser - friend class Reader; - - SymbolAtom(Reader&, const macho_nlist

*, const macho_section

*); - virtual ~SymbolAtom() {} - - Reader& fOwner; - const macho_nlist

* fSymbol; - pint_t fAddress; - pint_t fSize; - const macho_section

* fSection; - Segment* fSegment; - ReferenceVector fReferences; - std::vector fLineInfo; - ObjectFile::Atom::Scope fScope; - SymbolTableInclusion fSymbolTableInclusion; - ObjectFile::Atom::ContentType fType; - ObjectFile::Alignment fAlignment; -}; - - -template -SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const macho_section

* section) - : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fType(ObjectFile::Atom::kUnclassifiedType), fAlignment(0) -{ - fSingleUnwindInfo[0].startOffset = 0; - fSingleUnwindInfo[0].unwindInfo = 0; - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( (type & N_TYPE) == N_SECT ) { - // real definition - fSegment = new Segment(fSection); - fAddress = fSymbol->n_value(); - pint_t sectionStartAddr = section->addr(); - pint_t sectionEndAddr = section->addr()+section->size(); - if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) { - throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX", - this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(), - (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) ); - } - } - else { - warning("unknown symbol type: %d", type); - } - - //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress); - // support for .o files built with old ld64 - if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) { - const char* name = this->getName(); - const int nameLen = strlen(name); - if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) { - // switch symbol to point at name that does not have trailing $stub - char correctName[nameLen]; - strncpy(correctName, name, nameLen-5); - correctName[nameLen-5] = '\0'; - const macho_nlist

* symbolsStart = fOwner.fSymbols; - const macho_nlist

* symbolsEnd = &symbolsStart[fOwner.fSymbolCount]; - for(const macho_nlist

* s = symbolsStart; s < symbolsEnd; ++s) { - if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) { - fSymbol = s; - break; - } - } - } - } - // support for labeled stubs - switch ( section->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - setSize(section->reserved2()); - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - setSize(sizeof(pint_t)); - break; - case S_4BYTE_LITERALS: - setSize(4); - break; - case S_8BYTE_LITERALS: - setSize(8); - break; - case S_16BYTE_LITERALS: - setSize(16); - break; - case S_CSTRING_LITERALS: - setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); - fType = ObjectFile::Atom::kCStringType; - break; - case S_REGULAR: - case S_ZEROFILL: - case S_COALESCED: - // size calculate later after next atom is found - break; - } - - // compute alignment - fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); - - // compute whether this atom needs to be in symbol table - if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; - } - else if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) { - // labels beginning with a lowercase ell are automatically removed in final linked images - // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - } - else { - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - - // work around malformed icc generated .o files - // if section starts with a symbol and that symbol address does not match section alignment, then force it to - if ( (section->addr() == fAddress) && (fAlignment.modulus != 0) ) - fAlignment.modulus = 0; -} - - -template -bool SymbolAtom::dontDeadStrip() const -{ - // the symbol can have a no-dead-strip bit - if ( (fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 ) - return true; - // or the section can have a no-dead-strip bit - return ( fSection->flags() & S_ATTR_NO_DEAD_STRIP ); -} - - -template -const char* SymbolAtom::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage ) { - if ( strcmp(fSection->sectname(), "__textcoal_nt") == 0 ) - return "__text"; - else if ( strcmp(fSection->sectname(), "__const_coal") == 0 ) - return "__const"; - else if ( strcmp(fSection->sectname(), "__datacoal_nt") == 0 ) - return "__data"; - else if ( fOwner.fOptions.fAutoOrderInitializers && (strcmp(fSection->sectname(), "__StaticInit") == 0) ) - return "__text"; - else { - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - return "__const"; - } - } - } - - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -template -ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const -{ - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return ref->getTarget(); - } - return *((ObjectFile::Atom*)NULL); -} - -template -bool SymbolAtom::isZeroFill() const -{ - return ( ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL) && fOwner.fOptions.fOptimizeZeroFill ); -} - - -class Beyond -{ -public: - Beyond(uint64_t offset) : fOffset(offset) {} - bool operator()(ObjectFile::Reference* ref) const { - return ( ref->getFixUpOffset() >= fOffset ); - } -private: - uint64_t fOffset; -}; - - -template -void SymbolAtom::setSize(uint64_t size) -{ - // when resizing, any references beyond the new size are tossed - if ( (fSize != 0) && (fReferences.size() > 0) ) - fReferences.erase(std::remove_if(fReferences.begin(), fReferences.end(), Beyond(size)), fReferences.end()); - // set new size - fSize = size; -} - -template -void SymbolAtom::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - - - - -// -// A SymbolAliasAtom represents an alternate name for a SymbolAtom -// -// -template -class SymbolAliasAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fAliasOf.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); } - virtual bool dontDeadStrip() const { return fDontDeadStrip; } - virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); } - virtual bool isThumb() const { return fAliasOf.isThumb(); } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return fAliasOf.getSectionName(); } - virtual Segment& getSegment() const { return (Segment&)fAliasOf.getSegment(); } - virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return fAliasOf.getAlignment(); } - virtual void copyRawContent(uint8_t buffer[]) const {} - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fAliasOf.getOptions(); } - virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } - virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } - virtual unsigned int getSectionIndex() const { return fAliasOf.getSectionIndex(); } - virtual bool isAlias() const { return true; } - -protected: - typedef typename A::P P; - typedef typename std::vector*> ReferenceVector; - typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser - typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser - friend class Reader; - - SymbolAliasAtom(const char* name, const macho_nlist

*, const BaseAtom& ); - virtual ~SymbolAliasAtom() {} - - const char* fName; - const BaseAtom& fAliasOf; - ObjectFile::Atom::Scope fScope; - bool fDontDeadStrip; - ReferenceVector fReferences; -}; - - -template -SymbolAliasAtom::SymbolAliasAtom(const char* name, const macho_nlist

* symbol, const BaseAtom& aliasOf) - : fName(name), fAliasOf(aliasOf) -{ - //fprintf(stderr, "SymbolAliasAtom(%p) %s\n", this, name); - if ( symbol != NULL ) { - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - fDontDeadStrip = ((symbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); - } - else { - // aliases defined on the command line are initially global scope - fScope = ObjectFile::Atom::scopeGlobal; - fDontDeadStrip = false; - } - // add follow-on reference to real atom - new Reference(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf)); -} - - -// -// A TentativeAtom represents a C "common" or "tentative" defintion of data. -// For instance, "int foo;" is neither a declaration or a definition and -// is represented by a TentativeAtom. -// -template -class TentativeAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } - virtual bool isZeroFill() const { return fOwner.fOptions.fOptimizeZeroFill; } - virtual bool isThumb() const { return false; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) - ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } - virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } - virtual uint64_t getSize() const { return fSymbol->n_value(); } - virtual std::vector& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } - virtual const void* getSectionRecord() const { return NULL; } - virtual unsigned int getSectionIndex() const { return 0; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader; - - TentativeAtom(Reader&, const macho_nlist

*); - virtual ~TentativeAtom() {} - - Reader& fOwner; - const macho_nlist

* fSymbol; - ObjectFile::Atom::Scope fScope; - static std::vector fgNoReferences; -}; - -template -std::vector TentativeAtom::fgNoReferences; - -template -TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) - : fOwner(owner), fSymbol(symbol) -{ - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { - // tentative definition - } - else { - warning("unknown symbol type: %d", type); - } - //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName()); -} - - -template -ObjectFile::Alignment TentativeAtom::getAlignment() const -{ - uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc()); - if ( alignment == 0 ) { - // common symbols align to their size - // that is, a 4-byte common aligns to 4-bytes - // if this size is not a power of two, - // then round up to the next power of two - uint64_t size = this->getSize(); - alignment = 63 - (uint8_t)__builtin_clzll(size); - if ( size != (1ULL << alignment) ) - ++alignment; - } - // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignment < 12 ) - return ObjectFile::Alignment(alignment); - else - return ObjectFile::Alignment(12); -} - -template -const char* TentativeAtom::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal ) - return "__common"; - else - return "._tentdef"; -} - - -template -void TentativeAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - -// -// An AnonymousAtom represents compiler generated data that has no name. -// For instance, a literal C-string or a 64-bit floating point constant -// is represented by an AnonymousAtom. -// -template -class AnonymousAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fSynthesizedName; } - virtual const char* getDisplayName() const; - virtual ObjectFile::Atom::Scope getScope() const; - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } - virtual bool dontDeadStrip() const { return fDontDeadStrip; } - virtual bool isZeroFill() const; - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return fSize; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual Segment& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { fSize = size; } - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info); - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return fAddress; } - virtual const void* getSectionRecord() const { return (const void*)fSection; } - virtual unsigned int getSectionIndex() const { return fSectionIndex; } - BaseAtom* redirectTo() { return fRedirect; } - bool isWeakImportStub() { return fWeakImportStub; } - void resolveName(); - virtual uint8_t getLSDAReferenceKind() const; - virtual uint8_t getPersonalityReferenceKind() const; - virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - typedef typename std::vector*> ReferenceVector; - typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser - typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser - friend class Reader; - - AnonymousAtom(Reader&, const macho_section

*, pint_t addr, pint_t size); - virtual ~AnonymousAtom() {} - static bool cstringsHaveLabels(); - - Reader& fOwner; - const char* fSynthesizedName; - const char* fDisplayName; - const macho_section

* fSection; - pint_t fAddress; - pint_t fSize; - Segment* fSegment; - ReferenceVector fReferences; - BaseAtom* fRedirect; - bool fDontDeadStrip; - bool fWeakImportStub; - ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; - ObjectFile::Atom::Scope fScope; - ObjectFile::Atom::DefinitionKind fKind; - ObjectFile::Atom::ContentType fType; - unsigned int fSectionIndex; -}; - -template -AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, pint_t addr, pint_t size) - : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), - fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), - fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition), - fType(ObjectFile::Atom::kUnclassifiedType), fSectionIndex(1 + (section - owner.fSectionsStart)) -{ - fSegment = new Segment(fSection); - fRedirect = this; - uint8_t type = fSection->flags() & SECTION_TYPE; - //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath()); - switch ( type ) { - case S_ZEROFILL: - { - asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr); - } - break; - case S_COALESCED: - case S_REGULAR: - if ( section == owner.fehFrameSection ) { - if ( fSize == 1 ) { - // is CIE - fSize = 0; - fDontDeadStrip = false; - if ( fOwner.fOptions.fForFinalLinkedImage ) - fSynthesizedName = "CIE"; - else - fSynthesizedName = "EH_frame1"; - } - else { - // is FDE - fSynthesizedName = ".eh_PENDING"; - fDontDeadStrip = false; - owner.fAtomsPendingAName.push_back(this); - } - fType = ObjectFile::Atom::kCFIType; - // FDEs and CIEs don't need to be in symbol table of final linked images - if ( !fOwner.fOptions.fNoEHLabels ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - else if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { - // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only - fSynthesizedName = ".objc_class_name_PENDING"; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - if ( fOwner.fOptions.fForFinalLinkedImage ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - else - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAsAbsolute; - fScope = ObjectFile::Atom::scopeGlobal; - } - else if ( strcmp(fSection->sectname(), "__cstring") == 0 ) { - // handle .o files created by old ld64 -r that are missing cstring section type - const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - asprintf((char**)&fSynthesizedName, "cstring=%s", str); - } - else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) { - fSynthesizedName = "cfstring-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - fDontDeadStrip = false; - fKind = ObjectFile::Atom::kWeakDefinition; - } - else if ( (fSection->flags() & S_ATTR_SOME_INSTRUCTIONS) != 0 ) { - fDontDeadStrip = false; - asprintf((char**)&fSynthesizedName, "anon-func-0x%X", addr); - } - else if ( strncmp(fSection->sectname(), "__gcc_except_tab",16) == 0 ) { - fType = ObjectFile::Atom::kLSDAType; - fDontDeadStrip = false; - fSynthesizedName = ".lsda_PENDING"; - owner.fAtomsPendingAName.push_back(this); - if ( !fOwner.fOptions.fNoEHLabels ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - fSynthesizedName = "objc-class-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - fKind = ObjectFile::Atom::kWeakDefinition; - } - else if ( section == owner.fUTF16Section ) { - if ( fOwner.fOptions.fForFinalLinkedImage ) { - fDontDeadStrip = false; - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - char* name = new char[16+5*size]; - strcpy(name, "utf16-string="); - char* s = &name[13]; - const uint16_t* words = (uint16_t*)((char*)(owner.fHeader) + section->offset() + addr - section->addr()); - unsigned int wordCount = size/2; - bool needSeperator = false; - for(unsigned int i=0; i < wordCount; ++i) { - if ( needSeperator ) - strcpy(s++, "."); - sprintf(s, "%04X", E::get32(words[i])); - s += 4; - needSeperator = true; - } - fSynthesizedName = name; - } - else { - asprintf((char**)&fSynthesizedName, "lutf16-0x%X", addr); - } - } - break; - case S_CSTRING_LITERALS: - { - const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - if ( (strcmp(fSection->sectname(), "__cstring") == 0) && (strcmp(section->segname(), "__TEXT") == 0) ) - asprintf((char**)&fSynthesizedName, "cstring=%s", str); - else - asprintf((char**)&fSynthesizedName, "cstring%s%s=%s", fSection->segname(), fSection->sectname(), str); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fType = ObjectFile::Atom::kCStringType; - fDontDeadStrip = false; - if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - break; - case S_4BYTE_LITERALS: - { - uint32_t value = E::get32(*(uint32_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - asprintf((char**)&fSynthesizedName, "4-byte-literal=0x%08X", value); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_8BYTE_LITERALS: - { - uint64_t value = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - asprintf((char**)&fSynthesizedName, "8-byte-literal=0x%016llX", value); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_16BYTE_LITERALS: - { - uint64_t value1 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - uint64_t value2 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 8 - section->addr())); - asprintf((char**)&fSynthesizedName, "16-byte-literal=0x%016llX,%016llX", value1, value2); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_LITERAL_POINTERS: - { - //uint32_t literalNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - //const char* str = (char*)(owner.fHeader) + section->offset() + literalNameAddr - section->addr(); - //asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", section->segname(), section->sectname(), str); - fSynthesizedName = "literal-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - } - break; - case S_MOD_INIT_FUNC_POINTERS: - asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); - break; - case S_MOD_TERM_FUNC_POINTERS: - asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); - break; - case S_SYMBOL_STUBS: - { - uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2(); - index += fSection->reserved1(); - uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); - const macho_nlist

* sym = &fOwner.fSymbols[symbolIndex]; - uint32_t strOffset = sym->n_strx(); - // want name to not have $stub suffix, this is what automatic stub generation expects - fSynthesizedName = &fOwner.fStrings[strOffset]; - // check for weak import - fWeakImportStub = fOwner.isWeakImportSymbol(sym); - // sometimes the compiler gets confused and generates a stub to a static function - // if so, we should redirect any call to the stub to be calls to the real static function atom - if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) { - BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); - if ( staticAtom != NULL ) - fRedirect = staticAtom; - } - fKind = ObjectFile::Atom::kWeakDefinition; - // might be a spurious stub for a static function, make stub static too - if ( (sym->n_type() & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else - fScope = ObjectFile::Atom::scopeLinkageUnit; - } - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - { - // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when - // generating the new compressed LINKEDIT format - if ( (type == S_NON_LAZY_SYMBOL_POINTERS) && fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { - macho_section

* dummySection = new macho_section

(*fSection); - dummySection->set_segname("__DATA"); - dummySection->set_sectname("__nl_symbol_ptr"); - fSection = dummySection; - fSegment = new Segment(fSection); - } - - fDontDeadStrip = false; - fScope = ObjectFile::Atom::scopeLinkageUnit; - uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); - index += fSection->reserved1(); - uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); - if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { - // Silly codegen with non-lazy pointer to a local symbol - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fOwner.fHeader)+fileOffset))); - // All atoms not created yet, so we need to scan symbol table - const macho_nlist

* closestSym = NULL; - const macho_nlist

* end = &fOwner.fSymbols[fOwner.fSymbolCount]; - for (const macho_nlist

* sym = fOwner.fSymbols; sym < end; ++sym) { - if ( ((sym->n_type() & N_TYPE) == N_SECT) - && ((sym->n_type() & N_STAB) == 0) ) { - if ( sym->n_value() == nonLazyPtrValue ) { - const char* name = &fOwner.fStrings[sym->n_strx()]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - strcat(str, "$non_lazy_ptr"); - fSynthesizedName = str; - // add direct reference to target later, because its atom may not be constructed yet - fOwner.fLocalNonLazys.push_back(this); - fScope = ObjectFile::Atom::scopeTranslationUnit; - fType = ObjectFile::Atom::kNonLazyPointer; - return; - } - else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) { - closestSym = sym; - } - } - } - // add direct reference to target later, because its atom may not be constructed yet - if ( closestSym != NULL ) { - const char* name = &fOwner.fStrings[closestSym->n_strx()]; - char* str; - asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value()); - fSynthesizedName = str; - } - else { - fSynthesizedName = "$interior$non_lazy_ptr"; - } - fScope = ObjectFile::Atom::scopeTranslationUnit; - fOwner.fLocalNonLazys.push_back(this); - fType = ObjectFile::Atom::kNonLazyPointer; - return; - } - const macho_nlist

* targetSymbol = &fOwner.fSymbols[symbolIndex]; - const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - if ( type == S_LAZY_SYMBOL_POINTERS ) { - strcat(str, "$lazy_ptr"); - fType = ObjectFile::Atom::kLazyPointer; - } - else { - strcat(str, "$non_lazy_ptr"); - fType = ObjectFile::Atom::kNonLazyPointer; - } - fSynthesizedName = str; - - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) - fKind = ObjectFile::Atom::kWeakDefinition; - - if ( (targetSymbol->n_type() & N_EXT) == 0 ) { - // target is translation unit scoped, so add direct reference to target - //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); - new Reference(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value())); - } - else { - if ( fOwner.isWeakImportSymbol(targetSymbol) ) - new Reference(A::kPointerWeakImport, AtomAndOffset(this), name, 0); - else - new Reference(A::kPointer, AtomAndOffset(this), name, 0); - } - } - break; - default: - throwf("section type %d not supported with address=0x%08llX", type, (uint64_t)addr); - } - //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); -} - -// x86_64 uses L labels on cstrings to allow relocs with addends -template <> bool AnonymousAtom::cstringsHaveLabels() { return true; } -template bool AnonymousAtom::cstringsHaveLabels() { return false; } - -template -void AnonymousAtom::addLineInfo(const ObjectFile::LineInfo& info) -{ - // don't warn if line table has entries for stubs - if ( (fSection->flags() & SECTION_TYPE) != S_SYMBOL_STUBS ) - warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); -} - -template -void AnonymousAtom::resolveName() -{ - if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) { - std::vector& references = this->getReferences(); - // references are not yet sorted, so scan the vector - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* superStr = (*rit)->getTargetName(); - if ( strncmp(superStr, "cstring", 7) == 0 ) { - const char* superClassName; - asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]); - new Reference(A::kNoFixUp, AtomAndOffset(this), superClassName, 0); - } - break; - } - } - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* classStr = (*rit)->getTargetName(); - if ( strncmp(classStr, "cstring", 7) == 0 ) { - asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]); - } - break; - } - } - } - else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) { - std::vector& references = this->getReferences(); - if ( references.size() < 1 ) - throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname()); - ObjectFile::Reference* ref = references[0]; - const char* str = ref->getTargetName(); - if ( strncmp(str, "cstring", 7) == 0 ) { - asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]); - } - } - else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - // references are not yet sorted, so scan the vector - std::vector& references = this->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* superStr = (*rit)->getTargetName(); - if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) { - asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]); - } - else if ( (superStr != NULL) && (strncmp(superStr, "utf16-string=", 13) == 0) ) { - asprintf((char**)&fSynthesizedName, "cfstring-utf16=%s", &superStr[13]); - } - else { - // compiled with -fwritable-strings or a non-ASCII string - fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable - fScope = ObjectFile::Atom::scopeTranslationUnit; - fSynthesizedName = "cfstring-not-coalesable"; - if ( (*rit)->getTargetOffset() != 0 ) - warning("-fwritable-strings not compatible with literal CF/NSString in %s", fOwner.getPath()); - } - break; - } - } - } - else if ( fSection == fOwner.fehFrameSection ) { - // give name to FDE - ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromFDEAddress(fAddress); - if ( funcAtom != NULL ) - asprintf((char**)&fSynthesizedName, "%s.eh", funcAtom->getDisplayName()); - } - else if ( fOwner.fLSDAAtoms.count(this) != 0) { - // give name to LSDA - ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromLSDAAddress(fAddress); - if ( funcAtom != NULL ) - asprintf((char**)&fSynthesizedName, "%s.lsda", funcAtom->getDisplayName()); - } - else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - std::vector& references = this->getReferences(); - if ( references.size() != 1 ) - throwf("__objc_classrefs element missing reloc (count=%ld) for target class in %s", references.size(), fOwner.getPath()); - const char* targetName = references[0]->getTargetName(); - if ( strncmp(targetName, "_OBJC_CLASS_$_", 14) == 0 ) - asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", &targetName[14]); - else - asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", targetName); - } -} - - -template -const char* AnonymousAtom::getDisplayName() const -{ - if ( fSynthesizedName != NULL ) - return fSynthesizedName; - - if ( fDisplayName != NULL ) - return fDisplayName; - - if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); - } - else { - asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); - } - return fDisplayName; -} - - -template -ObjectFile::Atom::Scope AnonymousAtom::getScope() const -{ - return fScope; -} - - -template -bool AnonymousAtom::isZeroFill() const -{ - return ( ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL) && fOwner.fOptions.fOptimizeZeroFill ); -} - - -template -const char* AnonymousAtom::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage ) { - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - return "__const"; - } - } - - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -template -ObjectFile::Alignment AnonymousAtom::getAlignment() const -{ - // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment - if ( fType == ObjectFile::Atom::kCFIType ) - return ObjectFile::Alignment(0); - - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - return ObjectFile::Alignment(2); - case S_8BYTE_LITERALS: - return ObjectFile::Alignment(3); - case S_16BYTE_LITERALS: - return ObjectFile::Alignment(4); - case S_NON_LAZY_SYMBOL_POINTERS: - return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t))); - case S_CSTRING_LITERALS: - if ( ! fOwner.fOptions.fForFinalLinkedImage ) - return ObjectFile::Alignment(fSection->align()); - default: - return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); - } -} - - -template -ObjectFile::Atom& AnonymousAtom::getFollowOnAtom() const -{ - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return ref->getTarget(); - } - return *((ObjectFile::Atom*)NULL); -} - -template -void AnonymousAtom::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - -// -// An AbsoluteAtom represents an N_ABS symbol which can only be created in -// assembly language and usable by static executables such as the kernel/ -// -template -class AbsoluteAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; } - virtual bool dontDeadStrip() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return "._absolute"; } - virtual ObjectFile::Segment& getSegment() const { return LinkEditSegment::fgSingleton; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } - virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } - virtual const void* getSectionRecord() const { return NULL; } - virtual unsigned int getSectionIndex() const { return 0; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader; - - AbsoluteAtom(Reader&, const macho_nlist

*); - virtual ~AbsoluteAtom() {} - - Reader& fOwner; - const macho_nlist

* fSymbol; - ObjectFile::Atom::Scope fScope; - static std::vector fgNoReferences; -}; - -template -std::vector AbsoluteAtom::fgNoReferences; - -template -AbsoluteAtom::AbsoluteAtom(Reader& owner, const macho_nlist

* symbol) - : fOwner(owner), fSymbol(symbol) -{ - // store absolute adress in fSectionOffset - fSectionOffset = symbol->n_value(); - // compute scope - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName()); -} - - -// -// An SectionBoundaryAtom represent the start or end of a section -// -template -class SectionBoundaryAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return fSymbolName; } - virtual const char* getDisplayName() const { return fDisplayName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kWeakDefinition; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fStart ? ObjectFile::Atom::kSectionStart : ObjectFile::Atom::kSectionEnd; } - virtual bool isZeroFill() const { return fZeroFill; } - virtual bool isThumb() const { return false; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return fSectionName; } - virtual ObjectFile::Segment& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(ObjectFile::Atom::Scope newScope) { } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return 0; } - virtual const void* getSectionRecord() const { return NULL; } - virtual unsigned int getSectionIndex() const { return 0; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader; - - - class Segment : public ObjectFile::Segment - { - public: - Segment(const char* name, bool r, bool w, bool x): - fName(name), fReadable(r), fWritable(w), fExecutable(x) {} - - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - private: - const char* fName; - bool fReadable; - bool fWritable; - bool fExecutable; - }; - - SectionBoundaryAtom(Reader&, bool start, const char* symbolName, const char* segSectName); - virtual ~SectionBoundaryAtom() {} - - Reader& fOwner; - class Segment* fSegment; - const char* fSymbolName; - const char* fSectionName; - const char* fDisplayName; - bool fStart; - bool fZeroFill; - static std::vector fgNoReferences; -}; - -template -std::vector SectionBoundaryAtom::fgNoReferences; - -// examples: -// section$start$__DATA$__my -// section$end$__DATA$__my -template -SectionBoundaryAtom::SectionBoundaryAtom(Reader& owner, bool start, const char* symbolName, const char* segSectName) - : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start), fZeroFill(false) -{ - const char* segSectDividor = strrchr(segSectName, '$'); - if ( segSectDividor == NULL ) - throwf("malformed section reference name: %s", symbolName); - fSectionName = segSectDividor + 1; - int segNameLen = segSectDividor - segSectName; - if ( segNameLen > 16 ) - throwf("malformed section reference name: %s", symbolName); - char segName[18]; - strlcpy(segName, segSectName, segNameLen+1); - if ( strcmp(segName, "__TEXT") == 0 ) - fSegment = new Segment("__TEXT", true, false, true); - else if ( strcmp(segName, "__DATA") == 0 ) { - fSegment = new Segment("__DATA", true, true, false); - if ( (strcmp(fSectionName, "__bss") == 0) || (strcmp(fSectionName, "__common") == 0) ) - fZeroFill = true; - } - else - fSegment = new Segment(strdup(segName), true, true, false); - - asprintf((char**)&fDisplayName, "%s of section '%s' in segment '%s'", (start ? "start" : "end"), fSectionName, segName); -} - - - -/// -/// ObjectFileAddressSpace is used as a template parameter to UnwindCursor for parsing -/// dwarf CFI information in an object file. -/// -template -class ObjectFileAddressSpace -{ -public: - ObjectFileAddressSpace(Reader& reader); - - typedef typename A::P::uint_t pint_t; - typedef typename A::P P; - typedef typename A::P::uint_t sint_t; - - uint8_t get8(pint_t addr); - uint16_t get16(pint_t addr); - uint32_t get32(pint_t addr); - uint64_t get64(pint_t addr); - pint_t getP(pint_t addr); - uint64_t getULEB128(pint_t& addr, pint_t end); - int64_t getSLEB128(pint_t& addr, pint_t end); - pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); -private: - const void* mappedAddress(pint_t addr, pint_t* relocTarget=NULL); - pint_t relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount); - void buildRelocatedMap(const macho_section

* sect, std::map& map); - - Reader& fReader; - const uint8_t* fMappingStart; - const macho_section

* fSectionsStart; - const macho_section

* fSectionsEnd; - std::map fEHFrameOffsetToTargetMap; -}; - - -template -ObjectFileAddressSpace::ObjectFileAddressSpace(Reader& reader) - : fReader(reader), fMappingStart(NULL), fSectionsStart(NULL), fSectionsEnd(NULL) -{ -} - - - -template -const void* ObjectFileAddressSpace::mappedAddress(pint_t addr, pint_t* relocTarget) -{ - if ( fMappingStart == NULL ) { - // delay initialization until now when fReader.fSegment is set up - fMappingStart = (uint8_t*)fReader.fHeader; - fSectionsStart = (macho_section

*)((char*)fReader.fSegment + sizeof(macho_segment_command

)); - fSectionsEnd = &fSectionsStart[fReader.fSegment->nsects()]; - // find __eh_frame section and build map of relocations for performance - buildRelocatedMap(fReader.fehFrameSection, fEHFrameOffsetToTargetMap); - } - // special case lookups in __eh_frame section to be fast - const macho_section

* ehSect = fReader.fehFrameSection; - if ( (ehSect->addr() <= addr) && (addr < (ehSect->addr()+ehSect->size())) ) { - pint_t offsetOfAddrInSection = addr - ehSect->addr(); - if ( relocTarget != NULL ) { - std::map::iterator pos = fEHFrameOffsetToTargetMap.find(offsetOfAddrInSection); - if ( pos != fEHFrameOffsetToTargetMap.end() ) - *relocTarget = pos->second; - else - *relocTarget = 0; - } - return fMappingStart + ehSect->offset() + offsetOfAddrInSection; - } - else { - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { - pint_t offsetOfAddrInSection = addr - sect->addr(); - if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - const uint32_t indirectTableOffset = sect->reserved1(); - const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); - const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); - // return pointer to symbol name which this non-lazy-pointer will point to - if ( relocTarget != NULL ) - *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; - } - else { - if ( relocTarget != NULL ) - *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); - } - return fMappingStart + sect->offset() + offsetOfAddrInSection; - } - } - throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); - } -} - - - - -template -uint8_t ObjectFileAddressSpace::get8(pint_t logicalAddr) -{ - return *((uint8_t*)mappedAddress(logicalAddr)); -} - -template -uint16_t ObjectFileAddressSpace::get16(pint_t logicalAddr) -{ - return P::E::get16(*((uint16_t*)mappedAddress(logicalAddr))); -} - -template -uint32_t ObjectFileAddressSpace::get32(pint_t logicalAddr) -{ - pint_t relocTarget; - return P::E::get32(*((uint32_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; -} - -template -uint64_t ObjectFileAddressSpace::get64(pint_t logicalAddr) -{ - pint_t relocTarget; - return P::E::get64(*((uint64_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; -} - -template -typename A::P::uint_t ObjectFileAddressSpace::getP(pint_t logicalAddr) -{ - pint_t relocTarget; - return P::getP(*((pint_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; -} - -template -uint64_t ObjectFileAddressSpace::getULEB128(pint_t& logicalAddr, pint_t end) -{ - uintptr_t size = (end - logicalAddr); - libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); - libunwind::LocalAddressSpace::pint_t sladdr = laddr; - uint64_t result = libunwind::LocalAddressSpace::getULEB128(laddr, laddr+size); - logicalAddr += (laddr-sladdr); - return result; -} - -template -int64_t ObjectFileAddressSpace::getSLEB128(pint_t& logicalAddr, pint_t end) -{ - uintptr_t size = (end - logicalAddr); - libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); - libunwind::LocalAddressSpace::pint_t sladdr = laddr; - int64_t result = libunwind::LocalAddressSpace::getSLEB128(laddr, laddr+size); - logicalAddr += (laddr-sladdr); - return result; -} - - - - - - -template -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); - static const char* fileKind(const uint8_t* fileContent); - Reader(const uint8_t* fileContent, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return fModTime; } - virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; } - virtual std::vector& getAtoms() { return (std::vector&)(fAtoms); } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabs() { return &fStabs; } - virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; } - virtual uint32_t updateCpuConstraint(uint32_t current); - virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); } - virtual bool objcReplacementClasses(){ return fReplacementClasses; } - virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; } - - bool getTranslationUnitSource(const char** dir, const char** name) const; - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - //typedef typename std::vector*> AtomVector; - //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser - typedef typename A::ReferenceKinds Kinds; - typedef typename libunwind::CFI_Parser >::FDE_Atom_Info FDE_Atom_Info; - typedef typename libunwind::CFI_Parser >::CIE_Atom_Info CIE_Atom_Info; - typedef class ObjectFileAddressSpace OAS; - friend class ObjectFileAddressSpace; - friend class AnonymousAtom; - friend class TentativeAtom; - friend class AbsoluteAtom; - friend class SectionBoundaryAtom; - friend class SymbolAtom; - typedef std::map AddrToAtomMap; - - void addReferencesForSection(const macho_section

* sect); - bool addRelocReference(const macho_section

* sect, const macho_relocation_info

* reloc); - bool addRelocReference_powerpc(const macho_section

* sect, const macho_relocation_info

* reloc); - const char* getDwarfString(uint64_t form, const uint8_t* p); - bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); - static bool isWeakImportSymbol(const macho_nlist

* sym); - static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); - static const char* assureFullPath(const char* path); - AtomAndOffset findAtomAndOffset(pint_t addr); - AtomAndOffset findAtomAndOffsetForSection(pint_t addr, unsigned int sectionIndex); - AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr); - Reference* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr); - Reference* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr); - Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr); - Reference* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr); - Reference* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset); - BaseAtom* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect); - Reference* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset); - void validSectionType(uint8_t type); - void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName); - void setCpuConstraint(uint32_t cpusubtype); - const macho_section

* getSectionForAddress(pint_t); - ObjectFile::Atom* getFunctionAtomFromFDEAddress(pint_t); - ObjectFile::Atom* getFunctionAtomFromLSDAAddress(pint_t); - void addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target); - void addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding); - bool isSectDiffReloc(uint8_t r_type); - - - BaseAtom* findAtomByName(const char*); - - const char* fPath; - time_t fModTime; - uint32_t fOrdinalBase; - const ObjectFile::ReaderOptions& fOptions; - const macho_header

* fHeader; - const char* fStrings; - const macho_nlist

* fSymbols; - uint32_t fSymbolCount; - const macho_segment_command

* fSegment; - const uint32_t* fIndirectTable; - std::vector fAtoms; - AddrToAtomMap fAddrToAtom; - AddrToAtomMap fAddrToAbsoluteAtom; - std::vector*> fLocalNonLazys; - std::vector*> fAtomsPendingAName; - std::set*> fSectionsWithAtomsPendingAName; - std::vector fDtraceProviderInfo; - ObjectFile::Reader::DebugInfoKind fDebugInfo; - bool fHasUUID; - const macho_section

* fehFrameSection; - const macho_section

* fUTF16Section; - std::set fLSDAAtoms; - const macho_section

* fDwarfDebugInfoSect; - const macho_section

* fDwarfDebugAbbrevSect; - const macho_section

* fDwarfDebugLineSect; - const macho_section

* fDwarfDebugStringSect; - const char* fDwarfTranslationUnitDir; - const char* fDwarfTranslationUnitFile; - std::map fDwarfIndexToFile; - std::vector fStabs; - std::vector fFDEInfos; - std::vector fCIEInfos; - bool fAppleObjc; - bool fHasDTraceProbes; - bool fHaveIndirectSymbols; - bool fReplacementClasses; - bool fHasLongBranchStubs; - ObjectFile::Reader::ObjcConstraint fObjConstraint; - uint32_t fCpuConstraint; - const macho_section

* fSectionsStart; - const macho_section

* fSectionsEnd; - OAS fObjectAddressSpace; -}; - -template -Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fPath(strdup(path)), fModTime(modTime), fOrdinalBase(ordinalBase), fOptions(options), fHeader((const macho_header

*)fileContent), - fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), - fDebugInfo(kDebugInfoNone), fHasUUID(false), fehFrameSection(NULL), fUTF16Section(NULL), - fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), - fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), - fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), - fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny), - fSectionsStart(NULL), fSectionsEnd(NULL), fObjectAddressSpace(*this) -{ - // sanity check - if ( ! validFile(fileContent, false, 0) ) - throw "not a valid mach-o object file"; - - Reference::fgForFinalLinkedImage = options.fForFinalLinkedImage; - - // write out path for -t or -whatsloaded option - if ( options.fLogObjectFiles || options.fLogAllFiles ) - printf("%s\n", path); - - // cache intersting pointers - const macho_header

* header = (const macho_header

*)fileContent; - this->setCpuConstraint(header->cpusubtype()); - const uint32_t cmd_count = header->ncmds(); - const macho_load_command

* const cmds = (macho_load_command

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

)); - const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; - uint32_t undefinedStartIndex = 0; - uint32_t undefinedEndIndex = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - fSymbolCount = symtab->nsyms(); - fSymbols = (const macho_nlist

*)((char*)header + symtab->symoff()); - fStrings = (char*)header + symtab->stroff(); - if ( undefinedEndIndex == 0 ) { - undefinedStartIndex = 0; - undefinedEndIndex = symtab->nsyms(); - } - } - break; - case LC_DYSYMTAB: - { - const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; - fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); - undefinedStartIndex = dsymtab->iundefsym(); - undefinedEndIndex = undefinedStartIndex + dsymtab->nundefsym(); - } - break; - case LC_UUID: - fHasUUID = true; - break; - - default: - if ( cmd->cmd() == macho_segment_command

::CMD ) { - fSegment = (macho_segment_command

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

*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - - // if there are no load commands, then this file has no content, so no atoms - if ( header->ncmds() < 1 ) - return; - - fSectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - fSectionsEnd = &fSectionsStart[fSegment->nsects()]; - - // inital guess for number of atoms - fAtoms.reserve(fSymbolCount); - - // if there is an __eh_frame section, decode it into chunks to get atoms in that - // section as well as division points for functions in __text - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (strcmp(sect->sectname(), "__eh_frame") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { - fehFrameSection = sect; - const char* msg = libunwind::CFI_Parser >::getCFIs(fObjectAddressSpace, sect->addr(), - sect->size(), fFDEInfos, fCIEInfos); - if ( msg != NULL ) { - throwf("malformed __eh_frame section: %s", msg); - } - else { - //fprintf(stderr, "%lu CIEs, %lu FDEs\n", fCIEInfos.size(), fFDEInfos.size()); - // add anonymous atoms for each CIE - for (typename std::vector::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { - AnonymousAtom* cieAtom = new AnonymousAtom(*this, sect, it->cieAddress, 1); - fAtoms.push_back(cieAtom); - fAddrToAtom[it->cieAddress] = cieAtom; - } - // add anonymous atoms for each FDE and LSDA - for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - //fprintf(stderr, "fdeAddress=0x%08llX, lsdaAddr=0x%08llX, funcAddr=0x%08llX\n", (uint64_t)it->fdeAddress, (uint64_t)it->lsda.address, (uint64_t)it->function.address); - AnonymousAtom* fdeAtom = new AnonymousAtom(*this, sect, it->fdeAddress, 0); - fAtoms.push_back(fdeAtom); - fAddrToAtom[it->fdeAddress] = fdeAtom; - if ( it->lsda.address != 0 ) { - AnonymousAtom* lsdaAtom = new AnonymousAtom(*this, getSectionForAddress(it->lsda.address), it->lsda.address, 0); - fAtoms.push_back(lsdaAtom); - fAddrToAtom[it->lsda.address] = lsdaAtom; - fLSDAAtoms.insert(lsdaAtom); - } - } - } - } - else if ( (strcmp(sect->sectname(), "__ustring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) && (sect->size() != 0) ) { - // if there is a __ustring section parse it into atoms - fUTF16Section = sect; - // first find all cleave points - const uint16_t* words = (uint16_t*)((char*)(fHeader) + fUTF16Section->offset()); - unsigned int wordCount = fUTF16Section->size()/2; - std::vector utf16Addreses; - bool inString = false; - for (unsigned int i=0; i < wordCount; ++i) { - if ( inString ) { - if ( words[i] == 0x0000 ) { - inString = false; - } - } - else { - if ( words[i] == 0x0000 ) { - // skip over zero padding - } - else { - inString = true; - utf16Addreses.push_back(fUTF16Section->addr() + i*2); - } - } - } - utf16Addreses.push_back(fUTF16Section->addr() + sect->size()); - // build map of symbols - std::map* > symbolMap; - for (int i=fSymbolCount-1; i >= 0 ; --i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - uint8_t type = (sym.n_type() & N_TYPE); - if ( type == N_SECT ) { - if ( &fSectionsStart[sym.n_sect()-1] == fUTF16Section ) { - // rdar://problem/7429384 don't coalesce UTF16 strings unless label starts with ___utf16_string - if ( strncmp(&fStrings[sym.n_strx()], "___utf16_string", 15) != 0 ) { - symbolMap[sym.n_value()] = &sym; - // if this symbol is a string of just 0x0000, it may not be in utf16Addreses - if ( words[(sym.n_value() - sect->addr())/2] == 0x0000 ) { - for(typename std::vector::iterator sit=utf16Addreses.begin(); sit != utf16Addreses.end(); ++sit) { - if ( *sit == sym.n_value() ) { - // already in utf16Addreses - break; - } - if ( *sit > sym.n_value() ) { - // need to insert - utf16Addreses.insert(sit, sym.n_value()); - break; - } - } - } - } - } - } - } - } - // make atom for each string - for(int i=utf16Addreses.size()-2; i >=0 ; --i) { - pint_t size = utf16Addreses[i+1] - utf16Addreses[i]; - typename std::map* >::iterator pos = symbolMap.find(utf16Addreses[i]); - if ( pos == symbolMap.end() ) { - AnonymousAtom* strAtom = new AnonymousAtom(*this, fUTF16Section, utf16Addreses[i], size); - fAtoms.push_back(strAtom); - fAddrToAtom[utf16Addreses[i]] = strAtom; - } - else { - SymbolAtom* newAtom = new SymbolAtom(*this, pos->second, fUTF16Section); - fAtoms.push_back(newAtom); - fAddrToAtom[utf16Addreses[i]] = newAtom; - newAtom->setSize(size); - } - } - } - } - - - // add all atoms that have entries in symbol table - BaseAtom* sectionEndAtoms[fSegment->nsects()]; - for (unsigned int i=0; i < fSegment->nsects(); ++i) - sectionEndAtoms[i] = NULL; - for (int i=fSymbolCount-1; i >= 0 ; --i) { - // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the real name - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - uint8_t type = (sym.n_type() & N_TYPE); - if ( type == N_SECT ) { - const macho_section

* section = &fSectionsStart[sym.n_sect()-1]; - const pint_t sectionStartAddr = section->addr(); - const pint_t sectionEndAddr = sectionStartAddr + section->size(); - bool suppress = false; - // ignore atoms in debugger sections - if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { - if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) { - // ignore dtrace probe labels - fHasDTraceProbes = true; - } - else if ( fStrings[sym.n_strx()] == 'L' ) { - // ignore L labels, - } - else if ( section == fehFrameSection ) { - // ignore labels in __eh_frame section - } - else if ( section == fUTF16Section ) { - // ignore labels in __ustring section - } - else { - // ignore labels for atoms in other sections - switch ( section->flags() & SECTION_TYPE ) { - case S_REGULAR: - if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 ) - suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS - case S_ZEROFILL: - case S_COALESCED: - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - case S_CSTRING_LITERALS: - { - BaseAtom* newAtom; - typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value()); - if ( (pos != fAddrToAtom.end()) && (pos->second->getSectionRecord() == section) ) { - if ( fLSDAAtoms.count(pos->second) != 0 ) { - // already have LSDA atom from for this address, ignore compiler's label - suppress = true; - break; - } - else { - // another label to an existing address in the same section, make this an alias - newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *pos->second); - } - } - else { - if ( sym.n_value() == sectionEndAddr ) { - // Symbol address is at end of section. This can interfere - // with a symbol at the start of the next section, so don't - // add to fAddrToAtom. But do track in sectionEndAtoms so we - // still make aliases if there are duplicates. - if ( sectionEndAtoms[sym.n_sect()-1] == NULL ) { - newAtom = new SymbolAtom(*this, &sym, section); - sectionEndAtoms[sym.n_sect()-1] = newAtom; - // if this is a zero length section, so add to fAddrToAtom - if ( sym.n_value() == sectionStartAddr ) - fAddrToAtom[newAtom->getObjectAddress()] = newAtom; - } - else { - newAtom = new SymbolAliasAtom(&fStrings[sym.n_strx()], &sym, *sectionEndAtoms[sym.n_sect()-1]); - } - } - else { - // make SymbolAtom atom for this address - newAtom = new SymbolAtom(*this, &sym, section); - fAddrToAtom[newAtom->getObjectAddress()] = newAtom; - } - } - if ( ! suppress ) - fAtoms.push_back(newAtom); - } - break; - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - // ignore symboled stubs produces by old ld64 - break; - default: - warning("symbol %s found in unsupported section in %s", - &fStrings[sym.n_strx()], this->getPath()); - } - } - } - } - else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { - fAtoms.push_back(new TentativeAtom(*this, &sym)); - } - else if ( (type == N_UNDF) && (sym.n_value() == 0) ) { - const char* symName = &fStrings[sym.n_strx()]; - if ( strncmp(symName, "section$start$", 14) == 0) - fAtoms.push_back(new SectionBoundaryAtom(*this, true, symName, &symName[14])); - else if ( strncmp(symName, "section$end$", 12) == 0) - fAtoms.push_back(new SectionBoundaryAtom(*this, false, symName, &symName[12])); - } - else if ( type == N_ABS ) { - const char* symName = &fStrings[sym.n_strx()]; - if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) { - // ignore .objc_class_name_* symbols - fAppleObjc = true; - } - else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) { - // ignore empty *.eh symbols - } - else { - BaseAtom* abAtom = new AbsoluteAtom(*this, &sym); - fAtoms.push_back(abAtom); - fAddrToAbsoluteAtom[sym.n_value()] = abAtom; - } - } - else if ( type == N_INDR ) { - fHaveIndirectSymbols = true; - } - } - } - - // add anonymous atoms for any functions (as determined by dwarf unwind) have no symbol names - if ( fehFrameSection != NULL ) { - for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - // add if not already an atom at that address - if ( fAddrToAtom.find(it->function.address) == fAddrToAtom.end() ) { - AnonymousAtom* funcAtom = new AnonymousAtom(*this, getSectionForAddress(it->function.address), it->function.address, 0); - fAtoms.push_back(funcAtom); - fAddrToAtom[it->function.address] = funcAtom; - // even though we've made a new atom, be conservative and make sure they lay out together - if ( canScatterAtoms() ) { - AtomAndOffset prev = findAtomAndOffset(it->function.address-1); - if ( prev.atom != NULL ) { - if ( ((BaseAtom*)(prev.atom))->getSectionRecord() == funcAtom->getSectionRecord() ) - new Reference(A::kFollowOn, prev, AtomAndOffset(funcAtom)); - } - } - } - } - } - - - // add all fixed size anonymous atoms from special sections - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - pint_t atomSize = 0; - uint8_t type (sect->flags() & SECTION_TYPE); - validSectionType(type); - bool suppress = false; - switch ( type ) { - case S_SYMBOL_STUBS: - suppress = true; - atomSize = sect->reserved2(); - break; - case S_LAZY_SYMBOL_POINTERS: - suppress = true; - atomSize = sizeof(pint_t); - break; - case S_NON_LAZY_SYMBOL_POINTERS: - case S_LITERAL_POINTERS: - case S_MOD_INIT_FUNC_POINTERS: - case S_MOD_TERM_FUNC_POINTERS: - atomSize = sizeof(pint_t); - break; - case S_INTERPOSING: - atomSize = sizeof(pint_t)*2; - break; - case S_4BYTE_LITERALS: - atomSize = 4; - break; - case S_8BYTE_LITERALS: - atomSize = 8; - break; - case S_16BYTE_LITERALS: - atomSize = 16; - break; - case S_REGULAR: - // special case ObjC classes to synthesize .objc_class_name_* symbols - if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) && fAppleObjc ) { - // gcc sometimes over aligns class structure - uint32_t align = 1 << sect->align(); - atomSize = ((12 * sizeof(pint_t)) + align-1) & (-align); - } - // get objc Garbage Collection info - else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) - || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset()); - if ( (sect->size() >= 8) && (contents[0] == 0) ) { - uint32_t flags = E::get32(contents[1]); - if ( (flags & 4) == 4 ) - fObjConstraint = ObjectFile::Reader::kObjcGC; - else if ( (flags & 2) == 2 ) - fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - else - fObjConstraint = ObjectFile::Reader::kObjcRetainRelease; - if ( (flags & 1) == 1 ) - fReplacementClasses = true; - // don't make atom for this section - atomSize = sect->size(); - suppress = true; - } - else { - warning("can't parse __OBJC/__image_info section in %s", fPath); - } - } - // special case constant NS/CFString literals and make an atom out of each one - else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { - atomSize = 4 * sizeof(pint_t); - } - // special case class reference sections - else if ( (strncmp(sect->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0) ) { - atomSize = sizeof(pint_t);; - } - break; - } - if ( atomSize != 0 ) { - for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { - pint_t atomAddr = sect->addr() + sectOffset; - // add if not already an atom at that address - if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) { - AnonymousAtom* newAtom = new AnonymousAtom(*this, sect, atomAddr, atomSize); - if ( !suppress ) - fAtoms.push_back(newAtom); - fAddrToAtom[atomAddr] = newAtom->redirectTo(); - } - } - } - } - - // add all c-string anonymous atoms - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { - uint32_t stringLen; - pint_t stringAddr; - BaseAtom* mostAlignedEmptyString = NULL; - uint32_t mostAlignedEmptyStringTrailingZeros = 0; - std::vector > emptyStrings; - for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { - stringAddr = sect->addr() + sectOffset; - stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; - // add if not already a non-zero length atom at that address - typename AddrToAtomMap::iterator pos = fAddrToAtom.find(stringAddr); - if ( (pos == fAddrToAtom.end()) || (pos->second->getSize() == 0) ) { - BaseAtom* newAtom = new AnonymousAtom(*this, sect, stringAddr, stringLen); - if ( stringLen == 1 ) { - // because of padding it may look like there are lots of empty strings, keep track of all - emptyStrings.push_back(std::make_pair(stringAddr, newAtom)); - // record empty string with greatest alignment requirement - uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr); - if ( (mostAlignedEmptyString == NULL) - || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) { - mostAlignedEmptyString = newAtom; - mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros; - } - } - else { - fAtoms.push_back(newAtom); - fAddrToAtom[stringAddr] = newAtom; - } - } - } - // map all uses of empty strings to the most aligned one - if ( mostAlignedEmptyString != NULL ) { - // make most aligned atom a real atom - fAtoms.push_back(mostAlignedEmptyString); - // map all other empty atoms to this one - for (typename std::vector >::iterator it=emptyStrings.begin(); it != emptyStrings.end(); it++) { - fAddrToAtom[it->first] = mostAlignedEmptyString; - } - } - } - } - - // sort all atoms so far by address and section - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - - //fprintf(stderr, "sorted atoms:\n"); - //for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) - // fprintf(stderr, "0x%08llX %s\n", (*it)->getObjectAddress(), (*it)->getDisplayName()); - - // create atoms to cover any non-debug ranges not handled above - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - pint_t sectionStartAddr = sect->addr(); - pint_t sectionEndAddr = sect->addr() + sect->size(); - // don't set follow-on atoms in __eh_frame section - const bool setFollowOnAtom = !canScatterAtoms() && (sect != fehFrameSection); - if ( sect->size() != 0 ) { - // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change - if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { - fDebugInfo = kDebugInfoDwarf; - if ( strcmp(sect->sectname(), "__debug_info") == 0 ) - fDwarfDebugInfoSect = sect; - else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) - fDwarfDebugAbbrevSect = sect; - else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) - fDwarfDebugLineSect = sect; - else if ( strcmp(sect->sectname(), "__debug_str") == 0 ) - fDwarfDebugStringSect = sect; - } - else { - if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { - throw "object file contains old DWARF debug info - rebuild with newer compiler"; - } - uint8_t type (sect->flags() & SECTION_TYPE); - switch ( type ) { - case S_REGULAR: - case S_ZEROFILL: - case S_COALESCED: - // if there is not an atom already at the start of this section, add an anonymous one - pint_t previousAtomAddr = 0; - BaseAtom* previousAtom = NULL; - if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { - BaseAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr(), 0); - fAddrToAtom[sect->addr()] = newAtom; - fAtoms.push_back(newAtom); - previousAtomAddr = sectionStartAddr; - previousAtom = newAtom; - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - } - // calculate size of all atoms in this section and add follow-on references - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - pint_t atomAddr = atom->getObjectAddress(); - if ( atom->getSectionRecord() == sect ) { - //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName()); - if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) { - previousAtom->setSize(atomAddr - previousAtomAddr); - if ( setFollowOnAtom && (atom != previousAtom) ) - new Reference(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom)); - } - previousAtomAddr = atomAddr; - previousAtom = atom; - } - } - if ( previousAtom != NULL ) { - // set last atom in section - previousAtom->setSize(sectionEndAddr - previousAtomAddr); - } - break; - } - } - } - } - - // check for object file that defines no objc classes, but uses objc classes - // check for dtrace provider info - for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_UNDF ) { - const char* undefinedName = &fStrings[sym.n_strx()]; - if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) { - fAppleObjc = true; - } - else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) { - if ( strchr(undefinedName, '$') != NULL ) { - if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) { - // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* - // is extra provider info - fDtraceProviderInfo.push_back(undefinedName); - } - } - } - } - } - } - - // add relocation based references to sections that have atoms with pending names - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( fSectionsWithAtomsPendingAName.count(sect) != 0 ) - addReferencesForSection(sect); - } - - // update any anonymous atoms that need references built in order to name themselves - for (typename std::vector*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) { - (*it)->resolveName(); - } - - // add relocation based references to other sections - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( fSectionsWithAtomsPendingAName.count(sect) == 0 ) - addReferencesForSection(sect); - } - - // add objective-c references - if ( fAppleObjc ) { - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { - for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { - AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset); - ObjectFile::Reference* classRef = ao.atom->getReferences()[0]; - if ( classRef->getFixUpOffset() == 0 ) { - const char* classStr = classRef->getTargetName(); - if ( strncmp(classStr, "cstring=", 8) == 0 ) { - const char* className; - asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]); - new Reference(A::kNoFixUp, ao, className, 0); - } - } - } - } - } - } - - // add direct references to local non-lazy-pointers, can do this now that all atoms are constructed - for (typename std::vector*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) { - AnonymousAtom* localNonLazy = *it; - uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress; - pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset))); - makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); - } - - - // add personality references to CIEs - for (typename std::vector::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { - if ( it->personality.offsetInFDE != 0 ) - addCiePersonalityReference(fAddrToAtom[it->cieAddress], it->personality.offsetInFDE, it->personality.encodingOfAddress); - } - - // add all references for FDEs, including implicit group references - for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - AtomAndOffset funcAO = this->findAtomAndOffset(it->function.address); - if ( funcAO.offset != 0 ) - warning("FDE does not point to start of function %s\n", funcAO.atom->getDisplayName()); - AtomAndOffset fdeAO = this->findAtomAndOffset(it->fdeAddress); - if ( fdeAO.offset != 0 ) - warning("FDE does start its own atom %s\n", funcAO.atom->getDisplayName()); - AtomAndOffset cieAO = this->findAtomAndOffset(it->cie.address); - if ( cieAO.offset != 0 ) - warning("CIE does start its own atom %s\n", cieAO.atom->getDisplayName()); - AtomAndOffset lsdaAO; - if ( it->lsda.address != 0 ) { - lsdaAO = this->findAtomAndOffset(it->lsda.address); - if ( lsdaAO.offset != 0 ) - warning("LSDA does start its own atom %s\n", lsdaAO.atom->getDisplayName()); - } - - // add reference from FDE to CIE - AtomAndOffset cieInfdeAO = AtomAndOffset(fdeAO.atom, it->cie.offsetInFDE); - new Reference(A::kPointerDiff32, cieInfdeAO, cieAO, cieInfdeAO); - - // add reference from FDE to function - addFdeReference(it->function.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->function.offsetInFDE), funcAO); - - // add reference from FDE to LSDA - if ( it->lsda.address != 0 ) { - addFdeReference(it->lsda.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->lsda.offsetInFDE), lsdaAO); - } - - // FDE is in group lead by function atom - new Reference(A::kGroupSubordinate, funcAO, fdeAO); - - // LSDA is in group lead by function atom - if ( it->lsda.address != 0 ) { - new Reference(A::kGroupSubordinate, funcAO, lsdaAO); - // add back reference from LSDA to owning function - new Reference(A::kNoFixUp, lsdaAO, funcAO); - } - - // compute compact encoding for this FDE - if ( fOptions.fAddCompactUnwindEncoding ) { - ((BaseAtom*)(funcAO.atom))->setCompactUnwindEncoding(it->fdeAddress); - // add reference from function atom to personality function - // the only reference a CIE can have is the reference to the personality function - std::vector& cieRefs = cieAO.atom->getReferences(); - if ( cieRefs.size() == 1 ) { - new Reference((typename A::ReferenceKinds)((BaseAtom*)(funcAO.atom))->getPersonalityReferenceKind(), - funcAO, cieRefs[0]->getTargetName(), 0); - } - } - } - - // add command line aliases - for(std::vector::const_iterator it = fOptions.fAliases.begin(); it != fOptions.fAliases.end(); ++it) { - BaseAtom* target = this->findAtomByName(it->realName); - if ( (target != NULL) && target->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn ) - fAtoms.push_back(new SymbolAliasAtom(it->alias, NULL, *target)); - } - - // add dtrace probe locations - if ( fHasDTraceProbes ) { - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_SECT ) { - const char* symbolName = &fStrings[sym.n_strx()]; - if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) { - //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName); - makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0); - } - } - } - } - } - - // turn indirect symbols into SymbolAliasAtom - if ( fHaveIndirectSymbols ) { - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist

& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_INDR ) { - const char* aliasName = &fStrings[sym.n_strx()]; - const char* targetName = &fStrings[sym.n_value()]; - //fprintf(stderr, "found alias %s for %s\n", aliasName, targetName); - BaseAtom* target = this->findAtomByName(targetName); - // only currently support N_INDR based aliases to something in the same .o file - if ( target != NULL ) { - fAtoms.push_back(new SymbolAliasAtom(aliasName, &sym, *target)); - //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName); - } - } - } - } - } - - //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName()); - //} - - // add translation unit info from dwarf - uint64_t stmtList; - if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // compiler sometimes emits emtpty dwarf sections when there is no debug info, skip those - if ( (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { - if ( !read_comp_unit(&fDwarfTranslationUnitFile, &fDwarfTranslationUnitDir, &stmtList) ) { - // if can't parse dwarf, warn and give up - fDwarfTranslationUnitFile = NULL; - fDwarfTranslationUnitDir = NULL; - warning("can't parse dwarf compilation unit info in %s", this->getPath()); - fDebugInfo = kDebugInfoNone; - } - } - } - - // add line number info to atoms from dwarf - if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // file with just data will have no __debug_line info - if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0) - && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { - // validate stmt_list - if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) { - const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset(); - if ( debug_line != NULL ) { - struct line_reader_data* lines = line_open(&debug_line[stmtList], - fDwarfDebugLineSect->size() - stmtList, E::little_endian); - struct line_info result; - ObjectFile::Atom* curAtom = NULL; - uint32_t curAtomOffset = 0; - uint32_t curAtomAddress = 0; - uint32_t curAtomSize = 0; - if ( lines != NULL ) { - while ( line_next (lines, &result, line_stop_pc) ) { - //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", - // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); - // work around weird debug line table compiler generates if no functions in __text section - if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) - continue; - // for performance, see if in next pc is in current atom - if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - // or pc at end of current atom - else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - else { - // do slow look up of atom by address - AtomAndOffset ao = this->findAtomAndOffset(result.pc); - curAtom = ao.atom; - if ( curAtom == NULL ) - break; // file has line info but no functions - if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { - // a one line function can be returned by line_next() as one entry with pc at end of blob - // look for alt atom starting at end of previous atom - uint32_t previousEnd = curAtomAddress+curAtomSize; - AtomAndOffset alt = this->findAtomAndOffset(previousEnd); - if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { - curAtom = alt.atom; - curAtomOffset = alt.offset; - curAtomAddress = previousEnd - alt.offset; - curAtomSize = curAtom->getSize(); - } - else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); - } - } - else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); - } - } - const char* filename; - std::map::iterator pos = fDwarfIndexToFile.find(result.file); - if ( pos == fDwarfIndexToFile.end() ) { - filename = line_file(lines, result.file); - fDwarfIndexToFile[result.file] = filename; - } - else { - filename = pos->second; - } - ObjectFile::LineInfo info; - info.atomOffset = curAtomOffset; - info.fileName = filename; - info.lineNumber = result.line; - //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", - // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); - ((BaseAtom*)curAtom)->addLineInfo(info); - if ( result.end_of_sequence ) { - curAtom = NULL; - } - } - line_free(lines); - } - } - else { - warning("could not parse dwarf line number info in %s", this->getPath()); - } - } - } - } - - // if no dwarf, try processing stabs debugging info - if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // scan symbol table for stabs entries - fStabs.reserve(fSymbolCount); // reduce re-allocations - BaseAtom* currentAtom = NULL; - pint_t currentAtomAddress = 0; - enum { start, inBeginEnd, inFun } state = start; - for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { - const macho_nlist

* sym = &fSymbols[symbolIndex]; - bool useStab = true; - uint8_t type = sym->n_type(); - const char* symString = (sym->n_strx() != 0) ? &fStrings[sym->n_strx()] : NULL; - if ( (type & N_STAB) != 0 ) { - fDebugInfo = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs); - Stab stab; - stab.atom = NULL; - stab.type = type; - stab.other = sym->n_sect(); - stab.desc = sym->n_desc(); - stab.value = sym->n_value(); - stab.string = NULL; - switch (state) { - case start: - switch (type) { - case N_BNSYM: - // beginning of function block - state = inBeginEnd; - // fall into case to lookup atom by addresss - case N_LCSYM: - case N_STSYM: - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", - (uint64_t)sym->n_value(), path); - } - break; - case N_SO: - case N_OSO: - case N_OPT: - case N_LSYM: - case N_RSYM: - case N_PSYM: - // not associated with an atom, just copy - stab.string = symString; - break; - case N_GSYM: - { - // n_value field is NOT atom address ;-( - // need to find atom by name match - const char* colon = strchr(symString, ':'); - if ( colon != NULL ) { - // build underscore leading name - int nameLen = colon - symString; - char symName[nameLen+2]; - strlcpy(&symName[1], symString, nameLen+1); - symName[0] = '_'; - symName[nameLen+1] = '\0'; - currentAtom = findAtomByName(symName); - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - } - else { - // might be a debug-note without trailing :G() - currentAtom = findAtomByName(symString); - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - } - if ( stab.atom == NULL ) { - // ld_classic added bogus GSYM stabs for old style dtrace probes - if ( (strncmp(symString, "__dtrace_probe$", 15) != 0) ) - warning("can't find atom for N_GSYM stabs %s in %s", symString, path); - useStab = false; - } - break; - } - case N_FUN: - // old style stabs without BNSYM - state = inFun; - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs FUN at %08llX in %s", - (uint64_t)currentAtomAddress, path); - } - break; - case N_SOL: - case N_SLINE: - stab.string = symString; - // old stabs - break; - case N_BINCL: - case N_EINCL: - case N_EXCL: - stab.string = symString; - // -gfull built .o file - break; - default: - warning("unknown stabs type 0x%X in %s", type, path); - } - break; - case inBeginEnd: - stab.atom = currentAtom; - switch (type) { - case N_ENSYM: - state = start; - currentAtom = NULL; - break; - case N_LCSYM: - case N_STSYM: - { - BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; - if ( nestedAtom != NULL ) { - stab.atom = nestedAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs 0x%X at %08llX in %s", - type, (uint64_t)sym->n_value(), path); - } - break; - } - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - // adjust value to be offset in atom - stab.value -= currentAtomAddress; - default: - stab.string = symString; - break; - } - break; - case inFun: - switch (type) { - case N_FUN: - if ( sym->n_sect() != 0 ) { - // found another start stab, must be really old stabs... - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs FUN at %08llX in %s", - (uint64_t)currentAtomAddress, path); - } - } - else { - // found ending stab, switch back to start state - stab.string = symString; - stab.atom = currentAtom; - state = start; - currentAtom = NULL; - } - break; - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - // adjust value to be offset in atom - stab.value -= currentAtomAddress; - stab.atom = currentAtom; - break; - case N_SO: - stab.string = symString; - state = start; - break; - default: - stab.atom = currentAtom; - stab.string = symString; - break; - } - break; - } - // add to list of stabs for this .o file - if ( useStab ) - fStabs.push_back(stab); - } - } - } - -#if 0 - // special case precompiled header .o file (which has no content) to have one empty atom - if ( fAtoms.size() == 0 ) { - int pathLen = strlen(path); - if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) { - ObjectFile::Atom* phony = new AnonymousAtom(*this, (uint32_t)0); - //phony->fSynthesizedName = ".gch.o"; - fAtoms.push_back(phony); - } - } -#endif - - // sort all atoms by address - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - - // set ordinal and sort references in each atom - uint32_t index = fOrdinalBase; - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - atom->setOrdinal(index++); - atom->sortReferences(); - } - -} - -template -const macho_section* Reader::getSectionForAddress(pint_t addr) -{ - for (const macho_section

* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) - return sect; - } - throwf("section not found for address 0x%08llX", (uint64_t)addr); -} - -template -ObjectFile::Atom* Reader::getFunctionAtomFromFDEAddress(pint_t addr) -{ - for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - if ( it->fdeAddress == addr ) { - return findAtomAndOffset(it->function.address).atom; - } - } - // CIEs won't be in fFDEInfos - return NULL; -} - -template -ObjectFile::Atom* Reader::getFunctionAtomFromLSDAAddress(pint_t addr) -{ - for (typename std::vector::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - if ( it->lsda.address == addr ) { - return findAtomAndOffset(it->function.address).atom; - } - } - return NULL; -} - - -template <> -void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) -{ - // mach-o x86_64 is different, the content of a section with a relocation is the addend - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fReader.fHeader) + sect->reloff()); - const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { - std::map::iterator pos; - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - pos = map.find(reloc->r_address()); - if ( pos != map.end() ) - pos->second += fReader.fSymbols[reloc->r_symbolnum()].n_value(); - else - map[reloc->r_address()] = fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_SUBTRACTOR: - map[reloc->r_address()] = -fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_GOT: - // there is no good address to return here. - // GOT slots are synthsized by the linker - // this is used for the reference to the personality function in CIEs - map[reloc->r_address()] = 0; - break; - default: - fprintf(stderr, "ObjectFileAddressSpace::buildRelocatedMap() unexpected relocation at r_address=0x%08X\n", reloc->r_address()); - break; - } - } -} - -template -void ObjectFileAddressSpace::buildRelocatedMap(const macho_section

* sect, std::map& map) -{ - // in all architectures except x86_64, the section contents are already fixed up to point - // to content in the same object file. -} - -template <> -uint64_t ObjectFileAddressSpace::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) -{ - // mach-o x86_64 is different, the content of a section with a relocation is the addend - uint64_t result = 0; - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fReader.fHeader) + relocsOffset); - const macho_relocation_info

* relocsEnd = &relocs[relocsCount]; - for (const macho_relocation_info

* reloc = relocs; reloc < relocsEnd; ++reloc) { - //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X), r_address=0x%08X\n", sectOffset, reloc->r_address()); - if ( reloc->r_address() == sectOffset ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - result += fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_SUBTRACTOR: - result -= fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_GOT: - // there is no good address to return here. - // GOT slots are synthsized by the linker - // this is used for the reference to the personality function in CIEs - result = 0; - break; - default: - fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => type=%d, value=0x%08X\n", sectOffset, reloc->r_type(), reloc->r_symbolnum()); - break; - } - } - } - //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => 0x%0llX\n", sectOffset, result); - return result; -} - -template -typename A::P::uint_t ObjectFileAddressSpace::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) -{ - // in all architectures except x86_64, the section contents are already fixed up to point - // to content in the same object file. - return 0; -} - - - -// FSF exception handling Pointer-Encoding constants -// Used in CFI augmentation by gcc compiler -enum { - DW_EH_PE_ptr = 0x00, - DW_EH_PE_uleb128 = 0x01, - DW_EH_PE_udata2 = 0x02, - DW_EH_PE_udata4 = 0x03, - DW_EH_PE_udata8 = 0x04, - DW_EH_PE_signed = 0x08, - DW_EH_PE_sleb128 = 0x09, - DW_EH_PE_sdata2 = 0x0A, - DW_EH_PE_sdata4 = 0x0B, - DW_EH_PE_sdata8 = 0x0C, - DW_EH_PE_absptr = 0x00, - DW_EH_PE_pcrel = 0x10, - DW_EH_PE_textrel = 0x20, - DW_EH_PE_datarel = 0x30, - DW_EH_PE_funcrel = 0x40, - DW_EH_PE_aligned = 0x50, - DW_EH_PE_indirect = 0x80, - DW_EH_PE_omit = 0xFF -}; - -template <> -void Reader::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding) -{ - if ( encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4) ) - throw "unexpected personality encoding in CIE"; - - // walk relocs looking for reloc in this CIE - uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr(); - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + fehFrameSection->reloff()); - const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( reloc->r_address() == sectOffset ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_GOT: - if ( !reloc->r_extern() ) - throw "GOT reloc not extern for personality function"; - new Reference(x86_64::kPCRel32GOT, AtomAndOffset(cieAtom, offsetInCIE), &fStrings[fSymbols[reloc->r_symbolnum()].n_strx()], 4); - return; - default: - throw "expected GOT reloc for personality function"; - } - } - } - throw "personality function not found for CIE"; -} - -template <> -bool Reader::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case PPC_RELOC_LOCAL_SECTDIFF: - case PPC_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <> -bool Reader::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case PPC_RELOC_LOCAL_SECTDIFF: - case PPC_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <> -bool Reader::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case GENERIC_RELOC_LOCAL_SECTDIFF: - case GENERIC_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <> -bool Reader::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case ARM_RELOC_LOCAL_SECTDIFF: - case ARM_RELOC_SECTDIFF: - return true; - } - return false; -} - -template -void Reader::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding) -{ - if ( (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4)) && (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel)) ) - throw "unexpected personality encoding in CIE"; - - // walk relocs looking for personality reloc in this CIE - uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr(); - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + fehFrameSection->reloff()); - const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - // ignore - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - if ( sreloc->r_address() == sectOffset ) { - if ( isSectDiffReloc(sreloc->r_type()) ) { - // r_value is address of non-lazy-pointer to personality function - new Reference(A::kPointerDiff32, AtomAndOffset(cieAtom, offsetInCIE), AtomAndOffset(cieAtom, offsetInCIE), - findAtomAndOffset(sreloc->r_value())); - return; - } - } - } - } - throw "can't find relocation for personality in CIE"; -} - -template -void Reader::addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target) -{ - if ( (encoding & 0xF0) != DW_EH_PE_pcrel ) - throw "unsupported encoding in FDE"; - Kinds kind = A::kNoFixUp; - switch ( encoding & 0xF ) { - case DW_EH_PE_ptr: - kind = A::kPointerDiff; - break; - case DW_EH_PE_sdata4: - kind = A::kPointerDiff32; - break; - default: - throw "unsupported encoding in FDE"; - } - new Reference(kind, inFDE, inFDE, target); -} - -template -typename A::P::uint_t ObjectFileAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) -{ - pint_t startAddr = addr; - pint_t p = addr; - pint_t result; - - // first get value - switch (encoding & 0x0F) { - case DW_EH_PE_ptr: - result = getP(addr); - p += sizeof(pint_t); - addr = (pint_t)p; - break; - case DW_EH_PE_uleb128: - result = getULEB128(addr, end); - break; - case DW_EH_PE_udata2: - result = get16(addr); - p += 2; - addr = (pint_t)p; - break; - case DW_EH_PE_udata4: - result = get32(addr); - p += 4; - addr = (pint_t)p; - break; - case DW_EH_PE_udata8: - result = get64(addr); - p += 8; - addr = (pint_t)p; - break; - case DW_EH_PE_sleb128: - result = getSLEB128(addr, end); - break; - case DW_EH_PE_sdata2: - result = (int16_t)get16(addr); - p += 2; - addr = (pint_t)p; - break; - case DW_EH_PE_sdata4: - result = (int32_t)get32(addr); - p += 4; - addr = (pint_t)p; - break; - case DW_EH_PE_sdata8: - result = get64(addr); - p += 8; - addr = (pint_t)p; - break; - default: - throwf("ObjectFileAddressSpace::getEncodedP() encoding 0x%08X not supported", encoding); - } - - // then add relative offset - switch ( encoding & 0x70 ) { - case DW_EH_PE_absptr: - // do nothing - break; - case DW_EH_PE_pcrel: - // pc-rel sdata4 should return zero if content is zero - if ( (result != 0) || ((encoding & DW_EH_PE_indirect) != 0) ) - result += startAddr; - break; - case DW_EH_PE_textrel: - throw "DW_EH_PE_textrel pointer encoding not supported"; - break; - case DW_EH_PE_datarel: - throw "DW_EH_PE_datarel pointer encoding not supported"; - break; - case DW_EH_PE_funcrel: - throw "DW_EH_PE_funcrel pointer encoding not supported"; - break; - case DW_EH_PE_aligned: - throw "DW_EH_PE_aligned pointer encoding not supported"; - break; - default: - throwf("ObjectFileAddressSpace::getEncodedP() encoding 0x%08X not supported", encoding); - break; - } - - if ( encoding & DW_EH_PE_indirect ) - result = getP(result); - - return result; -} - -template <> -uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); - } - return result; -} - -template <> -uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); - } - return result; -} - -template <> -uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc - return 0; -} - -template <> -uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc64 - return 0; -} - -template <> -uint32_t SymbolAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for arm - return 0; -} - - -template -uint8_t SymbolAtom::getLSDAReferenceKind() const -{ - return A::kGroupSubordinate; -} - -template <> -uint8_t SymbolAtom::getPersonalityReferenceKind() const -{ - return x86_64::kGOTNoFixUp; -} - -template <> -uint8_t SymbolAtom::getPersonalityReferenceKind() const -{ - return x86::kNoFixUp; -} - -template -uint8_t SymbolAtom::getPersonalityReferenceKind() const -{ - // only used with architectures that support compact unwinding - return 0; -} - - -template <> -uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); - } - return result; -} - -template <> -uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); - } - return result; -} - -template <> -uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc - return 0; -} - -template <> -uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc64 - return 0; -} - -template <> -uint32_t AnonymousAtom::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for arm - return 0; -} - - -template -uint8_t AnonymousAtom::getLSDAReferenceKind() const -{ - return A::kGroupSubordinate; -} - -template <> -uint8_t AnonymousAtom::getPersonalityReferenceKind() const -{ - return x86_64::kGOTNoFixUp; -} - -template <> -uint8_t AnonymousAtom::getPersonalityReferenceKind() const -{ - return x86::kNoFixUp; -} - -template -uint8_t AnonymousAtom::getPersonalityReferenceKind() const -{ - // only used with architectures that support compact unwinding - return 0; -} - - - - - - - -template <> -void Reader::setCpuConstraint(uint32_t cpusubtype) -{ - switch (cpusubtype) { - case CPU_SUBTYPE_POWERPC_ALL: - case CPU_SUBTYPE_POWERPC_750: - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - case CPU_SUBTYPE_POWERPC_970: - fCpuConstraint = cpusubtype; - break; - default: - warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); - fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL; - break; - } -} - -template <> -void Reader::setCpuConstraint(uint32_t cpusubtype) -{ - switch (cpusubtype) { - case CPU_SUBTYPE_ARM_ALL: - case CPU_SUBTYPE_ARM_V4T: - case CPU_SUBTYPE_ARM_V5TEJ: - case CPU_SUBTYPE_ARM_V6: - case CPU_SUBTYPE_ARM_XSCALE: - case CPU_SUBTYPE_ARM_V7: - fCpuConstraint = cpusubtype; - break; - default: - warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); - fCpuConstraint = CPU_SUBTYPE_ARM_ALL; - break; - } -} - -template -void Reader::setCpuConstraint(uint32_t cpusubtype) -{ - // no cpu sub types for this architecture -} - -template <> -uint32_t Reader::updateCpuConstraint(uint32_t previous) -{ - switch ( previous ) { - case CPU_SUBTYPE_POWERPC_ALL: - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_750: - if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 || - fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 || - fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_970: - // G5 can run everything - break; - default: - throw "Unhandled PPC cpu subtype!"; - break; - } - return previous; -} - - - -template <> -uint32_t Reader::updateCpuConstraint(uint32_t previous) -{ - switch (previous) { - case CPU_SUBTYPE_ARM_ALL: - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - // v6, v7, and xscale are more constrained than previous file (v5), so use it - if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V7) - || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V4T: - // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it - if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V6) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ) - || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V6: - // v6 can run everything except xscale and v7 - if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) - throw "can't mix xscale and v6 code"; - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_XSCALE: - // xscale can run everything except v6 and v7 - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 ) - throw "can't mix xscale and v6 code"; - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) - throw "can't mix xscale and v7 code"; - break; - case CPU_SUBTYPE_ARM_V7: - // v7 can run everything except xscale - if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) - throw "can't mix xscale and v7 code"; - break; - default: - throw "Unhandled ARM cpu subtype!"; - } - return previous; -} - -template -uint32_t Reader::updateCpuConstraint(uint32_t current) -{ - // no cpu sub types for this architecture - return current; -} - -template -void Reader::addDtraceExtraInfos(uint32_t probeAddr, const char* providerName) -{ - // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with - // a matching provider name, add a by-name kDtraceTypeReference at probe site - const char* dollar = strchr(providerName, '$'); - if ( dollar != NULL ) { - int providerNameLen = dollar-providerName+1; - for ( std::vector::iterator it = fDtraceProviderInfo.begin(); it != fDtraceProviderInfo.end(); ++it) { - const char* typeDollar = strchr(*it, '$'); - if ( typeDollar != NULL ) { - if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) { - makeByNameReference(A::kDtraceTypeReference, probeAddr, *it, 0); - } - } - } - } -} - - -template <> -void Reader::validSectionType(uint8_t type) -{ - switch ( type ) { - case S_SYMBOL_STUBS: - throw "symbol_stub sections not valid in x86_64 object files"; - case S_LAZY_SYMBOL_POINTERS: - throw "lazy pointer sections not valid in x86_64 object files"; - case S_NON_LAZY_SYMBOL_POINTERS: - throw "non lazy pointer sections not valid in x86_64 object files"; - } -} - -template -void Reader::validSectionType(uint8_t type) -{ -} - -template -bool Reader::getTranslationUnitSource(const char** dir, const char** name) const -{ - if ( fDebugInfo == kDebugInfoDwarf ) { - *dir = fDwarfTranslationUnitDir; - *name = fDwarfTranslationUnitFile; - return (fDwarfTranslationUnitFile != NULL); - } - return false; -} - -template -BaseAtom* Reader::findAtomByName(const char* name) -{ - // first search the more important atoms - for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - const char* atomName = it->second->getName(); - if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { - return it->second; - } - } - // try all atoms, because this might have been a tentative definition - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - const char* atomName = atom->getName(); - if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { - return atom; - } - } - return NULL; -} - -template -Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr)); -} - -template -Reference* Reader::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr)); -} - -template -Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr)); -} - -template -Reference* Reader::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr) -{ - return new Reference(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr)); -} - -template -Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) -{ - return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); -} - -template -BaseAtom* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) -{ - // add a group subordinate reference from function atom to its eh frame atom - const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); - int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function - pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8; - ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom; - ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom; - new Reference(A::kGroupSubordinate, funcAtom, ehAtom); - return (BaseAtom*)funcAtom; -} - - -template <> -Reference* Reader::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) -{ - // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references - // instead check scope of target - BaseAtom* target = findAtomByName(toName); - if ( (target != NULL) && (target->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) - return new Reference(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset)); - else - return new Reference(kind, findAtomAndOffset(atAddr), toName, toOffset); -} - -template <> -Reference* Reader::makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist

* toSymbol, pint_t toOffset) -{ - // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references - // instead check scope of target - const char* symbolName = &fStrings[toSymbol->n_strx()]; - if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) { - AtomAndOffset targetAO = findAtomAndOffsetForSection(toSymbol->n_value(), toSymbol->n_sect()); - targetAO.offset = toOffset; - return new Reference(kind, findAtomAndOffset(atAddr), targetAO); - } - else - return new Reference(kind, findAtomAndOffset(atAddr), symbolName, toOffset); -} - - -template <> -BaseAtom* Reader::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

* ehSect) -{ - // add a group subordinate reference from function atom to its eh frame atom - // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target - uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + ehSect->reloff()); - const macho_relocation_info

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { - pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); - ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom; - ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom; - new Reference(x86_64::kGroupSubordinate, funcAtom, ehAtom); - return (BaseAtom*)funcAtom; - } - } - warning("can't find matching function for eh symbol %s", ehName); - return NULL; -} - -template -AtomAndOffset Reader::findAtomAndOffsetForSection(pint_t addr, unsigned int expectedSectionIndex) -{ - AtomAndOffset ao = findAtomAndOffset(addr); - if ( ao.atom != NULL ) { - if ( ((BaseAtom*)(ao.atom))->getSectionIndex() == expectedSectionIndex ) - return ao; - } - // The atom found is not in the section expected. - // This probably means there was a label at the end of the section. - // Do a slow sequential lookup - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); ++it) { - BaseAtom* atom = *it; - if ( atom->getSectionIndex() == expectedSectionIndex ) { - pint_t objAddr = atom->getObjectAddress(); - if ( (objAddr == addr) || ((objAddr < addr) && (objAddr+atom->getSize() > addr)) ) { - return AtomAndOffset(atom, addr-atom->getObjectAddress()); - } - } - } - // no atom found that matched section, fall back to one orginally found - return ao; -} - -template -AtomAndOffset Reader::findAtomAndOffset(pint_t addr) -{ - // STL has no built-in for "find largest key that is same or less than" - typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr); - // if no atoms up to this address return none found - if ( it == fAddrToAtom.begin() ) - return AtomAndOffset(NULL); - // otherwise upper_bound gets us next key, so we back up one - --it; - AtomAndOffset result; - result.atom = it->second; - result.offset = addr - it->first; - //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n", - // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize()); - return result; -} - -// "scattered" relocations enable you to offset into an atom past the end of it -// baseAddr is the address of the target atom, -// realAddr is the points into it -template -AtomAndOffset Reader::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) -{ - typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr); - if ( it != fAddrToAtom.end() ) { - AtomAndOffset result; - result.atom = it->second; - result.offset = realAddr - it->first; - if ( result.atom->isThumb() ) - result.offset &= -2; - //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); - return result; - } - // getting here means we have a scattered relocation to an address without a label - // so, find the atom that contains the baseAddr, and offset from that to the readAddr - AtomAndOffset result = findAtomAndOffset(baseAddr); - result.offset += (realAddr-baseAddr); - return result; -} - - -/* Skip over a LEB128 value (signed or unsigned). */ -static void -skip_leb128 (const uint8_t ** offset, const uint8_t * end) -{ - while (*offset != end && **offset >= 0x80) - (*offset)++; - if (*offset != end) - (*offset)++; -} - -/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow - or error. On overflow, skip past the rest of the uleb128. */ -static uint64_t -read_uleb128 (const uint8_t ** offset, const uint8_t * end) -{ - uint64_t result = 0; - int bit = 0; - - do { - uint64_t b; - - if (*offset == end) - return (uint64_t) -1; - - b = **offset & 0x7f; - - if (bit >= 64 || b << bit >> bit != b) - result = (uint64_t) -1; - else - result |= b << bit, bit += 7; - } while (*(*offset)++ >= 0x80); - return result; -} - - -/* Skip over a DWARF attribute of form FORM. */ -template -bool Reader::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, - uint8_t addr_size, bool dwarf64) -{ - int64_t sz=0; - - switch (form) - { - case DW_FORM_addr: - sz = addr_size; - break; - - case DW_FORM_block2: - if (end - *offset < 2) - return false; - sz = 2 + A::P::E::get16(*(uint16_t*)offset); - break; - - case DW_FORM_block4: - if (end - *offset < 4) - return false; - sz = 2 + A::P::E::get32(*(uint32_t*)offset); - break; - - case DW_FORM_data2: - case DW_FORM_ref2: - sz = 2; - break; - - case DW_FORM_data4: - case DW_FORM_ref4: - sz = 4; - break; - - case DW_FORM_data8: - case DW_FORM_ref8: - sz = 8; - break; - - case DW_FORM_string: - while (*offset != end && **offset) - ++*offset; - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_ref1: - sz = 1; - break; - - case DW_FORM_block: - sz = read_uleb128 (offset, end); - break; - - case DW_FORM_block1: - if (*offset == end) - return false; - sz = 1 + **offset; - break; - - case DW_FORM_sdata: - case DW_FORM_udata: - case DW_FORM_ref_udata: - skip_leb128 (offset, end); - return true; - - case DW_FORM_strp: - case DW_FORM_ref_addr: - sz = 4; - break; - - default: - return false; - } - if (end - *offset < sz) - return false; - *offset += sz; - return true; -} - -template -const char* Reader::getDwarfString(uint64_t form, const uint8_t* p) -{ - if ( form == DW_FORM_string ) - return (const char*)p; - else if ( form == DW_FORM_strp ) { - uint32_t offset = E::get32(*((uint32_t*)p)); - const char* dwarfStrings = (char*)(fHeader) + fDwarfDebugStringSect->offset(); - if ( offset > fDwarfDebugStringSect->size() ) { - warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->getPath()); - return NULL; - } - return &dwarfStrings[offset]; - } - warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->getPath()); - return NULL; -} - - -// Look at the compilation unit DIE and determine -// its NAME, compilation directory (in COMP_DIR) and its -// line number information offset (in STMT_LIST). NAME and COMP_DIR -// may be NULL (especially COMP_DIR) if they are not in the .o file; -// STMT_LIST will be (uint64_t) -1. -// -// At present this assumes that there's only one compilation unit DIE. -// -template -bool Reader::read_comp_unit(const char ** name, const char ** comp_dir, - uint64_t *stmt_list) -{ - const uint8_t * debug_info; - const uint8_t * debug_abbrev; - const uint8_t * di; - const uint8_t * da; - const uint8_t * end; - const uint8_t * enda; - uint64_t sz; - uint16_t vers; - uint64_t abbrev_base; - uint64_t abbrev; - uint8_t address_size; - bool dwarf64; - - *name = NULL; - *comp_dir = NULL; - *stmt_list = (uint64_t) -1; - - if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) ) - return false; - - debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset(); - debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset(); - di = debug_info; - - if (fDwarfDebugInfoSect->size() < 12) - /* Too small to be a real debug_info section. */ - return false; - sz = A::P::E::get32(*(uint32_t*)di); - di += 4; - dwarf64 = sz == 0xffffffff; - if (dwarf64) - sz = A::P::E::get64(*(uint64_t*)di), di += 8; - else if (sz > 0xffffff00) - /* Unknown dwarf format. */ - return false; - - /* Verify claimed size. */ - if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) - return false; - - vers = A::P::E::get16(*(uint16_t*)di); - if (vers < 2 || vers > 3) - /* DWARF version wrong for this code. - Chances are we could continue anyway, but we don't know for sure. */ - return false; - di += 2; - - /* Find the debug_abbrev section. */ - abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); - di += dwarf64 ? 8 : 4; - - if (abbrev_base > fDwarfDebugAbbrevSect->size()) - return false; - da = debug_abbrev + abbrev_base; - enda = debug_abbrev + fDwarfDebugAbbrevSect->size(); - - address_size = *di++; - - /* Find the abbrev number we're looking for. */ - end = di + sz; - abbrev = read_uleb128 (&di, end); - if (abbrev == (uint64_t) -1) - return false; - - /* Skip through the debug_abbrev section looking for that abbrev. */ - for (;;) - { - uint64_t this_abbrev = read_uleb128 (&da, enda); - uint64_t attr; - - if (this_abbrev == abbrev) - /* This is almost always taken. */ - break; - skip_leb128 (&da, enda); /* Skip the tag. */ - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - do { - attr = read_uleb128 (&da, enda); - skip_leb128 (&da, enda); - } while (attr != 0 && attr != (uint64_t) -1); - if (attr != 0) - return false; - } - - /* Check that the abbrev is one for a DW_TAG_compile_unit. */ - if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) - return false; - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - /* Now, go through the DIE looking for DW_AT_name, - DW_AT_comp_dir, and DW_AT_stmt_list. */ - for (;;) - { - uint64_t attr = read_uleb128 (&da, enda); - uint64_t form = read_uleb128 (&da, enda); - - if (attr == (uint64_t) -1) - return false; - else if (attr == 0) - return true; - - if (form == DW_FORM_indirect) - form = read_uleb128 (&di, end); - - if (attr == DW_AT_name) - *name = getDwarfString(form, di); - else if (attr == DW_AT_comp_dir) - *comp_dir = getDwarfString(form, di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) - *stmt_list = A::P::E::get32(*(uint32_t*)di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) - *stmt_list = A::P::E::get64(*(uint64_t*)di); - if (! skip_form (&di, end, form, address_size, dwarf64)) - return false; - } -} - -template -const char* Reader::assureFullPath(const char* path) -{ - if ( path[0] == '/' ) - return path; - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - char* result; - asprintf(&result, "%s/%s", cwdbuff, path); - if ( result != NULL ) - return result; - } - return path; -} - - -// -// -// To implement architecture xxx, you must write template specializations for the following six methods: -// Reader::validFile() -// Reader::addRelocReference() -// Reference::getDescription() -// -// - - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_I386 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_ARM ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - if ( subtypeMustMatch && ((cpu_subtype_t)header->cpusubtype() != subtype) ) - return false; - return true; -} - - -template <> -const char* Reader::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return NULL; - switch ( header->cpusubtype() ) { - case CPU_SUBTYPE_POWERPC_750: - return "ppc750"; - case CPU_SUBTYPE_POWERPC_7400: - return "ppc7400"; - case CPU_SUBTYPE_POWERPC_7450: - return "ppc7450"; - case CPU_SUBTYPE_POWERPC_970: - return "ppc970"; - case CPU_SUBTYPE_POWERPC_ALL: - return "ppc"; - } - return "ppc???"; -} - -template <> -const char* Reader::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return NULL; - return "ppc64"; -} - -template <> -const char* Reader::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_I386 ) - return NULL; - return "i386"; -} - -template <> -const char* Reader::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return NULL; - return "x86_64"; -} - -template <> -const char* Reader::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - 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"; - } - return "arm???"; -} - - -template -bool Reader::isWeakImportSymbol(const macho_nlist

* sym) -{ - return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) ); -} - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - return addRelocReference_powerpc(sect, reloc); -} - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - return addRelocReference_powerpc(sect, reloc); -} - - -// -// ppc and ppc64 both use the same relocations, so process them in one common routine -// -template -bool Reader::addRelocReference_powerpc(const macho_section* sect, - const macho_relocation_info* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - int32_t displacement = 0; - uint32_t instruction = 0; - uint32_t offsetInTarget; - int16_t lowBits; - bool result = false; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - const macho_relocation_info

* nextReloc = &reloc[1]; - const char* targetName = NULL; - bool weakImport = false; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != PPC_RELOC_PAIR ) - instruction = BigEndian::get32(*fixUpPtr); - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - weakImport = this->isWeakImportSymbol(targetSymbol); - } - switch ( reloc->r_type() ) { - case PPC_RELOC_BR24: - { - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - } - else { - printf("bad instruction for BR24 reloc"); - } - if ( reloc->r_extern() ) { - offsetInTarget = srcAddr + displacement; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(A::kBranch24WeakImport, srcAddr, dstAddr); - else - makeReference(A::kBranch24, srcAddr, dstAddr); - } - } - break; - case PPC_RELOC_BR14: - { - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - if ( reloc->r_extern() ) { - offsetInTarget = srcAddr + displacement; - makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - makeReference(A::kBranch14, srcAddr, dstAddr); - } - } - break; - case PPC_RELOC_PAIR: - // skip, processed by a previous look ahead - break; - case PPC_RELOC_LO16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_LO16 missing following pair"; - } - result = true; - lowBits = (instruction & 0xFFFF); - if ( reloc->r_extern() ) { - offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsLow16, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsLow16, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_LO14: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_LO14 missing following pair"; - } - result = true; - lowBits = (instruction & 0xFFFC); - if ( reloc->r_extern() ) { - offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsLow14, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsLow14, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_HI16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_HI16 missing following pair"; - } - result = true; - if ( reloc->r_extern() ) { - offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsHigh16, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsHigh16, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_HA16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_HA16 missing following pair"; - } - result = true; - lowBits = (nextReloc->r_address() & 0x0000FFFF); - if ( reloc->r_extern() ) { - offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_VANILLA: - { - pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - if ( weakImport ) - makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue); - else - makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); - } - else { - new Reference(A::kPointer, findAtomAndOffset(srcAddr), findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum())); - } - } - break; - case PPC_RELOC_JBSR: - // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_JBSR missing following pair"; - } - if ( !fHasLongBranchStubs ) - warning("object file compiled with -mlong-branch which is no longer needed. To remove this warning, recompile without -mlong-branch: %s", fPath); - fHasLongBranchStubs = true; - result = true; - if ( reloc->r_extern() ) { - throw "PPC_RELOC_JBSR should not be using an external relocation"; - } - makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - } - else { - fprintf(stderr, "bad instruction for BR24 reloc"); - } - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - uint32_t betterDstAddr; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - const macho_relocation_info

* nextReloc = &reloc[1]; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - } - switch (sreloc->r_type()) { - case PPC_RELOC_VANILLA: - { - betterDstAddr = P::getP(*(pint_t*)fixUpPtr); - //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_BR14: - { - instruction = BigEndian::get32(*fixUpPtr); - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - betterDstAddr = srcAddr+displacement; - //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement); - makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_BR24: - { - instruction = BigEndian::get32(*fixUpPtr); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - betterDstAddr = srcAddr+displacement; - makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr); - } - } - break; - case PPC_RELOC_LO16_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFF); - displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_LO14_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFC); - displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_HA16_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0x0000FFFF); - displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_LO14: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO14 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFC); - betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_LO16: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO16 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFF); - betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_HA16: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_HA16 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0xFFFF); - betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; - makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_HI16: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_HI16 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0xFFFF); - betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_SECTDIFF: - case PPC_RELOC_LOCAL_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_SECTDIFF missing following pair"; - } - Kinds kind = A::kPointerDiff32;; - uint32_t contentAddr = 0; - switch ( sreloc->r_length() ) { - case 0: - throw "bad diff relocations r_length (0) for ppc architecture"; - case 1: - kind = A::kPointerDiff16; - contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr)); - break; - case 2: - kind = A::kPointerDiff32; - contentAddr = BigEndian::get32(*fixUpPtr); - break; - case 3: - kind = A::kPointerDiff64; - contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr)); - break; - } - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", - // dstAddr, nextRelocValue, contentAddr); - if ( (dstAddr - nextRelocValue) != contentAddr ) { - if ( toao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - contentAddr) - nextRelocValue; - } - //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", - // srcao.atom->getDisplayName(), srcao.offset, - // fromao.atom->getDisplayName(), fromao.offset, - // toao.atom->getDisplayName(), toao.offset); - new Reference(kind, srcao, fromao, toao); - } - break; - case PPC_RELOC_PAIR: - break; - case PPC_RELOC_HI16_SECTDIFF: - warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - bool result = false; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - switch ( reloc->r_type() ) { - case GENERIC_RELOC_VANILLA: - { - x86::ReferenceKinds kind = x86::kPointer; - uint32_t pointerValue = E::get32(*fixUpPtr); - if ( reloc->r_pcrel() ) { - switch( reloc->r_length() ) { - case 0: - kind = x86::kPCRel8; - pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t); - break; - case 1: - kind = x86::kPCRel16; - pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t); - break; - case 2: - kind = x86::kPCRel32; - pointerValue += srcAddr + sizeof(uint32_t); - break; - case 3: - throw "bad pc-rel vanilla relocation length"; - } - } - else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { - kind = x86::kAbsolute32; - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - } - else { - kind = x86::kPointer; - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - } - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - if ( this->isWeakImportSymbol(targetSymbol) ) { - if ( reloc->r_pcrel() ) - kind = x86::kPCRel32WeakImport; - else - kind = x86::kPointerWeakImport; - } - const char* targetName = &fStrings[targetSymbol->n_strx()]; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else - makeByNameReference(kind, srcAddr, targetName, pointerValue); - } - else { - AtomAndOffset targetAO = findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum()); - const char* targetName = targetAO.atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - // if this is a reference to a stub, we need to see if the stub is for a weak imported symbol - else if ( reloc->r_pcrel() && (targetAO.atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)targetAO.atom)->isWeakImportStub() ) - new Reference(x86::kPCRel32WeakImport, findAtomAndOffset(srcAddr), targetAO); - else if ( reloc->r_symbolnum() != R_ABS ) - new Reference(kind, findAtomAndOffset(srcAddr), targetAO); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(kind, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - } - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - const macho_relocation_info

* nextReloc = &reloc[1]; - pint_t betterDstAddr; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - } - } - switch (sreloc->r_type()) { - case GENERIC_RELOC_VANILLA: - betterDstAddr = LittleEndian::get32(*fixUpPtr); - //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - if ( sreloc->r_pcrel() ) { - switch ( sreloc->r_length() ) { - case 2: - betterDstAddr += srcAddr + 4; - makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); - break; - case 1: - betterDstAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)) + srcAddr + 2; - makeReferenceWithToBase(x86::kPCRel16, srcAddr, betterDstAddr, dstAddr); - break; - case 0: - betterDstAddr = *((uint8_t*)fixUpPtr) + srcAddr + 1; - makeReferenceWithToBase(x86::kPCRel8, srcAddr, betterDstAddr, dstAddr); - break; - case 3: - throwf("unsupported r_length=3 for scattered pc-rel vanilla reloc"); - break; - } - } - else { - if ( sreloc->r_length() != 2 ) - throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length()); - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); - else - makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); - } - break; - case GENERIC_RELOC_SECTDIFF: - case GENERIC_RELOC_LOCAL_SECTDIFF: - { - if ( !nextRelocIsPair ) { - throw "GENERIC_RELOC_SECTDIFF missing following pair"; - } - x86::ReferenceKinds kind = x86::kPointerDiff; - uint32_t contentAddr = 0; - switch ( sreloc->r_length() ) { - case 0: - case 3: - throw "bad length for GENERIC_RELOC_SECTDIFF"; - case 1: - kind = x86::kPointerDiff16; - contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)); - break; - case 2: - kind = x86::kPointerDiff; - contentAddr = LittleEndian::get32(*fixUpPtr); - break; - } - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", - // dstAddr, nextRelocValue, contentAddr); - if ( (dstAddr - nextRelocValue) != contentAddr ) { - if ( toao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - contentAddr) - nextRelocValue; - } - //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", - // srcao.atom->getDisplayName(), srcao.offset, - // fromao.atom->getDisplayName(), fromao.offset, - // toao.atom->getDisplayName(), toao.offset); - new Reference(kind, srcao, fromao, toao); - } - break; - case GENERIC_RELOC_PAIR: - // do nothing, already used via a look ahead - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - -template <> -bool Reader::addRelocReference(const macho_section* sect, const macho_relocation_info* reloc) -{ - uint64_t srcAddr; - uint64_t dstAddr = 0; - uint64_t addend; - uint32_t* fixUpPtr; - x86_64::ReferenceKinds kind = x86_64::kNoFixUp; - bool result = false; - const macho_nlist

* targetSymbol = NULL; - const char* targetName = NULL; - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - //fprintf(stderr, "addReloc type=%d, len=%d, address=0x%X\n", reloc->r_type(), reloc->r_length(), reloc->r_address()); - if ( reloc->r_extern() ) { - targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - } - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - if ( reloc->r_pcrel() ) - throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; - switch ( reloc->r_length() ) { - case 0: - case 1: - throw "length < 2 and X86_64_RELOC_UNSIGNED not supported"; - case 2: - kind = x86_64::kPointer32; - break; - case 3: - if ( reloc->r_extern() && isWeakImportSymbol(targetSymbol) ) - kind = x86_64::kPointerWeakImport; - else - kind = x86_64::kPointer; - break; - } - dstAddr = E::get64(*((uint64_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(kind, srcAddr, dstAddr); - // verify that dstAddr is in the section being targeted - int sectNum = reloc->r_symbolnum(); - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

* const targetSection = §ionsStart[sectNum-1]; - if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { - throwf("local relocation for address 0x%08llX in section %s does not target section %s", - srcAddr, sect->sectname(), targetSection->sectname()); - } - } - break; - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( reloc->r_extern() ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - kind = x86_64::kPCRel32; - // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created - if ( addend == (uint64_t)(-1) ) { - addend = 0; - kind = x86_64::kPCRel32_1; - } - else if ( addend == (uint64_t)(-2) ) { - addend = 0; - kind = x86_64::kPCRel32_2; - } - else if ( addend == (uint64_t)(-4) ) { - addend = 0; - kind = x86_64::kPCRel32_4; - } - break; - // end support for old .o files before X86_64_RELOC_SIGNED_1 was created - case X86_64_RELOC_SIGNED_1: - kind = x86_64::kPCRel32_1; - addend += 1; - break; - case X86_64_RELOC_SIGNED_2: - kind = x86_64::kPCRel32_2; - addend += 2; - break; - case X86_64_RELOC_SIGNED_4: - kind = x86_64::kPCRel32_4; - addend += 4; - break; - } - makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend); - } - else { - uint64_t ripRelativeOffset = addend; - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - dstAddr = srcAddr + 4 + ripRelativeOffset; - kind = x86_64::kPCRel32; - break; - case X86_64_RELOC_SIGNED_1: - dstAddr = srcAddr + 5 + ripRelativeOffset; - kind = x86_64::kPCRel32_1; - break; - case X86_64_RELOC_SIGNED_2: - dstAddr = srcAddr + 6 + ripRelativeOffset; - kind = x86_64::kPCRel32_2; - break; - case X86_64_RELOC_SIGNED_4: - dstAddr = srcAddr + 8 + ripRelativeOffset; - kind = x86_64::kPCRel32_4; - break; - } - makeReference(kind, srcAddr, dstAddr); - // verify that dstAddr is in the section being targeted - int sectNum = reloc->r_symbolnum(); - const macho_section

* const sectionsStart = (macho_section

*)((char*)fSegment + sizeof(macho_segment_command

)); - const macho_section

* const targetSection = §ionsStart[sectNum-1]; - if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { - throwf("local relocation for address 0x%08llX in section %s does not target section %s", - srcAddr, sect->sectname(), targetSection->sectname()); - } - } - break; - case X86_64_RELOC_BRANCH: - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_BRANCH not supported"; - if ( reloc->r_length() == 2 ) { - dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( reloc->r_extern() ) { - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr); - else - makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr); - } - } - else if ( reloc->r_length() == 0 ) { - dstAddr = *((int8_t*)fixUpPtr); - if ( reloc->r_extern() ) { - makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr); - } - } - else { - throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());; - } - break; - case X86_64_RELOC_GOT: - if ( ! reloc->r_extern() ) - throw "not extern and X86_64_RELOC_GOT not supported"; - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_GOT not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_GOT not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend); - else - makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend); - break; - case X86_64_RELOC_GOT_LOAD: - if ( ! reloc->r_extern() ) - throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend); - else - makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend); - break; - case X86_64_RELOC_SUBTRACTOR: - { - if ( reloc->r_pcrel() ) - throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; - if ( reloc->r_length() < 2 ) - throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; - if ( !reloc->r_extern() ) - throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; - const macho_relocation_info* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) - throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; - result = true; - if ( nextReloc->r_pcrel() ) - throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; - if ( nextReloc->r_length() != reloc->r_length() ) - throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; - Reference* ref; - bool negativeAddend; - if ( reloc->r_length() == 2 ) { - kind = x86_64::kPointerDiff32; - dstAddr = E::get32(*fixUpPtr); // addend is in content - negativeAddend = ((dstAddr & 0x80000000) != 0); - } - else { - kind = x86_64::kPointerDiff; - dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content - negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); - } - AtomAndOffset inAtomAndOffset = this->findAtomAndOffset(srcAddr); - ObjectFile::Atom* inAtom = inAtomAndOffset.atom; - // create reference with "to" target - if ( nextReloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0); - // if "to" is in this atom, change by-name to a direct reference - if ( strcmp(targetName, inAtom->getName()) == 0 ) - ref->setTarget(*inAtom, 0); - } - else { - ref = makeReference(kind, srcAddr, dstAddr); - } - // add in "from" target - if ( reloc->r_extern() ) { - const macho_nlist

* targetFromSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()]; - if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) { - // from target is translation unit scoped, so use a direct reference - ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom)); - } - else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) { - // if "from" is in this atom, change by-name to a direct reference - ref->setFromTarget(*inAtom); - } - else { - // some non-static other atom - ref->setFromTargetName(fromTargetName); - } - } - else { - throw "X86_64_RELOC_SUBTRACTOR not supported with r_extern=0"; - } - // addend goes in from side iff negative - if ( negativeAddend ) - ref->setFromTargetOffset(-dstAddr); - else - ref->setToTargetOffset(dstAddr); - break; - } - default: - warning("unknown relocation type %d", reloc->r_type()); - } - return result; -} - - -/// Reader::addRelocReference - -/// turns arm relocation entries into references. Returns true if the next -/// relocation should be skipped, false otherwise. -template <> -bool Reader::addRelocReference(const macho_section* sect, - const macho_relocation_info* reloc) -{ - uint32_t * fixUpPtr; - int32_t displacement; - uint32_t instruction = 0; - bool result = false; - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t pointerValue; - arm::ReferenceKinds kind = arm::kNoFixUp; - - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - // non-scattered relocation - const char* targetName = NULL; - bool weakImport = false; - - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != ARM_RELOC_PAIR ) - instruction = LittleEndian::get32(*fixUpPtr); - - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - weakImport = this->isWeakImportSymbol(targetSymbol); - } - - switch ( reloc->r_type() ) { - case ARM_RELOC_BR24: - // Sign-extend displacement - displacement = (instruction & 0x00FFFFFF) << 2; - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - // The pc added will be +8 from the pc - displacement += 8; - // If this is BLX add H << 1 - if ((instruction & 0xFE000000) == 0xFA000000) - displacement += ((instruction & 0x01000000) >> 23); - - if ( reloc->r_extern() ) { - uint32_t offsetInTarget = srcAddr + displacement; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - // check for dtrace probes and weak_import stubs - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(arm::kBranch24, srcAddr, dstAddr); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - break; - - case ARM_THUMB_RELOC_BR22: - // thumb2 added two more bits to displacement, complicating the displacement decoding - { - uint32_t s = (instruction >> 10) & 0x1; - uint32_t j1 = (instruction >> 29) & 0x1; - uint32_t j2 = (instruction >> 27) & 0x1; - uint32_t imm10 = instruction & 0x3FF; - uint32_t imm11 = (instruction >> 16) & 0x7FF; - uint32_t i1 = (j1 == s); - uint32_t i2 = (j2 == s); - uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); - int32_t sdis = dis; - if ( s ) - sdis |= 0xFE000000; - displacement = sdis; - } - // The pc added will be +4 from the pc - displacement += 4; - // If the instruction was blx, force the low 2 bits to be clear - dstAddr = srcAddr + displacement; - if ((instruction & 0xF8000000) == 0xE8000000) - dstAddr &= 0xFFFFFFFC; - - if ( reloc->r_extern() ) { - uint32_t offsetInTarget = dstAddr; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget); - } - else { - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - // check for dtrace probes and weak_import stubs - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom*)atom)->isWeakImportStub() ) - makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(arm::kThumbBranch22, srcAddr, dstAddr); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - break; - - case ARM_RELOC_VANILLA: - if ( reloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_VANILLA"; - - pointerValue = instruction; - kind = arm::kPointer; - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - kind = arm::kReadOnlyPointer; - if ( weakImport ) - kind = arm::kPointerWeakImport; - if ( reloc->r_extern() ) { - const macho_nlist

* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - if ( (targetSymbol->n_desc() & N_ARM_THUMB_DEF) && (pointerValue == 1) ) - pointerValue = 0; - makeByNameReference(kind, srcAddr, targetName, pointerValue); - } - else { - AtomAndOffset at = findAtomAndOffset(srcAddr); - AtomAndOffset to = findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum()); - if ( to.atom->isThumb() ) - to.offset &= -2; - new Reference(kind, at, to); - } - break; - - case ARM_THUMB_32BIT_BRANCH: - // ignore old unnecessary relocs - break; - - default: - warning("unexpected relocation type %u", reloc->r_type()); - break; - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - int32_t addend; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - uint32_t betterDstAddr; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - instruction = LittleEndian::get32(*fixUpPtr); - - // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF} - // relocation types, and it is an error to see one otherwise. - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - - switch (sreloc->r_type()) { - case ARM_RELOC_VANILLA: - if ( sreloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_VANILLA"; - - //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); - betterDstAddr = LittleEndian::get32(*fixUpPtr); - kind = arm::kPointer; - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - kind = arm::kReadOnlyPointer; - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - makeReferenceWithToBase(kind, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_RELOC_BR24: - // Sign-extend displacement - displacement = (instruction & 0x00FFFFFF) << 2; - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - // The pc added will be +8 from the pc - displacement += 8; - // If this is BLX add H << 1 - if ((instruction & 0xFE000000) == 0xFA000000) - displacement += ((instruction & 0x01000000) >> 23); - betterDstAddr = srcAddr+displacement; - makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_THUMB_RELOC_BR22: - // thumb2 added two more bits to displacement, complicating the displacement decoding - { - uint32_t s = (instruction >> 10) & 0x1; - uint32_t j1 = (instruction >> 29) & 0x1; - uint32_t j2 = (instruction >> 27) & 0x1; - uint32_t imm10 = instruction & 0x3FF; - uint32_t imm11 = (instruction >> 16) & 0x7FF; - uint32_t i1 = (j1 == s); - uint32_t i2 = (j2 == s); - uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); - int32_t sdis = dis; - if ( s ) - sdis |= 0xFE000000; - displacement = sdis; - } - // The pc added will be +4 from the pc - displacement += 4; - betterDstAddr = srcAddr+displacement; - // If the instruction was blx, force the low 2 bits to be clear - if ((instruction & 0xF8000000) == 0xE8000000) - betterDstAddr &= 0xFFFFFFFC; - makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_RELOC_SECTDIFF: - case ARM_RELOC_LOCAL_SECTDIFF: - if ( !nextRelocIsPair ) { - throw "ARM_RELOC_SECTDIFF missing following pair"; - } - if ( sreloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_SECTDIFF"; - { - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - pointerValue = LittleEndian::get32(*fixUpPtr); - addend = pointerValue - (dstAddr - nextRelocValue); - if ( toao.atom->isThumb() && (addend & 1) ) - addend &= -2; // remove thumb bit - if ( (dstAddr - nextRelocValue) != pointerValue ) { - if ( fromao.atom == srcao.atom ) { - if ( ((const macho_section

*)(((BaseAtom*)(srcao.atom))->getSectionRecord()))->flags() & S_ATTR_PURE_INSTRUCTIONS ) { - int pcBaseOffset = srcao.atom->isThumb() ? 4 : 8; - if ( addend == -pcBaseOffset ) { - fromao.offset -= addend; - } - else { - toao.offset += addend; - } - } - else { - toao.offset += addend; - } - } - else if ( toao.atom == srcao.atom ) - toao.offset += addend; - else - fromao.offset -= addend; - } - new Reference(arm::kPointerDiff, srcao, fromao, toao); - } - break; - - default: - warning("unexpected srelocation type %u", sreloc->r_type()); - break; - } - } - return result; -} - -template -void Reader::addReferencesForSection(const macho_section

* sect) -{ - // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change - if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) { - switch ( sect->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - // we ignore compiler generated stubs, so ignore those relocs too - break; - default: - // ignore all relocations in __eh_frame section - if ( sect == fehFrameSection ) - return; - const macho_relocation_info

* relocs = (macho_relocation_info

*)((char*)(fHeader) + sect->reloff()); - const uint32_t relocCount = sect->nreloc(); - //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); - for (uint32_t r = 0; r < relocCount; ++r) { - try { - if ( addRelocReference(sect, &relocs[r]) ) - ++r; // skip next - } - catch (const char* msg) { - throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); - } - } - } - } -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case x86::kNoFixUp: - sprintf(temp, "reference to "); - break; - case x86::kFollowOn: - sprintf(temp, "followed by "); - break; - case x86::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case x86::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case x86::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case x86::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86::kPCRel32WeakImport: - sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc); - break; - case x86::kPCRel32: - sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86::kPCRel16: - sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc); - break; - case x86::kPCRel8: - sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc); - break; - case x86::kAbsolute32: - sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); - break; - case x86::kImageOffset32: - sprintf(temp, "offset 0x%04X, 32-bit offset of ", fFixUpOffsetInSrc); - break; - case x86::kPointerDiff24: - sprintf(temp, "offset 0x%04X, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", - fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset, - this->getFromTargetDisplayName(), fFromTarget.offset ); - return temp; - break; - case x86::kSectionOffset24: - sprintf(temp, "offset 0x%04X, 24-bit section offset of ", fFixUpOffsetInSrc); - break; - case x86::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case x86::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case x86::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case x86::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case ppc::kNoFixUp: - sprintf(temp, "reference to "); - break; - case ppc::kFollowOn: - sprintf(temp, "followed by "); - break; - case ppc::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case ppc::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case ppc::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case ppc::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc::kPointerDiff32: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc::kPointerDiff64: - throw "unsupported refrence kind"; - break; - case ppc::kBranch24WeakImport: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case ppc::kBranch24: - case ppc::kBranch14: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case ppc::kPICBaseLow16: - sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kPICBaseLow14: - sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kPICBaseHigh16: - sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kAbsLow16: - sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsLow14: - sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsHigh16: - sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsHigh16AddLow: - sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case ppc::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case ppc::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case ppc::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case ppc64::kNoFixUp: - sprintf(temp, "reference to "); - break; - case ppc64::kFollowOn: - sprintf(temp, "followed by "); - break; - case ppc64::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case ppc64::kPointerWeakImport: - sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); - break; - case ppc64::kPointer: - sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); - break; - case ppc64::kPointerDiff64: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kPointerDiff32: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kBranch24WeakImport: - sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case ppc64::kBranch24: - case ppc64::kBranch14: - sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case ppc64::kPICBaseLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kPICBaseLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kPICBaseHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kAbsLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsHigh16AddLow: - sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceProbe: - sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceProbeSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceTypeReference: - sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); - - return temp; -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case x86_64::kNoFixUp: - sprintf(temp, "reference to "); - break; - case x86_64::kFollowOn: - sprintf(temp, "followed by "); - break; - case x86_64::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case x86_64::kPointerWeakImport: - sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointer: - sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointer32: - sprintf(temp, "offset 0x%04llX, 32-bit pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointerDiff32: - case x86_64::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; - sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, size, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86_64::kPCRel32: - sprintf(temp, "offset 0x%04llX, rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_1: - sprintf(temp, "offset 0x%04llX, rel32-1 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_2: - sprintf(temp, "offset 0x%04llX, rel32-2 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_4: - sprintf(temp, "offset 0x%04llX, rel32-4 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel32: - sprintf(temp, "offset 0x%04llX, branch rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel32WeakImport: - sprintf(temp, "offset 0x%04llX, branch rel32 reference to weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOT: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTWeakImport: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTLoad: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTLoadWeakImport: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kGOTNoFixUp: - sprintf(temp, "reference to GOT entry for "); - break; - case x86_64::kBranchPCRel8: - sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointerDiff24: - sprintf(temp, "offset 0x%04llX, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", - fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset, - this->getFromTargetDisplayName(), fFromTarget.offset ); - return temp; - case x86_64::kImageOffset32: - sprintf(temp, "offset 0x%04llX, 32bit offset of ", fFixUpOffsetInSrc); - break; - case x86_64::kSectionOffset24: - sprintf(temp, "offset 0x%04llX, 24-bit section offset of ", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceProbe: - sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceProbeSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceTypeReference: - sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); - - return temp; -} - - -template <> -const char* Reference::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case arm::kNoFixUp: - sprintf(temp, "reference to "); - break; - case arm::kFollowOn: - sprintf(temp, "followed by "); - break; - case arm::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case arm::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case arm::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case arm::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case arm::kPointerDiff12: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 12-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case arm::kReadOnlyPointer: - sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc); - break; - case arm::kBranch24: - case arm::kThumbBranch22: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case arm::kBranch24WeakImport: - case arm::kThumbBranch22WeakImport: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case arm::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case arm::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case arm::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case arm::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - - -template <> -bool Reference::isBranch() const -{ - switch ( fKind ) { - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference::isBranch() const -{ - switch ( fKind ) { - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference::isBranch() const -{ - switch ( fKind ) { - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference::isBranch() const -{ - switch ( fKind ) { - case ppc64::kBranch24: - case ppc64::kBranch24WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference::isBranch() const -{ - switch ( fKind ) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - return true; - default: - return false; - } -} - - - -}; // namespace relocatable -}; // namespace mach_o - -#endif // __OBJECT_FILE_MACH_O__ diff --git a/ld64/src/ld/MachOWriterExecutable.hpp b/ld64/src/ld/MachOWriterExecutable.hpp deleted file mode 100644 index 5313669..0000000 --- a/ld64/src/ld/MachOWriterExecutable.hpp +++ /dev/null @@ -1,12114 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __EXECUTABLE_MACH_O__ -#define __EXECUTABLE_MACH_O__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "ObjectFile.h" -#include "ExecutableFile.h" -#include "Options.h" - -#include "MachOFileAbstraction.hpp" -#include "MachOTrie.hpp" - - -// -// -// To implement architecture xxx, you must write template specializations for the following methods: -// MachHeaderAtom::setHeaderInfo() -// ThreadsLoadCommandsAtom::getSize() -// ThreadsLoadCommandsAtom::copyRawContent() -// Writer::addObjectRelocs() -// Writer::fixUpReferenceRelocatable() -// Writer::fixUpReferenceFinal() -// Writer::stubableReference() -// Writer::weakImportReferenceKind() -// Writer::GOTReferenceKind() -// - - -namespace mach_o { -namespace executable { - -// forward references -template class WriterAtom; -template class PageZeroAtom; -template class CustomStackAtom; -template class MachHeaderAtom; -template class SegmentLoadCommandsAtom; -template class EncryptionLoadCommandsAtom; -template class SymbolTableLoadCommandsAtom; -template class DyldInfoLoadCommandsAtom; -template class ThreadsLoadCommandsAtom; -template class DylibIDLoadCommandsAtom; -template class RoutinesLoadCommandsAtom; -template class DyldLoadCommandsAtom; -template class UUIDLoadCommandAtom; -template class LinkEditAtom; -template class SectionRelocationsLinkEditAtom; -template class CompressedRebaseInfoLinkEditAtom; -template class CompressedBindingInfoLinkEditAtom; -template class CompressedWeakBindingInfoLinkEditAtom; -template class CompressedLazyBindingInfoLinkEditAtom; -template class CompressedExportInfoLinkEditAtom; -template class LocalRelocationsLinkEditAtom; -template class ExternalRelocationsLinkEditAtom; -template class SymbolTableLinkEditAtom; -template class SegmentSplitInfoLoadCommandsAtom; -template class SegmentSplitInfoContentAtom; -template class IndirectTableLinkEditAtom; -template class ModuleInfoLinkEditAtom; -template class StringsLinkEditAtom; -template class LoadCommandsPaddingAtom; -template class UnwindInfoAtom; -template class StubAtom; -template class StubHelperAtom; -template class ClassicStubHelperAtom; -template class HybridStubHelperAtom; -template class HybridStubHelperHelperAtom; -template class FastStubHelperAtom; -template class FastStubHelperHelperAtom; -template class LazyPointerAtom; -template class NonLazyPointerAtom; -template class DylibLoadCommandsAtom; -template class BranchIslandAtom; - - -// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes -class SectionInfo : public ObjectFile::Section { -public: - SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), - fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false), - fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false), - fAllSelfModifyingStubs(false), fAllStubHelpers(false), - fAllZeroFill(false), fVirtualSection(false), - fHasTextLocalRelocs(false), fHasTextExternalRelocs(false) - { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } - void setIndex(unsigned int index) { fIndex=index; } - std::vector fAtoms; - char fSegmentName[20]; - char fSectionName[20]; - uint64_t fFileOffset; - uint64_t fSize; - uint32_t fRelocCount; - uint32_t fRelocOffset; - uint32_t fIndirectSymbolOffset; - uint8_t fAlignment; - bool fAllLazyPointers; - bool fAllLazyDylibPointers; - bool fAllNonLazyPointers; - bool fAllStubs; - bool fAllSelfModifyingStubs; - bool fAllStubHelpers; - bool fAllZeroFill; - bool fVirtualSection; - bool fHasTextLocalRelocs; - bool fHasTextExternalRelocs; -}; - -// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes -class SegmentInfo -{ -public: - SegmentInfo(uint64_t pageSize) : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), - fBaseAddress(0), fSize(0), fPageSize(pageSize), fFixedAddress(false), - fIndependentAddress(false), fHasLoadCommand(true) { fName[0] = '\0'; } - std::vector fSections; - char fName[20]; - uint32_t fInitProtection; - uint32_t fMaxProtection; - uint64_t fFileOffset; - uint64_t fFileSize; - uint64_t fBaseAddress; - uint64_t fSize; - uint64_t fPageSize; - bool fFixedAddress; - bool fIndependentAddress; - bool fHasLoadCommand; -}; - - -struct RebaseInfo { - RebaseInfo(uint8_t t, uint64_t addr) : fType(t), fAddress(addr) {} - uint8_t fType; - uint64_t fAddress; - // for sorting - int operator<(const RebaseInfo& rhs) const { - // sort by type, then address - if ( this->fType != rhs.fType ) - return (this->fType < rhs.fType ); - return (this->fAddress < rhs.fAddress ); - } -}; - -struct BindingInfo { - BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t addend) - : fType(t), fFlags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), fLibraryOrdinal(ord), - fSymbolName(sym), fAddress(addr), fAddend(addend) {} - BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t addend) - : fType(t), fFlags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ), fLibraryOrdinal(0), - fSymbolName(sym), fAddress(addr), fAddend(addend) {} - uint8_t fType; - uint8_t fFlags; - int fLibraryOrdinal; - const char* fSymbolName; - uint64_t fAddress; - int64_t fAddend; - - // for sorting - int operator<(const BindingInfo& rhs) const { - // sort by library, symbol, type, then address - if ( this->fLibraryOrdinal != rhs.fLibraryOrdinal ) - return (this->fLibraryOrdinal < rhs.fLibraryOrdinal ); - if ( this->fSymbolName != rhs.fSymbolName ) - return ( strcmp(this->fSymbolName, rhs.fSymbolName) < 0 ); - if ( this->fType != rhs.fType ) - return (this->fType < rhs.fType ); - return (this->fAddress < rhs.fAddress ); - } -}; - - -class ByteStream { -private: - std::vector fData; -public: - std::vector& bytes() { return fData; } - unsigned long size() const { return fData.size(); } - void reserve(unsigned long l) { fData.reserve(l); } - const uint8_t* start() const { return &fData[0]; } - - void append_uleb128(uint64_t value) { - uint8_t byte; - do { - byte = value & 0x7F; - value &= ~0x7F; - if ( value != 0 ) - byte |= 0x80; - fData.push_back(byte); - value = value >> 7; - } while( byte >= 0x80 ); - } - - void append_sleb128(int64_t value) { - bool isNeg = ( value < 0 ); - uint8_t byte; - bool more; - do { - byte = value & 0x7F; - value = value >> 7; - if ( isNeg ) - more = ( (value != -1) || ((byte & 0x40) == 0) ); - else - more = ( (value != 0) || ((byte & 0x40) != 0) ); - if ( more ) - byte |= 0x80; - fData.push_back(byte); - } - while( more ); - } - - void append_string(const char* str) { - for (const char* s = str; *s != '\0'; ++s) - fData.push_back(*s); - fData.push_back('\0'); - } - - void append_byte(uint8_t byte) { - fData.push_back(byte); - } - - static unsigned int uleb128_size(uint64_t value) { - uint32_t result = 0; - do { - value = value >> 7; - ++result; - } while ( value != 0 ); - return result; - } - - void pad_to_size(unsigned int alignment) { - while ( (fData.size() % alignment) != 0 ) - fData.push_back(0); - } -}; - - -template -class Writer : public ExecutableFile::Writer -{ -public: - Writer(const char* path, Options& options, std::vector& dynamicLibraries); - virtual ~Writer(); - - virtual const char* getPath() { return fFilePath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms() { return fWriterSynthesizedAtoms; } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabs() { return NULL; } - - virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses); - virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); - virtual void addSynthesizedAtoms(const std::vector& existingAtoms, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool biggerThanTwoGigs, - uint32_t dylibSymbolCount, - std::vector& newAtoms); - virtual uint64_t write(std::vector& atoms, - std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, - bool createUUID, bool canScatter, - ObjectFile::Reader::CpuConstraint cpuConstraint, - std::set& atomsThatOverrideWeak, - bool hasExternalWeakDefinitions); - -private: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - - enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; - - void assignFileOffsets(); - void synthesizeStubs(const std::vector& existingAtoms, - std::vector& newAtoms); - void synthesizeKextGOT(const std::vector& existingAtoms, - std::vector& newAtoms); - void createSplitSegContent(); - void synthesizeUnwindInfoTable(); - void insertDummyStubs(); - void partitionIntoSections(); - bool addBranchIslands(); - bool createBranchIslands(); - bool isBranchThatMightNeedIsland(uint8_t kind); - uint32_t textSizeWhenMightNeedBranchIslands(); - uint32_t maxDistanceBetweenIslands(); - void adjustLoadCommandsAndPadding(); - void createDynamicLinkerCommand(); - void createDylibCommands(); - void buildLinkEdit(); - const char* getArchString(); - void writeMap(); - uint64_t writeAtoms(); - void writeNoOps(int fd, uint32_t from, uint32_t to); - void copyNoOps(uint8_t* from, uint8_t* to); - bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to); - void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref); - void collectExportedAndImportedAndLocalAtoms(); - void setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count); - void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); - void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); - void buildSymbolTable(); - bool stringsNeedLabelsInObjects(); - const char* symbolTableName(const ObjectFile::Atom* atom); - void setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); - void setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); - void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry); - void copyNlistRange(const std::vector >& entries, uint32_t startIndex); - uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); - uint8_t ordinalForLibrary(ObjectFile::Reader* file); - bool targetRequiresWeakBinding(const ObjectFile::Atom& target); - int compressedOrdinalForImortedAtom(ObjectFile::Atom* target); - bool shouldExport(const ObjectFile::Atom& atom) const; - void buildFixups(); - void adjustLinkEditSections(); - void buildObjectFileFixups(); - void buildExecutableFixups(); - bool preboundLazyPointerType(uint8_t* type); - uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; - void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; - void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; - void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, - uint8_t buffer[], bool finalLinkedImage) const; - uint32_t symbolIndex(ObjectFile::Atom& atom); - bool makesExternalRelocatableReference(ObjectFile::Atom& target) const; - uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); - uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref); - uint8_t getRelocPointerSize(); - uint64_t maxAddress(); - bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref); - bool GOTReferenceKind(uint8_t kind); - bool optimizableGOTReferenceKind(uint8_t kind); - bool weakImportReferenceKind(uint8_t kind); - unsigned int collectStabs(); - uint64_t valueForStab(const ObjectFile::Reader::Stab& stab); - uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab); - uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab); - void addStabs(uint32_t startIndex); - RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const; - bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&); - bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); - bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); - bool mightNeedPadSegment(); - void scanForAbsoluteReferences(); - bool needsModuleTable(); - void optimizeDylibReferences(); - bool indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const; - - struct DirectLibrary { - class ObjectFile::Reader* fLibrary; - bool fWeak; - bool fReExport; - }; - - friend class WriterAtom; - friend class PageZeroAtom; - friend class CustomStackAtom; - friend class MachHeaderAtom; - friend class SegmentLoadCommandsAtom; - friend class EncryptionLoadCommandsAtom; - friend class SymbolTableLoadCommandsAtom; - friend class DyldInfoLoadCommandsAtom; - friend class ThreadsLoadCommandsAtom; - friend class DylibIDLoadCommandsAtom; - friend class RoutinesLoadCommandsAtom; - friend class DyldLoadCommandsAtom; - friend class UUIDLoadCommandAtom; - friend class LinkEditAtom; - friend class SectionRelocationsLinkEditAtom; - friend class CompressedRebaseInfoLinkEditAtom; - friend class CompressedBindingInfoLinkEditAtom; - friend class CompressedWeakBindingInfoLinkEditAtom; - friend class CompressedLazyBindingInfoLinkEditAtom; - friend class CompressedExportInfoLinkEditAtom; - friend class LocalRelocationsLinkEditAtom; - friend class ExternalRelocationsLinkEditAtom; - friend class SymbolTableLinkEditAtom; - friend class SegmentSplitInfoLoadCommandsAtom; - friend class SegmentSplitInfoContentAtom; - friend class IndirectTableLinkEditAtom; - friend class ModuleInfoLinkEditAtom; - friend class StringsLinkEditAtom; - friend class LoadCommandsPaddingAtom; - friend class UnwindInfoAtom; - friend class StubAtom; - friend class StubHelperAtom; - friend class ClassicStubHelperAtom; - friend class HybridStubHelperAtom; - friend class FastStubHelperAtom; - friend class FastStubHelperHelperAtom; - friend class HybridStubHelperHelperAtom; - friend class LazyPointerAtom; - friend class NonLazyPointerAtom; - friend class DylibLoadCommandsAtom; - friend class BranchIslandAtom; - - const char* fFilePath; - Options& fOptions; - std::vector* fAllAtoms; - std::vector* fStabs; - std::set* fRegularDefAtomsThatOverrideADylibsWeakDef; - class SectionInfo* fLoadCommandsSection; - class SegmentInfo* fLoadCommandsSegment; - class MachHeaderAtom* fMachHeaderAtom; - class EncryptionLoadCommandsAtom* fEncryptionLoadCommand; - class SegmentLoadCommandsAtom* fSegmentCommands; - class SymbolTableLoadCommandsAtom* fSymbolTableCommands; - class LoadCommandsPaddingAtom* fHeaderPadding; - class UnwindInfoAtom* fUnwindInfoAtom; - class UUIDLoadCommandAtom* fUUIDAtom; - std::vector fWriterSynthesizedAtoms; - std::vector fSegmentInfos; - class SegmentInfo* fPadSegmentInfo; - class ObjectFile::Atom* fEntryPoint; - class ObjectFile::Atom* fDyldClassicHelperAtom; - class ObjectFile::Atom* fDyldCompressedHelperAtom; - class ObjectFile::Atom* fDyldLazyDylibHelper; - std::map*> fLibraryToLoadCommand; - std::map fLibraryToOrdinal; - std::map fLibraryAliases; - std::set fForcedWeakImportReaders; - std::vector fExportedAtoms; - std::vector fImportedAtoms; - std::vector fLocalSymbolAtoms; - std::vector > fLocalExtraLabels; - std::vector > fGlobalExtraLabels; - std::map fAtomToSymbolIndex; - class SectionRelocationsLinkEditAtom* fSectionRelocationsAtom; - class CompressedRebaseInfoLinkEditAtom* fCompressedRebaseInfoAtom; - class CompressedBindingInfoLinkEditAtom* fCompressedBindingInfoAtom; - class CompressedWeakBindingInfoLinkEditAtom* fCompressedWeakBindingInfoAtom; - class CompressedLazyBindingInfoLinkEditAtom* fCompressedLazyBindingInfoAtom; - class CompressedExportInfoLinkEditAtom* fCompressedExportInfoAtom; - class LocalRelocationsLinkEditAtom* fLocalRelocationsAtom; - class ExternalRelocationsLinkEditAtom* fExternalRelocationsAtom; - class SymbolTableLinkEditAtom* fSymbolTableAtom; - class SegmentSplitInfoContentAtom* fSplitCodeToDataContentAtom; - class IndirectTableLinkEditAtom* fIndirectTableAtom; - class ModuleInfoLinkEditAtom* fModuleInfoAtom; - class StringsLinkEditAtom* fStringsAtom; - class PageZeroAtom* fPageZeroAtom; - class NonLazyPointerAtom* fFastStubGOTAtom; - macho_nlist

* fSymbolTable; - std::vector > fSectionRelocs; - std::vector > fInternalRelocs; - std::vector > fExternalRelocs; - std::vector fRebaseInfo; - std::vector fBindingInfo; - std::vector fWeakBindingInfo; - std::map fStubsMap; - std::map fGOTMap; - std::vector*> fAllSynthesizedStubs; - std::vector fAllSynthesizedStubHelpers; - std::vector*> fAllSynthesizedLazyPointers; - std::vector*> fAllSynthesizedLazyDylibPointers; - std::vector*> fAllSynthesizedNonLazyPointers; - uint32_t fSymbolTableCount; - uint32_t fSymbolTableStabsCount; - uint32_t fSymbolTableStabsStartIndex; - uint32_t fSymbolTableLocalCount; - uint32_t fSymbolTableLocalStartIndex; - uint32_t fSymbolTableExportCount; - uint32_t fSymbolTableExportStartIndex; - uint32_t fSymbolTableImportCount; - uint32_t fSymbolTableImportStartIndex; - uint32_t fLargestAtomSize; - uint32_t fDylibSymbolCountUpperBound; - bool fEmitVirtualSections; - bool fHasWeakExports; - bool fReferencesWeakImports; - bool fCanScatter; - bool fWritableSegmentPastFirst4GB; - bool fNoReExportedDylibs; - bool fBiggerThanTwoGigs; - bool fSlideable; - bool fHasThumbBranches; - std::map fWeakImportMap; - std::set fDylibReadersWithNonWeakImports; - std::set fDylibReadersWithWeakImports; - SegmentInfo* fFirstWritableSegment; - ObjectFile::Reader::CpuConstraint fCpuConstraint; - uint32_t fAnonNameIndex; -}; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - - static Segment fgTextSegment; - static Segment fgPageZeroSegment; - static Segment fgLinkEditSegment; - static Segment fgStackSegment; - static Segment fgImportSegment; - static Segment fgROImportSegment; - static Segment fgDataSegment; - static Segment fgObjCSegment; - static Segment fgHeaderSegment; - - -private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; -}; - -Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true); -Segment Segment::fgTextSegment("__TEXT", true, false, true, false); -Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false); -Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true); -Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); -Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false); -Segment Segment::fgDataSegment("__DATA", true, true, false, false); -Segment Segment::fgObjCSegment("__OBJC", true, true, false, false); -Segment Segment::fgHeaderSegment("__HEADER", true, false, true, false); - - -template -class WriterAtom : public ObjectFile::Atom -{ -public: - enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; - WriterAtom(Writer& writer, Segment& segment) : fWriter(writer), fSegment(segment) { } - - virtual ObjectFile::Reader* getFile() const { return &fWriter; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return NULL; } - virtual const char* getDisplayName() const { return this->getName(); } - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return true; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual std::vector& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return true; } - virtual ObjectFile::Segment& getSegment() const { return fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return 0; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); } - virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } - virtual void setScope(Scope) { } - - -protected: - virtual ~WriterAtom() {} - typedef typename A::P P; - typedef typename A::P::E E; - - static Segment& headerSegment(Writer& writer) { return (writer.fOptions.outputKind()==Options::kPreload) - ? Segment::fgHeaderSegment : Segment::fgTextSegment; } - - static std::vector fgEmptyReferenceList; - - Writer& fWriter; - Segment& fSegment; -}; - -template std::vector WriterAtom::fgEmptyReferenceList; - - -template -class PageZeroAtom : public WriterAtom -{ -public: - PageZeroAtom(Writer& writer) : WriterAtom(writer, Segment::fgPageZeroSegment), - fSize(fWriter.fOptions.zeroPageSize()) {} - virtual const char* getDisplayName() const { return "page zero content"; } - virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fSize; } - virtual const char* getSectionName() const { return "._zeropage"; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - void setSize(uint64_t size) { fSize = size; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint64_t fSize; -}; - - -template -class DsoHandleAtom : public WriterAtom -{ -public: - DsoHandleAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment) {} - virtual const char* getName() const { return "___dso_handle"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual uint64_t getSize() const { return 0; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - virtual const char* getSectionName() const { return "._mach_header"; } - virtual void copyRawContent(uint8_t buffer[]) const {} -}; - - -template -class MachHeaderAtom : public WriterAtom -{ -public: - MachHeaderAtom(Writer& writer) : WriterAtom(writer, headerSegment(writer)) {} - virtual const char* getName() const; - virtual const char* getDisplayName() const; - virtual ObjectFile::Atom::Scope getScope() const; - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; - virtual uint64_t getSize() const { return sizeof(macho_header); } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - virtual const char* getSectionName() const { return "._mach_header"; } - virtual uint32_t getOrdinal() const { return 1; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - void setHeaderInfo(macho_header& header) const; -}; - -template -class CustomStackAtom : public WriterAtom -{ -public: - CustomStackAtom(Writer& writer); - virtual const char* getDisplayName() const { return "custom stack content"; } - virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } - virtual const char* getSectionName() const { return "._stack"; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - static bool stackGrowsDown(); -}; - -template -class LoadCommandAtom : public WriterAtom -{ -protected: - LoadCommandAtom(Writer& writer) : WriterAtom(writer, headerSegment(writer)), fOrdinal(fgCurrentOrdinal++) {} - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } - virtual const char* getSectionName() const { return "._load_commands"; } - virtual uint32_t getOrdinal() const { return fOrdinal; } - static uint64_t alignedSize(uint64_t size); -protected: - uint32_t fOrdinal; - static uint32_t fgCurrentOrdinal; -}; - -template uint32_t LoadCommandAtom::fgCurrentOrdinal = 0; - -template -class SegmentLoadCommandsAtom : public LoadCommandAtom -{ -public: - SegmentLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer), fCommandCount(0), fSize(0) - { writer.fSegmentCommands = this; } - virtual const char* getDisplayName() const { return "segment load commands"; } - virtual uint64_t getSize() const { return fSize; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void computeSize(); - void setup(); - unsigned int commandCount() { return fCommandCount; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - unsigned int fCommandCount; - uint32_t fSize; -}; - - -template -class SymbolTableLoadCommandsAtom : public LoadCommandAtom -{ -public: - SymbolTableLoadCommandsAtom(Writer&); - virtual const char* getDisplayName() const { return "symbol table load commands"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - unsigned int commandCount(); - void needDynamicTable(); -private: - using WriterAtom::fWriter; - typedef typename A::P P; - bool fNeedsDynamicSymbolTable; - macho_symtab_command fSymbolTable; - macho_dysymtab_command fDynamicSymbolTable; -}; - -template -class ThreadsLoadCommandsAtom : public LoadCommandAtom -{ -public: - ThreadsLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer) {} - virtual const char* getDisplayName() const { return "thread load commands"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint8_t* fBuffer; - uint32_t fBufferSize; -}; - -template -class DyldLoadCommandsAtom : public LoadCommandAtom -{ -public: - DyldLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} - virtual const char* getDisplayName() const { return "dyld load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom -{ -public: - SegmentSplitInfoLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} - virtual const char* getDisplayName() const { return "segment split info load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class AllowableClientLoadCommandsAtom : public LoadCommandAtom -{ -public: - AllowableClientLoadCommandsAtom(Writer& writer, const char* client) : - LoadCommandAtom(writer), clientString(client) {} - virtual const char* getDisplayName() const { return "allowable_client load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* clientString; -}; - -template -class DylibLoadCommandsAtom : public LoadCommandAtom -{ -public: - DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) - : LoadCommandAtom(writer), fInfo(info), - fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; } - virtual const char* getDisplayName() const { return "dylib load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void optimizeAway() { fOptimizedAway = true; } - bool linkedWeak() { return fInfo.options.fWeakImport; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - ExecutableFile::DyLibUsed fInfo; - bool fOptimizedAway; -}; - -template -class DylibIDLoadCommandsAtom : public LoadCommandAtom -{ -public: - DylibIDLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} - virtual const char* getDisplayName() const { return "dylib ID load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class RoutinesLoadCommandsAtom : public LoadCommandAtom -{ -public: - RoutinesLoadCommandsAtom(Writer& writer) : LoadCommandAtom(writer) {} - virtual const char* getDisplayName() const { return "routines load command"; } - virtual uint64_t getSize() const { return sizeof(macho_routines_command); } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom -{ -public: - SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name) - : LoadCommandAtom(writer), fName(name) {} - virtual const char* getDisplayName() const { return "sub-umbrella load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - typedef typename A::P P; - const char* fName; -}; - -template -class SubLibraryLoadCommandsAtom : public LoadCommandAtom -{ -public: - SubLibraryLoadCommandsAtom(Writer& writer, const char* nameStart, int nameLen) - : LoadCommandAtom(writer), fNameStart(nameStart), fNameLength(nameLen) {} - virtual const char* getDisplayName() const { return "sub-library load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fNameStart; - int fNameLength; -}; - -template -class UmbrellaLoadCommandsAtom : public LoadCommandAtom -{ -public: - UmbrellaLoadCommandsAtom(Writer& writer, const char* name) - : LoadCommandAtom(writer), fName(name) {} - virtual const char* getDisplayName() const { return "umbrella load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fName; -}; - -template -class UUIDLoadCommandAtom : public LoadCommandAtom -{ -public: - UUIDLoadCommandAtom(Writer& writer) - : LoadCommandAtom(writer), fEmit(false) {} - virtual const char* getDisplayName() const { return "uuid load command"; } - virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command) : 0; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void generate(); - void setContent(const uint8_t uuid[16]); - const uint8_t* getUUID() { return fUUID; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uuid_t fUUID; - bool fEmit; -}; - - -template -class RPathLoadCommandsAtom : public LoadCommandAtom -{ -public: - RPathLoadCommandsAtom(Writer& writer, const char* path) - : LoadCommandAtom(writer), fPath(path) {} - virtual const char* getDisplayName() const { return "rpath load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fPath; -}; - -template -class EncryptionLoadCommandsAtom : public LoadCommandAtom -{ -public: - EncryptionLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer), fStartOffset(0), - fEndOffset(0) {} - virtual const char* getDisplayName() const { return "encryption info load command"; } - virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command); } - virtual void copyRawContent(uint8_t buffer[]) const; - void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; } - void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint32_t fStartOffset; - uint32_t fEndOffset; -}; - -template -class DyldInfoLoadCommandsAtom : public LoadCommandAtom -{ -public: - DyldInfoLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer) {} - virtual const char* getDisplayName() const { return "dyld info load command"; } - virtual uint64_t getSize() const { return sizeof(macho_dyld_info_command); } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - - -template -class LoadCommandsPaddingAtom : public WriterAtom -{ -public: - LoadCommandsPaddingAtom(Writer& writer) - : WriterAtom(writer, headerSegment(writer)), fSize(0) {} - virtual const char* getDisplayName() const { return "header padding"; } - virtual uint64_t getSize() const { return fSize; } - virtual const char* getSectionName() const { return "._load_cmds_pad"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void setSize(uint64_t newSize); -private: - using WriterAtom::fWriter; - typedef typename A::P P; - uint64_t fSize; -}; - -template -class MinimalTextAtom : public WriterAtom -{ -public: - MinimalTextAtom(Writer& writer) - : WriterAtom(writer, headerSegment(writer)) {} - virtual const char* getDisplayName() const { return "minimal text"; } - virtual uint64_t getSize() const { return 0; } - virtual const char* getSectionName() const { return "__text"; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - -private: - using WriterAtom::fWriter; -}; - - -template -class UnwindInfoAtom : public WriterAtom -{ -public: - UnwindInfoAtom(Writer& writer) : WriterAtom(writer, Segment::fgTextSegment), - fHeaderSize(0), fPagesSize(0), fAlignment(4) {} - virtual const char* getName() const { return "unwind info"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual uint64_t getSize() const { return fHeaderSize+fPagesSize; } - virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } - virtual const char* getSectionName() const { return "__unwind_info"; } - virtual uint32_t getOrdinal() const { return 1; } - virtual std::vector& getReferences() const { return (std::vector&)fReferences; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsda, - ObjectFile::Atom* personalityPointer); - void generate(); - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - struct Info { ObjectFile::Atom* func; ObjectFile::Atom* fde; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; - struct LSDAEntry { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; }; - struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fde; }; - struct CompressedFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fromFunc; }; - struct CompressedEncodingFixUp { uint8_t* contentPointer; ObjectFile::Atom* fde; }; - - bool encodingMeansUseDwarf(compact_unwind_encoding_t encoding); - void compressDuplicates(std::vector& uniqueInfos); - void findCommonEncoding(const std::vector& uniqueInfos, std::map& commonEncodings); - void makeLsdaIndex(const std::vector& uniqueInfos, std::map& lsdaIndexOffsetMap); - unsigned int makeRegularSecondLevelPage(const std::vector& uniqueInfos, uint32_t pageSize, unsigned int endIndex, - uint8_t*& pageEnd); - unsigned int makeCompressedSecondLevelPage(const std::vector& uniqueInfos, - const std::map commonEncodings, - uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd); - void makePersonalityIndex(std::vector& uniqueInfos); - - - uint32_t fHeaderSize; - uint32_t fPagesSize; - uint8_t* fHeaderContent; - uint8_t* fPagesContent; - uint8_t* fPagesContentForDelete; - ObjectFile::Alignment fAlignment; - std::vector fInfos; - std::map fPersonalityIndexMap; - std::vector fLSDAIndex; - std::vector fRegFixUps; - std::vector fCompressedFixUps; - std::vector fCompressedEncodingFixUps; - std::vector fReferences; -}; - - - -template -class LinkEditAtom : public WriterAtom -{ -public: - LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment), fOrdinal(fgCurrentOrdinal++) {} - uint64_t getFileOffset() const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } - virtual uint32_t getOrdinal() const { return fOrdinal; } -private: - uint32_t fOrdinal; - static uint32_t fgCurrentOrdinal; -private: - typedef typename A::P P; -}; - -template uint32_t LinkEditAtom::fgCurrentOrdinal = 0; - -template -class SectionRelocationsLinkEditAtom : public LinkEditAtom -{ -public: - SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "section relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._section_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class CompressedInfoLinkEditAtom : public LinkEditAtom -{ -public: - CompressedInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual uint64_t getSize() const { return fEncodedData.size(); } - virtual void copyRawContent(uint8_t buffer[]) const { memcpy(buffer, fEncodedData.start(), fEncodedData.size()); } -protected: - typedef typename A::P::uint_t pint_t; - ByteStream fEncodedData; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - - - -template -class CompressedRebaseInfoLinkEditAtom : public CompressedInfoLinkEditAtom -{ -public: - CompressedRebaseInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "compressed rebase info"; } - virtual const char* getSectionName() const { return "._rebase info"; } - void encode(); -private: - using CompressedInfoLinkEditAtom::fEncodedData; - using CompressedInfoLinkEditAtom::fWriter; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - -template -class CompressedBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom -{ -public: - CompressedBindingInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "compressed binding info"; } - virtual const char* getSectionName() const { return "._binding info"; } - void encode(); -private: - using CompressedInfoLinkEditAtom::fWriter; - using CompressedInfoLinkEditAtom::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - -template -class CompressedWeakBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom -{ -public: - CompressedWeakBindingInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "compressed weak binding info"; } - virtual const char* getSectionName() const { return "._wkbinding info"; } - void encode(); -private: - using CompressedInfoLinkEditAtom::fWriter; - using CompressedInfoLinkEditAtom::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - -template -class CompressedLazyBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom -{ -public: - CompressedLazyBindingInfoLinkEditAtom(Writer& writer) : CompressedInfoLinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "compressed lazy binding info"; } - virtual const char* getSectionName() const { return "._lzbinding info"; } - void encode(); -private: - std::vector fStarts; - - using CompressedInfoLinkEditAtom::fWriter; - using CompressedInfoLinkEditAtom::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - - -template -class CompressedExportInfoLinkEditAtom : public CompressedInfoLinkEditAtom -{ -public: - CompressedExportInfoLinkEditAtom(Writer& writer) - : CompressedInfoLinkEditAtom(writer), fStartNode(strdup("")) { } - virtual const char* getDisplayName() const { return "compressed export info"; } - virtual const char* getSectionName() const { return "._export info"; } - void encode(); -private: - using WriterAtom::fWriter; - using CompressedInfoLinkEditAtom::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - struct node; - - struct edge - { - edge(const char* s, struct node* n) : fSubString(s), fChild(n) { } - ~edge() { } - const char* fSubString; - struct node* fChild; - - }; - - struct node - { - node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), - fHaveExportInfo(false), fTrieOffset(0) {} - ~node() { } - const char* fCummulativeString; - std::vector fChildren; - uint64_t fAddress; - uint32_t fFlags; - bool fOrdered; - bool fHaveExportInfo; - uint32_t fTrieOffset; - - void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { - const char* partialStr = &fullStr[strlen(fCummulativeString)]; - for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - int subStringLen = strlen(e.fSubString); - if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { - // already have matching edge, go down that path - e.fChild->addSymbol(fullStr, address, flags); - return; - } - else { - for (int i=subStringLen-1; i > 0; --i) { - if ( strncmp(e.fSubString, partialStr, i) == 0 ) { - // found a common substring, splice in new node - // was A -> C, now A -> B -> C - char* bNodeCummStr = strdup(e.fChild->fCummulativeString); - bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; - //node* aNode = this; - node* bNode = new node(bNodeCummStr); - node* cNode = e.fChild; - char* abEdgeStr = strdup(e.fSubString); - abEdgeStr[i] = '\0'; - char* bcEdgeStr = strdup(&e.fSubString[i]); - edge& abEdge = e; - abEdge.fSubString = abEdgeStr; - abEdge.fChild = bNode; - edge bcEdge(bcEdgeStr, cNode); - bNode->fChildren.push_back(bcEdge); - bNode->addSymbol(fullStr, address, flags); - return; - } - } - } - } - // no commonality with any existing child, make a new edge that is this whole string - node* newNode = new node(strdup(fullStr)); - edge newEdge(strdup(partialStr), newNode); - fChildren.push_back(newEdge); - newNode->fAddress = address; - newNode->fFlags = flags; - newNode->fHaveExportInfo = true; - } - - void addOrderedNodes(const char* name, std::vector& orderedNodes) { - if ( !fOrdered ) { - orderedNodes.push_back(this); - //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); - fOrdered = true; - } - const char* partialStr = &name[strlen(fCummulativeString)]; - for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - int subStringLen = strlen(e.fSubString); - if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { - // already have matching edge, go down that path - e.fChild->addOrderedNodes(name, orderedNodes); - return; - } - } - } - - // byte for terminal node size in bytes, or 0x00 if not terminal node - // teminal node (uleb128 flags, uleb128 addr) - // byte for child node count - // each child: zero terminated substring, uleb128 node offset - bool updateOffset(uint32_t& offset) { - uint32_t nodeSize = 1; // byte for length of export info - if ( fHaveExportInfo ) - nodeSize += ByteStream::uleb128_size(fFlags) + ByteStream::uleb128_size(fAddress); - - // add children - ++nodeSize; // byte for count of chidren - for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - nodeSize += strlen(e.fSubString) + 1 + ByteStream::uleb128_size(e.fChild->fTrieOffset); - } - bool result = (fTrieOffset != offset); - fTrieOffset = offset; - //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); - offset += nodeSize; - // return true if fTrieOffset was changed - return result; - } - - void appendToStream(ByteStream& out) { - if ( fHaveExportInfo ) { - // nodes with export info: size, flags, address - out.append_byte(out.uleb128_size(fFlags) + out.uleb128_size(fAddress)); - out.append_uleb128(fFlags); - out.append_uleb128(fAddress); - } - else { - // no export info - out.append_byte(0); - } - // write number of children - out.append_byte(fChildren.size()); - // write each child - for (typename std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - out.append_string(e.fSubString); - out.append_uleb128(e.fChild->fTrieOffset); - } - } - - }; - - - struct node fStartNode; -}; - -template -class LocalRelocationsLinkEditAtom : public LinkEditAtom -{ -public: - LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "local relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._local_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class SymbolTableLinkEditAtom : public LinkEditAtom -{ -public: - SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "symbol table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._symbol_table"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class ExternalRelocationsLinkEditAtom : public LinkEditAtom -{ -public: - ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "external relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._extern_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -struct IndirectEntry { - uint32_t indirectIndex; - uint32_t symbolIndex; -}; - - -template -class SegmentSplitInfoContentAtom : public LinkEditAtom -{ -public: - SegmentSplitInfoContentAtom(Writer& writer) : LinkEditAtom(writer), fCantEncode(false) { } - virtual const char* getDisplayName() const { return "split segment info"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._split_info"; } - virtual void copyRawContent(uint8_t buffer[]) const; - bool canEncode() { return !fCantEncode; } - void setCantEncode() { fCantEncode = true; } - void add32bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind1Locations.push_back(AtomAndOffset(atom, offset)); } - void add64bitPointerLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind2Locations.push_back(AtomAndOffset(atom, offset)); } - void addPPCHi16Location(const ObjectFile::Atom* atom, uint32_t offset) { fKind3Locations.push_back(AtomAndOffset(atom, offset)); } - void add32bitImportLocation(const ObjectFile::Atom* atom, uint32_t offset) { fKind4Locations.push_back(AtomAndOffset(atom, offset)); } - void encode(); - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - struct AtomAndOffset { - AtomAndOffset(const ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} - const ObjectFile::Atom* atom; - uint32_t offset; - }; - void uleb128EncodeAddresses(const std::vector& locations); - - std::vector fKind1Locations; - std::vector fKind2Locations; - std::vector fKind3Locations; - std::vector fKind4Locations; - std::vector fEncodedData; - bool fCantEncode; -}; - -template -class IndirectTableLinkEditAtom : public LinkEditAtom -{ -public: - IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } - virtual const char* getDisplayName() const { return "indirect symbol table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._indirect_syms"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - std::vector fTable; - -private: - using WriterAtom::fWriter; - typedef typename A::P P; -}; - -template -class ModuleInfoLinkEditAtom : public LinkEditAtom -{ -public: - ModuleInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer), fModuleNameOffset(0) { } - virtual const char* getDisplayName() const { return "module table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._module_info"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); } - uint32_t getTableOfContentsFileOffset() const; - uint32_t getModuleTableFileOffset() const; - uint32_t getReferencesFileOffset() const; - uint32_t getReferencesCount() const; - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - uint32_t fModuleNameOffset; -}; - - -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - -template -class StringsLinkEditAtom : public LinkEditAtom -{ -public: - StringsLinkEditAtom(Writer& writer); - virtual const char* getDisplayName() const { return "string pool"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._string_pool"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - int32_t add(const char* name); - int32_t addUnique(const char* name); - int32_t emptyString() { return 1; } - const char* stringForIndex(int32_t) const; - -private: - using WriterAtom::fWriter; - typedef typename A::P P; - enum { kBufferSize = 0x01000000 }; - typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; - - std::vector fFullBuffers; - char* fCurrentBuffer; - uint32_t fCurrentBufferUsed; - StringToOffset fUniqueStrings; -}; - - - -template -class UndefinedSymbolProxyAtom : public WriterAtom -{ -public: - UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, Segment::fgLinkEditSegment), fName(name) {} - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual uint64_t getSize() const { return 0; } - virtual const char* getSectionName() const { return "._imports"; } -private: - using WriterAtom::fWriter; - typedef typename A::P P; - const char* fName; -}; - -template -class BranchIslandAtom : public WriterAtom -{ -public: - BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, - ObjectFile::Atom& finalTarget, uint32_t finalTargetOffset); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const; - virtual bool isThumb() const { return (fIslandKind == kBranchIslandToThumb2); } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kBranchIsland; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual const char* getSectionName() const { return "__text"; } - virtual void copyRawContent(uint8_t buffer[]) const; - uint64_t getFinalTargetAdress() const { return fFinalTarget.getAddress() + fFinalTargetOffset; } -private: - using WriterAtom::fWriter; - enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1, kBranchIslandNoPicToThumb1 }; - const char* fName; - ObjectFile::Atom& fTarget; - ObjectFile::Atom& fFinalTarget; - uint32_t fFinalTargetOffset; - IslandKind fIslandKind; -}; - -template -class StubAtom : public WriterAtom -{ -public: - StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStub; } - virtual uint64_t getSize() const; - virtual ObjectFile::Alignment getAlignment() const; - virtual const char* getSectionName() const { return "__symbol_stub1"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fTarget; } - virtual uint32_t getOrdinal() const { return fSortingOrdinal; } - void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } -private: - static const char* stubName(const char* importName); - friend class LazyPointerAtom; - using WriterAtom::fWriter; - enum StubKind { kStubPIC, kStubNoPIC, kStubShort, kJumpTable }; - const char* fName; - ObjectFile::Atom& fTarget; - std::vector fReferences; - bool fForLazyDylib; - StubKind fKind; - uint32_t fSortingOrdinal; -}; - - -template -class FastStubHelperHelperAtom : public WriterAtom -{ -public: - FastStubHelperHelperAtom(Writer& writer); - virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual uint32_t getOrdinal() const { return 0; } -protected: - using WriterAtom::fWriter; - std::vector fReferences; -}; - -template -class HybridStubHelperHelperAtom : public WriterAtom -{ -public: - HybridStubHelperHelperAtom(Writer& writer); - virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual uint32_t getOrdinal() const { return 0; } -protected: - using WriterAtom::fWriter; - std::vector fReferences; -}; - -template -class StubHelperAtom : public WriterAtom -{ -public: - StubHelperAtom(Writer& writer, ObjectFile::Atom& target, - LazyPointerAtom& lazyPointer, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fLazyPointerAtom(lazyPointer) { - writer.fAllSynthesizedStubHelpers.push_back(this); - } - - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - ObjectFile::Atom* getTarget() { return &fTarget; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual uint32_t getOrdinal() const { return 1; } -protected: - static const char* stubName(const char* importName); - using WriterAtom::fWriter; - const char* fName; - ObjectFile::Atom& fTarget; - LazyPointerAtom& fLazyPointerAtom; - std::vector fReferences; -}; - -template -class ClassicStubHelperAtom : public StubHelperAtom -{ -public: - ClassicStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib); - - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -}; - - -template -class HybridStubHelperAtom : public StubHelperAtom -{ -public: - HybridStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib); - - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - static class HybridStubHelperHelperAtom* fgHelperHelperAtom; -}; -template class HybridStubHelperHelperAtom* HybridStubHelperAtom::fgHelperHelperAtom = NULL; - -template -class FastStubHelperAtom : public StubHelperAtom -{ -public: - FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib); - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - static FastStubHelperHelperAtom* fgHelperHelperAtom; -}; -template FastStubHelperHelperAtom* FastStubHelperAtom::fgHelperHelperAtom = NULL; - - - -template -class LazyPointerAtom : public WriterAtom -{ -public: - LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, - StubAtom& stub, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fForLazyDylib ? ObjectFile::Atom::kLazyDylibPointer : ObjectFile::Atom::kLazyPointer; } - virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const; - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fExternalTarget; } - void setLazyBindingInfoOffset(uint32_t off) { fLazyBindingOffset = off; } - uint32_t getLazyBindingInfoOffset() { return fLazyBindingOffset; } - virtual uint32_t getOrdinal() const { return fSortingOrdinal; } - void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } -private: - using WriterAtom::fWriter; - static const char* lazyPointerName(const char* importName); - const char* fName; - ObjectFile::Atom& fTarget; - ObjectFile::Atom& fExternalTarget; - std::vector fReferences; - bool fForLazyDylib; - bool fCloseStub; - uint32_t fLazyBindingOffset; - uint32_t fSortingOrdinal; -}; - - -template -class NonLazyPointerAtom : public WriterAtom -{ -public: - NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target); - NonLazyPointerAtom(Writer& writer, const char* targetName); - NonLazyPointerAtom(Writer& writer); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kNonLazyPointer; } - virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const { return (fWriter.fOptions.outputKind() == Options::kKextBundle) ? "__got" : "__nl_symbol_ptr"; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return fTarget; } - virtual uint32_t getOrdinal() const { return fSortingOrdinal; } - void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } -private: - using WriterAtom::fWriter; - static const char* nonlazyPointerName(const char* importName); - const char* fName; - ObjectFile::Atom* fTarget; - std::vector fReferences; - uint32_t fSortingOrdinal; -}; - - -template -class ObjCInfoAtom : public WriterAtom -{ -public: - ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses, bool abi2override); - virtual const char* getName() const { return "objc$info"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const { return 8; } - virtual const char* getSectionName() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - Segment& getInfoSegment(bool abi2override) const; - bool fAbi2override; - uint32_t fContent[2]; -}; - - -template -class WriterReference : public ObjectFile::Reference -{ -public: - typedef typename A::ReferenceKinds Kinds; - - WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, - uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) - : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), fTargetName(target->getName()), - fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} - WriterReference(uint32_t offset, Kinds kind, const char* targetName) - : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(NULL), fTargetName(targetName), - fTargetOffset(0), fFromTarget(NULL), fFromTargetOffset(0) {} - - virtual ~WriterReference() {} - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kUnboundByName; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return (uint8_t)fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return fTargetName; } - virtual ObjectFile::Atom& getTarget() const { return *fTarget; } - virtual uint64_t getTargetOffset() const { return fTargetOffset; } - virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } - virtual const char* getFromTargetName() const { return fFromTarget->getName(); } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; } - virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } - virtual void setFromTargetName(const char* name) { } - virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; } - virtual const char* getDescription() const { return "writer reference"; } - virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } - -private: - Kinds fKind; - uint32_t fFixUpOffsetInSrc; - ObjectFile::Atom* fTarget; - const char* fTargetName; - uint32_t fTargetOffset; - ObjectFile::Atom* fFromTarget; - uint32_t fFromTargetOffset; -}; - - -template -const char* StubHelperAtom::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stubHelper", name); - return buf; -} - -template <> -ClassicStubHelperAtom::ClassicStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &fLazyPointerAtom)); - if ( forLazyDylib ) { - if ( fWriter.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, fWriter.fDyldLazyDylibHelper)); - } - else { - if ( fWriter.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, fWriter.fDyldClassicHelperAtom)); - } -} - - -template <> -uint64_t ClassicStubHelperAtom::getSize() const -{ - return 12; -} - -template <> -void ClassicStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 - buffer[1] = 0x8D; - buffer[2] = 0x1D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0xE9; // jmp dyld_stub_binding_helper - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; -} - - -template <> -FastStubHelperHelperAtom::FastStubHelperHelperAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment) -{ - fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, new NonLazyPointerAtom(writer))); - fReferences.push_back(new WriterReference(11, x86_64::kPCRel32, writer.fFastStubGOTAtom)); -} - -template <> -uint64_t FastStubHelperHelperAtom::getSize() const -{ - return 16; -} - -template <> -void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x4C; // leaq dyld_mageLoaderCache(%rip),%r11 - buffer[1] = 0x8D; - buffer[2] = 0x1D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0x41; // pushq %r11 - buffer[8] = 0x53; - buffer[9] = 0xFF; // jmp *_fast_lazy_bind(%rip) - buffer[10] = 0x25; - buffer[11] = 0x00; - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x90; // nop -} - - -template <> -HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment) -{ - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference(3, x86_64::kPCRel32_1, writer.fFastStubGOTAtom)); - fReferences.push_back(new WriterReference(13, x86_64::kPCRel32, new NonLazyPointerAtom(writer))); - fReferences.push_back(new WriterReference(21, x86_64::kPCRel32, writer.fFastStubGOTAtom)); - fReferences.push_back(new WriterReference(30, x86_64::kPCRel32, writer.fDyldClassicHelperAtom)); -} - -template <> -uint64_t HybridStubHelperHelperAtom::getSize() const -{ - return 34; -} - -template <> -void HybridStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x48; // cmpl $0x00,_fast_lazy_bind - buffer[1] = 0x83; - buffer[2] = 0x3D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x74; // je 16 - buffer[9] = 0x0F; - buffer[10] = 0x4C; // leaq imageCache(%rip),%r11 - buffer[11] = 0x8D; - buffer[12] = 0x1D; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - buffer[16] = 0x00; - buffer[17] = 0x41; // pushq %r11 - buffer[18] = 0x53; - buffer[19] = 0xFF; // jmp *_fast_lazy_bind(%rip) - buffer[20] = 0x25; - buffer[21] = 0x00; - buffer[22] = 0x00; - buffer[23] = 0x00; - buffer[24] = 0x00; - buffer[25] = 0x48; // addq $8,%rsp - buffer[26] = 0x83; - buffer[27] = 0xC4; - buffer[28] = 0x08; - buffer[29] = 0xE9; // jmp dyld_stub_binding_helper - buffer[30] = 0x00; - buffer[31] = 0x00; - buffer[32] = 0x00; - buffer[33] = 0x00; -} - - -template <> -HybridStubHelperAtom::HybridStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, &fLazyPointerAtom)); - fReferences.push_back(new WriterReference(13, x86_64::kPCRel32, fgHelperHelperAtom)); -} - -template <> -uint64_t HybridStubHelperAtom::getSize() const -{ - return 18; -} - -template <> -void HybridStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushq $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 - buffer[6] = 0x8D; - buffer[7] = 0x1D; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; - buffer[12] = 0xE9; // jmp helper-helper - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - buffer[16] = 0x00; - buffer[17] = 0x90; // nop - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - -template <> -FastStubHelperAtom::FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new FastStubHelperHelperAtom::FastStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference(6, x86_64::kPCRel32, fgHelperHelperAtom)); -} - -template <> -uint64_t FastStubHelperAtom::getSize() const -{ - return 10; -} - -template <> -void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushq $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xE9; // jmp helperhelper - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - -template <> -FastStubHelperHelperAtom::FastStubHelperHelperAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment) -{ - fReferences.push_back(new WriterReference(1, x86::kAbsolute32, new NonLazyPointerAtom(writer))); - fReferences.push_back(new WriterReference(7, x86::kAbsolute32, writer.fFastStubGOTAtom)); -} - -template <> -uint64_t FastStubHelperHelperAtom::getSize() const -{ - return 12; -} - -template <> -void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $dyld_ImageLoaderCache - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xFF; // jmp *_fast_lazy_bind - buffer[6] = 0x25; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x90; // nop -} - - -template <> -FastStubHelperHelperAtom::FastStubHelperHelperAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment) -{ - fReferences.push_back(new WriterReference(28, arm::kPointerDiff, new NonLazyPointerAtom(writer), 0, this, 16)); - fReferences.push_back(new WriterReference(32, arm::kPointerDiff, writer.fFastStubGOTAtom, 0, this, 28)); -} - -template <> -uint64_t FastStubHelperHelperAtom::getSize() const -{ - return 36; -} - -template <> -void FastStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - // push lazy-info-offset - OSWriteLittleInt32(&buffer[ 0], 0, 0xe52dc004); // str ip, [sp, #-4]! - // push address of dyld_mageLoaderCache - OSWriteLittleInt32(&buffer[ 4], 0, 0xe59fc010); // ldr ip, L1 - OSWriteLittleInt32(&buffer[ 8], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[12], 0, 0xe52dc004); // str ip, [sp, #-4]! - // jump through _fast_lazy_bind - OSWriteLittleInt32(&buffer[16], 0, 0xe59fc008); // ldr ip, L2 - OSWriteLittleInt32(&buffer[20], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[24], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[28], 0, 0x00000000); // L1: .long fFastStubGOTAtom - (helperhelper+16) - OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // L2: .long _fast_lazy_bind - (helperhelper+28) -} - -template <> -ObjectFile::Alignment StubHelperAtom::getAlignment() const { return ObjectFile::Alignment(2); } - -template <> -FastStubHelperAtom::FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new FastStubHelperHelperAtom::FastStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference(4, arm::kBranch24, fgHelperHelperAtom)); -} - -template <> -uint64_t FastStubHelperAtom::getSize() const -{ - return 12; -} - -template <> -void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - OSWriteLittleInt32(&buffer[0], 0, 0xe59fc000); // ldr ip, [pc, #0] - OSWriteLittleInt32(&buffer[4], 0, 0xea000000); // b _helperhelper - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - OSWriteLittleInt32(&buffer[8], 0, fLazyPointerAtom.getLazyBindingInfoOffset()); -} - - -template <> -HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(Writer& writer) - : WriterAtom(writer, Segment::fgTextSegment) -{ - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference(2, x86::kAbsolute32, writer.fFastStubGOTAtom)); - fReferences.push_back(new WriterReference(18, x86::kPCRel32, writer.fDyldClassicHelperAtom)); - fReferences.push_back(new WriterReference(26, x86::kAbsolute32, new NonLazyPointerAtom(writer))); - fReferences.push_back(new WriterReference(32, x86::kAbsolute32, writer.fFastStubGOTAtom)); -} - -template <> -uint64_t HybridStubHelperHelperAtom::getSize() const -{ - return 36; -} - - -template <> -void HybridStubHelperHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x83; // cmpl $0x00,_fast_lazy_bind - buffer[1] = 0x3D; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0x75; // jne 22 - buffer[8] = 0x0D; - buffer[9] = 0x89; // %eax,4(%esp) - buffer[10] = 0x44; - buffer[11] = 0x24; - buffer[12] = 0x04; - buffer[13] = 0x58; // popl %eax - buffer[14] = 0x87; // xchgl (%esp),%eax - buffer[15] = 0x04; - buffer[16] = 0x24; - buffer[17] = 0xE9; // jmpl dyld_stub_binding_helper - buffer[18] = 0x00; - buffer[19] = 0x00; - buffer[20] = 0x00; - buffer[21] = 0x00; - buffer[22] = 0x83; // addl $0x04,%esp - buffer[23] = 0xC4; - buffer[24] = 0x04; - buffer[25] = 0x68; // pushl imageloadercahce - buffer[26] = 0x00; - buffer[27] = 0x00; - buffer[28] = 0x00; - buffer[29] = 0x00; - buffer[30] = 0xFF; // jmp *_fast_lazy_bind(%rip) - buffer[31] = 0x25; - buffer[32] = 0x00; - buffer[33] = 0x00; - buffer[34] = 0x00; - buffer[35] = 0x00; -} - - -template <> -ClassicStubHelperAtom::ClassicStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - fReferences.push_back(new WriterReference(1, x86::kAbsolute32, &fLazyPointerAtom)); - if ( forLazyDylib ) { - if ( fWriter.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - fReferences.push_back(new WriterReference(6, x86::kPCRel32, fWriter.fDyldLazyDylibHelper)); - } - else { - if ( fWriter.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference(6, x86::kPCRel32, fWriter.fDyldClassicHelperAtom)); - } -} - -template <> -uint64_t ClassicStubHelperAtom::getSize() const -{ - return 10; -} - -template <> -void ClassicStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $foo$lazy_ptr - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xE9; // jmp helperhelper - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; -} - -template <> -HybridStubHelperAtom::HybridStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new HybridStubHelperHelperAtom::HybridStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference(6, x86::kAbsolute32, &fLazyPointerAtom)); - fReferences.push_back(new WriterReference(11, x86::kPCRel32, fgHelperHelperAtom)); -} - - -template <> -uint64_t HybridStubHelperAtom::getSize() const -{ - return 16; -} - -template <> -void HybridStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x68; // pushl $foo$lazy_ptr - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0xE9; // jmp dyld_hybrid_stub_binding_helper - buffer[11] = 0x00; - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x90; // nop - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - - -template <> -FastStubHelperAtom::FastStubHelperAtom(Writer& writer, ObjectFile::Atom& target, - class LazyPointerAtom& lazyPointer, bool forLazyDylib) - : StubHelperAtom(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new FastStubHelperHelperAtom::FastStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference(6, x86::kPCRel32, fgHelperHelperAtom)); -} - - -template <> -uint64_t FastStubHelperAtom::getSize() const -{ - return 10; -} - -template <> -void FastStubHelperAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xE9; // jmp helperhelper - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - -template -const char* LazyPointerAtom::getSectionName() const -{ - if ( fCloseStub ) - return "__lazy_symbol"; - else if ( fForLazyDylib ) - return "__ld_symbol_ptr"; - else - return "__la_symbol_ptr"; -} - -// specialize lazy pointer for x86_64 to initially pointer to stub helper -template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - ObjectFile::Atom* helper; - if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) { - if ( writer.fOptions.makeClassicDyldInfo() ) - // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper - if ( writer.targetRequiresWeakBinding(target) ) - helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); - else - helper = new HybridStubHelperAtom(writer, target, *this, forLazyDylib); - else { - if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - helper = ⌖ - else - helper = new FastStubHelperAtom(writer, target, *this, forLazyDylib); - } - } - else { - helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); - } - fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); -} - - -// specialize lazy pointer for x86 to initially pointer to stub helper -template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - ObjectFile::Atom* helper; - if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) { - if ( writer.fOptions.makeClassicDyldInfo() ) { - // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper - if ( writer.targetRequiresWeakBinding(target) ) - helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); - else - helper = new HybridStubHelperAtom(writer, target, *this, forLazyDylib); - } - else { - if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - helper = ⌖ - else - helper = new FastStubHelperAtom(writer, target, *this, forLazyDylib); - } - } - else { - helper = new ClassicStubHelperAtom(writer, target, *this, forLazyDylib); - } - fReferences.push_back(new WriterReference(0, x86::kPointer, helper)); -} - -// specialize lazy pointer for arm to initially pointer to stub helper -template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - // The one instruction stubs must be close to the lazy pointers - if ( stub.fKind == StubAtom::kStubShort ) - fCloseStub = true; - - ObjectFile::Atom* helper; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - helper = writer.fDyldLazyDylibHelper; - } - else if ( writer.fOptions.makeCompressedDyldInfo() ) { - if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - helper = ⌖ - else - helper = new FastStubHelperAtom(writer, target, *this, forLazyDylib); - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldClassicHelperAtom; - } - fReferences.push_back(new WriterReference(0, arm::kPointer, helper)); -} - -template -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target, StubAtom& stub, bool forLazyDylib) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); -} - - - -template -const char* LazyPointerAtom::lazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$lazy_pointer", name); - return buf; -} - -template -void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - -template -NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(&target) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); -} - -template -NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer) - : WriterAtom(writer, Segment::fgDataSegment), fName("none"), fTarget(NULL) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); -} - -template -NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, const char* targetName) - : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(targetName)), fTarget(NULL) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); - fReferences.push_back(new WriterReference(0, A::kPointer, targetName)); -} - -template -const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$non_lazy_pointer", name); - return buf; -} - -template -void NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - - - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - writer.fAllSynthesizedStubs.push_back(this); - LazyPointerAtom* lp; - if ( fWriter.fOptions.prebind() ) { - // for prebound ppc, lazy pointer starts out pointing to target symbol's address - // if target is a weak definition within this linkage unit or zero if in some dylib - lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - } - else { - // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); - } - } - fKind = ( fWriter.fSlideable ? kStubPIC : kStubNoPIC ); - if ( fKind == kStubPIC ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, this, 8)); - fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, this, 8)); - } - else { - fReferences.push_back(new WriterReference(0, ppc::kAbsHigh16AddLow, lp)); - fReferences.push_back(new WriterReference(4, ppc::kAbsLow16, lp)); - } -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); - } - if ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ) - fKind = kStubPIC; - else - fKind = kStubNoPIC; - if ( fKind == kStubPIC ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, this, 8)); - fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, this, 8)); - } - else { - fReferences.push_back(new WriterReference(0, ppc64::kAbsHigh16AddLow, lp)); - fReferences.push_back(new WriterReference(4, ppc64::kAbsLow14, lp)); - } -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, (writer.fOptions.makeCompressedDyldInfo()|| forLazyDylib) ? Segment::fgTextSegment : Segment::fgImportSegment), - fName(NULL), fTarget(target), fForLazyDylib(forLazyDylib) -{ - if ( writer.fOptions.makeCompressedDyldInfo() || forLazyDylib ) { - fKind = kStubNoPIC; - fName = stubName(target.getName()); - LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference(2, x86::kAbsolute32, lp)); - writer.fAllSynthesizedStubs.push_back(this); - } - else { - fKind = kJumpTable; - if ( &target == NULL ) - asprintf((char**)&fName, "cache-line-crossing-stub %p", this); - else { - fName = stubName(target.getName()); - writer.fAllSynthesizedStubs.push_back(this); - } - } -} - - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, lp)); -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - if ( (writer.fDylibSymbolCountUpperBound < 900) - && writer.fOptions.makeCompressedDyldInfo() - && (writer.fOptions.outputKind() != Options::kDynamicLibrary) - && !forLazyDylib ) { - // dylibs might have __TEXT and __DATA pulled apart to live in shared region - // if > 1000 stubs, the displacement to the lazy pointer my be > 12 bits. - fKind = kStubShort; - } - else if ( fWriter.fSlideable ) { - fKind = kStubPIC; - } - else { - fKind = kStubNoPIC; - } - LazyPointerAtom* lp = new LazyPointerAtom(writer, target, *this, forLazyDylib); - switch ( fKind ) { - case kStubPIC: - fReferences.push_back(new WriterReference(12, arm::kPointerDiff, lp, 0, this, 12)); - break; - case kStubNoPIC: - fReferences.push_back(new WriterReference(8, arm::kReadOnlyPointer, lp)); - break; - case kStubShort: - fReferences.push_back(new WriterReference(0, arm::kPointerDiff12, lp, 0, this, 8)); - break; - default: - throw "internal error"; - } -} - - - -template -const char* StubAtom::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stub", name); - return buf; -} - -template <> -uint64_t StubAtom::getSize() const -{ - - return ( (fKind == kStubPIC) ? 32 : 16 ); -} - -template <> -uint64_t StubAtom::getSize() const -{ - return ( (fKind == kStubPIC) ? 32 : 16 ); -} - - -template <> -uint64_t StubAtom::getSize() const -{ - switch ( fKind ) { - case kStubPIC: - return 16; - case kStubNoPIC: - return 12; - case kStubShort: - return 4; - default: - throw "internal error"; - } -} - -template <> -uint64_t StubAtom::getSize() const -{ - switch ( fKind ) { - case kStubNoPIC: - return 6; - case kJumpTable: - return 5; - default: - throw "internal error"; - } -} - -template <> -uint64_t StubAtom::getSize() const -{ - return 6; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - switch ( fKind ) { - case kStubNoPIC: - return 1; - case kJumpTable: - return 0; // special case x86 self-modifying stubs to be byte aligned - default: - throw "internal error"; - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( fKind == kStubPIC ) { - OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - else { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) - OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( fKind == kStubPIC ) { - OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - else { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) - OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - switch ( fKind ) { - case kStubNoPIC: - buffer[0] = 0xFF; // jmp *foo$lazy_pointer - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - break; - case kJumpTable: - if ( fWriter.fOptions.prebind() ) { - uint32_t address = this->getAddress(); - int32_t rel32 = 0 - (address+5); - buffer[0] = 0xE9; - buffer[1] = rel32 & 0xFF; - buffer[2] = (rel32 >> 8) & 0xFF; - buffer[3] = (rel32 >> 16) & 0xFF; - buffer[4] = (rel32 >> 24) & 0xFF; - } - else { - buffer[0] = 0xF4; - buffer[1] = 0xF4; - buffer[2] = 0xF4; - buffer[3] = 0xF4; - buffer[4] = 0xF4; - } - break; - default: - throw "internal error"; - } -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; -} - -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const -{ - switch ( fKind ) { - case kStubPIC: - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 - OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) - break; - case kStubNoPIC: - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] - OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr - break; - case kStubShort: - OSWriteLittleInt32(&buffer[ 0], 0, 0xE59FF000);// ldr pc, [pc, #foo$lazy_ptr] - break; - default: - throw "internal error"; - } -} - -// x86_64 stubs are 6 bytes -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 1; -} - -template <> -const char* StubAtom::getSectionName() const -{ - return ( (fKind == kStubPIC) ? "__picsymbolstub1" : "__symbol_stub1"); -} - -template <> -const char* StubAtom::getSectionName() const -{ - return ( (fKind == kStubPIC) ? "__picsymbolstub1" : "__symbol_stub1"); -} - -template <> -const char* StubAtom::getSectionName() const -{ - switch ( fKind ) { - case kStubPIC: - return "__picsymbolstub4"; - case kStubNoPIC: - return "__symbol_stub4"; - case kStubShort: - return "__symbolstub1"; - default: - throw "internal error"; - } -} - -template <> -const char* StubAtom::getSectionName() const -{ - switch ( fKind ) { - case kStubNoPIC: - return "__symbol_stub"; - case kJumpTable: - return "__jump_table"; - default: - throw "internal error"; - } -} - - - - -struct AtomByNameSorter -{ - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) - { - return (strcmp(left->getName(), right->getName()) < 0); - } -}; - -template -struct ExternalRelocSorter -{ - bool operator()(const macho_relocation_info

& left, const macho_relocation_info

& right) - { - // sort first by symbol number - if ( left.r_symbolnum() != right.r_symbolnum() ) - return (left.r_symbolnum() < right.r_symbolnum()); - // then sort all uses of the same symbol by address - return (left.r_address() < right.r_address()); - } -}; - - -template -Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) - : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), - fAllAtoms(NULL), fStabs(NULL), fRegularDefAtomsThatOverrideADylibsWeakDef(NULL), fLoadCommandsSection(NULL), - fLoadCommandsSegment(NULL), fMachHeaderAtom(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), - fSymbolTableCommands(NULL), fHeaderPadding(NULL), fUnwindInfoAtom(NULL), - fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), - fDyldClassicHelperAtom(NULL), fDyldCompressedHelperAtom(NULL), fDyldLazyDylibHelper(NULL), - fSectionRelocationsAtom(NULL), fCompressedRebaseInfoAtom(NULL), fCompressedBindingInfoAtom(NULL), - fCompressedWeakBindingInfoAtom(NULL), fCompressedLazyBindingInfoAtom(NULL), fCompressedExportInfoAtom(NULL), - fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), - fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL), - fStringsAtom(NULL), fPageZeroAtom(NULL), fFastStubGOTAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), - fSymbolTableStabsCount(0), fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), - fLargestAtomSize(1), - fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), - fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), - fBiggerThanTwoGigs(false), fSlideable(false), fHasThumbBranches(false), - fFirstWritableSegment(NULL), fAnonNameIndex(1000) -{ - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - if ( fOptions.zeroPageSize() != 0 ) - fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom(*this)); - if ( fOptions.outputKind() == Options::kDynamicExecutable ) - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - if ( fOptions.makeCompressedDyldInfo() ) - fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - if ( fOptions.outputKind() == Options::kDynamicExecutable ) - fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); - if ( fOptions.hasCustomStack() ) - fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(new MinimalTextAtom(*this)); - if ( fOptions.needsUnwindInfoSection() ) - fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - if ( fOptions.makeCompressedDyldInfo() ) { - fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom(*this)); - } - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - case Options::kPreload: - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - case Options::kKextBundle: - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - if ( fOptions.outputKind() == Options::kDynamicLibrary ) { - fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this)); - if ( fOptions.initFunctionName() != NULL ) - fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this)); - } - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - if ( fOptions.makeCompressedDyldInfo() ) - fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - if ( fOptions.sharedRegionEligible() ) - fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(new MinimalTextAtom(*this)); - if ( fOptions.needsUnwindInfoSection() ) - fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - if ( fOptions.makeCompressedDyldInfo() ) { - fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom(*this)); - } - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - if ( fOptions.sharedRegionEligible() ) { - fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom(*this)); - } - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - if ( fOptions.outputKind() != Options::kKextBundle ) - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - if ( this->needsModuleTable() ) - fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - case Options::kObjectFile: - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - case Options::kDyld: - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom(*this)); - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); - if ( fOptions.needsUnwindInfoSection() ) - fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this)); - break; - } - - // add extra commmands - bool hasReExports = false; - uint32_t ordinal = 1; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - if ( fOptions.makeEncryptable() ) { - fEncryptionLoadCommand = new EncryptionLoadCommandsAtom(*this); - fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand); - } - // fall through - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - { - // add dylib load command atoms for all dynamic libraries - const unsigned int libCount = dynamicLibraries.size(); - for (unsigned int i=0; i < libCount; ++i) { - ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; - //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() ); - - if ( dylibInfo.options.fReExport ) { - hasReExports = true; - } - else { - const char* parentUmbrella = dylibInfo.reader->parentUmbrella(); - if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) { - const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/'); - if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) ) - hasReExports = true; - } - } - - if ( dylibInfo.options.fWeakImport ) { - fForcedWeakImportReaders.insert(dylibInfo.reader); - } - - if ( dylibInfo.options.fBundleLoader ) { - fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL; - } - else { - // see if a DylibLoadCommandsAtom has already been created for this install path - bool newDylib = true; - const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); - for (unsigned int seenLib=0; seenLib < i; ++seenLib) { - ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; - if ( !seenDylibInfo.options.fBundleLoader ) { - const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); - if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { - fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; - fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader]; - fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader; - newDylib = false; - break; - } - } - } - - if ( newDylib ) { - // assign new ordinal and check for other paired load commands - fLibraryToOrdinal[dylibInfo.reader] = ordinal++; - DylibLoadCommandsAtom* dyliblc = new DylibLoadCommandsAtom(*this, dylibInfo); - fLibraryToLoadCommand[dylibInfo.reader] = dyliblc; - fWriterSynthesizedAtoms.push_back(dyliblc); - if ( dylibInfo.options.fReExport - && !fOptions.useSimplifiedDylibReExports() - && (fOptions.outputKind() == Options::kDynamicLibrary) ) { - // see if child has sub-framework that is this - bool isSubFramework = false; - const char* childInUmbrella = dylibInfo.reader->parentUmbrella(); - if ( childInUmbrella != NULL ) { - const char* myLeaf = strrchr(fOptions.installPath(), '/'); - if ( myLeaf != NULL ) { - if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) - isSubFramework = true; - } - } - // LC_SUB_FRAMEWORK is in child, so do nothing in parent - if ( ! isSubFramework ) { - // this dylib also needs a sub_x load command - bool isFrameworkReExport = false; - const char* lastSlash = strrchr(dylibInstallPath, '/'); - if ( lastSlash != NULL ) { - char frameworkName[strlen(lastSlash)+20]; - sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); - isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); - } - if ( isFrameworkReExport ) { - // needs a LC_SUB_UMBRELLA command - fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1])); - } - else { - // needs a LC_SUB_LIBRARY command - const char* nameStart = &lastSlash[1]; - if ( lastSlash == NULL ) - nameStart = dylibInstallPath; - int len = strlen(nameStart); - const char* dot = strchr(nameStart, '.'); - if ( dot != NULL ) - len = dot - nameStart; - fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len)); - } - } - } - } - } - } - // add umbrella command if needed - if ( fOptions.umbrellaName() != NULL ) { - fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName())); - } - // add allowable client commands if used - std::vector& allowableClients = fOptions.allowableClients(); - for (std::vector::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it) - fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom(*this, *it)); - } - break; - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kDyld: - case Options::kPreload: - case Options::kKextBundle: - break; - } - fNoReExportedDylibs = !hasReExports; - - // add any rpath load commands - for(std::vector::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) { - fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom(*this, *it)); - } - - // set up fSlideable - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kStaticExecutable: - fSlideable = false; - break; - case Options::kDynamicExecutable: - fSlideable = fOptions.positionIndependentExecutable(); - break; - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kPreload: - case Options::kKextBundle: - fSlideable = true; - break; - } - - //fprintf(stderr, "ordinals table:\n"); - //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); - //} -} - -template -Writer::~Writer() -{ - if ( fFilePath != NULL ) - free((void*)fFilePath); - if ( fSymbolTable != NULL ) - delete [] fSymbolTable; -} - - -// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments -template <>bool Writer::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); } -template bool Writer::mightNeedPadSegment() { return false; } - - -template -ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) -{ - if ( fOptions.outputKind() == Options::kKextBundle ) { - return new UndefinedSymbolProxyAtom(*this, name); - } - else if ( fOptions.outputKind() == Options::kObjectFile ) { - // when doing -r -exported_symbols_list, don't create proxy for a symbol - // that is supposed to be exported. We want an error instead - // ld does not report error when -r is used and exported symbols are not defined. - if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) - return NULL; - else - return new UndefinedSymbolProxyAtom(*this, name); - } - else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) ) - return new UndefinedSymbolProxyAtom(*this, name); - else - return NULL; -} - -template -uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) -{ - // flat namespace images use zero for all ordinals - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) - return 0; - - // is an UndefinedSymbolProxyAtom - if ( lib == this ) - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) - return DYNAMIC_LOOKUP_ORDINAL; - - std::map::iterator pos = fLibraryToOrdinal.find(lib); - if ( pos != fLibraryToOrdinal.end() ) - return pos->second; - - throw "can't find ordinal for imported symbol"; -} - -template -bool Writer::targetRequiresWeakBinding(const ObjectFile::Atom& target) -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kWeakDefinition: - return true; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kTentativeDefinition: - break; - } - return false; -} - -template -int Writer::compressedOrdinalForImortedAtom(ObjectFile::Atom* target) -{ - // flat namespace images use zero for all ordinals - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) - return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; - - // is an UndefinedSymbolProxyAtom - ObjectFile::Reader* lib = target->getFile(); - if ( lib == this ) - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) - return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; - - std::map::iterator pos; - switch ( target->getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - pos = fLibraryToOrdinal.find(lib); - if ( pos != fLibraryToOrdinal.end() ) { - if ( pos->second == EXECUTABLE_ORDINAL ) - return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; - else - return pos->second; - } - break; - case ObjectFile::Atom::kWeakDefinition: - throw "compressedOrdinalForImortedAtom() should not have been called on a weak definition"; - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kTentativeDefinition: - return BIND_SPECIAL_DYLIB_SELF; - } - - throw "can't find ordinal for imported symbol"; -} - - -template -ObjectFile::Atom& Writer::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) -{ - - return *(new ObjCInfoAtom(*this, objcContraint, objcReplacementClasses, fOptions.objCABIVersion2POverride())); -} - -template -void Writer::addSynthesizedAtoms(const std::vector& existingAtoms, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool biggerThanTwoGigs, - uint32_t dylibSymbolCount, - std::vector& newAtoms) -{ - fDyldClassicHelperAtom = dyldClassicHelperAtom; - fDyldCompressedHelperAtom = dyldCompressedHelperAtom; - fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; - fBiggerThanTwoGigs = biggerThanTwoGigs; - fDylibSymbolCountUpperBound = dylibSymbolCount; - - // create inter-library stubs - synthesizeStubs(existingAtoms, newAtoms); -} - - -template -uint64_t Writer::write(std::vector& atoms, - std::vector& stabs, - class ObjectFile::Atom* entryPointAtom, - bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - std::set& atomsThatOverrideWeak, - bool hasExternalWeakDefinitions) -{ - fAllAtoms = &atoms; - fStabs = &stabs; - fEntryPoint = entryPointAtom; - fCanScatter = canScatter; - fCpuConstraint = cpuConstraint; - fHasWeakExports = hasExternalWeakDefinitions; // dyld needs to search this image as if it had weak exports - fRegularDefAtomsThatOverrideADylibsWeakDef = &atomsThatOverrideWeak; - - - try { - // Set for create UUID - if (createUUID) - fUUIDAtom->generate(); - - // remove uneeded dylib load commands - optimizeDylibReferences(); - - // check for mdynamic-no-pic codegen - scanForAbsoluteReferences(); - - // create table of unwind info - synthesizeUnwindInfoTable(); - - // create SegmentInfo and SectionInfo objects and assign all atoms to a section - partitionIntoSections(); - - // segment load command can now be sized and padding can be set - adjustLoadCommandsAndPadding(); - - // assign each section a file offset - assignFileOffsets(); - - // if need to add branch islands, reassign file offsets - if ( addBranchIslands() ) - assignFileOffsets(); - - // now that addresses are assigned, create unwind info - if ( fUnwindInfoAtom != NULL ) { - fUnwindInfoAtom->generate(); - // re-layout - adjustLoadCommandsAndPadding(); - assignFileOffsets(); - } - - // make spit-seg info now that all atoms exist - createSplitSegContent(); - - // build symbol table and relocations - buildLinkEdit(); - - // write map file if requested - writeMap(); - - // write everything - return writeAtoms(); - } catch (...) { - // clean up if any errors - (void)unlink(fFilePath); - throw; - } -} - -template -void Writer::buildLinkEdit() -{ - this->collectExportedAndImportedAndLocalAtoms(); - this->buildSymbolTable(); - this->buildFixups(); - this->adjustLinkEditSections(); -} - - - -template -uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) -{ - return atom->getAddress(); -// SectionInfo* info = (SectionInfo*)atom->getSection(); -// return info->getBaseAddress() + atom->getSectionOffset(); -} - -template <> -bool Writer::stringsNeedLabelsInObjects() -{ - return true; -} - -template -bool Writer::stringsNeedLabelsInObjects() -{ - return false; -} - -template -const char* Writer::symbolTableName(const ObjectFile::Atom* atom) -{ - static unsigned int counter = 0; - const char* name; - if ( stringsNeedLabelsInObjects() - && (atom->getContentType() == ObjectFile::Atom::kCStringType) - && (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) - asprintf((char**)&name, "LC%u", counter++); - else - name = atom->getName(); - return name; - return atom->getName(); -} - -template -void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) -{ - // set n_strx - entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom))); - - // set n_type - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { - entry->set_n_type(N_EXT | N_ABS); - } - else { - entry->set_n_type(N_EXT | N_SECT); - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { - if ( fOptions.keepPrivateExterns() ) - entry->set_n_type(N_EXT | N_SECT | N_PEXT); - } - } - - // set n_sect (section number of implementation ) - uint8_t sectionIndex = atom->getSection()->getIndex(); - entry->set_n_sect(sectionIndex); - - // the __mh_execute_header is magic and must be an absolute symbol - if ( (sectionIndex==0) - && (fOptions.outputKind() == Options::kDynamicExecutable) - && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) - entry->set_n_type(N_EXT | N_ABS); - - // set n_desc - uint16_t desc = 0; - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) - desc |= REFERENCED_DYNAMICALLY; - if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) - desc |= N_NO_DEAD_STRIP; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - desc |= N_WEAK_DEF; - fHasWeakExports = true; - } - entry->set_n_desc(desc); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - entry->set_n_value(atom->getSectionOffset()); - else - entry->set_n_value(this->getAtomLoadAddress(atom)); -} - -template -void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) -{ - // set n_strx - entry->set_n_strx(this->fStringsAtom->add(atom->getName())); - - // set n_type - if ( fOptions.outputKind() == Options::kObjectFile ) { - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) - entry->set_n_type(N_UNDF | N_EXT | N_PEXT); - else - entry->set_n_type(N_UNDF | N_EXT); - } - else { - if ( fOptions.prebind() ) - entry->set_n_type(N_PBUD | N_EXT); - else - entry->set_n_type(N_UNDF | N_EXT); - } - - // set n_sect - entry->set_n_sect(0); - - uint16_t desc = 0; - if ( fOptions.outputKind() != Options::kObjectFile ) { - // set n_desc ( high byte is library ordinal, low byte is reference type ) - std::map::iterator pos = fStubsMap.find(atom); - if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) - desc = REFERENCE_FLAG_UNDEFINED_LAZY; - else - desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY; - try { - uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); - //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); - SET_LIBRARY_ORDINAL(desc, ordinal); - } - catch (const char* msg) { - throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) { - uint8_t align = atom->getAlignment().powerOf2; - // always record custom alignment of common symbols to match what compiler does - SET_COMM_ALIGN(desc, align); - } - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) - desc |= REFERENCED_DYNAMICALLY; - if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - desc |= N_REF_TO_WEAK; - fReferencesWeakImports = true; - } - // set weak_import attribute - if ( fWeakImportMap[atom] ) - desc |= N_WEAK_REF; - entry->set_n_desc(desc); - - // set n_value, zero for import proxy and size for tentative definition - entry->set_n_value(atom->getSize()); -} - - -template -void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entry) -{ - // set n_strx - const char* symbolName = this->symbolTableName(atom); - char anonName[32]; - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { - if ( stringsNeedLabelsInObjects() && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { - // don't use 'l' labels for x86_64 strings - // x86_64 obj-c runtime confused when static lib is stripped - } - else { - sprintf(anonName, "l%u", fAnonNameIndex++); - symbolName = anonName; - } - } - entry->set_n_strx(this->fStringsAtom->add(symbolName)); - - // set n_type - uint8_t type = N_SECT; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - type = N_ABS; - if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) - type |= N_PEXT; - entry->set_n_type(type); - - // set n_sect (section number of implementation ) - uint8_t sectIndex = atom->getSection()->getIndex(); - if ( sectIndex == 0 ) { - // see synthesized lable for mach_header needs special section number... - if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) - sectIndex = 1; - } - entry->set_n_sect(sectIndex); - - // set n_desc - uint16_t desc = 0; - if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) - desc |= N_NO_DEAD_STRIP; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - desc |= N_WEAK_DEF; - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - entry->set_n_desc(desc); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - entry->set_n_value(atom->getSectionOffset()); - else - entry->set_n_value(this->getAtomLoadAddress(atom)); -} - - -template -void Writer::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) -{ - macho_nlist

entry; - - // set n_strx - entry.set_n_strx(fStringsAtom->add(name)); - - // set n_type - entry.set_n_type(N_SECT); - - // set n_sect (section number of implementation ) - entry.set_n_sect(atom.getSection()->getIndex()); - - // set n_desc - entry.set_n_desc(0); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); - - // add - fLocalExtraLabels.push_back(entry); -} - - - -template -void Writer::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) -{ - macho_nlist

entry; - - // set n_strx - entry.set_n_strx(fStringsAtom->add(name)); - - // set n_type - entry.set_n_type(N_SECT|N_EXT); - - // set n_sect (section number of implementation ) - entry.set_n_sect(atom.getSection()->getIndex()); - - // set n_desc - entry.set_n_desc(0); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - entry.set_n_value(this->getAtomLoadAddress(&atom) + offsetInAtom); - - // add - fGlobalExtraLabels.push_back(entry); -} - -template -void Writer::setNlistRange(std::vector& atoms, uint32_t startIndex, uint32_t count) -{ - macho_nlist

* entry = &fSymbolTable[startIndex]; - for (uint32_t i=0; i < count; ++i, ++entry) { - ObjectFile::Atom* atom = atoms[i]; - if ( &atoms == &fExportedAtoms ) { - this->setExportNlist(atom, entry); - } - else if ( &atoms == &fImportedAtoms ) { - this->setImportNlist(atom, entry); - } - else { - this->setLocalNlist(atom, entry); - } - } -} - -template -void Writer::copyNlistRange(const std::vector >& entries, uint32_t startIndex) -{ - for ( typename std::vector >::const_iterator it = entries.begin(); it != entries.end(); ++it) - fSymbolTable[startIndex++] = *it; -} - - -template -struct NListNameSorter -{ - NListNameSorter(StringsLinkEditAtom* pool) : fStringPool(pool) {} - - bool operator()(const macho_nlist& left, const macho_nlist& right) - { - return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0); - } -private: - StringsLinkEditAtom* fStringPool; -}; - - -template -void Writer::buildSymbolTable() -{ - fSymbolTableStabsStartIndex = 0; - fSymbolTableStabsCount = fStabs->size(); - fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; - fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size(); - fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; - fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size(); - fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; - fSymbolTableImportCount = fImportedAtoms.size(); - - // allocate symbol table - fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; - fSymbolTable = new macho_nlist

[fSymbolTableCount]; - - // fill in symbol table and string pool (do stabs last so strings are at end of pool) - setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size()); - if ( fLocalExtraLabels.size() != 0 ) - copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size()); - setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size()); - if ( fGlobalExtraLabels.size() != 0 ) { - copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size()); - // re-sort combined range - std::sort( &fSymbolTable[fSymbolTableExportStartIndex], - &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount], - NListNameSorter(fStringsAtom) ); - } - setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); - addStabs(fSymbolTableStabsStartIndex); - - // set up module table - if ( fModuleInfoAtom != NULL ) - fModuleInfoAtom->setName(); - - // create atom to symbol index map - // imports - int i = 0; - for(std::vector::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableImportStartIndex; - ++i; - } - // locals - i = 0; - for(std::vector::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableLocalStartIndex; - ++i; - } - // exports - i = 0; - for(std::vector::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableExportStartIndex; - ++i; - } - -} - - - -template -bool Writer::shouldExport(const ObjectFile::Atom& atom) const -{ - switch ( atom.getSymbolTableInclusion() ) { - case ObjectFile::Atom::kSymbolTableNotIn: - return false; - case ObjectFile::Atom::kSymbolTableInAndNeverStrip: - return true; - case ObjectFile::Atom::kSymbolTableInAsAbsolute: - case ObjectFile::Atom::kSymbolTableIn: - switch ( atom.getScope() ) { - case ObjectFile::Atom::scopeGlobal: - return true; - case ObjectFile::Atom::scopeLinkageUnit: - return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); - default: - return false; - } - break; - } - return false; -} - -template -void Writer::collectExportedAndImportedAndLocalAtoms() -{ - const int atomCount = fAllAtoms->size(); - // guess at sizes of each bucket to minimize re-allocations - fImportedAtoms.reserve(100); - fExportedAtoms.reserve(atomCount/2); - fLocalSymbolAtoms.reserve(atomCount); - - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - std::vector& sectionAtoms = (*secit)->fAtoms; - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - // only named atoms go in symbol table - if ( atom->getName() != NULL ) { - // put atom into correct bucket: imports, exports, locals - //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - fImportedAtoms.push_back(atom); - break; - case ObjectFile::Atom::kTentativeDefinition: - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { - fImportedAtoms.push_back(atom); - break; - } - // else fall into - case ObjectFile::Atom::kWeakDefinition: - if ( stringsNeedLabelsInObjects() - && (fOptions.outputKind() == Options::kObjectFile) - && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn) - && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { - fLocalSymbolAtoms.push_back(atom); - break; - } - // else fall into - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - if ( this->shouldExport(*atom) ) - fExportedAtoms.push_back(atom); - else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) - && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) - fLocalSymbolAtoms.push_back(atom); - break; - } - } - // when geneating a .o file, dtrace static probes become local labels - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); - } - } - } - // when linking kernel, old style dtrace static probes become global labels - else if ( fOptions.readerOptions().fForStatic ) { - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); - } - } - } - } - } - } - - // sort exported atoms by name - std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter()); - // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable) - std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter()); -} - - -template -uint64_t Writer::valueForStab(const ObjectFile::Reader::Stab& stab) -{ - switch ( stab.type ) { - case N_FUN: - if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { - // end of function N_FUN has size - return stab.atom->getSize(); - } - else { - // start of function N_FUN has address - return getAtomLoadAddress(stab.atom); - } - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - if ( stab.atom == NULL ) - // some weird assembly files have slines not associated with a function - return stab.value; - else - // all these stab types need their value changed from an offset in the atom to an address - return getAtomLoadAddress(stab.atom) + stab.value; - case N_STSYM: - case N_LCSYM: - case N_BNSYM: - // all these need address of atom - return getAtomLoadAddress(stab.atom);; - case N_ENSYM: - return stab.atom->getSize(); - case N_SO: - if ( stab.atom == NULL ) { - return 0; - } - else { - if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { - // end of translation unit N_SO has address of end of last atom - return getAtomLoadAddress(stab.atom) + stab.atom->getSize(); - } - else { - // start of translation unit N_SO has address of end of first atom - return getAtomLoadAddress(stab.atom); - } - } - break; - default: - return stab.value; - } -} - -template -uint32_t Writer::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) -{ - switch (stab.type) { - case N_SO: - if ( (stab.string == NULL) || stab.string[0] == '\0' ) { - return this->fStringsAtom->emptyString(); - break; - } - // fall into uniquing case - case N_SOL: - case N_BINCL: - case N_EXCL: - return this->fStringsAtom->addUnique(stab.string); - break; - default: - if ( stab.string == NULL ) - return 0; - else if ( stab.string[0] == '\0' ) - return this->fStringsAtom->emptyString(); - else - return this->fStringsAtom->add(stab.string); - } - return 0; -} - -template -uint8_t Writer::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) -{ - // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN - if ( stab.type == N_FUN ) - return stab.other; - else if ( stab.atom != NULL ) - return stab.atom->getSection()->getIndex(); - else - return stab.other; -} - -template -void Writer::addStabs(uint32_t startIndex) -{ - macho_nlist

* entry = &fSymbolTable[startIndex]; - for(std::vector::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) { - const ObjectFile::Reader::Stab& stab = *it; - entry->set_n_type(stab.type); - entry->set_n_sect(sectionIndexForStab(stab)); - entry->set_n_desc(stab.desc); - entry->set_n_value(valueForStab(stab)); - entry->set_n_strx(stringOffsetForStab(stab)); - } -} - - - -template -uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) -{ - std::map::iterator pos = fAtomToSymbolIndex.find(&atom); - if ( pos != fAtomToSymbolIndex.end() ) - return pos->second; - throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); -} - - -template <> -bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const -{ - switch ( target.getSymbolTableInclusion() ) { - case ObjectFile::Atom::kSymbolTableNotIn: - return false; - case ObjectFile::Atom::kSymbolTableInAsAbsolute: - case ObjectFile::Atom::kSymbolTableIn: - case ObjectFile::Atom::kSymbolTableInAndNeverStrip: - return true; - }; - return false; -} - -template -bool Writer::makesExternalRelocatableReference(ObjectFile::Atom& target) const -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kTentativeDefinition: - if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal ) - return false; - else - return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit); - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - return shouldExport(target); - } - return false; -} - -template -void Writer::buildFixups() -{ - if ( fOptions.outputKind() == Options::kObjectFile ) { - this->buildObjectFileFixups(); - } - else { - if ( fOptions.keepRelocations() ) - this->buildObjectFileFixups(); - this->buildExecutableFixups(); - } -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool external = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind(); - - switch ( kind ) { - case x86_64::kNoFixUp: - case x86_64::kGOTNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - return 0; - - case x86_64::kPointer: - case x86_64::kPointerWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(3); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointer32: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointerDiff32: - case x86_64::kPointerDiff: - { - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); - uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex(); - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - reloc2.set_r_address(address); - reloc2.set_r_symbolnum(fromSymbolIndex); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); - reloc2.set_r_extern(fromExternal); - reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); - fSectionRelocs.push_back(reloc1); - fSectionRelocs.push_back(reloc2); - return 2; - } - - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kDtraceProbeSite: - case x86_64::kDtraceIsEnabledSite: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_BRANCH); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_1: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_1); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_2: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_2); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_4: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_4); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kBranchPCRel8: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(0); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_BRANCH); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_GOT); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - - case x86_64::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - - case x86_64::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // generates no relocs - return 0; - } - return 0; -} - - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); - - if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) - warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", - target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); - - - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - return 0; - - case x86::kPointer: - case x86::kPointerWeakImport: - case x86::kAbsolute32: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case x86::kPointerDiff16: - case x86::kPointerDiff: - { - //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n", - // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(), - // ref->getFromTarget().getAddress(), ref->getFromTargetOffset()); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); - sreloc2->set_r_type(GENERIC_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( &ref->getFromTarget() == atom ) - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - else - sreloc2->set_r_value(ref->getFromTarget().getAddress()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case x86::kPCRel32WeakImport: - case x86::kPCRel32: - case x86::kPCRel16: - case x86::kPCRel8: - case x86::kDtraceProbeSite: - case x86::kDtraceIsEnabledSite: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case x86::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - - case x86::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - - case x86::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - - case x86::kDtraceTypeReference: - case x86::kDtraceProbe: - // generates no relocs - return 0; - - } - return 0; -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind(); - - if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) - warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", - target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); - - - switch ( kind ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - return 0; - - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kPointerDiff: - { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(ARM_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - if ( ref->getTargetOffset() >= target.getSize() ) - sreloc1->set_r_value(target.getAddress()); - else - sreloc1->set_r_value(target.getAddress()+ref->getTargetOffset()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(ARM_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( &ref->getFromTarget() == atom ) { - unsigned int pcBaseOffset = atom->isThumb() ? 4 : 8; - if ( (ref->getFromTargetOffset() > pcBaseOffset) && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()-pcBaseOffset); - } - else - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - } - else - sreloc2->set_r_value(ref->getFromTarget().getAddress()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case arm::kBranch24WeakImport: - case arm::kBranch24: - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_RELOC_BR24); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_THUMB_RELOC_BR22); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kPointerDiff12: - throw "internal error. no reloc for 12-bit pointer diffs"; - - case arm::kDtraceTypeReference: - case arm::kDtraceProbe: - // generates no relocs - return 0; - - } - return 0; -} - -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } -template <> uint64_t Writer::maxAddress() { return 0xFFFFFFFFULL; } - -template <> -uint8_t Writer::getRelocPointerSize() -{ - return 2; -} - -template <> -uint8_t Writer::getRelocPointerSize() -{ - return 3; -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - return addObjectRelocs_powerpc(atom, ref); -} - -template <> -uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - return addObjectRelocs_powerpc(atom, ref); -} - -// -// addObjectRelocs and addObjectRelocs are almost exactly the same, so -// they use a common addObjectRelocs_powerpc() method. -// -template -uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind(); - - switch ( kind ) { - case A::kNoFixUp: - case A::kFollowOn: - case A::kGroupSubordinate: - return 0; - - case A::kPointer: - case A::kPointerWeakImport: - if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) { - // use scattered reloc is target offset is outside target - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(getRelocPointerSize()); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(getRelocPointerSize()); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kPointerDiff16: - case A::kPointerDiff32: - case A::kPointerDiff64: - { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(PPC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(sreloc1->r_length()); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kBranch24WeakImport: - case A::kBranch24: - case A::kDtraceProbeSite: - case A::kDtraceIsEnabledSite: - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_type(PPC_RELOC_BR24); - reloc1.set_r_extern(isExtern); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kBranch14: - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_type(PPC_RELOC_BR14); - reloc1.set_r_extern(isExtern); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kPICBaseLow16: - case A::kPICBaseLow14: - { - pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kPICBaseHigh16: - { - pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsLow14: - case A::kAbsLow16: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() >> 16); - else - reloc2.set_r_address(toAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsHigh16: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(PPC_RELOC_HI16); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HI16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); - else - reloc2.set_r_address(toAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsHigh16AddLow: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - uint32_t overflow = 0; - if ( (toAddr & 0x00008000) != 0 ) - overflow = 0x10000; - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(PPC_RELOC_HA16); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); - else - reloc2.set_r_address(toAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kDtraceTypeReference: - case A::kDtraceProbe: - // generates no relocs - return 0; - } - return 0; -} - - - -// -// There are cases when an entry in the indirect symbol table is the magic value -// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens -// the content of the corresponding part of the __nl_symbol_pointer section -// must also change. -// -template -bool Writer::indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const -{ - // cannot use INDIRECT_SYMBOL_LOCAL to tentative definitions in object files - // because tentative defs don't have addresses - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) - return false; - - // must use INDIRECT_SYMBOL_LOCAL if there is an addend - if ( ref->getTargetOffset() != 0 ) - return true; - - // don't use INDIRECT_SYMBOL_LOCAL for external symbols - return ! this->shouldExport(ref->getTarget()); -} - - -template -void Writer::buildObjectFileFixups() -{ - uint32_t relocIndex = 0; - std::vector& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName); - std::vector& sectionAtoms = curSection->fAtoms; - if ( ! curSection->fAllZeroFill ) { - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers - || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); - curSection->fRelocOffset = relocIndex; - const int atomCount = sectionAtoms.size(); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; - //fprintf(stderr, "buildObjectFileFixups(): atom %s has %lu references\n", atom->getDisplayName(), atom->getReferences().size()); - std::vector& refs = atom->getReferences(); - const int refCount = refs.size(); - for (int l=0; l < refCount; ++l) { - ObjectFile::Reference* ref = refs[l]; - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers - || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) { - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / atom->getSize(); - uint32_t undefinedSymbolIndex; - if ( curSection->fAllStubs ) { - ObjectFile::Atom& stubTarget =ref->getTarget(); - ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget(); - undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); - //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); - } - else if ( curSection->fAllNonLazyPointers) { - // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - else - undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); - } - else { - // should never get here, fAllLazyPointers not used in generated .o files - undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - } - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize()); - fIndirectTableAtom->fTable.push_back(entry); - if ( curSection->fAllLazyPointers ) { - ObjectFile::Atom& target = ref->getTarget(); - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - if ( &fromTarget == NULL ) { - warning("lazy pointer %s missing initial binding", atom->getDisplayName()); - } - else { - bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition)) - && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ); - macho_relocation_info

reloc1; - reloc1.set_r_address(atom->getSectionOffset()); - reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - fSectionRelocs.push_back(reloc1); - ++relocIndex; - } - } - else if ( curSection->fAllStubs ) { - relocIndex += this->addObjectRelocs(atom, ref); - } - } - else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) { - relocIndex += this->addObjectRelocs(atom, ref); - } - } - } - curSection->fRelocCount = relocIndex - curSection->fRelocOffset; - } - } - } - - // reverse the relocs - std::reverse(fSectionRelocs.begin(), fSectionRelocs.end()); - - // now reverse section reloc offsets - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; - } - } - -} - - -template <> -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - uint64_t result; - if ( fOptions.outputKind() == Options::kKextBundle ) { - // for x86_64 kext bundles, the r_address field in relocs - // is the offset from the start address of the first segment - result = address - fSegmentInfos[0]->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("kext bundle too large: address can't fit in 31-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - // for x86_64, the r_address field in relocs for final linked images - // is the offset from the start address of the first writable segment - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) - throwf("text relocs not supported for x86_64 in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - else - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - return result; -} - - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) - return true; - } - return false; -} - - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) - return true; - } - return false; -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return fSlideable; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // illegal until we support TEXT relocs - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - // absolute symbbols only allowed in static executables - return ( fOptions.outputKind() != Options::kStaticExecutable); - } - } - return false; -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - if ( fOptions.outputKind() == Options::kKextBundle ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // true means we need a TEXT relocs - switch ( ref.getKind() ) { - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - return true; - } - break; - } - } - return false; -} - -template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( fOptions.outputKind()) { - case Options::kStaticExecutable: - case Options::kPreload: - // all relocations allowed in static executables - return false; - default: - break; - } - if ( ref.getKind() == arm::kReadOnlyPointer ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return fSlideable; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // illegal until we support TEXT relocs - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - // absolute symbbols only allowed in static executables - return true; - } - } - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - macho_relocation_info

reloc; - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(sectInfo->getIndex()); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(false); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(reloc); - atomSection->fHasTextLocalRelocs = true; - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset())); - } - return true; - } - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); - reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc1.set_r_symbolnum(sectInfo->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - reloc2.set_r_address(targetAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fInternalRelocs.push_back(reloc1); - fInternalRelocs.push_back(reloc2); - atomSection->fHasTextLocalRelocs = true; - return true; - } - break; - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) { - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); - reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc1.set_r_symbolnum(sectInfo->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); - reloc2.set_r_address(targetAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fInternalRelocs.push_back(reloc1); - fInternalRelocs.push_back(reloc2); - atomSection->fHasTextLocalRelocs = true; - return true; - } - } - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == arm::kReadOnlyPointer ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - macho_relocation_info

reloc; - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(sectInfo->getIndex()); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(false); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(reloc); - atomSection->fHasTextLocalRelocs = true; - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset())); - } - return true; - } - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - // text relocs not supported (usually never needed because of RIP addressing) - return false; -} - -template <> -bool Writer::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - // text relocs not supported - return false; -} - -template <> -bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - macho_relocation_info

reloc; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // a reference to the absolute address of something in another linkage unit can be - // encoded as an external text reloc in a dylib or bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(true); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - -template <> -bool Writer::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( fOptions.outputKind() == Options::kKextBundle ) { - macho_relocation_info

reloc; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - switch ( ref.getKind() ) { - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - // a branch to something in another linkage unit is - // encoded as an external text reloc in a kext bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(true); - reloc.set_r_length(2); - reloc.set_r_extern(true); - reloc.set_r_type(X86_64_RELOC_BRANCH); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - // a load of the GOT entry for a symbol in another linkage unit is - // encoded as an external text reloc in a kext bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(true); - reloc.set_r_length(2); - reloc.set_r_extern(true); - reloc.set_r_type(X86_64_RELOC_GOT_LOAD); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - // a use of the GOT entry for a symbol in another linkage unit is - // encoded as an external text reloc in a kext bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(true); - reloc.set_r_length(2); - reloc.set_r_extern(true); - reloc.set_r_type(X86_64_RELOC_GOT); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - } - break; - } - } - return false; -} - - -template -bool Writer::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - return false; -} - - - - -template -typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - // in main executables, the only way regular symbols are indirected is if -interposable is used - if ( fOptions.outputKind() == Options::kDynamicExecutable ) { - if ( this->shouldExport(target) && fOptions.interposable(target.getName()) ) - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - } - // for flat-namespace or interposable two-level-namespace - // all references to exported symbols get indirected - else if ( this->shouldExport(target) && - ((fOptions.nameSpace() == Options::kFlatNameSpace) - || (fOptions.nameSpace() == Options::kForceFlatNameSpace) - || fOptions.interposable(target.getName())) - && (target.getName() != NULL) - && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - case ObjectFile::Atom::kWeakDefinition: - // in static executables, references to weak definitions are not indirected - if ( fOptions.outputKind() == Options::kStaticExecutable) - return kRelocNone; - // in dynamic code, all calls to global weak definitions get indirected - if ( this->shouldExport(target) ) - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - return kRelocExternal; - case ObjectFile::Atom::kAbsoluteSymbol: - return kRelocNone; - } - return kRelocNone; -} - -template -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for 32-bit architectures, the r_address field in relocs - // for final linked images is the offset from the first segment - uint64_t result = address - fSegmentInfos[0]->fBaseAddress; - if ( fOptions.outputKind() == Options::kPreload ) { - // kPreload uses a virtual __HEADER segment to cover the load commands - result = address - fSegmentInfos[1]->fBaseAddress; - } - // or the offset from the first writable segment if built split-seg - if ( fOptions.splitSeg() ) - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0x7FFFFFFF ) { - throwf("image too large: address can't fit in 31-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - -template <> -uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address. - // the 10.5 dyld, iterprets the r_address as: - // 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise - // 2) an offset from the base address of the first writable segment - // For dyld, r_address is always the offset from the base address - uint64_t result; - bool badFor10_4 = false; - if ( fWritableSegmentPastFirst4GB ) { - if ( fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5 ) - badFor10_4 = true; - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - result = address - fSegmentInfos[0]->fBaseAddress; - if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) && (result > 0x7FFFFFFF) ) - badFor10_4 = true; - } - if ( badFor10_4 ) { - throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - - -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } -template <> bool Writer::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; } - -template -void Writer::buildExecutableFixups() -{ - if ( fIndirectTableAtom != NULL ) - fIndirectTableAtom->fTable.reserve(50); // minimize reallocations - std::vector& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "starting section %s\n", curSection->fSectionName); - std::vector& sectionAtoms = curSection->fAtoms; - if ( ! curSection->fAllZeroFill ) { - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers - || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) { - if ( fIndirectTableAtom != NULL ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); - } - const int atomCount = sectionAtoms.size(); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; - std::vector& refs = atom->getReferences(); - const int refCount = refs.size(); - //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); - if ( curSection->fAllNonLazyPointers && (refCount == 0) ) { - // handle imageloadercache GOT slot - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / sizeof(pint_t); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - // use INDIRECT_SYMBOL_ABS so 10.5 dyld will leave value as zero - IndirectEntry entry = { indirectTableIndex, INDIRECT_SYMBOL_ABS }; - //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n", - // indirectTableIndex, INDIRECT_SYMBOL_LOCAL, curSection->fSectionName); - fIndirectTableAtom->fTable.push_back(entry); - } - for (int l=0; l < refCount; ++l) { - ObjectFile::Reference* ref = refs[l]; - if ( (fOptions.outputKind() != Options::kKextBundle) && - (curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers) ) { - // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol - if ( atom->getSize() != sizeof(pint_t) ) { - warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath()); - } - ObjectFile::Atom* pointerTarget = &(ref->getTarget()); - if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - pointerTarget = ((LazyPointerAtom*)atom)->getTarget(); - } - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / sizeof(pint_t); - uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - if (atom == fFastStubGOTAtom) - undefinedSymbolIndex = INDIRECT_SYMBOL_ABS; - else if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) - undefinedSymbolIndex = this->symbolIndex(*pointerTarget); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n", - // indirectTableIndex, undefinedSymbolIndex, curSection->fSectionName); - fIndirectTableAtom->fTable.push_back(entry); - if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - uint8_t preboundLazyType; - if ( fOptions.prebind() && (fDyldClassicHelperAtom != NULL) - && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) { - // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid - macho_scattered_relocation_info

pblaReloc; - pblaReloc.set_r_scattered(true); - pblaReloc.set_r_pcrel(false); - pblaReloc.set_r_length(); - pblaReloc.set_r_type(preboundLazyType); - pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - pblaReloc.set_r_value(fDyldClassicHelperAtom->getAddress()); - fInternalRelocs.push_back(*((macho_relocation_info

*)&pblaReloc)); - } - else if ( fSlideable ) { - // this is a non-prebound dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides - macho_relocation_info

dyldHelperReloc; - uint32_t sectionNum = 1; - if ( fDyldClassicHelperAtom != NULL ) - sectionNum = ((SectionInfo*)(fDyldClassicHelperAtom->getSection()))->getIndex(); - //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); - dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - dyldHelperReloc.set_r_symbolnum(sectionNum); - dyldHelperReloc.set_r_pcrel(false); - dyldHelperReloc.set_r_length(); - dyldHelperReloc.set_r_extern(false); - dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(dyldHelperReloc); - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - } - } - if ( fOptions.makeCompressedDyldInfo() ) { - uint8_t type = BIND_TYPE_POINTER; - uint64_t addresss = atom->getAddress() + ref->getFixUpOffset(); - if ( pointerTarget->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - // This is a referece to a weak def in some dylib (e.g. operator new) - // need to bind into to directly bind this - // later weak binding info may override - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - fBindingInfo.push_back(BindingInfo(type, ordinal, pointerTarget->getName(), false, addresss, 0)); - } - if ( targetRequiresWeakBinding(*pointerTarget) ) { - // note: lazy pointers to weak symbols are not bound lazily - fWeakBindingInfo.push_back(BindingInfo(type, pointerTarget->getName(), false, addresss, 0)); - } - } - } - if ( curSection->fAllNonLazyPointers && fOptions.makeCompressedDyldInfo() ) { - if ( pointerTarget != NULL ) { - switch ( this->relocationNeededInFinalLinkedImage(*pointerTarget) ) { - case kRelocNone: - // no rebase or binding info needed - break; - case kRelocInternal: - // a non-lazy pointer that has been optimized to LOCAL needs rebasing info - // but not the magic fFastStubGOTAtom atom - if (atom != fFastStubGOTAtom) - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - break; - case kRelocExternal: - { - uint8_t type = BIND_TYPE_POINTER; - uint64_t addresss = atom->getAddress(); - if ( targetRequiresWeakBinding(ref->getTarget()) ) { - fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, 0)); - // if this is a non-lazy pointer to a weak definition within this linkage unit - // the pointer needs to initially point within linkage unit and have - // rebase command to slide it. - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - // unless if this is a hybrid format, in which case the non-lazy pointer - // is zero on disk. So use a bind instead of a rebase to set initial value - if ( fOptions.makeClassicDyldInfo() ) - fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, 0)); - else - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - } - // if this is a non-lazy pointer to a weak definition in a dylib, - // the pointer needs to initially bind to the dylib - else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, pointerTarget->getName(), false, addresss, 0)); - } - } - else { - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - bool weak_import = fWeakImportMap[pointerTarget]; - fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, 0)); - } - } - } - } - } - } - else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) { - if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { - if ( fOptions.allowTextRelocs() ) { - if ( fOptions.warnAboutTextRelocs() ) - warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); - } - else { - throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { - case kRelocNone: - // no reloc needed - break; - case kRelocInternal: - { - macho_relocation_info

internalReloc; - SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); - uint32_t sectionNum = sectInfo->getIndex(); - // special case _mh_dylib_header and friends which are not in any real section - if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) - sectionNum = 1; - internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); - internalReloc.set_r_symbolnum(sectionNum); - internalReloc.set_r_pcrel(false); - internalReloc.set_r_length(); - internalReloc.set_r_extern(false); - internalReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(internalReloc); - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, atom->getAddress() + ref->getFixUpOffset())); - } - } - break; - case kRelocExternal: - { - macho_relocation_info

externalReloc; - externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); - externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); - externalReloc.set_r_pcrel(false); - externalReloc.set_r_length(); - externalReloc.set_r_extern(true); - externalReloc.set_r_type(GENERIC_RELOC_VANILLA); - fExternalRelocs.push_back(externalReloc); - if ( fOptions.makeCompressedDyldInfo() ) { - int64_t addend = ref->getTargetOffset(); - uint64_t addresss = atom->getAddress() + ref->getFixUpOffset(); - if ( !fOptions.makeClassicDyldInfo() ) { - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - // pointers to internal weak defs need a rebase - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, addresss)); - } - } - uint8_t type = BIND_TYPE_POINTER; - if ( targetRequiresWeakBinding(ref->getTarget()) ) { - fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, addend)); - if ( fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // hybrid linkedit puts addend in data, so we need bind phase to reset pointer to local definifion - fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, addend)); - } - // if this is a pointer to a weak definition in a dylib, - // the pointer needs to initially bind to the dylib - else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); - fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, ref->getTarget().getName(), false, addresss, addend)); - } - } - else { - int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); - bool weak_import = fWeakImportMap[&(ref->getTarget())]; - fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, addend)); - } - } - } - break; - } - } - else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { - // new x86 stubs always require text relocs - if ( curSection->fAllStubs || curSection->fAllStubHelpers ) { - if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fInternalRelocs - } - } - else if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { - if ( fOptions.warnAboutTextRelocs() ) - warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); - if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fInternalRelocs - } - else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fExternalRelocs - } - else { - throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. " - "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath()); - } - } - } - if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { - ObjectFile::Atom* stubTarget = ((StubAtom*)atom)->getTarget(); - uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS; - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / atom->getSize(); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize()); - fIndirectTableAtom->fTable.push_back(entry); - } - } - } - } - } - if ( fSplitCodeToDataContentAtom != NULL ) - fSplitCodeToDataContentAtom->encode(); - if ( fCompressedRebaseInfoAtom != NULL ) - fCompressedRebaseInfoAtom->encode(); - if ( fCompressedBindingInfoAtom != NULL ) - fCompressedBindingInfoAtom->encode(); - if ( fCompressedWeakBindingInfoAtom != NULL ) - fCompressedWeakBindingInfoAtom->encode(); - if ( fCompressedLazyBindingInfoAtom != NULL ) - fCompressedLazyBindingInfoAtom->encode(); - if ( fCompressedExportInfoAtom != NULL ) - fCompressedExportInfoAtom->encode(); -} - - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (ppc::ReferenceKinds)ref->getKind() ) { - case ppc::kPICBaseHigh16: - fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); - break; - case ppc::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc::kPointerDiff64: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc::kNoFixUp: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - // ignore - break; - default: - warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (ppc64::ReferenceKinds)ref->getKind() ) { - case ppc64::kPICBaseHigh16: - fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); - break; - case ppc64::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc64::kPointerDiff64: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc64::kNoFixUp: - case ppc64::kGroupSubordinate: - case ppc64::kPointer: - case ppc64::kPointerWeakImport: - case ppc64::kPICBaseLow16: - case ppc64::kPICBaseLow14: - // ignore - break; - default: - warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (x86::ReferenceKinds)ref->getKind() ) { - case x86::kPointerDiff: - case x86::kImageOffset32: - if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 ) - fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); - else - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86::kNoFixUp: - case x86::kGroupSubordinate: - case x86::kPointer: - case x86::kPointerWeakImport: - // ignore - break; - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment) - || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) { - fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); - break; - } - // fall into warning case - default: - if ( fOptions.makeCompressedDyldInfo() && (ref->getKind() == x86::kAbsolute32) ) { - // will be encoded in rebase info - } - else { - warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); - fSplitCodeToDataContentAtom->setCantEncode(); - } - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (x86_64::ReferenceKinds)ref->getKind() ) { - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPointerDiff32: - case x86_64::kImageOffset32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86_64::kPointerDiff: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86_64::kNoFixUp: - case x86_64::kGroupSubordinate: - case x86_64::kPointer: - case x86_64::kGOTNoFixUp: - // ignore - break; - default: - warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (arm::ReferenceKinds)ref->getKind() ) { - case arm::kPointerDiff: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case arm::kNoFixUp: - case arm::kGroupSubordinate: - case arm::kPointer: - case arm::kPointerWeakImport: - case arm::kReadOnlyPointer: - // ignore - break; - default: - warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template -bool Writer::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to) -{ - switch ( to.getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // segments with same permissions slide together - return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable()) - || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) ); - } - throw "ld64 internal error"; -} - - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint32_t ppcNop; - OSWriteBigInt32(&ppcNop, 0, 0x60000000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &ppcNop, 4, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint32_t ppcNop; - OSWriteBigInt32(&ppcNop, 0, 0x60000000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &ppcNop, 4, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint8_t x86Nop = 0x90; - for (uint32_t p=from; p < to; ++p) - ::pwrite(fd, &x86Nop, 1, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint8_t x86Nop = 0x90; - for (uint32_t p=from; p < to; ++p) - ::pwrite(fd, &x86Nop, 1, p); -} - -template <> -void Writer::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - // FIXME: need thumb nop? - uint32_t armNop; - OSWriteLittleInt32(&armNop, 0, 0xe1a00000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &armNop, 4, p); -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; ++p) - *p = 0x90; -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; ++p) - *p = 0x90; -} - -template <> -void Writer::copyNoOps(uint8_t* from, uint8_t* to) -{ - // fixme: need thumb nop? - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); -} - -static const char* stringName(const char* str) -{ - if ( strncmp(str, "cstring=", 8) == 0) { - static char buffer[1024]; - char* t = buffer; - *t++ = '\"'; - for(const char*s = &str[8]; *s != '\0'; ++s) { - switch(*s) { - case '\n': - *t++ = '\\'; - *t++ = 'n'; - break; - case '\t': - *t++ = '\\'; - *t++ = 't'; - break; - default: - *t++ = *s; - break; - } - if ( t > &buffer[1020] ) { - *t++= '\"'; - *t++= '.'; - *t++= '.'; - *t++= '.'; - *t++= '\0'; - return buffer; - } - } - *t++= '\"'; - *t++= '\0'; - return buffer; - } - else { - return str; - } -} - - -template <> const char* Writer::getArchString() { return "ppc"; } -template <> const char* Writer::getArchString() { return "ppc64"; } -template <> const char* Writer::getArchString() { return "i386"; } -template <> const char* Writer::getArchString() { return "x86_64"; } -template <> const char* Writer::getArchString() { return "arm"; } - -template -void Writer::writeMap() -{ - if ( fOptions.generatedMapPath() != NULL ) { - FILE* mapFile = fopen(fOptions.generatedMapPath(), "w"); - if ( mapFile != NULL ) { - // write output path - fprintf(mapFile, "# Path: %s\n", fFilePath); - // write output architecure - fprintf(mapFile, "# Arch: %s\n", getArchString()); - // write UUID - if ( fUUIDAtom != NULL ) { - const uint8_t* uuid = fUUIDAtom->getUUID(); - fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n", - uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); - } - // write table of object files - std::map readerToOrdinal; - std::map ordinalToReader; - std::map readerToFileOrdinal; - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - std::vector& sectionAtoms = (*secit)->fAtoms; - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Reader* reader = (*ait)->getFile(); - uint32_t readerOrdinal = (*ait)->getOrdinal(); - std::map::iterator pos = readerToOrdinal.find(reader); - if ( pos == readerToOrdinal.end() ) { - readerToOrdinal[reader] = readerOrdinal; - ordinalToReader[readerOrdinal] = reader; - } - } - } - } - } - fprintf(mapFile, "# Object files:\n"); - fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); - uint32_t fileIndex = 0; - readerToFileOrdinal[this] = fileIndex++; - for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first != 0 ) { - fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->getPath()); - readerToFileOrdinal[it->second] = fileIndex++; - } - } - // write table of sections - fprintf(mapFile, "# Sections:\n"); - fprintf(mapFile, "# Address\tSize \tSegment\tSection\n"); - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - SectionInfo* sect = *secit; - fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->getBaseAddress(), sect->fSize, - (*segit)->fName, sect->fSectionName); - } - } - } - // write table of symbols - fprintf(mapFile, "# Symbols:\n"); - fprintf(mapFile, "# Address\tSize \tFile Name\n"); - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - std::vector& sectionAtoms = (*secit)->fAtoms; - bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0); - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(), - readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName()); - } - } - } - } - fclose(mapFile); - } - else { - warning("could not write map file: %s\n", fOptions.generatedMapPath()); - } - } -} - -static const char* sCleanupFile = NULL; -static void cleanup(int sig) -{ - ::signal(sig, SIG_DFL); - if ( sCleanupFile != NULL ) { - ::unlink(sCleanupFile); - } - if ( sig == SIGINT ) - ::exit(1); -} - - -template -uint64_t Writer::writeAtoms() -{ - // for UNIX conformance, error if file exists and is not writable - if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) ) - throwf("can't write output file: %s", fFilePath); - - int permissions = 0777; - if ( fOptions.outputKind() == Options::kObjectFile ) - permissions = 0666; - // Calling unlink first assures the file is gone so that open creates it with correct permissions - // It also handles the case where fFilePath file is not writable but its directory is - // And it means we don't have to truncate the file when done writing (in case new is smaller than old) - (void)unlink(fFilePath); - - // try to allocate buffer for entire output file content - int fd = -1; - SectionInfo* lastSection = fSegmentInfos.back()->fSections.back(); - uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096); - uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1); - uint8_t* atomBuffer = NULL; - bool streaming = false; - if ( wholeBuffer == NULL ) { - fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); - atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; - streaming = true; - // install signal handlers to delete output file if program is killed - sCleanupFile = fFilePath; - ::signal(SIGINT, cleanup); - ::signal(SIGBUS, cleanup); - ::signal(SIGSEGV, cleanup); - } - uint32_t size = 0; - uint32_t end = 0; - try { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - std::vector& sectionInfos = curSegment->fSections; - for (std::vector::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - SectionInfo* curSection = *secit; - std::vector& sectionAtoms = curSection->fAtoms; - //printf("writing with max atom size 0x%X\n", fLargestAtomSize); - //fprintf(stderr, "writing %lu atoms for section %p %s at file offset 0x%08llX\n", sectionAtoms.size(), curSection, curSection->fSectionName, curSection->fFileOffset); - if ( ! curSection->fAllZeroFill ) { - bool needsNops = ((strcmp(curSection->fSegmentName, "__TEXT") == 0) && (strncmp(curSection->fSectionName, "__text", 6) == 0)); - for (std::vector::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) - && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) - && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) { - uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset(); - if ( fileOffset != end ) { - //fprintf(stderr, "writing %d pad bytes, needsNops=%d\n", fileOffset-end, needsNops); - if ( needsNops ) { - // fill gaps with no-ops - if ( streaming ) - writeNoOps(fd, end, fileOffset); - else - copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]); - } - else if ( streaming ) { - // zero fill gaps - if ( (fileOffset-end) == 4 ) { - uint32_t zero = 0; - ::pwrite(fd, &zero, 4, end); - } - else { - uint8_t zero = 0x00; - for (uint32_t p=end; p < fileOffset; ++p) - ::pwrite(fd, &zero, 1, p); - } - } - } - uint64_t atomSize = atom->getSize(); - if ( streaming ) { - if ( atomSize > fLargestAtomSize ) - throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%llX > 0x%X", - atom->getDisplayName(), atomSize, fLargestAtomSize); - } - else { - if ( fileOffset > fileBufferSize ) - throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX", - atom->getDisplayName(), fileOffset, fileBufferSize); - } - uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset]; - end = fileOffset+atomSize; - // copy raw bytes - atom->copyRawContent(buffer); - // apply any fix-ups - try { - std::vector& references = atom->getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( fOptions.outputKind() == Options::kObjectFile ) { - // doing ld -r - // skip fix-ups for undefined targets - if ( &(ref->getTarget()) != NULL ) - this->fixUpReferenceRelocatable(ref, atom, buffer); - } - else { - // producing final linked image - this->fixUpReferenceFinal(ref, atom, buffer); - } - } - } - catch (const char* msg) { - throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); - } - //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %p %s from %s\n", - // fileOffset, end, atom->getAddress(), atom->getSize(), atom, atom->getDisplayName(), atom->getFile()->getPath()); - if ( streaming ) { - // write out - ::pwrite(fd, buffer, atomSize, fileOffset); - } - else { - if ( (fileOffset + atomSize) > size ) - size = fileOffset + atomSize; - } - } - } - } - } - } - - // update content based UUID - if ( fOptions.getUUIDMode() == Options::kUUIDContent ) { - uint8_t digest[CC_MD5_DIGEST_LENGTH]; - if ( streaming ) { - // if output file file did not fit in memory, re-read file to generate md5 hash - uint32_t kMD5BufferSize = 16*1024; - uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize); - if ( md5Buffer != NULL ) { - CC_MD5_CTX md5State; - CC_MD5_Init(&md5State); - ::lseek(fd, 0, SEEK_SET); - ssize_t len; - while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 ) - CC_MD5_Update(&md5State, md5Buffer, len); - CC_MD5_Final(digest, &md5State); - ::free(md5Buffer); - } - else { - // if malloc fails, fall back to random uuid - ::uuid_generate_random(digest); - } - fUUIDAtom->setContent(digest); - uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); - fUUIDAtom->copyRawContent(atomBuffer); - ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset); - } - else { - // if output file fit in memory, just genrate an md5 hash in memory - #if 1 - // temp hack for building on Tiger - CC_MD5_CTX md5State; - CC_MD5_Init(&md5State); - CC_MD5_Update(&md5State, wholeBuffer, size); - CC_MD5_Final(digest, &md5State); - #else - CC_MD5(wholeBuffer, size, digest); - #endif - fUUIDAtom->setContent(digest); - uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); - fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]); - } - } - } - catch (...) { - if ( sCleanupFile != NULL ) - ::unlink(sCleanupFile); - throw; - } - - // finish up - if ( streaming ) { - delete [] atomBuffer; - close(fd); - // restore default signal handlers - sCleanupFile = NULL; - ::signal(SIGINT, SIG_DFL); - ::signal(SIGBUS, SIG_DFL); - ::signal(SIGSEGV, SIG_DFL); - } - else { - // write whole output file in one chunk - fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); - ::pwrite(fd, wholeBuffer, size, 0); - close(fd); - delete [] wholeBuffer; - } - - return end; -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - int64_t displacement; - int64_t baseAddr; - uint32_t instruction; - uint32_t newInstruction; - uint64_t targetAddr = 0; - uint32_t firstDisp; - uint32_t nextDisp; - uint32_t opcode = 0; - int32_t diff; - bool relocateableExternal = false; - bool is_bl; - bool is_blx; - bool targetIsThumb; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - } - - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - switch ( (arm::ReferenceKinds)(ref->getKind()) ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - // do nothing - break; - case arm::kPointerWeakImport: - case arm::kPointer: - // If this is the lazy pointers section, then set all lazy pointers to - // point to the dyld stub binding helper. - if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers - || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound lazy pointer to another dylib ==> pointer contains zero - LittleEndian::set32(*fixUp, 0); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - // prebound lazy pointer to withing this dylib ==> pointer contains address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - break; - } - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition - if ( ref->getTarget().isThumb() ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - else { - // pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - break; - case arm::kPointerDiff: - diff = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - diff |= 1; - LittleEndian::set32(*fixUp, diff); - break; - case arm::kReadOnlyPointer: - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) - targetAddr |= 1; - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // pointer contains target address - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - // pointer contains target address - LittleEndian::set32(*fixUp, targetAddr); - break; - } - break; - case arm::kBranch24WeakImport: - case arm::kBranch24: - displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - // check if this is a branch to a branch island that can be skipped - if ( ref->getTarget().getContentType() == ObjectFile::Atom::kBranchIsland ) { - uint64_t finalTargetAddress = ((BranchIslandAtom*)(&(ref->getTarget())))->getFinalTargetAdress(); - int64_t altDisplacment = finalTargetAddress - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( (altDisplacment < 33554428LL) && (altDisplacment > (-33554432LL)) ) { - //fprintf(stderr, "using altDisplacment = %lld\n", altDisplacment); - // yes, we can skip the branch island - displacement = altDisplacment; - } - } - // The pc added will be +8 from the pc - displacement -= 8; - //fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); - // max positive displacement is 0x007FFFFF << 2 - // max negative displacement is 0xFF800000 << 2 - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("b/bl/blx out of range (%lld max is +/-32M) from 0x%08llX %s in %s to 0x%08llX %s in %s", - displacement, inAtom->getAddress(), inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getAddress(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - instruction = LittleEndian::get32(*fixUp); - // Make sure we are calling arm with bl, thumb with blx - is_bl = ((instruction & 0xFF000000) == 0xEB000000); - is_blx = ((instruction & 0xFE000000) == 0xFA000000); - if ( is_bl && ref->getTarget().isThumb() ) { - uint32_t opcode = 0xFA000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; - newInstruction = opcode | h_bit | disp; - } - else if ( is_blx && !ref->getTarget().isThumb() ) { - uint32_t opcode = 0xEB000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - newInstruction = opcode | disp; - } - else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { - throwf("don't know how to convert instruction %x referencing %s to thumb", - instruction, ref->getTarget().getDisplayName()); - } - else { - newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); - } - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xD000F800) == 0xD000F000); - is_blx = ((instruction & 0xD000F800) == 0xC000F000); - targetIsThumb = ref->getTarget().isThumb(); - - // The pc added will be +4 from the pc - baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; - // If the target is not thumb, we will be generating a blx instruction - // Since blx cannot have the low bit set, set bit[1] of the target to - // bit[1] of the base address, so that the difference is a multiple of - // 4 bytes. - if ( !targetIsThumb ) { - targetAddr &= -3ULL; - targetAddr |= (baseAddr & 2LL); - } - displacement = targetAddr - baseAddr; - - // max positive displacement is 0x003FFFFE - // max negative displacement is 0xFFC00000 - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - // armv7 supports a larger displacement - if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { - if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - else { - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - if ( is_bl ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // keep bl - else - opcode = 0xC000F000; // change to blx - } - else if ( is_blx ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // change to bl - else - opcode = 0xC000F000; // keep blx - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - nextDisp = (j1 << 13) | (j2 << 11) | imm11; - firstDisp = (s << 10) | imm10; - newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - LittleEndian::set32(*fixUp, newInstruction); - } - } - else { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - else { - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; - nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; - if ( is_bl && !targetIsThumb ) { - opcode = 0xE800F000; - } - else if ( is_blx && targetIsThumb ) { - opcode = 0xF800F000; - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - else { - opcode = instruction & 0xF800F800; - } - newInstruction = opcode | (nextDisp << 16) | firstDisp; - LittleEndian::set32(*fixUp, newInstruction); - } - break; - case arm::kDtraceProbeSite: - if ( inAtom->isThumb() ) { - // change 32-bit blx call site to two thumb NOPs - LittleEndian::set32(*fixUp, 0x46C046C0); - } - else { - // change call site to a NOP - LittleEndian::set32(*fixUp, 0xE1A00000); - } - break; - case arm::kDtraceIsEnabledSite: - if ( inAtom->isThumb() ) { - // change 32-bit blx call site to 'nop', 'eor r0, r0' - LittleEndian::set32(*fixUp, 0x46C04040); - } - else { - // change call site to 'eor r0, r0, r0' - LittleEndian::set32(*fixUp, 0xE0200000); - } - break; - case arm::kDtraceTypeReference: - case arm::kDtraceProbe: - // nothing to fix up - break; - case arm::kPointerDiff12: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > 4092LL) || (displacement <-4092LL) ) { - throwf("ldr 12-bit displacement out of range (%lld max +/-4096) in %s", displacement, inAtom->getDisplayName()); - } - instruction = LittleEndian::get32(*fixUp); - if ( displacement >= 0 ) { - instruction &= 0xFFFFF000; - instruction |= ((uint32_t)displacement & 0xFFF); - } - else { - instruction &= 0xFF7FF000; - instruction |= ((uint32_t)(-displacement) & 0xFFF); - } - LittleEndian::set32(*fixUp, instruction); - break; - } -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - int64_t displacement; - uint32_t instruction; - uint32_t newInstruction; - uint64_t targetAddr = 0; - int64_t baseAddr; - uint32_t firstDisp; - uint32_t nextDisp; - uint32_t opcode = 0; - int32_t diff; - bool relocateableExternal = false; - bool is_bl; - bool is_blx; - bool targetIsThumb; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); - } - - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - switch ( (arm::ReferenceKinds)(ref->getKind()) ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - // do nothing - break; - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - LittleEndian::set32(*fixUp, targetAddr); - else - LittleEndian::set32(*fixUp, 0); - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - } - else { - // internal relocation => pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - break; - case arm::kPointerDiff: - diff = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - diff |= 1; - LittleEndian::set32(*fixUp, diff); - break; - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - case arm::kBranch24WeakImport: - case arm::kBranch24: - displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - // The pc added will be +8 from the pc - displacement -= 8; - // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - // max positive displacement is 0x007FFFFF << 2 - // max negative displacement is 0xFF800000 << 2 - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - instruction = LittleEndian::get32(*fixUp); - // Make sure we are calling arm with bl, thumb with blx - is_bl = ((instruction & 0xFF000000) == 0xEB000000); - is_blx = ((instruction & 0xFE000000) == 0xFA000000); - if ( is_bl && ref->getTarget().isThumb() ) { - uint32_t opcode = 0xFA000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; - newInstruction = opcode | h_bit | disp; - } - else if ( is_blx && !ref->getTarget().isThumb() ) { - uint32_t opcode = 0xEB000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - newInstruction = opcode | disp; - } - else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { - throwf("don't know how to convert instruction %x referencing %s to thumb", - instruction, ref->getTarget().getDisplayName()); - } - else { - newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); - } - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xF8000000) == 0xF8000000); - is_blx = ((instruction & 0xF8000000) == 0xE8000000); - targetIsThumb = ref->getTarget().isThumb(); - - // The pc added will be +4 from the pc - baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; - // If the target is not thumb, we will be generating a blx instruction - // Since blx cannot have the low bit set, set bit[1] of the target to - // bit[1] of the base address, so that the difference is a multiple of - // 4 bytes. - if (!targetIsThumb) { - targetAddr &= -3ULL; - targetAddr |= (baseAddr & 2LL); - } - displacement = targetAddr - baseAddr; - - //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - // armv7 supports a larger displacement - if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { - if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - else { - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - if ( is_bl ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // keep bl - else - opcode = 0xC000F000; // change to blx - } - else if ( is_blx ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // change to bl - else - opcode = 0xC000F000; // keep blx - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - nextDisp = (j1 << 13) | (j2 << 11) | imm11; - firstDisp = (s << 10) | imm10; - newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - LittleEndian::set32(*fixUp, newInstruction); - break; - } - } - else { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the first - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the next - // 11 bits of the displacement, as well as differentiating bl and blx. - firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; - nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; - if ( is_bl && !targetIsThumb ) { - opcode = 0xE800F000; - } - else if ( is_blx && targetIsThumb ) { - opcode = 0xF800F000; - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - else { - opcode = instruction & 0xF800F800; - } - newInstruction = opcode | (nextDisp << 16) | firstDisp; - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kDtraceProbe: - case arm::kDtraceTypeReference: - // nothing to fix up - break; - case arm::kPointerDiff12: - throw "internal error. no reloc for 12-bit pointer diffs"; - } -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - uint8_t* dtraceProbeSite; - const int64_t kTwoGigLimit = 0x7FFFFFFF; - const int64_t kSixteenMegLimit = 0x00FFFFFF; - const int64_t kSixtyFourKiloLimit = 0x7FFF; - const int64_t kOneTwentyEightLimit = 0x7F; - int64_t displacement; - uint32_t temp; - x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - // do nothing - break; - case x86::kPointerWeakImport: - case x86::kPointer: - { - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - else { - // pointer contains target address - //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86::kPointerDiff: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - LittleEndian::set32(*fixUp, (uint32_t)displacement); - break; - case x86::kPointerDiff16: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) - throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - break; - case x86::kPointerDiff24: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*fixUp); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*fixUp, temp); - break; - case x86::kSectionOffset24: - displacement = ref->getTarget().getSectionOffset(); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*fixUp); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*fixUp, temp); - break; - case x86::kDtraceProbeSite: - // change call site to a NOP - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x90; // 1-byte nop - dtraceProbeSite[0] = 0x0F; // 4-byte nop - dtraceProbeSite[1] = 0x1F; - dtraceProbeSite[2] = 0x40; - dtraceProbeSite[3] = 0x00; - break; - case x86::kDtraceIsEnabledSite: - // change call site to a clear eax - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x33; // xorl eax,eax - dtraceProbeSite[0] = 0xC0; - dtraceProbeSite[1] = 0x90; // 1-byte nop - dtraceProbeSite[2] = 0x90; // 1-byte nop - dtraceProbeSite[3] = 0x90; // 1-byte nop - break; - case x86::kPCRel32WeakImport: - case x86::kPCRel32: - case x86::kPCRel16: - case x86::kPCRel8: - displacement = 0; - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throw "codegen problem, can't use rel32 to external symbol"; - case ObjectFile::Atom::kTentativeDefinition: - displacement = 0; - break; - case ObjectFile::Atom::kAbsoluteSymbol: - displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - } - if ( kind == x86::kPCRel8 ) { - displacement += 3; - if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel8 out of range in %s", inAtom->getDisplayName()); - } - *(int8_t*)fixUp = (int8_t)displacement; - } - else if ( kind == x86::kPCRel16 ) { - displacement += 2; - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel16 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - } - else { - if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel32 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set32(*fixUp, (int32_t)displacement); - } - break; - case x86::kAbsolute32: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); - break; - } - break; - case x86::kImageOffset32: - // offset of target atom from mach_header - displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress(); - LittleEndian::set32(*fixUp, (int32_t)displacement); - break; - case x86::kDtraceTypeReference: - case x86::kDtraceProbe: - // nothing to fix up - break; - } -} - - - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t kTwoGigLimit = 0x7FFFFFFF; - const int64_t kSixtyFourKiloLimit = 0x7FFF; - const int64_t kOneTwentyEightLimit = 0x7F; - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - bool isExtern = this->makesExternalRelocatableReference(ref->getTarget()); - int64_t displacement; - x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - // do nothing - break; - case x86::kPointer: - case x86::kPointerWeakImport: - case x86::kAbsolute32: - { - if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - else - LittleEndian::set32(*fixUp, 0); - } - else if ( isExtern ) { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { - // internal relocation => pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // internal relocation to tentative ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - break; - case x86::kPointerDiff: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - LittleEndian::set32(*fixUp, (uint32_t)displacement); - break; - case x86::kPointerDiff16: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) - throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - break; - case x86::kPCRel8: - case x86::kPCRel16: - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - case x86::kDtraceProbeSite: - case x86::kDtraceIsEnabledSite: - { - if ( isExtern ) - displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - else - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - if ( kind == x86::kPCRel8 ) { - displacement += 3; - if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName()); - } - int8_t byte = (int8_t)displacement; - *((int8_t*)fixUp) = byte; - } - else if ( kind == x86::kPCRel16 ) { - displacement += 2; - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel16 out of range in %s", inAtom->getDisplayName()); - } - int16_t word = (int16_t)displacement; - LittleEndian::set16(*((uint16_t*)fixUp), word); - } - else { - if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { - //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement, - // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - throwf("rel32 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set32(*fixUp, (int32_t)displacement); - } - } - break; - case x86::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - case x86::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - case x86::kDtraceProbe: - case x86::kDtraceTypeReference: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t twoGigLimit = 0x7FFFFFFF; - const int64_t kSixteenMegLimit = 0x00FFFFFF; - uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; - uint8_t* dtraceProbeSite; - int64_t displacement = 0; - uint32_t temp; - switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { - case x86_64::kNoFixUp: - case x86_64::kGOTNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - // do nothing - break; - case x86_64::kPointerWeakImport: - case x86_64::kPointer: - { - if ( &ref->getTarget() != NULL ) { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal) { - if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); - } - } - else { - // internal relocation - // pointer contains target address - //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - } - break; - case x86_64::kPointer32: - { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - // external relocation - throwf("32-bit pointer to dylib or weak symbol %s not supported for x86_64",ref->getTarget().getDisplayName()); - } - else { - // internal relocation - // pointer contains target address - //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - displacement = ref->getTarget().getAddress() + ref->getTargetOffset(); - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kPreload: - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kKextBundle: - throwf("32-bit pointer to symbol %s not supported for x86_64",ref->getTarget().getDisplayName()); - case Options::kDynamicExecutable: - // allow x86_64 main executables to use 32-bit pointers if program loads in load 2GB - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) - throw "32-bit pointer out of range"; - break; - case Options::kStaticExecutable: - // allow x86_64 mach_kernel to truncate pointers - break; - } - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); - } - } - break; - case x86_64::kPointerDiff32: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) - throw "32-bit pointer difference out of range"; - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); - break; - case x86_64::kPointerDiff: - LittleEndian::set64(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case x86_64::kPointerDiff24: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*((uint32_t*)fixUp)); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*((uint32_t*)fixUp), temp); - break; - case x86_64::kSectionOffset24: - displacement = ref->getTarget().getSectionOffset(); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*((uint32_t*)fixUp)); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*((uint32_t*)fixUp), temp); - break; - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - // if GOT entry was optimized away, change movq instruction to a leaq - if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) { - //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName()); - uint8_t* opcodes = (uint8_t*)fixUp; - if ( opcodes[-2] != 0x8B ) - throw "GOT load reloc does not point to a movq instruction"; - opcodes[-2] = 0x8D; - } - // fall into general rel32 case - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel8: - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - displacement = (ref->getTarget().getSectionOffset() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - if ( fOptions.outputKind() == Options::kKextBundle ) - displacement = 0; - else - throwf("codegen problem, can't use rel32 to external symbol %s", ref->getTarget().getDisplayName()); - break; - } - switch ( ref->getKind() ) { - case x86_64::kPCRel32_1: - displacement -= 1; - break; - case x86_64::kPCRel32_2: - displacement -= 2; - break; - case x86_64::kPCRel32_4: - displacement -= 4; - break; - case x86_64::kBranchPCRel8: - displacement += 3; - break; - } - if ( ref->getKind() == x86_64::kBranchPCRel8 ) { - if ( (displacement > 127) || (displacement < (-128)) ) { - fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n", - inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); - throw "rel8 out of range"; - } - *((int8_t*)fixUp) = (int8_t)displacement; - } - else { - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - fprintf(stderr, "reference out of range from %s (%llX) in %s to %s (%llX) in %s\n", - inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); - throw "rel32 out of range"; - } - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - } - break; - case x86_64::kImageOffset32: - // offset of target atom from mach_header - displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress(); - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - break; - case x86_64::kDtraceProbeSite: - // change call site to a NOP - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x90; // 1-byte nop - dtraceProbeSite[0] = 0x0F; // 4-byte nop - dtraceProbeSite[1] = 0x1F; - dtraceProbeSite[2] = 0x40; - dtraceProbeSite[3] = 0x00; - break; - case x86_64::kDtraceIsEnabledSite: - // change call site to a clear eax - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x48; // xorq eax,eax - dtraceProbeSite[0] = 0x33; - dtraceProbeSite[1] = 0xC0; - dtraceProbeSite[2] = 0x90; // 1-byte nop - dtraceProbeSite[3] = 0x90; // 1-byte nop - break; - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t twoGigLimit = 0x7FFFFFFF; - bool external = this->makesExternalRelocatableReference(ref->getTarget()); - uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; - int64_t displacement = 0; - int32_t temp32; - switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { - case x86_64::kNoFixUp: - case x86_64::kGOTNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - // do nothing - break; - case x86_64::kPointer: - case x86_64::kPointerWeakImport: - { - if ( external ) { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); - } - else { - // internal relocation ==> pointer contains target address - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86_64::kPointer32: - { - if ( external ) { - // external relocation ==> pointer contains addend - LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset()); - } - else { - // internal relocation ==> pointer contains target address - LittleEndian::set32(*((uint32_t*)fixUp), ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86_64::kPointerDiff32: - displacement = ref->getTargetOffset() - ref->getFromTargetOffset(); - if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement += ref->getTarget().getAddress(); - if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement -= ref->getFromTarget().getAddress(); - LittleEndian::set32(*((uint32_t*)fixUp), displacement); - break; - case x86_64::kPointerDiff: - displacement = ref->getTargetOffset() - ref->getFromTargetOffset(); - if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement += ref->getTarget().getAddress(); - if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement -= ref->getFromTarget().getAddress(); - LittleEndian::set64(*fixUp, displacement); - break; - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kDtraceProbeSite: - case x86_64::kDtraceIsEnabledSite: - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had - temp32 = ref->getTargetOffset(); - if ( external ) { - // extern relocation contains addend - displacement = temp32; - } - else { - // internal relocations contain delta to target address - displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - } - switch ( ref->getKind() ) { - case x86_64::kPCRel32_1: - displacement -= 1; - break; - case x86_64::kPCRel32_2: - displacement -= 2; - break; - case x86_64::kPCRel32_4: - displacement -= 4; - break; - } - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "rel32 out of range"; - } - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - break; - case x86_64::kBranchPCRel8: - // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had - temp32 = ref->getTargetOffset(); - if ( external ) { - // extern relocation contains addend - displacement = temp32; - } - else { - // internal relocations contain delta to target address - displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 1); - } - if ( (displacement > 127) || (displacement < (-128)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "rel8 out of range"; - } - *((int8_t*)fixUp) = (int8_t)displacement; - break; - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoadWeakImport: - // contains addend (usually zero) - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); - break; - case x86_64::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - case x86_64::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86_64::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, true); -} - -template <> -void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, true); -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, false); -} - -template <> -void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, false); -} - -// -// ppc and ppc64 are mostly the same, so they share a template specialzation -// -template -void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const -{ - uint32_t instruction; - uint32_t newInstruction; - int64_t displacement; - uint64_t targetAddr = 0; - uint64_t picBaseAddr; - uint16_t instructionLowHalf; - uint16_t instructionHighHalf; - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()]; - bool relocateableExternal = false; - const int64_t picbase_twoGigLimit = 0x80000000; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - if ( finalLinkedImage ) - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - else - relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); - } - - switch ( (typename A::ReferenceKinds)(ref->getKind()) ) { - case A::kNoFixUp: - case A::kFollowOn: - case A::kGroupSubordinate: - // do nothing - break; - case A::kPointerWeakImport: - case A::kPointer: - { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers - || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound lazy pointer to another dylib ==> pointer contains zero - P::setP(*fixUpPointer, 0); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - // prebound lazy pointer to withing this dylib ==> pointer contains address - P::setP(*fixUpPointer, targetAddr); - break; - } - } - else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - P::setP(*fixUpPointer, targetAddr); - else - P::setP(*fixUpPointer, 0); - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - P::setP(*fixUpPointer, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else { - // external relocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - } - } - else { - // internal relocation - if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { - // pointer contains target address - //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr); - P::setP(*fixUpPointer, targetAddr); - } - else { - // pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - } - } - } - break; - case A::kPointerDiff64: - P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kPointerDiff32: - P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kPointerDiff16: - P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kDtraceProbeSite: - if ( finalLinkedImage ) { - // change call site to a NOP - BigEndian::set32(*fixUp, 0x60000000); - } - else { - // set bl instuction to branch to address zero in .o file - int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kDtraceIsEnabledSite: - if ( finalLinkedImage ) { - // change call site to a li r3,0 - BigEndian::set32(*fixUp, 0x38600000); - } - else { - // set bl instuction to branch to address zero in .o file - int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kBranch24WeakImport: - case A::kBranch24: - { - //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); - int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - const int64_t bl_eightMegLimit = 0x00FFFFFF; - if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s", - displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath()); - } - } - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kBranch14: - { - int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; - if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - - //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC); - //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kPICBaseLow16: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - instructionLowHalf = (displacement & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kPICBaseLow14: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - if ( (displacement & 0x3) != 0 ) - throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement); - instructionLowHalf = (displacement & 0xFFFC); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kPICBaseHigh16: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - instructionLowHalf = displacement >> 16; - if ( (displacement & 0x00008000) != 0 ) - ++instructionLowHalf; - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsLow16: - if ( relocateableExternal && !finalLinkedImage ) - targetAddr -= ref->getTarget().getAddress(); - instructionLowHalf = (targetAddr & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsLow14: - if ( relocateableExternal && !finalLinkedImage ) - targetAddr -= ref->getTarget().getAddress(); - if ( (targetAddr & 0x3) != 0 ) - throw "bad address for absolute lo14 instruction fix-up"; - instructionLowHalf = (targetAddr & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsHigh16: - if ( relocateableExternal ) { - if ( finalLinkedImage ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // use target address - break; - case ObjectFile::Atom::kAbsoluteSymbol: - targetAddr = ref->getTarget().getSectionOffset(); - break; - } - } - else { - targetAddr -= ref->getTarget().getAddress(); - } - } - instructionHighHalf = (targetAddr >> 16); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsHigh16AddLow: - if ( relocateableExternal ) { - if ( finalLinkedImage ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // use target address - break; - case ObjectFile::Atom::kAbsoluteSymbol: - targetAddr = ref->getTarget().getSectionOffset(); - break; - } - } - else { - targetAddr -= ref->getTarget().getAddress(); - } - } - if ( targetAddr & 0x00008000 ) - targetAddr += 0x00010000; - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16); - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kDtraceTypeReference: - case A::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (ppc::ReferenceKinds)kind ) { - case ppc::kNoFixUp: - case ppc::kFollowOn: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPointerDiff16: - case ppc::kPointerDiff32: - case ppc::kPointerDiff64: - case ppc::kDtraceProbe: - case ppc::kDtraceProbeSite: - case ppc::kDtraceIsEnabledSite: - case ppc::kDtraceTypeReference: - // these are never used to call external functions - return false; - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - case ppc::kBranch14: - // these are used to call external functions - return true; - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - case ppc::kPICBaseHigh16: - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - // these are only used to call external functions - // in -mlong-branch stubs - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // if the .o file this atom came from has long-branch stubs, - // then assume these instructions in a stub. - // Otherwise, these are a direct reference to something (maybe a runtime text reloc) - return ( inAtom->getFile()->hasLongBranchStubs() ); - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - break; - } - return false; -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (arm::ReferenceKinds)kind ) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - return true; - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - fHasThumbBranches = true; - return true; - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - case arm::kPointerDiff: - case arm::kDtraceProbe: - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - case arm::kDtraceTypeReference: - case arm::kPointerDiff12: - return false; - } - return false; -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (ppc64::ReferenceKinds)kind ) { - case ppc::kNoFixUp: - case ppc::kFollowOn: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPointerDiff16: - case ppc::kPointerDiff32: - case ppc::kPointerDiff64: - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - case ppc::kPICBaseHigh16: - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - case ppc::kDtraceProbe: - case ppc::kDtraceProbeSite: - case ppc::kDtraceIsEnabledSite: - case ppc::kDtraceTypeReference: - // these are never used to call external functions - return false; - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - case ppc::kBranch14: - // these are used to call external functions - return true; - } - return false; -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); -} - -template <> -bool Writer::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport); -} - - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPointerWeakImport: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer::weakImportReferenceKind(uint8_t kind) -{ - return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport || - kind == arm::kPointerWeakImport); -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kGOTNoFixUp: - return true; - } - return false; -} - -template <> -bool Writer::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity -template bool Writer::needsModuleTable() {return fOptions.needsModuleTable(); } -template <> bool Writer::needsModuleTable() { return false; } -template <> bool Writer::needsModuleTable() { return false; } - - -template -void Writer::optimizeDylibReferences() -{ - //fprintf(stderr, "original ordinals table:\n"); - //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); - //} - // find unused dylibs that can be removed - std::map ordinalToReader; - std::map readerAliases; - for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - ObjectFile::Reader* reader = it->first; - std::map::iterator aliasPos = fLibraryAliases.find(reader); - if ( aliasPos != fLibraryAliases.end() ) { - // already noticed that this reader has same install name as another reader - readerAliases[reader] = aliasPos->second; - } - else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || reader->deadStrippable() || fOptions.deadStripDylibs()) ) { - // this reader can be optimized away - it->second = 0xFFFFFFFF; - typename std::map* >::iterator pos = fLibraryToLoadCommand.find(reader); - if ( pos != fLibraryToLoadCommand.end() ) - pos->second->optimizeAway(); - } - else { - // mark this reader as using it ordinal - std::map::iterator pos = ordinalToReader.find(it->second); - if ( pos == ordinalToReader.end() ) - ordinalToReader[it->second] = reader; - else - readerAliases[reader] = pos->second; - } - } - // renumber ordinals (depends on iterator walking in ordinal order) - // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals - uint32_t newOrdinal = 0; - for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first <= fLibraryToOrdinal.size() ) { - if ( ! it->second->isLazyLoadedDylib() ) - fLibraryToOrdinal[it->second] = ++newOrdinal; - } - } - for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first <= fLibraryToOrdinal.size() ) { - if ( it->second->isLazyLoadedDylib() ) { - fLibraryToOrdinal[it->second] = ++newOrdinal; - } - } - } - - // linker does not error when dylib ordinal exceeds 250 - if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) ) - throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal); - - // add aliases (e.g. -lm points to libSystem.dylib) - for (std::map::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) { - fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second]; - } - - //fprintf(stderr, "new ordinals table:\n"); - //for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); - //} -} - - -template <> -void Writer::scanForAbsoluteReferences() -{ - // arm codegen never has absolute references. FIXME: Is this correct? -} - -template <> -void Writer::scanForAbsoluteReferences() -{ - // x86_64 codegen never has absolute references -} - -template <> -void Writer::scanForAbsoluteReferences() -{ - // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used - if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getContentType() == ObjectFile::Atom::kStub ) - continue; - if ( atom->getContentType() == ObjectFile::Atom::kStubHelper ) - continue; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case x86::kAbsolute32: - throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); - return; - } - } - } - } -} - -template <> -void Writer::scanForAbsoluteReferences() -{ - // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used - if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); - return; - } - } - } - } -} - - -// for ppc64 look for any -mdynamic-no-pic codegen -template <> -void Writer::scanForAbsoluteReferences() -{ - // only do this for main executable - if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case ppc64::kAbsLow16: - case ppc64::kAbsLow14: - case ppc64::kAbsHigh16: - case ppc64::kAbsHigh16AddLow: - //fprintf(stderr, "found -mdynamic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); - // shrink page-zero and add pad segment to compensate - fPadSegmentInfo = new SegmentInfo(4096); - strcpy(fPadSegmentInfo->fName, "__4GBFILL"); - fPageZeroAtom->setSize(0x1000); - return; - } - } - } - } -} - - -template -void Writer::insertDummyStubs() -{ - // only needed for x86 -} - -template <> -void Writer::insertDummyStubs() -{ - // any 5-byte stubs that cross a 32-byte cache line may update incorrectly - std::vector*> betterStubs; - for (std::vector*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) { - switch (betterStubs.size() % 64 ) { - case 12:// stub would occupy 0x3C->0x41 - case 25:// stub would occupy 0x7D->0x82 - case 38:// stub would occupy 0xBE->0xC3 - case 51:// stub would occupy 0xFF->0x04 - betterStubs.push_back(new StubAtom(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub - break; - } - betterStubs.push_back(*it); - } - // replace - fAllSynthesizedStubs.clear(); - fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end()); -} - - -template -void Writer::synthesizeKextGOT(const std::vector& existingAtoms, - std::vector& newAtoms) -{ - // walk every atom and reference - for (std::vector::const_iterator it=existingAtoms.begin(); it != existingAtoms.end(); it++) { - const ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - ObjectFile::Atom& target = ref->getTarget(); - // create GOT slots (non-lazy pointers) as needed - if ( this->GOTReferenceKind(ref->getKind()) ) { - bool useGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); - // if this GOT usage cannot be optimized away then make a GOT enry - if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) - useGOT = true; - if ( useGOT ) { - ObjectFile::Atom* nlp = NULL; - std::map::iterator pos = fGOTMap.find(&target); - if ( pos == fGOTMap.end() ) { - nlp = new NonLazyPointerAtom(*this, target); - fGOTMap[&target] = nlp; - newAtoms.push_back(nlp); - } - else { - nlp = pos->second; - } - // alter reference to use non lazy pointer instead - ref->setTarget(*nlp, ref->getTargetOffset()); - } - } - // build map of which symbols need weak importing - if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - if ( this->weakImportReferenceKind(ref->getKind()) ) { - fWeakImportMap[&target] = true; - } - } - break; - } - } - } -} - - -template -void Writer::synthesizeStubs(const std::vector& existingAtoms, - std::vector& newAtoms) -{ - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kPreload: - // these output kinds never have stubs - return; - case Options::kKextBundle: - // new kext need a synthesized GOT only - synthesizeKextGOT(existingAtoms, newAtoms); - return; - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDynamicExecutable: - // try to synthesize stubs for these - break; - } - - // walk every atom and reference - for (std::vector::const_iterator it=existingAtoms.begin(); it != existingAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - ObjectFile::Atom& target = ref->getTarget(); - // build map of which symbols need weak importing - if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - bool weakImport = this->weakImportReferenceKind(ref->getKind()); - // Obj-C Symbols in Leopard Can't Be Weak Linked - // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols - // in dylibs that are weakly linked. - if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) { - typename std::map* >::iterator pos; - pos = fLibraryToLoadCommand.find(target.getFile()); - if ( pos != fLibraryToLoadCommand.end() ) { - if ( pos->second->linkedWeak() ) - weakImport = true; - } - } - // -weak_library no longer forces uses to be weak_import - if ( fForcedWeakImportReaders.count(target.getFile()) != 0 ) { - fWeakImportMap[&target] = true; - weakImport = true; - } - - std::map::iterator pos = fWeakImportMap.find(&target); - if ( pos == fWeakImportMap.end() ) { - // target not in fWeakImportMap, so add - fWeakImportMap[&target] = weakImport; - } - else { - // target in fWeakImportMap, check for weakness mismatch - if ( pos->second != weakImport ) { - // found mismatch - switch ( fOptions.weakReferenceMismatchTreatment() ) { - case Options::kWeakReferenceMismatchError: - throwf("mismatching weak references for symbol: %s", target.getName()); - case Options::kWeakReferenceMismatchWeak: - pos->second = true; - break; - case Options::kWeakReferenceMismatchNonWeak: - pos->second = false; - break; - } - } - } - // update if we use a weak_import or a strong import from this dylib - if ( fWeakImportMap[&target] ) - fDylibReadersWithWeakImports.insert(target.getFile()); - else - fDylibReadersWithNonWeakImports.insert(target.getFile()); - } - // create stubs as needed - if ( this->stubableReference(atom, ref) - && (ref->getTargetOffset() == 0) - && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { - ObjectFile::Atom* stub = NULL; - std::map::iterator pos = fStubsMap.find(&target); - if ( pos == fStubsMap.end() ) { - bool forLazyDylib = false; - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kTentativeDefinition: - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - if ( target.getFile()->isLazyLoadedDylib() ) - forLazyDylib = true; - break; - } - // just-in-time, create GOT slot to dyld_stub_binder - if ( fOptions.makeCompressedDyldInfo() && (fFastStubGOTAtom == NULL) ) { - if ( fDyldCompressedHelperAtom == NULL ) - throw "missing symbol dyld_stub_binder"; - fFastStubGOTAtom = new NonLazyPointerAtom(*this, *fDyldCompressedHelperAtom); - } - stub = new StubAtom(*this, target, forLazyDylib); - fStubsMap[&target] = stub; - } - else { - stub = pos->second; - } - // alter reference to use stub instead - ref->setTarget(*stub, 0); - } - else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) { - throwf("illegal reference to %s in lazy loaded dylib from %s in %s", - target.getDisplayName(), atom->getDisplayName(), - atom->getFile()->getPath()); - } - // create GOT slots (non-lazy pointers) as needed - else if ( this->GOTReferenceKind(ref->getKind()) ) { - // - bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); - bool useGOT; - if ( fBiggerThanTwoGigs ) { - // in big images use GOT for all zero fill atoms - // this is just a heuristic and may need to be re-examined - useGOT = mustUseGOT || ref->getTarget().isZeroFill(); - } - else { - // < 2GB image so remove all GOT entries that we can - useGOT = mustUseGOT; - } - // if this GOT usage cannot be optimized away then make a GOT enry - if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) - useGOT = true; - if ( useGOT ) { - ObjectFile::Atom* nlp = NULL; - std::map::iterator pos = fGOTMap.find(&target); - if ( pos == fGOTMap.end() ) { - nlp = new NonLazyPointerAtom(*this, target); - fGOTMap[&target] = nlp; - } - else { - nlp = pos->second; - } - // alter reference to use non lazy pointer instead - ref->setTarget(*nlp, ref->getTargetOffset()); - } - } - } - } - } - - // sort stubs - std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); - // add dummy self-modifying stubs (x86 only) - if ( ! fOptions.makeCompressedDyldInfo() ) - this->insertDummyStubs(); - // set ordinals so sorting is preserved - uint32_t sortOrder = 0; - for (typename std::vector*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) - (*it)->setSortingOrdinal(sortOrder++); - std::sort(fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end(), AtomByNameSorter()); - - // sort lazy pointers - std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); - sortOrder = 0; - for (typename std::vector*>::iterator it=fAllSynthesizedLazyPointers.begin(); it != fAllSynthesizedLazyPointers.end(); it++) - (*it)->setSortingOrdinal(sortOrder++); - std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter()); - - // sort non-lazy pointers - std::sort(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), AtomByNameSorter()); - sortOrder = 0; - for (typename std::vector*>::iterator it=fAllSynthesizedNonLazyPointers.begin(); it != fAllSynthesizedNonLazyPointers.end(); it++) - (*it)->setSortingOrdinal(sortOrder++); - std::sort(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), AtomByNameSorter()); - - // tell linker about all synthesized atoms - newAtoms.insert(newAtoms.end(), fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - -} - -template -void Writer::createSplitSegContent() -{ - // build LC_SEGMENT_SPLIT_INFO once all atoms exist - if ( fSplitCodeToDataContentAtom != NULL ) { - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) { - this->addCrossSegmentRef(atom, ref); - } - break; - } - } - } - // bad codegen may cause LC_SEGMENT_SPLIT_INFO to be removed - adjustLoadCommandsAndPadding(); - } - -} - - -template -void Writer::synthesizeUnwindInfoTable() -{ - if ( fUnwindInfoAtom != NULL ) { - // walk every atom and gets its unwind info - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->beginUnwind() == atom->endUnwind() ) { - // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info - if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) - fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL, NULL); - } - else { - // atom has unwind - for ( ObjectFile::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { - fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getFDE(), atom->getLSDA(), atom->getPersonalityPointer()); - } - } - } - } -} - - -template -void Writer::partitionIntoSections() -{ - const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); - - // for every atom, set its sectionInfo object and section offset - // build up fSegmentInfos along the way - ObjectFile::Section* curSection = (ObjectFile::Section*)(-1); - SectionInfo* currentSectionInfo = NULL; - SegmentInfo* currentSegmentInfo = NULL; - SectionInfo* cstringSectionInfo = NULL; - unsigned int sectionIndex = 1; - fSegmentInfos.reserve(8); - for (unsigned int i=0; i < fAllAtoms->size(); ++i) { - ObjectFile::Atom* atom = (*fAllAtoms)[i]; - if ( ((atom->getSection() != curSection) || (curSection==NULL)) - && ((currentSectionInfo == NULL) - || (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0) - || (strcmp(atom->getSegment().getName(),currentSectionInfo->fSegmentName) != 0)) ) { - if ( oneSegmentCommand ) { - if ( currentSegmentInfo == NULL ) { - currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment()); - currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - this->fSegmentInfos.push_back(currentSegmentInfo); - } - currentSectionInfo = new SectionInfo(); - strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); - strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; - currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.'); - if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) - currentSectionInfo->setIndex(sectionIndex++); - currentSegmentInfo->fSections.push_back(currentSectionInfo); - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) ) - cstringSectionInfo = currentSectionInfo; - } - else { - if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { - currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment()); - strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); - uint32_t initprot = 0; - if ( atom->getSegment().isContentReadable() ) - initprot |= VM_PROT_READ; - if ( atom->getSegment().isContentWritable() ) - initprot |= VM_PROT_WRITE; - if ( atom->getSegment().isContentExecutable() ) - initprot |= VM_PROT_EXECUTE; - if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) ) - initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker - currentSegmentInfo->fInitProtection = initprot; - if ( initprot == 0 ) - currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 - else if ( fOptions.architecture() == CPU_TYPE_ARM ) - currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init - else - currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - std::vector& customSegProtections = fOptions.customSegmentProtections(); - for(std::vector::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) { - if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) { - currentSegmentInfo->fInitProtection = it->init; - currentSegmentInfo->fMaxProtection = it->max; - } - } - currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); - currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); - if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) ) - currentSegmentInfo->fIndependentAddress = true; - if ( (fOptions.outputKind() == Options::kPreload) && (strcmp(currentSegmentInfo->fName, "__LINKEDIT")==0) ) - currentSegmentInfo->fHasLoadCommand = false; - if ( strcmp(currentSegmentInfo->fName, "__HEADER")==0 ) - currentSegmentInfo->fHasLoadCommand = false; - this->fSegmentInfos.push_back(currentSegmentInfo); - } - currentSectionInfo = new SectionInfo(); - currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large - strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); - strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; - // check for -sectalign override - std::vector& alignmentOverrides = fOptions.sectionAlignments(); - for(std::vector::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { - if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) - currentSectionInfo->fAlignment = it->alignment; - } - currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); - if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) - currentSectionInfo->setIndex(sectionIndex++); - currentSegmentInfo->fSections.push_back(currentSectionInfo); - } - //fprintf(stderr, "new section %s for atom %s\n", atom->getSectionName(), atom->getDisplayName()); - if ( strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0 ) { - fLoadCommandsSection = currentSectionInfo; - fLoadCommandsSegment = currentSegmentInfo; - } - switch ( atom->getContentType() ) { - case ObjectFile::Atom::kLazyPointer: - currentSectionInfo->fAllLazyPointers = true; - fSymbolTableCommands->needDynamicTable(); - break; - case ObjectFile::Atom::kNonLazyPointer: - currentSectionInfo->fAllNonLazyPointers = true; - fSymbolTableCommands->needDynamicTable(); - break; - case ObjectFile::Atom::kLazyDylibPointer: - currentSectionInfo->fAllLazyDylibPointers = true; - break; - case ObjectFile::Atom::kStubHelper: - currentSectionInfo->fAllStubHelpers = true; - break; - case ObjectFile::Atom::kCFIType: - currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned - break; - case ObjectFile::Atom::kStub: - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { - currentSectionInfo->fAllSelfModifyingStubs = true; - currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary - } - else { - currentSectionInfo->fAllStubs = true; - } - fSymbolTableCommands->needDynamicTable(); - break; - default: - break; - } - curSection = atom->getSection(); - } - // any non-zero fill atoms make whole section marked not-zero-fill - if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) - currentSectionInfo->fAllZeroFill = false; - // change section object to be Writer's SectionInfo object - atom->setSection(currentSectionInfo); - // section alignment is that of a contained atom with the greatest alignment - uint8_t atomAlign = atom->getAlignment().powerOf2; - if ( currentSectionInfo->fAlignment < atomAlign ) - currentSectionInfo->fAlignment = atomAlign; - // calculate section offset for this atom - uint64_t offset = currentSectionInfo->fSize; - uint64_t alignment = 1 << atomAlign; - uint64_t currentModulus = (offset % alignment); - uint64_t requiredModulus = atom->getAlignment().modulus; - if ( currentModulus != requiredModulus ) { - if ( requiredModulus > currentModulus ) - offset += requiredModulus-currentModulus; - else - offset += requiredModulus+alignment-currentModulus; - } - atom->setSectionOffset(offset); - uint64_t curAtomSize = atom->getSize(); - currentSectionInfo->fSize = offset + curAtomSize; - // add atom to section vector - currentSectionInfo->fAtoms.push_back(atom); - //fprintf(stderr, " adding atom %p %s size=0x%0llX to section %p %s from %s\n", atom, atom->getDisplayName(), atom->getSize(), - // currentSectionInfo, currentSectionInfo->fSectionName, atom->getFile()->getPath()); - // update largest size - if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) - fLargestAtomSize = curAtomSize; - } - if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) { - // when merging cstring sections in .o files, all strings need to use the max alignment - uint64_t offset = 0; - uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment; - for (std::vector::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) { - offset = (offset + (cstringAlignment-1)) & (-cstringAlignment); - ObjectFile::Atom* atom = *it; - atom->setSectionOffset(offset); - offset += atom->getSize(); - } - cstringSectionInfo->fSize = offset; - } -} - - -struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; -class TargetAndOffsetComparor -{ -public: - bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const - { - if ( left.atom != right.atom ) - return ( left.atom < right.atom ); - return ( left.offset < right.offset ); - } -}; - -template <> -bool Writer::addBranchIslands() -{ - return this->createBranchIslands(); -} - -template <> -bool Writer::addBranchIslands() -{ - return this->createBranchIslands(); -} - -template <> -bool Writer::addBranchIslands() -{ - // x86 branches can reach entire 4G address space, so no need for branch islands - return false; -} - -template <> -bool Writer::addBranchIslands() -{ - // x86 branches can reach entire 4G size of largest image - return false; -} - -template <> -bool Writer::addBranchIslands() -{ - return this->createBranchIslands(); -} - -template <> -bool Writer::isBranchThatMightNeedIsland(uint8_t kind) -{ - switch (kind) { - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - return true; - } - return false; -} - -template <> -bool Writer::isBranchThatMightNeedIsland(uint8_t kind) -{ - switch (kind) { - case ppc64::kBranch24: - case ppc64::kBranch24WeakImport: - return true; - } - return false; -} - -template <> -bool Writer::isBranchThatMightNeedIsland(uint8_t kind) -{ - switch (kind) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - return true; - } - return false; -} - -template <> -uint32_t Writer::textSizeWhenMightNeedBranchIslands() -{ - return 16000000; -} - -template <> -uint32_t Writer::textSizeWhenMightNeedBranchIslands() -{ - return 16000000; -} - -template <> -uint32_t Writer::textSizeWhenMightNeedBranchIslands() -{ - if ( fHasThumbBranches == false ) - return 32000000; // ARM can branch +/- 32MB - else if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) - return 16000000; // thumb2 can branch +/- 16MB - else - return 4000000; // thumb1 can branch +/- 4MB -} - -template <> -uint32_t Writer::maxDistanceBetweenIslands() -{ - return 14*1024*1024; -} - -template <> -uint32_t Writer::maxDistanceBetweenIslands() -{ - return 14*1024*1024; -} - -template <> -uint32_t Writer::maxDistanceBetweenIslands() -{ - if ( fHasThumbBranches == false ) - return 30*1024*1024; - else if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) - return 14*1024*1024; - else - return 3500000; -} - - -// -// PowerPC can do PC relative branches as far as +/-16MB. -// If a branch target is >16MB then we insert one or more -// "branch islands" between the branch and its target that -// allows island hopping to the target. -// -// Branch Island Algorithm -// -// If the __TEXT segment < 16MB, then no branch islands needed -// Otherwise, every 14MB into the __TEXT segment a region is -// added which can contain branch islands. Every out-of-range -// bl instruction is checked. If it crosses a region, an island -// is added to that region with the same target and the bl is -// adjusted to target the island instead. -// -// In theory, if too many islands are added to one region, it -// could grow the __TEXT enough that other previously in-range -// bl branches could be pushed out of range. We reduce the -// probability this could happen by placing the ranges every -// 14MB which means the region would have to be 2MB (512,000 islands) -// before any branches could be pushed out of range. -// -template -bool Writer::createBranchIslands() -{ - bool log = false; - bool result = false; - // Can only possibly need branch islands if __TEXT segment > 16M - if ( fLoadCommandsSegment->fSize > textSizeWhenMightNeedBranchIslands() ) { - if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); - const uint32_t kBetweenRegions = maxDistanceBetweenIslands(); // place regions of islands every 14MB in __text section - SectionInfo* textSection = NULL; - for (std::vector::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { - if ( strcmp((*it)->fSectionName, "__text") == 0 ) { - textSection = *it; - if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize); - break; - } - } - const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions; - typedef std::map AtomToIsland; - AtomToIsland regionsMap[kIslandRegionsCount]; - std::vector regionsIslands[kIslandRegionsCount]; - unsigned int islandCount = 0; - if (log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); - - // create islands for branch references that are out of range - for (std::vector::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( this->isBranchThatMightNeedIsland(ref->getKind()) ) { - ObjectFile::Atom& target = ref->getTarget(); - int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); - int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); - int64_t displacement = dstAddr - srcAddr; - TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; - const int64_t kBranchLimit = kBetweenRegions; - if ( displacement > kBranchLimit ) { - // create forward branch chain - ObjectFile::Atom* nextTarget = ⌖ - for (int i=kIslandRegionsCount-1; i >=0 ; --i) { - AtomToIsland* region = ®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress(); - if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { - AtomToIsland::iterator pos = region->find(finalTargetAndOffset); - if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *nextTarget, *finalTargetAndOffset.atom, finalTargetAndOffset.offset); - island->setSection(textSection); - (*region)[finalTargetAndOffset] = island; - if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); - regionsIslands[i].push_back(island); - ++islandCount; - nextTarget = island; - } - else { - nextTarget = pos->second; - } - } - } - if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName()); - ref->setTarget(*nextTarget, 0); - } - else if ( displacement < (-kBranchLimit) ) { - // create back branching chain - ObjectFile::Atom* prevTarget = ⌖ - for (int i=0; i < kIslandRegionsCount ; ++i) { - AtomToIsland* region = ®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { - AtomToIsland::iterator pos = region->find(finalTargetAndOffset); - if ( pos == region->end() ) { - BranchIslandAtom* island = new BranchIslandAtom(*this, target.getDisplayName(), i, *prevTarget, *finalTargetAndOffset.atom, finalTargetAndOffset.offset); - island->setSection(textSection); - (*region)[finalTargetAndOffset] = island; - if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); - regionsIslands[i].push_back(island); - ++islandCount; - prevTarget = island; - } - else { - prevTarget = pos->second; - } - } - } - if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); - ref->setTarget(*prevTarget, 0); - } - } - } - } - - // insert islands into __text section and adjust section offsets - if ( islandCount > 0 ) { - if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); - std::vector newAtomList; - newAtomList.reserve(textSection->fAtoms.size()+islandCount); - uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress(); - uint64_t textSectionAlignment = (1 << textSection->fAlignment); - int regionIndex = 0; - uint64_t atomSlide = 0; - uint64_t sectionOffset = 0; - for (std::vector::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getAddress()+atom->getSize()) > islandRegionAddr ) { - uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide; - sectionOffset = islandStartOffset; - std::vector* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - if ( log ) fprintf(stderr, "assigning __text offset 0x%08llx to %s\n", sectionOffset, islandAtom->getDisplayName()); - sectionOffset += islandAtom->getSize(); - } - ++regionIndex; - islandRegionAddr += kBetweenRegions; - uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment; - atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment); - } - newAtomList.push_back(atom); - if ( atomSlide != 0 ) - atom->setSectionOffset(atom->getSectionOffset()+atomSlide); - } - sectionOffset = textSection->fSize+atomSlide; - // put any remaining islands at end of __text section - if ( regionIndex < kIslandRegionsCount ) { - std::vector* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - if ( log ) fprintf(stderr, "assigning __text offset 0x%08llx to %s\n", sectionOffset, islandAtom->getDisplayName()); - sectionOffset += islandAtom->getSize(); - } - } - - textSection->fAtoms = newAtomList; - textSection->fSize = sectionOffset; - result = true; - } - - } - return result; -} - - -template -void Writer::adjustLoadCommandsAndPadding() -{ - fSegmentCommands->computeSize(); - - // recompute load command section offsets - uint64_t offset = 0; - std::vector& loadCommandAtoms = fLoadCommandsSection->fAtoms; - const unsigned int atomCount = loadCommandAtoms.size(); - for (unsigned int i=0; i < atomCount; ++i) { - ObjectFile::Atom* atom = loadCommandAtoms[i]; - uint64_t alignment = 1 << atom->getAlignment().powerOf2; - offset = ( (offset+alignment-1) & (-alignment) ); - atom->setSectionOffset(offset); - uint32_t atomSize = atom->getSize(); - if ( atomSize > fLargestAtomSize ) - fLargestAtomSize = atomSize; - offset += atomSize; - fLoadCommandsSection->fSize = offset; - } - const uint32_t sizeOfLoadCommandsPlusHeader = offset + sizeof(macho_header); - - std::vector& sectionInfos = fLoadCommandsSegment->fSections; - const int sectionCount = sectionInfos.size(); - uint32_t totalSizeOfTEXTLessHeaderAndLoadCommands = 0; - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) - break; - totalSizeOfTEXTLessHeaderAndLoadCommands += curSection->fSize; - } - uint64_t paddingSize = 0; - if ( fOptions.outputKind() == Options::kDyld ) { - // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address - paddingSize = 4096 - (totalSizeOfTEXTLessHeaderAndLoadCommands % 4096); - } - else if ( fOptions.outputKind() == Options::kObjectFile ) { - // mach-o .o files need no padding between load commands and first section - // but leave enough room that the object file could be signed - paddingSize = 32; - } - else if ( fOptions.outputKind() == Options::kPreload ) { - // mach-o MH_PRELOAD files need no padding between load commands and first section - paddingSize = 0; - } - else { - // work backwards from end of segment and lay out sections so that extra room goes to padding atom - uint64_t addr = 0; - for(int j=sectionCount-1; j >=0; --j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { - addr -= (fLoadCommandsSection->fSize+fMachHeaderAtom->getSize()); - paddingSize = addr % fOptions.segmentAlignment(); - break; - } - addr -= curSection->fSize; - addr = addr & (0 - (1 << curSection->fAlignment)); - } - - // if command line requires more padding than this - uint32_t minPad = fOptions.minimumHeaderPad(); - if ( fOptions.maxMminimumHeaderPad() ) { - // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes - uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN; - if ( fOptions.outputKind() == Options::kDynamicLibrary ) - altMin += MAXPATHLEN; - if ( altMin > minPad ) - minPad = altMin; - } - if ( paddingSize < minPad ) { - int extraPages = (minPad - paddingSize + fOptions.segmentAlignment() - 1)/fOptions.segmentAlignment(); - paddingSize += extraPages * fOptions.segmentAlignment(); - } - - if ( fOptions.makeEncryptable() ) { - // load commands must be on a separate non-encrypted page - int loadCommandsPage = (sizeOfLoadCommandsPlusHeader + minPad)/fOptions.segmentAlignment(); - int textPage = (sizeOfLoadCommandsPlusHeader + paddingSize)/fOptions.segmentAlignment(); - if ( loadCommandsPage == textPage ) { - paddingSize += fOptions.segmentAlignment(); - textPage += 1; - } - - //paddingSize = 4096 - ((totalSizeOfTEXTLessHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); - fEncryptionLoadCommand->setStartEncryptionOffset(textPage*fOptions.segmentAlignment()); - } - } - - // adjust atom size and update section size - fHeaderPadding->setSize(paddingSize); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) - curSection->fSize = paddingSize; - } -} - -static uint64_t segmentAlign(uint64_t addr, uint64_t alignment) -{ - return ((addr+alignment-1) & (-alignment)); -} - -// assign file offsets and logical address to all segments -template -void Writer::assignFileOffsets() -{ - const bool virtualSectionOccupyAddressSpace = ((fOptions.outputKind() != Options::kObjectFile) - && (fOptions.outputKind() != Options::kPreload)); - bool haveFixedSegments = false; - uint64_t fileOffset = 0; - uint64_t nextContiguousAddress = fOptions.baseAddress(); - uint64_t nextReadOnlyAddress = fOptions.baseAddress(); - uint64_t nextWritableAddress = fOptions.baseWritableAddress(); - - // process segments with fixed addresses (-segaddr) - for (std::vector::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, it->name) == 0 ) { - curSegment->fBaseAddress = it->address; - curSegment->fFixedAddress = true; - break; - } - } - } - - // process segments with fixed addresses (-seg_page_size) - for (std::vector::iterator it = fOptions.customSegmentSizes().begin(); it != fOptions.customSegmentSizes().end(); ++it) { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, it->name) == 0 ) { - curSegment->fPageSize = it->size; - break; - } - } - } - - // Run through the segments and each segment's sections to assign addresses - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - - if ( fOptions.splitSeg() ) { - if ( curSegment->fInitProtection & VM_PROT_WRITE ) - nextContiguousAddress = nextWritableAddress; - else - nextContiguousAddress = nextReadOnlyAddress; - } - - if ( fOptions.outputKind() == Options::kPreload ) { - if ( strcmp(curSegment->fName, "__HEADER") == 0 ) - nextContiguousAddress = 0; - else if ( strcmp(curSegment->fName, "__TEXT") == 0 ) - nextContiguousAddress = fOptions.baseAddress(); - } - - fileOffset = segmentAlign(fileOffset, curSegment->fPageSize); - curSegment->fFileOffset = fileOffset; - - // Set the segment base address - if ( curSegment->fFixedAddress ) - haveFixedSegments = true; - else - curSegment->fBaseAddress = segmentAlign(nextContiguousAddress, curSegment->fPageSize); - - // We've set the segment address, now run through each section. - uint64_t address = curSegment->fBaseAddress; - SectionInfo* firstZeroFillSection = NULL; - SectionInfo* prevSection = NULL; - - std::vector& sectionInfos = curSegment->fSections; - - for (std::vector::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) { - SectionInfo* curSection = *it; - - // adjust section address based on alignment - uint64_t alignment = 1 << curSection->fAlignment; - if ( curSection->fAtoms.size() == 1 ) { - // if there is only one atom in section, use modulus for even better layout - ObjectFile::Alignment atomAlign = curSection->fAtoms[0]->getAlignment(); - uint64_t atomAlignP2 = (1 << atomAlign.powerOf2); - uint64_t currentModulus = (address % atomAlignP2); - if ( currentModulus != atomAlign.modulus ) { - if ( atomAlign.modulus > currentModulus ) - address += atomAlign.modulus-currentModulus; - else - address += atomAlign.modulus+atomAlignP2-currentModulus; - } - } - else { - address = ( (address+alignment-1) & (-alignment) ); - } - // adjust file offset to match address - if ( prevSection != NULL ) { - if ( virtualSectionOccupyAddressSpace || !prevSection->fVirtualSection ) - fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; - else - fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); - } - - // update section info - curSection->fFileOffset = fileOffset; - curSection->setBaseAddress(address); - //fprintf(stderr, "%s %s addr=0x%llX, fileoffset=0x%llX, size=0x%llX\n", curSegment->fName, curSection->fSectionName, address, fileOffset, curSection->fSize); - - // keep track of trailing zero fill sections - if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) - firstZeroFillSection = curSection; - if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && (fOptions.outputKind() != Options::kObjectFile) ) - throwf("zero-fill section %s not at end of segment", curSection->fSectionName); - - // update running pointers - if ( virtualSectionOccupyAddressSpace || !curSection->fVirtualSection ) - address += curSection->fSize; - fileOffset += curSection->fSize; - - // sanity check size of 32-bit binaries - if ( address > maxAddress() ) - throwf("section %s exceeds 4GB limit", curSection->fSectionName); - - // update segment info - curSegment->fFileSize = fileOffset - curSegment->fFileOffset; - curSegment->fSize = curSegment->fFileSize; - prevSection = curSection; - } - - if ( fOptions.outputKind() == Options::kObjectFile ) { - // don't page align .o files - } - else { - // optimize trailing zero-fill sections to not occupy disk space - if ( firstZeroFillSection != NULL ) { - curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset; - fileOffset = firstZeroFillSection->fFileOffset; - } - // page align segment size - curSegment->fFileSize = segmentAlign(curSegment->fFileSize, curSegment->fPageSize); - curSegment->fSize = segmentAlign(curSegment->fSize, curSegment->fPageSize); - if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { - nextContiguousAddress = segmentAlign(curSegment->fBaseAddress+curSegment->fSize, curSegment->fPageSize); - fileOffset = segmentAlign(fileOffset, curSegment->fPageSize); - if ( curSegment->fInitProtection & VM_PROT_WRITE ) - nextWritableAddress = nextContiguousAddress; - else - nextReadOnlyAddress = nextContiguousAddress; - } - } - //fprintf(stderr, "end of seg %s, fileoffset=0x%llX, nextContiguousAddress=0x%llX\n", curSegment->fName, fileOffset, nextContiguousAddress); - } - - // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) - if ( haveFixedSegments ) { - int segCount = fSegmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* segment1 = fSegmentInfos[i]; - - for(int j=0; j < segCount; ++j) { - if ( i != j ) { - SegmentInfo* segment2 = fSegmentInfos[j]; - - if ( segment1->fBaseAddress < segment2->fBaseAddress ) { - if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { - if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) { - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - } - } - } - } - - // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) { - if ( fFirstWritableSegment == NULL ) - fFirstWritableSegment = curSegment; - if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL ) - fWritableSegmentPastFirst4GB = true; - } - } - - // record size of encrypted part of __TEXT segment - if ( fOptions.makeEncryptable() ) { - for (std::vector::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, "__TEXT") == 0 ) { - fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize); - break; - } - } - } - -} - -template -void Writer::adjustLinkEditSections() -{ - // link edit content is always in last segment - SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; - unsigned int firstLinkEditSectionIndex = 0; - while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) - ++firstLinkEditSectionIndex; - - const unsigned int linkEditSectionCount = lastSeg->fSections.size(); - uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; - uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); - if ( fPadSegmentInfo != NULL ) { - // insert __4GBFILL segment into segments vector before LINKEDIT - for(std::vector::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) { - if ( *it == lastSeg ) { - fSegmentInfos.insert(it, fPadSegmentInfo); - break; - } - } - // adjust __4GBFILL segment to span from end of last segment to zeroPageSize - fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address; - fPadSegmentInfo->fBaseAddress = address; - // adjust LINKEDIT to start at zeroPageSize - address = fOptions.zeroPageSize(); - lastSeg->fBaseAddress = fOptions.zeroPageSize(); - } - for (unsigned int i=firstLinkEditSectionIndex; i < linkEditSectionCount; ++i) { - std::vector& atoms = lastSeg->fSections[i]->fAtoms; - // adjust section address based on alignment - uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment; - uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address; - address += pad; - fileOffset += pad; // adjust file offset to match address - lastSeg->fSections[i]->setBaseAddress(address); - if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 ) - lastSeg->fSections[i]->setBaseAddress(0); - lastSeg->fSections[i]->fFileOffset = fileOffset; - uint64_t sectionOffset = 0; - for (unsigned int j=0; j < atoms.size(); ++j) { - ObjectFile::Atom* atom = atoms[j]; - uint64_t alignment = 1 << atom->getAlignment().powerOf2; - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - atom->setSectionOffset(sectionOffset); - uint64_t size = atom->getSize(); - sectionOffset += size; - if ( size > fLargestAtomSize ) - fLargestAtomSize = size; - } - //fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset); - lastSeg->fSections[i]->fSize = sectionOffset; - fileOffset += sectionOffset; - address += sectionOffset; - } - if ( fOptions.outputKind() == Options::kObjectFile ) { - //lastSeg->fBaseAddress = 0; - //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> - //lastSeg->fFileOffset = 0; - //lastSeg->fFileSize = - } - else { - lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; - lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); - } -} - - -template -ObjectFile::Atom::Scope MachHeaderAtom::getScope() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - return ObjectFile::Atom::scopeGlobal; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return ObjectFile::Atom::scopeLinkageUnit; - } - throw "unknown header type"; -} - -template -ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusion() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - return ObjectFile::Atom::kSymbolTableInAndNeverStrip; - case Options::kStaticExecutable: - return ObjectFile::Atom::kSymbolTableInAsAbsolute; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - return ObjectFile::Atom::kSymbolTableIn; - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return ObjectFile::Atom::kSymbolTableNotIn; - } - throw "unknown header type"; -} - -template -const char* MachHeaderAtom::getName() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - return "__mh_execute_header"; - case Options::kDynamicLibrary: - return "__mh_dylib_header"; - case Options::kDynamicBundle: - return "__mh_bundle_header"; - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return NULL; - case Options::kDyld: - return "__mh_dylinker_header"; - } - throw "unknown header type"; -} - -template -const char* MachHeaderAtom::getDisplayName() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - return this->getName(); - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return "mach header"; - } - throw "unknown header type"; -} - -template -void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const -{ - // get file type - uint32_t fileType = 0; - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - fileType = MH_EXECUTE; - break; - case Options::kDynamicLibrary: - fileType = MH_DYLIB; - break; - case Options::kDynamicBundle: - fileType = MH_BUNDLE; - break; - case Options::kObjectFile: - fileType = MH_OBJECT; - break; - case Options::kDyld: - fileType = MH_DYLINKER; - break; - case Options::kPreload: - fileType = MH_PRELOAD; - break; - case Options::kKextBundle: - fileType = MH_KEXT_BUNDLE; - break; - } - - // get flags - uint32_t flags = 0; - if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { - if ( fWriter.fCanScatter ) - flags = MH_SUBSECTIONS_VIA_SYMBOLS; - } - else { - if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { - flags |= MH_NOUNDEFS; - } - else if ( fWriter.fOptions.outputKind() == Options::kPreload ) { - flags |= MH_NOUNDEFS; - if ( fWriter.fOptions.positionIndependentExecutable() ) - flags |= MH_PIE; - } - else { - flags = MH_DYLDLINK; - if ( fWriter.fOptions.bindAtLoad() ) - flags |= MH_BINDATLOAD; - switch ( fWriter.fOptions.nameSpace() ) { - case Options::kTwoLevelNameSpace: - flags |= MH_TWOLEVEL | MH_NOUNDEFS; - break; - case Options::kFlatNameSpace: - break; - case Options::kForceFlatNameSpace: - flags |= MH_FORCE_FLAT; - break; - } - bool hasWeakDefines = fWriter.fHasWeakExports; - if ( fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->size() != 0 ) { - for(std::set::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin(); - it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) { - if ( fWriter.shouldExport(**it) ) { - hasWeakDefines = true; - break; - } - } - } - if ( hasWeakDefines ) - flags |= MH_WEAK_DEFINES; - if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) - flags |= MH_BINDS_TO_WEAK; - if ( fWriter.fOptions.prebind() ) - flags |= MH_PREBOUND; - if ( fWriter.fOptions.splitSeg() ) - flags |= MH_SPLIT_SEGS; - if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs ) - flags |= MH_NO_REEXPORTED_DYLIBS; - if ( fWriter.fOptions.positionIndependentExecutable() ) - flags |= MH_PIE; - if ( fWriter.fOptions.markAutoDeadStripDylib() ) - flags |= MH_DEAD_STRIPPABLE_DYLIB; - } - if ( fWriter.fOptions.hasExecutableStack() ) - flags |= MH_ALLOW_STACK_EXECUTION; - if ( fWriter.fOptions.readerOptions().fRootSafe ) - flags |= MH_ROOT_SAFE; - if ( fWriter.fOptions.readerOptions().fSetuidSafe ) - flags |= MH_SETUID_SAFE; - } - - // get commands info - uint32_t commandsSize = 0; - uint32_t commandsCount = 0; - - std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; - for (std::vector::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - commandsSize += atom->getSize(); - // segment and symbol table atoms can contain more than one load command - if ( atom == fWriter.fSegmentCommands ) - commandsCount += fWriter.fSegmentCommands->commandCount(); - else if ( atom == fWriter.fSymbolTableCommands ) - commandsCount += fWriter.fSymbolTableCommands->commandCount(); - else if ( atom->getSize() != 0 ) - ++commandsCount; - } - - // fill out mach_header - macho_header* mh = (macho_header*)buffer; - setHeaderInfo(*mh); - mh->set_filetype(fileType); - mh->set_ncmds(commandsCount); - mh->set_sizeofcmds(commandsSize); - mh->set_flags(flags); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_POWERPC); - header.set_cpusubtype(fWriter.fCpuConstraint); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC_64); - header.set_cputype(CPU_TYPE_POWERPC64); - if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000); - else - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); - header.set_reserved(0); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_I386); - header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC_64); - header.set_cputype(CPU_TYPE_X86_64); - if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000); - else - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); - header.set_reserved(0); -} - -template <> -void MachHeaderAtom::setHeaderInfo(macho_header& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_ARM); - header.set_cpusubtype(fWriter.fCpuConstraint); -} - -template -CustomStackAtom::CustomStackAtom(Writer& writer) - : WriterAtom(writer, Segment::fgStackSegment) -{ - if ( stackGrowsDown() ) - Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); - else - Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); -} - - -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } -template <> bool CustomStackAtom::stackGrowsDown() { return true; } - -template -void SegmentLoadCommandsAtom::computeSize() -{ - uint64_t size = 0; - std::vector& segmentInfos = fWriter.fSegmentInfos; - int segCount = 0; - for(std::vector::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) { - SegmentInfo* seg = *it; - if ( seg->fHasLoadCommand ) { - ++segCount; - size += sizeof(macho_segment_command

); - std::vector& sectionInfos = seg->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) - size += sizeof(macho_section

); - } - } - } - fSize = size; - fCommandCount = segCount; - if ( fWriter.fPadSegmentInfo != NULL ) { - ++fCommandCount; - fSize += sizeof(macho_segment_command

); - } -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o -} - -template <> -uint64_t LoadCommandAtom::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template -void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); - bzero(buffer, size); - uint8_t* p = buffer; - typename std::vector& segmentInfos = fWriter.fSegmentInfos; - for(std::vector::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) { - SegmentInfo* segInfo = *it; - if ( ! segInfo->fHasLoadCommand ) - continue; - const int sectionCount = segInfo->fSections.size(); - macho_segment_command

* cmd = (macho_segment_command

*)p; - cmd->set_cmd(macho_segment_command

::CMD); - cmd->set_segname(segInfo->fName); - cmd->set_vmaddr(segInfo->fBaseAddress); - cmd->set_vmsize(oneSegment ? 0 : segInfo->fSize); - cmd->set_fileoff(segInfo->fFileOffset); - cmd->set_filesize(oneSegment ? 0 : segInfo->fFileSize); - cmd->set_maxprot(segInfo->fMaxProtection); - cmd->set_initprot(segInfo->fInitProtection); - // add sections array - macho_section

* const sections = (macho_section

*)&p[sizeof(macho_segment_command

)]; - unsigned int sectionsEmitted = 0; - for (int j=0; j < sectionCount; ++j) { - SectionInfo* sectInfo = segInfo->fSections[j]; - if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { - macho_section

* sect = §ions[sectionsEmitted++]; - if ( oneSegment ) { - // .o file segment does not cover load commands, so recalc at first real section - if ( sectionsEmitted == 1 ) { - cmd->set_vmaddr(sectInfo->getBaseAddress()); - cmd->set_fileoff(sectInfo->fFileOffset); - } - // if last section is zero-fill don't add size to filesize total - if ( !sectInfo->fAllZeroFill ) { - cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); - } - cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); - } - sect->set_sectname(sectInfo->fSectionName); - sect->set_segname(sectInfo->fSegmentName); - sect->set_addr(sectInfo->getBaseAddress()); - sect->set_size(sectInfo->fSize); - sect->set_offset(sectInfo->fFileOffset); - sect->set_align(sectInfo->fAlignment); - if ( sectInfo->fRelocCount != 0 ) { - sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info

) + fWriter.fSectionRelocationsAtom->getFileOffset()); - sect->set_nreloc(sectInfo->fRelocCount); - } - if ( sectInfo->fAllZeroFill ) { - sect->set_flags(S_ZEROFILL); - sect->set_offset(0); - } - else if ( sectInfo->fAllLazyPointers ) { - sect->set_flags(S_LAZY_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllLazyDylibPointers ) { - sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllNonLazyPointers ) { - sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllStubs ) { - sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - } - else if ( sectInfo->fAllSelfModifyingStubs ) { - sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); - } - else if ( sectInfo->fAllStubHelpers ) { - sect->set_flags(S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - } - else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCStringType ) { - sect->set_flags(S_CSTRING_LITERALS); - } - else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCFIType ) { - sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); - } - else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_MOD_INIT_FUNC_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_MOD_TERM_FUNC_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_COALESCED); - } - else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_COALESCED); - } - else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_INTERPOSING); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_4BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_8BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_16BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__objc_selrefs") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_DTRACE_DOF); - } - else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_DTRACE_DOF); - } - else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - if ( sectInfo->fHasTextExternalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC); - } - //fprintf(stderr, "section %s flags=0x%08X\n", sectInfo->fSectionName, sect->flags()); - } - } - p = &p[sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)]; - cmd->set_cmdsize(sizeof(macho_segment_command

) + sectionsEmitted*sizeof(macho_section

)); - cmd->set_nsects(sectionsEmitted); - } -} - - -template -SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) - : LoadCommandAtom(writer), fNeedsDynamicSymbolTable(false) -{ - bzero(&fSymbolTable, sizeof(macho_symtab_command

)); - bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command

)); - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - case Options::kKextBundle: - fNeedsDynamicSymbolTable = true; - break; - case Options::kObjectFile: - case Options::kStaticExecutable: - fNeedsDynamicSymbolTable = false; - case Options::kPreload: - fNeedsDynamicSymbolTable = fWriter.fOptions.positionIndependentExecutable(); - break; - } - writer.fSymbolTableCommands = this; -} - - - -template -void SymbolTableLoadCommandsAtom::needDynamicTable() -{ - fNeedsDynamicSymbolTable = true; -} - - -template -uint64_t SymbolTableLoadCommandsAtom::getSize() const -{ - if ( fNeedsDynamicSymbolTable ) - return this->alignedSize(sizeof(macho_symtab_command

) + sizeof(macho_dysymtab_command

)); - else - return this->alignedSize(sizeof(macho_symtab_command

)); -} - -template -void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - // build LC_SYMTAB command - macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)buffer; - bzero(symbolTableCmd, sizeof(macho_symtab_command

)); - symbolTableCmd->set_cmd(LC_SYMTAB); - symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); - symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); - symbolTableCmd->set_symoff(fWriter.fSymbolTableCount == 0 ? 0 : fWriter.fSymbolTableAtom->getFileOffset()); - symbolTableCmd->set_stroff(fWriter.fStringsAtom->getSize() == 0 ? 0 : fWriter.fStringsAtom->getFileOffset()); - symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); - - // build LC_DYSYMTAB command - if ( fNeedsDynamicSymbolTable ) { - macho_dysymtab_command

* dynamicSymbolTableCmd = (macho_dysymtab_command

*)&buffer[sizeof(macho_symtab_command

)]; - bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command

)); - dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); - dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); - dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); - dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); - dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); - dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount); - dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex); - dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount); - if ( fWriter.fModuleInfoAtom != NULL ) { - dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); - dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); - dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); - dynamicSymbolTableCmd->set_nmodtab(1); - dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); - dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); - } - dynamicSymbolTableCmd->set_indirectsymoff((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nindirectsyms((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->fTable.size()); - if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { - if ( fWriter.fExternalRelocationsAtom != 0 ) { - dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); - } - if ( fWriter.fLocalRelocationsAtom != 0 ) { - dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); - } - } - } -} - - -template -unsigned int SymbolTableLoadCommandsAtom::commandCount() -{ - return fNeedsDynamicSymbolTable ? 2 : 1; -} - -template -uint64_t DyldLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_dylinker_command

) + strlen("/usr/lib/dyld") + 1); -} - -template -void DyldLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_dylinker_command

* cmd = (macho_dylinker_command

*)buffer; - if ( fWriter.fOptions.outputKind() == Options::kDyld ) - cmd->set_cmd(LC_ID_DYLINKER); - else - cmd->set_cmd(LC_LOAD_DYLINKER); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - strcpy((char*)&buffer[sizeof(macho_dylinker_command

)], "/usr/lib/dyld"); -} - -template -uint64_t AllowableClientLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_client_command

) + strlen(this->clientString) + 1); -} - -template -void AllowableClientLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - - bzero(buffer, size); - macho_sub_client_command

* cmd = (macho_sub_client_command

*)buffer; - cmd->set_cmd(LC_SUB_CLIENT); - cmd->set_cmdsize(size); - cmd->set_client_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_client_command

)], this->clientString); - -} - -template -uint64_t DylibLoadCommandsAtom::getSize() const -{ - if ( fOptimizedAway ) { - return 0; - } - else { - const char* path = fInfo.reader->getInstallPath(); - return this->alignedSize(sizeof(macho_dylib_command

) + strlen(path) + 1); - } -} - -template -void DylibLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - if ( fOptimizedAway ) - return; - uint64_t size = this->getSize(); - bzero(buffer, size); - const char* path = fInfo.reader->getInstallPath(); - macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) - && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); - if ( fInfo.options.fLazyLoad ) - cmd->set_cmd(LC_LAZY_LOAD_DYLIB); - else if ( fInfo.options.fWeakImport || autoWeakLoadDylib ) - cmd->set_cmd(LC_LOAD_WEAK_DYLIB); - else if ( fInfo.options.fReExport && fWriter.fOptions.useSimplifiedDylibReExports() ) - cmd->set_cmd(LC_REEXPORT_DYLIB); - else - cmd->set_cmd(LC_LOAD_DYLIB); - cmd->set_cmdsize(this->getSize()); - cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses - cmd->set_current_version(fInfo.reader->getCurrentVersion()); - cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); - cmd->set_name_offset(); - strcpy((char*)&buffer[sizeof(macho_dylib_command

)], path); -} - - - -template -uint64_t DylibIDLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_dylib_command

) + strlen(fWriter.fOptions.installPath()) + 1); -} - -template -void DylibIDLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; - cmd->set_cmd(LC_ID_DYLIB); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses - cmd->set_current_version(fWriter.fOptions.currentVersion()); - cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); - strcpy((char*)&buffer[sizeof(macho_dylib_command

)], fWriter.fOptions.installPath()); -} - - -template -void RoutinesLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - if (fWriter.fEntryPoint->isThumb()) - initAddr |= 1ULL; - bzero(buffer, sizeof(macho_routines_command

)); - macho_routines_command

* cmd = (macho_routines_command

*)buffer; - cmd->set_cmd(macho_routines_command

::CMD); - cmd->set_cmdsize(this->getSize()); - cmd->set_init_address(initAddr); -} - - -template -uint64_t SubUmbrellaLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_umbrella_command

) + strlen(fName) + 1); -} - -template -void SubUmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_umbrella_command

* cmd = (macho_sub_umbrella_command

*)buffer; - cmd->set_cmd(LC_SUB_UMBRELLA); - cmd->set_cmdsize(this->getSize()); - cmd->set_sub_umbrella_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command

)], fName); -} - -template -void UUIDLoadCommandAtom::generate() -{ - switch ( fWriter.fOptions.getUUIDMode() ) { - case Options::kUUIDNone: - fEmit = false; - break; - case Options::kUUIDRandom: - ::uuid_generate_random(fUUID); - fEmit = true; - break; - case Options::kUUIDContent: - bzero(fUUID, 16); - fEmit = true; - break; - } -} - -template -void UUIDLoadCommandAtom::setContent(const uint8_t uuid[16]) -{ - memcpy(fUUID, uuid, 16); -} - -template -void UUIDLoadCommandAtom::copyRawContent(uint8_t buffer[]) const -{ - if (fEmit) { - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_uuid_command

* cmd = (macho_uuid_command

*)buffer; - cmd->set_cmd(LC_UUID); - cmd->set_cmdsize(this->getSize()); - cmd->set_uuid((uint8_t*)fUUID); - } -} - - -template -uint64_t SubLibraryLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_library_command

) + fNameLength + 1); -} - -template -void SubLibraryLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_library_command

* cmd = (macho_sub_library_command

*)buffer; - cmd->set_cmd(LC_SUB_LIBRARY); - cmd->set_cmdsize(this->getSize()); - cmd->set_sub_library_offset(); - strncpy((char*)&buffer[sizeof(macho_sub_library_command

)], fNameStart, fNameLength); - buffer[sizeof(macho_sub_library_command

)+fNameLength] = '\0'; -} - -template -uint64_t UmbrellaLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_framework_command

) + strlen(fName) + 1); -} - -template -void UmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_framework_command

* cmd = (macho_sub_framework_command

*)buffer; - cmd->set_cmd(LC_SUB_FRAMEWORK); - cmd->set_cmdsize(this->getSize()); - cmd->set_umbrella_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_framework_command

)], fName); -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); -} - -// We should be picking it up from a header -template <> -uint64_t ThreadsLoadCommandsAtom::getSize() const -{ - return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); // PPC_THREAD_STATE - cmd->set_count(40); // PPC_THREAD_STATE_COUNT; - cmd->set_thread_register(0, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 -} - - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(5); // PPC_THREAD_STATE64 - cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; - cmd->set_thread_register(0, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); // i386_THREAD_STATE - cmd->set_count(16); // i386_THREAD_STATE_COUNT; - cmd->set_thread_register(10, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(x86_THREAD_STATE64); - cmd->set_count(x86_THREAD_STATE64_COUNT); - cmd->set_thread_register(16, start); // rip - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp -} - -template <> -void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - if ( fWriter.fEntryPoint->isThumb() ) - start |= 1ULL; - bzero(buffer, size); - macho_thread_command* cmd = (macho_thread_command*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); - cmd->set_count(17); - cmd->set_thread_register(15, start); // pc - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp? -} - -template -uint64_t RPathLoadCommandsAtom::getSize() const -{ - return this->alignedSize(sizeof(macho_rpath_command

) + strlen(fPath) + 1); -} - -template -void RPathLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_rpath_command

* cmd = (macho_rpath_command

*)buffer; - cmd->set_cmd(LC_RPATH); - cmd->set_cmdsize(this->getSize()); - cmd->set_path_offset(); - strcpy((char*)&buffer[sizeof(macho_rpath_command

)], fPath); -} - - - -template -void EncryptionLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)buffer; - cmd->set_cmd(LC_ENCRYPTION_INFO); - cmd->set_cmdsize(this->getSize()); - cmd->set_cryptoff(fStartOffset); - cmd->set_cryptsize(fEndOffset-fStartOffset); - cmd->set_cryptid(0); -} - - - -template -void LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, fSize); -} - -template -void LoadCommandsPaddingAtom::setSize(uint64_t newSize) -{ - fSize = newSize; - // this resizing by-passes the way fLargestAtomSize is set, so re-check here - if ( fWriter.fLargestAtomSize < newSize ) - fWriter.fLargestAtomSize = newSize; -} - -template -void UnwindInfoAtom::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsdaRef, - ObjectFile::Atom* personalityPointer) -{ - Info info; - info.func = func; - if ( fdeRef != NULL ) - info.fde = &fdeRef->getTarget(); - else - info.fde = NULL; - if ( lsdaRef != NULL ) { - info.lsda = &lsdaRef->getTarget(); - info.lsdaOffset = lsdaRef->getTargetOffset(); - } - else { - info.lsda = NULL; - info.lsdaOffset = 0; - } - info.personalityPointer = personalityPointer; - info.encoding = encoding; - fInfos.push_back(info); - //fprintf(stderr, "addUnwindInfo() encoding=0x%08X, lsda=%p, lsdaOffset=%d, person=%p, func=%s\n", - // encoding, info.lsda, info.lsdaOffset, personalityPointer, func->getDisplayName()); -} - -template <> -bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); -} - -template <> -bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); -} - -template -bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return false; -} - - -template -void UnwindInfoAtom::compressDuplicates(std::vector& uniqueInfos) -{ - // build new list removing entries where next function has same encoding - uniqueInfos.reserve(fInfos.size()); - Info last; - last.func = NULL; - last.lsda = NULL; - last.lsdaOffset = 0; - last.personalityPointer = NULL; - last.encoding = 0xFFFFFFFF; - for(typename std::vector::iterator it=fInfos.begin(); it != fInfos.end(); ++it) { - Info& newInfo = *it; - bool newNeedsDwarf = encodingMeansUseDwarf(newInfo.encoding); - // remove infos which have same encoding and personalityPointer as last one - if ( newNeedsDwarf || (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) - || (newInfo.lsda != NULL) || (last.lsda != NULL) ) { - uniqueInfos.push_back(newInfo); - } - last = newInfo; - } - //fprintf(stderr, "compressDuplicates() fInfos.size()=%lu, uniqueInfos.size()=%lu\n", fInfos.size(), uniqueInfos.size()); -} - -template -void UnwindInfoAtom::findCommonEncoding(const std::vector& uniqueInfos, std::map& commonEncodings) -{ - // scan infos to get frequency counts for each encoding - std::map encodingsUsed; - unsigned int mostCommonEncodingUsageCount = 0; - for(typename std::vector::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - // never put dwarf into common table - if ( encodingMeansUseDwarf(it->encoding) ) - continue; - std::map::iterator pos = encodingsUsed.find(it->encoding); - if ( pos == encodingsUsed.end() ) { - encodingsUsed[it->encoding] = 1; - } - else { - encodingsUsed[it->encoding] += 1; - if ( mostCommonEncodingUsageCount < encodingsUsed[it->encoding] ) - mostCommonEncodingUsageCount = encodingsUsed[it->encoding]; - } - } - // put the most common encodings into the common table, but at most 127 of them - for(unsigned int usages=mostCommonEncodingUsageCount; usages > 1; --usages) { - for (std::map::iterator euit=encodingsUsed.begin(); euit != encodingsUsed.end(); ++euit) { - if ( euit->second == usages ) { - unsigned int size = commonEncodings.size(); - if ( size < 127 ) { - commonEncodings[euit->first] = size; - } - } - } - } -} - -template -void UnwindInfoAtom::makeLsdaIndex(const std::vector& uniqueInfos, std::map& lsdaIndexOffsetMap) -{ - for(typename std::vector::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - lsdaIndexOffsetMap[it->func] = fLSDAIndex.size() * sizeof(macho_unwind_info_section_header_lsda_index_entry

); - if ( it->lsda != NULL ) { - LSDAEntry entry; - entry.func = it->func; - entry.lsda = it->lsda; - entry.lsdaOffset = it->lsdaOffset; - fLSDAIndex.push_back(entry); - } - } -} - -template -void UnwindInfoAtom::makePersonalityIndex(std::vector& uniqueInfos) -{ - for(typename std::vector::iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - if ( it->personalityPointer != NULL ) { - std::map::iterator pos = fPersonalityIndexMap.find(it->personalityPointer); - if ( pos == fPersonalityIndexMap.end() ) { - const uint32_t nextIndex = fPersonalityIndexMap.size() + 1; - fPersonalityIndexMap[it->personalityPointer] = nextIndex; - } - uint32_t personalityIndex = fPersonalityIndexMap[it->personalityPointer]; - it->encoding |= (personalityIndex << (__builtin_ctz(UNWIND_PERSONALITY_MASK)) ); - } - } -} - -template -unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vector& uniqueInfos, uint32_t pageSize, - unsigned int endIndex, uint8_t*& pageEnd) -{ - const unsigned int maxEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - const unsigned int entriesToAdd = ((endIndex > maxEntriesPerPage) ? maxEntriesPerPage : endIndex); - uint8_t* pageStart = pageEnd - - entriesToAdd*sizeof(unwind_info_regular_second_level_entry) - - sizeof(unwind_info_regular_second_level_page_header); - macho_unwind_info_regular_second_level_page_header

* page = (macho_unwind_info_regular_second_level_page_header

*)pageStart; - page->set_kind(UNWIND_SECOND_LEVEL_REGULAR); - page->set_entryPageOffset(sizeof(macho_unwind_info_regular_second_level_page_header

)); - page->set_entryCount(entriesToAdd); - macho_unwind_info_regular_second_level_entry

* entryTable = (macho_unwind_info_regular_second_level_entry

*)(pageStart + page->entryPageOffset()); - for (unsigned int i=0; i < entriesToAdd; ++i) { - const Info& info = uniqueInfos[endIndex-entriesToAdd+i]; - entryTable[i].set_functionOffset(0); - entryTable[i].set_encoding(info.encoding); - RegFixUp fixup; - fixup.contentPointer = (uint8_t*)(&entryTable[i]); - fixup.func = info.func; - fixup.fde = ( encodingMeansUseDwarf(info.encoding) ? info.fde : NULL ); - fRegFixUps.push_back(fixup); - } - //fprintf(stderr, "regular page with %u entries\n", entriesToAdd); - pageEnd = pageStart; - return endIndex - entriesToAdd; -} - - -template -unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector& uniqueInfos, - const std::map commonEncodings, - uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) -{ - const bool log = false; - if (log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); - // first pass calculates how many compressed entries we could fit in this sized page - // keep adding entries to page until: - // 1) encoding table plus entry table plus header exceed page size - // 2) the file offset delta from the first to last function > 24 bits - // 3) custom encoding index reachs 255 - // 4) run out of uniqueInfos to encode - std::map pageSpecificEncodings; - uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t); - std::vector encodingIndexes; - int index = endIndex-1; - int entryCount = 0; - uint64_t lastEntryAddress = uniqueInfos[index].func->getAddress(); - bool canDo = true; - while ( canDo && (index >= 0) ) { - const Info& info = uniqueInfos[index--]; - // compute encoding index - unsigned int encodingIndex; - std::map::const_iterator pos = commonEncodings.find(info.encoding); - if ( pos != commonEncodings.end() ) { - encodingIndex = pos->second; - } - else { - // no commmon entry, so add one on this page - uint32_t encoding = info.encoding; - if ( encodingMeansUseDwarf(encoding) ) { - // make unique pseudo encoding so this dwarf will gets is own encoding entry slot - encoding += (index+1); - } - std::map::iterator ppos = pageSpecificEncodings.find(encoding); - if ( ppos != pageSpecificEncodings.end() ) { - encodingIndex = pos->second; - } - else { - encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); - if ( encodingIndex <= 255 ) { - pageSpecificEncodings[encoding] = encodingIndex; - } - else { - canDo = false; // case 3) - if (log) fprintf(stderr, "end of compressed page with %u entries, %lu custom encodings because too many custom encodings\n", - entryCount, pageSpecificEncodings.size()); - } - } - } - if ( canDo ) - encodingIndexes.push_back(encodingIndex); - // compute function offset - uint32_t funcOffsetWithInPage = lastEntryAddress - info.func->getAddress(); - if ( funcOffsetWithInPage > 0x00FFFF00 ) { - // don't use 0x00FFFFFF because addresses may vary after atoms are laid out again - canDo = false; // case 2) - if (log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount); - } - else { - ++entryCount; - } - // check room for entry - if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { - canDo = false; // case 1) - --entryCount; - if (log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); - } - //if (log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); - } - - // check for cases where it would be better to use a regular (non-compressed) page - const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) - + pageSpecificEncodings.size()*sizeof(uint32_t) - + entryCount*sizeof(uint32_t); - if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { - const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - if ( entryCount < regularEntriesPerPage ) { - return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); - } - } - - // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 - uint32_t pad = 0; - if ( compressPageUsed == (pageSize-4) ) - pad = 4; - - // second pass fills in page - uint8_t* pageStart = pageEnd - compressPageUsed - pad; - macho_unwind_info_compressed_second_level_page_header

* page = (macho_unwind_info_compressed_second_level_page_header

*)pageStart; - page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); - page->set_entryPageOffset(sizeof(macho_unwind_info_compressed_second_level_page_header

)); - page->set_entryCount(entryCount); - page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); - page->set_encodingsCount(pageSpecificEncodings.size()); - uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; - // fill in entry table - uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; - ObjectFile::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; - for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { - const Info& info = uniqueInfos[i]; - uint8_t encodingIndex; - if ( encodingMeansUseDwarf(info.encoding) ) { - // dwarf entries are always in page specific encodings - encodingIndex = pageSpecificEncodings[info.encoding+i]; - } - else { - std::map::const_iterator pos = commonEncodings.find(info.encoding); - if ( pos != commonEncodings.end() ) - encodingIndex = pos->second; - else - encodingIndex = pageSpecificEncodings[info.encoding]; - } - uint32_t entryIndex = i - endIndex + entryCount; - A::P::E::set32(entiresArray[entryIndex], encodingIndex << 24); - CompressedFixUp funcStartFixUp; - funcStartFixUp.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); - funcStartFixUp.func = info.func; - funcStartFixUp.fromFunc = firstFunc; - fCompressedFixUps.push_back(funcStartFixUp); - if ( encodingMeansUseDwarf(info.encoding) ) { - CompressedEncodingFixUp dwarfStartFixup; - dwarfStartFixup.contentPointer = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]); - dwarfStartFixup.fde = info.fde; - fCompressedEncodingFixUps.push_back(dwarfStartFixup); - } - } - // fill in encodings table - for(std::map::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { - A::P::E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); - } - - if (log) fprintf(stderr, "compressed page with %u entries, %lu custom encodings\n", entryCount, pageSpecificEncodings.size()); - - // update pageEnd; - pageEnd = pageStart; - return endIndex-entryCount; // endIndex for next page -} - -template <> void UnwindInfoAtom::generate() { } -template <> void UnwindInfoAtom::generate() { } -template <> void UnwindInfoAtom::generate() { } - - -template -void UnwindInfoAtom::generate() -{ - // only generate table if there are functions with unwind info - if ( fInfos.size() > 0 ) { - // find offset of end of __unwind_info section - SectionInfo* unwindSectionInfo = (SectionInfo*)this->getSection(); - - // build new list that has proper offsetInImage and remove entries where next function has same encoding - std::vector uniqueInfos; - this->compressDuplicates(uniqueInfos); - - // build personality index, update encodings with personality index - this->makePersonalityIndex(uniqueInfos); - if ( fPersonalityIndexMap.size() > 3 ) - throw "too many personality routines for compact unwind to encode"; - - // put the most common encodings into the common table, but at most 127 of them - std::map commonEncodings; - this->findCommonEncoding(uniqueInfos, commonEncodings); - - // build lsda index - std::map lsdaIndexOffsetMap; - this->makeLsdaIndex(uniqueInfos, lsdaIndexOffsetMap); - - // calculate worst case size for all unwind info pages when allocating buffer - const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - const unsigned int pageCount = ((uniqueInfos.size() - 1)/entriesPerRegularPage) + 1; - fPagesContentForDelete = (uint8_t*)calloc(pageCount,4096); - fPagesSize = 0; - if ( fPagesContentForDelete == NULL ) - throw "could not allocate space for compact unwind info"; - ObjectFile::Atom* secondLevelFirstFuncs[pageCount*3]; - uint8_t* secondLevelPagesStarts[pageCount*3]; - - // make last second level page smaller so that all other second level pages can be page aligned - uint32_t maxLastPageSize = unwindSectionInfo->fFileOffset % 4096; - uint32_t tailPad = 0; - if ( maxLastPageSize < 128 ) { - tailPad = maxLastPageSize; - maxLastPageSize = 4096; - } - - // fill in pages in reverse order - unsigned int endIndex = uniqueInfos.size(); - unsigned int secondLevelPageCount = 0; - uint8_t* pageEnd = &fPagesContentForDelete[pageCount*4096]; - uint32_t pageSize = maxLastPageSize; - while ( endIndex > 0 ) { - endIndex = makeCompressedSecondLevelPage(uniqueInfos, commonEncodings, pageSize, endIndex, pageEnd); - secondLevelPagesStarts[secondLevelPageCount] = pageEnd; - secondLevelFirstFuncs[secondLevelPageCount] = uniqueInfos[endIndex].func; - ++secondLevelPageCount; - pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size - } - fPagesContent = pageEnd; - fPagesSize = &fPagesContentForDelete[pageCount*4096] - pageEnd; - - // calculate section layout - const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header

); - const uint32_t commonEncodingsArrayCount = commonEncodings.size(); - const uint32_t commonEncodingsArraySize = commonEncodingsArrayCount * sizeof(compact_unwind_encoding_t); - const uint32_t personalityArraySectionOffset = commonEncodingsArraySectionOffset + commonEncodingsArraySize; - const uint32_t personalityArrayCount = fPersonalityIndexMap.size(); - const uint32_t personalityArraySize = personalityArrayCount * sizeof(uint32_t); - const uint32_t indexSectionOffset = personalityArraySectionOffset + personalityArraySize; - const uint32_t indexCount = secondLevelPageCount+1; - const uint32_t indexSize = indexCount * sizeof(macho_unwind_info_section_header_index_entry

); - const uint32_t lsdaIndexArraySectionOffset = indexSectionOffset + indexSize; - const uint32_t lsdaIndexArrayCount = fLSDAIndex.size(); - const uint32_t lsdaIndexArraySize = lsdaIndexArrayCount * sizeof(macho_unwind_info_section_header_lsda_index_entry

); - const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; - - - // allocate and fill in section header - fHeaderSize = headerEndSectionOffset; - fHeaderContent = new uint8_t[fHeaderSize]; - bzero(fHeaderContent, fHeaderSize); - macho_unwind_info_section_header

* sectionHeader = (macho_unwind_info_section_header

*)fHeaderContent; - sectionHeader->set_version(UNWIND_SECTION_VERSION); - sectionHeader->set_commonEncodingsArraySectionOffset(commonEncodingsArraySectionOffset); - sectionHeader->set_commonEncodingsArrayCount(commonEncodingsArrayCount); - sectionHeader->set_personalityArraySectionOffset(personalityArraySectionOffset); - sectionHeader->set_personalityArrayCount(personalityArrayCount); - sectionHeader->set_indexSectionOffset(indexSectionOffset); - sectionHeader->set_indexCount(indexCount); - - // copy common encodings - uint32_t* commonEncodingsTable = (uint32_t*)&fHeaderContent[commonEncodingsArraySectionOffset]; - for (std::map::iterator it=commonEncodings.begin(); it != commonEncodings.end(); ++it) - A::P::E::set32(commonEncodingsTable[it->second], it->first); - - // make references for personality entries - uint32_t* personalityArray = (uint32_t*)&fHeaderContent[sectionHeader->personalityArraySectionOffset()]; - for (std::map::iterator it=fPersonalityIndexMap.begin(); it != fPersonalityIndexMap.end(); ++it) { - uint32_t offset = (uint8_t*)&personalityArray[it->second-1] - fHeaderContent; - fReferences.push_back(new WriterReference(offset, A::kImageOffset32, it->first)); - } - - // build first level index and references - macho_unwind_info_section_header_index_entry

* indexTable = (macho_unwind_info_section_header_index_entry

*)&fHeaderContent[indexSectionOffset]; - for (unsigned int i=0; i < secondLevelPageCount; ++i) { - unsigned int reverseIndex = secondLevelPageCount - 1 - i; - indexTable[i].set_functionOffset(0); - indexTable[i].set_secondLevelPagesSectionOffset(secondLevelPagesStarts[reverseIndex]-fPagesContent+headerEndSectionOffset); - indexTable[i].set_lsdaIndexArraySectionOffset(lsdaIndexOffsetMap[secondLevelFirstFuncs[reverseIndex]]+lsdaIndexArraySectionOffset); - uint32_t refOffset = (uint8_t*)&indexTable[i] - fHeaderContent; - fReferences.push_back(new WriterReference(refOffset, A::kImageOffset32, secondLevelFirstFuncs[reverseIndex])); - } - indexTable[secondLevelPageCount].set_functionOffset(0); - indexTable[secondLevelPageCount].set_secondLevelPagesSectionOffset(0); - indexTable[secondLevelPageCount].set_lsdaIndexArraySectionOffset(lsdaIndexArraySectionOffset+lsdaIndexArraySize); - fReferences.push_back(new WriterReference((uint8_t*)&indexTable[secondLevelPageCount] - fHeaderContent, A::kImageOffset32, - fInfos.back().func, fInfos.back().func->getSize()+1)); - - // build lsda references - uint32_t lsdaEntrySectionOffset = lsdaIndexArraySectionOffset; - for (typename std::vector::iterator it = fLSDAIndex.begin(); it != fLSDAIndex.end(); ++it) { - fReferences.push_back(new WriterReference(lsdaEntrySectionOffset, A::kImageOffset32, it->func)); - fReferences.push_back(new WriterReference(lsdaEntrySectionOffset+4, A::kImageOffset32, it->lsda, it->lsdaOffset)); - lsdaEntrySectionOffset += sizeof(unwind_info_section_header_lsda_index_entry); - } - - // make references for regular second level entries - for (typename std::vector::iterator it = fRegFixUps.begin(); it != fRegFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference(offset, A::kImageOffset32, it->func)); - if ( it->fde != NULL ) - fReferences.push_back(new WriterReference(offset+4, A::kSectionOffset24, it->fde)); - } - // make references for compressed second level entries - for (typename std::vector::iterator it = fCompressedFixUps.begin(); it != fCompressedFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference(offset, A::kPointerDiff24, it->func, 0, it->fromFunc, 0)); - } - for (typename std::vector::iterator it = fCompressedEncodingFixUps.begin(); it != fCompressedEncodingFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference(offset, A::kSectionOffset24, it->fde)); - } - - // update section record with new size - unwindSectionInfo->fSize = this->getSize(); - - // alter alignment so this section lays out so second level tables are page aligned - if ( secondLevelPageCount > 2 ) - fAlignment = ObjectFile::Alignment(12, (unwindSectionInfo->fFileOffset - this->getSize()) % 4096); - } - -} - - - - -template -void UnwindInfoAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fHeaderContent, fHeaderSize); - memcpy(&buffer[fHeaderSize], fPagesContent, fPagesSize); -} - - - -template -uint64_t LinkEditAtom::getFileOffset() const -{ - return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); -} - - -template -uint64_t SectionRelocationsLinkEditAtom::getSize() const -{ - return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info

); -} - -template -void SectionRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize()); -} - - -template -uint64_t LocalRelocationsLinkEditAtom::getSize() const -{ - return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info

); -} - -template -void LocalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize()); -} - - - -template -uint64_t SymbolTableLinkEditAtom::getSize() const -{ - return fWriter.fSymbolTableCount * sizeof(macho_nlist

); -} - -template -void SymbolTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fWriter.fSymbolTable, this->getSize()); -} - -template -uint64_t ExternalRelocationsLinkEditAtom::getSize() const -{ - return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info

); -} - -template -void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter

()); - memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); -} - - - -template -uint64_t IndirectTableLinkEditAtom::getSize() const -{ - return fTable.size() * sizeof(uint32_t); -} - -template -void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - const uint32_t indirectTableSize = fTable.size(); - uint32_t* indirectTable = (uint32_t*)buffer; - for(std::vector::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { - if ( it->indirectIndex < indirectTableSize ) - A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); - else - throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); - } -} - - - -template -uint64_t ModuleInfoLinkEditAtom::getSize() const -{ - return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

) - + sizeof(macho_dylib_module

) - + this->getReferencesCount()*sizeof(uint32_t); -} - -template -uint32_t ModuleInfoLinkEditAtom::getTableOfContentsFileOffset() const -{ - return this->getFileOffset(); -} - -template -uint32_t ModuleInfoLinkEditAtom::getModuleTableFileOffset() const -{ - return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

); -} - -template -uint32_t ModuleInfoLinkEditAtom::getReferencesFileOffset() const -{ - return this->getModuleTableFileOffset() + sizeof(macho_dylib_module

); -} - -template -uint32_t ModuleInfoLinkEditAtom::getReferencesCount() const -{ - return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount; -} - -template -void ModuleInfoLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - // create toc. The symbols are already sorted, they are all in the smae module - macho_dylib_table_of_contents

* p = (macho_dylib_table_of_contents

*)buffer; - for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) { - p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i); - p->set_module_index(0); - } - // create module table (one entry) - pint_t objcModuleSectionStart = 0; - pint_t objcModuleSectionSize = 0; - uint16_t numInits = 0; - uint16_t numTerms = 0; - std::vector& segmentInfos = fWriter.fSegmentInfos; - for (std::vector::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) { - std::vector& sectionInfos = (*segit)->fSections; - if ( strcmp((*segit)->fName, "__DATA") == 0 ) { - for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { - if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 ) - numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t); - else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 ) - numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t); - } - } - else if ( strcmp((*segit)->fName, "__OBJC") == 0 ) { - for (std::vector::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { - SectionInfo* sectInfo = (*sectit); - if ( strcmp(sectInfo->fSectionName, "__module_info") == 0 ) { - objcModuleSectionStart = sectInfo->getBaseAddress(); - objcModuleSectionSize = sectInfo->fSize; - } - } - } - } - macho_dylib_module

* module = (macho_dylib_module

*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

)]; - module->set_module_name(fModuleNameOffset); - module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); - module->set_nextdefsym(fWriter.fSymbolTableExportCount); - module->set_irefsym(0); - module->set_nrefsym(this->getReferencesCount()); - module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); - module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount); - module->set_iextrel(0); - module->set_nextrel(fWriter.fExternalRelocs.size()); - module->set_iinit_iterm(0,0); - module->set_ninit_nterm(numInits,numTerms); - module->set_objc_module_info_addr(objcModuleSectionStart); - module->set_objc_module_info_size(objcModuleSectionSize); - // create reference table - macho_dylib_reference

* ref = (macho_dylib_reference

*)((uint8_t*)module + sizeof(macho_dylib_module

)); - for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) { - ref->set_isym(fWriter.fSymbolTableExportStartIndex+i); - ref->set_flags(REFERENCE_FLAG_DEFINED); - } - for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) { - ref->set_isym(fWriter.fSymbolTableImportStartIndex+i); - std::map::iterator pos = fWriter.fStubsMap.find(fWriter.fImportedAtoms[i]); - if ( pos != fWriter.fStubsMap.end() ) - ref->set_flags(REFERENCE_FLAG_UNDEFINED_LAZY); - else - ref->set_flags(REFERENCE_FLAG_UNDEFINED_NON_LAZY); - } -} - - - -template -StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) - : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) -{ - fCurrentBuffer = new char[kBufferSize]; - // burn first byte of string pool (so zero is never a valid string offset) - fCurrentBuffer[fCurrentBufferUsed++] = ' '; - // make offset 1 always point to an empty string - fCurrentBuffer[fCurrentBufferUsed++] = '\0'; -} - -template -uint64_t StringsLinkEditAtom::getSize() const -{ - // align size - return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t)); -} - -template -void StringsLinkEditAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t offset = 0; - for (unsigned int i=0; i < fFullBuffers.size(); ++i) { - memcpy(&buffer[offset], fFullBuffers[i], kBufferSize); - offset += kBufferSize; - } - memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed); - // zero fill end to align - offset += fCurrentBufferUsed; - while ( (offset % sizeof(typename A::P::uint_t)) != 0 ) - buffer[offset++] = 0; -} - -template -int32_t StringsLinkEditAtom::add(const char* name) -{ - int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; - int lenNeeded = strlcpy(&fCurrentBuffer[fCurrentBufferUsed], name, kBufferSize-fCurrentBufferUsed)+1; - if ( (fCurrentBufferUsed+lenNeeded) < kBufferSize ) { - fCurrentBufferUsed += lenNeeded; - } - else { - int copied = kBufferSize-fCurrentBufferUsed-1; - // change trailing '\0' that strlcpy added to real char - fCurrentBuffer[kBufferSize-1] = name[copied]; - // alloc next buffer - fFullBuffers.push_back(fCurrentBuffer); - fCurrentBuffer = new char[kBufferSize]; - fCurrentBufferUsed = 0; - // append rest of string - this->add(&name[copied+1]); - } - return offset; -} - - -template -int32_t StringsLinkEditAtom::addUnique(const char* name) -{ - StringToOffset::iterator pos = fUniqueStrings.find(name); - if ( pos != fUniqueStrings.end() ) { - return pos->second; - } - else { - int32_t offset = this->add(name); - fUniqueStrings[name] = offset; - return offset; - } -} - - -template -const char* StringsLinkEditAtom::stringForIndex(int32_t index) const -{ - int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size(); - int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed; - // check for out of bounds - if ( index > maxIndex ) - return ""; - // check for index in fCurrentBuffer - if ( index > currentBufferStartIndex ) - return &fCurrentBuffer[index-currentBufferStartIndex]; - // otherwise index is in a full buffer - uint32_t fullBufferIndex = index/kBufferSize; - return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; -} - - - -template -BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, - ObjectFile::Atom& finalTarget, uint32_t finalTargetOffset) - : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fFinalTarget(finalTarget), fFinalTargetOffset(finalTargetOffset) -{ - if ( finalTargetOffset == 0 ) { - if ( islandRegion == 0 ) - asprintf((char**)&fName, "%s$island", name); - else - asprintf((char**)&fName, "%s$island$%d", name, islandRegion+1); - } - else { - asprintf((char**)&fName, "%s_plus_%d$island$%d", name, finalTargetOffset, islandRegion); - } - - if ( finalTarget.isThumb() ) { - if ( writer.fOptions.preferSubArchitecture() && writer.fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { - fIslandKind = kBranchIslandToThumb2; - } - else { - if ( writer.fSlideable ) - fIslandKind = kBranchIslandToThumb1; - else - fIslandKind = kBranchIslandNoPicToThumb1; - } - } - else { - fIslandKind = kBranchIslandToARM; - } -} - - -template <> -void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const -{ - int64_t displacement; - const int64_t bl_sixteenMegLimit = 0x00FFFFFF; - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress(); - if ( (displacement > bl_sixteenMegLimit) && (displacement < (-bl_sixteenMegLimit)) ) { - displacement = fTarget.getAddress() - this->getAddress(); - } - } - else { - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress(); - } - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); -} - -template <> -void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const -{ - int64_t displacement; - const int64_t bl_sixteenMegLimit = 0x00FFFFFF; - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress(); - if ( (displacement > bl_sixteenMegLimit) && (displacement < (-bl_sixteenMegLimit)) ) { - displacement = fTarget.getAddress() - this->getAddress(); - } - } - else { - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress(); - } - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); -} - -template <> -void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const -{ - const bool log = false; - switch ( fIslandKind ) { - case kBranchIslandToARM: - { - int64_t displacement; - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress() - 8; - if ( (displacement < 33554428LL) && (displacement > (-33554432LL)) ) { - // can skip branch island and jump straight to target - if (log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", fName, getFinalTargetAdress(), this->getAddress()); - } - else { - // ultimate target is too far, jump to island - displacement = fTarget.getAddress() - this->getAddress() - 8; - if (log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", fName, fTarget.getAddress()); - } - } - else { - // target of island is ultimate target - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress() - 8; - if (log) fprintf(stderr, "%s: jump to target at 0x%08llX\n", fName, fTarget.getAddress()); - } - uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; - int32_t branchInstruction = 0xEA000000 | imm24; - OSWriteLittleInt32(buffer, 0, branchInstruction); - } - break; - case kBranchIslandToThumb2: - { - int64_t displacement; - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress() - 4; - if ( (displacement < 16777214) && (displacement > (-16777216LL)) ) { - // can skip branch island and jump straight to target - if (log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", fName, getFinalTargetAdress(), this->getAddress()); - } - else { - // ultimate target is too far, jump to island - displacement = fTarget.getAddress() - this->getAddress() - 4; - if (log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", fName, fTarget.getAddress()); - } - } - else { - // target of island is ultimate target - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress() - 4; - if (log) fprintf(stderr, "%s: jump to target at 0x%08llX\n", fName, fTarget.getAddress()); - } - if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { - throwf("internal branch island error: thumb2 b/bx out of range (%lld max is +/-16M) from %s to %s in %s", - displacement, this->getDisplayName(), - fTarget.getDisplayName(), fTarget.getFile()->getPath()); - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - uint32_t opcode = 0x9000F000; - uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; - uint32_t firstDisp = (s << 10) | imm10; - uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - OSWriteLittleInt32(buffer, 0, newInstruction); - } - break; - case kBranchIslandToThumb1: - { - // There is no large displacement thumb1 branch instruction. - // Instead use ARM instructions that can jump to thumb. - // we use a 32-bit displacement, so we can directly jump to target which means no island hopping - int64_t displacement = getFinalTargetAdress() - (this->getAddress() + 12); - if ( fFinalTarget.isThumb() ) - displacement |= 1; - if (log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 - OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip - OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this - } - break; - case kBranchIslandNoPicToThumb1: - { - // There is no large displacement thumb1 branch instruction. - // Instead use ARM instructions that can jump to thumb. - // we use a 32-bit displacement, so we can directly jump to target which means no island hopping - uint32_t targetAddr = getFinalTargetAdress(); - if ( fFinalTarget.isThumb() ) - targetAddr |= 1; - if (log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); - OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] - OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this - } - break; - }; -} - -template <> -uint64_t BranchIslandAtom::getSize() const -{ - return 4; -} - -template <> -uint64_t BranchIslandAtom::getSize() const -{ - return 4; -} - -template <> -uint64_t BranchIslandAtom::getSize() const -{ - switch ( fIslandKind ) { - case kBranchIslandToARM: - return 4; - case kBranchIslandToThumb1: - return 16; - case kBranchIslandToThumb2: - return 4; - case kBranchIslandNoPicToThumb1: - return 8; - }; - throw "internal error: no ARM branch island kind"; -} - - - -template -uint64_t SegmentSplitInfoLoadCommandsAtom::getSize() const -{ - if ( fWriter.fSplitCodeToDataContentAtom->canEncode() ) - return this->alignedSize(sizeof(macho_linkedit_data_command

)); - else - return 0; // a zero size causes the load command to be suppressed -} - -template -void SegmentSplitInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - if ( size > 0 ) { - bzero(buffer, size); - macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)buffer; - cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); - cmd->set_cmdsize(size); - cmd->set_dataoff(fWriter.fSplitCodeToDataContentAtom->getFileOffset()); - cmd->set_datasize(fWriter.fSplitCodeToDataContentAtom->getSize()); - } -} - - -template -uint64_t SegmentSplitInfoContentAtom::getSize() const -{ - return fEncodedData.size(); -} - -template -void SegmentSplitInfoContentAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fEncodedData[0], fEncodedData.size()); -} - - -template -void SegmentSplitInfoContentAtom::uleb128EncodeAddresses(const std::vector::AtomAndOffset>& locations) -{ - pint_t addr = fWriter.fOptions.baseAddress(); - for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { - pint_t nextAddr = it->atom->getAddress() + it->offset; - //fprintf(stderr, "\t0x%0llX\n", (uint64_t)nextAddr); - uint64_t delta = nextAddr - addr; - if ( delta == 0 ) - throw "double split seg info for same address"; - // uleb128 encode - uint8_t byte; - do { - byte = delta & 0x7F; - delta &= ~0x7F; - if ( delta != 0 ) - byte |= 0x80; - fEncodedData.push_back(byte); - delta = delta >> 7; - } - while( byte >= 0x80 ); - addr = nextAddr; - } -} - -template -void SegmentSplitInfoContentAtom::encode() -{ - if ( ! fCantEncode ) { - fEncodedData.reserve(8192); - - if ( fKind1Locations.size() != 0 ) { - fEncodedData.push_back(1); - //fprintf(stderr, "type 1:\n"); - this->uleb128EncodeAddresses(fKind1Locations); - fEncodedData.push_back(0); - } - - if ( fKind2Locations.size() != 0 ) { - fEncodedData.push_back(2); - //fprintf(stderr, "type 2:\n"); - this->uleb128EncodeAddresses(fKind2Locations); - fEncodedData.push_back(0); - } - - if ( fKind3Locations.size() != 0 ) { - fEncodedData.push_back(3); - //fprintf(stderr, "type 3:\n"); - this->uleb128EncodeAddresses(fKind3Locations); - fEncodedData.push_back(0); - } - - if ( fKind4Locations.size() != 0 ) { - fEncodedData.push_back(4); - //fprintf(stderr, "type 4:\n"); - this->uleb128EncodeAddresses(fKind4Locations); - fEncodedData.push_back(0); - } - - // always add zero byte to mark end - fEncodedData.push_back(0); - - // add zeros to end to align size - while ( (fEncodedData.size() % sizeof(pint_t)) != 0 ) - fEncodedData.push_back(0); - } -} - - -template -ObjCInfoAtom::ObjCInfoAtom(Writer& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, - bool objcReplacementClasses, bool abi2override) - : WriterAtom(writer, getInfoSegment(abi2override)), fAbi2override(abi2override) -{ - fContent[0] = 0; - uint32_t value = 0; - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - if ( objcReplacementClasses ) - value = 1; - switch ( objcConstraint ) { - case ObjectFile::Reader::kObjcNone: - case ObjectFile::Reader::kObjcRetainRelease: - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - value |= 2; - break; - case ObjectFile::Reader::kObjcGC: - value |= 6; - break; - } - A::P::E::set32(fContent[1], value); -} - -template -void ObjCInfoAtom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fContent[0], 8); -} - - -// objc info section is in a different segment and section for 32 vs 64 bit runtimes -template <> const char* ObjCInfoAtom::getSectionName() const { return "__image_info"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return fAbi2override ? "__objc_imageinfo" : "__image_info"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } -template <> const char* ObjCInfoAtom::getSectionName() const { return "__objc_imageinfo"; } - -template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return abi2override ? Segment::fgDataSegment : Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } - - - - -template -void DyldInfoLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const -{ - // build LC_DYLD_INFO command - macho_dyld_info_command

* cmd = (macho_dyld_info_command

*)buffer; - bzero(cmd, sizeof(macho_dyld_info_command

)); - - cmd->set_cmd( fWriter.fOptions.makeClassicDyldInfo() ? LC_DYLD_INFO : LC_DYLD_INFO_ONLY); - cmd->set_cmdsize(sizeof(macho_dyld_info_command

)); - if ( (fWriter.fCompressedRebaseInfoAtom != NULL) && (fWriter.fCompressedRebaseInfoAtom->getSize() != 0) ) { - cmd->set_rebase_off(fWriter.fCompressedRebaseInfoAtom->getFileOffset()); - cmd->set_rebase_size(fWriter.fCompressedRebaseInfoAtom->getSize()); - } - if ( (fWriter.fCompressedBindingInfoAtom != NULL) && (fWriter.fCompressedBindingInfoAtom->getSize() != 0) ) { - cmd->set_bind_off(fWriter.fCompressedBindingInfoAtom->getFileOffset()); - cmd->set_bind_size(fWriter.fCompressedBindingInfoAtom->getSize()); - } - if ( (fWriter.fCompressedWeakBindingInfoAtom != NULL) && (fWriter.fCompressedWeakBindingInfoAtom->getSize() != 0) ) { - cmd->set_weak_bind_off(fWriter.fCompressedWeakBindingInfoAtom->getFileOffset()); - cmd->set_weak_bind_size(fWriter.fCompressedWeakBindingInfoAtom->getSize()); - } - if ( (fWriter.fCompressedLazyBindingInfoAtom != NULL) && (fWriter.fCompressedLazyBindingInfoAtom->getSize() != 0) ) { - cmd->set_lazy_bind_off(fWriter.fCompressedLazyBindingInfoAtom->getFileOffset()); - cmd->set_lazy_bind_size(fWriter.fCompressedLazyBindingInfoAtom->getSize()); - } - if ( (fWriter.fCompressedExportInfoAtom != NULL) && (fWriter.fCompressedExportInfoAtom->getSize() != 0) ) { - cmd->set_export_off(fWriter.fCompressedExportInfoAtom->getFileOffset()); - cmd->set_export_size(fWriter.fCompressedExportInfoAtom->getSize()); - } -} - - -struct rebase_tmp -{ - rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {} - uint8_t opcode; - uint64_t operand1; - uint64_t operand2; -}; - - -template -void CompressedRebaseInfoLinkEditAtom::encode() -{ - // sort rebase info by type, then address - const std::vector& segments = fWriter.fSegmentInfos; - std::vector& info = fWriter.fRebaseInfo; - std::sort(info.begin(), info.end()); - - // convert to temp encoding that can be more easily optimized - std::vector mid; - const SegmentInfo* currentSegment = NULL; - unsigned int segIndex = 0; - uint8_t type = 0; - uint64_t address = (uint64_t)(-1); - for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { - if ( type != it->fType ) { - mid.push_back(rebase_tmp(REBASE_OPCODE_SET_TYPE_IMM, it->fType)); - type = it->fType; - } - if ( address != it->fAddress ) { - if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <= it->fAddress) ) { - segIndex = 0; - for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - mid.push_back(rebase_tmp(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); - } - else { - mid.push_back(rebase_tmp(REBASE_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); - } - address = it->fAddress; - } - mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); - address += sizeof(pint_t); - } - mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); - - // optimize phase 1, compress packed runs of pointers - rebase_tmp* dst = &mid[0]; - for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { - if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) { - *dst = *src++; - while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) { - dst->operand1 += src->operand1; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = REBASE_OPCODE_DONE; - - // optimize phase 2, combine rebase/add pairs - dst = &mid[0]; - for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { - if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) - && (src->operand1 == 1) - && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) { - dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB; - dst->operand1 = src[1].operand1; - ++src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = REBASE_OPCODE_DONE; - - // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with - // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB - dst = &mid[0]; - for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { - uint64_t delta = src->operand1; - if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src[1].operand1 == delta) - && (src[2].operand1 == delta) ) { - // found at least three in a row, this is worth compressing - dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB; - dst->operand1 = 1; - dst->operand2 = delta; - ++src; - while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src->operand1 == delta) ) { - dst->operand1++; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = REBASE_OPCODE_DONE; - - // optimize phase 4, use immediate encodings - for (rebase_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { - if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB) - && (p->operand1 < (15*sizeof(pint_t))) - && ((p->operand1 % sizeof(pint_t)) == 0) ) { - p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED; - p->operand1 = p->operand1/sizeof(pint_t); - } - else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) { - p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES; - } - } - - // convert to compressed encoding - const static bool log = false; - fEncodedData.reserve(info.size()*2); - bool done = false; - for (std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { - switch ( it->opcode ) { - case REBASE_OPCODE_DONE: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DONE()\n"); - done = true; - break; - case REBASE_OPCODE_SET_TYPE_IMM: - if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_SET_TYPE_IMM | it->operand1); - break; - case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); - fEncodedData.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - case REBASE_OPCODE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: - if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); - fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | it->operand1 ); - break; - case REBASE_OPCODE_DO_REBASE_IMM_TIMES: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_IMM_TIMES(%lld)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | it->operand1); - break; - case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%lld)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); - fEncodedData.append_uleb128(it->operand1); - break; - case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); - fEncodedData.append_uleb128(it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - } - } - - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - if (log) fprintf(stderr, "total rebase info size = %ld\n", fEncodedData.size()); -} - - -struct binding_tmp -{ - binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) - : opcode(op), operand1(p1), operand2(p2), name(s) {} - uint8_t opcode; - uint64_t operand1; - uint64_t operand2; - const char* name; -}; - - - -template -void CompressedBindingInfoLinkEditAtom::encode() -{ - // sort by library, symbol, type, then address - const std::vector& segments = fWriter.fSegmentInfos; - std::vector& info = fWriter.fBindingInfo; - std::sort(info.begin(), info.end()); - - // convert to temp encoding that can be more easily optimized - std::vector mid; - const SegmentInfo* currentSegment = NULL; - unsigned int segIndex = 0; - int ordinal = 0x80000000; - const char* symbolName = NULL; - uint8_t type = 0; - uint64_t address = (uint64_t)(-1); - int64_t addend = 0; - for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { - if ( ordinal != it->fLibraryOrdinal ) { - if ( it->fLibraryOrdinal <= 0 ) { - // special lookups are encoded as negative numbers in BindingInfo - mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->fLibraryOrdinal)); - } - else { - mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->fLibraryOrdinal)); - } - ordinal = it->fLibraryOrdinal; - } - if ( symbolName != it->fSymbolName ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName)); - symbolName = it->fSymbolName; - } - if ( type != it->fType ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType)); - type = it->fType; - } - if ( address != it->fAddress ) { - if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) - || (it->fAddress < address) ) { - segIndex = 0; - for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); - } - else { - mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); - } - address = it->fAddress; - } - if ( addend != it->fAddend ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend)); - addend = it->fAddend; - } - mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); - address += sizeof(pint_t); - } - mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); - - - // optimize phase 1, combine bind/add pairs - binding_tmp* dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - if ( (src->opcode == BIND_OPCODE_DO_BIND) - && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { - dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; - dst->operand1 = src[1].operand1; - ++src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with - // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB - dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - uint64_t delta = src->operand1; - if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].operand1 == delta) ) { - // found at least two in a row, this is worth compressing - dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; - dst->operand1 = 1; - dst->operand2 = delta; - ++src; - while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src->operand1 == delta) ) { - dst->operand1++; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 3, use immediate encodings - for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { - if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (p->operand1 < (15*sizeof(pint_t))) - && ((p->operand1 % sizeof(pint_t)) == 0) ) { - p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; - p->operand1 = p->operand1/sizeof(pint_t); - } - else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { - p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // convert to compressed encoding - const static bool log = false; - fEncodedData.reserve(info.size()*2); - bool done = false; - for (std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { - switch ( it->opcode ) { - case BIND_OPCODE_DONE: - if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); - done = true; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); - fEncodedData.append_string(it->name); - break; - case BIND_OPCODE_SET_TYPE_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); - break; - case BIND_OPCODE_SET_ADDEND_SLEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); - fEncodedData.append_sleb128(it->operand1); - break; - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - case BIND_OPCODE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); - break; - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); - fEncodedData.append_uleb128(it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - } - } - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - if (log) fprintf(stderr, "total binding info size = %ld\n", fEncodedData.size()); - -} - - - -struct WeakBindingSorter -{ - bool operator()(const BindingInfo& left, const BindingInfo& right) - { - // sort by symbol, type, address - if ( left.fSymbolName != right.fSymbolName ) - return ( strcmp(left.fSymbolName, right.fSymbolName) < 0 ); - if ( left.fType != right.fType ) - return (left.fType < right.fType); - return (left.fAddress < right.fAddress); - } -}; - - - -template -void CompressedWeakBindingInfoLinkEditAtom::encode() -{ - // add regular atoms that override a dylib's weak definitions - for(std::set::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin(); - it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) { - if ( fWriter.shouldExport(**it) ) - fWriter.fWeakBindingInfo.push_back(BindingInfo(0, (*it)->getName(), true, 0, 0)); - } - - // add all exported weak definitions - for(std::vector::iterator it = fWriter.fAllAtoms->begin(); it != fWriter.fAllAtoms->end(); ++it) { - ObjectFile::Atom* atom = *it; - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) && fWriter.shouldExport(*atom) ) { - fWriter.fWeakBindingInfo.push_back(BindingInfo(0, atom->getName(), false, 0, 0)); - } - } - - // sort by symbol, type, address - const std::vector& segments = fWriter.fSegmentInfos; - std::vector& info = fWriter.fWeakBindingInfo; - if ( info.size() == 0 ) - return; - std::sort(info.begin(), info.end(), WeakBindingSorter()); - - // convert to temp encoding that can be more easily optimized - std::vector mid; - mid.reserve(info.size()); - const SegmentInfo* currentSegment = NULL; - unsigned int segIndex = 0; - const char* symbolName = NULL; - uint8_t type = 0; - uint64_t address = (uint64_t)(-1); - int64_t addend = 0; - for (std::vector::iterator it = info.begin(); it != info.end(); ++it) { - if ( symbolName != it->fSymbolName ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName)); - symbolName = it->fSymbolName; - } - if ( it->fType != 0 ) { - if ( type != it->fType ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType)); - type = it->fType; - } - if ( address != it->fAddress ) { - // non weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM - // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND - if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) ) { - segIndex = 0; - for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); - } - else { - mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); - } - address = it->fAddress; - } - if ( addend != it->fAddend ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend)); - addend = it->fAddend; - } - mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); - address += sizeof(pint_t); - } - } - mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); - - - // optimize phase 1, combine bind/add pairs - binding_tmp* dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - if ( (src->opcode == BIND_OPCODE_DO_BIND) - && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { - dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; - dst->operand1 = src[1].operand1; - ++src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with - // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB - dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - uint64_t delta = src->operand1; - if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].operand1 == delta) ) { - // found at least two in a row, this is worth compressing - dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; - dst->operand1 = 1; - dst->operand2 = delta; - ++src; - while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src->operand1 == delta) ) { - dst->operand1++; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 3, use immediate encodings - for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { - if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (p->operand1 < (15*sizeof(pint_t))) - && ((p->operand1 % sizeof(pint_t)) == 0) ) { - p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; - p->operand1 = p->operand1/sizeof(pint_t); - } - } - dst->opcode = BIND_OPCODE_DONE; - - - // convert to compressed encoding - const static bool log = false; - fEncodedData.reserve(info.size()*2); - bool done = false; - for (std::vector::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { - switch ( it->opcode ) { - case BIND_OPCODE_DONE: - if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); - fEncodedData.append_byte(BIND_OPCODE_DONE); - done = true; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); - fEncodedData.append_string(it->name); - break; - case BIND_OPCODE_SET_TYPE_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); - break; - case BIND_OPCODE_SET_ADDEND_SLEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); - fEncodedData.append_sleb128(it->operand1); - break; - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - case BIND_OPCODE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); - break; - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); - fEncodedData.append_uleb128(it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - } - } - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - if (log) fprintf(stderr, "total weak binding info size = %ld\n", fEncodedData.size()); - -} - -template -void CompressedLazyBindingInfoLinkEditAtom::encode() -{ - // stream all lazy bindings and record start offsets - const SegmentInfo* currentSegment = NULL; - uint8_t segIndex = 0; - const std::vector& segments = fWriter.fSegmentInfos; - std::vector*>& allLazys = fWriter.fAllSynthesizedLazyPointers; - for (typename std::vector*>::iterator it = allLazys.begin(); it != allLazys.end(); ++it) { - LazyPointerAtom* lazyPointerAtom = *it; - ObjectFile::Atom* lazyPointerTargetAtom = lazyPointerAtom->getTarget(); - - // skip lazy pointers that are bound non-lazily because they are coalesced - if ( ! fWriter.targetRequiresWeakBinding(*lazyPointerTargetAtom) ) { - // record start offset for use by stub helper - lazyPointerAtom->setLazyBindingInfoOffset(fEncodedData.size()); - - // write address to bind - pint_t address = lazyPointerAtom->getAddress(); - if ( (currentSegment == NULL) || (address < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <= address) ) { - segIndex = 0; - for (std::vector::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= address) && (address < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - } - fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); - fEncodedData.append_uleb128(lazyPointerAtom->getAddress() - currentSegment->fBaseAddress); - - // write ordinal - int ordinal = fWriter.compressedOrdinalForImortedAtom(lazyPointerTargetAtom); - if ( ordinal <= 0 ) { - // special lookups are encoded as negative numbers in BindingInfo - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (ordinal & BIND_IMMEDIATE_MASK) ); - } - else if ( ordinal <= 15 ) { - // small ordinals are encoded in opcode - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | ordinal); - } - else { - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - fEncodedData.append_uleb128(ordinal); - } - // write symbol name - bool weak_import = fWriter.fWeakImportMap[lazyPointerTargetAtom]; - if ( weak_import ) - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | BIND_SYMBOL_FLAGS_WEAK_IMPORT); - else - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); - fEncodedData.append_string(lazyPointerTargetAtom->getName()); - // write do bind - fEncodedData.append_byte(BIND_OPCODE_DO_BIND); - fEncodedData.append_byte(BIND_OPCODE_DONE); - } - } - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - //fprintf(stderr, "lazy binding info size = %ld, for %ld entries\n", fEncodedData.size(), allLazys.size()); -} - -struct TrieEntriesSorter -{ - TrieEntriesSorter(Options& o) : fOptions(o) {} - - bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) - { - unsigned int leftOrder; - unsigned int rightOrder; - fOptions.exportedSymbolOrder(left.name, &leftOrder); - fOptions.exportedSymbolOrder(right.name, &rightOrder); - if ( leftOrder != rightOrder ) - return (leftOrder < rightOrder); - else - return (left.address < right.address); - } -private: - Options& fOptions; -}; - - -template -void CompressedExportInfoLinkEditAtom::encode() -{ - // make vector of mach_o::trie::Entry for all exported symbols - std::vector& exports = fWriter.fExportedAtoms; - uint64_t imageBaseAddress = fWriter.fMachHeaderAtom->getAddress(); - std::vector entries; - entries.reserve(exports.size()); - for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { - ObjectFile::Atom* atom = *it; - uint64_t flags = 0; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - uint64_t address = atom->getAddress() - imageBaseAddress; - if ( atom->isThumb() ) - address |= 1; - mach_o::trie::Entry entry; - entry.name = atom->getName(); - entry.flags = flags; - entry.address = address; - entries.push_back(entry); - } - - // sort vector by -exported_symbols_order, and any others by address - std::sort(entries.begin(), entries.end(), TrieEntriesSorter(fWriter.fOptions)); - - // create trie - mach_o::trie::makeTrie(entries, fEncodedData.bytes()); - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); -} - - - - - -}; // namespace executable -}; // namespace mach_o - - -#endif // __EXECUTABLE_MACH_O__ diff --git a/ld64/src/ld/ObjectFile.h b/ld64/src/ld/ObjectFile.h deleted file mode 100644 index 2710bfb..0000000 --- a/ld64/src/ld/ObjectFile.h +++ /dev/null @@ -1,385 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#ifndef __OBJECTFILE__ -#define __OBJECTFILE__ - -#include -#include -#include -#include - - - -// -// These classes represent the abstract Atoms and References that are the basis of the linker. -// An Atom and a Reference correspond to a Node and Edge in graph theory. -// -// A Reader is a class which parses an object file and presents it as Atoms and References. -// All linking operations are done on Atoms and References. This makes the linker file -// format independent. -// -// A Writer takes a vector of Atoms with all References resolved and produces an executable file. -// -// - - - -namespace ObjectFile { - - -struct LineInfo -{ - uint32_t atomOffset; - const char* fileName; - uint32_t lineNumber; -}; - - -class ReaderOptions -{ -public: - ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), - fLinkingMainExecutable(false), - fForFinalLinkedImage(false), fNoEHLabels(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), - fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), - fImplicitlyLinkPublicDylibs(true), - fAddCompactUnwindEncoding(true), - fWarnCompactUnwind(false), - fRemoveDwarfUnwindIfCompactExists(false), - fMakeCompressedDyldInfo(false), - fAutoOrderInitializers(true), - fOptimizeZeroFill(true), - fLogObjectFiles(false), fLogAllFiles(false), - fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), - fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} - enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; - enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6, k10_7 }; - enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0, k3_1, k3_2, k4_0 }; - - struct AliasPair { - const char* realName; - const char* alias; - }; - - bool fFullyLoadArchives; - bool fLoadAllObjcObjectsFromArchives; - bool fFlatNamespace; - bool fLinkingMainExecutable; - bool fForFinalLinkedImage; - bool fNoEHLabels; - bool fForStatic; - bool fForDyld; - bool fMakeTentativeDefinitionsReal; - bool fWhyLoad; - bool fRootSafe; - bool fSetuidSafe; - DebugInfoStripping fDebugInfoStripping; - bool fImplicitlyLinkPublicDylibs; - bool fAddCompactUnwindEncoding; - bool fWarnCompactUnwind; - bool fRemoveDwarfUnwindIfCompactExists; - bool fMakeCompressedDyldInfo; - bool fAutoOrderInitializers; - bool fOptimizeZeroFill; - bool fLogObjectFiles; - bool fLogAllFiles; - bool fTraceDylibs; - bool fTraceIndirectDylibs; - bool fTraceArchives; - const char* fTraceOutputFile; - MacVersionMin fMacVersionMin; - IPhoneVersionMin fIPhoneVersionMin; - std::vector fAliases; -}; - - -class Reader -{ -public: - enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; - struct Stab - { - class Atom* atom; - uint8_t type; - uint8_t other; - uint16_t desc; - uint32_t value; - const char* string; - }; - enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC }; - enum CpuConstraint { kCpuAny = 0 }; - - class DylibHander - { - public: - virtual ~DylibHander() {} - virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0; - }; - - - static Reader* createReader(const char* path, const ReaderOptions& options); - - virtual const char* getPath() = 0; - virtual time_t getModificationTime() = 0; - virtual DebugInfoKind getDebugInfoKind() = 0; - virtual std::vector& getAtoms() = 0; - virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; - virtual std::vector* getStabs() = 0; - virtual ObjcConstraint getObjCConstraint() { return kObjcNone; } - virtual uint32_t updateCpuConstraint(uint32_t current) { return current; } - virtual bool objcReplacementClasses() { return false; } - - // For relocatable object files only - virtual bool canScatterAtoms() { return true; } - virtual bool optimize(const std::vector&, std::vector&, - std::vector&, const std::set&, - std::vector&, - uint32_t, ObjectFile::Reader* writer, - ObjectFile::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) { return false; } - virtual bool hasLongBranchStubs() { return false; } - - // For Dynamic Libraries only - virtual const char* getInstallPath() { return NULL; } - virtual uint32_t getTimestamp() { return 0; } - virtual uint32_t getCurrentVersion() { return 0; } - virtual uint32_t getCompatibilityVersion() { return 0; } - virtual void processIndirectLibraries(DylibHander* handler) { } - virtual void setExplicitlyLinked() { } - virtual bool explicitlyLinked() { return false; } - virtual bool implicitlyLinked() { return false; } - virtual bool providedExportAtom() { return false; } - virtual const char* parentUmbrella() { return NULL; } - virtual std::vector* getAllowableClients() { return NULL; } - virtual bool hasWeakExternals() { return false; } - virtual bool deadStrippable() { return false; } - virtual bool isLazyLoadedDylib() { return false; } - -protected: - Reader() {} - virtual ~Reader() {} -}; - -class Segment -{ -public: - virtual const char* getName() const = 0; - virtual bool isContentReadable() const = 0; - virtual bool isContentWritable() const = 0; - virtual bool isContentExecutable() const = 0; - - uint64_t getBaseAddress() const { return fBaseAddress; } - void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } - virtual bool hasFixedAddress() const { return false; } - -protected: - Segment() : fBaseAddress(0) {} - virtual ~Segment() {} - uint64_t fBaseAddress; -}; - -class Reference; - -class Section -{ -public: - unsigned int getIndex() { return fIndex; } - uint64_t getBaseAddress() { return fBaseAddress; } - void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } - void* fOther; - -protected: - Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} - uint64_t fBaseAddress; - unsigned int fIndex; -}; - - -struct Alignment -{ - Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} - uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } - uint16_t powerOf2; - uint16_t modulus; -}; - -struct UnwindInfo -{ - uint32_t startOffset; - uint32_t unwindInfo; - - typedef UnwindInfo* iterator; - -}; - - -// -// An atom is the fundamental unit of linking. A C function or global variable is an atom. -// An atom has content and some attributes. The content of a function atom is the instructions -// that implement the function. The content of a global variable atom is its initial bits. -// -// Name: -// The name of an atom is the label name generated by the compiler. A C compiler names foo() -// as _foo. A C++ compiler names foo() as __Z3foov. -// The name refers to the first byte of the content. An atom cannot have multiple entry points. -// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. -// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. -// -// Scope: -// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond -// to the C visibility of static, hidden, default. -// -// DefinitionKind: -// An atom is one of five defintion kinds: -// regular Most atoms. -// weak C++ compiler makes some functions weak if there might be multiple copies -// that the linker needs to coalesce. -// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. -// It could be a prototype or it could be a definition. -// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists -// so that all References can be resolved. -// external-weak Same as external, but the definition in the dylib is weak. -// -// SymbolTableInclusion: -// An atom may or may not be in the symbol table in an object file. -// in Most atoms for functions or global data -// not-in Anonymous atoms such literal c-strings, or other compiler generated data -// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) -// -// Ordinal: -// When a reader is created it is given a base ordinal number. All atoms created by the reader -// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal -// values are used by the linker to sort the atom graph when producing the output file. -// -class Atom -{ -public: - enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; - enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; - enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType, kSectionStart, kSectionEnd, kBranchIsland, - kLazyPointer, kStub, kNonLazyPointer, kLazyDylibPointer, kStubHelper }; - enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; - - virtual Reader* getFile() const = 0; - virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; - virtual const char* getName() const = 0; - virtual const char* getDisplayName() const = 0; - virtual Scope getScope() const = 0; - virtual DefinitionKind getDefinitionKind() const = 0; - virtual ContentType getContentType() const { return kUnclassifiedType; } - virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; - virtual bool dontDeadStrip() const = 0; - virtual bool isZeroFill() const = 0; - virtual bool isThumb() const = 0; - virtual uint64_t getSize() const = 0; - virtual std::vector& getReferences() const = 0; - virtual bool mustRemainInSection() const = 0; - virtual const char* getSectionName() const = 0; - virtual Segment& getSegment() const = 0; - virtual Atom& getFollowOnAtom() const = 0; - virtual uint32_t getOrdinal() const = 0; - virtual std::vector* getLineInfo() const = 0; - virtual Alignment getAlignment() const = 0; - virtual void copyRawContent(uint8_t buffer[]) const = 0; - virtual void setScope(Scope) = 0; - virtual UnwindInfo::iterator beginUnwind() { return NULL; } - virtual UnwindInfo::iterator endUnwind() { return NULL; } - virtual Reference* getLSDA() { return NULL; } - virtual Reference* getFDE() { return NULL; } - virtual Atom* getPersonalityPointer() { return NULL; } - - uint64_t getSectionOffset() const { return fSectionOffset; } - uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } - class Section* getSection() const { return fSection; } - - virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } - virtual void setSection(class Section* sect) { fSection = sect; } - -protected: - Atom() : fSectionOffset(0), fSection(NULL) {} - virtual ~Atom() {} - - uint64_t fSectionOffset; - class Section* fSection; -}; - - -// -// A Reference is a directed edge to another Atom. When an instruction in -// the content of an Atom refers to another Atom, that is represented by a -// Reference. -// -// There are two kinds of references: direct and by-name. With a direct Reference, -// the target is bound by the Reader that created it. For instance a reference to a -// static would produce a direct reference. A by-name reference requires the linker -// to find the target Atom with the required name in order to be bound. -// -// For a link to succeed all References must be bound. -// -// A Reference has an optional "from" target. This is used when the content to fix-up -// is the difference of two Atom address. For instance, if a pointer sized data Atom -// is to contain A - B, then the Atom would have on Reference with a target of "A" and -// a from-target of "B". -// -// A Reference also has a fix-up-offset. This is the offset into the content of the -// Atom holding the reference where the fix-up (relocation) will be applied. -// -// -// -class Reference -{ -public: - enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind }; - - virtual TargetBinding getTargetBinding() const = 0; - virtual TargetBinding getFromTargetBinding() const = 0; - virtual uint8_t getKind() const = 0; - virtual uint64_t getFixUpOffset() const = 0; - virtual const char* getTargetName() const = 0; - virtual Atom& getTarget() const = 0; - virtual uint64_t getTargetOffset() const = 0; - virtual Atom& getFromTarget() const = 0; - virtual const char* getFromTargetName() const = 0; - virtual uint64_t getFromTargetOffset() const = 0; - - virtual void setTarget(Atom&, uint64_t offset) = 0; - virtual void setFromTarget(Atom&) = 0; - virtual const char* getDescription() const = 0; - virtual bool isBranch() const { return false; } - -protected: - Reference() {} - virtual ~Reference() {} -}; - - -}; // namespace ObjectFile - - -#endif // __OBJECTFILE__ diff --git a/ld64/src/ld/OpaqueSection.hpp b/ld64/src/ld/OpaqueSection.hpp deleted file mode 100644 index cddd45c..0000000 --- a/ld64/src/ld/OpaqueSection.hpp +++ /dev/null @@ -1,199 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OPAQUE_SECTION__ -#define __OPAQUE_SECTION__ - - -#include - -#include "ObjectFile.h" - -namespace opaque_section { - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); } -private: - const char* fName; -}; - - -class Reader : public ObjectFile::Reader -{ -public: - Reader(const char* segmentName, const char* sectionName, const char* path, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL); - virtual ~Reader(); - - void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, - uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0); - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector& getAtoms() { return fAtoms; } - virtual std::vector* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector* getStabs() { return NULL; } - -private: - const char* fPath; - std::vector fAtoms; -}; - -class Reference : public ObjectFile::Reference -{ -public: - Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset, - const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0) - : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind), - fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {} - virtual ~Reference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffset; } - virtual const char* getTargetName() const { return fTarget->getName(); } - virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } - virtual uint64_t getTargetOffset() const { return fTargetOffset; } - virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); } - virtual const char* getFromTargetName() const { return fFromTarget->getName(); } - virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } - virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; } - virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } - virtual const char* getDescription() const { return "opaque section reference"; } - -private: - uint64_t fFixUpOffset; - const ObjectFile::Atom* fTarget; - uint64_t fTargetOffset; - uint8_t fKind; - const ObjectFile::Atom* fFromTarget; - uint64_t fFromTargetOffset; -}; - - -class Atom : public ObjectFile::Atom { -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const; - virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return true; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return fFileLength; } - virtual std::vector& getReferences() const { return (std::vector&)(fReferences); } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return fSectionName; } - virtual Segment& getSegment() const { return fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); } - virtual void copyRawContent(uint8_t buffer[]) const; - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - - Atom(Reader& owner, Segment& segment, const char* sectionName, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName); - virtual ~Atom() {} - - Reader& fOwner; - Segment& fSegment; - const char* fName; - const char* fSectionName; - const uint8_t* fFileContent; - uint32_t fOrdinal; - uint64_t fFileLength; - std::vector fReferences; -}; - - - -Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName) - : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength) -{ - if ( symbolName != NULL ) - fName = strdup(symbolName); - else - asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName); -} - - -Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], - uint64_t fileLength, uint32_t ordinal, const char* symbolName) - : fPath(path) -{ - fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName)); -} - -Reader::~Reader() -{ -} - -void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, - uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget) -{ - fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget)); -} - - -const char* Atom::getDisplayName() const -{ - static char name[64]; - sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName); - return name; -} - - -void Atom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fFileContent, fFileLength); -} - - - -}; - - - -#endif // __OPAQUE_SECTION__ - - - diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 492d140..1de3379 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,6 +28,7 @@ #include #include #include + #include #include "configure.h" @@ -35,11 +36,15 @@ #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" -extern void printLTOVersion(Options &opts); +// upward dependency on lto::version() +namespace lto { + extern const char* version(); +} // magic to place command line in crash reports +const int crashreporterBufferSize = 2000; extern "C" char* __crashreporter_info__; -static char crashreporterBuffer[1000]; +static char crashreporterBuffer[crashreporterBufferSize]; char* __crashreporter_info__ = crashreporterBuffer; static bool sEmitWarnings = true; @@ -81,31 +86,54 @@ void throwf(const char* format, ...) } Options::Options(int argc, const char* argv[]) - : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable), + : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), - fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0), + fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), + fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL), fBaseWritableAddress(0), fSplitSegs(false), - fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), - fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(true), fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), + fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), + fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), fSharedRegionEligible(false), fPrintOrderFileStatistics(false), - fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), - fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), - fUseSimplifiedDylibReExports(false), fObjCABIVersion2POverride(false), fSaveTempFiles(false) + fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), + fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), + fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), + fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), + fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), + fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), + fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), + fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), + fAutoOrderInitializers(true), fOptimizeZeroFill(true), 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), + fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fMacVersionMin(ld::macVersionUnset), fIPhoneVersionMin(ld::iPhoneVersionUnset), + fSaveTempFiles(false) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -119,48 +147,10 @@ Options::~Options() { } -const ObjectFile::ReaderOptions& Options::readerOptions() -{ - return fReaderOptions; -} - - -const char* Options::getOutputFilePath() -{ - return fOutputFile; -} - -std::vector& Options::getInputFiles() -{ - return fInputFiles; -} - -Options::OutputKind Options::outputKind() -{ - return fOutputKind; -} -bool Options::bindAtLoad() -{ - return fBindAtLoad; -} -bool Options::prebind() -{ - return fPrebind; -} -bool Options::fullyLoadArchives() -{ - return fReaderOptions.fFullyLoadArchives; -} - -Options::NameSpace Options::nameSpace() -{ - return fNameSpace; -} - -const char* Options::installPath() +const char* Options::installPath() const { if ( fDylibInstallName != NULL ) return fDylibInstallName; @@ -170,32 +160,8 @@ const char* Options::installPath() return fOutputFile; } -uint32_t Options::currentVersion() -{ - return fDylibCurrentVersion; -} - -uint32_t Options::compatibilityVersion() -{ - return fDylibCompatVersion; -} - -const char* Options::entryName() -{ - return fEntryName; -} - -uint64_t Options::baseAddress() -{ - return fBaseAddress; -} - -bool Options::keepPrivateExterns() -{ - return fKeepPrivateExterns; -} -bool Options::interposable(const char* name) +bool Options::interposable(const char* name) const { switch ( fInterposeMode ) { case kInterposeNone: @@ -208,121 +174,34 @@ bool Options::interposable(const char* name) throw "internal error"; } -bool Options::needsModuleTable() -{ - return fNeedsModuleTable; -} - -bool Options::ignoreOtherArchInputFiles() -{ - return fIgnoreOtherArchFiles; -} - -bool Options::forceCpuSubtypeAll() -{ - return fForceSubtypeAll; -} - -bool Options::traceDylibs() -{ - return fReaderOptions.fTraceDylibs; -} - -bool Options::traceArchives() -{ - return fReaderOptions.fTraceArchives; -} - -Options::UndefinedTreatment Options::undefinedTreatment() -{ - return fUndefinedTreatment; -} - -Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() -{ - return fWeakReferenceMismatchTreatment; -} - -const char* Options::umbrellaName() -{ - return fUmbrellaName; -} - -std::vector& Options::allowableClients() -{ - return fAllowableClients; -} - -const char* Options::clientName() -{ - return fClientName; -} - -uint64_t Options::zeroPageSize() -{ - return fZeroPageSize; -} - -bool Options::hasCustomStack() -{ - return (fStackSize != 0); -} - -uint64_t Options::customStackSize() -{ - return fStackSize; -} - -uint64_t Options::customStackAddr() -{ - return fStackAddr; -} -bool Options::hasExecutableStack() -{ - return fExecutableStack; -} -std::vector& Options::initialUndefines() -{ - return fInitialUndefines; -} -bool Options::printWhyLive(const char* symbolName) +bool Options::printWhyLive(const char* symbolName) const { return ( fWhyLive.find(symbolName) != fWhyLive.end() ); } -const char* Options::initFunctionName() -{ - return fInitFunctionName; -} - const char* Options::dotOutputFile() { return fDotOutputFile; } -bool Options::hasExportRestrictList() -{ - return (fExportMode != kExportDefault); -} -bool Options::hasExportMaskList() +bool Options::hasWildCardExportRestrictList() const { - return (fExportMode == kExportSome); + // has -exported_symbols_list which contains some wildcards + return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); } - -bool Options::hasWildCardExportRestrictList() +bool Options::hasWeakBitTweaks() const { // has -exported_symbols_list which contains some wildcards - return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); + return (!fForceWeakSymbols.empty() || !fForceNotWeakSymbols.empty()); } - -bool Options::allGlobalsAreDeadStripRoots() +bool Options::allGlobalsAreDeadStripRoots() const { // -exported_symbols_list means globals are not exported by default if ( fExportMode == kExportSome ) @@ -344,59 +223,122 @@ bool Options::allGlobalsAreDeadStripRoots() return false; } -uint32_t Options::minimumHeaderPad() + +bool Options::keepRelocations() { - return fMinimumHeaderPad; + return fKeepRelocations; } -std::vector& Options::extraSections() +bool Options::warnStabs() { - return fExtraSections; + return fWarnStabs; } -std::vector& Options::sectionAlignments() +const char* Options::executablePath() { - return fSectionAlignments; + return fExecutablePath; } -Options::CommonsMode Options::commonsMode() + +uint32_t Options::initialSegProtection(const char* segName) const { - return fCommonsMode; + for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) { + return it->init; + } + } + if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } + else if ( strcmp(segName, "__TEXT") == 0 ) { + return VM_PROT_READ | VM_PROT_EXECUTE; + } + else if ( strcmp(segName, "__LINKEDIT") == 0 ) { + return VM_PROT_READ; + } + + // all others default to read-write + return VM_PROT_READ | VM_PROT_WRITE; } -bool Options::warnCommons() +uint32_t Options::maxSegProtection(const char* segName) const { - return fWarnCommons; + // iPhoneOS always uses same protection for max and initial + if ( fIPhoneVersionMin != ld::iPhoneVersionUnset ) + return initialSegProtection(segName); + + for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) { + return it->max; + } + } + if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } + // all others default to all + return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; +} + +uint64_t Options::segPageSize(const char* segName) const +{ + for(std::vector::const_iterator it=fCustomSegmentSizes.begin(); it != fCustomSegmentSizes.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return it->size; + } + return fSegmentAlignment; } -bool Options::keepRelocations() +uint64_t Options::customSegmentAddress(const char* segName) const { - return fKeepRelocations; + for(std::vector::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return it->address; + } + // if custom stack in use, model as segment with custom address + if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) + return fStackAddr - fStackSize; + return 0; } -bool Options::warnStabs() +bool Options::hasCustomSegmentAddress(const char* segName) const { - return fWarnStabs; + for(std::vector::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return true; + } + // if custom stack in use, model as segment with custom address + if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) + return true; + return false; } -const char* Options::executablePath() +bool Options::hasCustomSectionAlignment(const char* segName, const char* sectName) const { - return fExecutablePath; + for (std::vector::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { + if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) + return true; + } + return false; } -Options::DeadStripMode Options::deadStrip() +uint8_t Options::customSectionAlignment(const char* segName, const char* sectName) const { - return fDeadStrip; + for (std::vector::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { + if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) + return it->alignment; + } + return 0; } + bool Options::hasExportedSymbolOrder() { return (fExportSymbolsOrder.size() > 0); } -bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) +bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) const { - NameToOrder::iterator pos = fExportSymbolsOrder.find(sym); + NameToOrder::const_iterator pos = fExportSymbolsOrder.find(sym); if ( pos != fExportSymbolsOrder.end() ) { *order = pos->second; return true; @@ -478,8 +420,28 @@ void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderM // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table } +bool Options::forceWeak(const char* symbolName) const +{ + return fForceWeakSymbols.contains(symbolName); +} + +bool Options::forceNotWeak(const char* symbolName) const +{ + return fForceNotWeakSymbols.contains(symbolName); +} + +bool Options::forceWeakNonWildCard(const char* symbolName) const +{ + return fForceWeakSymbols.containsNonWildcard(symbolName); +} + +bool Options::forceNotWeakNonWildcard(const char* symbolName) const +{ + return fForceNotWeakSymbols.containsNonWildcard(symbolName); +} + -bool Options::shouldExport(const char* symbolName) +bool Options::shouldExport(const char* symbolName) const { switch (fExportMode) { case kExportSome: @@ -492,7 +454,12 @@ bool Options::shouldExport(const char* symbolName) throw "internal error"; } -bool Options::keepLocalSymbol(const char* symbolName) +bool Options::shouldReExport(const char* symbolName) const +{ + return fReExportSymbols.contains(symbolName); +} + +bool Options::keepLocalSymbol(const char* symbolName) const { switch (fLocalSymbolHandling) { case kLocalSymbolsAll: @@ -507,81 +474,204 @@ bool Options::keepLocalSymbol(const char* symbolName) throw "internal error"; } -void Options::parseArch(const char* architecture) + +void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) { - if ( architecture == NULL ) + fArchitecture = type; + fSubArchitecture = subtype; + switch ( type ) { + case CPU_TYPE_POWERPC: + switch ( subtype ) { + case CPU_SUBTYPE_POWERPC_750: + fArchitectureName = "ppc750"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_7400: + fArchitectureName = "ppc7400"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_7450: + fArchitectureName = "ppc7450"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_970: + fArchitectureName = "ppc970"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_ALL: + fArchitectureName = "ppc"; + fHasPreferredSubType = false; + break; + default: + assert(0 && "unknown ppc subtype"); + fArchitectureName = "ppc"; + break; + } + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + break; + case CPU_TYPE_POWERPC64: + fArchitectureName = "ppc64"; + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + warning("-macosx_version_min not specificed, assuming 10.5"); + fMacVersionMin = ld::mac10_5; + } + break; + case CPU_TYPE_I386: + fArchitectureName = "i386"; + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + case CPU_TYPE_X86_64: + fArchitectureName = "x86_64"; + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + case CPU_TYPE_ARM: + 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; + } + if ( (fMacVersionMin == ld::macVersionUnset) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) && (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); +#elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); +#else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; +#endif + } + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iPhone3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + default: + fArchitectureName = "unknown architecture"; + break; + } +} + +void Options::parseArch(const char* arch) +{ + if ( arch == NULL ) throw "-arch must be followed by an architecture string"; - if ( strcmp(architecture, "ppc") == 0 ) { + fArchitectureName = arch; + if ( strcmp(arch, "ppc") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; } - else if ( strcmp(architecture, "ppc64") == 0 ) { + else if ( strcmp(arch, "ppc64") == 0 ) { fArchitecture = CPU_TYPE_POWERPC64; fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; } - else if ( strcmp(architecture, "i386") == 0 ) { + else if ( strcmp(arch, "i386") == 0 ) { fArchitecture = CPU_TYPE_I386; fSubArchitecture = CPU_SUBTYPE_I386_ALL; } - else if ( strcmp(architecture, "x86_64") == 0 ) { + else if ( strcmp(arch, "x86_64") == 0 ) { fArchitecture = CPU_TYPE_X86_64; fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; } - else if ( strcmp(architecture, "arm") == 0 ) { + else if ( strcmp(arch, "arm") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_ALL; } // compatibility support for cpu-sub-types - else if ( strcmp(architecture, "ppc750") == 0 ) { + else if ( strcmp(arch, "ppc750") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_750; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "ppc7400") == 0 ) { + else if ( strcmp(arch, "ppc7400") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "ppc7450") == 0 ) { + else if ( strcmp(arch, "ppc7450") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "ppc970") == 0 ) { + else if ( strcmp(arch, "ppc970") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_970; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv6") == 0 ) { + else if ( strcmp(arch, "armv6") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V6; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv5") == 0 ) { + else if ( strcmp(arch, "armv5") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv4t") == 0 ) { + else if ( strcmp(arch, "armv4t") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V4T; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "xscale") == 0 ) { + else if ( strcmp(arch, "xscale") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv7") == 0 ) { + else if ( strcmp(arch, "armv7") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V7; fHasPreferredSubType = true; } else - throwf("unknown/unsupported architecture name for: -arch %s", architecture); + throwf("unknown/unsupported architecture name for: -arch %s", arch); } -bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) +bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const { struct stat statBuffer; char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; @@ -723,7 +813,7 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } -Options::FileInfo Options::findFile(const char* path) +Options::FileInfo Options::findFile(const char* path) const { FileInfo result; struct stat statBuffer; @@ -731,7 +821,7 @@ Options::FileInfo Options::findFile(const char* path) // if absolute path and not a .o file, the use SDK prefix if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { const int pathLen = strlen(path); - for (std::vector::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { + for (std::vector::const_iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { // ??? Shouldn't we be using String here? const char* sdkPathDir = *it; const int sdkPathDirLen = strlen(sdkPathDir); @@ -777,7 +867,7 @@ Options::FileInfo Options::findFile(const char* path) throwf("file not found: %s", path); } -Options::FileInfo Options::findFileUsingPaths(const char* path) +Options::FileInfo Options::findFileUsingPaths(const char* path) const { FileInfo result; @@ -803,7 +893,7 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { const char* dir = *it; @@ -826,7 +916,7 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) && (strcmp(&leafName[leafLen-6], ".dylib") == 0) && (strstr(path, ".framework/") != NULL) ); if ( !embeddedDylib ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -842,7 +932,7 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) } -void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath) +void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) { FILE* file = fopen(segAddrPath, "r"); if ( file == NULL ) { @@ -879,7 +969,7 @@ void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath for(char* end = eol-1; (end > p) && isspace(*end); --end) *end = '\0'; // see if this line is for the dylib being linked - if ( strcmp(p, installPath) == 0 ) { + if ( strcmp(p, installPth) == 0 ) { fBaseAddress = firstColumAddress; if ( hasSecondColumn ) { fBaseWritableAddress = secondColumAddress; @@ -939,7 +1029,18 @@ void Options::loadFileList(const char* fileOfPaths) fclose(file); } -bool Options::SetWithWildcards::hasWildCards(const char* symbol) + +void Options::SetWithWildcards::remove(const NameSet& toBeRemoved) +{ + for(NameSet::const_iterator it=toBeRemoved.begin(); it != toBeRemoved.end(); ++it) { + const char* symbolName = *it; + NameSet::iterator pos = fRegular.find(symbolName); + if ( pos != fRegular.end() ) + fRegular.erase(pos); + } +} + +bool Options::SetWithWildcards::hasWildCards(const char* symbol) { // an exported symbol name containing *, ?, or [ requires wildcard matching return ( strpbrk(symbol, "*?[") != NULL ); @@ -953,21 +1054,28 @@ void Options::SetWithWildcards::insert(const char* symbol) fRegular.insert(symbol); } -bool Options::SetWithWildcards::contains(const char* symbol) +bool Options::SetWithWildcards::contains(const char* symbol) const { // first look at hash table on non-wildcard symbols if ( fRegular.find(symbol) != fRegular.end() ) return true; // next walk list of wild card symbols looking for a match - for(std::vector::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { + for(std::vector::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { if ( wildCardMatch(*it, symbol) ) return true; } return false; } +bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const +{ + // look at hash table on non-wildcard symbols + return ( fRegular.find(symbol) != fRegular.end() ); +} + + -bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) +bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const { ++p; // find end const char* b = p; @@ -996,7 +1104,7 @@ bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) return false; } -bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) +bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) const { const char* s = symbol; for (const char* p = pattern; *p != '\0'; ++p) { @@ -1031,6 +1139,8 @@ bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* s void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) { + if ( fileOfExports == NULL ) + throwf("missing file after %s", option); // read in whole file int fd = ::open(fileOfExports, O_RDONLY, 0); if ( fd == -1 ) @@ -1117,7 +1227,7 @@ void Options::parseAliasFile(const char* fileOfAliases) ::close(fd); // parse into symbols and add to fAliases - ObjectFile::ReaderOptions::AliasPair pair; + AliasPair pair; char * const end = &p[stat_buf.st_size+1]; enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; int lineNumber = 1; @@ -1163,7 +1273,7 @@ void Options::parseAliasFile(const char* fileOfAliases) *last = '\0'; --last; } - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); state = inComment; } else if ( *s == '\n' ) { @@ -1174,7 +1284,7 @@ void Options::parseAliasFile(const char* fileOfAliases) *last = '\0'; --last; } - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); state = lineStart; } break; @@ -1228,34 +1338,8 @@ void Options::setMacOSXVersionMin(const char* version) throw "-macosx_version_min argument missing"; if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - int num = version[3] - '0'; - switch ( num ) { - case 0: - case 1: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; - break; - case 7: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_7; - break; - default: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_7; - break; - } + unsigned int minorVersion = version[3] - '0'; + fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); } else { warning("unknown option to -macosx_version_min, not 10.x"); @@ -1265,41 +1349,26 @@ void Options::setMacOSXVersionMin(const char* version) void Options::setIPhoneVersionMin(const char* version) { if ( version == NULL ) - throw "-iphoneos_version_min argument missing"; + throw "-ios_version_min argument missing"; if ( ! isdigit(version[0]) ) - throw "-iphoneos_version_min argument is not a number"; + throw "-ios_version_min argument is not a number"; if ( version[1] != '.' ) - throw "-iphoneos_version_min argument is missing period as second character"; + throw "-ios_version_min argument is missing period as second character"; if ( ! isdigit(version[2]) ) - throw "-iphoneos_version_min argument is not a number"; - - if ( (version[0] == '2') && (version[2] == '0') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - else if ( (version[0] == '2') && (version[2] == '1') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1; - else if ( (version[0] == '2') && (version[2] >= '2') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; - else if ( (version[0] == '3') && (version[2] == '0') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; - else if ( (version[0] == '3') && (version[2] == '1') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; - else if ( (version[0] == '3') && (version[2] >= '2') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_2; - else if ( (version[0] >= '4') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k4_0; - else { - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - warning("unknown option to -iphoneos_version_min, not 2.x, 3.x, or 4.x"); - } + throw "-ios_version_min argument is not a number"; + + unsigned int majorVersion = version[0] - '0'; + unsigned int minorVersion = version[2] - '0'; + fIPhoneVersionMin = (ld::IPhoneVersionMin)((majorVersion << 16) | (minorVersion << 8)); } -bool Options::minOS(ObjectFile::ReaderOptions::MacVersionMin requiredMacMin, ObjectFile::ReaderOptions::IPhoneVersionMin requirediPhoneOSMin) +bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IPhoneVersionMin requirediPhoneOSMin) { - if ( fReaderOptions.fMacVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { - return ( fReaderOptions.fMacVersionMin >= requiredMacMin ); + if ( fMacVersionMin != ld::macVersionUnset ) { + return ( fMacVersionMin >= requiredMacMin ); } else { - return ( fReaderOptions.fIPhoneVersionMin >= requirediPhoneOSMin); + return ( fIPhoneVersionMin >= requirediPhoneOSMin); } } @@ -1488,7 +1557,7 @@ static const char* cstringSymbolName(const char* orderFileString) void Options::parseOrderFile(const char* path, bool cstring) { // order files override auto-ordering - fReaderOptions.fAutoOrderInitializers = false; + fAutoOrderInitializers = false; // read in whole file int fd = ::open(path, O_RDONLY, 0); @@ -1721,6 +1790,8 @@ void Options::parse(int argc, const char* argv[]) fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + if (arg[2] == '\0') + ++i; // previously handled by buildSearchPaths() } // The one gnu style option we have to keep compatibility @@ -1737,8 +1808,8 @@ void Options::parse(int argc, const char* argv[]) // default } else if ( strcmp(arg, "-static") == 0 ) { - fReaderOptions.fForStatic = true; - if ( fOutputKind != kObjectFile ) { + fForStatic = true; + if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) { fOutputKind = kStaticExecutable; } } @@ -1767,6 +1838,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-o") == 0 ) { fOutputFile = argv[++i]; } + else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7], true); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { addLibrary(findLibrary(&arg[2])); } @@ -1777,14 +1854,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fWeakImport = true; addLibrary(info); } - else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { - FileInfo info = findLibrary(&arg[7], true); - info.options.fLazyLoad = true; - addLibrary(info); - fUsingLazyDylibLinking = true; - } // Avoid lazy binding. - // ??? Deprecate. else if ( strcmp(arg, "-bind_at_load") == 0 ) { fBindAtLoad = true; } @@ -1802,14 +1872,14 @@ void Options::parse(int argc, const char* argv[]) } // Similar to --whole-archive. else if ( strcmp(arg, "-all_load") == 0 ) { - fReaderOptions.fFullyLoadArchives = true; + fFullyLoadArchives = true; } else if ( strcmp(arg, "-noall_load") == 0) { warnObsolete(arg); } // Similar to -all_load else if ( strcmp(arg, "-ObjC") == 0 ) { - fReaderOptions.fLoadAllObjcObjectsFromArchives = true; + fLoadAllObjcObjectsFromArchives = true; } // Similar to -all_load, but for the following archive only. else if ( strcmp(arg, "-force_load") == 0 ) { @@ -1981,6 +2051,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-search_paths_first") == 0 ) { // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-search_dylibs_first") == 0 ) { + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); } @@ -2148,6 +2221,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; } + else if ( strcmp(arg, "-allow_heap_execute") == 0 ) { + fDisableNonExecutableHeap = true; + } else if ( strcmp(arg, "-sectalign") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectalign missing

"; @@ -2179,7 +2255,7 @@ 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 ) { + else if ( (strcmp(arg, "-iphoneos_version_min") == 0) || (strcmp(arg, "-ios_version_min") == 0) ) { setIPhoneVersionMin(argv[++i]); } else if ( strcmp(arg, "-multiply_defined") == 0 ) { @@ -2209,7 +2285,7 @@ void Options::parse(int argc, const char* argv[]) warnObsolete(arg); } else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { - fReaderOptions.fWhyLoad = true; + fWhyLoad = true; } else if ( strcmp(arg, "-why_live") == 0 ) { const char* name = argv[++i]; @@ -2232,36 +2308,36 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-s") == 0 ) { warnObsolete(arg); fLocalSymbolHandling = kLocalSymbolsNone; - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + fDebugInfoStripping = Options::kDebugInfoNone; } else if ( strcmp(arg, "-x") == 0 ) { fLocalSymbolHandling = kLocalSymbolsNone; } else if ( strcmp(arg, "-S") == 0 ) { - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + fDebugInfoStripping = Options::kDebugInfoNone; } else if ( strcmp(arg, "-X") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-Si") == 0 ) { warnObsolete(arg); - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + fDebugInfoStripping = Options::kDebugInfoFull; } else if ( strcmp(arg, "-b") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-Sn") == 0 ) { warnObsolete(arg); - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + fDebugInfoStripping = Options::kDebugInfoFull; } else if ( strcmp(arg, "-Sp") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-dead_strip") == 0 ) { - fDeadStrip = kDeadStripOnPlusUnusedInits; + fDeadStrip = true; } else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { - fDeadStrip = kDeadStripOn; + fDeadStrip = true; } else if ( strcmp(arg, "-w") == 0 ) { // previously handled by buildSearchPaths() @@ -2282,10 +2358,10 @@ void Options::parse(int argc, const char* argv[]) fMaxMinimumHeaderPad = true; } else if ( strcmp(arg, "-t") == 0 ) { - fReaderOptions.fLogAllFiles = true; + fLogAllFiles = true; } else if ( strcmp(arg, "-whatsloaded") == 0 ) { - fReaderOptions.fLogObjectFiles = true; + fLogObjectFiles = true; } else if ( strcmp(arg, "-A") == 0 ) { warnObsolete(arg); @@ -2356,7 +2432,7 @@ void Options::parse(int argc, const char* argv[]) fStatistics = true; } else if ( strcmp(arg, "-d") == 0 ) { - fReaderOptions.fMakeTentativeDefinitionsReal = true; + fMakeTentativeDefinitionsReal = true; } else if ( strcmp(arg, "-v") == 0 ) { // previously handled by buildSearchPaths() @@ -2381,20 +2457,20 @@ void Options::parse(int argc, const char* argv[]) fDtraceScriptName = name; } else if ( strcmp(arg, "-root_safe") == 0 ) { - fReaderOptions.fRootSafe = true; + fRootSafe = true; } else if ( strcmp(arg, "-setuid_safe") == 0 ) { - fReaderOptions.fSetuidSafe = true; + fSetuidSafe = true; } else if ( strcmp(arg, "-alias") == 0 ) { - ObjectFile::ReaderOptions::AliasPair pair; + Options::AliasPair pair; pair.realName = argv[++i]; if ( pair.realName == NULL ) throw "missing argument to -alias"; pair.alias = argv[++i]; if ( pair.alias == NULL ) throw "missing argument to -alias"; - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); } else if ( strcmp(arg, "-alias_list") == 0 ) { parseAliasFile(argv[++i]); @@ -2404,12 +2480,12 @@ void Options::parse(int argc, const char* argv[]) const char* colon = strchr(arg, ':'); if ( colon == NULL ) throwf("unknown option: %s", arg); - ObjectFile::ReaderOptions::AliasPair pair; + Options::AliasPair pair; char* temp = new char[colon-arg]; strlcpy(temp, &arg[2], colon-arg-1); pair.realName = &colon[1]; pair.alias = temp; - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; @@ -2433,6 +2509,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-pie") == 0 ) { fPositionIndependentExecutable = true; + fPIEOnCommandLine = true; } else if ( strcmp(arg, "-no_pie") == 0 ) { fDisablePositionIndependentExecutable = true; @@ -2452,11 +2529,26 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; addLibrary(info); } + else if ( strncmp(arg, "-upward-l", 9) == 0 ) { + FileInfo info = findLibrary(&arg[9], true); + info.options.fUpward = true; + addLibrary(info); + } + else if ( strcmp(arg, "-upward_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fUpward = true; + addLibrary(info); + } + else if ( strcmp(arg, "-upward_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fUpward = true; + addLibrary(info); + } else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { fDeadStripDylibs = true; } else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { - fReaderOptions.fImplicitlyLinkPublicDylibs = false; + fImplicitlyLinkPublicDylibs = false; } else if ( strcmp(arg, "-new_linker") == 0 ) { // ignore @@ -2465,7 +2557,7 @@ void Options::parse(int argc, const char* argv[]) fEncryptable = false; } else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { - fReaderOptions.fAddCompactUnwindEncoding = false; + fAddCompactUnwindEncoding = false; } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = argv[++i]; @@ -2474,7 +2566,7 @@ void Options::parse(int argc, const char* argv[]) fLLVMOptions.push_back(opts); } else if ( strcmp(arg, "-no_order_inits") == 0 ) { - fReaderOptions.fAutoOrderInitializers = false; + fAutoOrderInitializers = false; } else if ( strcmp(arg, "-no_order_data") == 0 ) { fOrderData = false; @@ -2498,30 +2590,108 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; } else if ( strcmp(arg, "-no_eh_labels") == 0 ) { - fReaderOptions.fNoEHLabels = true; + fNoEHLabels = true; } else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { - fReaderOptions.fWarnCompactUnwind = true; + fWarnCompactUnwind = true; } else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { fAllowCpuSubtypeMismatches = true; } else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { - fReaderOptions.fOptimizeZeroFill = false; + fOptimizeZeroFill = false; } else if ( strcmp(arg, "-objc_abi_version") == 0 ) { const char* version = argv[++i]; if ( version == NULL ) throw "-objc_abi_version missing version number"; - if ( strcmp(version, "2") == 0 ) - fObjCABIVersion2POverride = true; + if ( strcmp(version, "2") == 0 ) { + fObjCABIVersion1Override = false; + fObjCABIVersion2Override = true; + } + else if ( strcmp(version, "1") == 0 ) { + fObjCABIVersion1Override = true; + fObjCABIVersion2Override = false; + } else warning("ignoring unrecognized argument (%s) to -objc_abi_version", version); } + else if ( strcmp(arg, "-warn_weak_exports") == 0 ) { + fWarnWeakExports = true; + } + else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { + fObjcGcCompaction = true; + } + else if ( strcmp(arg, "-objc_gc") == 0 ) { + fObjCGc = true; + if ( fObjCGcOnly ) { + warning("-objc_gc overriding -objc_gc_only"); + fObjCGcOnly = false; + } + } + else if ( strcmp(arg, "-objc_gc_only") == 0 ) { + fObjCGcOnly = true; + if ( fObjCGc ) { + warning("-objc_gc_only overriding -objc_gc"); + fObjCGc = false; + } + } else if ( strcmp(arg, "-demangle") == 0 ) { - // add -demangle noop to ld64-97 + fDemangle = true; + } + else if ( strcmp(arg, "-version_load_command") == 0 ) { + fVersionLoadCommand = true; + } + else if ( strcmp(arg, "-no_version_load_command") == 0 ) { + fVersionLoadCommand = false; + } + else if ( strcmp(arg, "-function_starts") == 0 ) { + fFunctionStartsLoadCommand = true; + } + else if ( strcmp(arg, "-no_function_starts") == 0 ) { + fFunctionStartsLoadCommand = false; + } + else if ( strcmp(arg, "-object_path_lto") == 0 ) { + fTempLtoObjectPath = argv[++i]; + if ( fTempLtoObjectPath == NULL ) + throw "missing argument to -object_path_lto"; + } + else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) { + fObjcCategoryMerging = false; + } + else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { + loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); + } + else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { + loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); + } + else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { + const char* symbol = argv[++i]; + if ( symbol == NULL ) + throw "-force_symbol_weak missing "; + fForceWeakSymbols.insert(symbol); + } + else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) { + const char* symbol = argv[++i]; + if ( symbol == NULL ) + throw "-force_symbol_not_weak missing "; + fForceNotWeakSymbols.insert(symbol); + } + else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -exported_symbols_list and -reexported_symbols_list"; + loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols); + } + else if ( strcmp(arg, "-dyld_env") == 0 ) { + const char* envarg = argv[++i]; + if ( envarg == NULL ) + throw "-dyld_env missing ENV=VALUE"; + if ( strchr(envarg, '=') == NULL ) + throw "-dyld_env missing ENV=VALUE"; + fDyldEnvironExtras.push_back(envarg); } else { throwf("unknown option: %s", arg); @@ -2564,6 +2734,14 @@ void Options::buildSearchPaths(int argc, const char* argv[]) for(int i=0; i < argc; ++i) { if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) { const char* libSearchDir = &argv[i][2]; + // Allow either "-L{path}" or "-L {path}". + if (argv[i][2] == '\0') { + // -L {path}. Make sure there is an argument following this. + const char* path = argv[++i]; + if ( path == NULL ) + throw "-L missing argument"; + libSearchDir = path; + } if ( libSearchDir[0] == '\0' ) throw "-L must be immediately followed by a directory path (no space)"; struct stat statbuf; @@ -2574,11 +2752,19 @@ void Options::buildSearchPaths(int argc, const char* argv[]) warning("path '%s' following -L not a directory", libSearchDir); } else { - warning("directory '%s' following -L not found", libSearchDir); + warning("directory not found for option '-L%s'", libSearchDir); } } else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) { const char* frameworkSearchDir = &argv[i][2]; + // Allow either "-F{path}" or "-F {path}". + if (argv[i][2] == '\0') { + // -F {path}. Make sure there is an argument following this. + const char* path = argv[++i]; + if ( path == NULL ) + throw "-F missing argument"; + frameworkSearchDir = path; + } if ( frameworkSearchDir[0] == '\0' ) throw "-F must be immediately followed by a directory path (no space)"; struct stat statbuf; @@ -2589,7 +2775,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) warning("path '%s' following -F not a directory", frameworkSearchDir); } else { - warning("directory '%s' following -F not found", frameworkSearchDir); + warning("directory not found for option '-F%s'", frameworkSearchDir); } } else if ( strcmp(argv[i], "-Z") == 0 ) @@ -2600,9 +2786,9 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fprintf(stderr, "%s", ldVersionString); // if only -v specified, exit cleanly if ( argc == 2 ) { -#if LTO_SUPPORT - printLTOVersion(*this); -#endif + const char* ltoVers = lto::version(); + if ( ltoVers != NULL ) + fprintf(stderr, "%s\n", ltoVers); exit(0); } } @@ -2613,9 +2799,11 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fSDKPaths.push_back(path); } else if ( strcmp(argv[i], "-search_paths_first") == 0 ) { - // ??? Deprecate when we get -Bstatic/-Bdynamic. fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; } + else if ( strcmp(argv[i], "-search_dylibs_first") == 0 ) { + fLibrarySearchMode = kSearchAllDirsForDylibsThenAllDirsForArchives; + } else if ( strcmp(argv[i], "-w") == 0 ) { sEmitWarnings = false; } @@ -2736,12 +2924,12 @@ void Options::parsePreCommandLineEnvironmentSettings() { if ((getenv("LD_TRACE_ARCHIVES") != NULL) || (getenv("RC_TRACE_ARCHIVES") != NULL)) - fReaderOptions.fTraceArchives = true; + fTraceArchives = true; if ((getenv("LD_TRACE_DYLIBS") != NULL) || (getenv("RC_TRACE_DYLIBS") != NULL)) { - fReaderOptions.fTraceDylibs = true; - fReaderOptions.fTraceIndirectDylibs = true; + fTraceDylibs = true; + fTraceIndirectDylibs = true; } if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { @@ -2751,8 +2939,8 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_PRINT_OPTIONS") != NULL) fPrintOptions = true; - if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) - fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); + if (fTraceDylibs || fTraceArchives) + fTraceOutputFile = getenv("LD_TRACE_FILE"); if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) fPrintOrderFileStatistics = true; @@ -2769,15 +2957,14 @@ void Options::parsePreCommandLineEnvironmentSettings() // for now disable compressed linkedit functionality if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) { fMakeCompressedDyldInfo = false; - fMakeClassicDyldInfo = true; - } - // temporary until projects adopt -no_pie - if ( getenv("LD_NO_PIE") != NULL ) { - warning("LD_NO_PIE being used to disble building a position independent executable"); - fDisablePositionIndependentExecutable = true; + fMakeCompressedDyldInfoForceOff = true; } sWarningsSideFilePath = getenv("LD_WARN_FILE"); + + const char* customDyldPath = getenv("LD_DYLD_PATH"); + if ( customDyldPath != NULL ) + fDyldInstallPath = customDyldPath; } @@ -2799,13 +2986,13 @@ void Options::parsePostCommandLineEnvironmentSettings() } // allow build system to force on dead-code-stripping - if ( fDeadStrip == kDeadStripOff ) { + if ( !fDeadStrip ) { if ( getenv("LD_DEAD_STRIP") != NULL ) { switch (fOutputKind) { case Options::kDynamicLibrary: case Options::kDynamicExecutable: case Options::kDynamicBundle: - fDeadStrip = kDeadStripOn; + fDeadStrip = true; break; case Options::kPreload: case Options::kObjectFile: @@ -2828,48 +3015,73 @@ void Options::reconfigureDefaults() // sync reader options switch ( fOutputKind ) { case Options::kObjectFile: - fReaderOptions.fForFinalLinkedImage = false; + fForFinalLinkedImage = false; break; case Options::kDyld: - fReaderOptions.fForDyld = true; - fReaderOptions.fForFinalLinkedImage = true; - fReaderOptions.fNoEHLabels = true; + fForDyld = true; + fForFinalLinkedImage = true; + fNoEHLabels = true; break; case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kKextBundle: - fReaderOptions.fForFinalLinkedImage = true; - fReaderOptions.fNoEHLabels = true; + fForFinalLinkedImage = true; + fNoEHLabels = true; break; case Options::kDynamicExecutable: case Options::kStaticExecutable: case Options::kPreload: - fReaderOptions.fLinkingMainExecutable = true; - fReaderOptions.fForFinalLinkedImage = true; - fReaderOptions.fNoEHLabels = true; + fLinkingMainExecutable = true; + fForFinalLinkedImage = true; + fNoEHLabels = true; break; } // set default min OS version - if ( (fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::kMinMacVersionUnset) - && (fReaderOptions.fIPhoneVersionMin == ObjectFile::ReaderOptions::kMinIPhoneVersionUnset) ) { + if ( (fMacVersionMin == ld::macVersionUnset) + && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) { // 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"); if ( macVers != NULL ) setMacOSXVersionMin(macVers); else if ( iPhoneVers != NULL ) setIPhoneVersionMin(iPhoneVers); + else if ( iOSVers != NULL ) + setIPhoneVersionMin(iOSVers); else { // if still nothing, set default based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_POWERPC: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; // FIX FIX, this really should be a check of the OS version the linker is running o + if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } break; case CPU_TYPE_ARM: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + 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); + #elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + break; + default: + // architecture will be infered ;ater by examining .o files break; } } @@ -2879,21 +3091,21 @@ void Options::reconfigureDefaults() // adjust min based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: - if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( (fMacVersionMin < ld::mac10_4) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) { //warning("-macosx_version_min should be 10.4 or later for i386"); - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; + fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_POWERPC64: - if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fMacVersionMin < ld::mac10_4 ) { //warning("-macosx_version_min should be 10.4 or later for ppc64"); - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; + fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_X86_64: - if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fMacVersionMin < ld::mac10_4 ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; + fMacVersionMin = ld::mac10_4; } break; } @@ -2903,8 +3115,8 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_X86_64: // x86_64 uses new MH_KEXT_BUNDLE type - fMakeClassicDyldInfo = true; fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; fAllowTextRelocs = true; fUndefinedTreatment = kUndefinedDynamicLookup; break; @@ -2919,8 +3131,8 @@ void Options::reconfigureDefaults() // disable implicit dylibs when targeting 10.3 // add option to disable implicit load commands for indirectly used public dylibs - if ( !minOS(ObjectFile::ReaderOptions::k10_4, ObjectFile::ReaderOptions::k2_0) ) - fReaderOptions.fImplicitlyLinkPublicDylibs = false; + if ( !minOS(ld::mac10_4, ld::iPhone2_0) ) + fImplicitlyLinkPublicDylibs = false; // allow build system to force linker to ignore -prebind @@ -2979,6 +3191,39 @@ void Options::reconfigureDefaults() } } + // set too-large size + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + fMaxAddress = 0xFFFFFFFF; + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + break; + case CPU_TYPE_ARM: + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + // user land code is limited to low 1GB + fMaxAddress = 0x2FFFFFFF; + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + fMaxAddress = 0xFFFFFFFF; + break; + } + // range check -seg1addr for ARM + if ( fBaseAddress > fMaxAddress ) { + warning("ignoring -seg1addr 0x%08llX. Address out of range.", fBaseAddress); + fBaseAddress = 0; + } + break; + } + // -r implies no prebinding for all architectures if ( fOutputKind == Options::kObjectFile ) fPrebind = false; @@ -2988,12 +3233,12 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_POWERPC: case CPU_TYPE_I386: - if ( fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::k10_4 ) { + if ( fMacVersionMin == ld::mac10_4 ) { // in 10.4 only split seg dylibs are prebound if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) fPrebind = false; } - else if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { + else if ( fMacVersionMin >= ld::mac10_5 ) { // in 10.5 nothing is prebound fPrebind = false; } @@ -3046,7 +3291,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { - if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_1) ) + if ( minOS(ld::mac10_5, ld::iPhone3_1) ) if ( !fPrebind ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) @@ -3058,7 +3303,7 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table - if ( fReaderOptions.fMacVersionMin <= ObjectFile::ReaderOptions::k10_5 ) + if ( fMacVersionMin <= ld::mac10_5 ) fNeedsModuleTable = true; break; case CPU_TYPE_ARM: @@ -3070,7 +3315,7 @@ void Options::reconfigureDefaults() // -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + fDebugInfoStripping = Options::kDebugInfoNone; // choose how to process unwind info switch ( fArchitecture ) { @@ -3081,26 +3326,26 @@ void Options::reconfigureDefaults() case Options::kStaticExecutable: case Options::kPreload: case Options::kKextBundle: - fReaderOptions.fAddCompactUnwindEncoding = false; + fAddCompactUnwindEncoding = false; break; case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDynamicExecutable: - //if ( fReaderOptions.fAddCompactUnwindEncoding && (fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_6) ) - // fReaderOptions.fRemoveDwarfUnwindIfCompactExists = true; + //if ( fAddCompactUnwindEncoding && (fVersionMin >= ld::mac10_6) ) + // fRemoveDwarfUnwindIfCompactExists = true; break; } break; case CPU_TYPE_POWERPC: case CPU_TYPE_POWERPC64: case CPU_TYPE_ARM: - fReaderOptions.fAddCompactUnwindEncoding = false; - fReaderOptions.fRemoveDwarfUnwindIfCompactExists = false; + fAddCompactUnwindEncoding = false; + fRemoveDwarfUnwindIfCompactExists = false; break; case 0: // if -arch is missing, assume we don't want compact unwind info - fReaderOptions.fAddCompactUnwindEncoding = false; + fAddCompactUnwindEncoding = false; break; } @@ -3113,7 +3358,7 @@ void Options::reconfigureDefaults() // don't move inits in dyld because dyld wants certain // entries point at stable locations at the start of __text if ( fOutputKind == Options::kDyld ) - fReaderOptions.fAutoOrderInitializers = false; + fAutoOrderInitializers = false; // disable __data ordering for some output kinds @@ -3131,6 +3376,24 @@ void Options::reconfigureDefaults() break; } + // only use compressed LINKEDIT for final linked images + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kPreload: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kKextBundle: + fMakeCompressedDyldInfoForceOff = true; + break; + } + if ( fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = false; + + // only use compressed LINKEDIT for: // x86_64 and i386 on Mac OS X 10.6 or later // arm on iPhoneOS 3.1 or later @@ -3138,15 +3401,11 @@ void Options::reconfigureDefaults() switch (fArchitecture) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_6 ) - fMakeClassicDyldInfo = false; - else if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_5 ) + if ( fMacVersionMin < ld::mac10_6 ) fMakeCompressedDyldInfo = false; break; case CPU_TYPE_ARM: - if ( fReaderOptions.fIPhoneVersionMin >= ObjectFile::ReaderOptions::k3_1 ) - fMakeClassicDyldInfo = false; - else if ( fReaderOptions.fIPhoneVersionMin < ObjectFile::ReaderOptions::k3_1 ) + if ( !minOS(ld::mac10_6, ld::iPhone3_1) ) fMakeCompressedDyldInfo = false; break; case CPU_TYPE_POWERPC: @@ -3155,25 +3414,7 @@ void Options::reconfigureDefaults() fMakeCompressedDyldInfo = false; } } - - // only use compressed LINKEDIT for final linked images - if ( fMakeCompressedDyldInfo ) { - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - break; - case Options::kPreload: - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kDyld: - case Options::kKextBundle: - fMakeCompressedDyldInfo = false; - break; - } - } - fReaderOptions.fMakeCompressedDyldInfo = fMakeCompressedDyldInfo; // only ARM enforces that cpu-sub-types must match if ( fArchitecture != CPU_TYPE_ARM ) @@ -3181,7 +3422,11 @@ void Options::reconfigureDefaults() // only final linked images can not optimize zero fill sections if ( fOutputKind == Options::kObjectFile ) - fReaderOptions.fOptimizeZeroFill = true; + fOptimizeZeroFill = true; + + // all undefines in -r mode +// if ( fOutputKind == Options::kObjectFile ) +// fUndefinedTreatment = kUndefinedSuppress; // only dynamic final linked images should warn about use of commmons if ( fWarnCommons ) { @@ -3201,14 +3446,95 @@ void Options::reconfigureDefaults() } // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB - if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) + if ( minOS(ld::mac10_5, ld::iPhone2_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) ) + fCanUseUpwardDylib = true; // x86_64 for MacOSX 10.7 defaults to PIE - if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) - && (fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_7) ) { + if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_7) ) { fPositionIndependentExecutable = true; } + + // armv7 for iOS4.3 defaults to PIE + if ( (fArchitecture == CPU_TYPE_ARM) + && (fSubArchitecture == CPU_SUBTYPE_ARM_V7) + && (fOutputKind == kDynamicExecutable) + && (fIPhoneVersionMin >= ld::iPhone4_3) ) { + fPositionIndependentExecutable = true; + } + + // -no_pie anywhere on command line disable PIE + if ( fDisablePositionIndependentExecutable ) + fPositionIndependentExecutable = false; + + // set fOutputSlidable + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + fOutputSlidable = false; + break; + case Options::kDynamicExecutable: + fOutputSlidable = fPositionIndependentExecutable; + break; + case Options::kPreload: + fOutputSlidable = fPIEOnCommandLine; + break; + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kKextBundle: + fOutputSlidable = true; + break; + } + + // let linker know if thread local variables are supported + if ( fMacVersionMin >= ld::mac10_7 ) { + fTLVSupport = true; + } + + // version load command is only in some kinds of output files + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + fVersionLoadCommand = false; + fFunctionStartsLoadCommand = false; + break; + case Options::kDynamicExecutable: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + 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) ) + fCanReExportSymbols = true; + + // ObjC optimization is only in dynamic final linked images + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + case Options::kDyld: + fObjcCategoryMerging = false; + break; + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + } + + // i386 main executables linked on Mac OS X 10.7 default to NX heap + // regardless of target unless overriden with -allow_heap_execute anywhere + // on the command line + if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && !fDisableNonExecutableHeap) + fNonExecutableHeap = true; } void Options::checkIllegalOptionCombinations() @@ -3270,7 +3596,7 @@ void Options::checkIllegalOptionCombinations() // sync reader options if ( fNameSpace != kTwoLevelNameSpace ) - fReaderOptions.fFlatNamespace = true; + fFlatNamespace = true; // check -stack_addr if ( fStackAddr != 0 ) { @@ -3333,6 +3659,8 @@ void Options::checkIllegalOptionCombinations() case Options::kKextBundle: throw "-stack_size option can only be used when linking a main executable"; } + if ( fStackSize > fStackAddr ) + throwf("-stack_size (0x%08llX) must be smaller than -stack_addr (0x%08llX)", fStackSize, fStackAddr); } // check that -allow_stack_execute is only used with main executables @@ -3352,6 +3680,25 @@ void Options::checkIllegalOptionCombinations() } } + // check that -allow_heap_execute is only used with i386 main executables + if ( fDisableNonExecutableHeap ) { + if ( fArchitecture != CPU_TYPE_I386 ) + throw "-allow_heap_execute option can only be used when linking for i386"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + // -allow_heap_execute only legal when building main executable + break; + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + throw "-allow_heap_execute option can only be used when linking a main executable"; + } + } + // check -client_name is only used when making a bundle or main executable if ( fClientName != NULL ) { switch ( fOutputKind ) { @@ -3381,63 +3728,85 @@ void Options::checkIllegalOptionCombinations() throw "-dtrace can only be used when creating final linked images"; // check -d can only be used with -r - if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) + if ( fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) throw "-d can only be used with -r"; // check that -root_safe is not used with -r - if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) ) + if ( fRootSafe && (fOutputKind == Options::kObjectFile) ) throw "-root_safe cannot be used with -r"; // check that -setuid_safe is not used with -r - if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) ) + if ( fSetuidSafe && (fOutputKind == Options::kObjectFile) ) throw "-setuid_safe cannot be used with -r"; + // rdar://problem/4718189 map ObjC class names to new runtime names + bool alterObjC1ClassNamesToObjC2 = false; + switch (fArchitecture) { + case CPU_TYPE_I386: + // i386 only uses new symbols when using objc2 ABI + if ( fObjCABIVersion2Override ) + alterObjC1ClassNamesToObjC2 = true; + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: + alterObjC1ClassNamesToObjC2 = true; + break; + } + // make sure all required exported symbols exist std::vector impliedExports; - for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) { + for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); ++it) { const char* name = *it; - // never export .eh symbols const int len = strlen(name); - if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) + if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) { + // never export .eh symbols warning("ignoring %s in export list", name); - else + } + else if ( (fArchitecture == CPU_TYPE_I386) && !fObjCABIVersion2Override && (strncmp(name, "_OBJC_CLASS_$", 13) == 0) ) { + warning("ignoring Objc2 Class symbol %s in i386 export list", name); + fRemovedExports.insert(name); + } + else if ( alterObjC1ClassNamesToObjC2 && (strncmp(name, ".objc_class_name_", 17) == 0) ) { + // linking ObjC2 ABI, but have ObjC1 ABI name in export list. Change it to intended name + fRemovedExports.insert(name); + char* temp; + asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + } + else { fInitialUndefines.push_back(name); - if ( strncmp(name, ".objc_class_name_", 17) == 0 ) { - // rdar://problem/4718189 map ObjC class names to new runtime names - switch (fArchitecture) { - case CPU_TYPE_I386: - // i386 only uses new symbols when using objc2 ABI - if ( !fObjCABIVersion2POverride ) - break; - // when using objc2 ABI to same as archs below - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - case CPU_TYPE_ARM: - char* temp; - asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); - impliedExports.push_back(temp); - asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); - impliedExports.push_back(temp); - break; - } } } - for (std::vector::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) { + fExportSymbols.remove(fRemovedExports); + for (std::vector::iterator it=impliedExports.begin(); it != impliedExports.end(); ++it) { const char* name = *it; fExportSymbols.insert(name); fInitialUndefines.push_back(name); } + // make sure all required re-exported symbols exist + for (NameSet::iterator it=fReExportSymbols.regularBegin(); it != fReExportSymbols.regularEnd(); ++it) { + fInitialUndefines.push_back(*it); + } + // make sure that -init symbol exist if ( fInitFunctionName != NULL ) fInitialUndefines.push_back(fInitFunctionName); + // make sure every alias base exists + for (std::vector::iterator it=fAliases.begin(); it != fAliases.end(); ++it) { + fInitialUndefines.push_back(it->realName); + } + // check custom segments if ( fCustomSegmentAddresses.size() != 0 ) { // verify no segment is in zero page if ( fZeroPageSize != ULLONG_MAX ) { for (std::vector::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { - if ( (it->address >= 0) && (it->address < fZeroPageSize) ) + if ( it->address < fZeroPageSize ) throwf("-segaddr %s 0x%llX conflicts with -pagezero_size", it->name, it->address); } } @@ -3464,7 +3833,7 @@ void Options::checkIllegalOptionCombinations() break; case CPU_TYPE_POWERPC64: // first 4GB for ppc64 on 10.5 - if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + if ( fMacVersionMin >= ld::mac10_5 ) fZeroPageSize = 0x100000000ULL; else fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page @@ -3495,13 +3864,21 @@ void Options::checkIllegalOptionCombinations() } } + // if main executable with custom base address, model zero page as custom segment + if ( (fOutputKind == Options::kDynamicExecutable) && (fBaseAddress != 0) && (fZeroPageSize != 0) ) { + SegmentStart seg; + seg.name = "__PAGEZERO"; + seg.address = 0;; + fCustomSegmentAddresses.push_back(seg); + } + // -dead_strip and -r are incompatible - if ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) ) + if ( fDeadStrip && (fOutputKind == Options::kObjectFile) ) throw "-r and -dead_strip cannot be used together"; // can't use -rpath unless targeting 10.5 or later if ( fRPaths.size() > 0 ) { - if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) + if ( !minOS(ld::mac10_5, ld::iPhone2_0) ) throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; switch ( fOutputKind ) { case Options::kDynamicExecutable: @@ -3521,15 +3898,19 @@ void Options::checkIllegalOptionCombinations() if ( fPositionIndependentExecutable ) { switch ( fOutputKind ) { case Options::kDynamicExecutable: - // -no_pie anywhere on command line disable PIE - if ( fDisablePositionIndependentExecutable ) - fPositionIndependentExecutable = false; + if ( !minOS(ld::mac10_5, ld::iPhone4_2) ) { + if ( fIPhoneVersionMin == ld::iPhoneVersionUnset ) + 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"; + } break; case Options::kPreload: break; case Options::kDynamicLibrary: case Options::kDynamicBundle: warning("-pie being ignored. It is only used when linking a main executable"); + fPositionIndependentExecutable = false; break; case Options::kStaticExecutable: case Options::kObjectFile: @@ -3537,8 +3918,6 @@ void Options::checkIllegalOptionCombinations() case Options::kKextBundle: throw "-pie can only be used when linking a main executable"; } - if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) - throw "-pie can only be used when targeting Mac OS X 10.5 or later"; } // check -read_only_relocs is not used with x86_64 @@ -3563,6 +3942,18 @@ void Options::checkIllegalOptionCombinations() warning("-force_cpusubtype_ALL will become unsupported for ARM architectures"); } } + + // -reexported_symbols_list can only be used with -dynamiclib + 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) ) + throw "targeted OS version does not support -reexported_symbols_list"; + } + + // -dyld_env can only be used with main executables + if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) ) + throw "-dyld_env can only used used when created main executables"; } @@ -3578,9 +3969,20 @@ void Options::checkForClassic(int argc, const char* argv[]) bool newLinker = false; // build command line buffer in case ld crashes + const char* srcRoot = getenv("SRCROOT"); + if ( srcRoot != NULL ) { + strlcpy(crashreporterBuffer, "SRCROOT=", crashreporterBufferSize); + strlcat(crashreporterBuffer, srcRoot, crashreporterBufferSize); + strlcat(crashreporterBuffer, "\n", crashreporterBufferSize); + } +#ifdef LD_VERS + strlcat(crashreporterBuffer, LD_VERS, crashreporterBufferSize); + strlcat(crashreporterBuffer, "\n", crashreporterBufferSize); +#endif + strlcat(crashreporterBuffer, "ld ", crashreporterBufferSize); for(int i=1; i < argc; ++i) { - strlcat(crashreporterBuffer, argv[i], 1000); - strlcat(crashreporterBuffer, " ", 1000); + strlcat(crashreporterBuffer, argv[i], crashreporterBufferSize); + strlcat(crashreporterBuffer, " ", crashreporterBufferSize); } for(int i=0; i < argc; ++i) { @@ -3618,23 +4020,22 @@ void Options::checkForClassic(int argc, const char* argv[]) } } } - + // -dtrace only supported by new linker if( dtraceFound ) return; if( archFound ) { switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: case CPU_TYPE_I386: - case CPU_TYPE_ARM: -// if ( staticFound && (rFound || !creatingMachKernel) ) { + 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) { + 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"; @@ -3650,6 +4051,11 @@ void Options::checkForClassic(int argc, const char* argv[]) 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); } } @@ -3667,6 +4073,15 @@ void Options::checkForClassic(int argc, const char* argv[]) void Options::gotoClassicLinker(int argc, const char* argv[]) { argv[0] = "ld_classic"; + // in -v mode, print command line passed to ld_classic + for(int i=0; i < argc; ++i) { + if ( strcmp(argv[i], "-v") == 0 ) { + for(int j=0; j < argc; ++j) + printf("%s ", argv[j]); + printf("\n"); + break; + } + } char rawPath[PATH_MAX]; char path[PATH_MAX]; uint32_t bufSize = PATH_MAX; diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 4a06a5e..7ce5766 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -33,7 +33,7 @@ #include #include -#include "ObjectFile.h" +#include "ld.hpp" extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); @@ -41,12 +41,14 @@ extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2) class LibraryOptions { public: - LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false), fForceLoad(false) {} + LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), + fLazyLoad(false), fUpward(false), fForceLoad(false) {} // for dynamic libraries bool fWeakImport; bool fReExport; bool fBundleLoader; bool fLazyLoad; + bool fUpward; // for static libraries bool fForceLoad; }; @@ -76,9 +78,9 @@ class Options enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, kWeakReferenceMismatchNonWeak }; enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; - enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; + enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; struct FileInfo { const char* path; @@ -93,6 +95,8 @@ class Options const char* path; const uint8_t* data; uint64_t dataLen; + typedef ExtraSection* iterator; + typedef const ExtraSection* const_iterator; }; struct SectionAlignment { @@ -105,6 +109,7 @@ class Options const char* symbolName; const char* objectFileName; }; + typedef const OrderedSymbol* OrderedSymbolsIterator; struct SegmentStart { const char* name; @@ -127,112 +132,164 @@ class Options const char* useInstead; }; + struct AliasPair { + const char* realName; + const char* alias; + }; + + typedef const char* const* UndefinesIterator; - const ObjectFile::ReaderOptions& readerOptions(); - const char* getOutputFilePath(); - std::vector& getInputFiles(); +// const ObjectFile::ReaderOptions& readerOptions(); + const char* outputFilePath() const { return fOutputFile; } + const std::vector& getInputFiles() const { return fInputFiles; } - cpu_type_t architecture() { return fArchitecture; } - bool preferSubArchitecture() { return fHasPreferredSubType; } - cpu_subtype_t subArchitecture() { return fSubArchitecture; } - bool allowSubArchitectureMismatches() { return fAllowCpuSubtypeMismatches; } - OutputKind outputKind(); - bool prebind(); - bool bindAtLoad(); - bool fullyLoadArchives(); - NameSpace nameSpace(); - const char* installPath(); // only for kDynamicLibrary - uint32_t currentVersion(); // only for kDynamicLibrary - uint32_t compatibilityVersion(); // only for kDynamicLibrary - const char* entryName(); // only for kDynamicExecutable or kStaticExecutable + cpu_type_t architecture() const { return fArchitecture; } + bool preferSubArchitecture() const { return fHasPreferredSubType; } + cpu_subtype_t subArchitecture() const { return fSubArchitecture; } + bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; } + bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } + const char* architectureName() const { return fArchitectureName; } + void setArchitecture(cpu_type_t, cpu_subtype_t subtype); + OutputKind outputKind() const { return fOutputKind; } + bool prebind() const { return fPrebind; } + bool bindAtLoad() const { return fBindAtLoad; } + NameSpace nameSpace() const { return fNameSpace; } + const char* installPath() const; // only for kDynamicLibrary + uint32_t currentVersion() const { return fDylibCurrentVersion; } // only for kDynamicLibrary + uint32_t compatibilityVersion() const { return fDylibCompatVersion; } // only for kDynamicLibrary + const char* entryName() const { return fEntryName; } // only for kDynamicExecutable or kStaticExecutable const char* executablePath(); - uint64_t baseAddress(); - bool keepPrivateExterns(); // only for kObjectFile - bool needsModuleTable(); // only for kDynamicLibrary - bool interposable(const char* name); - bool hasExportRestrictList(); // -exported_symbol or -unexported_symbol - bool hasExportMaskList(); // just -exported_symbol - bool hasWildCardExportRestrictList(); - bool allGlobalsAreDeadStripRoots(); - bool shouldExport(const char*); - bool ignoreOtherArchInputFiles(); - bool forceCpuSubtypeAll(); - bool traceDylibs(); - bool traceArchives(); - DeadStripMode deadStrip(); - UndefinedTreatment undefinedTreatment(); - ObjectFile::ReaderOptions::MacVersionMin macosxVersionMin() { return fReaderOptions.fMacVersionMin; } - ObjectFile::ReaderOptions::IPhoneVersionMin iphoneOSVersionMin() { return fReaderOptions.fIPhoneVersionMin; } - bool minOS(ObjectFile::ReaderOptions::MacVersionMin mac, ObjectFile::ReaderOptions::IPhoneVersionMin iPhoneOS); + uint64_t baseAddress() const { return fBaseAddress; } + uint64_t maxAddress() const { return fMaxAddress; } + bool keepPrivateExterns() const { return fKeepPrivateExterns; } // only for kObjectFile + bool needsModuleTable() const { return fNeedsModuleTable; } // only for kDynamicLibrary + bool interposable(const char* name) const; + bool hasExportRestrictList() const { return (fExportMode != kExportDefault); } // -exported_symbol or -unexported_symbol + bool hasExportMaskList() const { return (fExportMode == kExportSome); } // just -exported_symbol + bool hasWildCardExportRestrictList() const; + bool hasReExportList() const { return ! fReExportSymbols.empty(); } + bool wasRemovedExport(const char* sym) const { return ( fRemovedExports.find(sym) != fRemovedExports.end() ); } + bool allGlobalsAreDeadStripRoots() const; + bool shouldExport(const char*) const; + bool shouldReExport(const char*) const; + bool ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; } + bool traceDylibs() const { return fTraceDylibs; } + bool traceArchives() const { return fTraceArchives; } + 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); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); - WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); - const char* umbrellaName(); - std::vector& allowableClients(); - const char* clientName(); - const char* initFunctionName(); // only for kDynamicLibrary + WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; } + const char* umbrellaName() const { return fUmbrellaName; } + const std::vector& allowableClients() const { return fAllowableClients; } + const char* clientName() const { return fClientName; } + const char* initFunctionName() const { return fInitFunctionName; } // only for kDynamicLibrary const char* dotOutputFile(); - uint64_t zeroPageSize(); - bool hasCustomStack(); - uint64_t customStackSize(); - uint64_t customStackAddr(); - bool hasExecutableStack(); - std::vector& initialUndefines(); - bool printWhyLive(const char* name); - uint32_t minimumHeaderPad(); - uint64_t segmentAlignment() { return fSegmentAlignment; } - bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; } - std::vector& extraSections(); - std::vector& sectionAlignments(); - CommonsMode commonsMode(); - bool warnCommons(); + uint64_t pageZeroSize() const { return fZeroPageSize; } + bool hasCustomStack() const { return (fStackSize != 0); } + uint64_t customStackSize() const { return fStackSize; } + uint64_t customStackAddr() const { return fStackAddr; } + bool hasExecutableStack() const { return fExecutableStack; } + bool hasNonExecutableHeap() const { return fNonExecutableHeap; } + UndefinesIterator initialUndefinesBegin() const { return &fInitialUndefines[0]; } + UndefinesIterator initialUndefinesEnd() const { return &fInitialUndefines[fInitialUndefines.size()]; } + bool printWhyLive(const char* name) const; + uint32_t minimumHeaderPad() const { return fMinimumHeaderPad; } + bool maxMminimumHeaderPad() const { return fMaxMinimumHeaderPad; } + ExtraSection::const_iterator extraSectionsBegin() const { return &fExtraSections[0]; } + ExtraSection::const_iterator extraSectionsEnd() const { return &fExtraSections[fExtraSections.size()]; } + CommonsMode commonsMode() const { return fCommonsMode; } + bool warnCommons() const { return fWarnCommons; } bool keepRelocations(); - FileInfo findFile(const char* path); - UUIDMode getUUIDMode() { return fUUIDMode; } + FileInfo findFile(const char* path) const; + UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } - bool printStatistics() { return fStatistics; } - bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } + bool printStatistics() const { return fStatistics; } + bool printArchPrefix() const { return fMessagesPrefixedWithArchitecture; } void gotoClassicLinker(int argc, const char* argv[]); - bool sharedRegionEligible() { return fSharedRegionEligible; } - bool printOrderFileStatistics() { return fPrintOrderFileStatistics; } + bool sharedRegionEligible() const { return fSharedRegionEligible; } + bool printOrderFileStatistics() const { return fPrintOrderFileStatistics; } const char* dTraceScriptName() { return fDtraceScriptName; } bool dTrace() { return (fDtraceScriptName != NULL); } - std::vector& orderedSymbols() { return fOrderedSymbols; } - bool splitSeg() { return fSplitSegs; } + unsigned long orderedSymbolsCount() const { return fOrderedSymbols.size(); } + OrderedSymbolsIterator orderedSymbolsBegin() const { return &fOrderedSymbols[0]; } + OrderedSymbolsIterator orderedSymbolsEnd() const { return &fOrderedSymbols[fOrderedSymbols.size()]; } + bool splitSeg() const { return fSplitSegs; } uint64_t baseWritableAddress() { return fBaseWritableAddress; } - std::vector& customSegmentAddresses() { return fCustomSegmentAddresses; } - std::vector& customSegmentSizes() { return fCustomSegmentSizes; } - std::vector& customSegmentProtections() { return fCustomSegmentProtections; } - bool saveTempFiles() { return fSaveTempFiles; } - const std::vector& rpaths() { return fRPaths; } + uint64_t segmentAlignment() const { return fSegmentAlignment; } + uint64_t segPageSize(const char* segName) const; + uint64_t customSegmentAddress(const char* segName) const; + bool hasCustomSegmentAddress(const char* segName) const; + bool hasCustomSectionAlignment(const char* segName, const char* sectName) const; + uint8_t customSectionAlignment(const char* segName, const char* sectName) const; + uint32_t initialSegProtection(const char*) const; + uint32_t maxSegProtection(const char*) const; + bool saveTempFiles() const { return fSaveTempFiles; } + const std::vector& rpaths() const { return fRPaths; } bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; } - std::vector& dylibOverrides() { return fDylibOverrides; } - const char* generatedMapPath() { return fMapPath; } - bool positionIndependentExecutable() { return fPositionIndependentExecutable; } - Options::FileInfo findFileUsingPaths(const char* path); - bool deadStripDylibs() { return fDeadStripDylibs; } - bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } - bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); } + const std::vector& dylibOverrides() const { return fDylibOverrides; } + const char* generatedMapPath() const { return fMapPath; } + bool positionIndependentExecutable() const { return fPositionIndependentExecutable; } + Options::FileInfo findFileUsingPaths(const char* path) const; + bool deadStripDylibs() const { return fDeadStripDylibs; } + bool allowedUndefined(const char* name) const { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } + bool someAllowedUndefines() const { return (fAllowedUndefined.size() != 0); } LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; } - bool keepLocalSymbol(const char* symbolName); - bool allowTextRelocs() { return fAllowTextRelocs; } - bool warnAboutTextRelocs() { return fWarnTextRelocs; } - bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; } - bool verbose() { return fVerbose; } - bool makeEncryptable() { return fEncryptable; } - bool needsUnwindInfoSection() { return fReaderOptions.fAddCompactUnwindEncoding; } - std::vector& llvmOptions() { return fLLVMOptions; } - bool makeClassicDyldInfo() { return fMakeClassicDyldInfo; } - bool makeCompressedDyldInfo() { return fMakeCompressedDyldInfo; } + bool keepLocalSymbol(const char* symbolName) const; + bool allowTextRelocs() const { return fAllowTextRelocs; } + bool warnAboutTextRelocs() const { return fWarnTextRelocs; } + bool usingLazyDylibLinking() const { return fUsingLazyDylibLinking; } + bool verbose() const { return fVerbose; } + bool makeEncryptable() const { return fEncryptable; } + bool needsUnwindInfoSection() const { return fAddCompactUnwindEncoding; } + const std::vector& llvmOptions() const{ return fLLVMOptions; } + const std::vector& dyldEnvironExtras() const{ return fDyldEnvironExtras; } + bool makeCompressedDyldInfo() const { return fMakeCompressedDyldInfo; } bool hasExportedSymbolOrder(); - bool exportedSymbolOrder(const char* sym, unsigned int* order); + bool exportedSymbolOrder(const char* sym, unsigned int* order) const; bool orderData() { return fOrderData; } - bool errorOnOtherArchFiles() { return fErrorOnOtherArchFiles; } - bool markAutoDeadStripDylib() { return fMarkDeadStrippableDylib; } - bool removeEHLabels() { return fReaderOptions.fNoEHLabels; } - bool useSimplifiedDylibReExports() { return fUseSimplifiedDylibReExports; } - bool objCABIVersion2POverride() { return fObjCABIVersion2POverride; } + bool errorOnOtherArchFiles() const { return fErrorOnOtherArchFiles; } + bool markAutoDeadStripDylib() const { return fMarkDeadStrippableDylib; } + bool removeEHLabels() const { return fNoEHLabels; } + bool useSimplifiedDylibReExports() const { return fUseSimplifiedDylibReExports; } + bool objCABIVersion2POverride() const { return fObjCABIVersion2Override; } + bool useUpwardDylibs() const { return fCanUseUpwardDylib; } + bool fullyLoadArchives() const { return fFullyLoadArchives; } + bool loadAllObjcObjectsFromArchives() const { return fLoadAllObjcObjectsFromArchives; } + bool autoOrderInitializers() const { return fAutoOrderInitializers; } + bool optimizeZeroFill() const { return fOptimizeZeroFill; } + bool logAllFiles() const { return fLogAllFiles; } + DebugInfoStripping debugInfoStripping() const { return fDebugInfoStripping; } + bool flatNamespace() const { return fFlatNamespace; } + bool linkingMainExecutable() const { return fLinkingMainExecutable; } + bool implicitlyLinkIndirectPublicDylibs() const { return fImplicitlyLinkPublicDylibs; } + bool whyLoad() const { return fWhyLoad; } + const char* traceOutputFile() const { return fTraceOutputFile; } + bool outputSlidable() const { return fOutputSlidable; } + bool haveCmdLineAliases() const { return (fAliases.size() != 0); } + const std::vector& cmdLineAliases() const { return fAliases; } + bool makeTentativeDefinitionsReal() const { return fMakeTentativeDefinitionsReal; } + const char* dyldInstallPath() const { return fDyldInstallPath; } + bool warnWeakExports() const { return fWarnWeakExports; } + bool objcGcCompaction() const { return fObjcGcCompaction; } + bool objcGc() const { return fObjCGc; } + bool objcGcOnly() const { return fObjCGcOnly; } + bool canUseThreadLocalVariables() const { return fTLVSupport; } + bool demangleSymbols() const { return fDemangle; } + bool addVersionLoadCommand() const { return fVersionLoadCommand; } + bool addFunctionStarts() const { return fFunctionStartsLoadCommand; } + bool canReExportSymbols() const { return fCanReExportSymbols; } + const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } + bool objcCategoryMerging() const { return fObjcCategoryMerging; } + bool hasWeakBitTweaks() const; + bool forceWeak(const char* symbolName) const; + bool forceNotWeak(const char* symbolName) const; + bool forceWeakNonWildCard(const char* symbolName) const; + bool forceNotWeakNonWildcard(const char* symbolName) const; private: class CStringEquals @@ -249,14 +306,17 @@ class Options class SetWithWildcards { public: void insert(const char*); - bool contains(const char*); - bool hasWildCards() { return !fWildCard.empty(); } - NameSet::iterator regularBegin() { return fRegular.begin(); } - NameSet::iterator regularEnd() { return fRegular.end(); } + bool contains(const char*) const; + bool containsNonWildcard(const char*) const; + bool empty() const { return fRegular.empty() && fWildCard.empty(); } + bool hasWildCards() const { return !fWildCard.empty(); } + NameSet::iterator regularBegin() const { return fRegular.begin(); } + NameSet::iterator regularEnd() const { return fRegular.end(); } + void remove(const NameSet&); private: static bool hasWildCards(const char*); - bool wildCardMatch(const char* pattern, const char* candidate); - bool inCharRange(const char*& range, unsigned char c); + bool wildCardMatch(const char* pattern, const char* candidate) const; + bool inCharRange(const char*& range, unsigned char c) const; NameSet fRegular; std::vector fWildCard; @@ -271,7 +331,7 @@ class Options FileInfo findFramework(const char* frameworkName); FileInfo findFramework(const char* rootName, const char* suffix); bool checkForFile(const char* format, const char* dir, const char* rootName, - FileInfo& result); + FileInfo& result) const; uint32_t parseVersionNumber(const char*); void parseSectionOrderFile(const char* segment, const char* section, const char* path); void parseOrderFile(const char* path, bool cstring); @@ -300,11 +360,13 @@ class Options void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping); - ObjectFile::ReaderOptions fReaderOptions; + +// ObjectFile::ReaderOptions fReaderOptions; const char* fOutputFile; std::vector fInputFiles; cpu_type_t fArchitecture; cpu_subtype_t fSubArchitecture; + const char* fArchitectureName; OutputKind fOutputKind; bool fHasPreferredSubType; bool fPrebind; @@ -315,7 +377,7 @@ class Options bool fErrorOnOtherArchFiles; bool fForceSubtypeAll; InterposeMode fInterposeMode; - DeadStripMode fDeadStrip; + bool fDeadStrip; NameSpace fNameSpace; uint32_t fDylibCompatVersion; uint32_t fDylibCurrentVersion; @@ -323,11 +385,16 @@ class Options const char* fFinalName; const char* fEntryName; uint64_t fBaseAddress; + uint64_t fMaxAddress; uint64_t fBaseWritableAddress; bool fSplitSegs; SetWithWildcards fExportSymbols; SetWithWildcards fDontExportSymbols; SetWithWildcards fInterposeList; + SetWithWildcards fForceWeakSymbols; + SetWithWildcards fForceNotWeakSymbols; + SetWithWildcards fReExportSymbols; + NameSet fRemovedExports; NameToOrder fExportSymbolsOrder; ExportMode fExportMode; LibrarySearchMode fLibrarySearchMode; @@ -347,14 +414,18 @@ class Options const char* fDtraceScriptName; const char* fSegAddrTablePath; const char* fMapPath; + const char* fDyldInstallPath; + const char* fTempLtoObjectPath; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; bool fExecutableStack; + bool fNonExecutableHeap; + bool fDisableNonExecutableHeap; uint32_t fMinimumHeaderPad; uint64_t fSegmentAlignment; CommonsMode fCommonsMode; - UUIDMode fUUIDMode; + enum UUIDMode fUUIDMode; SetWithWildcards fLocalSymbolsIncluded; SetWithWildcards fLocalSymbolsExcluded; LocalSymbolHandling fLocalSymbolHandling; @@ -370,6 +441,7 @@ class Options bool fPrintOrderFileStatistics; bool fReadOnlyx86Stubs; bool fPositionIndependentExecutable; + bool fPIEOnCommandLine; bool fDisablePositionIndependentExecutable; bool fMaxMinimumHeaderPad; bool fDeadStripDylibs; @@ -379,12 +451,52 @@ class Options bool fEncryptable; bool fOrderData; bool fMarkDeadStrippableDylib; - bool fMakeClassicDyldInfo; bool fMakeCompressedDyldInfo; + bool fMakeCompressedDyldInfoForceOff; bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; bool fUseSimplifiedDylibReExports; - bool fObjCABIVersion2POverride; + bool fObjCABIVersion2Override; + bool fObjCABIVersion1Override; + bool fCanUseUpwardDylib; + bool fFullyLoadArchives; + bool fLoadAllObjcObjectsFromArchives; + bool fFlatNamespace; + bool fLinkingMainExecutable; + bool fForFinalLinkedImage; + bool fForStatic; + bool fForDyld; + bool fMakeTentativeDefinitionsReal; + bool fWhyLoad; + bool fRootSafe; + bool fSetuidSafe; + bool fImplicitlyLinkPublicDylibs; + bool fAddCompactUnwindEncoding; + bool fWarnCompactUnwind; + bool fRemoveDwarfUnwindIfCompactExists; + bool fAutoOrderInitializers; + bool fOptimizeZeroFill; + bool fLogObjectFiles; + bool fLogAllFiles; + bool fTraceDylibs; + bool fTraceIndirectDylibs; + bool fTraceArchives; + bool fOutputSlidable; + bool fWarnWeakExports; + bool fObjcGcCompaction; + bool fObjCGc; + bool fObjCGcOnly; + bool fDemangle; + bool fTLVSupport; + bool fVersionLoadCommand; + bool fFunctionStartsLoadCommand; + bool fCanReExportSymbols; + bool fObjcCategoryMerging; + DebugInfoStripping fDebugInfoStripping; + const char* fTraceOutputFile; + ld::MacVersionMin fMacVersionMin; + ld::IPhoneVersionMin fIPhoneVersionMin; + std::vector fAliases; std::vector fInitialUndefines; NameSet fAllowedUndefined; NameSet fWhyLive; @@ -399,6 +511,7 @@ class Options std::vector fLibrarySearchPaths; std::vector fFrameworkSearchPaths; std::vector fSDKPaths; + std::vector fDyldEnvironExtras; bool fSaveTempFiles; }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp new file mode 100644 index 0000000..bb368c2 --- /dev/null +++ b/ld64/src/ld/OutputFile.cpp @@ -0,0 +1,3503 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-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@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "MachOTrie.hpp" + +#include "Options.h" + +#include "OutputFile.h" +#include "Architectures.hpp" +#include "HeaderAndLoadCommands.hpp" +#include "LinkEdit.hpp" +#include "LinkEditClassic.hpp" + + +namespace ld { +namespace tool { + + +OutputFile::OutputFile(const Options& opts) + : + hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), + _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), + headerAndLoadCommandsSection(NULL), + rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), + lazyBindingSection(NULL), exportSection(NULL), + splitSegInfoSection(NULL), functionStartsSection(NULL), + symbolTableSection(NULL), stringPoolSection(NULL), + localRelocationsSection(NULL), externalRelocationsSection(NULL), + sectionRelocationsSection(NULL), + indirectSymbolTableSection(NULL), + _options(opts), + _hasDyldInfo(opts.makeCompressedDyldInfo()), + _hasSymbolTable(true), + _hasSectionRelocations(opts.outputKind() == Options::kObjectFile), + _hasSplitSegInfo(opts.sharedRegionEligible()), + _hasFunctionStartsInfo(opts.addFunctionStarts()), + _hasDynamicSymbolTable(true), + _hasLocalRelocations(!opts.makeCompressedDyldInfo()), + _hasExternalRelocations(!opts.makeCompressedDyldInfo()), + _encryptedTEXTstartOffset(0), + _encryptedTEXTendOffset(0), + _localSymbolsStartIndex(0), + _localSymbolsCount(0), + _globalSymbolsStartIndex(0), + _globalSymbolsCount(0), + _importSymbolsStartIndex(0), + _importSymbolsCount(0), + _sectionsRelocationsAtom(NULL), + _localRelocsAtom(NULL), + _externalRelocsAtom(NULL), + _symbolTableAtom(NULL), + _indirectSymbolTableAtom(NULL), + _rebasingInfoAtom(NULL), + _bindingInfoAtom(NULL), + _lazyBindingInfoAtom(NULL), + _weakBindingInfoAtom(NULL), + _exportInfoAtom(NULL), + _splitSegInfoAtom(NULL), + _functionStartsAtom(NULL) +{ +} + +void OutputFile::dumpAtomsBySection(ld::Internal& state, bool printAtoms) +{ + fprintf(stderr, "SORTED:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + fprintf(stderr, "final section %p %s/%s %s start addr=0x%08llX, size=0x%08llX, alignment=%02d, fileOffset=0x%08llX\n", + (*it), (*it)->segmentName(), (*it)->sectionName(), (*it)->isSectionHidden() ? "(hidden)" : "", + (*it)->address, (*it)->size, (*it)->alignment, (*it)->fileOffset); + if ( printAtoms ) { + std::vector& atoms = (*it)->atoms; + for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { + fprintf(stderr, " %p (0x%04llX) %s\n", *ait, (*ait)->size(), (*ait)->name()); + } + } + } + fprintf(stderr, "DYLIBS:\n"); + for (std::vector::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it ) + fprintf(stderr, " %s\n", (*it)->installPath()); +} + +void OutputFile::write(ld::Internal& state) +{ + this->buildDylibOrdinalMapping(state); + this->addLoadCommands(state); + this->addLinkEdit(state); + this->setSectionSizesAndAlignments(state); + this->setLoadCommandsPadding(state); + this->assignFileOffsets(state); + this->assignAtomAddresses(state); + this->synthesizeDebugNotes(state); + this->buildSymbolTable(state); + this->generateLinkEditInfo(state); + this->makeSplitSegInfo(state); + this->updateLINKEDITAddresses(state); + //this->dumpAtomsBySection(state, false); + this->writeOutputFile(state); + this->writeMapFile(state); +} + +bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start, uint64_t* end, uint32_t* index) +{ + uint32_t segIndex = 0; + ld::Internal::FinalSection* segFirstSection = NULL; + ld::Internal::FinalSection* lastSection = NULL; + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( (segFirstSection == NULL ) || strcmp(segFirstSection->segmentName(), sect->segmentName()) != 0 ) { + if ( segFirstSection != NULL ) { + //fprintf(stderr, "findSegment(0x%llX) seg changed to %s\n", addr, sect->segmentName()); + if ( (addr >= segFirstSection->address) && (addr < lastSection->address+lastSection->size) ) { + *start = segFirstSection->address; + *end = lastSection->address+lastSection->size; + *index = segIndex; + return true; + } + ++segIndex; + } + segFirstSection = sect; + } + lastSection = sect; + } + return false; +} + + +void OutputFile::assignAtomAddresses(ld::Internal& state) +{ + 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; + switch ( sect-> type() ) { + case ld::Section::typeImportProxies: + // want finalAddress() of all proxy atoms to be zero + (const_cast(atom))->setSectionStartAddress(0); + break; + case ld::Section::typeAbsoluteSymbols: + // want finalAddress() of all absolute atoms to be value of abs symbol + (const_cast(atom))->setSectionStartAddress(0); + break; + case ld::Section::typeLinkEdit: + // linkedit layout is assigned later + break; + default: + (const_cast(atom))->setSectionStartAddress(sect->address); + break; + } + } + } +} + +void OutputFile::updateLINKEDITAddresses(ld::Internal& state) +{ + if ( _options.makeCompressedDyldInfo() ) { + // build dylb rebasing info + assert(_rebasingInfoAtom != NULL); + _rebasingInfoAtom->encode(); + + // build dyld binding info + assert(_bindingInfoAtom != NULL); + _bindingInfoAtom->encode(); + + // build dyld lazy binding info + assert(_lazyBindingInfoAtom != NULL); + _lazyBindingInfoAtom->encode(); + + // build dyld weak binding info + assert(_weakBindingInfoAtom != NULL); + _weakBindingInfoAtom->encode(); + + // build dyld export info + assert(_exportInfoAtom != NULL); + _exportInfoAtom->encode(); + } + + if ( _options.sharedRegionEligible() ) { + // build split seg info + assert(_splitSegInfoAtom != NULL); + _splitSegInfoAtom->encode(); + } + + if ( _options.addFunctionStarts() ) { + // build function starts info + assert(_functionStartsAtom != NULL); + _functionStartsAtom->encode(); + } + + // build classic symbol table + assert(_symbolTableAtom != NULL); + _symbolTableAtom->encode(); + assert(_indirectSymbolTableAtom != NULL); + _indirectSymbolTableAtom->encode(); + + // add relocations to .o files + if ( _options.outputKind() == Options::kObjectFile ) { + assert(_sectionsRelocationsAtom != NULL); + _sectionsRelocationsAtom->encode(); + } + + if ( ! _options.makeCompressedDyldInfo() ) { + // build external relocations + assert(_externalRelocsAtom != NULL); + _externalRelocsAtom->encode(); + // build local relocations + assert(_localRelocsAtom != NULL); + _localRelocsAtom->encode(); + } + + // update address and file offsets now that linkedit content has been generated + uint64_t curLinkEditAddress = 0; + uint64_t curLinkEditfileOffset = 0; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeLinkEdit ) + continue; + if ( curLinkEditAddress == 0 ) { + curLinkEditAddress = sect->address; + curLinkEditfileOffset = sect->fileOffset; + } + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + //fprintf(stderr, "setting linkedit atom offset for %s\n", atom->name()); + if ( atom->alignment().powerOf2 > maxAlignment ) + maxAlignment = atom->alignment().powerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->alignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + (const_cast(atom))->setSectionOffset(offset); + (const_cast(atom))->setSectionStartAddress(curLinkEditAddress); + offset += atom->size(); + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + sect->address = curLinkEditAddress; + sect->fileOffset = curLinkEditfileOffset; + curLinkEditAddress += sect->size; + curLinkEditfileOffset += sect->size; + } + + _fileSize = state.sections.back()->fileOffset + state.sections.back()->size; +} + +void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) +{ + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { + // absolute symbols need their finalAddress() to their value + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + (const_cast(atom))->setSectionOffset(atom->objectAddress()); + } + } + else { + uint16_t maxAlignment = 0; + 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; + // calculate section offset for this atom + uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->alignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + // LINKEDIT atoms are laid out later + if ( sect->type() != ld::Section::typeLinkEdit ) { + (const_cast(atom))->setSectionOffset(offset); + offset += atom->size(); + } + if ( (atom->scope() == ld::Atom::scopeGlobal) + && (atom->definition() == ld::Atom::definitionRegular) + && (atom->combine() == ld::Atom::combineByName) + && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { + this->hasWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("weak external symbol: %s", atom->name()); + } + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + // unless -sectalign command line option overrides + if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) + sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); + // each atom in __eh_frame has zero alignment to assure they pack together, + // but compilers usually make the CFIs pointer sized, so we want whole section + // to start on pointer sized boundary. + if ( sect->type() == ld::Section::typeCFI ) + sect->alignment = 3; + if ( sect->type() == ld::Section::typeTLVDefs ) + this->hasThreadLocalVariableDefinitions = true; + } + } +} + +void OutputFile::setLoadCommandsPadding(ld::Internal& state) +{ + // In other sections, any extra space is put and end of segment. + // In __TEXT segment, any extra space is put after load commands to allow post-processing of load commands + // Do a reverse layout of __TEXT segment to determine padding size and adjust section size + uint64_t paddingSize = 0; + switch ( _options.outputKind() ) { + case Options::kDyld: + // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address + assert(strcmp(state.sections[1]->sectionName(),"__text") == 0); + state.sections[1]->alignment = 12; // page align __text + break; + case Options::kObjectFile: + // mach-o .o files need no padding between load commands and first section + // but leave enough room that the object file could be signed + paddingSize = 32; + break; + case Options::kPreload: + // mach-o MH_PRELOAD files need no padding between load commands and first section + paddingSize = 0; + default: + // work backwards from end of segment and lay out sections so that extra room goes to padding atom + uint64_t addr = 0; + for (std::vector::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( strcmp(sect->segmentName(), "__TEXT") != 0 ) + continue; + if ( sect == headerAndLoadCommandsSection ) { + addr -= headerAndLoadCommandsSection->size; + paddingSize = addr % _options.segmentAlignment(); + break; + } + addr -= sect->size; + addr = addr & (0 - (1 << sect->alignment)); + } + + // if command line requires more padding than this + uint32_t minPad = _options.minimumHeaderPad(); + if ( _options.maxMminimumHeaderPad() ) { + // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes + uint32_t altMin = _dylibsToLoad.size() * MAXPATHLEN; + if ( _options.outputKind() == Options::kDynamicLibrary ) + altMin += MAXPATHLEN; + if ( altMin > minPad ) + minPad = altMin; + } + if ( paddingSize < minPad ) { + int extraPages = (minPad - paddingSize + _options.segmentAlignment() - 1)/_options.segmentAlignment(); + paddingSize += extraPages * _options.segmentAlignment(); + } + + if ( _options.makeEncryptable() ) { + // load commands must be on a separate non-encrypted page + int loadCommandsPage = (headerAndLoadCommandsSection->size + minPad)/_options.segmentAlignment(); + int textPage = (headerAndLoadCommandsSection->size + paddingSize)/_options.segmentAlignment(); + if ( loadCommandsPage == textPage ) { + paddingSize += _options.segmentAlignment(); + textPage += 1; + } + // remember start for later use by load command + _encryptedTEXTstartOffset = textPage*_options.segmentAlignment(); + } + break; + } + // add padding to size of section + headerAndLoadCommandsSection->size += paddingSize; +} + + +uint64_t OutputFile::pageAlign(uint64_t addr) +{ + const uint64_t alignment = _options.segmentAlignment(); + return ((addr+alignment-1) & (-alignment)); +} + +uint64_t OutputFile::pageAlign(uint64_t addr, uint64_t pageSize) +{ + return ((addr+pageSize-1) & (-pageSize)); +} + + +void OutputFile::assignFileOffsets(ld::Internal& state) +{ + const bool log = false; + const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kPreload)); + const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); + + uint64_t address = 0; + const char* lastSegName = ""; + uint64_t floatingAddressStart = _options.baseAddress(); + + // first pass, assign addresses to sections in segments with fixed start addresses + if ( log ) fprintf(stderr, "Fixed address segments:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + address = _options.customSegmentAddress(sect->segmentName()); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + + // if TEXT segment address is fixed, then flow other segments after it + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + floatingAddressStart = address; + } + } + + // second pass, assign section address to sections in segments that are contiguous with previous segment + address = floatingAddressStart; + lastSegName = ""; + if ( log ) fprintf(stderr, "Regular layout segments:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { + sect->alignmentPaddingBytes = 0; + continue; + } + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + // round up size of last segment if needed + if ( *lastSegName != '\0' ) { + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // set segment address based on end of last segment + address = pageAlign(address); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + + + // third pass, assign section file offsets + uint64_t fileOffset = 0; + lastSegName = ""; + if ( log ) fprintf(stderr, "All segments with file offsets:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( hasZeroForFileOffset(sect) ) { + // fileoff of zerofill sections is moot, but historically it is set to zero + sect->fileOffset = 0; + } + else { + // page align file offset at start of each segment + if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { + fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); + } + lastSegName = sect->segmentName(); + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + + // update section info + sect->fileOffset = fileOffset; + + // update running total + fileOffset += sect->size; + } + + if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", + sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, + sect->segmentName(), sect->sectionName()); + } + + + // for encrypted iPhoneOS apps + if ( _options.makeEncryptable() ) { + // remember end of __TEXT for later use by load command + 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); + } + } + } + + // remember total file size + _fileSize = fileOffset; +} + + +static const char* makeName(const ld::Atom& atom) +{ + static char buffer[4096]; + switch ( atom.symbolTableInclusion() ) { + case ld::Atom::symbolTableNotIn: + case ld::Atom::symbolTableNotInFinalLinkedImages: + sprintf(buffer, "%s@0x%08llX", atom.name(), atom.objectAddress()); + break; + case ld::Atom::symbolTableIn: + case ld::Atom::symbolTableInAndNeverStrip: + case ld::Atom::symbolTableInAsAbsolute: + case ld::Atom::symbolTableInWithRandomAutoStripLabel: + strlcpy(buffer, atom.name(), 4096); + break; + } + return buffer; +} + +static const char* referenceTargetAtomName(ld::Internal& state, const ld::Fixup* ref) +{ + switch ( ref->binding ) { + case ld::Fixup::bindingNone: + return "NO BINDING"; + case ld::Fixup::bindingByNameUnbound: + return (char*)(ref->u.target); + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + return makeName(*((ld::Atom*)(ref->u.target))); + case ld::Fixup::bindingsIndirectlyBound: + return makeName(*state.indirectBindingTable[ref->u.bindingIndex]); + } + return "BAD BINDING"; +} + +bool OutputFile::targetIsThumb(ld::Internal& state, const ld::Fixup* fixup) +{ + switch ( fixup->binding ) { + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + return fixup->u.target->isThumb(); + case ld::Fixup::bindingsIndirectlyBound: + return state.indirectBindingTable[fixup->u.bindingIndex]->isThumb(); + default: + break; + } + throw "unexpected binding"; +} + +uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target) +{ + if ( !_options.makeCompressedDyldInfo() ) { + // For external relocations the classic mach-o format + // has addend only stored in the content. That means + // that the address of the target is not used. + if ( fixup->contentAddendOnly ) + return 0; + } + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + *target = fixup->u.target; + return (*target)->finalAddress(); + case ld::Fixup::bindingsIndirectlyBound: + *target = state.indirectBindingTable[fixup->u.bindingIndex]; + return (*target)->finalAddress(); + } + throw "unexpected binding"; +} + +uint64_t OutputFile::sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup) +{ + const ld::Atom* target = NULL; + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fixup->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fixup->u.bindingIndex]; + break; + } + assert(target != NULL); + + uint64_t targetAddress = target->finalAddress(); + for (std::vector::const_iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + const ld::Internal::FinalSection* sect = *it; + if ( (sect->address <= targetAddress) && (targetAddress < (sect->address+sect->size)) ) + return targetAddress - sect->address; + } + throw "section not found for section offset"; +} + + + +uint64_t OutputFile::tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup) +{ + const ld::Atom* target = NULL; + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fixup->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fixup->u.bindingIndex]; + break; + } + assert(target != NULL); + + for (std::vector::const_iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + const ld::Internal::FinalSection* sect = *it; + switch ( sect->type() ) { + case ld::Section::typeTLVInitialValues: + case ld::Section::typeTLVZeroFill: + return target->finalAddress() - sect->address; + default: + break; + } + } + throw "section not found for tlvTemplateOffsetOf"; +} + +void OutputFile::printSectionLayout(ld::Internal& state) +{ + // show layout of final image + fprintf(stderr, "final section layout:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + if ( (*it)->isSectionHidden() ) + continue; + fprintf(stderr, " %s/%s addr=0x%08llX, size=0x%08llX, fileOffset=0x%08llX, type=%d\n", + (*it)->segmentName(), (*it)->sectionName(), + (*it)->address, (*it)->size, (*it)->fileOffset, (*it)->type()); + } +} + + +void OutputFile::rangeCheck8(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( (displacement > 127) || (displacement < -128) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("8-bit reference out of range (%lld max is +/-127B): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheck16(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t thirtyTwoKLimit = 0x00007FFF; + if ( (displacement > thirtyTwoKLimit) || (displacement < (-thirtyTwoKLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("16-bit reference out of range (%lld max is +/-32KB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckBranch32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("32-bit branch out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), 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)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("32-bit RIP relative reference out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckARM12(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( (displacement > 4092LL) || (displacement < (-4092LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("ARM ldr 12-bit displacement out of range (%lld max is +/-4096B): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + + +void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +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) ) { + if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } + } + else { + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } + } +} + +void OutputFile::rangeCheckPPCBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t bl_eightMegLimit = 0x00FFFFFF; + if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("bl PPC branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckPPCBranch14(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; + if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("bcc PPC branch out of range (%lld max is +/-64KB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + + + + +uint16_t OutputFile::get16LE(uint8_t* loc) { return LittleEndian::get16(*(uint16_t*)loc); } +void OutputFile::set16LE(uint8_t* loc, uint16_t value) { LittleEndian::set16(*(uint16_t*)loc, value); } + +uint32_t OutputFile::get32LE(uint8_t* loc) { return LittleEndian::get32(*(uint32_t*)loc); } +void OutputFile::set32LE(uint8_t* loc, uint32_t value) { LittleEndian::set32(*(uint32_t*)loc, value); } + +uint64_t OutputFile::get64LE(uint8_t* loc) { return LittleEndian::get64(*(uint64_t*)loc); } +void OutputFile::set64LE(uint8_t* loc, uint64_t value) { LittleEndian::set64(*(uint64_t*)loc, value); } + +uint16_t OutputFile::get16BE(uint8_t* loc) { return BigEndian::get16(*(uint16_t*)loc); } +void OutputFile::set16BE(uint8_t* loc, uint16_t value) { BigEndian::set16(*(uint16_t*)loc, value); } + +uint32_t OutputFile::get32BE(uint8_t* loc) { return BigEndian::get32(*(uint32_t*)loc); } +void OutputFile::set32BE(uint8_t* loc, uint32_t value) { BigEndian::set32(*(uint32_t*)loc, value); } + +uint64_t OutputFile::get64BE(uint8_t* loc) { return BigEndian::get64(*(uint64_t*)loc); } +void OutputFile::set64BE(uint8_t* loc, uint64_t value) { BigEndian::set64(*(uint64_t*)loc, value); } + +void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer) +{ + //fprintf(stderr, "applyFixUps() on %s\n", atom->name()); + int64_t accumulator = 0; + const ld::Atom* toTarget = NULL; + const ld::Atom* fromTarget; + int64_t delta; + uint32_t instruction; + uint32_t newInstruction; + uint16_t instructionLowHalf; + bool is_bl; + bool is_blx; + bool is_b; + bool thumbTarget = false; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + uint8_t* fixUpLocation = &buffer[fit->offsetInAtom]; + switch ( (ld::Fixup::Kind)(fit->kind) ) { + case ld::Fixup::kindNone: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: + break; + case ld::Fixup::kindSetTargetAddress: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentAddendOnly || fit->contentDetlaToAddendOnly ) + accumulator = 0; + break; + case ld::Fixup::kindSubtractTargetAddress: + delta = addressOf(state, fit, &fromTarget); + if ( ! fit->contentAddendOnly ) + accumulator -= delta; + break; + case ld::Fixup::kindAddAddend: + // ARM main executables main contain .long constants pointing + // into themselves such as jump tables. These .long should not have thumb bit set + // even though the target is a thumb instruction. We can tell it is an interior pointer + // because we are processing an addend. + if ( thumbTarget && (toTarget == atom) && ((int32_t)fit->u.addend > 0) ) { + accumulator &= (-2); + //warning("removing thumb bit from intra-atom pointer in %s %s+0x%0X", + // atom->section().sectionName(), atom->name(), fit->offsetInAtom); + } + accumulator += fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + accumulator -= fit->u.addend; + break; + case ld::Fixup::kindSetTargetImageOffset: + accumulator = addressOf(state, fit, &toTarget) - mhAddress; + break; + case ld::Fixup::kindSetTargetSectionOffset: + accumulator = sectionOffsetOf(state, fit); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffset: + accumulator = tlvTemplateOffsetOf(state, fit); + break; + case ld::Fixup::kindStore8: + *fixUpLocation += accumulator; + break; + case ld::Fixup::kindStoreLittleEndian16: + set16LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreLittleEndianLow24of32: + set32LE(fixUpLocation, (get32LE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); + break; + case ld::Fixup::kindStoreLittleEndian32: + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreLittleEndian64: + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreBigEndian16: + set16BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreBigEndianLow24of32: + set32BE(fixUpLocation, (get32BE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); + break; + case ld::Fixup::kindStoreBigEndian32: + set32BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreBigEndian64: + set64BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreX86PCRel8: + case ld::Fixup::kindStoreX86BranchPCRel8: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 1); + rangeCheck8(delta, state, atom, fit); + *fixUpLocation = delta; + break; + case ld::Fixup::kindStoreX86PCRel16: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 2); + rangeCheck16(delta, state, atom, fit); + set16LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86BranchPCRel32: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckBranch32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32_1: + if ( fit->contentAddendOnly ) + delta = accumulator - 1; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 5); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32_2: + if ( fit->contentAddendOnly ) + delta = accumulator - 2; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 6); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32_4: + if ( fit->contentAddendOnly ) + delta = accumulator - 4; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA: + assert(_options.outputKind() != Options::kObjectFile); + // TLV entry was optimized away, change movl instruction to a leal + if ( fixUpLocation[-1] != 0xA1 ) + throw "TLV load reloc does not point to a movl instruction"; + fixUpLocation[-1] = 0xB8; + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + assert(_options.outputKind() != Options::kObjectFile); + // GOT entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "GOT load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + assert(_options.outputKind() != Options::kObjectFile); + // TLV entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "TLV load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressARMLoad12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARMLoad12 case + case ld::Fixup::kindStoreARMLoad12: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); + rangeCheckARM12(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + if ( delta >= 0 ) { + newInstruction = instruction & 0xFFFFF000; + newInstruction |= ((uint32_t)delta & 0xFFF); + } + else { + newInstruction = instruction & 0xFF7FF000; + newInstruction |= ((uint32_t)(-delta) & 0xFFF); + } + set32LE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCBranch14: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckPPCBranch14(delta, state, atom, fit); + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)delta & 0x0000FFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCAbsLow14: + instruction = get32BE(fixUpLocation); + if ( (accumulator & 0x3) != 0 ) + throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)accumulator); + newInstruction = (instruction & 0xFFFF0003) | (accumulator & 0xFFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsLow16: + case ld::Fixup::kindStorePPCPicLow16: + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | (accumulator & 0xFFFF); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + instructionLowHalf = (accumulator >> 16) & 0xFFFF; + if ( accumulator & 0x00008000 ) + ++instructionLowHalf; + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsHigh16: + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | ((accumulator >> 16) & 0xFFFF); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindDtraceExtra: + break; + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + fixUpLocation[-1] = 0x90; // 1-byte nop + fixUpLocation[0] = 0x0F; // 4-byte nop + fixUpLocation[1] = 0x1F; + fixUpLocation[2] = 0x40; + fixUpLocation[3] = 0x00; + } + break; + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a clear eax + fixUpLocation[-1] = 0x33; // xorl eax,eax + fixUpLocation[0] = 0xC0; + fixUpLocation[1] = 0x90; // 1-byte nop + fixUpLocation[2] = 0x90; // 1-byte nop + fixUpLocation[3] = 0x90; // 1-byte nop + } + break; + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32BE(fixUpLocation, 0x60000000); + } + break; + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a li r3,0 + set32BE(fixUpLocation, 0x38600000); + } + break; + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32LE(fixUpLocation, 0xE1A00000); + } + break; + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to 'eor r0, r0, r0' + set32LE(fixUpLocation, 0xE0200000); + } + break; + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change 32-bit blx call site to two thumb NOPs + set32LE(fixUpLocation, 0x46C046C0); + } + break; + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change 32-bit blx call site to 'nop', 'eor r0, r0' + set32LE(fixUpLocation, 0x46C04040); + } + break; + case ld::Fixup::kindLazyTarget: + break; + case ld::Fixup::kindSetLazyOffset: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + accumulator = this->lazyBindingInfoOffsetForLazyPointerAddress(fit->u.target->finalAddress()); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentAddendOnly ) + accumulator = 0; + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentAddendOnly ) + accumulator = 0; + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian32: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentAddendOnly ) + accumulator = 0; + set32BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian64: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentAddendOnly ) + accumulator = 0; + set64BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: + accumulator = tlvTemplateOffsetOf(state, fit); + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: + accumulator = tlvTemplateOffsetOf(state, fit); + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + if ( fit->contentAddendOnly ) + delta = 0; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: + // TLV entry was optimized away, change movl instruction to a leal + if ( fixUpLocation[-1] != 0xA1 ) + throw "TLV load reloc does not point to a movl , instruction"; + fixUpLocation[-1] = 0xB8; + accumulator = addressOf(state, fit, &toTarget); + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + // GOT entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "GOT load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + accumulator = addressOf(state, fit, &toTarget); + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: + // TLV entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "TLV load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + accumulator = addressOf(state, fit, &toTarget); + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressARMBranch24: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStoreARMBranch24 case + case ld::Fixup::kindStoreARMBranch24: + // The pc added will be +8 from the pc + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); + rangeCheckARMBranch24(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + // Make sure we are calling arm with bl, thumb with blx + is_bl = ((instruction & 0xFF000000) == 0xEB000000); + is_blx = ((instruction & 0xFE000000) == 0xFA000000); + if ( is_bl && thumbTarget ) { + uint32_t opcode = 0xFA000000; + uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; + uint32_t h_bit = (uint32_t)(delta << 23) & 0x01000000; + newInstruction = opcode | h_bit | disp; + } + else if ( is_blx && !thumbTarget ) { + uint32_t opcode = 0xEB000000; + uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; + newInstruction = opcode | disp; + } + else if ( !is_bl && !is_blx && thumbTarget ) { + throwf("don't know how to convert instruction %x referencing %s to thumb", + instruction, referenceTargetAtomName(state, fit)); + } + else { + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(delta >> 2) & 0x00FFFFFF); + } + set32LE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStoreThumbBranch22 case + case ld::Fixup::kindStoreThumbBranch22: + instruction = get32LE(fixUpLocation); + is_bl = ((instruction & 0xD000F800) == 0xD000F000); + is_blx = ((instruction & 0xD000F800) == 0xC000F000); + is_b = ((instruction & 0xD000F800) == 0x9000F000); + // If the target is not thumb, we will be generating a blx instruction + // Since blx cannot have the low bit set, set bit[1] of the target to + // bit[1] of the base address, so that the difference is a multiple of + // 4 bytes. + if ( !thumbTarget ) { + 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 ) { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(delta >> 24) & 0x1; + uint32_t i1 = (uint32_t)(delta >> 23) & 0x1; + uint32_t i2 = (uint32_t)(delta >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(delta >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(delta >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + if ( is_bl ) { + if ( thumbTarget ) + instruction = 0xD000F000; // keep bl + else + instruction = 0xC000F000; // change to blx + } + else if ( is_blx ) { + if ( thumbTarget ) + instruction = 0xD000F000; // change to bl + else + 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 + } + else if ( is_b ) { + if ( !thumbTarget ) + throwf("don't know how to convert branch instruction %x referencing %s to bx", + instruction, referenceTargetAtomName(state, fit)); + instruction = 0x9000F000; // keep b + } + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + newInstruction = instruction | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, delta, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + set32LE(fixUpLocation, newInstruction); + } + else { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t firstDisp = (uint32_t)(delta >> 12) & 0x7FF; + uint32_t nextDisp = (uint32_t)(delta >> 1) & 0x7FF; + if ( is_bl && !thumbTarget ) { + instruction = 0xE800F000; + } + 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 { + instruction = instruction & 0xF800F800; + } + newInstruction = instruction | (nextDisp << 16) | firstDisp; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreARMLow16: + { + uint32_t imm4 = (accumulator & 0x0000F000) >> 12; + uint32_t imm12 = accumulator & 0x00000FFF; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreARMHigh16: + { + uint32_t imm4 = (accumulator & 0xF0000000) >> 28; + uint32_t imm12 = (accumulator & 0x0FFF0000) >> 16; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreThumbLow16: + { + uint32_t imm4 = (accumulator & 0x0000F000) >> 12; + uint32_t i = (accumulator & 0x00000800) >> 11; + uint32_t imm3 = (accumulator & 0x00000700) >> 8; + uint32_t imm8 = accumulator & 0x000000FF; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreThumbHigh16: + { + uint32_t imm4 = (accumulator & 0xF0000000) >> 28; + uint32_t i = (accumulator & 0x08000000) >> 27; + uint32_t imm3 = (accumulator & 0x07000000) >> 24; + uint32_t imm8 = (accumulator & 0x00FF0000) >> 16; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStorePPCBranch24 case + case ld::Fixup::kindStorePPCBranch24: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckPPCBranch24(delta, state, atom, fit); + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)delta & 0x03FFFFFC); + set32BE(fixUpLocation, newInstruction); + break; + } + } +} + +void OutputFile::copyNoOps(uint8_t* from, uint8_t* to) +{ + switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); + break; + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + for (uint8_t* p=from; p < to; ++p) + *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); + break; + default: + for (uint8_t* p=from; p < to; ++p) + *p = 0x00; + break; + } +} + +bool OutputFile::takesNoDiskSpace(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeAbsoluteSymbols: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + +bool OutputFile::hasZeroForFileOffset(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + + +void OutputFile::writeOutputFile(ld::Internal& state) +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) ) + throwf("can't write output file: %s", _options.outputFilePath()); + + int permissions = 0777; + if ( _options.outputKind() == Options::kObjectFile ) + permissions = 0666; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where __options.outputFilePath() file is not writable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). + struct stat stat_buf; + if ( (stat(_options.outputFilePath(), &stat_buf) != -1) && (stat_buf.st_mode & S_IFREG) ) + (void)unlink(_options.outputFilePath()); + + // try to allocate buffer for entire output file content + uint8_t* wholeBuffer = (uint8_t*)calloc(_fileSize, 1); + if ( wholeBuffer == NULL ) + throwf("can't create buffer of %llu bytes for output", _fileSize); + + if ( _options.UUIDMode() == Options::kUUIDRandom ) { + uint8_t bits[16]; + ::uuid_generate_random(bits); + _headersAndLoadCommandAtom->setUUID(bits); + } + + // have each atom write itself + uint64_t fileOffsetOfEndOfLastAtom = 0; + uint64_t mhAddress = 0; + bool lastAtomUsesNoOps = false; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeMachHeader ) + mhAddress = sect->address; + if ( takesNoDiskSpace(sect) ) + continue; + 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; + for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->definition() == ld::Atom::definitionProxy ) + continue; + try { + 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]); + } + // copy atom content + atom->copyRawContent(&wholeBuffer[fileOffset]); + // apply fix ups + this->applyFixUps(state, mhAddress, atom, &wholeBuffer[fileOffset]); + fileOffsetOfEndOfLastAtom = fileOffset+atom->size(); + lastAtomUsesNoOps = sectionUsesNops; + } + catch (const char* msg) { + if ( atom->file() != NULL ) + throwf("%s in %s from %s", msg, atom->name(), atom->file()->path()); + else + throwf("%s in %s", msg, atom->name()); + } + } + } + + // compute UUID + if ( _options.UUIDMode() == Options::kUUIDContent ) { + const bool log = false; + if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + uint32_t stabsStringsOffsetStart; + uint32_t tabsStringsOffsetEnd; + uint32_t stabsOffsetStart; + uint32_t stabsOffsetEnd; + if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) { + // find two areas of file that are stabs info and should not contribute to checksum + uint64_t stringPoolFileOffset = 0; + uint64_t symbolTableFileOffset = 0; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeLinkEdit ) { + if ( strcmp(sect->sectionName(), "__string_pool") == 0 ) + stringPoolFileOffset = sect->fileOffset; + else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 ) + symbolTableFileOffset = sect->fileOffset; + } + } + uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart; + uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; + uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; + uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; + if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); + if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); + if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); + if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); + assert(firstStabNlistFileOffset <= firstStabStringFileOffset); + + CC_MD5_CTX md5state; + CC_MD5_Init(&md5state); + // checksum everything up to first stabs nlist + if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); + // checkusm everything after last stabs nlist and up to first stabs string + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); + // checksum everything after last stabs string to end of file + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); + CC_MD5_Final(digest, &md5state); + if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], + digest[3], digest[4], digest[5], digest[6], digest[7]); + } + else { + CC_MD5(wholeBuffer, _fileSize, digest); + } + // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); + digest[8] = ( digest[8] & 0x3F ) | 0x80; + // update buffer with new UUID + _headersAndLoadCommandAtom->setUUID(digest); + _headersAndLoadCommandAtom->recopyUUIDCommand(); + } + } + + // write whole output file in one chunk + int fd = open(_options.outputFilePath(), O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); + if ( ::pwrite(fd, wholeBuffer, _fileSize, 0) == -1 ) + throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); + close(fd); + free(wholeBuffer); +} + +struct AtomByNameSorter +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } +}; + +void OutputFile::buildSymbolTable(ld::Internal& state) +{ + unsigned int machoSectionIndex = 0; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + bool setMachoSectionIndex = !sect->isSectionHidden() && (sect->type() != ld::Section::typeTentativeDefs); + if ( setMachoSectionIndex ) + ++machoSectionIndex; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( setMachoSectionIndex ) + (const_cast(atom))->setMachoSection(machoSectionIndex); + else if ( sect->type() == ld::Section::typeMachHeader ) + (const_cast(atom))->setMachoSection(1); // __mh_execute_header is not in any section by needs n_sect==1 + else if ( sect->type() == ld::Section::typeLastSection ) + (const_cast(atom))->setMachoSection(machoSectionIndex); // use section index of previous section + else if ( sect->type() == ld::Section::typeFirstSection ) + (const_cast(atom))->setMachoSection(machoSectionIndex+1); // use section index of next section + + // in -r mode, clarify symbolTableNotInFinalLinkedImages + if ( _options.outputKind() == Options::kObjectFile ) { + if ( _options.architecture() == CPU_TYPE_X86_64 ) { + // x86_64 .o files need labels on anonymous literal strings + if ( (sect->type() == ld::Section::typeCString) && (atom->combine() == ld::Atom::combineByNameAndContent) ) { + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + _localAtoms.push_back(atom); + continue; + } + } + if ( sect->type() == ld::Section::typeCFI ) { + if ( _options.removeEHLabels() ) + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); + else + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + } + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + } + + // TEMP work around until goes in + if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) + && (atom->scope() == ld::Atom::scopeLinkageUnit) + && (_options.outputKind() == Options::kDynamicLibrary) ) { + (const_cast(atom))->setScope(ld::Atom::scopeGlobal); + } + + // support auto hidden weak symbols: .weak_def_can_be_hidden + if ( atom->autoHide() && (_options.outputKind() != Options::kObjectFile) ) { + // adding auto-hide symbol to .exp file should keep it global + if ( !_options.hasExportMaskList() || !_options.shouldExport(atom->name()) ) + (const_cast(atom))->setScope(ld::Atom::scopeLinkageUnit); + } + + // ld should consistently warn when resolvers are not exported + if ( (atom->contentType() == ld::Atom::typeResolver) && (atom->scope() == ld::Atom::scopeLinkageUnit) ) + warning("resolver functions should be external, but '%s' is hidden", atom->name()); + + if ( sect->type() == ld::Section::typeImportProxies ) { + if ( atom->combine() == ld::Atom::combineByName ) + this->usesWeakExternalSymbols = true; + // alias proxy is a re-export with a name change, don't import changed name + if ( ! atom->isAlias() ) + _importedAtoms.push_back(atom); + // scope of proxies are usually linkage unit, so done + // if scope is global, we need to re-export it too + if ( atom->scope() == ld::Atom::scopeGlobal ) + _exportedAtoms.push_back(atom); + continue; + } + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) { + assert(_options.outputKind() != Options::kObjectFile); + continue; // don't add to symbol table + } + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotIn ) { + continue; // don't add to symbol table + } + + if ( (atom->definition() == ld::Atom::definitionTentative) && (_options.outputKind() == Options::kObjectFile) ) { + if ( _options.makeTentativeDefinitionsReal() ) { + // -r -d turns tentative defintions into real def + _exportedAtoms.push_back(atom); + } + else { + // in mach-o object files tentative defintions are stored like undefined symbols + _importedAtoms.push_back(atom); + } + continue; + } + + // Add command line options to control symbol weak-def bit on exported symbols + if ( _options.hasWeakBitTweaks() && (atom->definition() == ld::Atom::definitionRegular) ) { + const char* name = atom->name(); + if ( atom->scope() == ld::Atom::scopeGlobal ) { + if ( atom->combine() == ld::Atom::combineNever ) { + if ( _options.forceWeak(name) ) + (const_cast(atom))->setCombine(ld::Atom::combineByName); + } + else if ( atom->combine() == ld::Atom::combineByName ) { + if ( _options.forceNotWeak(name) ) + (const_cast(atom))->setCombine(ld::Atom::combineNever); + } + } + else { + if ( _options.forceWeakNonWildCard(name) ) + warning("cannot force to be weak, non-external symbol %s", name); + else if ( _options.forceNotWeakNonWildcard(name) ) + warning("cannot force to be not-weak, non-external symbol %s", name); + } + } + + switch ( atom->scope() ) { + case ld::Atom::scopeTranslationUnit: + if ( _options.keepLocalSymbol(atom->name()) ) { + _localAtoms.push_back(atom); + } + else { + if ( _options.outputKind() == Options::kObjectFile ) { + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableInWithRandomAutoStripLabel); + _localAtoms.push_back(atom); + } + else + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); + } + break; + case ld::Atom::scopeGlobal: + _exportedAtoms.push_back(atom); + break; + case ld::Atom::scopeLinkageUnit: + if ( _options.outputKind() == Options::kObjectFile ) { + if ( _options.keepPrivateExterns() ) { + assert( (atom->combine() == ld::Atom::combineNever) || (atom->combine() == ld::Atom::combineByName) ); + _exportedAtoms.push_back(atom); + } + else if ( _options.keepLocalSymbol(atom->name()) ) { + _localAtoms.push_back(atom); + } + else { + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableInWithRandomAutoStripLabel); + _localAtoms.push_back(atom); + } + } + else { + if ( _options.keepLocalSymbol(atom->name()) ) + _localAtoms.push_back(atom); + else + (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); + } + break; + } + } + } + + // sort by name + std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter()); + std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter()); + +} + +void OutputFile::addPreloadLinkEdit(ld::Internal& state) +{ + switch ( _options.architecture() ) { + case CPU_TYPE_I386: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_X86_64: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_ARM: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + default: + throw "architecture not supported for -preload"; + } + +} + + +void OutputFile::addLinkEdit(ld::Internal& state) +{ + // for historical reasons, -preload orders LINKEDIT content differently + if ( _options.outputKind() == Options::kPreload ) + return addPreloadLinkEdit(state); + + switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_I386: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_X86_64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 8); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_ARM: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_POWERPC64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + default: + throw "unknown architecture"; + } +} + +void OutputFile::addLoadCommands(ld::Internal& state) +{ + switch ( _options.architecture() ) { + case CPU_TYPE_X86_64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_ARM: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_I386: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_POWERPC: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_POWERPC64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + default: + throw "unknown architecture"; + } +} + +uint32_t OutputFile::dylibCount() +{ + return _dylibsToLoad.size(); +} + +const ld::dylib::File* OutputFile::dylibByOrdinal(unsigned int ordinal) +{ + assert( ordinal > 0 ); + assert( ordinal <= _dylibsToLoad.size() ); + return _dylibsToLoad[ordinal-1]; +} + +bool OutputFile::hasOrdinalForInstallPath(const char* path, int* ordinal) +{ + for (std::map::const_iterator it = _dylibToOrdinal.begin(); it != _dylibToOrdinal.end(); ++it) { + const char* installPath = it->first->installPath(); + if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) { + *ordinal = it->second; + return true; + } + } + return false; +} + +uint32_t OutputFile::dylibToOrdinal(const ld::dylib::File* dylib) +{ + return _dylibToOrdinal[dylib]; +} + + +void OutputFile::buildDylibOrdinalMapping(ld::Internal& state) +{ + // count non-public re-exported dylibs + unsigned int nonPublicReExportCount = 0; + for (std::vector::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + if ( aDylib->willBeReExported() && ! aDylib->hasPublicInstallName() ) + ++nonPublicReExportCount; + } + + // look at each dylib supplied in state + bool hasReExports = false; + bool haveLazyDylibs = false; + for (std::vector::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + int ordinal; + if ( aDylib == state.bundleLoader ) { + _dylibToOrdinal[aDylib] = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; + } + else if ( this->hasOrdinalForInstallPath(aDylib->installPath(), &ordinal) ) { + // already have a dylib with that install path, map all uses to that ordinal + _dylibToOrdinal[aDylib] = ordinal; + } + else if ( aDylib->willBeLazyLoadedDylib() ) { + // all lazy dylib need to be at end of ordinals + haveLazyDylibs = true; + } + else if ( aDylib->willBeReExported() && ! aDylib->hasPublicInstallName() && (nonPublicReExportCount >= 2) ) { + _dylibsToLoad.push_back(aDylib); + _dylibToOrdinal[aDylib] = BIND_SPECIAL_DYLIB_SELF; + } + else { + // first time this install path seen, create new ordinal + _dylibsToLoad.push_back(aDylib); + _dylibToOrdinal[aDylib] = _dylibsToLoad.size(); + } + if ( aDylib->explicitlyLinked() && aDylib->willBeReExported() ) + hasReExports = true; + } + if ( haveLazyDylibs ) { + // second pass to determine ordinals for lazy loaded dylibs + for (std::vector::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + if ( aDylib->willBeLazyLoadedDylib() ) { + int ordinal; + if ( this->hasOrdinalForInstallPath(aDylib->installPath(), &ordinal) ) { + // already have a dylib with that install path, map all uses to that ordinal + _dylibToOrdinal[aDylib] = ordinal; + } + else { + // first time this install path seen, create new ordinal + _dylibsToLoad.push_back(aDylib); + _dylibToOrdinal[aDylib] = _dylibsToLoad.size(); + } + } + } + } + _noReExportedDylibs = !hasReExports; + //fprintf(stderr, "dylibs:\n"); + //for (std::map::const_iterator it = _dylibToOrdinal.begin(); it != _dylibToOrdinal.end(); ++it) { + // fprintf(stderr, " %p ord=%u, install_name=%s\n",it->first, it->second, it->first->installPath()); + //} +} + +uint32_t OutputFile::lazyBindingInfoOffsetForLazyPointerAddress(uint64_t lpAddress) +{ + return _lazyPointerAddressToInfoOffset[lpAddress]; +} + +void OutputFile::setLazyBindingInfoOffset(uint64_t lpAddress, uint32_t lpInfoOffset) +{ + _lazyPointerAddressToInfoOffset[lpAddress] = lpInfoOffset; +} + +int OutputFile::compressedOrdinalForAtom(const ld::Atom* target) +{ + // flat namespace images use zero for all ordinals + if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + // handle -interposable + if ( target->definition() == ld::Atom::definitionRegular ) + return BIND_SPECIAL_DYLIB_SELF; + + // regular ordinal + const ld::dylib::File* dylib = dynamic_cast(target->file()); + if ( dylib != NULL ) + return _dylibToOrdinal[dylib]; + + // handle undefined dynamic_lookup + if ( _options.undefinedTreatment() == Options::kUndefinedDynamicLookup ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + // handle -U _foo + if ( _options.allowedUndefined(target->name()) ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + throw "can't find ordinal for imported symbol"; +} + + +bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindStoreX86BranchPCRel8: + case ld::Fixup::kindStoreX86BranchPCRel32: + case ld::Fixup::kindStoreX86PCRel8: + case ld::Fixup::kindStoreX86PCRel16: + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreARMLoad12: + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStorePPCBranch14: + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMLoad12: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return true; + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + return (_options.outputKind() != Options::kKextBundle); + default: + break; + } + return false; +} + +bool OutputFile::isStore(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindNone: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindSubtractTargetAddress: + case ld::Fixup::kindAddAddend: + case ld::Fixup::kindSubtractAddend: + case ld::Fixup::kindSetTargetImageOffset: + case ld::Fixup::kindSetTargetSectionOffset: + return false; + default: + break; + } + return true; +} + + +bool OutputFile::setsTarget(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindLazyTarget: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMLoad12: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return true; + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + return (_options.outputKind() == Options::kObjectFile); + default: + break; + } + return false; +} + +bool OutputFile::isPointerToTarget(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + case ld::Fixup::kindLazyTarget: + return true; + default: + break; + } + return false; +} +bool OutputFile::isPointerFromTarget(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindSubtractTargetAddress: + return true; + default: + break; + } + return false; +} + + +uint64_t OutputFile::lookBackAddend(ld::Fixup::iterator fit) +{ + uint64_t addend = 0; + switch ( fit->clusterSize ) { + case ld::Fixup::k1of1: + case ld::Fixup::k1of2: + case ld::Fixup::k2of2: + break; + case ld::Fixup::k2of3: + --fit; + switch ( fit->kind ) { + case ld::Fixup::kindAddAddend: + addend += fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + addend -= fit->u.addend; + break; + default: + throw "unexpected fixup kind for binding"; + } + break; + case ld::Fixup::k1of3: + ++fit; + switch ( fit->kind ) { + case ld::Fixup::kindAddAddend: + addend += fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + addend -= fit->u.addend; + break; + default: + throw "unexpected fixup kind for binding"; + } + break; + default: + throw "unexpected fixup cluster size for binding"; + } + return addend; +} + + + + + +void OutputFile::generateLinkEditInfo(ld::Internal& state) +{ + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer) + && (strcmp(sect->sectionName(), "__cls_refs") == 0) + && (strcmp(sect->segmentName(), "__OBJC") == 0) ); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + + // Record regular atoms that override a dylib's weak definitions + if ( (atom->scope() == ld::Atom::scopeGlobal) && atom->overridesDylibsWeakDef() ) { + if ( _options.makeCompressedDyldInfo() ) { + uint8_t wtype = BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB; + bool nonWeakDef = (atom->combine() == ld::Atom::combineNever); + _weakBindingInfo.push_back(BindingInfo(wtype, atom->name(), nonWeakDef, atom->finalAddress(), 0)); + } + this->overridesWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("overrides weak external symbol: %s", atom->name()); + } + + ld::Fixup* fixupWithTarget = NULL; + ld::Fixup* fixupWithMinusTarget = NULL; + ld::Fixup* fixupWithStore = NULL; + const ld::Atom* target = NULL; + const ld::Atom* minusTarget = NULL; + uint64_t targetAddend = 0; + uint64_t minusTargetAddend = 0; + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->firstInCluster() ) { + fixupWithTarget = NULL; + fixupWithMinusTarget = NULL; + fixupWithStore = NULL; + target = NULL; + minusTarget = NULL; + targetAddend = 0; + minusTargetAddend = 0; + } + if ( this->setsTarget(fit->kind) ) { + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + fixupWithTarget = fit; + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + fixupWithTarget = fit; + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + assert(target != NULL); + } + switch ( fit->kind ) { + case ld::Fixup::kindAddAddend: + targetAddend = fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + minusTargetAddend = fit->u.addend; + break; + case ld::Fixup::kindSubtractTargetAddress: + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + fixupWithMinusTarget = fit; + minusTarget = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + fixupWithMinusTarget = fit; + minusTarget = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + assert(minusTarget != NULL); + break; + default: + break; + } + if ( this->isStore(fit->kind) ) { + fixupWithStore = fit; + } + if ( fit->lastInCluster() ) { + if ( (fixupWithStore != NULL) && (target != NULL) ) { + if ( _options.outputKind() == Options::kObjectFile ) { + this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + target, minusTarget, targetAddend, minusTargetAddend); + } + else { + if ( _options.makeCompressedDyldInfo() ) { + this->addDyldInfo(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + target, minusTarget, targetAddend, minusTargetAddend); + } + else { + this->addClassicRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + target, minusTarget, targetAddend, minusTargetAddend); + } + } + } + else if ( objc1ClassRefSection && (target != NULL) && (fixupWithStore == NULL) ) { + // check for class refs to lazy loaded dylibs + const ld::dylib::File* dylib = dynamic_cast(target->file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal class reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + } + } + } + } + } +} + + +void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) +{ + if ( (atom->contentType() == ld::Atom::typeStub) || (atom->contentType() == ld::Atom::typeStubHelper) ) { + // silently let stubs (synthesized by linker) use text relocs + } + else if ( _options.allowTextRelocs() ) { + if ( _options.warnAboutTextRelocs() ) + warning("text reloc in %s to %s", atom->name(), target->name()); + } + else if ( _options.positionIndependentExecutable() && (_options.iphoneOSVersionMin() >= ld::iPhone4_3) ) { + if ( ! this->pieDisabled ) { + warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " + "but used in %s from %s. " + "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", + atom->name(), atom->file()->path()); + } + this->pieDisabled = true; + } + else { + throwf("illegal text reloc to %s from %s in %s", target->name(), target->file()->path(), atom->name()); + } +} + +void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend) +{ + if ( sect->isSectionHidden() ) + return; + + // 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) ) + return; + } + + // no need to rebase or bind PIC internal pointer diff + if ( minusTarget != NULL ) { + // with pointer diffs, both need to be in same linkage unit + assert(minusTarget->definition() != ld::Atom::definitionProxy); + assert(target != NULL); + assert(target->definition() != ld::Atom::definitionProxy); + if ( target == minusTarget ) { + // This is a compile time constant and could have been optimized away by compiler + 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()); + } + return; + } + + // no need to rebase or bind an atom's references to itself if the output is not slidable + if ( (atom == target) && !_options.outputSlidable() ) + return; + + // cluster has no target, so needs no rebasing or binding + if ( target == NULL ) + return; + + bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 ); + bool needsRebase = false; + bool needsBinding = false; + bool needsLazyBinding = false; + bool needsWeakBinding = false; + + 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())); + uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom; + uint64_t addend = targetAddend - minusTargetAddend; + + // special case lazy pointers + if ( fixupWithTarget->kind == ld::Fixup::kindLazyTarget ) { + assert(fixupWithTarget->u.target == target); + assert(addend == 0); + // lazy dylib lazy pointers do not have any dyld info + if ( atom->section().type() == ld::Section::typeLazyDylibPointer ) + return; + // lazy binding to weak definitions are done differently + // they are directly bound to target, then have a weak bind in case of a collision + if ( target->combine() == ld::Atom::combineByName ) { + if ( target->definition() == ld::Atom::definitionProxy ) { + // weak def exported from another dylib + // must non-lazy bind to it plus have weak binding info in case of collision + needsBinding = true; + needsWeakBinding = true; + } + else { + // weak def in this linkage unit. + // just rebase, plus have weak binding info in case of collision + // this will be done by other cluster on lazy pointer atom + } + } + else if ( (target->contentType() == ld::Atom::typeResolver) && (target->scope() != ld::Atom::scopeGlobal) ) { + // Hidden resolver functions should not have lazy binding info + needsLazyBinding = false; + } + else { + // normal case of a pointer to non-weak-def symbol, so can lazily bind + needsLazyBinding = true; + } + } + else { + // everything except lazy pointers + switch ( target->definition() ) { + case ld::Atom::definitionProxy: + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + if ( target->contentType() == ld::Atom::typeTLV ) { + if ( sect->type() != ld::Section::typeTLVPointers ) + throwf("illegal data reference in %s to thread local variable %s in dylib %s", + atom->name(), target->name(), dylib->path()); + } + if ( inReadOnlySeg ) + type = BIND_TYPE_TEXT_ABSOLUTE32; + needsBinding = true; + if ( target->combine() == ld::Atom::combineByName ) + needsWeakBinding = true; + break; + case ld::Atom::definitionRegular: + case ld::Atom::definitionTentative: + // only slideable images need rebasing info + if ( _options.outputSlidable() ) { + needsRebase = true; + } + // references to internal symbol never need binding + if ( target->scope() != ld::Atom::scopeGlobal ) + break; + // reference to global weak def needs weak binding + if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) + needsWeakBinding = true; + else if ( _options.outputKind() == Options::kDynamicExecutable ) { + // in main executables, the only way regular symbols are indirected is if -interposable is used + if ( _options.interposable(target->name()) ) { + needsRebase = false; + needsBinding = true; + } + } + else { + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + if ( (_options.nameSpace() != Options::kTwoLevelNameSpace) || _options.interposable(target->name()) ) { + // no external relocs for flat objc classes + if ( strncmp(target->name(), ".objc_class_", 12) == 0 ) + break; + // no rebase info for references to global symbols that will have binding info + needsRebase = false; + needsBinding = true; + } + } + break; + case ld::Atom::definitionAbsolute: + break; + } + } + + // record dyld info for this cluster + if ( needsRebase ) { + if ( inReadOnlySeg ) { + noteTextReloc(atom, target); + sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment + rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32; + } + _rebaseInfo.push_back(RebaseInfo(rebaseType, address)); + } + if ( needsBinding ) { + if ( inReadOnlySeg ) { + noteTextReloc(atom, target); + sect->hasExternalRelocs = true; // so dyld knows to change permissions on __TEXT segment + } + _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); + } + if ( needsLazyBinding ) { + if ( _options.bindAtLoad() ) + _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); + else + _lazyBindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); + } + 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(); +} + + +void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend) +{ + if ( sect->isSectionHidden() ) + return; + + // non-lazy-pointer section is encoded in indirect symbol table - not using relocations + if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) { + assert(target != NULL); + assert(fixupWithTarget != NULL); + // 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; + } + + // 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) ) + return; + } + + // no need to rebase or bind PIC internal pointer diff + if ( minusTarget != NULL ) { + // with pointer diffs, both need to be in same linkage unit + assert(minusTarget->definition() != ld::Atom::definitionProxy); + assert(target != NULL); + assert(target->definition() != ld::Atom::definitionProxy); + // 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) + && (minusTarget != target) ) { + // 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()); + } + return; + } + + // cluster has no target, so needs no rebasing or binding + if ( target == NULL ) + return; + + assert(_localRelocsAtom != NULL); + uint64_t relocAddress = atom->finalAddress() + fixupWithTarget->offsetInAtom - _localRelocsAtom->relocBaseAddress(state); + + bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 ); + bool needsLocalReloc = false; + bool needsExternReloc = false; + + 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(); + } + break; + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreBigEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + // is pointer + switch ( target->definition() ) { + case ld::Atom::definitionProxy: + needsExternReloc = true; + break; + case ld::Atom::definitionRegular: + case ld::Atom::definitionTentative: + // only slideable images need local relocs + if ( _options.outputSlidable() ) + needsLocalReloc = true; + // references to internal symbol never need binding + if ( target->scope() != ld::Atom::scopeGlobal ) + break; + // reference to global weak def needs weak binding in dynamic images + if ( (target->combine() == ld::Atom::combineByName) + && (target->definition() == ld::Atom::definitionRegular) + && (_options.outputKind() != Options::kStaticExecutable) ) { + needsExternReloc = true; + } + else if ( _options.outputKind() == Options::kDynamicExecutable ) { + // in main executables, the only way regular symbols are indirected is if -interposable is used + if ( _options.interposable(target->name()) ) + needsExternReloc = true; + } + else { + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + if ( (_options.nameSpace() != Options::kTwoLevelNameSpace) || _options.interposable(target->name()) ) { + // no external relocs for flat objc classes + if ( strncmp(target->name(), ".objc_class_", 12) == 0 ) + break; + // no rebase info for references to global symbols that will have binding info + needsExternReloc = true; + } + } + if ( needsExternReloc ) + needsLocalReloc = false; + break; + case ld::Atom::definitionAbsolute: + break; + } + if ( needsExternReloc ) { + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + const ld::dylib::File* dylib = dynamic_cast(target->file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + _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); + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + _localRelocsAtom->addPointerReloc(relocAddress, target->machoSection()); + sect->hasLocalRelocs = true; + } + break; + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCAbsHigh16: + { + assert(target != NULL); + if ( target->definition() == ld::Atom::definitionProxy ) + throwf("half word text relocs not supported in %s", atom->name()); + if ( _options.outputSlidable() ) { + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + uint32_t machoSectionIndex = (target->definition() == ld::Atom::definitionAbsolute) + ? R_ABS : target->machoSection(); + _localRelocsAtom->addTextReloc(relocAddress, fixupWithTarget->kind, + target->finalAddress(), machoSectionIndex); + sect->hasLocalRelocs = true; + } + } + break; + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + if ( _options.outputKind() == Options::kKextBundle ) { + assert(target != NULL); + if ( target->definition() == ld::Atom::definitionProxy ) { + _externalRelocsAtom->addExternalCallSiteReloc(relocAddress, target); + fixupWithStore->contentAddendOnly = true; + } + } + break; + default: + break; + } +} + + +bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget) +{ + if ( _options.architecture() == CPU_TYPE_X86_64 ) { + // x86_64 uses external relocations for everthing that has a symbol + return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); + } + // 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); + if ( target->definition() == ld::Atom::definitionProxy ) + return true; + if ( (target->definition() == ld::Atom::definitionTentative) && ! _options.makeTentativeDefinitionsReal() ) + return true; + if ( target->scope() != ld::Atom::scopeGlobal ) + return false; + if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) + return true; + return false; +} + + + + +void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend) +{ + if ( sect->isSectionHidden() ) + return; + + // in -r mode where there will be no labels on __eh_frame section, there is no need for relocations + if ( (sect->type() == ld::Section::typeCFI) && _options.removeEHLabels() ) + return; + + // non-lazy-pointer section is encoded in indirect symbol table - not using relocations + if ( sect->type() == ld::Section::typeNonLazyPointer ) + return; + + // tentative defs don't have any relocations + if ( sect->type() == ld::Section::typeTentativeDefs ) + return; + + assert(target != NULL); + assert(fixupWithTarget != NULL); + bool targetUsesExternalReloc = this->useExternalSectionReloc(atom, target, fixupWithTarget); + bool minusTargetUsesExternalReloc = (minusTarget != NULL) && this->useExternalSectionReloc(atom, minusTarget, fixupWithMinusTarget); + + // in x86_64 .o files an external reloc means the content contains just the addend + if ( _options.architecture() == CPU_TYPE_X86_64 ) { + if ( targetUsesExternalReloc ) { + fixupWithTarget->contentAddendOnly = true; + fixupWithStore->contentAddendOnly = true; + } + if ( minusTargetUsesExternalReloc ) + fixupWithMinusTarget->contentAddendOnly = true; + } + else { + // for other archs, content is addend only with (non pc-rel) pointers + // pc-rel instructions are funny. If the target is _foo+8 and _foo is + // external, then the pc-rel instruction *evalutates* to the address 8. + if ( targetUsesExternalReloc ) { + if ( isPcRelStore(fixupWithStore->kind) ) { + fixupWithTarget->contentDetlaToAddendOnly = true; + fixupWithStore->contentDetlaToAddendOnly = true; + } + else if ( minusTarget == NULL ){ + fixupWithTarget->contentAddendOnly = true; + fixupWithStore->contentAddendOnly = true; + } + } + } + + if ( fixupWithStore != NULL ) { + _sectionsRelocationsAtom->addSectionReloc(sect, fixupWithStore->kind, atom, fixupWithStore->offsetInAtom, + targetUsesExternalReloc, minusTargetUsesExternalReloc, + target, targetAddend, minusTarget, minusTargetAddend); + } + +} + + +void OutputFile::makeSplitSegInfo(ld::Internal& state) +{ + if ( !_options.sharedRegionEligible() ) + return; + + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + if ( strcmp(sect->segmentName(), "__TEXT") != 0 ) + continue; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target = NULL; + 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; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + switch ( fit->kind ) { + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + // if no subtract, then this is an absolute pointer which means + // there is also a text reloc which update_dyld_shared_cache will use. + if ( ! hadSubtract ) + break; + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + assert(target != NULL); + if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { + _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind)); + } + break; + default: + break; + } + } + } + } +} + + +void OutputFile::writeMapFile(ld::Internal& state) +{ + if ( _options.generatedMapPath() != NULL ) { + FILE* mapFile = fopen(_options.generatedMapPath(), "w"); + if ( mapFile != NULL ) { + // write output path + fprintf(mapFile, "# Path: %s\n", _options.outputFilePath()); + // write output architecure + fprintf(mapFile, "# Arch: %s\n", _options.architectureName()); + // write UUID + //if ( fUUIDAtom != NULL ) { + // const uint8_t* uuid = fUUIDAtom->getUUID(); + // fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n", + // uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + //} + // write table of object files + std::map readerToOrdinal; + std::map ordinalToReader; + std::map readerToFileOrdinal; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::File* reader = atom->file(); + if ( reader == NULL ) + continue; + uint32_t readerOrdinal = reader->ordinal(); + std::map::iterator pos = readerToOrdinal.find(reader); + if ( pos == readerToOrdinal.end() ) { + readerToOrdinal[reader] = readerOrdinal; + ordinalToReader[readerOrdinal] = reader; + } + } + } + fprintf(mapFile, "# Object files:\n"); + fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); + uint32_t fileIndex = 0; + readerToFileOrdinal[NULL] = fileIndex++; + for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first != 0 ) { + fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path()); + readerToFileOrdinal[it->second] = fileIndex++; + } + } + // write table of sections + fprintf(mapFile, "# Sections:\n"); + fprintf(mapFile, "# Address\tSize \tSegment\tSection\n"); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->address, sect->size, + sect->segmentName(), sect->sectionName()); + } + // write table of symbols + fprintf(mapFile, "# Symbols:\n"); + fprintf(mapFile, "# Address\tSize \tFile Name\n"); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + //bool isCstring = (sect->type() == ld::Section::typeCString); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + char buffer[4096]; + const ld::Atom* atom = *ait; + const char* name = atom->name(); + if ( atom->contentType() == ld::Atom::typeCString ) { + strcpy(buffer, "literal string: "); + strlcat(buffer, (char*)atom->rawContentPointer(), 4096); + name = buffer; + } + else if ( (atom->contentType() == ld::Atom::typeCFI) && (strcmp(name, "FDE") == 0) ) { + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( (fit->kind == ld::Fixup::kindSetTargetAddress) && (fit->clusterSize == ld::Fixup::k1of4) ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target->section().type() == ld::Section::typeCode) { + strcpy(buffer, "FDE for: "); + strlcat(buffer, fit->u.target->name(), 4096); + name = buffer; + } + } + } + } + else if ( atom->contentType() == ld::Atom::typeNonLazyPointer ) { + strcpy(buffer, "non-lazy-pointer"); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingsIndirectlyBound ) { + strcpy(buffer, "non-lazy-pointer-to: "); + strlcat(buffer, state.indirectBindingTable[fit->u.bindingIndex]->name(), 4096); + break; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + strcpy(buffer, "non-lazy-pointer-to-local: "); + strlcat(buffer, fit->u.target->name(), 4096); + break; + } + } + name = buffer; + } + fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->finalAddress(), atom->size(), + readerToFileOrdinal[atom->file()], name); + } + } + fclose(mapFile); + } + else { + warning("could not write map file: %s\n", _options.generatedMapPath()); + } + } +} + + +// used to sort atoms with debug notes +class DebugNoteSorter +{ +public: + bool operator()(const ld::Atom* left, const ld::Atom* right) const + { + // first sort by reader + uint32_t leftFileOrdinal = left->file()->ordinal(); + uint32_t rightFileOrdinal = right->file()->ordinal(); + if ( leftFileOrdinal!= rightFileOrdinal) + return (leftFileOrdinal < rightFileOrdinal); + + // then sort by atom objectAddress + uint64_t leftAddr = left->objectAddress(); + uint64_t rightAddr = right->objectAddress(); + return leftAddr < rightAddr; + } +}; + + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +const char* OutputFile::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + +void OutputFile::synthesizeDebugNotes(ld::Internal& state) +{ + // -S means don't synthesize debug map + if ( _options.debugInfoStripping() == Options::kDebugInfoNone ) + return; + // make a vector of atoms that come from files compiled with dwarf debug info + std::vector atomsNeedingDebugNotes; + std::set atomsWithStabs; + atomsNeedingDebugNotes.reserve(1024); + const ld::relocatable::File* objFile = NULL; + bool objFileHasDwarf = false; + bool objFileHasStabs = false; + 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; + // no stabs for atoms that would not be in the symbol table + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotIn ) + continue; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) + continue; + // no stabs for absolute symbols + if ( atom->definition() == ld::Atom::definitionAbsolute ) + continue; + // no stabs for .eh atoms + if ( atom->contentType() == ld::Atom::typeCFI ) + continue; + const ld::File* file = atom->file(); + if ( file != NULL ) { + if ( file != objFile ) { + objFileHasDwarf = false; + objFileHasStabs = false; + objFile = dynamic_cast(file); + if ( objFile != NULL ) { + switch ( objFile->debugInfo() ) { + case ld::relocatable::File::kDebugInfoNone: + break; + case ld::relocatable::File::kDebugInfoDwarf: + objFileHasDwarf = true; + break; + case ld::relocatable::File::kDebugInfoStabs: + case ld::relocatable::File::kDebugInfoStabsUUID: + objFileHasStabs = true; + break; + } + } + } + if ( objFileHasDwarf ) + atomsNeedingDebugNotes.push_back(atom); + if ( objFileHasStabs ) + atomsWithStabs.insert(atom); + } + } + } + + // sort by file ordinal then atom ordinal + std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), DebugNoteSorter()); + + // synthesize "debug notes" and add them to master stabs vector + const char* dirPath = NULL; + const char* filename = NULL; + bool wroteStartSO = false; + state.stabs.reserve(atomsNeedingDebugNotes.size()*4); + __gnu_cxx::hash_set, CStringEquals> seenFiles; + for (std::vector::iterator it=atomsNeedingDebugNotes.begin(); it != atomsNeedingDebugNotes.end(); it++) { + const ld::Atom* atom = *it; + const ld::File* atomFile = atom->file(); + const ld::relocatable::File* atomObjFile = dynamic_cast(atomFile); + const char* newDirPath; + const char* newFilename; + //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + if ( atom->translationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( filename != NULL ) { + // translation unit change, emit ending SO + ld::relocatable::File::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + state.stabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ld::relocatable::File::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + state.stabs.push_back(dirPathStab); + ld::relocatable::File::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + state.stabs.push_back(fileStab); + // Synthesize OSO for start of file + ld::relocatable::File::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + // linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries + objStab.other = atomFile->cpuSubType(); + objStab.desc = 1; + if ( atomObjFile != NULL ) { + objStab.string = assureFullPath(atomObjFile->debugInfoPath()); + objStab.value = atomObjFile->debugInfoModificationTime(); + } + else { + objStab.string = assureFullPath(atomFile->path()); + objStab.value = atomFile->modificationTime(); + } + state.stabs.push_back(objStab); + wroteStartSO = true; + // 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); + // add both leaf path and full path + seenFiles.insert(fullFilePath); + } + filename = newFilename; + dirPath = newDirPath; + if ( atom->section().type() == ld::Section::typeCode ) { + // Synthesize BNSYM and start FUN stabs + ld::relocatable::File::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + state.stabs.push_back(beginSym); + ld::relocatable::File::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = atom->name(); + state.stabs.push_back(startFun); + // Synthesize any SOL stabs needed + const char* curFile = NULL; + for (ld::Atom::LineInfo::iterator lit = atom->beginLineInfo(); lit != atom->endLineInfo(); ++lit) { + if ( lit->fileName != curFile ) { + if ( seenFiles.count(lit->fileName) == 0 ) { + seenFiles.insert(lit->fileName); + ld::relocatable::File::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = lit->fileName; + state.stabs.push_back(sol); + } + curFile = lit->fileName; + } + } + // Synthesize end FUN and ENSYM stabs + ld::relocatable::File::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + state.stabs.push_back(endFun); + ld::relocatable::File::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + state.stabs.push_back(endSym); + } + else { + ld::relocatable::File::Stab globalsStab; + const char* name = atom->name(); + if ( atom->scope() == ld::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + state.stabs.push_back(globalsStab); + } + else { + // Synthesize GSYM stab for other globals + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + state.stabs.push_back(globalsStab); + } + } + } + } + + if ( wroteStartSO ) { + // emit ending SO + ld::relocatable::File::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + state.stabs.push_back(endFileStab); + } + + // copy any stabs from .o file + std::set filesSeenWithStabs; + for (std::set::iterator it=atomsWithStabs.begin(); it != atomsWithStabs.end(); it++) { + const ld::Atom* atom = *it; + objFile = dynamic_cast(atom->file()); + if ( objFile != NULL ) { + if ( filesSeenWithStabs.count(objFile) == 0 ) { + filesSeenWithStabs.insert(objFile); + const std::vector* stabs = objFile->stabs(); + if ( stabs != NULL ) { + for(std::vector::const_iterator sit = stabs->begin(); sit != stabs->end(); ++sit) { + ld::relocatable::File::Stab stab = *sit; + // ignore stabs associated with atoms that were dead stripped or coalesced away + if ( (sit->atom != NULL) && (atomsWithStabs.count(sit->atom) == 0) ) + continue; + // Value of N_SO stabs should be address of first atom from translation unit + if ( (stab.type == N_SO) && (stab.string != NULL) && (stab.string[0] != '\0') ) { + stab.atom = atom; + } + state.stabs.push_back(stab); + } + } + } + } + } + +} + + +} // namespace tool +} // namespace ld + diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h new file mode 100644 index 0000000..9b49289 --- /dev/null +++ b/ld64/src/ld/OutputFile.h @@ -0,0 +1,289 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OUTPUT_FILE_H__ +#define __OUTPUT_FILE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + +class OutputFile +{ +public: + OutputFile(const Options& opts); + + + // iterates all atoms in initial files + void write(ld::Internal&); + bool findSegment(ld::Internal& state, uint64_t addr, uint64_t* start, uint64_t* end, uint32_t* index); + void setLazyBindingInfoOffset(uint64_t lpAddress, uint32_t lpInfoOffset); + uint32_t dylibCount(); + const ld::dylib::File* dylibByOrdinal(unsigned int ordinal); + uint32_t dylibToOrdinal(const ld::dylib::File*); + uint32_t encryptedTextStartOffset() { return _encryptedTEXTstartOffset; } + uint32_t encryptedTextEndOffset() { return _encryptedTEXTendOffset; } + int compressedOrdinalForAtom(const ld::Atom* target); + + + + bool hasWeakExternalSymbols; + bool usesWeakExternalSymbols; + bool overridesWeakExternalSymbols; + bool _noReExportedDylibs; + bool hasThreadLocalVariableDefinitions; + bool pieDisabled; + ld::Internal::FinalSection* headerAndLoadCommandsSection; + ld::Internal::FinalSection* rebaseSection; + ld::Internal::FinalSection* bindingSection; + ld::Internal::FinalSection* weakBindingSection; + ld::Internal::FinalSection* lazyBindingSection; + ld::Internal::FinalSection* exportSection; + ld::Internal::FinalSection* splitSegInfoSection; + ld::Internal::FinalSection* functionStartsSection; + ld::Internal::FinalSection* symbolTableSection; + ld::Internal::FinalSection* stringPoolSection; + ld::Internal::FinalSection* localRelocationsSection; + ld::Internal::FinalSection* externalRelocationsSection; + ld::Internal::FinalSection* sectionRelocationsSection; + ld::Internal::FinalSection* indirectSymbolTableSection; + + struct RebaseInfo { + RebaseInfo(uint8_t t, uint64_t addr) : _type(t), _address(addr) {} + uint8_t _type; + uint64_t _address; + // for sorting + int operator<(const RebaseInfo& rhs) const { + // sort by type, then address + if ( this->_type != rhs._type ) + return (this->_type < rhs._type ); + return (this->_address < rhs._address ); + } + }; + + struct BindingInfo { + BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t add) + : _type(t), _flags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), _libraryOrdinal(ord), + _symbolName(sym), _address(addr), _addend(add) {} + BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t add) + : _type(t), _flags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ), + _libraryOrdinal(0), _symbolName(sym), _address(addr), _addend(add) {} + uint8_t _type; + uint8_t _flags; + int _libraryOrdinal; + const char* _symbolName; + uint64_t _address; + int64_t _addend; + + // for sorting + int operator<(const BindingInfo& rhs) const { + // sort by library, symbol, type, then address + if ( this->_libraryOrdinal != rhs._libraryOrdinal ) + return (this->_libraryOrdinal < rhs._libraryOrdinal ); + if ( this->_symbolName != rhs._symbolName ) + return ( strcmp(this->_symbolName, rhs._symbolName) < 0 ); + if ( this->_type != rhs._type ) + return (this->_type < rhs._type ); + return (this->_address < rhs._address ); + } + }; + + struct SplitSegInfoEntry { + SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k) : address(a), kind(k) {} + uint64_t address; + ld::Fixup::Kind kind; + }; + +private: + void buildDylibOrdinalMapping(ld::Internal&); + bool hasOrdinalForInstallPath(const char* path, int* ordinal); + void addLoadCommands(ld::Internal& state); + void addLinkEdit(ld::Internal& state); + void addPreloadLinkEdit(ld::Internal& state); + void generateLinkEditInfo(ld::Internal& state); + void buildSymbolTable(ld::Internal& state); + void writeOutputFile(ld::Internal& state); + void assignFileOffsets(ld::Internal& state); + void setSectionSizesAndAlignments(ld::Internal& state); + void addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, + const ld::Atom* atom, ld::Fixup* fixupWithTarget, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend); + void addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect, + const ld::Atom* atom, ld::Fixup* fixupWithTarget, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend); + void addClassicRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, + const ld::Atom* atom, ld::Fixup* fixupWithTarget, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend); + bool useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, + ld::Fixup* fixupWithTarget); + uint64_t pageAlign(uint64_t addr); + uint64_t pageAlign(uint64_t addr, uint64_t pageSize); + void setLoadCommandsPadding(ld::Internal& state); + void assignAtomAddresses(ld::Internal& state); + void addRebaseInfo(const ld::Atom* atom, const ld::Fixup* fixup, const ld::Atom* target); + void makeRebasingInfo(ld::Internal& state); + void makeBindingInfo(ld::Internal& state); + void updateLINKEDITAddresses(ld::Internal& state); + void applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer); + 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); + bool isPointerToTarget(ld::Fixup::Kind kind); + bool isPointerFromTarget(ld::Fixup::Kind kind); + bool isPcRelStore(ld::Fixup::Kind kind); + bool isStore(ld::Fixup::Kind kind); + bool storeAddendOnly(const ld::Atom* inAtom, const ld::Atom* target, bool pcRel=false); + bool setsTarget(ld::Fixup::Kind kind); + void addFixupOutInfo(ld::Internal& state); + void makeRelocations(ld::Internal& state); + void makeSectionRelocations(ld::Internal& state); + void makeDyldInfo(ld::Internal& state); + void makeSplitSegInfo(ld::Internal& state); + void writeMapFile(ld::Internal& state); + uint64_t lookBackAddend(ld::Fixup::iterator fit); + bool takesNoDiskSpace(const ld::Section* sect); + bool hasZeroForFileOffset(const ld::Section* sect); + + void printSectionLayout(ld::Internal& state); + void rangeCheck8(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheck16(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckBranch32(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, + const ld::Fixup* fixup); + void rangeCheckARMBranch24(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckThumbBranch22(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckPPCBranch24(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckPPCBranch14(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); + uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); + void dumpAtomsBySection(ld::Internal& state, bool); + void synthesizeDebugNotes(ld::Internal& state); + const char* assureFullPath(const char* path); + void noteTextReloc(const ld::Atom* atom, const ld::Atom* target); + + + static uint16_t get16LE(uint8_t* loc); + static void set16LE(uint8_t* loc, uint16_t value); + static uint32_t get32LE(uint8_t* loc); + static void set32LE(uint8_t* loc, uint32_t value); + static uint64_t get64LE(uint8_t* loc); + static void set64LE(uint8_t* loc, uint64_t value); + + static uint16_t get16BE(uint8_t* loc); + static void set16BE(uint8_t* loc, uint16_t value); + static uint32_t get32BE(uint8_t* loc); + static void set32BE(uint8_t* loc, uint32_t value); + static uint64_t get64BE(uint8_t* loc); + static void set64BE(uint8_t* loc, uint64_t value); + + + + const Options& _options; + std::map _dylibToOrdinal; + std::vector _dylibsToLoad; + std::vector _dylibOrdinalPaths; + const bool _hasDyldInfo; + const bool _hasSymbolTable; + const bool _hasSectionRelocations; + const bool _hasSplitSegInfo; + const bool _hasFunctionStartsInfo; + bool _hasDynamicSymbolTable; + bool _hasLocalRelocations; + bool _hasExternalRelocations; + uint64_t _fileSize; + std::map _lazyPointerAddressToInfoOffset; + uint32_t _encryptedTEXTstartOffset; + uint32_t _encryptedTEXTendOffset; +public: + std::vector _localAtoms; + std::vector _exportedAtoms; + std::vector _importedAtoms; + uint32_t _localSymbolsStartIndex; + uint32_t _localSymbolsCount; + uint32_t _globalSymbolsStartIndex; + uint32_t _globalSymbolsCount; + uint32_t _importSymbolsStartIndex; + uint32_t _importSymbolsCount; + std::map _atomToSymbolIndex; + std::vector _rebaseInfo; + std::vector _bindingInfo; + std::vector _lazyBindingInfo; + std::vector _weakBindingInfo; + std::vector _splitSegInfos; + class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom; + class RelocationsAtomAbstract* _sectionsRelocationsAtom; + class RelocationsAtomAbstract* _localRelocsAtom; + class RelocationsAtomAbstract* _externalRelocsAtom; + class ClassicLinkEditAtom* _symbolTableAtom; + class ClassicLinkEditAtom* _indirectSymbolTableAtom; + class StringPoolAtom* _stringPoolAtom; + class LinkEditAtom* _rebasingInfoAtom; + class LinkEditAtom* _bindingInfoAtom; + class LinkEditAtom* _lazyBindingInfoAtom; + class LinkEditAtom* _weakBindingInfoAtom; + class LinkEditAtom* _exportInfoAtom; + class LinkEditAtom* _splitSegInfoAtom; + class LinkEditAtom* _functionStartsAtom; +}; + +} // namespace tool +} // namespace ld + +#endif // __OUTPUT_FILE_H__ diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp new file mode 100644 index 0000000..e17e632 --- /dev/null +++ b/ld64/src/ld/Resolver.cpp @@ -0,0 +1,1402 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-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@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Options.h" + +#include "ld.hpp" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Resolver.h" +#include "parsers/lto_file.h" + + +namespace ld { +namespace tool { + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +class UndefinedProxyAtom : public ld::Atom +{ +public: + UndefinedProxyAtom(const char* nm) + : ld::Atom(_s_section, ld::Atom::definitionProxy, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, + ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(0)), + _name(nm) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + + virtual ~UndefinedProxyAtom() {} + + const char* _name; + + static ld::Section _s_section; +}; + +ld::Section UndefinedProxyAtom::_s_section("__TEXT", "__import", ld::Section::typeImportProxies, true); + + + + +class AliasAtom : public ld::Atom +{ +public: + AliasAtom(const ld::Atom& target, const char* nm) : + ld::Atom(target.section(), target.definition(), ld::Atom::combineNever, + ld::Atom::scopeGlobal, target.contentType(), + target.symbolTableInclusion(), target.dontDeadStrip(), + target.isThumb(), true, target.alignment()), + _name(nm), + _aliasOf(target), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, &target) { } + + // overrides of ld::Atom + virtual const ld::File* file() const { return _aliasOf.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return _aliasOf.translationUnitSource(dir, nm); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return _aliasOf.objectAddress(); } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + { return _aliasOf.contentHash(ibt); } + virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + { return _aliasOf.canCoalesceWith(rhs,ibt); } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + virtual ld::Atom::UnwindInfo::iterator beginUnwind() const { return NULL; } + virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return NULL; } + virtual ld::Atom::LineInfo::iterator beginLineInfo() const { return NULL; } + virtual ld::Atom::LineInfo::iterator endLineInfo() const { return NULL; } + +private: + const char* _name; + const ld::Atom& _aliasOf; + ld::Fixup _fixup; +}; + + + +class SectionBoundaryAtom : public ld::Atom +{ +public: + static SectionBoundaryAtom* makeSectionBoundaryAtom(const char* name, bool start, const char* segSectName); + static SectionBoundaryAtom* makeOldSectionBoundaryAtom(const char* name, bool start); + + // overrides of ld::Atom + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual uint64_t objectAddress() const { return 0; } + +private: + + SectionBoundaryAtom(const char* nm, const ld::Section& sect, + ld::Atom::ContentType cont) : + ld::Atom(sect, + ld::Atom::definitionRegular, + ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, + cont, + ld::Atom::symbolTableNotIn, + false, false, true, ld::Atom::Alignment(0)), + _name(nm) { } + + const char* _name; +}; + +SectionBoundaryAtom* SectionBoundaryAtom::makeSectionBoundaryAtom(const char* name, bool start, const char* segSectName) +{ + + const char* segSectDividor = strrchr(segSectName, '$'); + if ( segSectDividor == NULL ) + throwf("malformed section$ symbol name: %s", name); + const char* sectionName = segSectDividor + 1; + int segNameLen = segSectDividor - segSectName; + if ( segNameLen > 16 ) + throwf("malformed section$ symbol name: %s", name); + char segName[18]; + strlcpy(segName, segSectName, segNameLen+1); + + const ld::Section* section = new ld::Section(strdup(segName), sectionName, ld::Section::typeUnclassified); + return new SectionBoundaryAtom(name, *section, (start ? ld::Atom::typeSectionStart : typeSectionEnd)); +} + +SectionBoundaryAtom* SectionBoundaryAtom::makeOldSectionBoundaryAtom(const char* name, bool start) +{ + // e.g. __DATA__bss__begin + char segName[18]; + strlcpy(segName, name, 7); + + char sectName[18]; + int nameLen = strlen(name); + strlcpy(sectName, &name[6], (start ? nameLen-12 : nameLen-10)); + warning("grandfathering in old symbol '%s' as alias for 'section$%s$%s$%s'", name, start ? "start" : "end", segName, sectName); + const ld::Section* section = new ld::Section(strdup(segName), strdup(sectName), ld::Section::typeUnclassified); + return new SectionBoundaryAtom(name, *section, (start ? ld::Atom::typeSectionStart : typeSectionEnd)); +} + + + + +class SegmentBoundaryAtom : public ld::Atom +{ +public: + static SegmentBoundaryAtom* makeSegmentBoundaryAtom(const char* name, bool start, const char* segName); + static SegmentBoundaryAtom* makeOldSegmentBoundaryAtom(const char* name, bool start); + + // overrides of ld::Atom + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual uint64_t objectAddress() const { return 0; } + +private: + + SegmentBoundaryAtom(const char* nm, const ld::Section& sect, + ld::Atom::ContentType cont) : + ld::Atom(sect, + ld::Atom::definitionRegular, + ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, + cont, + ld::Atom::symbolTableNotIn, + false, false, true, ld::Atom::Alignment(0)), + _name(nm) { } + + const char* _name; +}; + +SegmentBoundaryAtom* SegmentBoundaryAtom::makeSegmentBoundaryAtom(const char* name, bool start, const char* segName) +{ + if ( *segName == '\0' ) + throwf("malformed segment$ symbol name: %s", name); + if ( strlen(segName) > 16 ) + throwf("malformed segment$ symbol name: %s", name); + + if ( start ) { + const ld::Section* section = new ld::Section(segName, "__start", ld::Section::typeFirstSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionStart); + } + else { + const ld::Section* section = new ld::Section(segName, "__end", ld::Section::typeLastSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionEnd); + } +} + +SegmentBoundaryAtom* SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(const char* name, bool start) +{ + // e.g. __DATA__begin + char temp[18]; + strlcpy(temp, name, 7); + char* segName = strdup(temp); + + warning("grandfathering in old symbol '%s' as alias for 'segment$%s$%s'", name, start ? "start" : "end", segName); + + if ( start ) { + const ld::Section* section = new ld::Section(segName, "__start", ld::Section::typeFirstSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionStart); + } + else { + const ld::Section* section = new ld::Section(segName, "__end", ld::Section::typeLastSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionEnd); + } +} + +void Resolver::initializeState() +{ + // set initial objc constraint based on command line options + if ( _options.objcGc() ) + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( _options.objcGcOnly() ) + _internal.objcObjectConstraint = ld::File::objcConstraintGC; + + _internal.cpuSubType = _options.subArchitecture(); +} + +void Resolver::buildAtomList() +{ + // each input files contributes initial atoms + _atoms.reserve(1024); + _inputFiles.forEachInitialAtom(*this); + _completedInitialObjectFiles = true; + + //_symbolTable.printStatistics(); +} + +unsigned int Resolver::ppcSubTypeIndex(uint32_t subtype) +{ + switch ( subtype ) { + case CPU_SUBTYPE_POWERPC_ALL: + return 0; + case CPU_SUBTYPE_POWERPC_750: + // G3 + return 1; + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + // G4 + return 2; + case CPU_SUBTYPE_POWERPC_970: + // G5 can run everything + return 3; + default: + throw "Unhandled PPC cpu subtype!"; + break; + } +} + +void Resolver::doFile(const ld::File& file) +{ + const ld::relocatable::File* objFile = dynamic_cast(&file); + const ld::dylib::File* dylibFile = dynamic_cast(&file); + + if ( objFile != NULL ) { + // update which form of ObjC is being used + switch ( file.objCConstraint() ) { + case ld::File::objcConstraintNone: + break; + case ld::File::objcConstraintRetainRelease: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintGC ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); + if ( _options.objcGcOnly() ) + throwf("command line specified -objc_gc_only, but file is retain/release based: %s", file.path()); + if ( _options.objcGc() ) + throwf("command line specified -objc_gc, but file is retain/release based: %s", file.path()); + _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; + break; + case ld::File::objcConstraintRetainReleaseOrGC: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; + break; + case ld::File::objcConstraintGC: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintRetainRelease ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); + _internal.objcObjectConstraint = ld::File::objcConstraintGC; + break; + } + + // in -r mode, if any .o files have dwarf then add UUID to output .o file + if ( objFile->debugInfo() == ld::relocatable::File::kDebugInfoDwarf ) + _internal.someObjectFileHasDwarf = true; + + // remember if any objc classes built for fix-and-continue + if ( objFile->objcReplacementClasses() ) + _internal.hasObjcReplacementClasses = true; + + // remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set + if ( ! objFile->canScatterAtoms() ) + _internal.allObjectFilesScatterable = false; + + // update cpu-sub-type + cpu_subtype_t nextObjectSubType = file.cpuSubType(); + switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + // no checking when -force_cpusubtype_ALL is used + if ( _options.forceCpuSubtypeAll() ) + return; + if ( _options.preferSubArchitecture() ) { + // warn if some .o file is not compatible with desired output sub-type + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_options.subArchitecture()) ) { + if ( !_inputFiles.inferredArch() ) + warning("cpu-sub-type of %s is not compatible with command line cpu-sub-type", file.path()); + _internal.cpuSubType = nextObjectSubType; + } + } + } + else { + // command line to linker just had -arch ppc + // figure out final sub-type based on sub-type of all .o files + if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_internal.cpuSubType) ) { + _internal.cpuSubType = nextObjectSubType; + } + } + break; + + case CPU_TYPE_ARM: + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( (_options.subArchitecture() == CPU_SUBTYPE_ARM_ALL) && _options.forceCpuSubtypeAll() ) { + // hack to support gcc multillib build that tries to make sub-type-all slice + } + else if ( nextObjectSubType == CPU_SUBTYPE_ARM_ALL ) { + warning("CPU_SUBTYPE_ARM_ALL subtype is deprecated: %s", file.path()); + } + else { + throwf("object file %s was built for different arm sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + } + break; + + case CPU_TYPE_POWERPC64: + break; + + case CPU_TYPE_I386: + _internal.cpuSubType = CPU_SUBTYPE_I386_ALL; + break; + + case CPU_TYPE_X86_64: + _internal.cpuSubType = CPU_SUBTYPE_X86_64_ALL; + break; + } + } + if ( dylibFile != NULL ) { + // update which form of ObjC dylibs are being linked + switch ( dylibFile->objCConstraint() ) { + case ld::File::objcConstraintNone: + break; + case ld::File::objcConstraintRetainRelease: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintGC ) + throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); + if ( _options.objcGcOnly() ) + throwf("command line specified -objc_gc_only, but dylib is retain/release based: %s", file.path()); + if ( _options.objcGc() ) + throwf("command line specified -objc_gc, but dylib is retain/release based: %s", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintRetainRelease; + break; + case ld::File::objcConstraintRetainReleaseOrGC: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) + _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseOrGC; + break; + case ld::File::objcConstraintGC: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintRetainRelease ) + throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintGC; + break; + } + } + +} + +void Resolver::doAtom(const ld::Atom& atom) +{ + //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s\n", &atom, atom.name(), atom.section().sectionName()); + + // add to list of known atoms + _atoms.push_back(&atom); + + // adjust scope + if ( _options.hasExportRestrictList() || _options.hasReExportList() ) { + const char* name = atom.name(); + switch ( atom.scope() ) { + case ld::Atom::scopeTranslationUnit: + break; + case ld::Atom::scopeLinkageUnit: + if ( _options.hasExportMaskList() && _options.shouldExport(name) ) { + // ld does not report error when -r is used and exported symbols are not defined. + if ( _options.outputKind() == Options::kObjectFile ) + throwf("cannot export hidden symbol %s", name); + // .objc_class_name_* symbols are special + if ( atom.section().type() != ld::Section::typeObjC1Classes ) { + if ( atom.definition() == ld::Atom::definitionProxy ) { + // .exp file says to export a symbol, but that symbol is in some dylib being linked + if ( _options.canReExportSymbols() ) { + // marking proxy atom as global triggers the re-export + (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); + } + else { + if ( atom.file() != NULL ) + warning("cannot re-export symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path()); + else + warning("cannot re-export symbol %s\n", SymbolTable::demangle(name)); + } + } + else { + if ( atom.file() != NULL ) + warning("cannot export hidden symbol %s from %s", SymbolTable::demangle(name), atom.file()->path()); + else + warning("cannot export hidden symbol %s", SymbolTable::demangle(name)); + } + } + } + else if ( _options.shouldReExport(name) && _options.canReExportSymbols() ) { + if ( atom.definition() == ld::Atom::definitionProxy ) { + // marking proxy atom as global triggers the re-export + (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); + } + else { + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path()); + } + } + break; + case ld::Atom::scopeGlobal: + // check for globals that are downgraded to hidden + if ( ! _options.shouldExport(name) ) { + (const_cast(&atom))->setScope(ld::Atom::scopeLinkageUnit); + //fprintf(stderr, "demote %s to hidden\n", name); + } + if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) { + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path()); + } + break; + } + } + + // work around for kernel that uses 'l' labels in assembly code + if ( (atom.symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) + && (atom.name()[0] == 'l') && (_options.outputKind() == Options::kStaticExecutable) ) + (const_cast(&atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + + + // tell symbol table about non-static atoms + if ( atom.scope() != ld::Atom::scopeTranslationUnit ) { + _symbolTable.add(atom, _options.deadCodeStrip() && _completedInitialObjectFiles); + + // add symbol aliases defined on the command line + if ( _options.haveCmdLineAliases() ) { + const std::vector& aliases = _options.cmdLineAliases(); + for (std::vector::const_iterator it=aliases.begin(); it != aliases.end(); ++it) { + if ( strcmp(it->realName, atom.name()) == 0 ) { + const ld::Atom* alias = new AliasAtom(atom, it->alias); + this->doAtom(*alias); + } + } + } + } + + // convert references by-name or by-content to by-slot + this->convertReferencesToIndirect(atom); + + // remember if any atoms are proxies that require LTO + if ( atom.contentType() == ld::Atom::typeLTOtemporary ) + _haveLLVMObjs = true; + + // if we've already partitioned into final sections, and lto needs a symbol very late, add it + if ( _addToFinalSection ) + _internal.addAtom(atom); + + if ( _options.deadCodeStrip() ) { + // add to set of dead-strip-roots, all symbols that the compiler marks as don't strip + if ( atom.dontDeadStrip() ) + _deadStripRoots.insert(&atom); + + if ( atom.scope() == ld::Atom::scopeGlobal ) { + // -exported_symbols_list that has wildcards and -dead_strip + // in dylibs, every global atom in initial .o files is a root + if ( _options.hasWildCardExportRestrictList() || _options.allGlobalsAreDeadStripRoots() ) { + if ( _options.shouldExport(atom.name()) ) + _deadStripRoots.insert(&atom); + } + } + } +} + +bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) +{ + switch (kind) { + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + case ld::Fixup::kindDtraceExtra: + return true; + default: + break; + } + return false; +} + +void Resolver::convertReferencesToIndirect(const ld::Atom& atom) +{ + // convert references by-name or by-content to by-slot + SymbolTable::IndirectBindingSlot slot; + const ld::Atom* dummy; + ld::Fixup::iterator end = atom.fixupsEnd(); + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != end; ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + if ( isDtraceProbe(fit->kind) && (_options.outputKind() != Options::kObjectFile ) ) { + // in final linked images, remove reference + fit->binding = ld::Fixup::bindingNone; + } + else { + slot = _symbolTable.findSlotForName(fit->u.name); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + } + break; + case ld::Fixup::bindingByContentBound: + switch ( fit->u.target->combine() ) { + case ld::Atom::combineNever: + case ld::Atom::combineByName: + assert(0 && "wrong combine type for bind by content"); + break; + case ld::Atom::combineByNameAndContent: + slot = _symbolTable.findSlotForContent(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + case ld::Atom::combineByNameAndReferences: + slot = _symbolTable.findSlotForReferences(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + } + break; + case ld::Fixup::bindingNone: + case ld::Fixup::bindingDirectlyBound: + case ld::Fixup::bindingsIndirectlyBound: + break; + } + } +} + + +void Resolver::addInitialUndefines() +{ + // add initial undefines from -u option + for (Options::UndefinesIterator it=_options.initialUndefinesBegin(); it != _options.initialUndefinesEnd(); ++it) { + _symbolTable.findSlotForName(*it); + } +} + +void Resolver::resolveUndefines() +{ + // keep looping until no more undefines were added in last loop + unsigned int undefineGenCount = 0xFFFFFFFF; + while ( undefineGenCount != _symbolTable.updateCount() ) { + undefineGenCount = _symbolTable.updateCount(); + std::vector undefineNames; + _symbolTable.undefines(undefineNames); + for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + 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); + if ( !_symbolTable.hasName(undef) && (_options.outputKind() != Options::kObjectFile) ) { + if ( strncmp(undef, "section$", 8) == 0 ) { + if ( strncmp(undef, "section$start$", 14) == 0 ) { + this->doAtom(*SectionBoundaryAtom::makeSectionBoundaryAtom(undef, true, &undef[14])); + } + else if ( strncmp(undef, "section$end$", 12) == 0 ) { + this->doAtom(*SectionBoundaryAtom::makeSectionBoundaryAtom(undef, false, &undef[12])); + } + } + else if ( strncmp(undef, "segment$", 8) == 0 ) { + if ( strncmp(undef, "segment$start$", 14) == 0 ) { + this->doAtom(*SegmentBoundaryAtom::makeSegmentBoundaryAtom(undef, true, &undef[14])); + } + else if ( strncmp(undef, "segment$end$", 12) == 0 ) { + this->doAtom(*SegmentBoundaryAtom::makeSegmentBoundaryAtom(undef, false, &undef[12])); + } + } + else if ( _options.outputKind() == Options::kPreload ) { + // for iBoot grandfather in old style section labels + int undefLen = strlen(undef); + if ( strcmp(&undef[undefLen-7], "__begin") == 0 ) { + if ( undefLen > 13 ) + this->doAtom(*SectionBoundaryAtom::makeOldSectionBoundaryAtom(undef, true)); + else + this->doAtom(*SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(undef, true)); + } + else if ( strcmp(&undef[undefLen-5], "__end") == 0 ) { + if ( undefLen > 11 ) + this->doAtom(*SectionBoundaryAtom::makeOldSectionBoundaryAtom(undef, false)); + else + this->doAtom(*SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(undef, false)); + } + } + } + } + } + // need to search archives for overrides of common symbols + if ( _symbolTable.hasExternalTentativeDefinitions() ) { + bool searchDylibs = (_options.commonsMode() == Options::kCommonsOverriddenByDylibs); + std::vector tents; + _symbolTable.tentativeDefs(tents); + for(std::vector::iterator it = tents.begin(); it != tents.end(); ++it) { + // load for previous tentative may also have loaded this tentative, so check again + const ld::Atom* curAtom = _symbolTable.atomForSlot(_symbolTable.findSlotForName(*it)); + assert(curAtom != NULL); + if ( curAtom->definition() == ld::Atom::definitionTentative ) { + _inputFiles.searchLibraries(*it, searchDylibs, true, *this); + } + } + } + } + + // create proxies as needed for undefined symbols + if ( (_options.undefinedTreatment() != Options::kUndefinedError) || (_options.outputKind() == Options::kObjectFile) ) { + std::vector undefineNames; + _symbolTable.undefines(undefineNames); + for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + // make proxy + this->doAtom(*new UndefinedProxyAtom(*it)); + } + } + + // support -U option + if ( _options.someAllowedUndefines() ) { + std::vector undefineNames; + _symbolTable.undefines(undefineNames); + for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + if ( _options.allowedUndefined(*it) ) { + // make proxy + this->doAtom(*new UndefinedProxyAtom(*it)); + } + } + } + +} + + +void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) +{ + //fprintf(stderr, "markLive(%p) %s\n", &atom, atom.name()); + // if -why_live cares about this symbol, then dump chain + if ( (previous->referer != NULL) && _options.printWhyLive(atom.name()) ) { + fprintf(stderr, "%s from %s\n", atom.name(), atom.file()->path()); + int depth = 1; + for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { + for(int i=depth; i > 0; --i) + fprintf(stderr, " "); + fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->file()->path()); + } + } + + // if already marked live, then done (stop recursion) + if ( atom.live() ) + return; + + // mark this atom is live + (const_cast(&atom))->setLive(); + + // mark all atoms it references as live + WhyLiveBackChain thisChain; + thisChain.previous = previous; + thisChain.referer = &atom; + for (ld::Fixup::iterator fit = atom.fixupsBegin(), end=atom.fixupsEnd(); fit != end; ++fit) { + const ld::Atom* target; + switch ( fit->kind ) { + case ld::Fixup::kindNone: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindSubtractTargetAddress: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + // normally this was done in convertReferencesToIndirect() + // but a archive loaded .o file may have a forward reference + SymbolTable::IndirectBindingSlot slot; + const ld::Atom* dummy; + switch ( fit->u.target->combine() ) { + case ld::Atom::combineNever: + case ld::Atom::combineByName: + assert(0 && "wrong combine type for bind by content"); + break; + case ld::Atom::combineByNameAndContent: + slot = _symbolTable.findSlotForContent(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + case ld::Atom::combineByNameAndReferences: + slot = _symbolTable.findSlotForReferences(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + } + } + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + markLive(*(fit->u.target), &thisChain); + break; + case ld::Fixup::bindingByNameUnbound: + // doAtom() did not convert to indirect in dead-strip mode, so that now + fit->u.bindingIndex = _symbolTable.findSlotForName(fit->u.name); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + // fall into next case + case ld::Fixup::bindingsIndirectlyBound: + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + if ( target == NULL ) { + const char* targetName = _symbolTable.indirectName(fit->u.bindingIndex); + _inputFiles.searchLibraries(targetName, true, true, *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); + // recompute target since it may have been overridden by searchLibraries() + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + } + this->markLive(*target, &thisChain); + } + else { + _atomsWithUnresolvedReferences.push_back(&atom); + } + break; + default: + assert(0 && "bad binding during dead stripping"); + } + break; + default: + break; + } + } + +} + + +void Resolver::deadStripOptimize() +{ + // only do this optimization with -dead_strip + if ( ! _options.deadCodeStrip() ) + return; + + // add entry point (main) to live roots + const ld::Atom* entry = this->entryPoint(true); + if ( entry != NULL ) + _deadStripRoots.insert(entry); + + // add -exported_symbols_list, -init, and -u entries to live roots + 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); + } + if ( _internal.indirectBindingTable[slot] != NULL ) + _deadStripRoots.insert(_internal.indirectBindingTable[slot]); + } + + // this helper is only referenced by synthesize stubs, assume it will be used + if ( _internal.classicBindingHelper != NULL ) + _deadStripRoots.insert(_internal.classicBindingHelper); + + // this helper is only referenced by synthesize stubs, assume it will be used + if ( _internal.compressedFastBinderProxy != NULL ) + _deadStripRoots.insert(_internal.compressedFastBinderProxy); + + // this helper is only referenced by synthesized lazy stubs, assume it will be used + if ( _internal.lazyBindingHelper != NULL ) + _deadStripRoots.insert(_internal.lazyBindingHelper); + + // add all dont-dead-strip atoms as roots + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->dontDeadStrip() ) { + //fprintf(stderr, "dont dead strip: %p %s %s\n", atom, atom->section().sectionName(), atom->name()); + _deadStripRoots.insert(atom); + // unset liveness, so markLive() will recurse + (const_cast(atom))->setLive(0); + } + } + + // mark all roots as live, and all atoms they reference + for (std::set::iterator it=_deadStripRoots.begin(); it != _deadStripRoots.end(); ++it) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.referer = *it; + this->markLive(**it, &rootChain); + } + + // now remove all non-live atoms from _atoms + const bool log = false; + if ( log ) { + fprintf(stderr, "deadStripOptimize() all atoms with liveness:\n"); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name()); + } + } + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); +} + + +void Resolver::liveUndefines(std::vector& undefs) +{ + StringSet undefSet; + // 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()); + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( (ld::Fixup::TargetBinding)fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + assert(0 && "should not be by-name this late"); + undefSet.insert(fit->u.name); + break; + case ld::Fixup::bindingsIndirectlyBound: + if ( _internal.indirectBindingTable[fit->u.bindingIndex] == NULL ) { + undefSet.insert(_symbolTable.indirectName(fit->u.bindingIndex)); + } + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingNone: + case ld::Fixup::bindingDirectlyBound: + break; + } + } + } + // look for any initial undefines that are still undefined + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + if ( ! _symbolTable.hasName(*uit) ) { + undefSet.insert(*uit); + } + } + + // copy set to vector + for (StringSet::const_iterator it=undefSet.begin(); it != undefSet.end(); ++it) { + undefs.push_back(*it); + } +} + + + +// warn when .objc_class_name_* symbol missing +class ExportedObjcClass +{ +public: + ExportedObjcClass(const Options& opt) : _options(opt) {} + + bool operator()(const char* name) const { + if ( (strncmp(name, ".objc_class_name_", 17) == 0) && _options.shouldExport(name) ) { + warning("ignoring undefined symbol %s from -exported_symbols_list", name); + return true; + } + const char* s = strstr(name, "CLASS_$_"); + if ( s != NULL ) { + char temp[strlen(name)+16]; + strcpy(temp, ".objc_class_name_"); + strcat(temp, &s[8]); + if ( _options.wasRemovedExport(temp) ) { + warning("ignoring undefined symbol %s from -exported_symbols_list", temp); + return true; + } + } + return false; + } +private: + const Options& _options; +}; + + +// temp hack for undefined aliases +class UndefinedAlias +{ +public: + UndefinedAlias(const Options& opt) : _aliases(opt.cmdLineAliases()) {} + + bool operator()(const char* name) const { + for (std::vector::const_iterator it=_aliases.begin(); it != _aliases.end(); ++it) { + if ( strcmp(it->realName, name) == 0 ) { + warning("undefined base symbol '%s' for alias '%s'", name, it->alias); + return true; + } + } + return false; + } +private: + const std::vector& _aliases; +}; + + + +static const char* pathLeafName(const char* path) +{ + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + return path; + else + return &shortPath[1]; +} + +bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot) +{ + unsigned foundReferenceCount = 0; + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingsIndirectlyBound ) { + if ( fit->u.bindingIndex == slot ) { + if ( atom->contentType() == ld::Atom::typeNonLazyPointer ) { + const ld::Atom* existingAtom; + unsigned int nlSlot = _symbolTable.findSlotForReferences(atom, &existingAtom); + if ( printReferencedBy(name, nlSlot) ) + ++foundReferenceCount; + } + else if ( atom->contentType() == ld::Atom::typeCFI ) { + fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->file()->path())); + ++foundReferenceCount; + } + else { + fprintf(stderr, " %s in %s\n", SymbolTable::demangle(atom->name()), pathLeafName(atom->file()->path())); + ++foundReferenceCount; + break; // if undefined used twice in a function, only show first + } + } + } + } + if ( foundReferenceCount > 6 ) { + fprintf(stderr, " ...\n"); + break; // only show first six uses of undefined symbol + } + } + return (foundReferenceCount != 0); +} + +void Resolver::checkUndefines(bool force) +{ + // when using LTO, undefines are checked after bitcode is optimized + if ( _haveLLVMObjs && !force ) + return; + + // error out on any remaining undefines + bool doPrint = true; + bool doError = true; + switch ( _options.undefinedTreatment() ) { + case Options::kUndefinedError: + break; + case Options::kUndefinedDynamicLookup: + doError = false; + break; + case Options::kUndefinedWarning: + doError = false; + break; + case Options::kUndefinedSuppress: + doError = false; + doPrint = false; + break; + } + std::vector unresolvableUndefines; + if ( _options.deadCodeStrip() ) + this->liveUndefines(unresolvableUndefines); + else + _symbolTable.undefines(unresolvableUndefines); + + // assert when .objc_class_name_* symbol missing + if ( _options.hasExportMaskList() ) { + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(_options)), unresolvableUndefines.end()); + } + + // hack to temporarily make missing aliases a warning + if ( _options.haveCmdLineAliases() ) { + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), UndefinedAlias(_options)), unresolvableUndefines.end()); + } + + const int unresolvableCount = unresolvableUndefines.size(); + int unresolvableExportsCount = 0; + if ( unresolvableCount != 0 ) { + if ( doPrint ) { + if ( _options.printArchPrefix() ) + fprintf(stderr, "Undefined symbols for architecture %s:\n", _options.architectureName()); + else + fprintf(stderr, "Undefined symbols:\n"); + for (int i=0; i < unresolvableCount; ++i) { + const char* name = unresolvableUndefines[i]; + unsigned int slot = _symbolTable.findSlotForName(name); + fprintf(stderr, " \"%s\", referenced from:\n", SymbolTable::demangle(name)); + // scan all atoms for references + bool foundAtomReference = printReferencedBy(name, slot); + // scan command line options + if ( !foundAtomReference ) { + // might be from -init command line option + if ( (_options.initFunctionName() != NULL) && (strcmp(name, _options.initFunctionName()) == 0) ) { + fprintf(stderr, " -init command line option\n"); + } + // or might be from exported symbol option + else if ( _options.hasExportMaskList() && _options.shouldExport(name) ) { + fprintf(stderr, " -exported_symbol[s_list] command line option\n"); + } + // or might be from re-exported symbol option + else if ( _options.hasReExportList() && _options.shouldReExport(name) ) { + fprintf(stderr, " -reexported_symbols_list command line option\n"); + } + else { + bool isInitialUndefine = false; + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + if ( strcmp(*uit, name) == 0 ) { + isInitialUndefine = true; + break; + } + } + if ( isInitialUndefine ) + fprintf(stderr, " -u command line option\n"); + } + ++unresolvableExportsCount; + } + // be helpful and check for typos + 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 ( ! printedStart ) { + fprintf(stderr, " (maybe you meant: %s", atom->name()); + printedStart = true; + } + else { + fprintf(stderr, ", %s ", atom->name()); + } + } + } + if ( printedStart ) + fprintf(stderr, ")\n"); + } + } + if ( doError ) + throw "symbol(s) not found"; + } + +} + + + +void Resolver::checkDylibSymbolCollisions() +{ + for (SymbolTable::byNameIterator it=_symbolTable.begin(); it != _symbolTable.end(); it++) { + const ld::Atom* atom = *it; + if ( atom == NULL ) + continue; + if ( atom->scope() == ld::Atom::scopeGlobal ) { + // 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); + } + // record any overrides of weak symbols in any linked dylib + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) ) { + if ( _inputFiles.searchWeakDefInDylib(atom->name()) ) + (const_cast(atom))->setOverridesDylibsWeakDef(); + } + } + } +} + + +const ld::Atom* Resolver::entryPoint(bool searchArchives) +{ + const char* symbolName = NULL; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + symbolName = _options.entryName(); + break; + case Options::kDynamicLibrary: + symbolName = _options.initFunctionName(); + break; + case Options::kObjectFile: + case Options::kDynamicBundle: + case Options::kKextBundle: + return NULL; + break; + } + if ( symbolName != NULL ) { + 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); + } + if ( _internal.indirectBindingTable[slot] == NULL ) { + if ( strcmp(symbolName, "start") == 0 ) + throwf("entry point (%s) undefined. Usually in crt1.o", symbolName); + else + throwf("entry point (%s) undefined.", symbolName); + } + return _internal.indirectBindingTable[slot]; + } + return NULL; +} + + +void Resolver::fillInHelpersInInternalState() +{ + // look up well known atoms + bool needsStubHelper = true; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + needsStubHelper = true; + break; + case Options::kDyld: + case Options::kKextBundle: + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + needsStubHelper = false; + break; + } + + _internal.classicBindingHelper = NULL; + if ( needsStubHelper && !_options.makeCompressedDyldInfo() ) { + // "dyld_stub_binding_helper" comes from .o file, so should already exist in symbol table + if ( _symbolTable.hasName("dyld_stub_binding_helper") ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binding_helper"); + _internal.classicBindingHelper = _internal.indirectBindingTable[slot]; + } + } + + _internal.lazyBindingHelper = NULL; + if ( _options.usingLazyDylibLinking() ) { + // "dyld_lazy_dylib_stub_binding_helper" comes from lazydylib1.o file, so should already exist in symbol table + if ( _symbolTable.hasName("dyld_lazy_dylib_stub_binding_helper") ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_lazy_dylib_stub_binding_helper"); + _internal.lazyBindingHelper = _internal.indirectBindingTable[slot]; + } + if ( _internal.lazyBindingHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + } + + _internal.compressedFastBinderProxy = NULL; + 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); + } + if ( _symbolTable.hasName("dyld_stub_binder") ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binder"); + _internal.compressedFastBinderProxy = _internal.indirectBindingTable[slot]; + } + if ( _internal.compressedFastBinderProxy == NULL ) { + if ( _options.undefinedTreatment() != Options::kUndefinedError ) { + // make proxy + _internal.compressedFastBinderProxy = new UndefinedProxyAtom("dyld_stub_binder"); + this->doAtom(*_internal.compressedFastBinderProxy); + } + else { + warning("symbol dyld_stub_binder not found, normally in libSystem.dylib"); + } + } + } +} + + +void Resolver::fillInInternalState() +{ + // store atoms into their final section + for (std::vector::iterator it = _atoms.begin(); it != _atoms.end(); ++it) { + _internal.addAtom(**it); + } + + // make sure there is a __text section so that codesigning works + if ( (_options.outputKind() == Options::kDynamicLibrary) || (_options.outputKind() == Options::kDynamicBundle) ) + _internal.getFinalSection(ld::Section("__TEXT", "__text", ld::Section::typeCode)); + + // add entry point + _internal.entryPoint = this->entryPoint(true); +} + + + +void Resolver::removeCoalescedAwayAtoms() +{ + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), AtomCoalescedAway()), _atoms.end()); +} + +void Resolver::linkTimeOptimize() +{ + // only do work here if some llvm obj files where loaded + if ( ! _haveLLVMObjs ) + return; + + // run LLVM lto code-gen + lto::OptimizeOptions optOpt; + optOpt.outputFilePath = _options.outputFilePath(); + optOpt.tmpObjectFilePath = _options.tempLtoObjectPath(); + optOpt.allGlobalsAReDeadStripRoots = _options.allGlobalsAreDeadStripRoots(); + optOpt.verbose = _options.verbose(); + optOpt.saveTemps = _options.saveTempFiles(); + optOpt.pie = _options.positionIndependentExecutable(); + optOpt.mainExecutable = _options.linkingMainExecutable();; + optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable); + optOpt.relocatable = (_options.outputKind() == Options::kObjectFile); + optOpt.allowTextRelocs = _options.allowTextRelocs(); + optOpt.linkerDeadStripping = _options.deadCodeStrip(); + optOpt.arch = _options.architecture(); + optOpt.llvmOptions = &_options.llvmOptions(); + + std::vector newAtoms; + std::vector additionalUndefines; + if ( ! lto::optimize(_atoms, _internal, _inputFiles.nextInputOrdinal(), optOpt, *this, newAtoms, additionalUndefines) ) + return; // if nothing done + + + // add all newly created atoms to _atoms and update symbol table + for(std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) + this->doAtom(**it); + + // some atoms might have been optimized way (marked coalesced), remove them + this->removeCoalescedAwayAtoms(); + + // add new atoms into their final section + for (std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) { + _internal.addAtom(**it); + } + + // remove temp lto section and move all of its atoms to their final section + ld::Internal::FinalSection* tempLTOsection = NULL; + for (std::vector::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeTempLTO ) { + tempLTOsection = sect; + // remove temp lto section from final image + _internal.sections.erase(sit); + break; + } + } + // lto atoms now have proper section info, so add to final section + if ( tempLTOsection != NULL ) { + for (std::vector::iterator ait=tempLTOsection->atoms.begin(); ait != tempLTOsection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( ! atom->coalescedAway() ) { + this->convertReferencesToIndirect(*atom); + _internal.addAtom(*atom); + } + } + } + + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) + _addToFinalSection = true; + for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { + const char *targetName = *uit; + // these symbols may or may not already be in linker's symbol table + if ( ! _symbolTable.hasName(targetName) ) { + _inputFiles.searchLibraries(targetName, true, true, *this); + } + } + _addToFinalSection = false; + + // if -dead_strip on command line + if ( _options.deadCodeStrip() ) { + // clear liveness bit + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + (const_cast(*it))->setLive((*it)->dontDeadStrip()); + } + // and re-compute dead code + this->deadStripOptimize(); + + // remove newly dead atoms from each section + for (std::vector::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NotLive()), sect->atoms.end()); + } + } + + if ( _options.outputKind() == Options::kObjectFile ) { + // if -r mode, add proxies for new undefines (e.g. ___stack_chk_fail) + _addToFinalSection = true; + this->resolveUndefines(); + _addToFinalSection = false; + } + else { + // last chance to check for undefines + this->checkUndefines(true); + + // check new code does not override some dylib + this->checkDylibSymbolCollisions(); + } +} + + +void Resolver::resolve() +{ + this->initializeState(); + this->buildAtomList(); + this->addInitialUndefines(); + this->fillInHelpersInInternalState(); + this->resolveUndefines(); + this->deadStripOptimize(); + this->checkUndefines(); + this->checkDylibSymbolCollisions(); + this->removeCoalescedAwayAtoms(); + this->fillInInternalState(); + this->linkTimeOptimize(); +} + + + +} // namespace tool +} // namespace ld + + + diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h new file mode 100644 index 0000000..1202fc7 --- /dev/null +++ b/ld64/src/ld/Resolver.h @@ -0,0 +1,148 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __RESOLVER_H__ +#define __RESOLVER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Options.h" +#include "ld.hpp" +#include "SymbolTable.h" + + +namespace ld { +namespace tool { + + + + +class Resolver : public ld::File::AtomHandler +{ +public: + Resolver(const Options& opts, const InputFiles& inputs, ld::Internal& state) + : _options(opts), _inputFiles(inputs), _internal(state), + _symbolTable(opts, state.indirectBindingTable), + _haveLLVMObjs(false), _addToFinalSection(false), + _completedInitialObjectFiles(false) {} + + + virtual void doAtom(const ld::Atom&); + virtual void doFile(const class File&); + + void resolve(); + + +private: + struct WhyLiveBackChain + { + WhyLiveBackChain* previous; + const ld::Atom* referer; + }; + + void initializeState(); + void buildAtomList(); + void addInitialUndefines(); + void deadStripOptimize(); + void resolveUndefines(); + void checkUndefines(bool force=false); + void checkDylibSymbolCollisions(); + void tentativeOverrideOfDylib(ld::Atom&); + void fillInInternalState(); + void fillInHelpersInInternalState(); + void removeCoalescedAwayAtoms(); + void linkTimeOptimize(); + void convertReferencesToIndirect(const ld::Atom& atom); + const ld::Atom* entryPoint(bool searchArchives); + void markLive(const ld::Atom& atom, WhyLiveBackChain* previous); + bool isDtraceProbe(ld::Fixup::Kind kind); + void liveUndefines(std::vector&); + static unsigned int ppcSubTypeIndex(uint32_t subtype); + bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); + + + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + + class NotLive { + public: + bool operator()(const ld::Atom* atom) const { + return ! (atom->live() || atom->dontDeadStrip()); + } + }; + + class AtomCoalescedAway { + public: + bool operator()(const ld::Atom* atom) const { + return atom->coalescedAway(); + } + }; + + const Options& _options; + const InputFiles& _inputFiles; + ld::Internal& _internal; + std::vector _atoms; + std::set _deadStripRoots; + std::vector _atomsWithUnresolvedReferences; + SymbolTable _symbolTable; + bool _haveLLVMObjs; + bool _addToFinalSection; + bool _completedInitialObjectFiles; +}; + + +class DeadStripResolver +{ +public: + + +private: + +}; + +} // namespace tool +} // namespace ld + + + +#endif // __RESOLVER_H__ diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp new file mode 100644 index 0000000..f9ed04c --- /dev/null +++ b/ld64/src/ld/SymbolTable.cpp @@ -0,0 +1,721 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-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@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Options.h" + +#include "ld.hpp" +#include "InputFiles.h" +#include "SymbolTable.h" + + + +namespace ld { +namespace tool { + + +// HACK, I can't find a way to pass values in the compare classes (e.g. ContentFuncs) +// so use global variable to pass info. +static ld::IndirectBindingTable* _s_indirectBindingTable = NULL; +bool SymbolTable::_s_doDemangle = false; + + +SymbolTable::SymbolTable(const Options& opts, std::vector& ibt) + : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) +{ + _s_indirectBindingTable = this; + _s_doDemangle = _options.demangleSymbols(); +} + + +size_t SymbolTable::ContentFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::ContentFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + return (memcmp(left->rawContentPointer(), right->rawContentPointer(), left->size()) == 0); +} + + + +size_t SymbolTable::CStringHashFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::CStringHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + return (strcmp((char*)left->rawContentPointer(), (char*)right->rawContentPointer()) == 0); +} + + +size_t SymbolTable::UTF16StringHashFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::UTF16StringHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + if ( left == right ) + return true; + const void* leftContent = left->rawContentPointer(); + const void* rightContent = right->rawContentPointer(); + unsigned int amount = left->size()-2; + bool result = (memcmp(leftContent, rightContent, amount) == 0); + return result; +} + + +size_t SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + return left->canCoalesceWith(*right, *_s_indirectBindingTable); +} + + + +bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) +{ + bool useNew = true; + bool checkVisibilityMismatch = false; + assert(newAtom.name() != NULL); + const char* name = newAtom.name(); + IndirectBindingSlot slot = this->findSlotForName(name); + const ld::Atom* existingAtom = _indirectBindingTable[slot]; + //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + assert(&newAtom != existingAtom); + switch ( existingAtom->definition() ) { + case ld::Atom::definitionRegular: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + if ( existingAtom->combine() == ld::Atom::combineByName ) { + if ( newAtom.combine() == ld::Atom::combineByName ) { + // both weak, prefer non-auto-hide one + if ( newAtom.autoHide() != existingAtom->autoHide() ) { + // support auto hidden weak symbols: .weak_def_can_be_hidden + useNew = existingAtom->autoHide(); + // don't check for visibility mismatch + } + else if ( newAtom.autoHide() && existingAtom->autoHide() ) { + // both have auto-hide, so use one with greater alignment + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + else { + // neither auto-hide, check visibility + if ( newAtom.scope() != existingAtom->scope() ) { + // use more visible weak def symbol + useNew = (newAtom.scope() == ld::Atom::scopeGlobal); + } + else { + // both have same visibility, use one with greater alignment + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + } + } + else { + // existing weak, new is not-weak + useNew = true; + } + } + else { + if ( newAtom.combine() == ld::Atom::combineByName ) { + // existing not-weak, new is weak + useNew = false; + } + else { + // existing not-weak, new is not-weak + if ( ignoreDuplicates ) { + useNew = false; + static bool fullWarning = false; + if ( ! fullWarning ) { + warning("-dead_strip with lazy loaded static (library) archives " + "has resulted in a duplicate symbol. You can change your " + "source code to rename symbols to avoid the collision. " + "This will be an error in a future linker."); + fullWarning = true; + } + warning("duplicate symbol %s originally in %s now lazily loaded from %s", + SymbolTable::demangle(name), existingAtom->file()->path(), newAtom.file()->path()); + } + else { + throwf("duplicate symbol %s in %s and %s", + SymbolTable::demangle(name), newAtom.file()->path(), existingAtom->file()->path()); + } + } + } + break; + case ld::Atom::definitionTentative: + // ignore new tentative atom, because we already have a regular one + useNew = false; + checkVisibilityMismatch = true; + if ( newAtom.size() > existingAtom->size() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "is smaller than the real definition of size %llu from %s", + newAtom.name(), newAtom.size(), newAtom.file()->path(), + existingAtom->size(), existingAtom->file()->path()); + } + break; + case ld::Atom::definitionAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + case ld::Atom::definitionProxy: + // ignore external atom, because we already have a one + useNew = false; + break; + } + break; + case ld::Atom::definitionTentative: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + // replace existing tentative atom with regular one + checkVisibilityMismatch = true; + if ( newAtom.size() < existingAtom->size() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s", + newAtom.name(), existingAtom->size(), existingAtom->file()->path(), + newAtom.size(), newAtom.file()->path()); + } + break; + case ld::Atom::definitionTentative: + // new and existing are both tentative definitions, use largest + checkVisibilityMismatch = true; + if ( newAtom.size() < existingAtom->size() ) { + useNew = false; + } + else { + if ( newAtom.alignment().trailingZeros() < existingAtom->alignment().trailingZeros() ) + warning("alignment lost in merging tentative definition %s", newAtom.name()); + } + break; + case ld::Atom::definitionAbsolute: + // replace tentative with absolute + useNew = true; + break; + case ld::Atom::definitionProxy: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( _options.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( _options.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); + useNew = false; + break; + case Options::kCommonsOverriddenByDylibs: + if ( _options.warnCommons() ) + warning("replacing common symbol %s from %s with true definition from dylib %s", + existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); + } + break; + } + break; + case ld::Atom::definitionAbsolute: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + case ld::Atom::definitionTentative: + // ignore new tentative atom, because we already have a regular one + useNew = false; + break; + case ld::Atom::definitionAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + case ld::Atom::definitionProxy: + // ignore external atom, because we already have a one + useNew = false; + break; + } + break; + case ld::Atom::definitionProxy: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + // replace external atom with regular one + useNew = true; + break; + case ld::Atom::definitionTentative: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( _options.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( _options.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + newAtom.name(), newAtom.file()->path(), existingAtom->file()->path()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( _options.warnCommons() ) + warning("replacing defintion of %s from dylib %s with common symbol from %s", + newAtom.name(), existingAtom->file()->path(), newAtom.file()->path()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.name(), newAtom.file()->path(), existingAtom->file()->path()); + } + break; + case ld::Atom::definitionAbsolute: + // replace external atom with absolute one + useNew = true; + break; + case ld::Atom::definitionProxy: + // ld should keep looking when it finds a weak definition in a dylib + if ( newAtom.combine() == ld::Atom::combineByName ) { + useNew = false; + } + else { + if ( existingAtom->combine() == ld::Atom::combineByName ) + useNew = true; + else + throwf("symbol %s exported from both %s and %s\n", name, newAtom.file()->path(), existingAtom->file()->path()); + } + break; + } + break; + } + } + if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.scope() != existingAtom->scope()) ) { + warning("%s has different visibility (%s) in %s and (%s) in %s", + SymbolTable::demangle(newAtom.name()), (newAtom.scope() == 1 ? "hidden" : "default"), newAtom.file()->path(), (existingAtom->scope() == 1 ? "hidden" : "default"), existingAtom->file()->path()); + } + if ( useNew ) { + _indirectBindingTable[slot] = &newAtom; + if ( existingAtom != NULL ) { + markCoalescedAway(existingAtom); +// if ( fOwner.fInitialLoadsDone ) { +// //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->name(), &newAtom); +// fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom); +// } + } + if ( newAtom.scope() == ld::Atom::scopeGlobal ) { + if ( newAtom.definition() == ld::Atom::definitionTentative ) { + _hasExternalTentativeDefinitions = true; + } + } + } + else { + markCoalescedAway(&newAtom); + } + // return if existing atom in symbol table was replaced + return useNew && (existingAtom != NULL); +} + + +bool SymbolTable::addByContent(const ld::Atom& newAtom) +{ + bool useNew = true; + const ld::Atom* existingAtom; + IndirectBindingSlot slot = this->findSlotForContent(&newAtom, &existingAtom); + //fprintf(stderr, "addByContent(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + // use existing unless new one has greater alignment requirements + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + if ( useNew ) { + _indirectBindingTable[slot] = &newAtom; + if ( existingAtom != NULL ) + markCoalescedAway(existingAtom); + } + else { + _indirectBindingTable[slot] = existingAtom; + if ( existingAtom != &newAtom ) + markCoalescedAway(&newAtom); + } + // return if existing atom in symbol table was replaced + return useNew && (existingAtom != NULL); +} + +bool SymbolTable::addByReferences(const ld::Atom& newAtom) +{ + bool useNew = true; + const ld::Atom* existingAtom; + IndirectBindingSlot slot = this->findSlotForReferences(&newAtom, &existingAtom); + //fprintf(stderr, "addByReferences(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + // use existing unless new one has greater alignment requirements + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + if ( useNew ) { + _indirectBindingTable[slot] = &newAtom; + if ( existingAtom != NULL ) + markCoalescedAway(existingAtom); + } + else { + if ( existingAtom != &newAtom ) + markCoalescedAway(&newAtom); + } + // return if existing atom in symbol table was replaced + return useNew && (existingAtom != NULL); +} + + +bool SymbolTable::add(const ld::Atom& atom, bool ignoreDuplicates) +{ + //fprintf(stderr, "SymbolTable::add(%p), name=%s\n", &atom, atom.name()); + assert(atom.scope() != ld::Atom::scopeTranslationUnit); + switch ( atom.combine() ) { + case ld::Atom::combineNever: + case ld::Atom::combineByName: + return this->addByName(atom, ignoreDuplicates); + break; + case ld::Atom::combineByNameAndContent: + return this->addByContent(atom); + break; + case ld::Atom::combineByNameAndReferences: + return this->addByReferences(atom); + break; + } + + return false; +} + +void SymbolTable::markCoalescedAway(const ld::Atom* atom) +{ + // remove this from list of all atoms used + //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->file()->path()); + (const_cast(atom))->setCoalescedAway(); + + // + // The fixupNoneGroupSubordinate* fixup kind is used to model group comdat. + // The "signature" atom in the group has a fixupNoneGroupSubordinate* fixup to + // all other members of the group. So, if the signature atom is + // coalesced away, all other atoms in the group should also be removed. + // + for (ld::Fixup::iterator fit=atom->fixupsBegin(), fend=atom->fixupsEnd(); fit != fend; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + this->markCoalescedAway(fit->u.target); + break; + default: + break; + } + } + +} + +void SymbolTable::undefines(std::vector& undefs) +{ + // return all names in _byNameTable that have no associated atom + for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { + //fprintf(stderr, " _byNameTable[%s] = slot %d which has atom %p\n", it->first, it->second, _indirectBindingTable[it->second]); + if ( _indirectBindingTable[it->second] == NULL ) + undefs.push_back(it->first); + } + // sort so that undefines are in a stable order (not dependent on hashing functions) + std::sort(undefs.begin(), undefs.end()); +} + + +void SymbolTable::tentativeDefs(std::vector& tents) +{ + // return all names in _byNameTable that have no associated atom + for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { + const char* name = it->first; + const ld::Atom* atom = _indirectBindingTable[it->second]; + if ( (atom != NULL) && (atom->definition() == ld::Atom::definitionTentative) ) + tents.push_back(name); + } + std::sort(tents.begin(), tents.end()); +} + + +bool SymbolTable::hasName(const char* name) +{ + NameToSlot::iterator pos = _byNameTable.find(name); + if ( pos == _byNameTable.end() ) + return false; + return (_indirectBindingTable[pos->second] != NULL); +} + +// find existing or create new slot +SymbolTable::IndirectBindingSlot SymbolTable::findSlotForName(const char* name) +{ + NameToSlot::iterator pos = _byNameTable.find(name); + if ( pos != _byNameTable.end() ) + return pos->second; + // create new slot for this name + SymbolTable::IndirectBindingSlot slot = _indirectBindingTable.size(); + _indirectBindingTable.push_back(NULL); + _byNameTable[name] = slot; + _byNameReverseTable[slot] = name; + return slot; +} + + +// find existing or create new slot +SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom) +{ + //fprintf(stderr, "findSlotForContent(%p)\n", atom); + SymbolTable::IndirectBindingSlot slot = 0; + UTF16StringToSlot::iterator upos; + CStringToSlot::iterator cspos; + ContentToSlot::iterator pos; + switch ( atom->section().type() ) { + case ld::Section::typeCString: + cspos = _cstringTable.find(atom); + if ( cspos != _cstringTable.end() ) { + *existingAtom = _indirectBindingTable[cspos->second]; + return cspos->second; + } + slot = _indirectBindingTable.size(); + _cstringTable[atom] = slot; + break; + case ld::Section::typeNonStdCString: + { + // use seg/sect name is key to map to avoid coalescing across segments and sections + char segsect[64]; + sprintf(segsect, "%s/%s", atom->section().segmentName(), atom->section().sectionName()); + NameToMap::iterator mpos = _nonStdCStringSectionToMap.find(segsect); + CStringToSlot* map = NULL; + if ( mpos == _nonStdCStringSectionToMap.end() ) { + map = new CStringToSlot(); + _nonStdCStringSectionToMap[strdup(segsect)] = map; + } + else { + map = mpos->second; + } + cspos = map->find(atom); + if ( cspos != map->end() ) { + *existingAtom = _indirectBindingTable[cspos->second]; + return cspos->second; + } + slot = _indirectBindingTable.size(); + map->operator[](atom) = slot; + } + break; + case ld::Section::typeUTF16Strings: + upos = _utf16Table.find(atom); + if ( upos != _utf16Table.end() ) { + *existingAtom = _indirectBindingTable[upos->second]; + return upos->second; + } + slot = _indirectBindingTable.size(); + _utf16Table[atom] = slot; + break; + case ld::Section::typeLiteral4: + pos = _literal4Table.find(atom); + if ( pos != _literal4Table.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _literal4Table[atom] = slot; + break; + case ld::Section::typeLiteral8: + pos = _literal8Table.find(atom); + if ( pos != _literal8Table.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _literal8Table[atom] = slot; + break; + case ld::Section::typeLiteral16: + pos = _literal16Table.find(atom); + if ( pos != _literal16Table.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _literal16Table[atom] = slot; + break; + default: + assert(0 && "section type does not support coalescing by content"); + } + _indirectBindingTable.push_back(atom); + *existingAtom = NULL; + return slot; +} + + + +// find existing or create new slot +SymbolTable::IndirectBindingSlot SymbolTable::findSlotForReferences(const ld::Atom* atom, const ld::Atom** existingAtom) +{ + //fprintf(stderr, "findSlotForReferences(%p)\n", atom); + + SymbolTable::IndirectBindingSlot slot = 0; + ReferencesToSlot::iterator pos; + switch ( atom->section().type() ) { + case ld::Section::typeNonLazyPointer: + pos = _nonLazyPointerTable.find(atom); + if ( pos != _nonLazyPointerTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _nonLazyPointerTable[atom] = slot; + break; + case ld::Section::typeCFString: + pos = _cfStringTable.find(atom); + if ( pos != _cfStringTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _cfStringTable[atom] = slot; + break; + case ld::Section::typeObjCClassRefs: + pos = _objc2ClassRefTable.find(atom); + if ( pos != _objc2ClassRefTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _objc2ClassRefTable[atom] = slot; + break; + case ld::Section::typeCStringPointer: + pos = _pointerToCStringTable.find(atom); + if ( pos != _pointerToCStringTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _pointerToCStringTable[atom] = slot; + break; + default: + assert(0 && "section type does not support coalescing by references"); + } + _indirectBindingTable.push_back(atom); + *existingAtom = NULL; + return slot; +} + + +const char* SymbolTable::indirectName(IndirectBindingSlot slot) const +{ + assert(slot < _indirectBindingTable.size()); + const ld::Atom* target = _indirectBindingTable[slot]; + if ( target != NULL ) { + return target->name(); + } + // handle case when by-name reference is indirected and no atom yet in _byNameTable + SlotToName::const_iterator pos = _byNameReverseTable.find(slot); + if ( pos != _byNameReverseTable.end() ) + return pos->second; + assert(0); + return NULL; +} + +const ld::Atom* SymbolTable::indirectAtom(IndirectBindingSlot slot) const +{ + assert(slot < _indirectBindingTable.size()); + return _indirectBindingTable[slot]; +} + +extern "C" char* __cxa_demangle (const char* mangled_name, + char* buf, + size_t* n, + int* status); + +const char* SymbolTable::demangle(const char* sym) +{ + // only try to demangle symbols if -demangle on command line + if ( !_s_doDemangle ) + return sym; + + // only try to demangle symbols that look like C++ symbols + if ( strncmp(sym, "__Z", 3) != 0 ) + return sym; + + static size_t size = 1024; + static char* buff = (char*)malloc(size); + int status; + + char* result = __cxa_demangle(&sym[1], buff, &size, &status); + if ( result != NULL ) { + // if demangling succesful, keep buffer for next demangle + buff = result; + return buff; + } + return sym; +} + + +void SymbolTable::printStatistics() +{ +// fprintf(stderr, "cstring table size: %lu, bucket count: %lu, hash func called %u times\n", +// _cstringTable.size(), _cstringTable.bucket_count(), cstringHashCount); + int count[11]; + for(unsigned int b=0; b < 11; ++b) { + count[b] = 0; + } + for(unsigned int i=0; i < _cstringTable.bucket_count(); ++i) { + unsigned int n = _cstringTable.elems_in_bucket(i); + if ( n < 10 ) + count[n] += 1; + else + count[10] += 1; + } + fprintf(stderr, "cstring table distribution\n"); + for(unsigned int b=0; b < 11; ++b) { + fprintf(stderr, "%u buckets have %u elements\n", count[b], b); + } + fprintf(stderr, "indirect table size: %lu\n", _indirectBindingTable.size()); + fprintf(stderr, "by-name table size: %lu\n", _byNameTable.size()); +// fprintf(stderr, "by-content table size: %lu, hash count: %u, equals count: %u, lookup count: %u\n", +// _byContentTable.size(), contentHashCount, contentEqualCount, contentLookupCount); +// fprintf(stderr, "by-ref table size: %lu, hashed count: %u, equals count: %u, lookup count: %u, insert count: %u\n", +// _byReferencesTable.size(), refHashCount, refEqualsCount, refLookupCount, refInsertCount); + + //ReferencesHash obj; + //for(ReferencesHashToSlot::iterator it=_byReferencesTable.begin(); it != _byReferencesTable.end(); ++it) { + // if ( obj.operator()(it->first) == 0x2F3AC0EAC744EA70 ) { + // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->file()->path()); + // + // } + //} + +} + +} // namespace tool +} // namespace ld + diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h new file mode 100644 index 0000000..f352a51 --- /dev/null +++ b/ld64/src/ld/SymbolTable.h @@ -0,0 +1,165 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __SYMBOL_TABLE_H__ +#define __SYMBOL_TABLE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + + +class SymbolTable : public ld::IndirectBindingTable +{ +public: + typedef uint32_t IndirectBindingSlot; + +private: + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToSlot; + + class ContentFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map ContentToSlot; + + class ReferencesHashFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map ReferencesToSlot; + + class CStringHashFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map CStringToSlot; + + class UTF16StringHashFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map UTF16StringToSlot; + + typedef std::map SlotToName; + typedef __gnu_cxx::hash_map, CStringEquals> NameToMap; + +public: + + class byNameIterator { + public: + byNameIterator& operator++(int) { ++_nameTableIterator; return *this; } + const ld::Atom* operator*() { return _slotTable[_nameTableIterator->second]; } + bool operator!=(const byNameIterator& lhs) { return _nameTableIterator != lhs._nameTableIterator; } + + private: + friend class SymbolTable; + byNameIterator(NameToSlot::iterator it, std::vector& indirectTable) + : _nameTableIterator(it), _slotTable(indirectTable) {} + + NameToSlot::iterator _nameTableIterator; + std::vector& _slotTable; + }; + + SymbolTable(const Options& opts, std::vector& ibt); + + bool add(const ld::Atom& atom, bool ignoreDuplicates); + IndirectBindingSlot findSlotForName(const char* name); + IndirectBindingSlot findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom); + IndirectBindingSlot findSlotForReferences(const ld::Atom* atom, const ld::Atom** existingAtom); + const ld::Atom* atomForSlot(IndirectBindingSlot s) { return _indirectBindingTable[s]; } + unsigned int updateCount() { return _indirectBindingTable.size(); } + void undefines(std::vector& undefines); + void tentativeDefs(std::vector& undefines); + bool hasName(const char* name); + bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; } + byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } + byNameIterator end() { return byNameIterator(_byNameTable.end(),_indirectBindingTable); } + void printStatistics(); + static const char* demangle(const char* sym); + + // from ld::IndirectBindingTable + virtual const char* indirectName(IndirectBindingSlot slot) const; + virtual const ld::Atom* indirectAtom(IndirectBindingSlot slot) const; + +private: + bool addByName(const ld::Atom& atom, bool ignoreDuplicates); + bool addByContent(const ld::Atom& atom); + bool addByReferences(const ld::Atom& atom); + void markCoalescedAway(const ld::Atom* atom); + + const Options& _options; + NameToSlot _byNameTable; + SlotToName _byNameReverseTable; + ContentToSlot _literal4Table; + ContentToSlot _literal8Table; + ContentToSlot _literal16Table; + UTF16StringToSlot _utf16Table; + CStringToSlot _cstringTable; + NameToMap _nonStdCStringSectionToMap; + ReferencesToSlot _nonLazyPointerTable; + ReferencesToSlot _cfStringTable; + ReferencesToSlot _objc2ClassRefTable; + ReferencesToSlot _pointerToCStringTable; + std::vector& _indirectBindingTable; + bool _hasExternalTentativeDefinitions; + + static bool _s_doDemangle; + +}; + +} // namespace tool +} // namespace ld + + +#endif // __SYMBOL_TABLE_H__ diff --git a/ld64/src/ld/debugline.c b/ld64/src/ld/debugline.c index 971e616..c367d00 100644 --- a/ld64/src/ld/debugline.c +++ b/ld64/src/ld/debugline.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -345,6 +345,8 @@ line_at_eof (struct line_reader_data * lnd) return lnd->cpos == lnd->end; } +static const bool verbose = false; + static bool next_state (struct line_reader_data *lnd) { @@ -373,28 +375,34 @@ next_state (struct line_reader_data *lnd) case DW_LNS_extended_op: { uint64_t sz = read_uleb128 (lnd); - const uint8_t * op = lnd->cpos; + const uint8_t * eop = lnd->cpos; - if ((uint64_t)(lnd->end - op) < sz || sz == 0) + if ((uint64_t)(lnd->end - eop) < sz || sz == 0) return false; lnd->cpos += sz; - switch (*op++) + switch (*eop++) { case DW_LNE_end_sequence: + if (verbose) fprintf(stderr, "DW_LNE_end_sequence\n"); lnd->cur.end_of_sequence = true; return true; case DW_LNE_set_address: - if (sz == 9) - lnd->cur.pc = read_64 (op); - else if (sz == 5) - lnd->cur.pc = read_32 (op); + if (sz == 9) { + lnd->cur.pc = read_64 (eop); + if (verbose) fprintf(stderr, "DW_LNE_set_address(0x%08llX)\n", lnd->cur.pc); + } + else if (sz == 5) { + lnd->cur.pc = read_32 (eop); + if (verbose) fprintf(stderr, "DW_LNE_set_address(0x%08llX)\n", lnd->cur.pc); + } else return false; break; case DW_LNE_define_file: { + if (verbose) fprintf(stderr, "DW_LNE_define_file\n"); const uint8_t * * filenames; filenames = realloc (lnd->filenames, @@ -402,9 +410,9 @@ next_state (struct line_reader_data *lnd) if (! filenames) return false; /* Check for zero-termination. */ - if (! memchr (op, 0, lnd->cpos - op)) + if (! memchr (eop, 0, lnd->cpos - eop)) return false; - filenames[lnd->numfile++] = op; + filenames[lnd->numfile++] = eop; lnd->filenames = filenames; /* There's other data here, like file sizes and modification @@ -414,40 +422,41 @@ next_state (struct line_reader_data *lnd) default: /* Don't understand it, so skip it. */ + if (verbose) fprintf(stderr, "DW_LNS_extended_op unknown\n"); break; } break; } case DW_LNS_copy: - //fprintf(stderr, "DW_LNS_copy\n"); + if (verbose) fprintf(stderr, "DW_LNS_copy\n"); return true; case DW_LNS_advance_pc: - //fprintf(stderr, "DW_LNS_advance_pc\n"); tmp = read_uleb128 (lnd); if (tmp == (uint64_t) -1) return false; lnd->cur.pc += tmp * lnd->minimum_instruction_length; + if (verbose) fprintf(stderr, "DW_LNS_advance_pc(0x%08llX)\n", lnd->cur.pc); break; case DW_LNS_advance_line: - //fprintf(stderr, "DW_LNS_advance_line\n"); lnd->cur.line += read_sleb128 (lnd); + if (verbose) fprintf(stderr, "DW_LNS_advance_line(%lld)\n", lnd->cur.line); break; case DW_LNS_set_file: - //fprintf(stderr, "DW_LNS_set_file\n"); + if (verbose) fprintf(stderr, "DW_LNS_set_file\n"); lnd->cur.file = read_uleb128 (lnd); break; case DW_LNS_set_column: - //fprintf(stderr, "DW_LNS_set_column\n"); + if (verbose) fprintf(stderr, "DW_LNS_set_column\n"); lnd->cur.col = read_uleb128 (lnd); break; case DW_LNS_const_add_pc: - //fprintf(stderr, "DW_LNS_const_add_pc\n"); + if (verbose) fprintf(stderr, "DW_LNS_const_add_pc\n"); lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range * lnd->minimum_instruction_length); break; case DW_LNS_fixed_advance_pc: - //fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); + if (verbose) fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); if (lnd->end - lnd->cpos < 2) return false; lnd->cur.pc += read_16 (lnd->cpos); @@ -456,6 +465,7 @@ next_state (struct line_reader_data *lnd) default: { /* Don't know what it is, so skip it. */ + if (verbose) fprintf(stderr, "unknown opcode\n"); int i; for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++) skip_leb128 (lnd); diff --git a/ld64/src/ld/debugline.h b/ld64/src/ld/debugline.h index 51d585e..e45c290 100644 --- a/ld64/src/ld/debugline.h +++ b/ld64/src/ld/debugline.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006 Apple, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -20,6 +20,9 @@ * * @APPLE_LICENSE_HEADER_END@ */ +#ifndef __DEBUG_LINE_H__ +#define __DEBUG_LINE_H__ + #include #include @@ -107,3 +110,5 @@ void line_free (struct line_reader_data * lnd); } #endif +#endif // __DEBUG_LINE_H__ + diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 115240e..d9d66a6 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -1,5 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,7 +24,7 @@ // start temp HACK for cross builds extern "C" double log2 ( double ); -#define __MATH__ +//#define __MATH__ // end temp HACK for cross builds @@ -36,11 +37,15 @@ extern "C" double log2 ( double ); #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include @@ -50,848 +55,495 @@ extern "C" double log2 ( double ); #include #include #include -#include -#include +#include +#include -#include "configure.h" #include "Options.h" -#include "ObjectFile.h" - -#include "MachOReaderRelocatable.hpp" -#include "ArchiveReader.hpp" -#include "MachOReaderDylib.hpp" -#include "MachOWriterExecutable.hpp" - - -#if LTO_SUPPORT -#include "LTOReader.hpp" -#endif +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" -#include "OpaqueSection.hpp" +#include "InputFiles.h" +#include "Resolver.h" +#include "OutputFile.h" +#include "passes/stubs/make_stubs.h" +#include "passes/dtrace_dof.h" +#include "passes/got.h" +#include "passes/tlvp.h" +#include "passes/huge.h" +#include "passes/compact_unwind.h" +#include "passes/order_file.h" +#include "passes/branch_island.h" +#include "passes/branch_shim.h" +#include "passes/objc.h" +#include "passes/dylibs.h" -class CStringComparor -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } -}; +#include "parsers/archive_file.h" +#include "parsers/macho_relocatable_file.h" +#include "parsers/macho_dylib_file.h" +#include "parsers/lto_file.h" +#include "parsers/opaque_section_file.h" -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; -class Section : public ObjectFile::Section +class InternalState : public ld::Internal { public: - static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded=true); - static void assignIndexes(bool objfile); - const char* getName() { return fSectionName; } + InternalState(const Options& opts) : _options(opts) { } + virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); + virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); + + void sortSections(); + virtual ~InternalState() {} private: - Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill); - struct Sorter { - static int segmentOrdinal(const char* segName); - bool operator()(Section* left, Section* right); + class FinalSection : public ld::Internal::FinalSection + { + 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& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal); + private: + friend class InternalState; + static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen); + static uint32_t segmentOrder(const ld::Section& sect, bool objFile); + uint32_t _segmentOrder; + uint32_t _sectionOrder; + + static std::vector _s_segmentsSeen; + static ld::Section _s_DATA_data; + static ld::Section _s_DATA_const; + static ld::Section _s_TEXT_text; + static ld::Section _s_TEXT_const; + static ld::Section _s_DATA_nl_symbol_ptr; + static ld::Section _s_DATA_common; }; - - typedef __gnu_cxx::hash_map, CStringEquals> NameToOrdinal; - typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; - //typedef std::map NameToSection; - - char fSectionName[18]; - char fSegmentName[18]; - bool fZeroFill; - bool fUntrustedZeroFill; - - static NameToSection fgMapping; - static std::vector fgSections; - static NameToOrdinal fgSegmentDiscoverOrder; - static bool fgMakingObjectFile; -}; - -Section::NameToSection Section::fgMapping; -std::vector Section::fgSections; -Section::NameToOrdinal Section::fgSegmentDiscoverOrder; -bool Section::fgMakingObjectFile; - -Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill) - : fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill) -{ - strlcpy(fSectionName, sectionName, sizeof(fSectionName)); - strlcpy(fSegmentName, segmentName, sizeof(fSegmentName)); - - this->fIndex = fgSections.size() + 20; // room for 20 standard sections - // special placement of some sections - if ( strcmp(segmentName, "__TEXT") == 0 ) { - // sort mach header and load commands to start of TEXT - if ( strcmp(sectionName, "._mach_header") == 0 ) - this->fIndex = 1; - else if ( strcmp(sectionName, "._load_commands") == 0 ) - this->fIndex = 2; - else if ( strcmp(sectionName, "._load_cmds_pad") == 0 ) - this->fIndex = 3; - // sort __text after load commands - else if ( strcmp(sectionName, "__text") == 0 ) - this->fIndex = 10; - // sort arm/ppc stubs after text to make branch islands feasible - else if ( strcmp(sectionName, "__picsymbolstub4") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__symbol_stub4") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__picsymbolstub1") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__symbol_stub1") == 0 ) - this->fIndex = 11; - // sort fast arm stubs to end of __TEXT to be close to lazy pointers - else if ( strcmp(sectionName, "__symbolstub1") == 0 ) - this->fIndex = INT_MAX; - // sort unwind info to end of segment - else if ( strcmp(sectionName, "__eh_frame") == 0 ) - this->fIndex = INT_MAX-1; - else if ( strcmp(sectionName, "__unwind_info") == 0 ) - this->fIndex = INT_MAX-2; - else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) - this->fIndex = INT_MAX-3; - } - else if ( strcmp(segmentName, "__DATA") == 0 ) { - // sort arm lazy symbol pointers that must be at start of __DATA - if ( strcmp(sectionName, "__lazy_symbol") == 0 ) - this->fIndex = 0; - // sort sections dyld will touch to start of segment - else if ( strcmp(sectionName, "__dyld") == 0 ) - this->fIndex = 1; - else if ( strcmp(sectionName, "__program_vars") == 0 ) - this->fIndex = 1; - else if ( strcmp(sectionName, "__mod_init_func") == 0 ) - this->fIndex = 2; - else if ( strcmp(sectionName, "__nl_symbol_ptr") == 0 ) - this->fIndex = 3; - else if ( strcmp(sectionName, "__la_symbol_ptr") == 0 ) - this->fIndex = 4; - else if ( strcmp(sectionName, "__const") == 0 ) - this->fIndex = 5; - else if ( strcmp(sectionName, "__cfstring") == 0 ) - this->fIndex = 6; - else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) - this->fIndex = 7; - else if ( strcmp(sectionName, "__objc_data") == 0 ) - this->fIndex = 8; - else if ( strcmp(sectionName, "__objc_msgrefs") == 0 ) - this->fIndex = 9; - else if ( strcmp(sectionName, "__objc_protorefs") == 0 ) - this->fIndex = 10; - else if ( strcmp(sectionName, "__objc_selrefs") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__objc_classrefs") == 0 ) - this->fIndex = 12; - else if ( strcmp(sectionName, "__objc_superrefs") == 0 ) - this->fIndex = 13; - else if ( strcmp(sectionName, "__objc_const") == 0 ) - this->fIndex = 14; - else if ( strcmp(sectionName, "__objc_classlist") == 0 ) - this->fIndex = 15; - else if ( strcmp(sectionName, "__objc_nlclslist") == 0 ) - this->fIndex = 16; - else if ( strcmp(sectionName, "__objc_catlist") == 0 ) - this->fIndex = 17; - else if ( strcmp(sectionName, "__objc_protolist") == 0 ) - this->fIndex = 18; - else if ( strcmp(sectionName, "__objc_imageinfo") == 0 ) - this->fIndex = 19; - else if ( strcmp(sectionName, "__huge") == 0 ) - this->fIndex = INT_MAX; - } - //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); -} - -Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded) -{ - NameToSection::iterator pos = fgMapping.find(sectionName); - if ( pos != fgMapping.end() ) { - if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) { - if ( !untrustedZeroFill && pos->second->fUntrustedZeroFill ) { - pos->second->fZeroFill = zeroFill; - pos->second->fUntrustedZeroFill = false; - } - return pos->second; - } - // otherwise same section name is used in different segments, look slow way - for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) - return *it; - } - } - - if ( !createIfNeeded ) - return NULL; + struct SectionHash { + size_t operator()(const ld::Section*) const; + }; + struct SectionEquals { + bool operator()(const ld::Section* left, const ld::Section* right) const; + }; + typedef __gnu_cxx::hash_map SectionInToOut; - // does not exist, so make a new one - Section* sect = new Section(sectionName, segmentName, zeroFill, untrustedZeroFill); - fgMapping[sectionName] = sect; - fgSections.push_back(sect); - if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { - // special case __StaticInit to be right after __text - find("__StaticInit", "__TEXT", false, true); - } + SectionInToOut _sectionInToFinalMap; + const Options& _options; +}; - // remember segment discovery order - if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) - fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); +ld::Section InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_DATA_const("__DATA", "__const", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld::Section::typeCode); +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); +std::vector InternalState::FinalSection::_s_segmentsSeen; - return sect; -} -int Section::Sorter::segmentOrdinal(const char* segName) +size_t InternalState::SectionHash::operator()(const ld::Section* sect) const { - if ( strcmp(segName, "__HEADER") == 0 ) - return 1; - if ( strcmp(segName, "__PAGEZERO") == 0 ) - return 1; - if ( strcmp(segName, "__TEXT") == 0 ) - return 2; - if ( strcmp(segName, "__DATA") == 0 ) - return (fgMakingObjectFile ? 6 : 3); // __DATA is last in .o files and here in FLI - if ( strcmp(segName, "__OBJC") == 0 ) - return 4; - if ( strcmp(segName, "__IMPORT") == 0 ) - return 5; - if ( strcmp(segName, "__LINKEDIT") == 0 ) - return INT_MAX; // linkedit segment should always sort last - else - return fgSegmentDiscoverOrder[segName]+6; + size_t hash = 0; + __gnu_cxx::hash temp; + hash += temp.operator()(sect->segmentName()); + hash += temp.operator()(sect->sectionName()); + return hash; } - -bool Section::Sorter::operator()(Section* left, Section* right) +bool InternalState::SectionEquals::operator()(const ld::Section* left, const ld::Section* right) const { - // Segment is primary sort key - int leftSegOrdinal = segmentOrdinal(left->fSegmentName); - int rightSegOrdinal = segmentOrdinal(right->fSegmentName); - if ( leftSegOrdinal < rightSegOrdinal ) - return true; - if ( leftSegOrdinal > rightSegOrdinal ) - return false; - - // zerofill section sort to the end - if ( !left->fZeroFill && right->fZeroFill ) - return true; - if ( left->fZeroFill && !right->fZeroFill ) - return false; - - // section discovery order is last sort key - return left->fIndex < right->fIndex; + return (*left == *right); } -void Section::assignIndexes(bool objfile) -{ - //printf("unsorted sections:\n"); - //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); - //} - - // sort it - Section::fgMakingObjectFile = objfile; - std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); - // assign correct section ordering to each Section object - unsigned int newOrder = 1; - for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) - (*it)->fIndex = newOrder++; - - //printf("sorted sections:\n"); - //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); - //} +InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile) + : ld::Internal::FinalSection(sect), + _segmentOrder(segmentOrder(sect, objFile)), + _sectionOrder(sectionOrder(sect, sectionsSeen)) +{ + //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n", + // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder); } -class Linker : public ObjectFile::Reader::DylibHander { -public: - Linker(int argc, const char* argv[]); - - const char* getArchPrefix(); - const char* architectureName(); - bool showArchitectureInErrors(); - bool isInferredArchitecture(); - void createReaders(); - void createWriter(); - void addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& ); - void setOutputFile(ExecutableFile::Writer* writer); - void link(); - void optimize(); - - // implemenation from ObjectFile::Reader::DylibHander - virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath); - -private: - struct WhyLiveBackChain - { - WhyLiveBackChain* previous; - ObjectFile::Atom* referer; - }; - - ObjectFile::Reader* createReader(const Options::FileInfo&); - const char* fileArch(const void* p); - void addAtom(ObjectFile::Atom& atom); - void addAtoms(std::vector& atoms); - void buildAtomList(); - void adjustScope(); - void processDylibs(); - void markDead(ObjectFile::Atom* atom); - void updateConstraints(ObjectFile::Reader* reader); - void loadAndResolve(); - void processDTrace(); - void checkObjC(); - void addSynthesizedAtoms(); - void loadUndefines(); - void checkUndefines(); - void resolveReferences(); - void deadStripResolve(); - void addLiveRoot(const char* name); - void moveToFrontOfSection(ObjectFile::Atom* atom); - ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); - void logArchive(ObjectFile::Reader* reader); - void sortSections(); - void sortAtoms(); - void tweakLayout(); - void writeDotOutput(); - static bool minimizeStab(ObjectFile::Reader::Stab& stab); - static const char* truncateStabString(const char* str); - void collectDebugInfo(); - void writeOutput(); - ObjectFile::Atom* entryPoint(bool orInit, bool searchArchives=false); - ObjectFile::Atom* dyldClassicHelper(); - ObjectFile::Atom* dyldCompressedHelper(); - ObjectFile::Atom* dyldLazyLibraryHelper(); - const char* assureFullPath(const char* path); - void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); - void collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals); - void synthesizeDebugNotes(std::vector& allAtomsByReader); - void printStatistics(); - void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); - char* commatize(uint64_t in, char* out); - void getVMInfo(vm_statistics_data_t& info); - cpu_type_t inferArchitecture(); - void checkDylibClientRestrictions(ObjectFile::Reader* reader); - void logDylib(ObjectFile::Reader* reader, bool indirect); - - void resolve(ObjectFile::Reference* reference); - void resolveFrom(ObjectFile::Reference* reference); - std::vector* addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy); - void addJustInTimeAtomsAndMarkLive(const char* name); - - ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - - void logTraceInfo(const char* format, ...); - - - class SymbolTable - { - public: - typedef __gnu_cxx::hash_map, CStringEquals> Mapper; - - SymbolTable(Linker&); - void require(const char* name); - bool add(ObjectFile::Atom& atom); - ObjectFile::Atom* find(const char* name); - void erase(const char* name); - unsigned int getRequireCount() { return fRequireCount; } - void getUndefinesNames(std::vector& undefines); - void getTentativesNames(std::vector& tents); - bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } - bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } - void setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; } - uint32_t dylibSymbolCount() { return fDylibSymbolCount; } - Mapper::iterator begin() { return fTable.begin(); } - Mapper::iterator end() { return fTable.end(); } - - private: - Linker& fOwner; - Mapper fTable; - unsigned int fRequireCount; - bool fHasExternalTentativeDefinitions; - bool fHasExternalWeakDefinitions; - uint32_t fDylibSymbolCount; - }; - - class AtomSorter - { - public: - AtomSorter(std::map* map, std::set& inits, - std::set& terms) : - fOverriddenOrdinalMap(map), fInitializerSet(inits), fTerminatorSet(terms) {} - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); - private: - std::map* fOverriddenOrdinalMap; - std::set& fInitializerSet; - std::set& fTerminatorSet; - }; - - typedef std::map SectionOrder; - - struct DTraceProbeInfo { - DTraceProbeInfo(const ObjectFile::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} - const ObjectFile::Atom* atom; - uint32_t offset; - const char* probeName; - }; - typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToReader; - - struct IndirectLibrary { - const char* path; - uint64_t fileLen; - ObjectFile::Reader* reader; - std::set parents; - ObjectFile::Reader* reExportedViaDirectLibrary; - }; - - ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib); - - Options fOptions; - SymbolTable fGlobalSymbolTable; - uint32_t fNextInputOrdinal; - std::vector fInputFiles; - ExecutableFile::Writer* fOutputFile; - InstallNameToReader fDylibMap; - std::map fDylibOptionsMap; - std::set fDylibsProcessed; - ObjectFile::Reader* fBundleLoaderReader; - std::vector fReadersThatHaveSuppliedAtoms; - std::vector fAllAtoms; - std::set fArchiveReaders; - std::set fArchiveReadersLogged; - std::set fDeadAtoms; - std::set fLiveAtoms; - std::set fLiveRootAtoms; - std::set fInitializerAtoms; - std::set fTerminatorAtoms; - std::set fRegularDefAtomsThatOverrideADylibsWeakDef; - std::vector fStabs; - std::vector fAtomsWithUnresolvedReferences; - std::set fAtomsOverriddenByLateLoads; - bool fInitialLoadsDone; - bool fCreateUUID; - bool fCanScatter; - SectionOrder fSectionOrder; - cpu_type_t fArchitecture; - const char* fArchitectureName; - bool fArchitectureInferred; - bool fDirectLibrariesComplete; - bool fBiggerThanTwoGigOutput; - uint64_t fOutputFileSize; - uint64_t fTotalZeroFillSize; - uint64_t fTotalSize; - uint64_t fStartTime; - uint64_t fStartCreateReadersTime; - uint64_t fStartCreateWriterTime; - uint64_t fStartBuildAtomsTime; - uint64_t fStartLoadAndResolveTime; - uint64_t fStartSortTime; - uint64_t fStartDebugTime; - uint64_t fStartWriteTime; - uint64_t fEndTime; - uint64_t fTotalObjectSize; - uint64_t fTotalArchiveSize; - uint32_t fTotalObjectLoaded; - uint32_t fTotalArchivesLoaded; - uint32_t fTotalDylibsLoaded; - vm_statistics_data_t fStartVMInfo; - ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint; - ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint; - bool fObjcReplacmentClasses; - bool fAllDirectDylibsLoaded; -}; - - -Linker::Linker(int argc, const char* argv[]) - : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), - fInitialLoadsDone(false), fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), - fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), - fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), - fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), - fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny), - fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false) +const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect) { - fStartTime = mach_absolute_time(); - if ( fOptions.printStatistics() ) - getVMInfo(fStartVMInfo); - - fArchitecture = fOptions.architecture(); - if ( fArchitecture == 0 ) { - // -arch not specified, scan .o files to figure out what it should be - fArchitecture = inferArchitecture(); - fArchitectureInferred = true; - } - switch (fArchitecture) { - case CPU_TYPE_POWERPC: - fArchitectureName = "ppc"; - break; - case CPU_TYPE_POWERPC64: - fArchitectureName = "ppc64"; - break; - case CPU_TYPE_I386: - fArchitectureName = "i386"; + // merge sections in final linked image + switch ( sect.type() ) { + case ld::Section::typeLiteral4: + case ld::Section::typeLiteral8: + case ld::Section::typeLiteral16: + return _s_TEXT_const; + case ld::Section::typeUnclassified: + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) + return _s_DATA_data; + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_DATA_const; + } + else if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_TEXT_const; + } break; - case CPU_TYPE_X86_64: - fArchitectureName = "x86_64"; + case ld::Section::typeCode: + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { + if ( strcmp(sect.sectionName(), "__textcoal_nt") == 0 ) + return _s_TEXT_text; + else if ( strcmp(sect.sectionName(), "__StaticInit") == 0 ) + return _s_TEXT_text; + } break; - case CPU_TYPE_ARM: - fArchitectureName = "arm"; - if ( fOptions.preferSubArchitecture() ) { - switch ( fOptions.subArchitecture() ) { - case CPU_SUBTYPE_ARM_V4T: - fArchitectureName = "armv4t"; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - fArchitectureName = "armv5"; - break; - case CPU_SUBTYPE_ARM_V6: - fArchitectureName = "armv6"; - break; - case CPU_SUBTYPE_ARM_V7: - fArchitectureName = "armv7"; - break; - } + case ld::Section::typeNonLazyPointer: + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + if ( strcmp(sect.sectionName(), "__nl_symbol_ptr") == 0 ) + return _s_DATA_nl_symbol_ptr; + } + else if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) { + if ( strcmp(sect.sectionName(), "__pointers") == 0 ) + return _s_DATA_nl_symbol_ptr; } break; + case ld::Section::typeTentativeDefs: + return _s_DATA_common; + break; + // FIX ME: more default: - fArchitectureName = "unknown architecture"; break; } + return sect; } -const char* Linker::architectureName() -{ - return fArchitectureName; -} - -bool Linker::showArchitectureInErrors() +const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal) { - return fOptions.printArchPrefix(); + // in -r mode the only section that ever changes is __tenative -> __common with -d option + if ( (sect.type() == ld::Section::typeTentativeDefs) && makeTentativeDefsReal) + return _s_DATA_common; + return sect; } -bool Linker::isInferredArchitecture() +uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, bool objFile) { - return fArchitectureInferred; + if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) + return 0; + if ( strcmp(sect.segmentName(), "__HEADER") == 0 ) // only used with -preload + return 0; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return 1; + // in -r mode, want __DATA last so zerofill sections are at end + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) + return (objFile ? 5 : 2); + if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) + return 3; + if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) + return 4; + + // layout non-standard segments in order seen (+10 to shift beyond standard segments) + for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) { + if ( strcmp(_s_segmentsSeen[i], sect.segmentName()) == 0 ) + return i+10; + } + _s_segmentsSeen.push_back(sect.segmentName()); + return _s_segmentsSeen.size()-1+10; } -cpu_type_t Linker::inferArchitecture() +uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen) { - // scan all input files, looking for a thin .o file. - // the first one found is presumably the architecture to link - uint8_t buffer[sizeof(mach_header_64)]; - std::vector& files = fOptions.getInputFiles(); - for (std::vector::iterator it = files.begin(); it != files.end(); ++it) { - int fd = ::open(it->path, O_RDONLY, 0); - if ( fd != -1 ) { - ssize_t amount = read(fd, buffer, sizeof(buffer)); - ::close(fd); - if ( amount >= (ssize_t)sizeof(buffer) ) { - if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch ppc based on %s", it->path); - return CPU_TYPE_POWERPC; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch ppc64 based on %s", it->path); - return CPU_TYPE_POWERPC64; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch i386 based on %s", it->path); - return CPU_TYPE_I386; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch x86_64 based on %s", it->path); - return CPU_TYPE_X86_64; - } - else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //warning("-arch not used, infering -arch arm based on %s", it->path); - return CPU_TYPE_ARM; - } + if ( sect.type() == ld::Section::typeFirstSection ) + return 0; + if ( sect.type() == ld::Section::typeMachHeader ) + return 1; + if ( sect.type() == ld::Section::typeLastSection ) + return INT_MAX; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { + switch ( sect.type() ) { + case ld::Section::typeCode: + // make __text always be first "code" section + if ( strcmp(sect.sectionName(), "__text") == 0 ) + return 10; + else + return 11; + case ld::Section::typeStub: + return 12; + case ld::Section::typeStubHelper: + return 13; + case ld::Section::typeLSDA: + return INT_MAX-3; + case ld::Section::typeUnwindInfo: + return INT_MAX-2; + case ld::Section::typeCFI: + return INT_MAX-1; + case ld::Section::typeStubClose: + return INT_MAX; + default: + return sectionsSeen+20; + } + } + else if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + switch ( sect.type() ) { + case ld::Section::typeLazyPointerClose: + return 8; + case ld::Section::typeDyldInfo: + return 9; + case ld::Section::typeNonLazyPointer: + return 10; + case ld::Section::typeLazyPointer: + return 11; + case ld::Section::typeInitializerPointers: + return 12; + case ld::Section::typeTerminatorPointers: + return 13; + case ld::Section::typeTLVInitialValues: + return INT_MAX-4; // need TLV zero-fill to follow TLV init values + case ld::Section::typeTLVZeroFill: + return INT_MAX-3; + case ld::Section::typeZeroFill: + // make sure __huge is always last zerofill section + if ( strcmp(sect.sectionName(), "__huge") == 0 ) + return INT_MAX-1; + else + return INT_MAX-2; + default: + // Reorder sections to reduce page faults in object files + if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) + return 20; + else if ( strcmp(sect.sectionName(), "__objc_nlclslist") == 0 ) + return 21; + else if ( strcmp(sect.sectionName(), "__objc_catlist") == 0 ) + return 22; + else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 ) + return 23; + else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) + return 24; + else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) + return 25; + else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) + return 26; + else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) + return 27; + else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) + return 28; + else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) + return 29; + else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) + return 30; + else if ( strcmp(sect.sectionName(), "__objc_data") == 0 ) + return 31; + else + return sectionsSeen+40; + } + } + // make sure zerofill in any other section is at end of segment + if ( sect.type() == ld::Section::typeZeroFill ) + return INT_MAX-1; + return sectionsSeen+20; +} + +#ifndef NDEBUG +static void validateFixups(const ld::Atom& atom) +{ + //fprintf(stderr, "validateFixups %s\n", atom.name()); + bool lastWasClusterEnd = true; + ld::Fixup::Cluster lastClusterSize = ld::Fixup::k1of1; + uint32_t curClusterOffsetInAtom = 0; + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize); + assert((fit->offsetInAtom < atom.size()) || (fit->offsetInAtom == 0)); + if ( fit->firstInCluster() ) { + assert(lastWasClusterEnd); + curClusterOffsetInAtom = fit->offsetInAtom; + lastWasClusterEnd = (fit->clusterSize == ld::Fixup::k1of1); + } + else { + assert(!lastWasClusterEnd); + assert(fit->offsetInAtom == curClusterOffsetInAtom); + switch ((ld::Fixup::Cluster)fit->clusterSize) { + case ld::Fixup::k1of1: + case ld::Fixup::k1of2: + case ld::Fixup::k1of3: + case ld::Fixup::k1of4: + case ld::Fixup::k1of5: + lastWasClusterEnd = false; + break; + case ld::Fixup::k2of2: + assert(lastClusterSize = ld::Fixup::k1of2); + lastWasClusterEnd = true; + break; + case ld::Fixup::k2of3: + assert(lastClusterSize = ld::Fixup::k1of3); + lastWasClusterEnd = false; + break; + case ld::Fixup::k2of4: + assert(lastClusterSize = ld::Fixup::k1of4); + lastWasClusterEnd = false; + break; + case ld::Fixup::k2of5: + assert(lastClusterSize = ld::Fixup::k1of5); + lastWasClusterEnd = false; + break; + case ld::Fixup::k3of3: + assert(lastClusterSize = ld::Fixup::k2of3); + lastWasClusterEnd = true; + break; + case ld::Fixup::k3of4: + assert(lastClusterSize = ld::Fixup::k2of4); + lastWasClusterEnd = false; + break; + case ld::Fixup::k3of5: + assert(lastClusterSize = ld::Fixup::k2of5); + lastWasClusterEnd = false; + break; + case ld::Fixup::k4of4: + assert(lastClusterSize = ld::Fixup::k3of4); + lastWasClusterEnd = true; + break; + case ld::Fixup::k4of5: + assert(lastClusterSize = ld::Fixup::k3of5); + lastWasClusterEnd = false; + break; + case ld::Fixup::k5of5: + assert(lastClusterSize = ld::Fixup::k4of5); + lastWasClusterEnd = true; + break; } } + lastClusterSize = fit->clusterSize; + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + assert(fit->u.target != NULL); + } } - - // no thin .o files found, so default to same architecture this was built as - warning("-arch not specified"); -#if __ppc__ - return CPU_TYPE_POWERPC; -#elif __i386__ - return CPU_TYPE_I386; -#elif __ppc64__ - return CPU_TYPE_POWERPC64; -#elif __x86_64__ - return CPU_TYPE_X86_64; -#elif __arm__ - return CPU_TYPE_ARM; -#else - #error unknown default architecture -#endif -} - - -void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) -{ - fInputFiles.push_back(reader); - fDylibOptionsMap[reader] = info.options; -} - -void Linker::setOutputFile(ExecutableFile::Writer* writer) -{ - fOutputFile = writer; -} - -class InSet -{ -public: - InSet(std::set& deadAtoms) : fDeadAtoms(deadAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fDeadAtoms.count(atom) != 0 ); - } - -private: - std::set& fDeadAtoms; -}; - -void Linker::loadAndResolve() -{ - fStartLoadAndResolveTime = mach_absolute_time(); - if ( fOptions.deadStrip() == Options::kDeadStripOff ) { - // without dead-code-stripping: - // find atoms to resolve all undefines - this->loadUndefines(); - // verify nothing is missing - this->checkUndefines(); - // once all undefines fulfill, then bind all references - this->resolveReferences(); - // remove atoms weak atoms that have been overridden - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - } - else { - // with dead code stripping: - // start binding references from roots, - this->deadStripResolve(); - // verify nothing is missing - this->checkUndefines(); + switch (lastClusterSize) { + case ld::Fixup::k1of1: + case ld::Fixup::k2of2: + case ld::Fixup::k3of3: + case ld::Fixup::k4of4: + case ld::Fixup::k5of5: + break; + default: + assert(0 && "last fixup was not end of cluster"); + break; } } +#endif -void Linker::addSynthesizedAtoms() -{ - // give write a chance to synthesize stub, GOT, and lazy pointer atoms - std::vector newAtoms; - fOutputFile->addSynthesizedAtoms(fAllAtoms, this->dyldClassicHelper(), - this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(), - fBiggerThanTwoGigOutput, - fGlobalSymbolTable.dylibSymbolCount(), - newAtoms); - - // add all newly created atoms to fAllAtoms and update symbol table - this->addAtoms(newAtoms); -} - -void Linker::optimize() -{ - // give each reader a chance to do any optimizations - bool didSomething = false; - std::vector newAtoms; - std::vector additionalUndefines; - std::vector newlyDeadAtoms; - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - didSomething |= (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, newlyDeadAtoms, fNextInputOrdinal, - fOutputFile, entryPoint(true), fOptions.llvmOptions(), - fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(), - fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(), - fOptions.allowTextRelocs()); - } - - // only do next steps if some optimization was actually done - if ( didSomething ) { - - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - for(std::vector::iterator itr = newAtoms.begin(); itr != newAtoms.end(); ++itr) { - ObjectFile::Atom* atom = *itr; - const char* name = atom->getName(); - if ( name != NULL ) { - ObjectFile::Atom* existingAtom = fGlobalSymbolTable.find(name); - if ( (existingAtom != NULL) && fLiveAtoms.count(existingAtom) == 0 ) { - // While dead code stripping, the atoms were not removed from fGlobalSymbolTable - // for performance reasons. Normally, libLTO will never recreate an atom - // that was previously dead stripped away, but if it does remove - // the remnents of the previous so the new one can be added - fGlobalSymbolTable.erase(name); +ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) +{ + ld::Internal::FinalSection* fs = this->getFinalSection(atom.section()); + + // When order file used on data, turn ordered zero fill symbols into zero data + switch ( atom.section().type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTentativeDefs: + if ( (_options.outputKind() == Options::kDyld) && (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) ) { + // found in order file, move to __data section + fs = getFinalSection(InternalState::FinalSection::_s_DATA_data);\ + //fprintf(stderr, "moved %s to __data section\n", atom.name()); + break; } } } - } - - // add all newly created atoms to fAllAtoms and update symbol table - this->addAtoms(newAtoms); - - // add dead atoms to dead list and remove from fAllAtoms - for(std::vector::iterator itr = newlyDeadAtoms.begin(); itr != newlyDeadAtoms.end(); ++itr) - markDead(*itr); - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - - // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could - // not have their section set until now. - for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - if ( atom->getSection() == NULL ) - atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill(), true)); - } - - // resolve new undefines - for(std::vector::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { - const char *targetName = *riter; - //fprintf(stderr, "LTO additional undefine: %s\n", targetName); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL) { - // mark that this symbol is needed - fGlobalSymbolTable.require(targetName); - // try to find it in some library - this->addJustInTimeAtoms(targetName, true, true, true); - } - } - - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - // LTO may optimize away some atoms, so dead stripping must be redone - fLiveAtoms.clear(); - this->deadStripResolve(); - this->checkUndefines(); - } - else { - // LTO may require new library symbols to be loaded, so redo - this->checkUndefines(); - this->resolveReferences(); - } + break; + default: + break; } + + //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs); +#ifndef NDEBUG + validateFixups(atom); +#endif + fs->atoms.push_back(&atom); + return fs; } +ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& inputSection) +{ + const ld::Section* baseForFinalSection = &inputSection; + + // see if input section already has a FinalSection + SectionInToOut::iterator pos = _sectionInToFinalMap.find(&inputSection); + if ( pos != _sectionInToFinalMap.end() ) { + return pos->second; + } -void Linker::adjustScope() -{ - // if -exported_symbols_list is used, demoted to hidden, symbols that are not in it - if ( fOptions.hasExportRestrictList() ) { - // The use of an -export file means the previous computation of fHasExternalWeakDefinitions could change - fGlobalSymbolTable.setHasExternalWeakDefinitions(false); - for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - ObjectFile::Atom::Scope scope = atom->getScope(); - const char* name = atom->getName(); - if ( name != NULL ) { - if ( scope == ObjectFile::Atom::scopeGlobal ) { - // check for globals that are downgraded to hidden - if ( !fOptions.shouldExport(name) ) { - atom->setScope(ObjectFile::Atom::scopeLinkageUnit); - //fprintf(stderr, "demote %s to hidden\n", name); - } - else if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn ) { - // we do have an exported weak symbol, turn WEAK_DEFINES back on - fGlobalSymbolTable.setHasExternalWeakDefinitions(true); - } - } + // otherwise, create a new final section + bool objFile = false; + switch ( _options.outputKind() ) { + case Options::kStaticExecutable: + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kKextBundle: + case Options::kPreload: + { + // coalesce some sections + const ld::Section& outSect = FinalSection::outputSection(inputSection); + pos = _sectionInToFinalMap.find(&outSect); + if ( pos != _sectionInToFinalMap.end() ) { + _sectionInToFinalMap[&inputSection] = pos->second; + //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); + return pos->second; } - else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { - // check for hiddens that were requested to be exported - if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { - warning("cannot export hidden symbol %s from %s", name, atom->getFile()->getPath()); - } + else if ( outSect != inputSection ) { + // new output section created, but not in map + baseForFinalSection = &outSect; } } - } - } - - // linking is done, so demote hidden symbols to static - if ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ) { - // ld -r -keep_private_externs does not move hidden symbols to static - } - else { - for(std::vector::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - // hidden common symbols cannot be demoted to static - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (atom->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { - atom->setScope(ObjectFile::Atom::scopeTranslationUnit); - //fprintf(stderr, "demote %s to static\n", atom->getDisplayName()); + break; + case Options::kObjectFile: + baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options.makeTentativeDefinitionsReal()); + pos = _sectionInToFinalMap.find(baseForFinalSection); + if ( pos != _sectionInToFinalMap.end() ) { + _sectionInToFinalMap[&inputSection] = pos->second; + //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); + return pos->second; } - } + objFile = true; + break; } -} - -void Linker::link() -{ - this->buildAtomList(); - this->loadAndResolve(); - this->optimize(); - this->adjustScope(); - this->checkObjC(); - this->processDTrace(); - this->tweakLayout(); - this->addSynthesizedAtoms(); - this->sortSections(); - this->sortAtoms(); - this->writeDotOutput(); - this->collectDebugInfo(); - this->writeOutput(); - this->printStatistics(); - if ( fOptions.pauseAtEnd() ) - sleep(10); + InternalState::FinalSection* result = new InternalState::FinalSection(*baseForFinalSection, + _sectionInToFinalMap.size(), objFile); + _sectionInToFinalMap[baseForFinalSection] = result; + //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", baseForFinalSection, result); + sections.push_back(result); + return result; } -void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) + +int InternalState::FinalSection::sectionComparer(const void* l, const void* r) { - static uint64_t sUnitsPerSecond = 0; - if ( sUnitsPerSecond == 0 ) { - struct mach_timebase_info timeBaseInfo; - if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; - //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); - } - } - if ( partTime < sUnitsPerSecond ) { - uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; - uint32_t milliSeconds = milliSecondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; - uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); - } - else { - uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; - uint32_t seconds = secondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; - uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); - } + const FinalSection* left = *(FinalSection**)l; + const FinalSection* right = *(FinalSection**)r; + if ( left->_segmentOrder != right->_segmentOrder ) + return (left->_segmentOrder - right->_segmentOrder); + return (left->_sectionOrder - right->_sectionOrder); } -char* Linker::commatize(uint64_t in, char* out) +void InternalState::sortSections() { - char* result = out; - char rawNum[30]; - sprintf(rawNum, "%llu", in); - const int rawNumLen = strlen(rawNum); - for(int i=0; i < rawNumLen-1; ++i) { - *out++ = rawNum[i]; - if ( ((rawNumLen-i) % 3) == 1 ) - *out++ = ','; - } - *out++ = rawNum[rawNumLen-1]; - *out = '\0'; - return result; + //fprintf(stderr, "UNSORTED final sections:\n"); + //for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); + //} + qsort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer); + //fprintf(stderr, "SORTED final sections:\n"); + //for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); + //} + assert((sections[0]->type() == ld::Section::typeMachHeader) + || ((sections[0]->type() == ld::Section::typeFirstSection) && (sections[1]->type() == ld::Section::typeMachHeader)) + || ((sections[0]->type() == ld::Section::typePageZero) && (sections[1]->type() == ld::Section::typeMachHeader)) + || ((sections[0]->type() == ld::Section::typePageZero) && (sections[1]->type() == ld::Section::typeFirstSection) && (sections[2]->type() == ld::Section::typeMachHeader)) ); + } -void Linker::getVMInfo(vm_statistics_data_t& info) +static void getVMInfo(vm_statistics_data_t& info) { mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, @@ -901,3331 +553,113 @@ void Linker::getVMInfo(vm_statistics_data_t& info) } } -void Linker::printStatistics() +int main(int argc, const char* argv[]) { - fEndTime = mach_absolute_time(); - if ( fOptions.printStatistics() ) { - vm_statistics_data_t endVMInfo; - getVMInfo(endVMInfo); - - uint64_t totalTime = fEndTime - fStartTime; - printTime("ld total time", totalTime, totalTime); - printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); - printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); - printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); - printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime); - printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime); - printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); - printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); - printTime(" write output", fEndTime - fStartWriteTime, totalTime); - fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, - endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); - char temp[40]; - fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp)); - fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp)); - fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded); - fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp)); +#if DEBUG + usleep(1000000); +#endif + const char* archName = NULL; + bool showArch = false; + bool archInferred = false; + try { + vm_statistics_data_t vmStart; + vm_statistics_data_t vmEnd; + getVMInfo(vmStart); + + // create object to track command line arguments + Options options(argc, argv); + + // gather stats + if ( options.printStatistics() ) + getVMInfo(vmStart); + + // update strings for error messages + showArch = options.printArchPrefix(); + archName = options.architectureName(); + archInferred = (options.architecture() == 0); + + // open and parse input files + ld::tool::InputFiles inputFiles(options, &archName); + + // load and resolve all references + InternalState state(options); + ld::tool::Resolver resolver(options, inputFiles, state); + resolver.resolve(); + + // add dylibs used + inputFiles.dylibs(state); + + // do initial section sorting so passes have rough idea of the layout + state.sortSections(); + + // run passes + ld::passes::objc::doPass(options, state); + ld::passes::stubs::doPass(options, state); + ld::passes::huge::doPass(options, state); + ld::passes::got::doPass(options, state); + ld::passes::tlvp::doPass(options, state); + ld::passes::dylibs::doPass(options, state); // must be after stubs and GOT passes + ld::passes::order_file::doPass(options, state); + ld::passes::branch_shim::doPass(options, state); // must be after stubs + ld::passes::branch_island::doPass(options, state); // must be after stubs and order_file pass + ld::passes::dtrace::doPass(options, state); + ld::passes::compact_unwind::doPass(options, state); // must be after order-file pass + + // sort final sections + state.sortSections(); + + // write output file + ld::tool::OutputFile out(options); + out.write(state); + + // print statistics + //mach_o::relocatable::printCounts(); + if ( options.printStatistics() ) { + getVMInfo(vmEnd); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", vmEnd.pageins-vmStart.pageins, + vmEnd.pageouts-vmStart.pageouts, vmEnd.faults-vmStart.faults); + + } + } + catch (const char* msg) { + if ( archInferred ) + fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); + else if ( showArch ) + fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); + else + fprintf(stderr, "ld: %s\n", msg); + return 1; } + + return 0; } -inline void Linker::addAtom(ObjectFile::Atom& atom) + +#ifndef NDEBUG +// implement assert() function to print out a backtrace before aborting +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) { - // add to list of all atoms - fAllAtoms.push_back(&atom); + fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); - if ( fOptions.deadStrip() == Options::kDeadStripOff ) { - // not dead-stripping code, so add atom's references's names to symbol table as to-be-resolved-later - std::vector& references = atom.getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) - fGlobalSymbolTable.require(reference->getTargetName()); - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) - fGlobalSymbolTable.require(reference->getFromTargetName()); - } - // update total size info (except for __ZEROPAGE atom) - if ( atom.getSegment().isContentReadable() ) { - fTotalSize += atom.getSize(); - if ( atom.isZeroFill() ) - fTotalZeroFillSize += atom.getSize(); + void* callStack[128]; + int depth = ::backtrace(callStack, 128); + char* buffer = (char*)malloc(1024); + for(int i=0; i < depth-1; ++i) { + Dl_info info; + dladdr(callStack[i], &info); + const char* symboName = info.dli_sname; + if ( (symboName != NULL) && (strncmp(symboName, "_Z", 2) == 0) ) { + size_t bufLen = 1024; + int result; + char* unmangled = abi::__cxa_demangle(symboName, buffer, &bufLen, &result); + if ( unmangled != NULL ) + symboName = unmangled; } + long offset = (uintptr_t)callStack[i] - (uintptr_t)info.dli_saddr; + fprintf(stderr, "%d %p %s + %ld\n", i, callStack[i], symboName, offset); } - else { - if ( atom.dontDeadStrip() ) - fLiveRootAtoms.insert(&atom); - } + exit(1); +} +#endif - // if in global namespace, add atom itself to symbol table - ObjectFile::Atom::Scope scope = atom.getScope(); - const char* name = atom.getName(); - if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { - // add to symbol table - fGlobalSymbolTable.add(atom); - } - // record section orders so output file can have same order - if (atom.getSectionName()) { - bool untrusted = false; - switch ( atom.getContentType() ) { - case ObjectFile::Atom::kSectionStart: - case ObjectFile::Atom::kSectionEnd: - untrusted = true; - default: - break; - } - atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill(), untrusted)); - } -} - - -void Linker::markDead(ObjectFile::Atom* atom) -{ - //fprintf(stderr, "markDead(%p) %s from %s\n", atom, atom->getDisplayName(), atom->getFile()->getPath()); - fDeadAtoms.insert(atom); - - // -dead_strip inhibits weak coalescing in no_dead_strip section - if ( fLiveRootAtoms.count(atom) != 0 ) { - fLiveRootAtoms.erase(atom); - } - - // - // The kGroupSubordinate reference kind is used to model group comdat. - // The "signature" atom in the group has a kGroupSubordinate reference to - // all other members of the group. So, if the signature atom is - // coalesced away, all other atoms in the group should also be removed. - // - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX - ObjectFile::Atom* targetAtom = &(ref->getTarget()); - //fprintf(stderr, " markDead(%p) subordinate %s\n", targetAtom, targetAtom->getDisplayName()); - if ( targetAtom == NULL ) { - warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName()); - } - else { - if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) { - // ok for .eh symbols to be not static in -r mode - if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) ) - warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName()); - } - this->markDead(targetAtom); - } - } - } -} - -void Linker::updateConstraints(ObjectFile::Reader* reader) -{ - // check objc objects were compiled compatibly - ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); - if ( reader->getInstallPath() == NULL ) { - // adding a .o file - switch ( objcAddition ) { - case ObjectFile::Reader::kObjcNone: - break; - case ObjectFile::Reader::kObjcRetainRelease: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); - fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease; - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone ) - fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - break; - case ObjectFile::Reader::kObjcGC: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); - fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC; - break; - } - } - if ( reader->objcReplacementClasses() ) - fObjcReplacmentClasses = true; - - // check cpu sub-types for stricter sub-type - fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint); -} - -inline void Linker::addAtoms(std::vector& atoms) -{ - bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; - bool first = true; - for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { - // usually we only need to get the first atom's reader, but - // with -all_load all atoms from all .o files come come back together - // so we need to scan all atoms - if ( first || scanAll ) { - // update fReadersThatHaveSuppliedAtoms - ObjectFile::Reader* reader = (*it)->getFile(); - if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) - == fReadersThatHaveSuppliedAtoms.end() ) { - fReadersThatHaveSuppliedAtoms.push_back(reader); - updateConstraints(reader); - } - } - this->addAtom(**it); - first = false; - } -} - -void Linker::logArchive(ObjectFile::Reader* reader) -{ - if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) { - fArchiveReadersLogged.insert(reader); - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); - } -} - - -void Linker::buildAtomList() -{ - fStartBuildAtomsTime = mach_absolute_time(); - // add initial undefines from -u option - std::vector& initialUndefines = fOptions.initialUndefines(); - for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { - fGlobalSymbolTable.require(*it); - } - - // writer can contribute atoms - this->addAtoms(fOutputFile->getAtoms()); - - // each reader contributes atoms - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - std::vector& atoms = reader->getAtoms(); - this->addAtoms(atoms); - if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) ) - logArchive(reader); - } - - // extra command line section always at end - std::vector& extraSections = fOptions.extraSections(); - for( std::vector::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { - this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); - fNextInputOrdinal += it->dataLen; - } - - // done with all .o files on command line - // everything loaded from now on is a just-in-time atom - fInitialLoadsDone = true; -} - -static const char* pathLeafName(const char* path) -{ - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - return path; - else - return &shortPath[1]; -} - - -void Linker::loadUndefines() -{ - // keep looping until no more undefines were added in last loop - unsigned int undefineCount = 0xFFFFFFFF; - while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { - undefineCount = fGlobalSymbolTable.getRequireCount(); - std::vector undefineNames; - fGlobalSymbolTable.getUndefinesNames(undefineNames); - for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - // load for previous undefine may also have loaded this undefine, so check again - if ( fGlobalSymbolTable.find(*it) == NULL ) { - std::vector* atoms = this->addJustInTimeAtoms(*it, true, true, true); - if ( atoms != NULL ) - delete atoms; - } - } - // need to search archives for overrides of common symbols - if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { - bool searchDylibs = (fOptions.commonsMode() == Options::kCommonsOverriddenByDylibs); - std::vector tentativeDefinitionNames; - fGlobalSymbolTable.getTentativesNames(tentativeDefinitionNames); - for(std::vector::iterator it = tentativeDefinitionNames.begin(); it != tentativeDefinitionNames.end(); ++it) { - // load for previous tentative may also have overridden this tentative, so check again - ObjectFile::Atom* tent = fGlobalSymbolTable.find(*it); - if ( (tent != NULL) && (tent->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { - std::vector* atoms = this->addJustInTimeAtoms(*it, searchDylibs, true, false); - if ( atoms != NULL ) - delete atoms; - } - } - } - } -} - -// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names -class ExportedObjcClass -{ -public: - ExportedObjcClass(Options& opt) : fOptions(opt) {} - - bool operator()(const char* name) const { - if ( fOptions.shouldExport(name) ) { - if ( strncmp(name, ".objc_class_name_", 17) == 0 ) - return true; - if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 ) - return true; - if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 ) - return true; - } - //fprintf(stderr, "%s is not exported\n", name); - return false; - } -private: - Options& fOptions; -}; - - -void Linker::checkUndefines() -{ - // error out on any remaining undefines - bool doPrint = true; - bool doError = true; - switch ( fOptions.undefinedTreatment() ) { - case Options::kUndefinedError: - break; - case Options::kUndefinedDynamicLookup: - doError = false; - break; - case Options::kUndefinedWarning: - doError = false; - break; - case Options::kUndefinedSuppress: - doError = false; - doPrint = false; - break; - } - std::vector unresolvableUndefines; - fGlobalSymbolTable.getUndefinesNames(unresolvableUndefines); - - // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names - // ignore unresolved references to Objc class names that are listed in -exported_symbols_list - if ( fOptions.hasExportRestrictList() ) - unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end()); - - const int unresolvableCount = unresolvableUndefines.size(); - int unresolvableExportsCount = 0; - if ( unresolvableCount != 0 ) { - if ( doPrint ) { - if ( fOptions.printArchPrefix() ) - fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); - else - fprintf(stderr, "Undefined symbols:\n"); - for (int i=0; i < unresolvableCount; ++i) { - const char* name = unresolvableUndefines[i]; - fprintf(stderr, " \"%s\", referenced from:\n", name); - // scan all atoms for references - bool foundAtomReference = false; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - if ( strcmp(reference->getTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } - } - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - if ( strcmp(reference->getFromTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } - } - } - } - // scan command line options - if ( !foundAtomReference ) { - // might be from -init command line option - if ( (fOptions.initFunctionName() != NULL) && (strcmp(name, fOptions.initFunctionName()) == 0) ) { - fprintf(stderr, " -init command line option\n"); - } - // or might be from exported symbol option - else if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { - fprintf(stderr, " -exported_symbol[s_list] command line option\n"); - } - else { - bool isInitialUndefine = false; - std::vector& clundefs = fOptions.initialUndefines(); - for (std::vector::iterator uit = clundefs.begin(); uit != clundefs.end(); ++uit) { - if ( strcmp(*uit, name) == 0 ) { - isInitialUndefine = true; - break; - } - } - if ( isInitialUndefine ) - fprintf(stderr, " -u command line option\n"); - } - ++unresolvableExportsCount; - } - // be helpful and check for typos - bool printedStart = false; - for (SymbolTable::Mapper::iterator sit=fGlobalSymbolTable.begin(); sit != fGlobalSymbolTable.end(); ++sit) { - if ( (sit->second != NULL) && (strstr(sit->first, name) != NULL) ) { - if ( ! printedStart ) { - fprintf(stderr, " (maybe you meant: %s", sit->first); - printedStart = true; - } - else { - fprintf(stderr, ", %s ", sit->first); - } - } - } - if ( printedStart ) - fprintf(stderr, ")\n"); - } - } - if ( doError ) - throw "symbol(s) not found"; - } - - // for each tentative definition in symbol table look for dylib that exports same symbol name - if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - // look for dylibs that export same name as used by global tentative definition - addJustInTimeAtoms(atom->getName(), true, false, false); - } - } - } - - - // record any overrides of weak symbols any linked dylib - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - const char* name = atom->getName(); - //fprintf(stderr, "looking for dylibs with a weak %s\n", name); - // look for dylibs with weak exports of the same name - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - ObjectFile::Reader* reader = it->second; - if ( reader->hasWeakExternals() ) { - std::vector* dylibAtoms = reader->getJustInTimeAtomsFor(name); - if ( dylibAtoms != NULL ) { - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - // if this is a weak definition in a dylib - if ( (dylibAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fRegularDefAtomsThatOverrideADylibsWeakDef.insert(atom); - } - } - } - } - } - } - -} - - - -std::vector* Linker::addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy) -{ - //fprintf(stderr, "addJustInTimeAtoms(%s, searchDylibs=%d, searchArchives=%d)\n", name, searchDylibs, searchArchives ); - // when creating final linked image, writer gets first chance - if ( fOptions.outputKind() != Options::kObjectFile ) { - std::vector* atoms = fOutputFile->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); - return atoms; // found a definition, no need to search anymore - } - } - - // give readers a chance - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - // if this reader is a static archive that has the symbol we need, pull in all atoms in that module - // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. - //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); - bool isDylibReader = (reader->getInstallPath() != NULL); - if ( isDylibReader ? searchDylibs : searchArchives ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) { - logArchive(reader); - } - // if this is a weak definition in a dylib - if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - // keep looking for a non-weak definition - } - else { - // found a definition, no need to search anymore - return atoms; - } - } - } - } - } - - // for two level namesapce, give all implicitly link dylibs a chance - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->implicitlyLinked() ) { - //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() ); - std::vector* atoms = it->second->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - // if this is a weak definition in a dylib - if ( (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - // keep looking for a non-weak definition - } - else { - // found a definition, no need to search anymore - return atoms; - } - } - } - } - } - - // for flat namespace, give indirect dylibs - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) { - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( ! it->second->explicitlyLinked() ) { - std::vector* atoms = it->second->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - return atoms; // found a definition, no need to search anymore - } - } - } - } - - // writer creates a proxy in two cases: - // 1) ld -r is being used to create a .o file - // 2) -undefined dynamic_lookup is being used - // 3) -U _foo is being used - // 4) x86_64 kext bundle is being created - if ( (fOptions.outputKind() == Options::kObjectFile) - || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && okToMakeProxy) - || (fOptions.someAllowedUndefines() && okToMakeProxy) - || (fOptions.outputKind() == Options::kKextBundle) ) { - ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); - if ( atom != NULL ) { - this->addAtom(*atom); - return NULL; - } - } - //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); - return NULL; -} - -void Linker::resolve(ObjectFile::Reference* reference) -{ - // look in global symbol table - const char* targetName = reference->getTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { - throwf("unexpected undefined symbol: %s", targetName); - } - reference->setTarget(*target, reference->getTargetOffset()); -} - -void Linker::resolveFrom(ObjectFile::Reference* reference) -{ - // handle references that have two (from and to) targets - const char* fromTargetName = reference->getFromTargetName(); - ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); - if ( fromTarget == NULL ) { - throwf("unexpected undefined symbol: %s", fromTargetName); - } - reference->setFromTarget(*fromTarget); -} - - -void Linker::resolveReferences() -{ - // note: the atom list may grow during this loop as libraries supply needed atoms - for (unsigned int j=0; j < fAllAtoms.size(); ++j) { - ObjectFile::Atom* atom = fAllAtoms[j]; - std::vector& references = atom->getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) - this->resolve(reference); - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) - this->resolveFrom(reference); - } - } -} - - -// used to remove stabs associated with atoms that won't be in output file -class NotInSet -{ -public: - NotInSet(std::set& theSet) : fSet(theSet) {} - - bool operator()(const ObjectFile::Reader::Stab& stab) const { - if ( stab.atom == NULL ) - return false; // leave stabs that are not associated with any atome - else - return ( fSet.count(stab.atom) == 0 ); - } - -private: - std::set& fSet; -}; - - -class NotLive -{ -public: - NotLive(std::set& set) : fLiveAtoms(set) {} - - bool operator()(ObjectFile::Atom*& atom) const { - //if ( fLiveAtoms.count(atom) == 0 ) - // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); - return ( fLiveAtoms.count(atom) == 0 ); - } -private: - std::set& fLiveAtoms; -}; - - -void Linker::addJustInTimeAtomsAndMarkLive(const char* name) -{ - //fprintf(stderr, "addJustInTimeAtomsAndMarkLive(%s)\n", name); - std::vector* atoms = this->addJustInTimeAtoms(name, true, true, true); - if ( atoms != NULL ) { - if ( fOptions.allGlobalsAreDeadStripRoots() ) { - for (std::vector::iterator it=atoms->begin(); it != atoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.referer = atom; - this->markLive(*atom, &rootChain); - } - } - } - delete atoms; - } -} - -void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) -{ - //fprintf(stderr, "markLive(%p)\n", &atom); - if ( fLiveAtoms.count(&atom) == 0 ) { - // if -why_live cares about this symbol, then dump chain - if ( (previous->referer != NULL) && fOptions.printWhyLive(previous->referer->getDisplayName()) ) { - int depth = 0; - for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { - for(int i=depth; i > 0; --i) - fprintf(stderr, " "); - fprintf(stderr, "%p %s from %s\n", p->referer, p->referer->getDisplayName(), p->referer->getFile()->getPath()); - } - } - // set up next chain - WhyLiveBackChain thisChain; - thisChain.previous = previous; - // this atom is live - fLiveAtoms.insert(&atom); - // update total size info (except for __ZEROPAGE atom) - if ( atom.getSegment().isContentReadable() ) { - fTotalSize += atom.getSize(); - if ( atom.isZeroFill() ) - fTotalZeroFillSize += atom.getSize(); - } - // and all atoms it references - std::vector& references = atom.getReferences(); - for (std::vector::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - // look in global symbol table - const char* targetName = reference->getTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { - // load archives or dylibs - this->addJustInTimeAtomsAndMarkLive(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setTarget(*target, reference->getTargetOffset()); - } - else { - // mark as undefined, for later error processing - fAtomsWithUnresolvedReferences.push_back(&atom); - fGlobalSymbolTable.require(targetName); - } - } - switch ( reference->getTargetBinding() ) { - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - thisChain.referer = &reference->getTarget(); - markLive(reference->getTarget(), &thisChain); - break; - case ObjectFile::Reference::kDontBind: - case ObjectFile::Reference::kUnboundByName: - // do nothing - break; - } - // do the same as above, for "from target" - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - // look in global symbol table - const char* targetName = reference->getFromTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { - // load archives or dylibs - this->addJustInTimeAtomsAndMarkLive(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setFromTarget(*target); - } - else { - // mark as undefined, for later error processing - fGlobalSymbolTable.require(targetName); - } - } - switch ( reference->getFromTargetBinding() ) { - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - thisChain.referer = &reference->getFromTarget(); - markLive(reference->getFromTarget(), &thisChain); - break; - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - // do nothing - break; - } - } - } -} - - -void Linker::addLiveRoot(const char* name) -{ - ObjectFile::Atom* target = fGlobalSymbolTable.find(name); - if ( target == NULL ) { - this->addJustInTimeAtomsAndMarkLive(name); - target = fGlobalSymbolTable.find(name); - } - if ( target != NULL ) - fLiveRootAtoms.insert(target); -} - -void Linker::moveToFrontOfSection(ObjectFile::Atom* atom) -{ - // check if already moved to front - if ( fInitializerAtoms.find(atom) == fInitializerAtoms.end() ) { - // don't re-order initializers from .o files without MH_SUBSECTIONS_VIA_SYMBOLS - // since that could make all atoms in the file look like initializers - if ( atom->getFile()->canScatterAtoms() ) { - //fprintf(stdout, "marking as initializer: %s\n", atom->getDisplayName()); - fInitializerAtoms.insert(atom); - // mark all functions that this function references - std::vector& references = atom->getReferences(); - for (std::vector::const_iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Atom* childAtom = &((*rit)->getTarget()); - if ( childAtom != NULL ) { - if ( (*rit)->isBranch() ) { - this->moveToFrontOfSection(childAtom); - } - else if ( (childAtom->getName() != NULL) && (strncmp(childAtom->getName(), "___tcf_", 7) == 0) ) { - //fprintf(stdout, "marking as terminator: %s\n", childAtom->getDisplayName()); - fTerminatorAtoms.insert(childAtom); - } - } - } - } - } -} - -void Linker::deadStripResolve() -{ - // add main() to live roots - ObjectFile::Atom* entryPoint = this->entryPoint(false, true); - if ( entryPoint != NULL ) - fLiveRootAtoms.insert(entryPoint); - - // add dyld_stub_binding_helper/dyld_stub_binder to live roots - ObjectFile::Atom* dyldHelper = this->dyldClassicHelper(); - if ( dyldHelper != NULL ) - fLiveRootAtoms.insert(dyldHelper); - dyldHelper = this->dyldCompressedHelper(); - if ( dyldHelper != NULL ) - fLiveRootAtoms.insert(dyldHelper); - - // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots - if ( fOptions.usingLazyDylibLinking() ) { - ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper(); - if ( dyldLazyDylibHelper != NULL ) - fLiveRootAtoms.insert(dyldLazyDylibHelper); - } - - // add -exported_symbols_list, -init, and -u entries to live roots - std::vector& initialUndefines = fOptions.initialUndefines(); - for (std::vector::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) - addLiveRoot(*it); - - // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots - // - if ( fOptions.hasWildCardExportRestrictList() ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) - && (fDeadAtoms.count(atom) == 0) - && fOptions.shouldExport(atom->getName()) ) - fLiveRootAtoms.insert(atom); - } - } - - // in some cases, every global scope atom in initial .o files is a root - if ( fOptions.allGlobalsAreDeadStripRoots() ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) - fLiveRootAtoms.insert(atom); - } - } - - // mark all roots as live, and all atoms they reference - for (std::set::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.referer = *it; - markLive(**it, &rootChain); - } - - // it is possible that there are unresolved references that can be resolved now - // this can happen if the first reference to a common symbol in an archive. - // common symbols are not in the archive TOC, but the .o could have been pulled in later. - // ld64 while linking cc1 [ when dead_strip is ON] - for (std::vector::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { - std::vector& references = (*it)->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); - if ( target != NULL ) { - reference->setTarget(*target, reference->getTargetOffset()); - fLiveAtoms.insert(target); - // by just adding this atom to fLiveAtoms set, we are assuming it has no - // references, which is true for commons. - if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) - warning("internal error %s is not a tentative definition", target->getDisplayName()); - } - } - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); - if ( target != NULL ) { - reference->setFromTarget(*target); - fLiveAtoms.insert(target); - // by just adding this atom to fLiveAtoms set, we are assuming it has no - // references, which is true for commons. - if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) - warning("internal error %s is not a tentative definition", target->getDisplayName()); - } - } - } - } - - // It is possible that some weak symbols were overridden by lazily load objects from archives - // and we have some atoms that still refer to the overridden ones. - // In that case we need to go back and rebind - if ( fAtomsOverriddenByLateLoads.size() > 0 ) { - for (std::set::iterator it=fLiveAtoms.begin(); it != fLiveAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* reference = *rit; - ObjectFile::Atom* toTarget = &reference->getTarget(); - if ( fAtomsOverriddenByLateLoads.count(toTarget) ) { - //fprintf(stderr, "change reference in %p from %p to %p\n", atom, toTarget, fGlobalSymbolTable.find(toTarget->getName())); - reference->setTarget(*fGlobalSymbolTable.find(toTarget->getName()), reference->getTargetOffset()); - } - ObjectFile::Atom* fromTarget = &reference->getFromTarget(); - if ( (fromTarget != NULL) && fAtomsOverriddenByLateLoads.count(fromTarget) ) { - //fprintf(stderr, "change from reference in %p from %p to %p\n", atom, fromTarget, fGlobalSymbolTable.find(fromTarget->getName())); - reference->setTarget(*fGlobalSymbolTable.find(fromTarget->getName()), reference->getFromTargetOffset()); - } - } - } - - // make sure overriders are live if the atom they overrid was live - for (std::set::iterator it=fAtomsOverriddenByLateLoads.begin(); it != fAtomsOverriddenByLateLoads.end(); ++it) { - ObjectFile::Atom* overriderAtom = *it; - if ( fLiveAtoms.count(overriderAtom) ) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.referer = *it; - markLive(*fGlobalSymbolTable.find(overriderAtom->getName()), &rootChain); - } - } - - // remove overridden atoms from fLiveAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fAtomsOverriddenByLateLoads)), fAllAtoms.end()); - fAtomsOverriddenByLateLoads.clear(); - // remove dead atoms from fLiveAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - } - - // now remove all non-live atoms from fAllAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); -} - -void Linker::checkObjC() -{ - // check dylibs - switch ( fCurrentObjCConstraint ) { - case ObjectFile::Reader::kObjcNone: - // can link against any dylib - break; - case ObjectFile::Reader::kObjcRetainRelease: - // cannot link against GC-only dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->explicitlyLinked() ) { - if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC ) - throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath()); - } - } - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - // can link against GC or RR dylibs - break; - case ObjectFile::Reader::kObjcGC: - // cannot link against RR-only dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->explicitlyLinked() ) { - if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease ) - throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath()); - } - } - break; - } - - // synthesize __OBJC __image_info atom if needed - if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) { - this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses)); - } -} - - -static uint8_t pcRelKind(cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - return ppc::kPointerDiff32; - case CPU_TYPE_POWERPC64: - return ppc64::kPointerDiff32; - case CPU_TYPE_I386: - return x86::kPointerDiff; - case CPU_TYPE_X86_64: - return x86_64::kPointerDiff32; - case CPU_TYPE_ARM: - return arm::kPointerDiff; - } - throw "uknown architecture"; -} - -typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); -typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); - - -void Linker::processDTrace() -{ - // only make __dof section in final linked images - if ( fOptions.outputKind() == Options::kObjectFile ) - return; - - // scan all atoms looking for dtrace probes - std::vector probeSites; - std::vector isEnabledSites; - std::map atomToDtraceTypes; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* ref = *rit; - if ( ref->getTargetBinding() == ObjectFile::Reference::kDontBind ) { - const char* probeName = ref->getTargetName(); - if ( probeName != NULL ) { - uint32_t offsetInAtom = ref->getFixUpOffset(); - if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) - probeSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) - isEnabledSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) - atomToDtraceTypes[atom].insert(probeName); - } - } - } - } - - // if no probes, we're done - if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) ) - return; - - // partition probes by provider name - // The symbol names looks like: - // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] - // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] - ProviderToProbes providerToProbes; - std::vector emptyList; - for(std::vector::iterator it = probeSites.begin(); it != probeSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[16]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - for(std::vector::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[20]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - - // create a DOF section for each provider - int dofIndex=1; - CStringSet sectionNamesUsed; - for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { - const char* providerName = pit->first; - const std::vector& probes = pit->second; - - // open library and find dtrace_create_dof() - void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); - if ( handle == NULL ) - throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); - createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); - if ( pCreateDOF == NULL ) - throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); - // build list of typedefs/stability infos for this provider - CStringSet types; - for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { - std::map::iterator pos = atomToDtraceTypes.find(it->atom); - if ( pos != atomToDtraceTypes.end() ) { - for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { - const char* providerStart = strchr(*sit, '$')+1; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char aProviderName[providerEnd-providerStart+1]; - strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); - if ( strcmp(aProviderName, providerName) == 0 ) - types.insert(*sit); - } - } - } - } - int typeCount = types.size(); - const char* typeNames[typeCount]; - //fprintf(stderr, "types for %s:\n", providerName); - uint32_t index = 0; - for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { - typeNames[index] = *it; - //fprintf(stderr, "\t%s\n", *it); - ++index; - } - - // build list of probe/isenabled sites - const uint32_t probeCount = probes.size(); - const char* probeNames[probeCount]; - const char* funtionNames[probeCount]; - uint64_t offsetsInDOF[probeCount]; - index = 0; - for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { - probeNames[index] = it->probeName; - funtionNames[index] = it->atom->getName(); - offsetsInDOF[index] = 0; - ++index; - } - //fprintf(stderr, "calling libtrace to create DOF\n"); - //for(uint32_t i=0; i < probeCount; ++i) - // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); - // call dtrace library to create DOF section - size_t dofSectionSize; - uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); - if ( p != NULL ) { - char sectionName[18]; - strcpy(sectionName, "__dof_"); - strlcpy(§ionName[6], providerName, 10); - // create unique section name so each DOF is in its own section - if ( sectionNamesUsed.count(sectionName) != 0 ) { - sectionName[15] = '0'; - sectionName[16] = '\0'; - while ( sectionNamesUsed.count(sectionName) != 0 ) - ++sectionName[15]; - } - sectionNamesUsed.insert(sectionName); - char symbolName[strlen(providerName)+64]; - sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); - opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, - "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); - fNextInputOrdinal += dofSectionSize; - // add references - for (uint32_t i=0; i < probeCount; ++i) { - uint64_t offset = offsetsInDOF[i]; - //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); - if ( offset > dofSectionSize ) - throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); - reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); - } - this->addAtoms(reader->getAtoms()); - } - else { - throw "error creating dtrace DOF section"; - } - } -} - - -static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName) -{ - if ( objectFileLeafName == NULL ) - return true; - const char* atomFullPath = atom->getFile()->getPath(); - const char* lastSlash = strrchr(atomFullPath, '/'); - if ( lastSlash != NULL ) { - if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) - return true; - } - else { - if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) - return true; - } - return false; -} - - -ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) -{ - ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); - if ( atom != NULL ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) - return atom; - } - else { - // slow case. The requested symbol is not in symbol table, so might be static function - static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; - static bool built = false; - // build a hash_map the first time - if ( !built ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - const char* name = atom->getName(); - if ( name != NULL) { - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // static function or data - SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); - if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) - hashTableOfTranslationUnitScopedSymbols[name] = atom; - else - hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL - } - } - } - //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size()); - built = true; - } - - // look for name in hashTableOfTranslationUnitScopedSymbols - SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName); - if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) { - if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { - //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); - return pos->second; - } - if ( pos->second == NULL ) - // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - const char* name = atom->getName(); - if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { - if ( fOptions.printOrderFileStatistics() ) - warning("%s specified in order_file but it exists in multiple .o files. " - "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); - return atom; - } - } - } - } - } - - } - return NULL; -} - - -void Linker::sortSections() -{ - Section::assignIndexes(fOptions.outputKind() == Options::kObjectFile); -} - - -// -// Linker::sortAtoms() -// -// The purpose of this method is to take the graph of all Atoms and produce an ordered -// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must -// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified -// in an order_file are seqenced as in the order_file and before Atoms not specified, -// 4) Atoms in the same section from the same .o file should be contiguous and sequenced -// in the same order they were in the .o file, 5) Atoms in the same Section but which came -// from different .o files should be sequenced in the same order that the .o files -// were passed to the linker (i.e. command line order). -// -// The way this is implemented is that the linker passes a "base ordinal" to each Reader -// as it is constructed. The reader should construct it Atoms so that calling getOrdinal() -// on its atoms returns a contiguous range of values starting at the base ordinal. Then -// sorting is just sorting by section, then by ordinal. -// -// If an order_file is specified, it gets more complicated. First, an override-ordinal map -// is created. It causes the sort routine to ignore the value returned by getOrdinal() and -// use the override value instead. Next some Atoms must be layed out consecutively -// (e.g. hand written assembly that does not end with return, but rather falls into -// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of -// kFollowOn refernces produces "clusters" of atoms that must stay together. -// If an order_file tries to move one atom, it may need to move a whole cluster. The -// algorithm to do this models clusters using two maps. The "starts" maps maps any -// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a -// cluster to the next Atom in the cluster. With this in place, while processing an -// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is -// given ordinal overrides. -// -void Linker::sortAtoms() -{ - fStartSortTime = mach_absolute_time(); - // if -order_file is used, build map of atom ordinal overrides - std::map* ordinalOverrideMap = NULL; - std::map theOrdinalOverrideMap; - const bool log = false; - if ( fOptions.orderedSymbols().size() != 0 ) { - // first make a pass to find all follow-on references and build start/next maps - // which are a way to represent clusters of atoms that must layout together - std::map followOnStarts; - std::map followOnNexts; - for (std::vector::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) { - ObjectFile::Atom* atom = *ait; - std::vector& references = atom->getReferences(); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == 1 ) { // FIX FIX - ObjectFile::Atom* targetAtom = &ref->getTarget(); - if ( log ) fprintf(stderr, "ref %s -> %s", atom->getDisplayName(), targetAtom->getDisplayName()); - std::map::iterator startFrom = followOnStarts.find(atom); - std::map::iterator startTo = followOnStarts.find(targetAtom); - if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) { - // this is first time we've seen either atom, make simple cluster of the two - if ( log ) fprintf(stderr, " new cluster\n"); - followOnStarts[atom] = atom; - followOnStarts[targetAtom] = atom; - followOnNexts[atom] = targetAtom; - followOnNexts[targetAtom] = NULL; - } - else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) { - // atom is at end of an existing cluster, so append target to end of cluster - if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName()); - followOnNexts[atom] = targetAtom; - followOnNexts[targetAtom] = NULL; - followOnStarts[targetAtom] = followOnStarts[atom]; - } - else { - // gerneral case of inserting into an existing cluster - if ( followOnNexts[atom] != NULL ) { - // an atom with two follow-ons is illegal - warning("can't order %s because both %s and %s must follow it", - atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName()); - } - else { - // there already exists an atom that says target must be its follow-on - const ObjectFile::Atom* originalStart = startTo->second; - const ObjectFile::Atom* originalPrevious = originalStart; - while ( followOnNexts[originalPrevious] != targetAtom ) - originalPrevious = followOnNexts[originalPrevious]; - bool otherIsAlias = (originalPrevious->getSize() == 0); - bool thisIsAlias = (atom->getSize() == 0); - if ( !otherIsAlias && !thisIsAlias ) { - warning("can't order %s because both %s and %s must preceed it", - targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName()); - } - else if ( otherIsAlias ) { - if ( originalPrevious == originalStart ) { - // other is alias at start of cluster, make this the new start of cluster - if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName()); - followOnNexts[atom] = originalPrevious; - for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) - followOnStarts[nextAtom] = atom; - } - else { - // other is alias in middle of cluster, insert new atom before it - if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); - followOnStarts[atom] = originalStart; - followOnNexts[atom] = originalPrevious; - for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) { - if ( followOnNexts[a] == originalPrevious ) { - followOnNexts[a] = atom; - break; - } - } - } - } - else { - // this is alias, so it can go inbetween originalPrevious and targetAtom - if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); - followOnStarts[atom] = originalStart; - followOnNexts[atom] = followOnNexts[originalPrevious]; - followOnNexts[originalPrevious] = atom; - } - } - } - } - } - } - - if ( log ) { - for(std::map::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it) - fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName()); - - for(std::map::iterator it = followOnNexts.begin(); it != followOnNexts.end(); ++it) - fprintf(stderr, "next %s -> %s\n", it->first->getDisplayName(), (it->second != NULL) ? it->second->getDisplayName() : "null"); - } - - // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals - ordinalOverrideMap = &theOrdinalOverrideMap; - uint32_t index = 0; - uint32_t matchCount = 0; - std::vector& orderedSymbols = fOptions.orderedSymbols(); - for(std::vector::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) { - ObjectFile::Atom* atom = this->findAtom(*it); - if ( atom != NULL ) { - std::map::iterator start = followOnStarts.find(atom); - if ( start != followOnStarts.end() ) { - // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together - for(const ObjectFile::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) { - std::map::iterator pos = theOrdinalOverrideMap.find(nextAtom); - if ( pos == theOrdinalOverrideMap.end() ) { - theOrdinalOverrideMap[nextAtom] = index++; - if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath()); - } - else { - if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", - atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] ); - } - } - } - else { - theOrdinalOverrideMap[atom] = index; - if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); - } - ++matchCount; - } - else { - if ( fOptions.printOrderFileStatistics() ) { - if ( it->objectFileName == NULL ) - warning("can't find match for order_file entry: %s", it->symbolName); - else - warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName); - } - } - ++index; - } - if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) { - warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() ); - } - } - - // sort atoms - std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap, fInitializerAtoms, fTerminatorAtoms)); - - //fprintf(stderr, "Sorted atoms:\n"); - //for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - // fprintf(stderr, "\t%s, %u %s\t%s\n", (*it)->getSectionName(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath()); - //} -} - - -// make sure given addresses are within reach of branches, etc -void Linker::tweakLayout() -{ - // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB - if ( fTotalSize > 0x7F000000 ) { - fBiggerThanTwoGigOutput = true; - - if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 ) - throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); - - // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment - Section* hugeZeroFills = Section::find("__huge", "__DATA", true, true); - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) ) - atom->setSection(hugeZeroFills); - } - } - - // move all initializers to start of __text section - if ( fOptions.readerOptions().fAutoOrderInitializers ) { - // move -init function to front of __text - if ( fOptions.initFunctionName() != NULL ) { - ObjectFile::Atom* initAtom = fGlobalSymbolTable.find(fOptions.initFunctionName()); - if ( initAtom == NULL ) - throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); - moveToFrontOfSection(initAtom); - } - - // move all functions pointed to by __mod_init_func section to front of __text - Section* initSection = Section::find("__mod_init_func", "__DATA", false, true, false); - if ( initSection != NULL ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - if ( (*it)->getSection() == initSection ) { - std::vector& references = (*it)->getReferences(); - if ( references.size() == 1 ) - moveToFrontOfSection(&(references[0]->getTarget())); - } - } - } - } - - // move atoms with relocations to start of __DATA,__data section - // linker should order __DATA segment to reduce dyld dirtied pages - if ( fOptions.orderData() ) { - bool slideable = false; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kPreload: - case Options::kObjectFile: - case Options::kKextBundle: - slideable = false; - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - slideable = true; - break; - } - const bool hasPreferredLoadAddress = (fOptions.baseAddress() != 0); - Section* dataSection = Section::find("__data", "__DATA", false, true, false); - if ( dataSection != NULL ) { - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - ObjectFile::Atom* dataAtom = *it; - if ( dataAtom->getSection() == dataSection ) { - std::vector& references = dataAtom->getReferences(); - if ( references.size() > 0 ) { - if ( slideable && !hasPreferredLoadAddress ) { - // in a slidable image dyld will need to rebase and bind so any references will need runtime fixups - // if image has preferred base address, assume it will load there and not rebase - moveToFrontOfSection(dataAtom); - } - else { - // in a non-slideable image, dyld will only do binding, so only references to - // symbols in another dylib will need runtime fixups - //fprintf(stderr, "reference from atom %s\n", dataAtom->getDisplayName()); - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - //fprintf(stderr, "\t%d %s\n", reference->getTarget().getDefinitionKind(), reference->getTarget().getDisplayName()); - if ( (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - moveToFrontOfSection(dataAtom); - break; - } - } - } - } - } - } - } - } - -} - - -void Linker::writeDotOutput() -{ - const char* dotOutFilePath = fOptions.dotOutputFile(); - if ( dotOutFilePath != NULL ) { - FILE* out = fopen(dotOutFilePath, "w"); - if ( out != NULL ) { - // print header - fprintf(out, "digraph dg\n{\n"); - fprintf(out, "\tconcentrate = true;\n"); - fprintf(out, "\trankdir = LR;\n"); - - // print each atom as a node - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() != fOutputFile ) { - const char* name = atom->getDisplayName(); - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); - } - else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { - char cstring[atom->getSize()+2]; - atom->copyRawContent((uint8_t*)cstring); - fprintf(out, "\taddr%p [ label = \"string: '", atom); - for (const char* s=cstring; *s != '\0'; ++s) { - if ( *s == '\n' ) - fprintf(out, "\\\\n"); - else - fputc(*s, out); - } - fprintf(out, "'\" ];\n"); - } - else { - fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name); - } - } - } - fprintf(out, "\n"); - - // print each reference as an edge - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* fromAtom = *it; - if ( fromAtom->getFile() != fOutputFile ) { - std::vector& references = fromAtom->getReferences(); - std::set seenTargets; - for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - ObjectFile::Atom* toAtom = &(reference->getTarget()); - if ( seenTargets.count(toAtom) == 0 ) { - seenTargets.insert(toAtom); - fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); - } - } - } - } - fprintf(out, "\n"); - - // push all imports to bottom of graph - fprintf(out, "{ rank = same; "); - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() != fOutputFile ) - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fprintf(out, "addr%p; ", atom); - } - } - fprintf(out, "};\n "); - - // print footer - fprintf(out, "}\n"); - fclose(out); - } - else { - warning("could not write dot output file: %s", dotOutFilePath); - } - } -} - -ObjectFile::Atom* Linker::entryPoint(bool orInit, bool searchArchives) -{ - // if main executable, find entry point atom - ObjectFile::Atom* entryPoint = NULL; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kPreload: - entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); - if ( (entryPoint == NULL) && searchArchives ) { - // ld64 can not find a -e entry point from an archive - this->addJustInTimeAtoms(fOptions.entryName(), false, true, false); - entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); - } - if ( entryPoint == NULL ) { - throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); - } - break; - case Options::kDynamicLibrary: - if ( orInit && (fOptions.initFunctionName() != NULL) ) { - entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); - if ( entryPoint == NULL ) { - throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); - } - } - break; - case Options::kObjectFile: - case Options::kDynamicBundle: - case Options::kKextBundle: - entryPoint = NULL; - break; - } - return entryPoint; -} - -ObjectFile::Atom* Linker::dyldClassicHelper() -{ - if ( fOptions.makeClassicDyldInfo() ) - return fGlobalSymbolTable.find("dyld_stub_binding_helper"); - else - return NULL; -} - -ObjectFile::Atom* Linker::dyldCompressedHelper() -{ - if ( fOptions.makeCompressedDyldInfo() ) { - // dyld_stub_binder is in libSystem.B.dylib - ObjectFile::Atom* atom = fGlobalSymbolTable.find("dyld_stub_binder"); - if ( atom == NULL ) { - this->addJustInTimeAtoms("dyld_stub_binder", true, false, true); - } - atom = fGlobalSymbolTable.find("dyld_stub_binder"); - return atom; - } - else - return NULL; -} - -ObjectFile::Atom* Linker::dyldLazyLibraryHelper() -{ - return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper"); -} - -const char* Linker::assureFullPath(const char* path) -{ - if ( path[0] == '/' ) - return path; - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - char* result; - asprintf(&result, "%s/%s", cwdbuff, path); - if ( result != NULL ) - return result; - } - return path; -} - - -// -// The stab strings are of the form: -// ':' -// but the contain a colon. -// For C++ may contain a double colon (e.g. std::string:f(0,1) ) -// For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) -// -const char* Linker::truncateStabString(const char* str) -{ - enum { start, inObjc } state = start; - for (const char* s = str; *s != 0; ++s) { - char c = *s; - switch (state) { - case start: - if ( c == '[' ) { - state = inObjc; - } - else { - if ( c == ':' ) { - if ( s[1] == ':' ) { - ++s; - } - else { - // found colon - // Duplicate strndup behavior here. - int trunStrLen = s-str+2; - char* temp = new char[trunStrLen+1]; - memcpy(temp, str, trunStrLen); - temp[trunStrLen] = '\0'; - return temp; - } - } - } - break; - case inObjc: - if ( c == ']' ) { - state = start; - } - break; - } - } - // malformed - return str; -} - - -bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) -{ - switch(stab.type){ - case N_GSYM: - case N_STSYM: - case N_LCSYM: - case N_FUN: - // these all need truncated strings - stab.string = truncateStabString(stab.string); - return true; - case N_SO: - case N_OSO: - case N_OPT: - case N_SOL: - // these are included in the minimal stabs, but they keep their full string - return true; - default: - return false; - } -} - - -struct HeaderRange { - std::vector::iterator begin; - std::vector::iterator end; - int parentRangeIndex; - uint32_t sum; - bool sumPrecomputed; - bool useEXCL; - bool cannotEXCL; // because of SLINE, etc stabs -}; - - -typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> PathToSums; - -// hash table that maps header path to a vector of known checksums for that path -static PathToSums sKnownBINCLs; - - -void Linker::collectStabs(ObjectFile::Reader* reader, std::map& atomOrdinals) -{ - const bool log = false; - bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); - std::vector* readerStabs = reader->getStabs(); - if ( readerStabs == NULL ) - return; - - if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); - std::vector ranges; - int curRangeIndex = -1; - int count = 0; - ObjectFile::Atom* atomWithLowestOrdinal = NULL; - ObjectFile::Atom* atomWithHighestOrdinal = NULL; - uint32_t highestOrdinal = 0; - uint32_t lowestOrdinal = UINT_MAX; - std::vector > soRanges; - // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums - // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - ++count; - switch ( it->type ) { - case N_BINCL: - { - HeaderRange range; - range.begin = it; - range.end = readerStabs->end(); - range.parentRangeIndex = curRangeIndex; - range.sum = it->value; - range.sumPrecomputed = (range.sum != 0); - range.useEXCL = false; - range.cannotEXCL = false; - curRangeIndex = ranges.size(); - if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string); - ranges.push_back(range); - } - break; - case N_EINCL: - if ( curRangeIndex == -1 ) { - warning("EINCL missing BINCL in %s", reader->getPath()); - } - else { - ranges[curRangeIndex].end = it+1; - if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); - curRangeIndex = ranges[curRangeIndex].parentRangeIndex; - } - break; - case N_FUN: - { - std::map::iterator pos = atomOrdinals.find(it->atom); - if ( pos != atomOrdinals.end() ) { - uint32_t ordinal = pos->second; - if ( ordinal > highestOrdinal ) { - highestOrdinal = ordinal; - atomWithHighestOrdinal = it->atom; - } - if ( ordinal < lowestOrdinal ) { - lowestOrdinal = ordinal; - atomWithLowestOrdinal = it->atom; - } - } - } - // fall through - case N_BNSYM: - case N_ENSYM: - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - case N_STSYM: - case N_LCSYM: - if ( curRangeIndex != -1 ) { - ranges[curRangeIndex].cannotEXCL = true; - if ( fOptions.warnStabs() ) - warning("cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); - } - break; - case N_SO: - if ( (it->string != NULL) && (strlen(it->string) > 0) ) { - // start SO, reset hi/low FUN tracking - atomWithLowestOrdinal = NULL; - atomWithHighestOrdinal = NULL; - highestOrdinal = 0; - lowestOrdinal = UINT_MAX; - } - else { - // end SO, record hi/low atoms for this SO range - soRanges.push_back(std::make_pair(atomWithLowestOrdinal, atomWithHighestOrdinal)); - } - // fall through - default: - if ( curRangeIndex != -1 ) { - if ( ! ranges[curRangeIndex].sumPrecomputed ) { - uint32_t sum = 0; - const char* s = it->string; - char c; - while ( (c = *s++) != 0 ) { - sum += c; - // don't checkusm first number (file index) after open paren in string - if ( c == '(' ) { - while(isdigit(*s)) - ++s; - } - } - ranges[curRangeIndex].sum += sum; - } - } - - } - } - if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); - if ( curRangeIndex != -1 ) - warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath()); - - // if no BINCLs - if ( ranges.size() == 0 ) { - unsigned int soIndex = 0; - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - // copy minimal or all stabs - ObjectFile::Reader::Stab stab = *it; - if ( !minimal || minimizeStab(stab) ) { - if ( stab.type == N_SO ) { - if ( soIndex < soRanges.size() ) { - if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { - // starting SO is associated with first atom - stab.atom = soRanges[soIndex].first; - } - else { - // ending SO is associated with last atom - stab.atom = soRanges[soIndex].second; - ++soIndex; - } - } - } - fStabs.push_back(stab); - } - } - return; - } - - //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); - //for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { - // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); - //} - - // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL - for(std::vector::iterator it=ranges.begin(); it != ranges.end(); ++it) { - if ( ! it->cannotEXCL ) { - const char* header = it->begin->string; - uint32_t sum = it->sum; - PathToSums::iterator pos = sKnownBINCLs.find(header); - if ( pos != sKnownBINCLs.end() ) { - std::vector& sums = pos->second; - for(std::vector::iterator sit=sums.begin(); sit != sums.end(); ++sit) { - if (*sit == sum) { - //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath()); - it->useEXCL = true; - break; - } - } - if ( ! it->useEXCL ) { - // have seen this path, but not this checksum - //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header); - sums.push_back(sum); - } - } - else { - // have not seen this path, so add to known BINCLs - std::vector empty; - sKnownBINCLs[header] = empty; - sKnownBINCLs[header].push_back(sum); - //fprintf(stderr, "registering checksum %08X for %s\n", sum, header); - } - } - } - - // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs - curRangeIndex = -1; - const int maxRangeIndex = ranges.size(); - int soIndex = 0; - for(std::vector::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - switch ( it->type ) { - case N_BINCL: - for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) { - if ( ranges[i].begin == it ) { - curRangeIndex = i; - HeaderRange& range = ranges[curRangeIndex]; - ObjectFile::Reader::Stab stab = *it; - stab.value = range.sum; // BINCL and EXCL have n_value set to checksum - if ( range.useEXCL ) - stab.type = N_EXCL; // transform BINCL into EXCL - if ( !minimal ) - fStabs.push_back(stab); - break; - } - } - break; - case N_EINCL: - if ( curRangeIndex != -1 ) { - if ( !ranges[curRangeIndex].useEXCL && !minimal ) - fStabs.push_back(*it); - curRangeIndex = ranges[curRangeIndex].parentRangeIndex; - } - break; - default: - if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { - ObjectFile::Reader::Stab stab = *it; - if ( !minimal || minimizeStab(stab) ) { - if ( stab.type == N_SO ) { - if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { - // starting SO is associated with first atom - stab.atom = soRanges[soIndex].first; - } - else { - // ending SO is associated with last atom - stab.atom = soRanges[soIndex].second; - ++soIndex; - } - } - fStabs.push_back(stab); - } - } - } - } - -} - - -// used to prune out atoms that don't need debug notes generated -class NoDebugNoteAtom -{ -public: - NoDebugNoteAtom(const std::map& readersWithDwarfOrdinals) - : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} - - bool operator()(const ObjectFile::Atom* atom) const { - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - return true; - if ( atom->getName() == NULL ) - return true; - if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) - return true; - return false; - } - -private: - const std::map& fReadersWithDwarfOrdinals; -}; - -// used to sort atoms with debug notes -class ReadersWithDwarfSorter -{ -public: - ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, - const std::map& atomOrdinals) - : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} - - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const - { - // first sort by reader - unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; - unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; - if ( leftReaderIndex != rightReaderIndex ) - return (leftReaderIndex < rightReaderIndex); - - // then sort by atom ordinal - unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; - unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; - return leftAtomIndex < rightAtomIndex; - } - -private: - const std::map& fReadersWithDwarfOrdinals; - const std::map& fAtomOrdinals; -}; - - - - - -void Linker::synthesizeDebugNotes(std::vector& allAtomsByReader) -{ - // synthesize "debug notes" and add them to master stabs vector - const char* dirPath = NULL; - const char* filename = NULL; - bool wroteStartSO = false; - bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL); - __gnu_cxx::hash_set, CStringEquals> seenFiles; - for (std::vector::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { - ObjectFile::Atom* atom = *it; - const char* newDirPath; - const char* newFilename; - //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); - if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { - // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) - asprintf((char**)&newDirPath, "%s/", newDirPath); - if ( filename != NULL ) { - // translation unit change, emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } - // new translation unit, emit start SO's - ObjectFile::Reader::Stab dirPathStab; - dirPathStab.atom = NULL; - dirPathStab.type = N_SO; - dirPathStab.other = 0; - dirPathStab.desc = 0; - dirPathStab.value = 0; - dirPathStab.string = newDirPath; - fStabs.push_back(dirPathStab); - ObjectFile::Reader::Stab fileStab; - fileStab.atom = NULL; - fileStab.type = N_SO; - fileStab.other = 0; - fileStab.desc = 0; - fileStab.value = 0; - fileStab.string = newFilename; - fStabs.push_back(fileStab); - // Synthesize OSO for start of file - ObjectFile::Reader::Stab objStab; - objStab.atom = NULL; - objStab.type = N_OSO; - // linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries - objStab.other = atom->getFile()->updateCpuConstraint(0); - objStab.desc = 1; - objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); - objStab.string = assureFullPath(atom->getFile()->getPath()); - fStabs.push_back(objStab); - wroteStartSO = true; - // add the source file path to seenFiles so it does not show up in SOLs - seenFiles.insert(newFilename); - } - filename = newFilename; - dirPath = newDirPath; - if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - // Synthesize BNSYM and start FUN stabs - ObjectFile::Reader::Stab beginSym; - beginSym.atom = atom; - beginSym.type = N_BNSYM; - beginSym.other = 1; - beginSym.desc = 0; - beginSym.value = 0; - beginSym.string = ""; - fStabs.push_back(beginSym); - ObjectFile::Reader::Stab startFun; - startFun.atom = atom; - startFun.type = N_FUN; - startFun.other = 1; - startFun.desc = 0; - startFun.value = 0; - startFun.string = atom->getName(); - fStabs.push_back(startFun); - // Synthesize any SOL stabs needed - std::vector* lineInfo = atom->getLineInfo(); - if ( lineInfo != NULL ) { - const char* curFile = NULL; - for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { - if ( it->fileName != curFile ) { - if ( seenFiles.count(it->fileName) == 0 ) { - seenFiles.insert(it->fileName); - ObjectFile::Reader::Stab sol; - sol.atom = 0; - sol.type = N_SOL; - sol.other = 0; - sol.desc = 0; - sol.value = 0; - sol.string = it->fileName; - fStabs.push_back(sol); - } - curFile = it->fileName; - } - } - } - // Synthesize end FUN and ENSYM stabs - ObjectFile::Reader::Stab endFun; - endFun.atom = atom; - endFun.type = N_FUN; - endFun.other = 0; - endFun.desc = 0; - endFun.value = 0; - endFun.string = ""; - fStabs.push_back(endFun); - ObjectFile::Reader::Stab endSym; - endSym.atom = atom; - endSym.type = N_ENSYM; - endSym.other = 1; - endSym.desc = 0; - endSym.value = 0; - endSym.string = ""; - fStabs.push_back(endSym); - } - else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) { - // no stabs for atoms that would not be in the symbol table - } - else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { - // no stabs for absolute symbols - } - else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) { - // no stabs for .eh atoms - } - else if ( (strncmp(atom->getName(), "__dtrace_probe$", 15) == 0) ) { - // no stabs for old style dtrace probes - } - else { - ObjectFile::Reader::Stab globalsStab; - const char* name = atom->getName(); - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // Synthesize STSYM stab for statics - globalsStab.atom = atom; - globalsStab.type = N_STSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - else { - // Synthesize GSYM stab for other globals - globalsStab.atom = atom; - globalsStab.type = N_GSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } - } - } - - if ( wroteStartSO ) { - // emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } -} - - - - -void Linker::collectDebugInfo() -{ - std::map atomOrdinals; - fStartDebugTime = mach_absolute_time(); - if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { - - // determine mixture of stabs and dwarf - bool someStabs = false; - bool someDwarf = false; - for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - switch ( reader->getDebugInfoKind() ) { - case ObjectFile::Reader::kDebugInfoNone: - break; - case ObjectFile::Reader::kDebugInfoStabs: - someStabs = true; - break; - case ObjectFile::Reader::kDebugInfoDwarf: - someDwarf = true; - fCreateUUID = true; - break; - case ObjectFile::Reader::kDebugInfoStabsUUID: - someStabs = true; - fCreateUUID = true; - break; - default: - throw "Unhandled type of debug information"; - } - } - } - - if ( someDwarf || someStabs ) { - // try to minimize re-allocations - fStabs.reserve(1024); - - // make mapping from atoms to ordinal - uint32_t ordinal = 1; - for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atomOrdinals[*it] = ordinal++; - } - } - - // process all dwarf .o files as a batch - if ( someDwarf ) { - // make mapping from readers with dwarf to ordinal - std::map readersWithDwarfOrdinals; - uint32_t readerOrdinal = 1; - for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { - readersWithDwarfOrdinals[reader] = readerOrdinal++; - } - } - - // make a vector of atoms - std::vector allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); - // remove those not from a reader that has dwarf - allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), - NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); - // sort by reader then atom ordinal - std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); - // add debug notes for each atom - this->synthesizeDebugNotes(allAtomsByReader); - } - - // process all stabs .o files one by one - if ( someStabs ) { - // get stabs from each reader, in command line order - for (std::vector::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - switch ( reader->getDebugInfoKind() ) { - case ObjectFile::Reader::kDebugInfoDwarf: - case ObjectFile::Reader::kDebugInfoNone: - // do nothing - break; - case ObjectFile::Reader::kDebugInfoStabs: - case ObjectFile::Reader::kDebugInfoStabsUUID: - collectStabs(reader, atomOrdinals); - break; - default: - throw "Unhandled type of debug information"; - } - } - } - // remove stabs associated with atoms that won't be in output - std::set allAtomsSet; - allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); - fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); - } - } -} - -void Linker::writeOutput() -{ - // ld -r of empty .o file should preserve sub-type - // empty dylib should have subtype from command line - if ( fOptions.preferSubArchitecture() && (fOptions.architecture() == CPU_TYPE_ARM) ) { - fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)fOptions.subArchitecture(); - } - - if ( fOptions.forceCpuSubtypeAll() ) - fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; - - fStartWriteTime = mach_absolute_time(); - // tell writer about each segment's atoms - fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), - fCreateUUID, fCanScatter, - fCurrentCpuConstraint, - fRegularDefAtomsThatOverrideADylibsWeakDef, - fGlobalSymbolTable.hasExternalWeakDefinitions()); -} - -const char* Linker::fileArch(const void* p) -{ - const uint8_t* bytes = (uint8_t*)p; - const char* result; - result = mach_o::relocatable::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - - result = lto::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - - return "unsupported file format"; -} - -ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) -{ - // map in whole file - uint64_t len = info.fileLen; - int fd = ::open(info.path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open file, errno=%d", errno); - if ( info.fileLen < 20 ) - throw "file too small"; - - uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - if ( p == (uint8_t*)(-1) ) - throwf("can't map file, errno=%d", errno); - - // if fat file, skip to architecture we want - // Note: fat header is always big-endian - bool isFatFile = false; - const fat_header* fh = (fat_header*)p; - if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - isFatFile = true; - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - uint32_t sliceToUse; - bool sliceFound = false; - if ( fOptions.preferSubArchitecture() ) { - // first try to find a slice that match cpu-type and cpu-sub-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture) - && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) { - sliceToUse = i; - sliceFound = true; - break; - } - } - } - if ( !sliceFound ) { - // look for any slice that matches just cpu-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) { - sliceToUse = i; - sliceFound = true; - break; - } - } - } - if ( sliceFound ) { - uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); - len = OSSwapBigToHostInt32(archs[sliceToUse].size); - // if requested architecture is page aligned within fat file, then remap just that portion of file - if ( (fileOffset & 0x00000FFF) == 0 ) { - // unmap whole file - munmap((caddr_t)p, info.fileLen); - // re-map just part we need - p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); - if ( p == (uint8_t*)(-1) ) - throwf("can't re-map file, errno=%d", errno); - } - else { - p = &p[fileOffset]; - } - } - } - ::close(fd); - - bool objSubtypeMustMatch = (fOptions.preferSubArchitecture() && !fOptions.allowSubArchitectureMismatches()); - switch (fArchitecture) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader::validFile(p, objSubtypeMustMatch, fOptions.subArchitecture()) ) - return this->addObject(new mach_o::relocatable::Reader::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader::validFile(p, len) ) - return this->addArchive(new archive::Reader::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - } - -#if LTO_SUPPORT - if ( lto::Reader::validFile(p, len, fArchitecture) ) { - return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); - } - else if ( lto::Reader::fileKind((uint8_t*)p) != NULL ) { - if ( lto::Reader::loaded() ) { - throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); - } - else { - const char* libLTO = "libLTO.dylib"; - char ldPath[PATH_MAX]; - char tmpPath[PATH_MAX]; - char libLTOPath[PATH_MAX]; - uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { - if ( realpath(ldPath, tmpPath) != NULL ) { - char* lastSlash = strrchr(tmpPath, '/'); - if ( lastSlash != NULL ) - strcpy(lastSlash, "/../lib/libLTO.dylib"); - libLTO = tmpPath; - if ( realpath(tmpPath, libLTOPath) != NULL ) - libLTO = libLTOPath; - } - } - throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); - } - } -#endif - // error handling - if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - throwf("missing required architecture %s in file", fArchitectureName); - } - else { - if ( isFatFile ) - throwf("file is universal but does not contain a(n) %s slice", fArchitectureName); - else - throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); - } -} - -void Linker::logDylib(ObjectFile::Reader* reader, bool indirect) -{ - if ( fOptions.readerOptions().fTraceDylibs ) { - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - if ( indirect ) - logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); - else - logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); - } -} - - - -ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath) -{ - //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); - InstallNameToReader::iterator pos = fDylibMap.find(installPath); - if ( pos != fDylibMap.end() ) { - return pos->second; - } - else { - // allow -dylib_path option to override indirect library to use - for (std::vector::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) { - if ( strcmp(dit->installName,installPath) == 0 ) {\ - try { - Options::FileInfo info = fOptions.findFile(dit->useInstead); - ObjectFile::Reader* reader = this->createReader(info); - fDylibMap[strdup(installPath)] = reader; - this->logDylib(reader, true); - return reader; - } - catch (const char* msg) { - warning("ignoring -dylib_file option, %s", msg); - } - } - } - char newPath[MAXPATHLEN]; - // handle @loader_path - if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { - strcpy(newPath, fromPath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &installPath[13]); - else - strcpy(newPath, &installPath[13]); - installPath = newPath; - } - // note: @executable_path case is handled inside findFileUsingPaths() - // search for dylib using -F and -L paths - Options::FileInfo info = fOptions.findFileUsingPaths(installPath); - try { - ObjectFile::Reader* reader = this->createReader(info); - fDylibMap[strdup(installPath)] = reader; - this->logDylib(reader, true); - return reader; - } - catch (const char* msg) { - throwf("in %s, %s", info.path, msg); - } - } -} - - -void Linker::processDylibs() -{ - fAllDirectDylibsLoaded = true; - - // mark all dylibs initially specified as required and check if they can be used - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - it->second->setExplicitlyLinked(); - this->checkDylibClientRestrictions(it->second); - } - - // keep processing dylibs until no more dylibs are added - unsigned long lastMapSize = 0; - while ( lastMapSize != fDylibMap.size() ) { - lastMapSize = fDylibMap.size(); - // can't iterator fDylibMap while modifying it, so use temp buffer - std::vector currentUnprocessedReaders; - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( fDylibsProcessed.count(it->second) == 0 ) - currentUnprocessedReaders.push_back(it->second); - } - for (std::vector::iterator it=currentUnprocessedReaders.begin(); it != currentUnprocessedReaders.end(); it++) { - fDylibsProcessed.insert(*it); - (*it)->processIndirectLibraries(this); - } - } - - // go back over original dylibs and mark sub frameworks as re-exported - if ( fOptions.outputKind() == Options::kDynamicLibrary ) { - const char* myLeaf = strrchr(fOptions.installPath(), '/'); - if ( myLeaf != NULL ) { - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - const char* childParent = reader->parentUmbrella(); - if ( childParent != NULL ) { - if ( strcmp(childParent, &myLeaf[1]) == 0 ) { - // set re-export bit of info - std::map::iterator pos = fDylibOptionsMap.find(reader); - if ( pos != fDylibOptionsMap.end() ) { - pos->second.fReExport = true; - } - } - } - } - } - } - -} - - - -void Linker::createReaders() -{ - fStartCreateReadersTime = mach_absolute_time(); - std::vector& files = fOptions.getInputFiles(); - const int count = files.size(); - if ( count == 0 ) - throw "no object files specified"; - // add all direct object, archives, and dylibs - for (int i=0; i < count; ++i) { - Options::FileInfo& entry = files[i]; - // ignore /usr/lib/dyld on command line in crt.o build - if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { - try { - this->addInputFile(this->createReader(entry), entry); - } - catch (const char* msg) { - if ( (strstr(msg, "architecture") != NULL) && !fOptions.errorOnOtherArchFiles() ) { - if ( fOptions.ignoreOtherArchInputFiles() ) { - // ignore, because this is about an architecture not in use - } - else { - warning("in %s, %s", entry.path, msg); - } - } - else { - throwf("in %s, %s", entry.path, msg); - } - } - } - } - - this->processDylibs(); -} - - - -ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - // remember which readers are archives because they are logged differently - fArchiveReaders.insert(reader); - - // update stats - fTotalArchiveSize += mappedLen; - ++fTotalArchivesLoaded; - return reader; -} - -ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't - if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() ) - fCanScatter = false; - - // update stats - fTotalObjectSize += mappedLen; - ++fTotalObjectLoaded; - return reader; -} - - -void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) -{ - // Check for any restrictions on who can link with this dylib - const char* readerParentName = reader->parentUmbrella() ; - std::vector* clients = reader->getAllowableClients(); - if ( (readerParentName != NULL) || (clients != NULL) ) { - // only dylibs that are in an umbrella or have a client list need verification - const char* installName = fOptions.installPath(); - const char* installNameLastSlash = strrchr(installName, '/'); - bool isParent = false; - bool isSibling = false; - bool isAllowableClient = false; - // There are three cases: - if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) { - // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella - isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 ); - - // hack to support umbrella variants that encode the variant name in the install name - // e.g. CoreServices_profile - if ( !isParent ) { - const char* underscore = strchr(&installNameLastSlash[1], '_'); - if ( underscore != NULL ) { - isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 ); - } - } - - // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent - isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); - } - - if ( !isParent && !isSibling && (clients != NULL) ) { - // case 3) the dylib has a list of allowable clients, and we are creating one of them - const char* clientName = fOptions.clientName(); - int clientNameLen = 0; - if ( clientName != NULL ) { - // use client name as specified on command line - clientNameLen = strlen(clientName); - } - else { - // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) - clientName = installName; - clientNameLen = strlen(clientName); - // starts after last slash - if ( installNameLastSlash != NULL ) - clientName = &installNameLastSlash[1]; - if ( strncmp(clientName, "lib", 3) == 0 ) - clientName = &clientName[3]; - // up to first dot - const char* firstDot = strchr(clientName, '.'); - if ( firstDot != NULL ) - clientNameLen = firstDot - clientName; - // up to first underscore - const char* firstUnderscore = strchr(clientName, '_'); - if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) - clientNameLen = firstUnderscore - clientName; - } - - // Use clientName to check if this dylib is able to link against the allowable clients. - for (std::vector::iterator it = clients->begin(); it != clients->end(); it++) { - if ( strncmp(*it, clientName, clientNameLen) == 0 ) - isAllowableClient = true; - } - } - - if ( !isParent && !isSibling && !isAllowableClient ) { - if ( readerParentName != NULL ) { - throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", - reader->getPath(), readerParentName); - } - else { - throwf("cannot link directly with %s", reader->getPath()); - } - } - } -} - - -ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - break; - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kPreload: - case Options::kObjectFile: - case Options::kKextBundle: - warning("unexpected dylib (%s) on link line", reader->getPath()); - break; - } - - fNextInputOrdinal += mappedLen; - if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { - // this is a "blank" stub - // silently ignore it - return reader; - } - // add to map of loaded dylibs - const char* installPath = reader->getInstallPath(); - if ( installPath != NULL ) { - InstallNameToReader::iterator pos = fDylibMap.find(installPath); - if ( pos == fDylibMap.end() ) { - fDylibMap[strdup(installPath)] = reader; - } - else { - InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); - if ( pos2 == fDylibMap.end() ) - fDylibMap[strdup(reader->getPath())] = reader; - else - warning("duplicate dylib %s", reader->getPath()); - } - } - else if ( info.options.fBundleLoader ) - fBundleLoaderReader = reader; - - // log direct readers - if ( !fAllDirectDylibsLoaded ) - this->logDylib(reader, false); - - // update stats - ++fTotalDylibsLoaded; - - return reader; -} - - -void Linker::logTraceInfo (const char* format, ...) -{ - static int trace_file = -1; - char trace_buffer[MAXPATHLEN * 2]; - char *buffer_ptr; - int length; - ssize_t amount_written; - const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile; - - if(trace_file == -1) { - if(trace_file_path != NULL) { - trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); - if(trace_file == -1) - throwf("Could not open or create trace file: %s", trace_file_path); - } - else { - trace_file = fileno(stderr); - } - } - - va_list ap; - va_start(ap, format); - length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); - va_end(ap); - buffer_ptr = trace_buffer; - - while(length > 0) { - amount_written = write(trace_file, buffer_ptr, length); - if(amount_written == -1) - /* Failure to write shouldn't fail the build. */ - return; - buffer_ptr += amount_written; - length -= amount_written; - } -} - - - -void Linker::createWriter() -{ - fStartCreateWriterTime = mach_absolute_time(); - - // make a vector out of all required dylibs in fDylibMap - std::vector dynamicLibraries; - // need to preserve command line order - for (std::vector::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - for (InstallNameToReader::iterator mit=fDylibMap.begin(); mit != fDylibMap.end(); mit++) { - if ( reader == mit->second ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = reader; - dylibInfo.options = fDylibOptionsMap[reader]; - dynamicLibraries.push_back(dylibInfo); - break; - } - } - } - // then add any other dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->implicitlyLinked() ) { - // if not already in dynamicLibraries - bool alreadyInDynamicLibraries = false; - for (std::vector::iterator dit=dynamicLibraries.begin(); dit != dynamicLibraries.end(); dit++) { - if ( dit->reader == it->second ) { - alreadyInDynamicLibraries = true; - break; - } - } - if ( ! alreadyInDynamicLibraries ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = it->second; - std::map::iterator pos = fDylibOptionsMap.find(it->second); - if ( pos != fDylibOptionsMap.end() ) { - dylibInfo.options = pos->second; - } - else { - dylibInfo.options.fWeakImport = false; // FIX ME - dylibInfo.options.fReExport = false; - dylibInfo.options.fBundleLoader = false; - } - dynamicLibraries.push_back(dylibInfo); - } - } - } - if ( fBundleLoaderReader != NULL ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = fBundleLoaderReader; - dylibInfo.options.fWeakImport = false; - dylibInfo.options.fReExport = false; - dylibInfo.options.fBundleLoader = true; - dynamicLibraries.push_back(dylibInfo); - } - - const char* path = fOptions.getOutputFilePath(); - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_POWERPC64: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_I386: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_X86_64: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_ARM: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); - break; - default: - throw "unknown architecture"; - } -} - - -Linker::SymbolTable::SymbolTable(Linker& owner) - : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false) -{ -} - -void Linker::SymbolTable::require(const char* name) -{ - //fprintf(stderr, "require(%s)\n", name); - Mapper::iterator pos = fTable.find(name); - if ( pos == fTable.end() ) { - fTable[name] = NULL; - ++fRequireCount; - } -} - -// convenience labels for 2-dimensional switch statement -enum AllDefinitionCombinations { - kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition, - kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition, - kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition, - kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition, - kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol -}; - -bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) -{ - bool useNew = true; - bool checkVisibilityMismatch = false; - const char* name = newAtom.getName(); - //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); - Mapper::iterator pos = fTable.find(name); - ObjectFile::Atom* existingAtom = NULL; - if ( pos != fTable.end() ) - existingAtom = pos->second; - if ( existingAtom != NULL ) { - // already have atom with same name in symbol table - switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { - case kRegAndReg: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kRegAndWeak: - // ignore new weak atom, because we already have a non-weak one - useNew = false; - break; - case kRegAndTent: - // ignore new tentative atom, because we already have a regular one - useNew = false; - checkVisibilityMismatch = true; - if ( newAtom.getSize() > existingAtom->getSize() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "is smaller than the real definition of size %llu from %s", - newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), - existingAtom->getSize(), existingAtom->getFile()->getPath()); - } - break; - case kRegAndExtern: - // ignore external atom, because we already have a one - useNew = false; - break; - case kRegAndExternWeak: - // ignore external atom, because we already have a one - useNew = false; - break; - case kRegAndAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case kWeakAndReg: - // replace existing weak atom with regular one - break; - case kWeakAndWeak: - // have another weak atom, use whichever has largest alignment requirement - // because codegen of some client may require alignment - useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); - checkVisibilityMismatch = true; - break; - case kWeakAndTent: - // replace existing weak atom with tentative one ??? - break; - case kWeakAndExtern: - // keep weak atom, at runtime external one may override - useNew = false; - break; - case kWeakAndExternWeak: - // keep weak atom, at runtime external one may override - useNew = false; - break; - case kWeakAndAbsolute: - // replace existing weak atom with absolute one - break; - case kTentAndReg: - // replace existing tentative atom with regular one - checkVisibilityMismatch = true; - if ( newAtom.getSize() < existingAtom->getSize() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "being replaced by a real definition of size %llu from %s", - newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), - newAtom.getSize(), newAtom.getFile()->getPath()); - } - break; - case kTentAndWeak: - // replace existing tentative atom with weak one ??? - break; - case kTentAndTent: - // use largest - checkVisibilityMismatch = true; - if ( newAtom.getSize() < existingAtom->getSize() ) { - useNew = false; - } - else { - if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) - warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName()); - } - break; - case kTentAndExtern: - case kTentAndExternWeak: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing common symbol %s from %s with true definition from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - } - break; - case kTentAndAbsolute: - // replace tentative with absolute (can't size check because absolutes have no size) - break; - case kExternAndReg: - // replace external atom with regular one - break; - case kExternAndWeak: - // replace external atom with weak one - break; - case kExternAndTent: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - } - break; - case kExternAndExtern: - throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kExternAndExternWeak: - // keep strong dylib atom, ignore weak one - useNew = false; - break; - case kExternAndAbsolute: - // replace external atom with absolute one - break; - case kExternWeakAndReg: - // replace existing weak external with regular - break; - case kExternWeakAndWeak: - // replace existing weak external with weak (let dyld decide at runtime which to use) - break; - case kExternWeakAndTent: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - } - break; - case kExternWeakAndExtern: - // replace existing weak external with external - break; - case kExternWeakAndExternWeak: - // keep existing external weak - useNew = false; - break; - case kExternWeakAndAbsolute: - // replace existing weak external with absolute - break; - case kAbsoluteAndReg: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kAbsoluteAndWeak: - // ignore new weak atom, because we already have a non-weak one - useNew = false; - break; - case kAbsoluteAndTent: - // ignore new tentative atom, because we already have a regular one - useNew = false; - break; - case kAbsoluteAndExtern: - // ignore external atom, because we already have a one - useNew = false; - break; - case kAbsoluteAndExternWeak: - // ignore external atom, because we already have a one - useNew = false; - break; - case kAbsoluteAndAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - } - } - if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) { - warning("%s has different visibility (%s) in %s and (%s) in %s", - newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath()); - } - if ( useNew ) { - fTable[name] = &newAtom; - if ( existingAtom != NULL ) { - fOwner.markDead(existingAtom); - if ( fOwner.fInitialLoadsDone ) { - //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->getName(), &newAtom); - fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom); - } - } - if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { - switch ( newAtom.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - fHasExternalTentativeDefinitions = true; - ++fRequireCount; // added a tentative definition means loadUndefines() needs to continue - break; - case ObjectFile::Atom::kWeakDefinition: - if ( newAtom.getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn ) - fHasExternalWeakDefinitions = true; - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - ++fDylibSymbolCount; - break; - default: - break; - } - } - } - else { - fOwner.markDead(&newAtom); - } - return useNew; -} - - - -ObjectFile::Atom* Linker::SymbolTable::find(const char* name) -{ - Mapper::iterator pos = fTable.find(name); - if ( pos != fTable.end() ) { - return pos->second; - } - return NULL; -} - -void Linker::SymbolTable::erase(const char* name) { - fTable.erase(name); -} - -void Linker::SymbolTable::getUndefinesNames(std::vector& undefines) -{ - for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( it->second == NULL ) { - undefines.push_back(it->first); - } - } -} - -void Linker::SymbolTable::getTentativesNames(std::vector& tents) -{ - for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( it->second != NULL ) { - if ( (it->second->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) - && (it->second->getScope() == ObjectFile::Atom::scopeGlobal) ) { - tents.push_back(it->first); - } - } - } -} - - - -bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) -{ - if ( left == right ) - return false; - - // first sort by section order (which is already sorted by segment) - unsigned int leftSectionIndex = left->getSection()->getIndex(); - unsigned int rightSectionIndex = right->getSection()->getIndex(); - if ( leftSectionIndex != rightSectionIndex) - return (leftSectionIndex < rightSectionIndex); - - // magic section$start symbol always sorts to the start of its section - if ( left->getContentType() == ObjectFile::Atom::kSectionStart ) - return true; - if ( right->getContentType() == ObjectFile::Atom::kSectionStart ) - return false; - - // if a -order_file is specified, then sorting is altered to sort those symbols first - if ( fOverriddenOrdinalMap != NULL ) { - std::map::iterator leftPos = fOverriddenOrdinalMap->find(left); - std::map::iterator rightPos = fOverriddenOrdinalMap->find(right); - std::map::iterator end = fOverriddenOrdinalMap->end(); - if ( leftPos != end ) { - if ( rightPos != end ) { - // both left and right are overridden, so compare overridden ordinals - return leftPos->second < rightPos->second; - } - else { - // left is overridden and right is not, so left < right - return true; - } - } - else { - if ( rightPos != end ) { - // right is overridden and left is not, so right < left - return false; - } - else { - // neither are overridden, do default sort - // fall into default sorting below - } - } - } - - // magic section$end symbol always sorts to the end of its section - if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) - return false; - if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) - return true; - - // the __common section can have real or tentative definitions - // we want the real ones to sort before tentative ones - bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); - bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); - if ( leftIsTent != rightIsTent ) - return rightIsTent; - - // initializers are auto sorted to start of section - if ( !fInitializerSet.empty() ) { - bool leftFirst = (fInitializerSet.count(left) != 0); - bool rightFirst = (fInitializerSet.count(right) != 0); - if ( leftFirst != rightFirst ) - return leftFirst; - } - - // terminators are auto sorted to end of section - if ( !fTerminatorSet.empty() ) { - bool leftLast = (fTerminatorSet.count(left) != 0); - bool rightLast = (fTerminatorSet.count(right) != 0); - if ( leftLast != rightLast ) - return rightLast; - } - - // lastly sort by atom ordinal. this is already sorted by .o order - return left->getOrdinal() < right->getOrdinal(); -} - - -int main(int argc, const char* argv[]) -{ - const char* archName = NULL; - bool showArch = false; - bool archInferred = false; - try { - // create linker object given command line arguments - Linker ld(argc, argv); - - // save error message prefix - archName = ld.architectureName(); - archInferred = ld.isInferredArchitecture(); - showArch = ld.showArchitectureInErrors(); - - // open all input files - ld.createReaders(); - - // open output file - ld.createWriter(); - - // do linking - ld.link(); - } - catch (const char* msg) { - if ( archInferred ) - fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); - else if ( showArch ) - fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); - else - fprintf(stderr, "ld: %s\n", msg); - return 1; - } - - return 0; -} diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp new file mode 100644 index 0000000..e5319bb --- /dev/null +++ b/ld64/src/ld/ld.hpp @@ -0,0 +1,710 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-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@ + */ + + +#ifndef __LD_HPP__ +#define __LD_HPP__ + +#include +#include +#include +#include + +#include +#include + + +namespace ld { + +// +// ld::File +// +// Abstract base class for all object or library files the linker processes. +// +// forEachAtom() iterates over the Atoms in the order they occur in the file. +// +// justInTimeforEachAtom(name) iterates over lazily created Atoms. For instance if +// File is a static library, justInTimeforEachAtom() will iterate over the base set +// of Atoms from the archive member implementing 'name'. +// +class File +{ +public: + enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, objcConstraintRetainReleaseOrGC, objcConstraintGC }; + + class AtomHandler { + public: + virtual ~AtomHandler() {} + virtual void doAtom(const class Atom&) = 0; + virtual void doFile(const class File&) = 0; + }; + + File(const char* pth, time_t modTime, uint32_t ord) + : _path(pth), _modTime(modTime), _ordinal(ord) { } + virtual ~File() {} + const char* path() const { return _path; } + time_t modificationTime() const{ return _modTime; } + uint32_t ordinal() const { return _ordinal; } + virtual bool forEachAtom(AtomHandler&) const = 0; + virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0; + virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; } + virtual uint32_t cpuSubType() const { return 0; } + virtual uint32_t subFileCount() const { return 1; } +private: + const char* _path; + time_t _modTime; + uint32_t _ordinal; +}; + + +// +// minumum OS versions +// +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 }; + +namespace relocatable { + // + // ld::relocatable::File + // + // Abstract base class for object files the linker processes. + // + // objcReplacementClasses() is reflects if the file was compiled for fix-and-continue + // + // debugInfo() returns if the object file contains debugger information (stabs or dwarf). + // + // stabs() lazily creates a vector of Stab objects for each atom + // + // canScatterAtoms() true for all compiler generated code. Hand written assembly can opt-in + // via .subsections_via_symbols directive. When true it means the linker can break up section + // content at symbol boundaries and do optimizations like coalescing, dead code stripping, or + // apply order files. + // + // optimize() used by libLTO to lazily generate code from llvm bit-code files + // + class File : public ld::File + { + public: + enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + struct Stab { + const class Atom* atom; + uint8_t type; + uint8_t other; + uint16_t desc; + uint32_t value; + const char* string; + }; + + File(const char* pth, time_t modTime, uint32_t ord) + : ld::File(pth, modTime, ord) { } + virtual ~File() {} + virtual bool objcReplacementClasses() const = 0; + virtual DebugInfoKind debugInfo() const = 0; + virtual const char* debugInfoPath() const { return path(); } + virtual time_t debugInfoModificationTime() const { return modificationTime(); } + virtual const std::vector* stabs() const = 0; + virtual bool canScatterAtoms() const = 0; + virtual bool hasLongBranchStubs() { return false; } + }; +} // namespace relocatable + + +namespace dylib { + + // + // ld::dylib::File + // + // Abstract base class for dynamic shared libraries read by the linker processes. + // + class File : public ld::File + { + public: + class DylibHandler + { + public: + virtual ~DylibHandler() {} + virtual File* findDylib(const char* installPath, const char* fromPath) = 0; + }; + + File(const char* pth, time_t modTime, uint32_t ord) + : ld::File(pth, modTime, ord), _dylibInstallPath(NULL), + _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0), + _explicitlyLinked(false), _implicitlyLinked(false), + _lazyLoadedDylib(false), _weakLinked(false), _reExported(false), + _upward(false), _hasNonWeakImportedSymbols(false), + _hasWeakImportedSymbols(false), _dead(false) { } + const char* installPath() const { return _dylibInstallPath; } + uint32_t timestamp() const { return _dylibTimeStamp; } + uint32_t currentVersion() const { return _dylibCurrentVersion; } + uint32_t compatibilityVersion() const{ return _dylibCompatibilityVersion; } + void setExplicitlyLinked() { _explicitlyLinked = true; } + bool explicitlyLinked() const { return _explicitlyLinked; } + void setImplicitlyLinked() { _implicitlyLinked = true; } + bool implicitlyLinked() const { return _implicitlyLinked; } + // attributes of how dylib will be used when linked + void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; } + bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; } + void setWillBeWeakLinked() { _weakLinked = true; } + bool willBeWeakLinked() const { return _weakLinked || + (_hasWeakImportedSymbols && !_hasNonWeakImportedSymbols); } + 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; } + + virtual void processIndirectLibraries(DylibHandler* handler, bool addImplicitDylibs) = 0; + virtual bool providedExportAtom() const = 0; + virtual const char* parentUmbrella() const = 0; + virtual const std::vector* allowableClients() const = 0; + virtual bool hasWeakExternals() const = 0; + virtual bool deadStrippable() const = 0; + virtual bool hasWeakDefinition(const char* name) const = 0; + virtual bool hasPublicInstallName() const = 0; + protected: + const char* _dylibInstallPath; + uint32_t _dylibTimeStamp; + uint32_t _dylibCurrentVersion; + uint32_t _dylibCompatibilityVersion; + bool _explicitlyLinked; + bool _implicitlyLinked; + bool _lazyLoadedDylib; + bool _weakLinked; + bool _reExported; + bool _upward; + bool _hasNonWeakImportedSymbols; + bool _hasWeakImportedSymbols; + bool _dead; + }; +} // namespace dylib + + + +// +// ld::Section +// +class Section +{ +public: + enum Type { typeUnclassified, typeCode, typePageZero, typeImportProxies, typeLinkEdit, typeMachHeader, typeStack, + typeLiteral4, typeLiteral8, typeLiteral16, typeConstants, typeTempLTO, + typeCString, typeNonStdCString, typeCStringPointer, typeUTF16Strings, typeCFString, typeObjC1Classes, + typeCFI, typeLSDA, typeDtraceDOF, typeUnwindInfo, typeObjCClassRefs, typeObjC2CategoryList, + typeZeroFill, typeTentativeDefs, typeLazyPointer, typeStub, typeNonLazyPointer, typeDyldInfo, + typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, + typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols, + typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers, + typeFirstSection, typeLastSection }; + + + Section(const char* sgName, const char* sctName, + Type t, bool hidden=false) + : _segmentName(sgName), _sectionName(sctName), + _type(t), _hidden(hidden) {} + Section(const Section& sect) + : _segmentName(sect.segmentName()), _sectionName(sect.sectionName()), + _type(sect.type()), _hidden(sect.isSectionHidden()) {} + + bool operator==(const Section& rhs) const { return ( (_hidden==rhs._hidden) && + (strcmp(_segmentName, rhs._segmentName)==0) && + (strcmp(_sectionName, rhs._sectionName)==0) ); } + bool operator!=(const Section& rhs) const { return ! (*this == rhs); } + const char* segmentName() const { return _segmentName; } + const char* sectionName() const { return _sectionName; } + Type type() const { return _type; } + bool isSectionHidden() const { return _hidden; } + +private: + const char* _segmentName; + const char* _sectionName; + Type _type; + bool _hidden; +}; + + + +// +// ld::Fixup +// +// A Fixup describes how part of an Atom's content must be fixed up. For instance, +// an instruction may contain a displacement to another Atom that must be +// fixed up by the linker. +// +// A Fixup my reference another Atom. There are two kinds of references: direct and by-name. +// With a direct reference, the target is bound by the File that created it. +// For instance a reference to a static would produce a direct reference. +// A by-name reference requires the linker to find the target Atom with the +// required name in order to be bound. +// +// For a link to succeed all Fixup must be bound. +// +// A Reference also has a fix-up-offset. This is the offset into the content of the +// Atom holding the reference where the fix-up (relocation) will be applied. +// +// +struct Fixup +{ + enum TargetBinding { bindingNone, bindingByNameUnbound, bindingDirectlyBound, bindingByContentBound, bindingsIndirectlyBound }; + enum Cluster { k1of1, k1of2, k2of2, k1of3, k2of3, k3of3, k1of4, k2of4, k3of4, k4of4, k1of5, k2of5, k3of5, k4of5, k5of5 }; + enum Kind { kindNone, kindNoneFollowOn, + // grouping + kindNoneGroupSubordinate, + kindNoneGroupSubordinateFDE, kindNoneGroupSubordinateLSDA, kindNoneGroupSubordinatePersonality, + // value calculations + kindSetTargetAddress, + kindSubtractTargetAddress, + kindAddAddend, + kindSubtractAddend, + kindSetTargetImageOffset, + kindSetTargetSectionOffset, + kindSetTargetTLVTemplateOffset, + // pointer store kinds (of current calculated value) + kindStore8, + kindStoreLittleEndian16, + kindStoreLittleEndianLow24of32, + kindStoreLittleEndian32, + kindStoreLittleEndian64, + kindStoreBigEndian16, + kindStoreBigEndianLow24of32, + kindStoreBigEndian32, + kindStoreBigEndian64, + // Intel specific store kinds + kindStoreX86BranchPCRel8, kindStoreX86BranchPCRel32, + kindStoreX86PCRel8, kindStoreX86PCRel16, + kindStoreX86PCRel32, kindStoreX86PCRel32_1, kindStoreX86PCRel32_2, kindStoreX86PCRel32_4, + kindStoreX86PCRel32GOTLoad, kindStoreX86PCRel32GOTLoadNowLEA, kindStoreX86PCRel32GOT, + kindStoreX86PCRel32TLVLoad, kindStoreX86PCRel32TLVLoadNowLEA, + kindStoreX86Abs32TLVLoad, kindStoreX86Abs32TLVLoadNowLEA, + // ARM specific store kinds + kindStoreARMBranch24, kindStoreThumbBranch22, + kindStoreARMLoad12, + kindStoreARMLow16, kindStoreARMHigh16, + kindStoreThumbLow16, kindStoreThumbHigh16, + // PowerPC specific store kinds + kindStorePPCBranch24, kindStorePPCBranch14, + kindStorePPCPicLow14, kindStorePPCPicLow16, kindStorePPCPicHigh16AddLow, + kindStorePPCAbsLow14, kindStorePPCAbsLow16, kindStorePPCAbsHigh16AddLow, kindStorePPCAbsHigh16, + // dtrace probes + kindDtraceExtra, + kindStoreX86DtraceCallSiteNop, kindStoreX86DtraceIsEnableSiteClear, + kindStoreARMDtraceCallSiteNop, kindStoreARMDtraceIsEnableSiteClear, + kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, + kindStorePPCDtraceCallSiteNop, kindStorePPCDtraceIsEnableSiteClear, + // lazy binding + kindLazyTarget, kindSetLazyOffset, + // pointer store combinations + kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32 + kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64 + kindStoreTargetAddressBigEndian32, // kindSetTargetAddress + kindStoreBigEndian32 + kindStoreTargetAddressBigEndian64, // kindSetTargetAddress + kindStoreBigEndian364 + kindSetTargetTLVTemplateOffsetLittleEndian32, // kindSetTargetTLVTemplateOffset + kindStoreLittleEndian32 + kindSetTargetTLVTemplateOffsetLittleEndian64, // kindSetTargetTLVTemplateOffset + kindStoreLittleEndian64 + // Intel value calculation and store combinations + kindStoreTargetAddressX86PCRel32, // kindSetTargetAddress + kindStoreX86PCRel32 + kindStoreTargetAddressX86BranchPCRel32, // kindSetTargetAddress + kindStoreX86BranchPCRel32 + kindStoreTargetAddressX86PCRel32GOTLoad,// kindSetTargetAddress + kindStoreX86PCRel32GOTLoad + kindStoreTargetAddressX86PCRel32GOTLoadNowLEA,// kindSetTargetAddress + kindStoreX86PCRel32GOTLoadNowLEA + kindStoreTargetAddressX86PCRel32TLVLoad, // kindSetTargetAddress + kindStoreX86PCRel32TLVLoad + kindStoreTargetAddressX86PCRel32TLVLoadNowLEA, // kindSetTargetAddress + kindStoreX86PCRel32TLVLoadNowLEA + kindStoreTargetAddressX86Abs32TLVLoad, // kindSetTargetAddress + kindStoreX86Abs32TLVLoad + kindStoreTargetAddressX86Abs32TLVLoadNowLEA, // kindSetTargetAddress + kindStoreX86Abs32TLVLoadNowLEA + // ARM value calculation and store combinations + kindStoreTargetAddressARMBranch24, // kindSetTargetAddress + kindStoreARMBranch24 + kindStoreTargetAddressThumbBranch22, // kindSetTargetAddress + kindStoreThumbBranch22 + kindStoreTargetAddressARMLoad12, // kindSetTargetAddress + kindStoreARMLoad12 + // PowerPC value calculation and store combinations + kindStoreTargetAddressPPCBranch24, // kindSetTargetAddress + kindStorePPCBranch24 + }; + + union { + const Atom* target; + const char* name; + uint64_t addend; + uint32_t bindingIndex; + } u; + uint32_t offsetInAtom; + Kind kind : 8; + Cluster clusterSize : 4; + bool weakImport : 1; + TargetBinding binding : 3; + bool contentAddendOnly : 1; + bool contentDetlaToAddendOnly : 1; + + typedef Fixup* iterator; + + Fixup() : + offsetInAtom(0), kind(kindNone), clusterSize(k1of1), weakImport(false), + binding(bindingNone), + contentAddendOnly(false), contentDetlaToAddendOnly(false) { u.target = NULL; } + + Fixup(Kind k, Atom* targetAtom) : + offsetInAtom(0), kind(k), clusterSize(k1of1), weakImport(false), + binding(Fixup::bindingDirectlyBound), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(targetAtom != NULL); u.target = targetAtom; } + + Fixup(uint32_t off, Cluster c, Kind k) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), + binding(Fixup::bindingNone), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { u.addend = 0; } + + Fixup(uint32_t off, Cluster c, Kind k, bool weakIm, const char* name) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(weakIm), + binding(Fixup::bindingByNameUnbound), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(name != NULL); u.name = name; } + + Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const char* name) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(name != NULL); u.name = name; } + + Fixup(uint32_t off, Cluster c, Kind k, const Atom* targetAtom) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), + binding(Fixup::bindingDirectlyBound), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(targetAtom != NULL); u.target = targetAtom; } + + Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const Atom* targetAtom) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(targetAtom != NULL); u.target = targetAtom; } + + Fixup(uint32_t off, Cluster c, Kind k, uint64_t addend) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), + binding(Fixup::bindingNone), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { u.addend = addend; } + + bool firstInCluster() const { + switch (clusterSize) { + case k1of1: + case k1of2: + case k1of3: + case k1of4: + case k1of5: + return true; + default: + break; + } + return false; + } + + bool lastInCluster() const { + switch (clusterSize) { + case k1of1: + case k2of2: + case k3of3: + case k4of4: + case k5of5: + return true; + default: + break; + } + return false; + } + +}; + +// +// ld::Atom +// +// An atom is the fundamental unit of linking. A C function or global variable is an atom. +// An atom has content and attributes. The content of a function atom is the instructions +// that implement the function. The content of a global variable atom is its initial bits. +// +// Name: +// The name of an atom is the label name generated by the compiler. A C compiler names foo() +// as _foo. A C++ compiler names foo() as __Z3foov. +// The name refers to the first byte of the content. An atom cannot have multiple entry points. +// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. +// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. +// +// Scope: +// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond +// to the C visibility of static, hidden, default. +// +// DefinitionKind: +// An atom is one of five defintion kinds: +// regular Most atoms. +// weak C++ compiler makes some functions weak if there might be multiple copies +// that the linker needs to coalesce. +// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. +// It could be a prototype or it could be a definition. +// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists +// so that the graph of Atoms can be complete. +// external-weak Same as external, but the definition in the dylib is weak. +// +// SymbolTableInclusion: +// An atom may or may not be in the symbol table in an object file. +// in Most atoms for functions or global data +// not-in Anonymous atoms such literal c-strings, or other compiler generated data +// not-in-final Atom whose name should not be in the symbol table of final linkd image (e.g. 'l' labels .eh labels) +// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) +// +// ContentType: +// Some atoms require specially processing by the linker based on their content. For instance, zero-fill data +// atom are group together at the end of the DATA segment to reduce disk size. +// +// ObjectAddress: +// For reproducability, the linker lays out atoms in the order they occurred in the source (object) files. +// The objectAddress() method returns the address of an atom in the object file so that the linker +// can arrange the atoms. +// +// +class Atom +{ +public: + enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; + enum Definition { definitionRegular, definitionTentative, definitionAbsolute, definitionProxy }; + enum Combine { combineNever, combineByName, combineByNameAndContent, combineByNameAndReferences }; + enum ContentType { typeUnclassified, typeZeroFill, typeCString, typeCFI, typeLSDA, typeSectionStart, + typeSectionEnd, typeBranchIsland, typeLazyPointer, typeStub, typeNonLazyPointer, + typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, + typeLTOtemporary, typeResolver, + typeTLV, typeTLVZeroFill, typeTLVInitialValue, typeTLVInitializerPointers }; + + enum SymbolTableInclusion { symbolTableNotIn, symbolTableNotInFinalLinkedImages, symbolTableIn, + symbolTableInAndNeverStrip, symbolTableInAsAbsolute, + symbolTableInWithRandomAutoStripLabel }; + struct Alignment { + Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} + uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } + uint16_t powerOf2; + uint16_t modulus; + }; + struct LineInfo { + const char* fileName; + uint32_t atomOffset; + uint32_t lineNumber; + + typedef LineInfo* iterator; + }; + struct UnwindInfo { + uint32_t startOffset; + uint32_t unwindInfo; + + typedef UnwindInfo* iterator; + }; + + Atom(const Section& sect, Definition d, Combine c, Scope s, ContentType ct, + SymbolTableInclusion i, bool dds, bool thumb, bool al, Alignment a) : + _section(§), _address(0), _alignmentModulus(a.modulus), + _alignmentPowerOf2(a.powerOf2), _definition(d), _combine(c), + _dontDeadStrip(dds), _thumb(thumb), _alias(al), _autoHide(false), + _contentType(ct), _symbolTableInclusion(i), + _scope(s), _mode(modeSectionOffset), + _overridesADylibsWeakDef(false), _coalescedAway(false), + _weakImport(false), _live(false), _machoSection(0) + { + #ifndef NDEBUG + switch ( _combine ) { + case combineByNameAndContent: + case combineByNameAndReferences: + assert(_symbolTableInclusion == symbolTableNotIn); + assert(_scope != scopeGlobal); + break; + case combineByName: + case combineNever: + break; + }; + #endif + } + virtual ~Atom() {} + + const Section& section() const { return *_section; } + Definition definition() const { return _definition; } + Combine combine() const { return _combine; } + Scope scope() const { return _scope; } + ContentType contentType() const { return _contentType; } + SymbolTableInclusion symbolTableInclusion() const{ return _symbolTableInclusion; } + bool dontDeadStrip() const { return _dontDeadStrip; } + bool isThumb() const { return _thumb; } + bool isAlias() const { return _alias; } + Alignment alignment() const { return Alignment(_alignmentPowerOf2, _alignmentModulus); } + bool overridesDylibsWeakDef() const { return _overridesADylibsWeakDef; } + bool coalescedAway() const { return _coalescedAway; } + bool weakImported() const { return _weakImport; } + bool autoHide() const { return _autoHide; } + bool live() const { return _live; } + uint8_t machoSection() const { assert(_machoSection != 0); return _machoSection; } + + void setScope(Scope s) { _scope = s; } + void setSymbolTableInclusion(SymbolTableInclusion i) + { _symbolTableInclusion = i; } + void setCombine(Combine c) { _combine = c; } + void setOverridesDylibsWeakDef() { _overridesADylibsWeakDef = true; } + void setCoalescedAway() { _coalescedAway = true; } + void setWeakImported() { _weakImport = true; assert(_definition == definitionProxy); } + void setAutoHide() { _autoHide = true; } + void setLive() { _live = true; } + void setLive(bool value) { _live = value; } + void setMachoSection(unsigned x) { assert(x != 0); assert(x < 256); _machoSection = x; } + void setSectionOffset(uint64_t o){ assert(_mode == modeSectionOffset); _address = o; _mode = modeSectionOffset; } + void setSectionStartAddress(uint64_t a) { assert(_mode == modeSectionOffset); _address += a; _mode = modeFinalAddress; } + uint64_t sectionOffset() const { assert(_mode == modeSectionOffset); return _address; } + uint64_t finalAddress() const { assert(_mode == modeFinalAddress); return _address; } + + virtual const File* file() const = 0; + virtual bool translationUnitSource(const char** dir, const char** name) const = 0; + virtual const char* name() const = 0; + virtual uint64_t objectAddress() const = 0; + virtual uint64_t size() const = 0; + virtual void copyRawContent(uint8_t buffer[]) const = 0; + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual unsigned long contentHash(const class IndirectBindingTable&) const { return 0; } + virtual bool canCoalesceWith(const Atom& rhs, const class IndirectBindingTable&) const { return false; } + virtual Fixup::iterator fixupsBegin() const { return NULL; } + virtual Fixup::iterator fixupsEnd() const { return NULL; } + virtual UnwindInfo::iterator beginUnwind() const { return NULL; } + virtual UnwindInfo::iterator endUnwind() const { return NULL; } + virtual LineInfo::iterator beginLineInfo() const { return NULL; } + virtual LineInfo::iterator endLineInfo() const { return NULL; } + +protected: + enum AddressMode { modeSectionOffset, modeFinalAddress }; + + void setAttributesFromAtom(const Atom& a) { + _section = a._section; + _alignmentModulus = a._alignmentModulus; + _alignmentPowerOf2 = a._alignmentPowerOf2; + _definition = a._definition; + _combine = a._combine; + _dontDeadStrip = a._dontDeadStrip; + _thumb = a._thumb; + _alias = a._alias; + _autoHide = a._autoHide; + _contentType = a._contentType; + _symbolTableInclusion = a._symbolTableInclusion; + _scope = a._scope; + _mode = a._mode; + _overridesADylibsWeakDef = a._overridesADylibsWeakDef; + _coalescedAway = a._coalescedAway; + _weakImport = a._weakImport; + } + + const Section * _section; + uint64_t _address; + uint16_t _alignmentModulus; + uint8_t _alignmentPowerOf2; + Definition _definition : 2; + Combine _combine : 2; + bool _dontDeadStrip : 1; + bool _thumb : 1; + bool _alias : 1; + int _autoHide : 1; + ContentType _contentType : 5; + SymbolTableInclusion _symbolTableInclusion : 3; + Scope _scope : 2; + AddressMode _mode: 2; + bool _overridesADylibsWeakDef : 1; + bool _coalescedAway : 1; + bool _weakImport : 1; + bool _live : 1; + unsigned _machoSection : 8; +}; + + +class IndirectBindingTable +{ +public: + virtual const char* indirectName(uint32_t bindingIndex) const = 0; + virtual const ld::Atom* indirectAtom(uint32_t bindingIndex) const = 0; +}; + + +class Internal +{ +public: + class FinalSection : public ld::Section { + public: + FinalSection(const Section& sect) : Section(sect), address(0), + fileOffset(0), size(0), alignment(0), + indirectSymTabStartIndex(0), indirectSymTabElementSize(0), + relocStart(0), relocCount(0), + hasLocalRelocs(false), hasExternalRelocs(false) {} + std::vector atoms; + uint64_t address; + uint64_t fileOffset; + uint64_t size; + uint32_t alignmentPaddingBytes; + uint8_t alignment; + uint32_t indirectSymTabStartIndex; + uint32_t indirectSymTabElementSize; + uint32_t relocStart; + uint32_t relocCount; + bool hasLocalRelocs; + bool hasExternalRelocs; + }; + + virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0; + virtual ld::Internal::FinalSection* getFinalSection(const ld::Section& inputSection) = 0; + virtual ~Internal() {} + Internal() : bundleLoader(NULL), + entryPoint(NULL), classicBindingHelper(NULL), + lazyBindingHelper(NULL), compressedFastBinderProxy(NULL), + objcObjectConstraint(ld::File::objcConstraintNone), + objcDylibConstraint(ld::File::objcConstraintNone), + cpuSubType(0), + allObjectFilesScatterable(true), hasObjcReplacementClasses(false), + someObjectFileHasDwarf(false), usingHugeSections(false) { } + + std::vector sections; + std::vector dylibs; + std::vector stabs; + std::vector indirectBindingTable; + const ld::dylib::File* bundleLoader; + const Atom* entryPoint; + const Atom* classicBindingHelper; + const Atom* lazyBindingHelper; + const Atom* compressedFastBinderProxy; + ld::File::ObjcConstraint objcObjectConstraint; + ld::File::ObjcConstraint objcDylibConstraint; + uint32_t cpuSubType; + bool allObjectFilesScatterable; + bool hasObjcReplacementClasses; + bool someObjectFileHasDwarf; + bool usingHugeSections; +}; + + + + + + + + + + + + +} // namespace ld + +#endif // __LD_HPP__ diff --git a/ld64/src/ld/lto_file.hpp b/ld64/src/ld/lto_file.hpp new file mode 100644 index 0000000..24d3f58 --- /dev/null +++ b/ld64/src/ld/lto_file.hpp @@ -0,0 +1,642 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LTO_READER_H__ +#define __LTO_READER_H__ + +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" + +#include "llvm-c/lto.h" + + +namespace lto { + + +// +// ld64 only tracks non-internal symbols from an llvm bitcode file. +// We model this by having an InternalAtom which represent all internal functions and data. +// All non-interal symbols from a bitcode file are represented by an Atom +// and each Atom has a reference to the InternalAtom. The InternalAtom +// also has references to each symbol external to the bitcode file. +// +class InternalAtom : public ld::Atom +{ +public: + InternalAtom(class File& f); + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() { return &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() { return &_undefs[_undefs.size()]; } + + // for adding references to symbols outside bitcode file + void addReference(const char* name) + { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, + ld::Fixup::fixupNone, false, name)); } +private: + + ld::File& _file; + std::vector _undefs; +}; + + +// +// LLVM bitcode file +// +class File : public ld::relocatable::File +{ +public: + File(const char* path, time_t mTime, const uint8_t* content, + uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); + virtual ~File(); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&); + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) + { return false; } + + // overrides of ld::relocatable::File + virtual bool objcReplacementClasses() { return false; } + virtual DebugInfoKind debugInfo() { return ld::relocatable::File::kDebugInfoNone; } + virtual std::vector* stabs() { return NULL; } + virtual bool canScatterAtoms() { return true; } + + lto_module_t module() { return _module; } + class InternalAtom& internalAtom() { return _internalAtom; } +private: + friend class Atom; + friend class InternalAtom; + + cpu_type_t _architecture; + class InternalAtom _internalAtom; + class Atom* _atomArray; + uint32_t _atomArrayCount; + lto_module_t _module; + ld::Section _section; +}; + +// +// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, +// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After +// optimization is performed, real Atoms are created for these symobls. However these real Atoms +// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate +// methods to real atom. +// +class Atom : public ld::Atom +{ +public: + Atom(File& f, const char* name, ld::Atom::Scope s, + ld::Atom::Definition d, ld::Atom::Alignment a); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } + virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } + virtual void copyRawContent(uint8_t buffer[]) const + { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } + + ld::Atom* compiledAtom() { return _compiledAtom; } + void setCompiledAtom(ld::Atom& atom) + { _compiledAtom = &atom; } +private: + + File& _file; + const char* _name; + ld::Atom* _compiledAtom; +}; + + + + + + + +class Parser +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); + static const char* fileKind(const uint8_t* fileContent); + static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, uint32_t ordinal, cpu_type_t architecture); + static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } + static bool optimize(const std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, + const std::set&, + std::vector& newDeadAtoms, + uint32_t nextInputOrdinal, + ld::OutFile* writer, ld::Atom* entryPointAtom, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, + bool verbose, bool saveTemps, + const char* outputFilePath, + bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, + bool allowTextRelocs, cpu_type_t arch); + + static const char* ltoVersion() { return ::lto_get_version(); } + +private: + static const char* tripletPrefixForArch(cpu_type_t arch); + static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch); + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; + + class AtomSyncer : public ld::File::AtomHandler { + public: + AtomSyncer(std::vector& a, std::vector&na, + CStringToAtom la, CStringToAtom dla) : + additionalUndefines(a), newAtoms(na), llvmAtoms(la), deadllvmAtoms(dla) { } + virtual void doAtom(class ld::Atom&); + + std::vector& additionalUndefines; + std::vector& newAtoms; + CStringToAtom llvmAtoms; + CStringToAtom deadllvmAtoms; + }; + + static std::vector _s_files; +}; + +std::vector Parser::_s_files; + + +const char* Parser::tripletPrefixForArch(cpu_type_t arch) +{ + switch (arch) { + case CPU_TYPE_POWERPC: + return "powerpc-"; + case CPU_TYPE_POWERPC64: + return "powerpc64-"; + case CPU_TYPE_I386: + return "i386-"; + case CPU_TYPE_X86_64: + return "x86_64-"; + case CPU_TYPE_ARM: + return "arm"; + } + return ""; +} + +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) +{ + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); +} + +const char* Parser::fileKind(const uint8_t* p) +{ + if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { + uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + switch (arch) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + return "arm"; + } + return "unknown bitcode architecture"; + } + return NULL; +} + +File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + uint32_t ordinal, cpu_type_t architecture) +{ + File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); + _s_files.push_back(f); + return f; +} + + +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Parser::validFile(p) ) + return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Parser::validFile(p) ) + return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Parser::validFile(p) ) + return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Parser::validFile(p) ) + return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Parser::validFile(p) ) + return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + } + throw "LLVM LTO, file is not of required architecture"; +} + + + +File::File(const char* path, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ordinal, cpu_type_t arch) + : ld::relocatable::File(path,mTime,ordinal), _architecture(arch), _internalAtom(*this), + _atomArray(NULL), _atomArrayCount(0), _module(NULL), + _section("__TEXT_", "__tmp_lto", ld::Section::typeUnclassified) +{ + // create llvm module + _module = ::lto_module_create_from_memory(content, contentLength); + if ( _module == NULL ) + throwf("could not parse object file %s: %s", path, lto_get_error_message()); + + // create atom for each global symbol in module + uint32_t count = ::lto_module_get_num_symbols(_module); + _atomArray = (Atom*)malloc(sizeof(Atom)*count); + for (uint32_t i=0; i < count; ++i) { + const char* name = ::lto_module_get_symbol_name(_module, i); + lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i); + + // LTO doesn't like dtrace symbols + // ignore dtrace static probes for now + // later when codegen is done and a mach-o file is produces the probes will be processed + if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) + continue; + + ld::Atom::Definition def; + switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { + case LTO_SYMBOL_DEFINITION_REGULAR: + def = ld::Atom::definitionRegular; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + def = ld::Atom::definitionTentative; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + def = ld::Atom::definitionRegular; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: + def = ld::Atom::definitionProxy; + break; + default: + throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); + } + + // make LLVM atoms for definitions and a reference for undefines + if ( def != ld::Atom::definitionProxy ) { + ld::Atom::Scope scope; + switch ( attr & LTO_SYMBOL_SCOPE_MASK) { + case LTO_SYMBOL_SCOPE_INTERNAL: + scope = ld::Atom::scopeTranslationUnit; + break; + case LTO_SYMBOL_SCOPE_HIDDEN: + scope = ld::Atom::scopeLinkageUnit; + break; + case LTO_SYMBOL_SCOPE_DEFAULT: + scope = ld::Atom::scopeGlobal; + break; + default: + throwf("unknown scope for symbol %s in bitcode file %s", name, path); + } + // only make atoms for non-internal symbols + if ( scope == ld::Atom::scopeTranslationUnit ) + continue; + uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); + // make Atom using placement new operator + new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, alignment); + } + else { + // add to list of external references + _internalAtom.addReference(name); + } + } +} + +File::~File() +{ + if ( _module != NULL ) + ::lto_module_dispose(_module); +} + +bool File::forEachAtom(ld::File::AtomHandler& handler) +{ + handler.doAtom(_internalAtom); + for(uint32_t i=0; i < _atomArrayCount; ++i) { + handler.doAtom(_atomArray[i]); + } + return true; +} + +InternalAtom::InternalAtom(File& f) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, false, false, ld::Atom::Alignment(0)), + _file(f) +{ +} + +Atom::Atom(File& f, const char* name, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Alignment a) + : ld::Atom(f._section, d, ld::Atom::combineNever, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, a), + _file(f), _name(name), _compiledAtom(NULL) +{ +} + + + + +bool Parser::optimize(const std::vector& allAtoms, std::vector& newAtoms, + std::vector& additionalUndefines, + const std::set& deadAtoms, + std::vector& newlyDeadAtoms, + uint32_t nextInputOrdinal, + ld::OutFile* writer, ld::Atom* entryPointAtom, + const std::vector& llvmOptions, + bool allGlobalsAReDeadStripRoots, + bool verbose, bool saveTemps, + const char* outputFilePath, + bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, + bool allowTextRelocs, cpu_type_t arch) +{ + // exit quickly if nothing to do + if ( _s_files.size() == 0 ) + return false; + + // print out LTO version string if -v was used + if ( verbose ) + fprintf(stderr, "%s\n", lto_get_version()); + + // create optimizer and add each Reader + lto_code_gen_t generator = ::lto_codegen_create(); + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + if ( ::lto_codegen_add_module(generator, (*it)->module()) ) + throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); + } + + // add any -mllvm command line options + for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { + ::lto_codegen_debug_options(generator, *it); + } + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringToAtom llvmAtoms; + bool hasNonllvmAtoms = false; + for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + ld::Atom* atom = *it; + // only look at references that come from an atom that is not an llvm atom + if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { + // remember if we've seen any atoms not from an llvm reader and not from the writer +// if ( atom->getFile() != writer ) +// hasNonllvmAtoms = true; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->binding != ld::Fixup::bindingByNameBound ) + continue; + // and reference an llvm atom + if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(fit->u.target->name()); + } + } + else { + llvmAtoms[atom->name()] = (Atom*)atom; + } + } + // if entry point is in a llvm bitcode file, it must be preserved by LTO + if ( entryPointAtom != NULL ) { + if ( entryPointAtom->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(entryPointAtom->name()); + } + + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions + // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // atom so that the linker can replace it with the mach-o one later. + CStringToAtom deadllvmAtoms; + for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { + ld::Atom* atom = *it; + if ( atom->contentType() == ld::Atom::typeLTOtemporary ) { + const char* name = atom->name(); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)atom; + } + } + + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( (atom->scope() == ld::Atom::scopeGlobal) ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( relocatable && !hasNonllvmAtoms ) { + if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { + // HACK, no good way to tell linker we are all done, so just quit + exit(0); + } + warning("could not produce merged bitcode file"); + } + + // set code-gen model + lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + if ( mainExecutable ) { + if ( staticExecutable ) { + // darwin x86_64 "static" code model is really dynamic code model + if ( arch == CPU_TYPE_X86_64 ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_STATIC; + } + else { + if ( pie ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + } + } + else { + if ( allowTextRelocs ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + } + if ( ::lto_codegen_set_pic_model(generator, model) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // if requested, save off merged bitcode file + if ( saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + +#if LTO_API_VERSION >= 3 + // find assembler next to linker + char path[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "as"); + struct stat statInfo; + if ( stat(path, &statInfo) == 0 ) + ::lto_codegen_set_assembler_path(generator, path); + } + } +#endif + // run code generator + size_t machOFileLen; + const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + + // if requested, save off temp mach-o file + if ( saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } + + // parse generated mach-o file into a MachOReader + ld::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, arch); + + // sync generated mach-o atoms with existing atoms ld knows about + AtomSyncer syncer(additionalUndefines,newAtoms,llvmAtoms,deadllvmAtoms); + machoFile->forEachAtom(syncer); + + // Remove InternalAtoms from ld + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + newlyDeadAtoms.push_back(&((*it)->internalAtom())); + } + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->compiledAtom() == NULL ) + newlyDeadAtoms.push_back(li->second); + } + + return true; +} + + +void Parser::AtomSyncer::doAtom(ld::Atom& machoAtom) +{ + // update proxy atoms to point to real atoms and find new atoms + const char* name = machoAtom.name(); + if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { + CStringToAtom::iterator pos = llvmAtoms.find(name); + if ( pos != llvmAtoms.end() ) { + // turn Atom into a proxy for this mach-o atom + pos->second->setCompiledAtom(machoAtom); + } + else { + // an atom of this name was not in the allAtoms list the linker gave us + if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { + // this corresponding to an atom that the linker coalesced away. + // Don't pass it back as a new atom + } + else + { + // this is something new that lto conjured up, tell ld its new + newAtoms.push_back(&machoAtom); + } + } + } + else { + // ld only knew about non-satic atoms, so this one must be new + newAtoms.push_back(&machoAtom); + } + + // adjust fixups to go through proxy atoms + for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + break; + case ld::Fixup::bindingByNameUnbound: + // don't know if this target has been seen by linker before or if it is new + // be conservitive and tell linker it is new + additionalUndefines.push_back(fit->u.name); + break; + case ld::Fixup::bindingByNameBound: + break; + case ld::Fixup::bindingDirectlyBound: + // If mach-o atom is referencing another mach-o atom then + // reference is not going through Atom proxy. Fix it here to ensure that all + // llvm symbol references always go through Atom proxy. + break; + case ld::Fixup::bindingByContentBound: + break; + } + } + +} + + + +}; // namespace lto + + +#endif + diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp new file mode 100644 index 0000000..8c866cd --- /dev/null +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -0,0 +1,522 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + +#include "macho_relocatable_file.h" +#include "lto_file.h" +#include "archive_file.h" + + +namespace archive { + +typedef const struct ranlib* ConstRanLibPtr; + +// forward reference +template class File; + + +template +class Parser +{ +public: + typedef typename A::P P; + + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts) { + return File::validFile(fileContent, fileLength, opts); } + static File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t mTime, + uint32_t ordinal, const ParserOptions& opts) { + return new File(fileContent, fileLength, path, mTime, + ordinal, opts); + } + +}; + +template +class File : public ld::File +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts); + File(const uint8_t* fileContent, uint64_t fileLength, + const char* pth, time_t modTime, + uint32_t ord, const ParserOptions& opts); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual uint32_t subFileCount() const { return _archiveFilelength/sizeof(ar_hdr); } + +private: + static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts); + static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts); + static cpu_type_t architecture(); + + + class Entry : ar_hdr + { + public: + const char* name() const; + time_t modificationTime() const; + const uint8_t* content() const; + uint32_t contentSize() const; + const Entry* next() const; + private: + bool hasLongName() const; + unsigned int getLongNameSpace() const; + + }; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; + + typedef typename A::P P; + typedef typename A::P::E E; + + const struct ranlib* ranlibHashSearch(const char* name) const; + ld::relocatable::File* makeObjectFileForMember(const Entry* member) const; + bool memberHasObjCCategories(const Entry* member) const; + void dumpTableOfContents(); + void buildHashTable(); + + const uint8_t* _archiveFileContent; + uint64_t _archiveFilelength; + const struct ranlib* _tableOfContents; + uint32_t _tableOfContentCount; + const char* _tableOfContentStrings; + mutable std::vector _instantiatedFiles; + mutable std::set _instantiatedEntries; + NameToEntryMap _hashTable; + const bool _forceLoadAll; + const bool _forceLoadObjC; + const bool _forceLoadThis; + const bool _verboseLoad; + const bool _logAllFiles; + const mach_o::relocatable::ParserOptions _objOpts; +}; + + +template +bool File::Entry::hasLongName() const +{ + return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); +} + +template +unsigned int File::Entry::getLongNameSpace() const +{ + char* endptr; + long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); + return result; +} + +template +const char* File::Entry::name() const +{ + if ( this->hasLongName() ) { + int len = this->getLongNameSpace(); + static char longName[256]; + strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); + longName[len] = '\0'; + return longName; + } + else { + static char shortName[20]; + strncpy(shortName, this->ar_name, 16); + shortName[16] = '\0'; + char* space = strchr(shortName, ' '); + if ( space != NULL ) + *space = '\0'; + return shortName; + } +} + +template +time_t File::Entry::modificationTime() const +{ + char temp[14]; + strncpy(temp, this->ar_date, 12); + temp[12] = '\0'; + char* endptr; + return (time_t)strtol(temp, &endptr, 10); +} + + +template +const uint8_t* File::Entry::content() const +{ + if ( this->hasLongName() ) + return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); + else + return ((uint8_t*)this) + sizeof(ar_hdr); +} + + +template +uint32_t File::Entry::contentSize() const +{ + char temp[12]; + strncpy(temp, this->ar_size, 10); + temp[10] = '\0'; + char* endptr; + long size = strtol(temp, &endptr, 10); + // long name is included in ar_size + if ( this->hasLongName() ) + size -= this->getLongNameSpace(); + return size; +} + + +template +const class File::Entry* File::Entry::next() const +{ + const uint8_t* p = this->content() + contentSize(); + p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align + return (class File::Entry*)p; +} + + +template <> cpu_type_t File::architecture() { return CPU_TYPE_POWERPC; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_I386; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } + + +template +bool File::validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) +{ + return mach_o::relocatable::isObjectFile(fileContent, fileLength, opts); +} + +template +bool File::validLTOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) +{ + return lto::isObjectFile(fileContent, fileLength, opts.architecture, opts.subType); +} + + + +template +bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) +{ + // must have valid archive header + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + return false; + + // peak at first .o file and verify it is correct architecture + const Entry* const start = (Entry*)&fileContent[8]; + const Entry* const end = (Entry*)&fileContent[fileLength]; + for (const Entry* p=start; p < end; p = p->next()) { + const char* memberName = p->name(); + // skip option table-of-content member + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + // archive is valid if first .o file is valid + return (validMachOFile(p->content(), p->contentSize(), opts) || validLTOFile(p->content(), p->contentSize(), opts)); + } + // empty archive + return true; +} + + +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), + _archiveFileContent(fileContent), _archiveFilelength(fileLength), + _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL), + _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC), + _forceLoadThis(opts.forceLoadThisArchive), _verboseLoad(opts.verboseLoad), + _logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts) +{ + if ( strncmp((const char*)fileContent, "!\n", 8) != 0 ) + throw "not an archive"; + + if ( !_forceLoadAll ) { + const Entry* const firstMember = (Entry*)&_archiveFileContent[8]; + if ( (strcmp(firstMember->name(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->name(), SYMDEF) == 0) ) { + const uint8_t* contents = firstMember->content(); + uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); + _tableOfContents = (const struct ranlib*)&contents[4]; + _tableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); + _tableOfContentStrings = (const char*)&contents[ranlibArrayLen+8]; + if ( ((uint8_t*)(&_tableOfContents[_tableOfContentCount]) > &fileContent[fileLength]) + || ((uint8_t*)_tableOfContentStrings > &fileContent[fileLength]) ) + throw "malformed archive, perhaps wrong architecture"; + this->buildHashTable(); + } + else + throw "archive has no table of contents"; + } +} + +template <> +bool File::memberHasObjCCategories(const Entry* member) const +{ + // i386 uses ObjC1 ABI which has .objc_category* global symbols + return false; +} + +template <> +bool File::memberHasObjCCategories(const Entry* member) const +{ + // ppc uses ObjC1 ABI which has .objc_category* global symbols + return false; +} + + +template +bool File::memberHasObjCCategories(const Entry* member) const +{ + // x86_64 and ARM use ObjC2 which has no global symbol for categories + return mach_o::relocatable::hasObjC2Categories(member->content()); +} + + +template +ld::relocatable::File* File::makeObjectFileForMember(const Entry* member) const +{ + const char* memberName = member->name(); + char memberPath[strlen(this->path()) + strlen(memberName)+4]; + strcpy(memberPath, this->path()); + strcat(memberPath, "("); + strcat(memberPath, memberName); + strcat(memberPath, ")"); + //fprintf(stderr, "using %s from %s\n", memberName, this->path()); + try { + // range check + if ( member > (Entry*)(_archiveFileContent+_archiveFilelength) ) + throwf("corrupt archive, member starts past end of file"); + if ( (member->content() + member->contentSize()) > (_archiveFileContent+_archiveFilelength) ) + throwf("corrupt archive, member contents extends past end of file"); + const char* mPath = strdup(memberPath); + // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive + uint32_t memberIndex = ((uint8_t*)member - _archiveFileContent)/sizeof(ar_hdr); + // see if member is mach-o file + ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(), + mPath, member->modificationTime(), + this->ordinal() + memberIndex, _objOpts); + if ( result != NULL ) + return result; + // 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; + + throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName, member->contentSize()); + } + catch (const char* msg) { + throwf("in %s, %s", memberPath, msg); + } +} + + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + bool didSome = false; + if ( _forceLoadAll || _forceLoadThis ) { + // call handler on all .o files in this archive + const Entry* const start = (Entry*)&_archiveFileContent[8]; + const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; + for (const Entry* p=start; p < end; p = p->next()) { + const char* memberName = p->name(); + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + 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); + } + ld::relocatable::File* file = this->makeObjectFileForMember(p); + didSome |= file->forEachAtom(handler); + } + } + else if ( _forceLoadObjC ) { + // call handler on all .o files in this archive containing objc classes + 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); + } + } + } + // ObjC2 has no symbols in .o files with categories, but not classes, look deeper for those + const Entry* const start = (Entry*)&_archiveFileContent[8]; + const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; + for (const Entry* member=start; member < end; member = member->next()) { + // only look at files not already instantiated + if ( _instantiatedEntries.count(member) == 0 ) { + //fprintf(stderr, "checking member %s\n", member->name()); + if ( this->memberHasObjCCategories(member) ) { + 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); + } + } + } + } + return didSome; +} + +template +bool File::justInTimeforEachAtom(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)]; + // only call handler for each member once + if ( _instantiatedEntries.count(member) == 0 ) { + _instantiatedEntries.insert(member); + 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); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); + return false; +} + + +typedef const struct ranlib* ConstRanLibPtr; + +template +ConstRanLibPtr File::ranlibHashSearch(const char* name) const +{ + typename NameToEntryMap::const_iterator pos = _hashTable.find(name); + if ( pos != _hashTable.end() ) + return pos->second; + else + return NULL; +} + +template +void File::buildHashTable() +{ + // walk through list backwards, adding/overwriting entries + // this assures that with duplicates those earliest in the list will be found + for (int i = _tableOfContentCount-1; i >= 0; --i) { + const struct ranlib* entry = &_tableOfContents[i]; + const char* entryName = &_tableOfContentStrings[E::get32(entry->ran_un.ran_strx)]; + if ( E::get32(entry->ran_off) > _archiveFilelength ) { + throwf("malformed archive TOC entry for %s, offset %d is beyond end of file %lld\n", + entryName, entry->ran_off, _archiveFilelength); + } + + //const Entry* member = (Entry*)&_archiveFileContent[E::get32(entry->ran_off)]; + //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); + _hashTable[entryName] = entry; + } +} + +template +void File::dumpTableOfContents() +{ + for (unsigned int i=0; i < _tableOfContentCount; ++i) { + const struct ranlib* e = &_tableOfContents[i]; + printf("%s in %s\n", &_tableOfContentStrings[E::get32(e->ran_un.ran_strx)], ((Entry*)&_archiveFileContent[E::get32(e->ran_off)])->name()); + } +} + + +// +// main function used by linker to instantiate archive files +// +ld::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 ) { + case CPU_TYPE_X86_64: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + + + +}; // namespace archive + + diff --git a/ld64/src/ld/parsers/archive_file.h b/ld64/src/ld/parsers/archive_file.h new file mode 100644 index 0000000..4dcbc8b --- /dev/null +++ b/ld64/src/ld/parsers/archive_file.h @@ -0,0 +1,48 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ARCHIVE_FILE_H__ +#define __ARCHIVE_FILE_H__ + +#include "ld.hpp" +#include "macho_relocatable_file.h" + +namespace archive { + +struct ParserOptions { + mach_o::relocatable::ParserOptions objOpts; + bool forceLoadThisArchive; + bool forceLoadAll; + bool forceLoadObjC; + bool verboseLoad; + bool logAllFiles; +}; + +extern ld::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts); + +} // namespace archive + + +#endif // __ARCHIVE_FILE_H__ diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp new file mode 100644 index 0000000..6221c90 --- /dev/null +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -0,0 +1,870 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-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@ + */ + +#ifndef __LTO_READER_H__ +#define __LTO_READER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" +#include "macho_relocatable_file.h" +#include "lto_file.h" + +#include "llvm-c/lto.h" + + +namespace lto { + + +// +// ld64 only tracks non-internal symbols from an llvm bitcode file. +// We model this by having an InternalAtom which represent all internal functions and data. +// All non-interal symbols from a bitcode file are represented by an Atom +// and each Atom has a reference to the InternalAtom. The InternalAtom +// also has references to each symbol external to the bitcode file. +// +class InternalAtom : public ld::Atom +{ +public: + InternalAtom(class File& f); + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + virtual uint64_t size() const { return 0; } + 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 &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } + + // for adding references to symbols outside bitcode file + void addReference(const char* nm) + { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, + ld::Fixup::kindNone, false, nm)); } +private: + + ld::File& _file; + mutable std::vector _undefs; +}; + + +// +// LLVM bitcode file +// +class File : public ld::relocatable::File +{ +public: + File(const char* path, time_t mTime, const uint8_t* content, + uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); + virtual ~File(); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const + { return false; } + virtual uint32_t cpuSubType() const { return _cpuSubType; } + + // overrides of ld::relocatable::File + virtual bool objcReplacementClasses() const { return false; } + virtual DebugInfoKind debugInfo() const { return _debugInfo; } + virtual const char* debugInfoPath() const { return _debugInfoPath; } + virtual time_t debugInfoModificationTime() const + { return _debugInfoModTime; } + virtual const std::vector* stabs() const { return NULL; } + virtual bool canScatterAtoms() const { return true; } + + lto_module_t module() { return _module; } + class InternalAtom& internalAtom() { return _internalAtom; } + void setDebugInfo(ld::relocatable::File::DebugInfoKind k, + const char* pth, time_t modTime, uint32_t subtype) + { _debugInfo = k; + _debugInfoPath = pth; + _debugInfoModTime = modTime; + _cpuSubType = subtype;} + +private: + friend class Atom; + friend class InternalAtom; + friend class Parser; + + cpu_type_t _architecture; + class InternalAtom _internalAtom; + class Atom* _atomArray; + uint32_t _atomArrayCount; + lto_module_t _module; + const char* _debugInfoPath; + time_t _debugInfoModTime; + ld::Section _section; + ld::Fixup _fixupToInternal; + ld::relocatable::File::DebugInfoKind _debugInfo; + uint32_t _cpuSubType; +}; + +// +// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, +// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After +// optimization is performed, real Atoms are created for these symobls. However these real Atoms +// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate +// methods to real atom. +// +class Atom : public ld::Atom +{ +public: + Atom(File& f, const char* name, ld::Atom::Scope s, + ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } + virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } + virtual void copyRawContent(uint8_t buffer[]) const + { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } + virtual const uint8_t* rawContentPointer() const + { return (_compiledAtom ? _compiledAtom->rawContentPointer() : NULL); } + virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + { return (_compiledAtom ? _compiledAtom->contentHash(ibt) : 0); } + virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + { return (_compiledAtom ? _compiledAtom->canCoalesceWith(rhs,ibt) : false); } + virtual ld::Fixup::iterator fixupsBegin() const + { return (_compiledAtom ? _compiledAtom->fixupsBegin() : (ld::Fixup*)&_file._fixupToInternal); } + virtual ld::Fixup::iterator fixupsEnd() const + { return (_compiledAtom ? _compiledAtom->fixupsEnd() : &((ld::Fixup*)&_file._fixupToInternal)[1]); } + virtual ld::Atom::UnwindInfo::iterator beginUnwind() const + { return (_compiledAtom ? _compiledAtom->beginUnwind() : NULL); } + virtual ld::Atom::UnwindInfo::iterator endUnwind() const + { return (_compiledAtom ? _compiledAtom->endUnwind() : NULL); } + virtual ld::Atom::LineInfo::iterator beginLineInfo() const + { return (_compiledAtom ? _compiledAtom->beginLineInfo() : NULL); } + virtual ld::Atom::LineInfo::iterator endLineInfo() const + { return (_compiledAtom ? _compiledAtom->endLineInfo() : NULL); } + + const ld::Atom* compiledAtom() { return _compiledAtom; } + void setCompiledAtom(const ld::Atom& atom); + +private: + + File& _file; + const char* _name; + const ld::Atom* _compiledAtom; +}; + + + + + + + +class Parser +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); + static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); + static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } + static bool optimize( const std::vector& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines); + + static const char* ltoVersion() { return ::lto_get_version(); } + +private: + static const char* tripletPrefixForArch(cpu_type_t arch); + static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options); + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; + + class AtomSyncer : public ld::File::AtomHandler { + public: + AtomSyncer(std::vector& a, std::vector&na, + CStringToAtom la, CStringToAtom dla, const OptimizeOptions& options) : + _options(options), _additionalUndefines(a), _newAtoms(na), _llvmAtoms(la), _deadllvmAtoms(dla) { } + virtual void doAtom(const class ld::Atom&); + virtual void doFile(const class ld::File&) { } + + const OptimizeOptions& _options; + std::vector& _additionalUndefines; + std::vector& _newAtoms; + CStringToAtom _llvmAtoms; + CStringToAtom _deadllvmAtoms; + }; + + static std::vector _s_files; +}; + +std::vector Parser::_s_files; + + +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) +{ + switch (architecture) { + case CPU_TYPE_I386: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "i386-"); + case CPU_TYPE_X86_64: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-"); + case CPU_TYPE_ARM: + 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-"); + } + break; + case CPU_TYPE_POWERPC: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "powerpc-"); + } + return false; +} + +const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) +{ + if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { + uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + switch (arch) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_I386: + return "i386"; + 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"; + return "arm"; + } + return "unknown bitcode architecture"; + } + return NULL; +} + +File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) +{ + File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); + _s_files.push_back(f); + if ( logAllFiles ) + printf("%s\n", path); + return f; +} + + +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options) +{ + mach_o::relocatable::ParserOptions objOpts; + objOpts.architecture = options.arch; + objOpts.objSubtypeMustMatch = false; + objOpts.logAllFiles = false; + objOpts.convertUnwindInfo = true; + objOpts.subType = 0; + + // mach-o parsing is done in-memory, but need path for debug notes + const char* path = "/tmp/lto.o"; + time_t modTime = 0; + if ( options.tmpObjectFilePath != NULL ) { + path = options.tmpObjectFilePath; + struct stat statBuffer; + if ( stat(options.tmpObjectFilePath, &statBuffer) == 0 ) + modTime = statBuffer.st_mtime; + } + + ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, nextInputOrdinal, objOpts); + if ( result != NULL ) + return result; + throw "LLVM LTO, file is not of required architecture"; +} + + + +File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ord, cpu_type_t arch) + : ld::relocatable::File(pth,mTime,ord), _architecture(arch), _internalAtom(*this), + _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), + _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), + _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), + _debugInfo(ld::relocatable::File::kDebugInfoNone), _cpuSubType(0) +{ + const bool log = false; + + // create llvm module + _module = ::lto_module_create_from_memory(content, contentLength); + if ( _module == NULL ) + throwf("could not parse object file %s: %s", pth, lto_get_error_message()); + + if ( log ) fprintf(stderr, "bitcode file: %s\n", pth); + + // create atom for each global symbol in module + uint32_t count = ::lto_module_get_num_symbols(_module); + _atomArray = (Atom*)malloc(sizeof(Atom)*count); + for (uint32_t i=0; i < count; ++i) { + const char* name = ::lto_module_get_symbol_name(_module, i); + lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i); + + // LTO doesn't like dtrace symbols + // ignore dtrace static probes for now + // later when codegen is done and a mach-o file is produces the probes will be processed + if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) + continue; + + ld::Atom::Definition def; + ld::Atom::Combine combine = ld::Atom::combineNever; + switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { + case LTO_SYMBOL_DEFINITION_REGULAR: + def = ld::Atom::definitionRegular; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + def = ld::Atom::definitionTentative; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + def = ld::Atom::definitionRegular; + combine = ld::Atom::combineByName; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: + def = ld::Atom::definitionProxy; + break; + default: + throwf("unknown definition kind for symbol %s in bitcode file %s", name, pth); + } + + // make LLVM atoms for definitions and a reference for undefines + if ( def != ld::Atom::definitionProxy ) { + ld::Atom::Scope scope; + switch ( attr & LTO_SYMBOL_SCOPE_MASK) { + case LTO_SYMBOL_SCOPE_INTERNAL: + scope = ld::Atom::scopeTranslationUnit; + break; + case LTO_SYMBOL_SCOPE_HIDDEN: + scope = ld::Atom::scopeLinkageUnit; + break; + case LTO_SYMBOL_SCOPE_DEFAULT: + scope = ld::Atom::scopeGlobal; + break; + default: + throwf("unknown scope for symbol %s in bitcode file %s", name, pth); + } + // only make atoms for non-internal symbols + if ( scope == ld::Atom::scopeTranslationUnit ) + continue; + uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); + // make Atom using placement new operator + new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment); + if ( scope == ld::Atom::scopeLinkageUnit ) + _internalAtom.addReference(name); + if ( log ) fprintf(stderr, "\t0x%08X %s\n", attr, name); + } + else { + // add to list of external references + _internalAtom.addReference(name); + if ( log ) fprintf(stderr, "\t%s (undefined)\n", name); + } + } +} + +File::~File() +{ + if ( _module != NULL ) + ::lto_module_dispose(_module); +} + +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doAtom(_internalAtom); + for(uint32_t i=0; i < _atomArrayCount; ++i) { + handler.doAtom(_atomArray[i]); + } + return true; +} + +InternalAtom::InternalAtom(File& f) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _file(f) +{ +} + +Atom::Atom(File& f, const char* nm, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a) + : ld::Atom(f._section, d, c, s, ld::Atom::typeLTOtemporary, + ld::Atom::symbolTableIn, false, false, false, a), + _file(f), _name(nm), _compiledAtom(NULL) +{ +} + +void Atom::setCompiledAtom(const ld::Atom& atom) +{ + // set delegate so virtual methods go to it + _compiledAtom = &atom; + + //fprintf(stderr, "setting lto atom %p to delegate to mach-o atom %p (%s)\n", this, &atom, atom.name()); + + // update fields in ld::Atom to match newly constructed mach-o atom + (const_cast(this))->setAttributesFromAtom(atom); +} + + + +bool Parser::optimize( const std::vector& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines) +{ + const bool logMustPreserve = false; + const bool logExtraOptions = false; + const bool logBitcodeFiles = false; + const bool logAtomsBeforeSync = false; + + // exit quickly if nothing to do + if ( _s_files.size() == 0 ) + return false; + + // print out LTO version string if -v was used + if ( options.verbose ) + fprintf(stderr, "%s\n", lto_get_version()); + + // create optimizer and add each Reader + lto_code_gen_t generator = ::lto_codegen_create(); + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); + if ( ::lto_codegen_add_module(generator, (*it)->module()) ) + throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); + } + + // add any -mllvm command line options + for (std::vector::const_iterator it=options.llvmOptions->begin(); it != options.llvmOptions->end(); ++it) { + if ( logExtraOptions ) fprintf(stderr, "passing option to llvm: %s\n", *it); + ::lto_codegen_debug_options(generator, *it); + } + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringToAtom llvmAtoms; + bool hasNonllvmAtoms = false; + for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + const ld::Atom* atom = *it; + // only look at references that come from an atom that is not an llvm atom + if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { + if ( (atom->section().type() != ld::Section::typeMachHeader) && (atom->definition() != ld::Atom::definitionProxy) ) { + hasNonllvmAtoms = true; + } + const ld::Atom* target; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + // that reference an llvm atom + if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(fit->u.target->name()); + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + if ( target == NULL ) + throwf("'%s' in %s contains undefined reference", atom->name(), atom->file()->path()); + assert(target != NULL); + if ( target->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(target->name()); + default: + break; + } + } + } + else { + llvmAtoms[atom->name()] = (Atom*)atom; + } + } + // if entry point is in a llvm bitcode file, it must be preserved by LTO + if ( state.entryPoint!= NULL ) { + if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(state.entryPoint->name()); + } + + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions + // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // atom so that the linker can replace it with the mach-o one later. + CStringToAtom deadllvmAtoms; + for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->coalescedAway() && (atom->contentType() == ld::Atom::typeLTOtemporary) ) { + const char* name = atom->name(); + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)atom; + } + } + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + File* file = *it; + for(uint32_t i=0; i < file->_atomArrayCount; ++i) { + Atom* llvmAtom = &file->_atomArray[i]; + if ( llvmAtom->coalescedAway() ) { + const char* name = llvmAtom->name(); + if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) { + if ( logMustPreserve ) + fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)llvmAtom; + } + } + else if ( options.linkerDeadStripping && !llvmAtom->live() ) { + const char* name = llvmAtom->name(); + deadllvmAtoms[name] = (Atom*)llvmAtom; + } + } + } + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( (atom->scope() == ld::Atom::scopeGlobal) ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced by a mach-o atom\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + } + + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( options.relocatable && !hasNonllvmAtoms ) { + if ( ! ::lto_codegen_write_merged_modules(generator, options.outputFilePath) ) { + // HACK, no good way to tell linker we are all done, so just quit + exit(0); + } + warning("could not produce merged bitcode file"); + } + + // set code-gen model + lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + if ( options.mainExecutable ) { + if ( options.staticExecutable ) { + // darwin x86_64 "static" code model is really dynamic code model + if ( options.arch == CPU_TYPE_X86_64 ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_STATIC; + } + else { + if ( options.pie ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + } + } + else { + if ( options.allowTextRelocs ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + } + if ( ::lto_codegen_set_pic_model(generator, model) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // if requested, save off merged bitcode file + if ( options.saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, options.outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + +#if LTO_API_VERSION >= 3 + // find assembler next to linker + char path[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "as"); + struct stat statInfo; + if ( stat(path, &statInfo) == 0 ) + ::lto_codegen_set_assembler_path(generator, path); + } + } +#endif + // run code generator + size_t machOFileLen; + const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + + // if requested, save off temp mach-o file + if ( options.saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, options.outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, options.outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } + + // if needed, save temp mach-o file to specific location + if ( options.tmpObjectFilePath != NULL ) { + int fd = ::open(options.tmpObjectFilePath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + else { + warning("could not write LTO temp file '%s', errno=%d", options.tmpObjectFilePath, errno); + } + } + + // parse generated mach-o file into a MachOReader + ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, options); + + // sync generated mach-o atoms with existing atoms ld knows about + if ( logAtomsBeforeSync ) { + fprintf(stderr, "llvmAtoms:\n"); + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + //Atom* atom = it->second; + fprintf(stderr, "\t%s\n", name); + } + fprintf(stderr, "deadllvmAtoms:\n"); + for (CStringToAtom::iterator it = deadllvmAtoms.begin(); it != deadllvmAtoms.end(); ++it) { + const char* name = it->first; + //Atom* atom = it->second; + fprintf(stderr, "\t%s\n", name); + } + } + AtomSyncer syncer(additionalUndefines, newAtoms, llvmAtoms, deadllvmAtoms, options); + machoFile->forEachAtom(syncer); + + // Remove InternalAtoms from ld + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + (*it)->internalAtom().setCoalescedAway(); + } + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->compiledAtom() == NULL ) { + //fprintf(stderr, "llvm optimized away %p %s\n", li->second, li->second->name()); + li->second->setCoalescedAway(); + } + } + + // notify about file level attributes + handler.doFile(*machoFile); + + // if final mach-o file has debug info, update original bitcode files to match + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + (*it)->setDebugInfo(machoFile->debugInfo(), machoFile->path(), + machoFile->modificationTime(), machoFile->cpuSubType()); + } + + return true; +} + + +void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) +{ + // update proxy atoms to point to real atoms and find new atoms + const char* name = machoAtom.name(); + if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { + CStringToAtom::iterator pos = _llvmAtoms.find(name); + if ( pos != _llvmAtoms.end() ) { + // turn Atom into a proxy for this mach-o atom + pos->second->setCompiledAtom(machoAtom); + } + else { + // an atom of this name was not in the allAtoms list the linker gave us + if ( _deadllvmAtoms.find(name) != _deadllvmAtoms.end() ) { + // this corresponding to an atom that the linker coalesced away or marked not-live + if ( _options.linkerDeadStripping ) { + // llvm seems to want this atom and -dead_strip is enabled, so it will be deleted if not needed, so add back + Atom* llvmAtom = _deadllvmAtoms[name]; + llvmAtom->setCompiledAtom(machoAtom); + _newAtoms.push_back(&machoAtom); + } + else { + // Don't pass it back as a new atom + } + } + else + { + // this is something new that lto conjured up, tell ld its new + _newAtoms.push_back(&machoAtom); + } + } + } + else { + // ld only knew about non-static atoms, so this one must be new + _newAtoms.push_back(&machoAtom); + } + + // adjust fixups to go through proxy atoms + //fprintf(stderr, "adjusting fixups in atom: %s\n", machoAtom.name()); + for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + break; + case ld::Fixup::bindingByNameUnbound: + // don't know if this target has been seen by linker before or if it is new + // be conservative and tell linker it is new + _additionalUndefines.push_back(fit->u.name); + //fprintf(stderr, " by name ref to: %s\n", fit->u.name); + break; + case ld::Fixup::bindingDirectlyBound: + // If mach-o atom is referencing another mach-o atom then + // reference is not going through Atom proxy. Fix it here to ensure that all + // llvm symbol references always go through Atom proxy. + if ( fit->u.target->scope() != ld::Atom::scopeTranslationUnit ) { + const char* targetName = fit->u.target->name(); + CStringToAtom::iterator pos = _llvmAtoms.find(targetName); + if ( pos != _llvmAtoms.end() ) { + fit->u.target = pos->second; + } + else { + if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) { + // target was coalesed away and replace by mach-o atom from a non llvm .o file + fit->binding = ld::Fixup::bindingByNameUnbound; + fit->u.name = targetName; + } + } + } + //fprintf(stderr, " direct ref to: %s (scope=%d)\n", fit->u.target->name(), fit->u.target->scope()); + break; + case ld::Fixup::bindingByContentBound: + //fprintf(stderr, " direct by content to: %s\n", fit->u.target->name()); + break; + case ld::Fixup::bindingsIndirectlyBound: + assert(0 && "indirect binding found in initial mach-o file?"); + //fprintf(stderr, " indirect by content to: %u\n", fit->u.bindingIndex); + break; + } + } + +} + + +// +// Used by archive reader to see if member is an llvm bitcode file +// +bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) +{ + return Parser::validFile(fileContent, fileLength, architecture, subarch); +} + + +// +// main function used by linker to instantiate ld::Files +// +ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) +{ + if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); + else + return NULL; +} + +// +// used by "ld -v" to report version of libLTO.dylib being used +// +const char* version() +{ + return ::lto_get_version(); +} + + +// +// used by ld for error reporting +// +bool libLTOisLoaded() +{ + return (::lto_get_version() != NULL); +} + +// +// used by ld for error reporting +// +const char* archName(const uint8_t* fileContent, uint64_t fileLength) +{ + return Parser::fileKind(fileContent, fileLength); +} + +// +// used by ld for doing link time optimization +// +bool optimize( const std::vector& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines) +{ + return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines); +} + + + +}; // namespace lto + + +#endif + diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h new file mode 100644 index 0000000..dbb82a5 --- /dev/null +++ b/ld64/src/ld/parsers/lto_file.h @@ -0,0 +1,73 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ + +#ifndef __LTO_FILE_H__ +#define __LTO_FILE_H__ + +#include "ld.hpp" + +namespace lto { + +extern const char* version(); + +extern bool libLTOisLoaded(); + +extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); + +extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); + +extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + +struct OptimizeOptions { + const char* outputFilePath; + const char* tmpObjectFilePath; + bool allGlobalsAReDeadStripRoots; + bool verbose; + bool saveTemps; + bool pie; + bool mainExecutable; + bool staticExecutable; + bool relocatable; + bool allowTextRelocs; + bool linkerDeadStripping; + cpu_type_t arch; + const std::vector* llvmOptions; +}; + +extern bool optimize( const std::vector& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines); + +} // namespace lto + + +#endif // __LTO_FILE_H__ + + diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp new file mode 100644 index 0000000..40dad13 --- /dev/null +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -0,0 +1,996 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" +#include "macho_dylib_file.h" + + +namespace mach_o { +namespace dylib { + + +// forward reference +template class File; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template +class ExportAtom : public ld::Atom +{ +public: + ExportAtom(const File& f, const char* nm, bool weakDef, + bool tlv, typename A::P::uint_t address) + : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, + (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), + ld::Atom::scopeLinkageUnit, + (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(nm), _address(address) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return _address; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + virtual ~ExportAtom() {} + + const File& _file; + const char* _name; + pint_t _address; +}; + + + +// +// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace +// the imports of all flat dylibs are checked +// +template +class ImportAtom : public ld::Atom +{ +public: + ImportAtom(File& f, std::vector& imports); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + virtual uint64_t size() const { return 0; } + 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 &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } + +protected: + typedef typename A::P P; + + virtual ~ImportAtom() {} + + + File& _file; + mutable std::vector _undefs; +}; + +template +ImportAtom::ImportAtom(File& f, std::vector& imports) +: ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), _file(f) +{ + for(std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { + _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(*it))); + } +} + + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class File : public ld::dylib::File +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + File(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, uint32_t ordinal, bool linkingFlatNamespace, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, + bool logAllFiles); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } + + // overrides of ld::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); + virtual bool providedExportAtom() const { return _providedAtom; } + virtual const char* parentUmbrella() const { return _parentUmbrella; } + virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : NULL; } + virtual bool hasWeakExternals() const { return _hasWeakExports; } + virtual bool deadStrippable() const { return _deadStrippable; } + virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } + virtual bool hasWeakDefinition(const char* name) const; + + +protected: + + struct ReExportChain { ReExportChain* prev; File* file; }; + + void assertNoReExportCycles(ReExportChain*); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + friend class ExportAtom; + friend class ImportAtom; + + class CStringEquals + { + 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; }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAtomMap; + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + + struct Dependent { const char* path; File* dylib; bool reExport; }; + + bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; + bool isPublicLocation(const char* pth); + void addSymbol(const char* name, bool weak, bool tlv, pint_t address); + void addDyldFastStub(); + void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent); + void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, const char* strings, + const uint8_t* fileContent); + static const char* objCInfoSegmentName(); + static const char* objCInfoSectionName(); + + const ld::MacVersionMin _macVersionMin; + const ld::IPhoneVersionMin _iPhoneVersionMin; + bool _linkingFlat; + bool _implicitlyLinkPublicDylibs; + ld::File::ObjcConstraint _objcContraint; + ld::Section _importProxySection; + ld::Section _flatDummySection; + std::vector _dependentDylibs; + std::vector _allowableClients; + mutable NameToAtomMap _atoms; + NameSet _ignoreExports; + const char* _parentUmbrella; + ImportAtom* _importAtom; + bool _noRexports; + bool _hasWeakExports; + bool _deadStrippable; + bool _hasPublicInstallName; + mutable bool _providedAtom; + bool _explictReExportFound; + + static bool _s_logHashtable; +}; + +template +bool File::_s_logHashtable = false; + +template <> const char* File::objCInfoSegmentName() { return "__DATA"; } +template <> const char* File::objCInfoSegmentName() { return "__DATA"; } +template const char* File::objCInfoSegmentName() { return "__OBJC"; } + +template <> const char* File::objCInfoSectionName() { return "__objc_imageinfo"; } +template <> const char* File::objCInfoSectionName() { return "__objc_imageinfo"; } +template const char* File::objCInfoSectionName() { return "__image_info"; } + +template +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord, + bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, bool logAllFiles) + : ld::dylib::File(strdup(pth), mTime, ord), + _macVersionMin(macMin), _iPhoneVersionMin(iPhoneMin), + _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), + _objcContraint(ld::File::objcConstraintNone), + _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), + _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), + _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false), + _deadStrippable(false), _hasPublicInstallName(false), + _providedAtom(false), _explictReExportFound(false) +{ + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

) + header->sizeofcmds()); + + // write out path for -t option + if ( logAllFiles ) + printf("%s\n", pth); + + // a "blank" stub has zero load commands + if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { + // no further processing needed + munmap((caddr_t)fileContent, fileLength); + return; + } + + + // optimize the case where we know there is no reason to look at indirect dylibs + _noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) + || (header->filetype() == MH_BUNDLE) + || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader + _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); + _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + + // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format + const macho_dysymtab_command

* dynamicInfo = NULL; + const macho_dyld_info_command

* dyldInfo = NULL; + const macho_nlist

* symbolTable = NULL; + const char* strings = NULL; + bool compressedLinkEdit = false; + uint32_t dependentLibCount = 0; + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + macho_dylib_command

* dylibID; + const macho_symtab_command

* symtab; + switch (cmd->cmd()) { + case LC_SYMTAB: + symtab = (macho_symtab_command

*)cmd; + symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); + strings = (char*)header + symtab->stroff(); + break; + case LC_DYSYMTAB: + dynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command

*)cmd; + compressedLinkEdit = true; + break; + case LC_ID_DYLIB: + dylibID = (macho_dylib_command

*)cmd; + _dylibInstallPath = strdup(dylibID->name()); + _dylibTimeStamp = dylibID->timestamp(); + _dylibCurrentVersion = dylibID->current_version(); + _dylibCompatibilityVersion = dylibID->compatibility_version(); + _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + ++dependentLibCount; + break; + case LC_REEXPORT_DYLIB: + _explictReExportFound = true; + ++dependentLibCount; + break; + case LC_SUB_FRAMEWORK: + _parentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + break; + case LC_SUB_CLIENT: + _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); + break; + case macho_segment_command

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

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strncmp(sect->sectname(), objCInfoSectionName(), strlen(objCInfoSectionName())) == 0 ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + _objcContraint = ld::File::objcConstraintGC; + else if ( (flags & 2) == 2 ) + _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + else + _objcContraint = ld::File::objcConstraintRetainRelease; + } + else if ( sect->size() > 0 ) { + warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); + } + } + } + } + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); + } + + // figure out if we need to examine dependent dylibs + // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted + bool processDependentLibraries = true; + if ( compressedLinkEdit && _noRexports && !linkingFlatNamespace) + processDependentLibraries = false; + + if ( processDependentLibraries ) { + // pass 2 builds list of all dependent libraries + _dependentDylibs.reserve(dependentLibCount); + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + // with new linkedit format only care about LC_REEXPORT_DYLIB + if ( compressedLinkEdit && !linkingFlatNamespace ) + break; + case LC_REEXPORT_DYLIB: + Dependent entry; + entry.path = strdup(((macho_dylib_command

*)cmd)->name()); + entry.dylib = NULL; + entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + _dependentDylibs.push_back(entry); + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + // verify MH_NO_REEXPORTED_DYLIBS bit was correct + if ( compressedLinkEdit && !linkingFlatNamespace ) { + assert(_dependentDylibs.size() != 0); + } + // pass 3 add re-export info + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + const char* frameworkLeafName; + const char* dylibBaseName; + switch (cmd->cmd()) { + case LC_SUB_UMBRELLA: + frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + break; + case LC_SUB_LIBRARY: + dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + break; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + } + } + + // validate minimal load commands + if ( (_dylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", pth); + if ( dyldInfo == NULL ) { + if ( symbolTable == NULL ) + throw "binary missing LC_SYMTAB load command"; + if ( dynamicInfo == NULL ) + throw "binary missing LC_DYSYMTAB load command"; + } + + // if linking flat and this is a flat dylib, create one atom that references all imported symbols + if ( linkingFlatNamespace && linkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { + std::vector importNames; + importNames.reserve(dynamicInfo->nundefsym()); + const macho_nlist

* start = &symbolTable[dynamicInfo->iundefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nundefsym()]; + for (const macho_nlist

* sym=start; sym < end; ++sym) { + importNames.push_back(&strings[sym->n_strx()]); + } + _importAtom = new ImportAtom(*this, importNames); + } + + // build hash table + if ( dyldInfo != NULL ) + buildExportHashTableFromExportInfo(dyldInfo, fileContent); + else + buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + + +template +void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, const char* strings, + const uint8_t* fileContent) +{ + if ( dynamicInfo->tocoff() == 0 ) { + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); + const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; + const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; + _atoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + for (const macho_nlist

* sym=start; sym < end; ++sym) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); + } + } + else { + int32_t count = dynamicInfo->ntoc(); + _atoms.resize(count); // set initial bucket count + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); + for (int32_t i = 0; i < count; ++i) { + const uint32_t index = E::get32(toc[i].symbol_index); + const macho_nlist

* sym = &symbolTable[index]; + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); + } + } + + // special case old libSystem + if ( (_dylibInstallPath != NULL) && (strcmp(_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) + addDyldFastStub(); +} + + +template +void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent) +{ + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); + if ( dyldInfo->export_size() > 0 ) { + const uint8_t* start = fileContent + dyldInfo->export_off(); + const uint8_t* end = &start[dyldInfo->export_size()]; + std::vector list; + parseTrie(start, end, list); + for (std::vector::iterator it=list.begin(); it != list.end(); ++it) + this->addSymbol(it->name, + it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, + (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, + it->address); + } +} + + +template <> +void File::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false, false, 0); +} + +template <> +void File::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false, false, 0); +} + +template +void File::addDyldFastStub() +{ + // do nothing +} + +template +void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) +{ + if ( weakDef ) { + assert(_hasWeakExports); + } + //fprintf(stderr, "addSymbol() %s\n", name); + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != NULL ) { + char curOSVers[16]; + 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 { + assert(0 && "targeting neither macosx nor iphoneos"); + } + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( _s_logHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + _ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef, false, 0); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( _ignoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = weakDef; + bucket.tlv = tlv; + bucket.address = address; + if ( _s_logHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + _atoms[strdup(name)] = bucket; + } +} + + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + // if doing flatnamespace and need all this dylib's imports resolve + // add atom which references alls undefines in this dylib + if ( _importAtom != NULL ) { + handler.doAtom(*_importAtom); + return true; + } + return false; +} + +template +bool File::hasWeakDefinition(const char* name) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + typename NameToAtomMap::const_iterator pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + return pos->second.weak; + } + else { + // look in children that I re-export + for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + if ( it->reExport ) { + //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 false; +} + +template +bool File::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const +{ + // check myself + typename NameToAtomMap::iterator pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + *weakDef = pos->second.weak; + *tlv = pos->second.tlv; + *defAddress = pos->second.address; + return true; + } + + // check dylibs I re-export + for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + if ( it->reExport && !it->dylib->implicitlyLinked() ) { + if ( it->dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) + return true; + } + } + + return false; +} + + +template +bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + + AtomAndWeak bucket; + if ( this->containsOrReExports(name, &bucket.weak, &bucket.tlv, &bucket.address) ) { + bucket.atom = new ExportAtom(*this, name, bucket.weak, bucket.tlv, bucket.address); + _atoms[name] = bucket; + _providedAtom = true; + if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); + // call handler with new export atom + handler.doAtom(*bucket.atom); + return true; + } + + return false; +} + + + +template +bool File::isPublicLocation(const char* pth) +{ + // -no_implicit_dylibs disables this optimization + if ( ! _implicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&pth[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &pth[27]; + if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +{ + const static bool log = false; + if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); + if ( _linkingFlat ) { + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + it->dylib = (File*)handler->findDylib(it->path, this->path()); + } + } + else if ( _noRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + if ( it->reExport ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + it->dylib = (File*)handler->findDylib(it->path, this->path()); + if ( it->dylib->hasPublicInstallName() ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); + it->dylib->setImplicitlyLinked(); + } + else if ( it->dylib->explicitlyLinked() || it->dylib->implicitlyLinked() ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } + else { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + else { + // add all child's symbols to me + if ( log ) fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + else if ( !_explictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + it->dylib = (File*)handler->findDylib(it->path, this->path()); + const char* parentUmbrellaName = it->dylib->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->path(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + it->reExport = true; + if ( log ) fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = NULL; + chain.file = this; + this->assertNoReExportCycles(&chain); +} + +template +void File::assertNoReExportCycles(ReExportChain* prev) +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.file = this; + for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + if ( it->reExport ) { + ld::File* child = it->dylib; + // check child is not already in chain + for (ReExportChain* p = prev; p != NULL; p = p->prev) { + if ( p->file == child ) { + throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); + } + } + if ( it->dylib != NULL ) + it->dylib->assertNoReExportCycles(&chain); + } + } +} + + +struct ParserOptions { + bool linkingFlat; + bool linkingMain; + bool addImplictDylibs; + ld::MacVersionMin macOSMin; + ld::IPhoneVersionMin iphoneOSMin; +}; + + +template +class Parser +{ +public: + typedef typename A::P P; + + 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) { + return new File(fileContent, fileLength, path, mTime, + ordinal, opts.flatNamespace(), + opts.linkingMainExecutable(), + opts.implicitlyLinkIndirectPublicDylibs(), + opts.macosxVersionMin(), + opts.iphoneOSVersionMin(), + opts.logAllFiles()); + } + +}; + + + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + + + +// +// 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) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + + +}; // namespace dylib +}; // namespace mach_o + + diff --git a/ld64/src/ld/parsers/macho_dylib_file.h b/ld64/src/ld/parsers/macho_dylib_file.h new file mode 100644 index 0000000..5c858e2 --- /dev/null +++ b/ld64/src/ld/parsers/macho_dylib_file.h @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __MACHO_DYLIB_FILE_H__ +#define __MACHO_DYLIB_FILE_H__ + +#include "ld.hpp" +#include "Options.h" + +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); + +} // namespace dylib +} // namespace mach_o + + +#endif // __MACHO_DYLIB_FILE_H__ diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp new file mode 100644 index 0000000..25c1965 --- /dev/null +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -0,0 +1,6552 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +#include "dwarf2.h" +#include "debugline.h" + +#include "Architectures.hpp" +#include "ld.hpp" +#include "macho_relocatable_file.h" + + + +extern void throwf(const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); +extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); + +namespace mach_o { +namespace relocatable { + + +// forward reference +template class Parser; +template class Atom; +template class Section; +template class CFISection; + +template +class File : public ld::relocatable::File +{ +public: + File(const char* p, time_t mTime, const uint8_t* content, uint32_t ord) : + ld::relocatable::File(p,mTime,ord), _fileContent(content), + _sectionsArray(NULL), _atomsArray(NULL), + _sectionsArrayCount(0), _atomsArrayCount(0), + _debugInfoKind(ld::relocatable::File::kDebugInfoNone), + _dwarfTranslationUnitDir(NULL), _dwarfTranslationUnitFile(NULL), + _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), + _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), + _objConstraint(ld::File::objcConstraintNone), + _cpuSubType(0), + _ojcReplacmentClass(false), _canScatterAtoms(false) {} + virtual ~File(); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const + { return false; } + + // overrides of ld::relocatable::File + virtual bool objcReplacementClasses() const { return _ojcReplacmentClass; } + virtual ObjcConstraint objCConstraint() const { return _objConstraint; } + virtual uint32_t cpuSubType() const { return _cpuSubType; } + virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } + virtual const std::vector* stabs() const { return &_stabs; } + virtual bool canScatterAtoms() const { return _canScatterAtoms; } + bool translationUnitSource(const char** dir, const char** name) const; + + const uint8_t* fileContent() { return _fileContent; } +private: + friend class Atom; + friend class Section; + friend class Parser; + friend class CFISection::OAS; + + typedef typename A::P P; + + const uint8_t* _fileContent; + Section** _sectionsArray; + uint8_t* _atomsArray; + uint32_t _sectionsArrayCount; + uint32_t _atomsArrayCount; + std::vector _fixups; + std::vector _unwindInfos; + std::vector _lineInfos; + std::vector_stabs; + ld::relocatable::File::DebugInfoKind _debugInfoKind; + const char* _dwarfTranslationUnitDir; + const char* _dwarfTranslationUnitFile; + const macho_section

* _dwarfDebugInfoSect; + const macho_section

* _dwarfDebugAbbrevSect; + const macho_section

* _dwarfDebugLineSect; + const macho_section

* _dwarfDebugStringSect; + ld::File::ObjcConstraint _objConstraint; + uint32_t _cpuSubType; + bool _ojcReplacmentClass; + bool _canScatterAtoms; +}; + + +template +class Section : public ld::Section +{ +public: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual ~Section() { } + class File& file() const { return _file; } + const macho_section

* machoSection() const { return _machOSection; } + uint32_t sectionNum(class Parser&) const; + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr); + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeUnclassified; } + virtual bool dontDeadStrip() { return (this->_machOSection->flags() & S_ATTR_NO_DEAD_STRIP); } + virtual Atom* findAtomByAddress(pint_t addr) { return this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); } + 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; + 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&); + 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; } + +protected: + Section(File& f, const macho_section* s) + : ld::Section(makeSegmentName(s), makeSectionName(s), sectionType(s)), + _file(f), _machOSection(s), _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } + Section(File& f, const char* segName, const char* sectName, ld::Section::Type t, bool hidden=false) + : ld::Section(segName, sectName, t, hidden), _file(f), _machOSection(NULL), + _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } + + + bool addRelocFixup_powerpc(class Parser& parser,const macho_relocation_info* reloc); + 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); + static ld::Section::Type sectionType(const macho_section* s); + + File& _file; + const macho_section

* _machOSection; + class Atom* _beginAtoms; + class Atom* _endAtoms; + bool _hasAliases; +}; + + +template +class CFISection : public Section +{ +public: + CFISection(Parser& parser, File& f, const macho_section* s) + : Section(f, s) { } + 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 bool addFollowOnFixups() const { return false; } + + + /// + /// ObjectFileAddressSpace is used as a template parameter to UnwindCursor for parsing + /// dwarf CFI information in an object file. + /// + class OAS + { + public: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t sint_t; + + OAS(CFISection& ehFrameSection, const uint8_t* ehFrameBuffer) : + _ehFrameSection(ehFrameSection), + _ehFrameContent(ehFrameBuffer), + _ehFrameStartAddr(ehFrameSection.machoSection()->addr()), + _ehFrameEndAddr(ehFrameSection.machoSection()->addr()+ehFrameSection.machoSection()->size()) {} + + uint8_t get8(pint_t addr) { return *((uint8_t*)mappedAddress(addr)); } + uint16_t get16(pint_t addr) { return E::get16(*((uint16_t*)mappedAddress(addr))); } + uint32_t get32(pint_t addr) { return E::get32(*((uint32_t*)mappedAddress(addr))); } + uint64_t get64(pint_t addr) { return E::get64(*((uint64_t*)mappedAddress(addr))); } + pint_t getP(pint_t addr) { return P::getP(*((pint_t*)mappedAddress(addr))); } + uint64_t getULEB128(pint_t& addr, pint_t end); + int64_t getSLEB128(pint_t& addr, pint_t end); + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + private: + const void* mappedAddress(pint_t addr); + + CFISection& _ehFrameSection; + const uint8_t* _ehFrameContent; + pint_t _ehFrameStartAddr; + pint_t _ehFrameEndAddr; + }; + + + typedef typename A::P::uint_t pint_t; + typedef libunwind::CFI_Atom_Info CFI_Atom_Info; + + void cfiParse(class Parser& parser, uint8_t* buffer, CFI_Atom_Info cfiArray[], uint32_t cfiCount); + bool needsRelocating(); + + static bool bigEndian(); +private: + void addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo); + static void warnFunc(void* ref, uint64_t funcAddr, const char* msg); +}; + + +template +class TentativeDefinitionSection : public Section +{ +public: + TentativeDefinitionSection(Parser& parser, File& f) + : Section(f, "__DATA", "__comm/tent", ld::Section::typeTentativeDefs) {} + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeZeroFill; } + 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&); + 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&) {} +private: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; +}; + + +template +class AbsoluteSymbolSection : public Section +{ +public: + AbsoluteSymbolSection(Parser& parser, File& f) + : Section(f, "__DATA", "__abs", ld::Section::typeAbsoluteSymbols, true) {} + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeUnclassified; } + virtual bool dontDeadStrip() { return false; } + virtual ld::Atom::Alignment alignmentForAddress(typename A::P::uint_t addr) { return ld::Atom::Alignment(0); } + 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&); + 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 Atom* findAbsAtomForValue(typename A::P::uint_t); + +private: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; +}; + + +template +class SymboledSection : public Section +{ +public: + SymboledSection(Parser& parser, File& f, const macho_section* s); + 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&); + virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&); +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + ld::Atom::ContentType _type; +}; + + +template +class TLVDefsSection : public SymboledSection +{ +public: + TLVDefsSection(Parser& parser, File& f, const macho_section* s) : + SymboledSection(parser, f, s) { } + +private: + +}; + + +template +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&); +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual bool addFollowOnFixups() const { return false; } + virtual const char* unlabeledAtomName(Parser& parser, pint_t addr) = 0; + virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableNotIn; } + virtual pint_t elementSizeAtAddress(pint_t addr) = 0; + virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr) { return ld::Atom::scopeLinkageUnit; } + virtual bool useElementAt(Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, pint_t addr) = 0; + virtual ld::Atom::Definition definition() { return ld::Atom::definitionRegular; } + virtual ld::Atom::Combine combine(Parser& parser, pint_t addr) = 0; + virtual bool ignoreLabel(const char* label) { return (label[0] == 'L'); } +}; + +template +class FixedSizeSection : public ImplicitSizeSection +{ +public: + FixedSizeSection(Parser& parser, File& f, const macho_section* s) + : ImplicitSizeSection(parser, f, s) { } +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual bool useElementAt(Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, pint_t addr) + { return true; } +}; + + +template +class Literal4Section : public FixedSizeSection +{ +public: + Literal4Section(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(2); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "4-byte-literal"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 4; } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + +template +class Literal8Section : public FixedSizeSection +{ +public: + Literal8Section(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(3); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "8-byte-literal"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 8; } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + +template +class Literal16Section : public FixedSizeSection +{ +public: + Literal16Section(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(4); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "16-byte-literal"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 16; } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + + +template +class NonLazyPointerSection : public FixedSizeSection +{ +public: + NonLazyPointerSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +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 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"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr); + virtual ld::Atom::Combine combine(Parser&, pint_t); + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + +private: + static const char* targetName(const class Atom* atom, const ld::IndirectBindingTable& ind); + static ld::Fixup::Kind fixupKind(); +}; + + +template +class CFStringSection : public FixedSizeSection +{ +public: + CFStringSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "CFString"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 4*sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +private: + enum ContentType { contentUTF8, contentUTF16, contentUnknown }; + static const uint8_t* targetContent(const class Atom* atom, const ld::IndirectBindingTable& ind, + ContentType* ct, unsigned int* count); +}; + + +template +class ObjC1ClassSection : public FixedSizeSection +{ +public: + ObjC1ClassSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual ld::Atom::Scope scopeAtAddress(Parser& , pint_t ) { return ld::Atom::scopeGlobal; } + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(2); } + virtual const char* unlabeledAtomName(Parser&, pint_t); + virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableIn; } + virtual pint_t elementSizeAtAddress(pint_t addr); + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineNever; } + virtual bool ignoreLabel(const char* label) { return true; } + 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; } + virtual bool addRelocFixup(class Parser& parser, const macho_relocation_info

*); +}; + + +template +class ObjC2ClassRefsSection : public FixedSizeSection +{ +public: + ObjC2ClassRefsSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "objc-class-ref"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +private: + const char* targetClassName(const class Atom* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template +class ObjC2CategoryListSection : public FixedSizeSection +{ +public: + ObjC2CategoryListSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr) { return ld::Atom::scopeTranslationUnit; } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "objc-cat-list"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineNever; } + virtual bool ignoreLabel(const char* label) { return true; } +private: + const char* targetClassName(const class Atom* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template +class PointerToCStringSection : public FixedSizeSection +{ +public: + PointerToCStringSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "pointer-to-literal-cstring"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + virtual const char* targetCString(const class Atom* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template +class Objc1ClassReferences : public PointerToCStringSection +{ +public: + Objc1ClassReferences(Parser& parser, File& f, const macho_section* s) + : PointerToCStringSection(parser, f, s) {} + + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "pointer-to-literal-objc-class-name"; } + virtual bool addRelocFixup(class Parser& parser, const macho_relocation_info

*); + virtual const char* targetCString(const class Atom* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template +class CStringSection : public ImplicitSizeSection +{ +public: + CStringSection(Parser& parser, File& f, const macho_section* s) + : ImplicitSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCString; } + virtual Atom* findAtomByAddress(pint_t addr); + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "cstring"; } + virtual pint_t elementSizeAtAddress(pint_t addr); + virtual bool useElementAt(Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, pint_t addr); + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + +}; + + +template +class UTF16StringSection : public SymboledSection +{ +public: + UTF16StringSection(Parser& parser, File& f, const macho_section* s) + : SymboledSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + + +// +// Atoms in mach-o files +// +template +class Atom : public ld::Atom +{ +public: + // overrides of ld::Atom + virtual ld::File* file() const { return §().file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return sect().file().translationUnitSource(dir, nm); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return _objAddress; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual const uint8_t* rawContentPointer() const { return contentPointer(); } + virtual unsigned long contentHash(const ld::IndirectBindingTable& ind) const + { if ( _hash == 0 ) _hash = sect().contentHash(this, ind); return _hash; } + virtual bool canCoalesceWith(const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const + { return sect().canCoalesceWith(this, rhs, ind); } + virtual ld::Fixup::iterator fixupsBegin() const { return &machofile()._fixups[_fixupsStartIndex]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &machofile()._fixups[_fixupsStartIndex+_fixupsCount]; } + virtual ld::Atom::UnwindInfo::iterator beginUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex]; } + virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex+_unwindInfoCount]; } + virtual ld::Atom::LineInfo::iterator beginLineInfo() const{ return &machofile()._lineInfos[_lineInfoStartIndex]; } + virtual ld::Atom::LineInfo::iterator endLineInfo() const { return &machofile()._lineInfos[_lineInfoStartIndex+_lineInfoCount]; } + +private: + + enum { kFixupStartIndexBits = 32, + kLineInfoStartIndexBits = 32, + kUnwindInfoStartIndexBits = 24, + kFixupCountBits = 24, + kLineInfoCountBits = 12, + kUnwindInfoCountBits = 4 + }; // must sum to 128 + +public: + // methods for all atoms from mach-o object file + Section& sect() const { return (Section&)section(); } + 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 setLineInfoRange(uint32_t s, uint32_t c); + bool roomForMoreLineInfoCount() { return (_lineInfoCount < ((1<& sct, const char* nm, pint_t addr, uint64_t sz, + ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Scope s, + ld::Atom::ContentType ct, ld::Atom::SymbolTableInclusion i, + bool dds, bool thumb, bool al, ld::Atom::Alignment a) + : ld::Atom((ld::Section&)sct, d, c, s, ct, i, dds, thumb, al, a), + _size(sz), _objAddress(addr), _name(nm), _hash(0), + _fixupsStartIndex(0), _lineInfoStartIndex(0), + _unwindInfoStartIndex(0), _fixupsCount(0), + _lineInfoCount(0), _unwindInfoCount(0) { } + // construct via symbol table entry + Atom(Section& sct, Parser& parser, const macho_nlist

& sym, + uint64_t sz, bool alias=false) + : ld::Atom((ld::Section&)sct, parser.definitionFromSymbol(sym), + parser.combineFromSymbol(sym), parser.scopeFromSymbol(sym), + parser.resolverFromSymbol(sym) ? ld::Atom::typeResolver : sct.contentType(), + parser.inclusionFromSymbol(sym), + parser.dontDeadStripFromSymbol(sym) || sct.dontDeadStrip(), + parser.isThumbFromSymbol(sym), alias, + sct.alignmentForAddress(sym.n_value())), + _size(sz), _objAddress(sym.n_value()), + _name(parser.nameFromSymbol(sym)), _hash(0), + _fixupsStartIndex(0), _lineInfoStartIndex(0), + _unwindInfoStartIndex(0), _fixupsCount(0), + _lineInfoCount(0), _unwindInfoCount(0) { + // support auto-hidden weak symbols + if ( _scope == ld::Atom::scopeGlobal && + (sym.n_desc() & (N_WEAK_DEF|N_WEAK_REF)) == (N_WEAK_DEF|N_WEAK_REF) ) + this->setAutoHide(); + this->verifyAlignment(); + } + +private: + friend class Parser; + friend class Section; + friend class CStringSection; + friend class AbsoluteSymbolSection; + + pint_t _size; + pint_t _objAddress; + const char* _name; + mutable unsigned long _hash; + + uint64_t _fixupsStartIndex : kFixupStartIndexBits, + _lineInfoStartIndex : kLineInfoStartIndexBits, + _unwindInfoStartIndex : kUnwindInfoStartIndexBits, + _fixupsCount : kFixupCountBits, + _lineInfoCount : kLineInfoCountBits, + _unwindInfoCount : kUnwindInfoCountBits; + +}; + + + +template +void Atom::setFixupsRange(uint32_t startIndex, uint32_t count) +{ + if ( count >= (1 << kFixupCountBits) ) + throwf("too many fixups in function %s", this->name()); + if ( startIndex >= (1 << kFixupStartIndexBits) ) + throwf("too many fixups in file"); + assert(((startIndex+count) <= sect().file()._fixups.size()) && "fixup index out of range"); + _fixupsStartIndex = startIndex; + _fixupsCount = count; +} + +template +void Atom::setUnwindInfoRange(uint32_t startIndex, uint32_t count) +{ + if ( count >= (1 << kUnwindInfoCountBits) ) + throwf("too many compact unwind infos in function %s", this->name()); + if ( startIndex >= (1 << kUnwindInfoStartIndexBits) ) + throwf("too many compact unwind infos (%d) in file", startIndex); + assert((startIndex+count) <= sect().file()._unwindInfos.size() && "unwindinfo index out of range"); + _unwindInfoStartIndex = startIndex; + _unwindInfoCount = count; +} + +template +void Atom::setLineInfoRange(uint32_t startIndex, uint32_t count) +{ + assert((count < (1 << kLineInfoCountBits)) && "too many line infos"); + assert((startIndex+count) < sect().file()._lineInfos.size() && "line info index out of range"); + _lineInfoStartIndex = startIndex; + _lineInfoCount = count; +} + +template +const uint8_t* Atom::contentPointer() const +{ + const macho_section

* sct = this->sect().machoSection(); + uint32_t fileOffset = sct->offset() - sct->addr() + this->_objAddress; + return this->sect().file().fileContent()+fileOffset; +} + + +template +void Atom::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( this->contentType() == ld::Atom::typeZeroFill ) { + bzero(buffer, _size); + } + else if ( _size != 0 ) { + memcpy(buffer, this->contentPointer(), _size); + } +} + +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()); + } +} + +template +void Atom::verifyAlignment() const +{ +} + + +template +class Parser +{ +public: + static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, + cpu_subtype_t subtype=0); + static const char* fileKind(const uint8_t* fileContent); + static bool hasObjC2Categories(const uint8_t* fileContent); + static ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + const ParserOptions& opts) { + Parser p(fileContent, fileLength, path, modTime, + ordinal, opts.convertUnwindInfo); + return p.parse(opts); + } + + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct SourceLocation { + SourceLocation() {} + SourceLocation(Atom* a, uint32_t o) : atom(a), offsetInAtom(o) {} + Atom* atom; + uint32_t offsetInAtom; + }; + + struct TargetDesc { + Atom* atom; + const char* name; // only used if targetAtom is NULL + int64_t addend; + bool weakImport; // only used if targetAtom is NULL + }; + + struct FixupInAtom { + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, Atom* target) : + fixup(src.offsetInAtom, c, k, target), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, Atom* target) : + fixup(src.offsetInAtom, c, k, b, target), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, bool wi, const char* name) : + fixup(src.offsetInAtom, c, k, wi, name), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, const char* name) : + fixup(src.offsetInAtom, c, k, b, name), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, uint64_t addend) : + fixup(src.offsetInAtom, c, k, addend), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k) : + fixup(src.offsetInAtom, c, k, (uint64_t)0), atom(src.atom) { src.atom->incrementFixupCount(); } + + ld::Fixup fixup; + Atom* atom; + }; + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, Atom* target) { + _allFixups.push_back(FixupInAtom(src, c, k, target)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, Atom* target) { + _allFixups.push_back(FixupInAtom(src, c, k, b, target)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, bool wi, const char* name) { + _allFixups.push_back(FixupInAtom(src, c, k, wi, name)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, const char* name) { + _allFixups.push_back(FixupInAtom(src, c, k, b, name)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, uint64_t addend) { + _allFixups.push_back(FixupInAtom(src, c, k, addend)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k) { + _allFixups.push_back(FixupInAtom(src, c, k)); + } + + + uint32_t symbolCount() { return _symbolCount; } + uint32_t indirectSymbol(uint32_t indirectIndex); + const macho_nlist

& symbolFromIndex(uint32_t index); + const char* nameFromSymbol(const macho_nlist

& sym); + ld::Atom::Scope scopeFromSymbol(const macho_nlist

& sym); + static ld::Atom::Definition definitionFromSymbol(const macho_nlist

& sym); + static ld::Atom::Combine combineFromSymbol(const macho_nlist

& sym); + ld::Atom::SymbolTableInclusion inclusionFromSymbol(const macho_nlist

& sym); + static bool dontDeadStripFromSymbol(const macho_nlist

& sym); + static bool isThumbFromSymbol(const macho_nlist

& sym); + static bool weakImportFromSymbol(const macho_nlist

& sym); + static bool resolverFromSymbol(const macho_nlist

& sym); + uint32_t symbolIndexFromIndirectSectionAddress(pint_t,const macho_section

*); + const macho_section

* firstMachOSection() { return _sectionsStart; } + const macho_section

* machOSectionFromSectionIndex(uint32_t index); + uint32_t machOSectionCount() { return _machOSectionsCount; } + uint32_t undefinedStartIndex() { return _undefinedStartIndex; } + uint32_t undefinedEndIndex() { return _undefinedEndIndex; } + void addFixup(FixupInAtom f) { _allFixups.push_back(f); } + Section* sectionForNum(unsigned int sectNum); + Section* sectionForAddress(pint_t addr); + Atom* findAtomByAddress(pint_t addr); + Atom* findAtomByAddressOrNullIfStub(pint_t addr); + Atom* findAtomByAddressOrLocalTargetOfStub(pint_t addr, uint32_t* offsetInAtom); + Atom* findAtomByName(const char* name); // slow! + void findTargetFromAddress(pint_t addr, TargetDesc& target); + void findTargetFromAddress(pint_t baseAddr, pint_t addr, TargetDesc& target); + void findTargetFromAddressAndSectionNum(pint_t addr, unsigned int sectNum, + TargetDesc& target); + uint32_t tentativeDefinitionCount() { return _tentativeDefinitionCount; } + uint32_t absoluteSymbolCount() { return _absoluteSymbolCount; } + + bool hasStubsSection() { return (_stubsSectionNum != 0); } + unsigned int stubsSectionNum() { return _stubsSectionNum; } + void addDtraceExtraInfos(const SourceLocation& src, const char* provider); + const char* scanSymbolTableForAddress(uint64_t addr); + bool convertUnwindInfo() { return _convertUnwindInfo; } + + + void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target); + void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase); + + + + struct LabelAndCFIBreakIterator { + typedef typename CFISection::CFI_Atom_Info CFI_Atom_Info; + LabelAndCFIBreakIterator(const uint32_t* ssa, uint32_t ssc, const pint_t* cfisa, + uint32_t cfisc, bool ols) + : sortedSymbolIndexes(ssa), sortedSymbolCount(ssc), cfiStartsArray(cfisa), + cfiStartsCount(cfisc), fileHasOverlappingSymbols(ols), + newSection(false), cfiIndex(0), symIndex(0) {} + bool next(Parser& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, + pint_t* addr, pint_t* size, const macho_nlist

** sym); + pint_t peek(Parser& parser, pint_t startAddr, pint_t endAddr); + void beginSection() { newSection = true; symIndex = 0; } + + const uint32_t* const sortedSymbolIndexes; + const uint32_t sortedSymbolCount; + const pint_t* cfiStartsArray; + const uint32_t cfiStartsCount; + const bool fileHasOverlappingSymbols; + bool newSection; + uint32_t cfiIndex; + uint32_t symIndex; + }; + + struct CFIInfoArray { + 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; + }; + + +private: + friend class Section; + + enum SectionType { sectionTypeIgnore, sectionTypeLiteral4, sectionTypeLiteral8, sectionTypeLiteral16, + sectionTypeNonLazy, sectionTypeCFI, sectionTypeCString, sectionTypeCStringPointer, + sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList, + sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs, + sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs }; + + template + struct MachOSectionAndSectionClass + { + const macho_section

* sect; + SectionType type; + + static int sorter(const void* l, const void* r) { + const MachOSectionAndSectionClass

* left = (MachOSectionAndSectionClass

*)l; + const MachOSectionAndSectionClass

* right = (MachOSectionAndSectionClass

*)r; + int64_t diff = left->sect->addr() - right->sect->addr(); + if ( diff == 0 ) + return 0; + if ( diff < 0 ) + return -1; + else + return 1; + } + }; + + Parser(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, + uint32_t ordinal, bool convertUnwindInfo); + ld::relocatable::File* parse(const ParserOptions& opts); + uint8_t loadCommandSizeMask(); + bool parseLoadCommands(); + void makeSections(); + void checkForLSDA(); + void prescanSymbolTable(); + void makeSortedSymbolsArray(uint32_t array[]); + static int pointerSorter(const void* l, const void* r); + static int symbolIndexSorter(void* extra, const void* l, const void* r); + void parseDebugInfo(); + void parseStabs(); + static bool isConstFunStabs(const char *stabStr); + bool read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list); + const char* getDwarfString(uint64_t form, const uint8_t* p); + bool skip_form(const uint8_t ** offset, const uint8_t * end, + uint64_t form, uint8_t addr_size, bool dwarf64); + + + // filled in by constructor + const uint8_t* _fileContent; + uint32_t _fileLength; + const char* _path; + time_t _modTime; + uint32_t _ordinal; + + // filled in by parseLoadCommands() + File* _file; + const macho_nlist

* _symbols; + uint32_t _symbolCount; + const char* _strings; + uint32_t _stringsSize; + const uint32_t* _indirectTable; + uint32_t _indirectTableCount; + uint32_t _undefinedStartIndex; + uint32_t _undefinedEndIndex; + const macho_section

* _sectionsStart; + uint32_t _machOSectionsCount; + bool _hasUUID; + + // filled in by parse() + CFISection* _EHFrameSection; + AbsoluteSymbolSection* _absoluteSection; + uint32_t _lsdaTextSectionNum; + uint32_t _lsdaDataSectionNum; + uint32_t _tentativeDefinitionCount; + uint32_t _absoluteSymbolCount; + uint32_t _symbolsInSections; + bool _hasLongBranchStubs; + bool _AppleObjc; // FSF has objc that uses different data layout + bool _overlappingSymbols; + bool _convertUnwindInfo; + unsigned int _stubsSectionNum; + const macho_section

* _stubsMachOSection; + std::vector _dtraceProviderInfo; + std::vector _allFixups; +}; + + + +template +Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + uint32_t ordinal, bool convertDUI) + : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), + _ordinal(ordinal), _file(NULL), + _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), + _indirectTable(NULL), _indirectTableCount(0), + _undefinedStartIndex(0), _undefinedEndIndex(0), + _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), + _EHFrameSection(NULL), _absoluteSection(NULL), + _lsdaTextSectionNum(0), _lsdaDataSectionNum(0), + _tentativeDefinitionCount(0), _absoluteSymbolCount(0), + _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), + _overlappingSymbols(false), _convertUnwindInfo(convertDUI), + _stubsSectionNum(0), _stubsMachOSection(NULL) +{ +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_ARM ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + if ( subtypeMustMatch ) { + if ( (cpu_subtype_t)header->cpusubtype() == subtype ) + return true; + // hack until libcc_kext.a is made fat + if ( header->cpusubtype() == CPU_SUBTYPE_ARM_ALL ) + return true; + return false; + } + return true; +} + + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_POWERPC_750: + return "ppc750"; + case CPU_SUBTYPE_POWERPC_7400: + return "ppc7400"; + case CPU_SUBTYPE_POWERPC_7450: + return "ppc7450"; + case CPU_SUBTYPE_POWERPC_970: + return "ppc970"; + case CPU_SUBTYPE_POWERPC_ALL: + return "ppc"; + } + return "ppc???"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return NULL; + return "ppc64"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_I386 ) + return NULL; + return "i386"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return NULL; + return "x86_64"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + 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"; + } + return "arm???"; +} + + +template +bool Parser::hasObjC2Categories(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* sectionsStart = (macho_section

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

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

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

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return false; +} + +template +int Parser::pointerSorter(const void* l, const void* r) +{ + // sort references by address + const pint_t* left = (pint_t*)l; + const pint_t* right = (pint_t*)r; + return (*left - *right); +} + +template +typename A::P::uint_t Parser::LabelAndCFIBreakIterator::peek(Parser& parser, pint_t startAddr, pint_t endAddr) +{ + pint_t symbolAddr; + if ( symIndex < sortedSymbolCount ) + symbolAddr = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]).n_value(); + else + symbolAddr = endAddr; + pint_t cfiAddr; + if ( cfiIndex < cfiStartsCount ) + cfiAddr = cfiStartsArray[cfiIndex]; + else + cfiAddr = endAddr; + if ( (cfiAddr < symbolAddr) && (cfiAddr >= startAddr) ) { + if ( cfiAddr < endAddr ) + return cfiAddr; + else + return endAddr; + } + else { + if ( symbolAddr < endAddr ) + return symbolAddr; + else + return endAddr; + } +} + +// +// Parses up a section into chunks based on labels and CFI information. +// Each call returns the next chunk address and size, and (if the break +// was becuase of a label, the symbol). Returns false when no more chunks. +// +template +bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, + pint_t* addr, pint_t* size, const macho_nlist

** symbol) +{ + // may not be a label on start of section, but need atom demarcation there + if ( newSection ) { + newSection = false; + // advance symIndex until we get to the first label at or past the start of this section + while ( symIndex < sortedSymbolCount ) { + const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + if ( (nextSymbolAddr >= startAddr) && (sym.n_sect() >= sectNum) ) + break; + ++symIndex; + } + if ( symIndex < sortedSymbolCount ) { + const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + // if next symbol found is not in this section + if ( sym.n_sect() != sectNum ) { + // check for CFI break instead of symbol break + if ( cfiIndex < cfiStartsCount ) { + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextCfiAddr < endAddr ) { + // use cfi + ++cfiIndex; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + } + *addr = startAddr; + *size = endAddr - startAddr; + *symbol = NULL; + if ( startAddr == endAddr ) + return false; // zero size section + else + return true; // whole section is one atom with no label + } + // if also CFI break here, eat it + if ( cfiIndex < cfiStartsCount ) { + if ( cfiStartsArray[cfiIndex] == nextSymbolAddr ) + ++cfiIndex; + } + if ( nextSymbolAddr == startAddr ) { + // label at start of section, return it as chunk + ++symIndex; + *addr = startAddr; + *size = peek(parser, startAddr, endAddr) - startAddr; + *symbol = &sym; + return true; + } + // return chunk before first symbol + *addr = startAddr; + *size = nextSymbolAddr - startAddr; + *symbol = NULL; + return true; + } + // no symbols left in whole file, so entire section is one chunk + *addr = startAddr; + *size = endAddr - startAddr; + *symbol = NULL; + if ( startAddr == endAddr ) + return false; // zero size section + else + return true; // whole section is one atom with no label + } + + while ( (symIndex < sortedSymbolCount) && (cfiIndex < cfiStartsCount) ) { + const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextSymbolAddr < nextCfiAddr ) { + if ( nextSymbolAddr >= endAddr ) + return false; + ++symIndex; + if ( nextSymbolAddr < startAddr ) + continue; + *addr = nextSymbolAddr; + *size = peek(parser, startAddr, endAddr) - nextSymbolAddr; + *symbol = &sym; + return true; + } + else if ( nextCfiAddr < nextSymbolAddr ) { + if ( nextCfiAddr >= endAddr ) + return false; + ++cfiIndex; + if ( nextCfiAddr < startAddr ) + continue; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + else { + if ( nextCfiAddr >= endAddr ) + return false; + ++symIndex; + ++cfiIndex; + if ( nextCfiAddr < startAddr ) + continue; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = &sym; + return true; + } + } + while ( symIndex < sortedSymbolCount ) { + const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + // if next symbol found is not in this section, then done with iteration + if ( sym.n_sect() != sectNum ) + return false; + ++symIndex; + if ( nextSymbolAddr < startAddr ) + continue; + *addr = nextSymbolAddr; + *size = peek(parser, startAddr, endAddr) - nextSymbolAddr; + *symbol = &sym; + return true; + } + while ( cfiIndex < cfiStartsCount ) { + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextCfiAddr >= endAddr ) + return false; + ++cfiIndex; + if ( nextCfiAddr < startAddr ) + continue; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + return false; +} + + + +template +ld::relocatable::File* Parser::parse(const ParserOptions& opts) +{ + // create file object + _file = new File(_path, _modTime, _fileContent, _ordinal); + + // respond to -t option + if ( opts.logAllFiles ) + printf("%s\n", _path); + + // parse start of mach-o file + if ( ! parseLoadCommands() ) + return _file; + + // make symbol table sorted by address + this->checkForLSDA(); + this->prescanSymbolTable(); + uint32_t sortedSymbolIndexes[_symbolsInSections]; + this->makeSortedSymbolsArray(sortedSymbolIndexes); + + // allocate Section object for each mach-o section + makeSections(); + + // if it exists, do special parsing of __eh_frame section + // stack allocate array of CFI_Atom_Info + uint32_t countOfCFIs = 0; + if ( _EHFrameSection != NULL ) + countOfCFIs = _EHFrameSection->cfiCount(); + typename CFISection::CFI_Atom_Info cfiArray[countOfCFIs]; + // stack allocate (if not too large) a copy of __eh_frame to apply relocations to + uint8_t* ehBuffer = NULL; + uint32_t stackAllocSize = 0; + if ( (countOfCFIs != 0) && _EHFrameSection->needsRelocating() ) { + uint32_t sectSize = _EHFrameSection->machoSection()->size(); + if ( sectSize > 50*1024 ) + ehBuffer = (uint8_t*)malloc(sectSize); + else + stackAllocSize = sectSize; + } + uint32_t ehStackBuffer[1+stackAllocSize/4]; // make 4-byte aligned stack bufffer + if ( ehBuffer == NULL ) + ehBuffer = (uint8_t*)&ehStackBuffer; + uint32_t cfiStartsCount = 0; + if ( countOfCFIs != 0 ) { + _EHFrameSection->cfiParse(*this, ehBuffer, cfiArray, countOfCFIs); + // count functions and lsdas + for(uint32_t i=0; i < countOfCFIs; ++i) { + if ( cfiArray[i].isCIE ) + continue; + //fprintf(stderr, "cfiArray[i].func = 0x%08llX, cfiArray[i].lsda = 0x%08llX, encoding=0x%08X\n", + // (uint64_t)cfiArray[i].u.fdeInfo.function.targetAddress, + // (uint64_t)cfiArray[i].u.fdeInfo.lsda.targetAddress, + // cfiArray[i].u.fdeInfo.compactUnwindInfo); + if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) + ++cfiStartsCount; + if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) + ++cfiStartsCount; + } + } + CFIInfoArray cfis(cfiArray, countOfCFIs); + + // create sorted array of function starts and lsda starts + pint_t cfiStartsArray[cfiStartsCount]; + uint32_t countOfFDEs = 0; + if ( countOfCFIs != 0 ) { + int index = 0; + for(uint32_t i=0; i < countOfCFIs; ++i) { + if ( cfiArray[i].isCIE ) + continue; + if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) + cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.function.targetAddress; + if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) + cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; + ++countOfFDEs; + } + ::qsort(cfiStartsArray, cfiStartsCount, sizeof(pint_t), pointerSorter); + #ifndef NDEBUG + // scan for FDEs claming the same function + for(int i=1; i < index; ++i) { + assert( cfiStartsArray[i] != cfiStartsArray[i-1] ); + } + #endif + } + + Section** sections = _file->_sectionsArray; + uint32_t sectionsCount = _file->_sectionsArrayCount; + + // figure out how many atoms will be allocated and allocate + LabelAndCFIBreakIterator breakIterator(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, + cfiStartsCount, _overlappingSymbols); + uint32_t computedAtomCount = 0; + for (uint32_t i=0; i < sectionsCount; ++i ) { + breakIterator.beginSection(); + uint32_t count = sections[i]->computeAtomCount(*this, breakIterator, cfis); + //const macho_section

* sect = sections[i]->machoSection(); + //fprintf(stderr, "computed count=%u for section %s size=%llu\n", count, sect->sectname(), (sect != NULL) ? sect->size() : 0); + computedAtomCount += count; + } + //fprintf(stderr, "allocating %d atoms * sizeof(Atom)=%ld, sizeof(ld::Atom)=%ld\n", computedAtomCount, sizeof(Atom), sizeof(ld::Atom)); + _file->_atomsArray = new uint8_t[computedAtomCount*sizeof(Atom)]; + _file->_atomsArrayCount = 0; + + // have each section append atoms to _atomsArray + LabelAndCFIBreakIterator breakIterator2(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, + cfiStartsCount, _overlappingSymbols); + for (uint32_t i=0; i < sectionsCount; ++i ) { + 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()); + _file->_atomsArrayCount += count; + } + assert( _file->_atomsArrayCount == computedAtomCount && "more atoms allocated than expected"); + + + // have each section add all fix-ups for its atoms + _allFixups.reserve(computedAtomCount*5); + for (uint32_t i=0; i < sectionsCount; ++i ) + sections[i]->makeFixups(*this, cfis); + + // assign fixups start offset for each atom + uint8_t* p = _file->_atomsArray; + uint32_t fixupOffset = 0; + for(int i=_file->_atomsArrayCount; i > 0; --i) { + Atom* atom = (Atom*)p; + atom->_fixupsStartIndex = fixupOffset; + fixupOffset += atom->_fixupsCount; + atom->_fixupsCount = 0; + p += sizeof(Atom); + } + assert(fixupOffset == _allFixups.size()); + _file->_fixups.reserve(fixupOffset); + + // copy each fixup for each atom + for(typename std::vector::iterator it=_allFixups.begin(); it != _allFixups.end(); ++it) { + uint32_t slot = it->atom->_fixupsStartIndex + it->atom->_fixupsCount; + _file->_fixups[slot] = it->fixup; + it->atom->_fixupsCount++; + } + + // done with temp vector + _allFixups.clear(); + + // add unwind info + _file->_unwindInfos.reserve(countOfFDEs); + for(uint32_t i=0; i < countOfCFIs; ++i) { + if ( cfiArray[i].isCIE ) + continue; + if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) { + ld::Atom::UnwindInfo info; + info.startOffset = 0; + info.unwindInfo = cfiArray[i].u.fdeInfo.compactUnwindInfo; + _file->_unwindInfos.push_back(info); + Atom* func = findAtomByAddress(cfiArray[i].u.fdeInfo.function.targetAddress); + func->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + } + } + + // parse dwarf debug info to get line info + this->parseDebugInfo(); + + return _file; +} + + + +template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } + +template +bool Parser::parseLoadCommands() +{ + const macho_header

* header = (const macho_header

*)_fileContent; + + // set File attributes + _file->_canScatterAtoms = (header->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); + _file->_cpuSubType = header->cpusubtype(); + + const macho_segment_command

* segment = NULL; + const uint8_t* const endOfFile = _fileContent + _fileLength; + const uint32_t cmd_count = header->ncmds(); + // an empty .o file with zero load commands will crash linker + if ( cmd_count == 0 ) + return false; + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & this->loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > (uint8_t*)cmdsEnd ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + _symbolCount = symtab->nsyms(); + _symbols = (const macho_nlist

*)(_fileContent + symtab->symoff()); + _strings = (char*)_fileContent + symtab->stroff(); + _stringsSize = symtab->strsize(); + if ( (symtab->symoff() + _symbolCount*sizeof(macho_nlist

)) > _fileLength ) + throw "mach-o symbol table extends beyond end of file"; + if ( (_strings + _stringsSize) > (char*)endOfFile ) + throw "mach-o string pool extends beyond end of file"; + if ( _indirectTable == NULL ) { + if ( _undefinedEndIndex == 0 ) { + _undefinedStartIndex = 0; + _undefinedEndIndex = symtab->nsyms(); + } + } + } + break; + case LC_DYSYMTAB: + { + const macho_dysymtab_command

* dsymtab = (macho_dysymtab_command

*)cmd; + _indirectTable = (uint32_t*)(_fileContent + dsymtab->indirectsymoff()); + _indirectTableCount = dsymtab->nindirectsyms(); + if ( &_indirectTable[_indirectTableCount] > (uint32_t*)endOfFile ) + throw "indirect symbol table extends beyond end of file"; + _undefinedStartIndex = dsymtab->iundefsym(); + _undefinedEndIndex = _undefinedStartIndex + dsymtab->nundefsym(); + } + break; + case LC_UUID: + _hasUUID = true; + break; + + default: + if ( cmd->cmd() == macho_segment_command

::CMD ) { + if ( segment != NULL ) + throw "more than one LC_SEGMENT found in object file"; + segment = (macho_segment_command

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

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + + // record range of sections + if ( segment == NULL ) + throw "missing LC_SEGMENT"; + _sectionsStart = (macho_section

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

)); + _machOSectionsCount = segment->nsects(); + + 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() +{ + _tentativeDefinitionCount = 0; + _absoluteSymbolCount = 0; + _symbolsInSections = 0; + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist

& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // look at undefines + const char* symbolName = this->nameFromSymbol(sym); + if ( (sym.n_type() & N_TYPE) == N_UNDF ) { + if ( sym.n_value() != 0 ) { + // count tentative definitions + ++_tentativeDefinitionCount; + } + else if ( strncmp(symbolName, "___dtrace_", 10) == 0 ) { + // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* + // is extra provider info + if ( (strncmp(&symbolName[10], "probe$", 6) != 0) && (strncmp(&symbolName[10], "isenabled$", 10) != 0) ) { + _dtraceProviderInfo.push_back(symbolName); + } + } + continue; + } + + // count absolute symbols + if ( (sym.n_type() & N_TYPE) == N_ABS ) { + const char* absName = this->nameFromSymbol(sym); + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_class_name_", 17) == 0 ) { + _AppleObjc = true; + continue; + } + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_category_name_", 20) == 0 ) + continue; + // ignore empty *.eh symbols + if ( strcmp(&absName[strlen(absName)-3], ".eh") == 0 ) + continue; + ++_absoluteSymbolCount; + } + + // only look at definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // 'L' labels do not denote atom breaks + 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"; + + _symbolsInSections++; + } +} + +template +int Parser::symbolIndexSorter(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_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 + int64_t result = leftSym.n_value() - rightSym.n_value(); + 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 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 ) + return -1; + else + return 1; + } + // if both are global, make alphabetically last one be the alias + return ( strcmp(parser->nameFromSymbol(rightSym), parser->nameFromSymbol(leftSym)) ); + } + else if ( result < 0 ) + return -1; + else + return 1; +} + +template +void Parser::makeSortedSymbolsArray(uint32_t array[]) +{ + uint32_t* p = array; + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist

& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // only look at definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // 'L' labels do not denote atom breaks + const char* symbolName = this->nameFromSymbol(sym); + 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"; + + // append to array + *p++ = i; + } + 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); + + // look for two symbols at same address + _overlappingSymbols = false; + for (unsigned int i=1; i < _symbolsInSections; ++i) { + if ( symbolFromIndex(array[i-1]).n_value() == symbolFromIndex(array[i]).n_value() ) { + //fprintf(stderr, "overlapping symbols at 0x%08llX\n", symbolFromIndex(array[i-1]).n_value()); + _overlappingSymbols = true; + } + } + + //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])) ); +} + + +template +void Parser::makeSections() +{ + // classify each section by type + // compute how many Section objects will be needed and total size for all + unsigned int totalSectionsSize = 0; + uint8_t machOSectsStorage[sizeof(MachOSectionAndSectionClass

)*(_machOSectionsCount+2)]; // also room for tentative-defs and absolute symbols + // allocate raw storage for all section objects on stack + MachOSectionAndSectionClass

* machOSects = (MachOSectionAndSectionClass

*)machOSectsStorage; + 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; + } + // ignore empty __OBJC sections + if ( (sect->size() == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) + continue; + // objc image info section is really attributes and not content + if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) + || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset()); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + _file->_objConstraint = ld::File::objcConstraintGC; + else if ( (flags & 2) == 2 ) + _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else + _file->_objConstraint = ld::File::objcConstraintRetainRelease; + if ( (flags & 1) == 1 ) + _file->_ojcReplacmentClass = true; + if ( sect->size() > 8 ) { + warning("section %s/%s has unexpectedly large size %llu in %s", + sect->segname(), sect->sectname(), sect->size(), _file->path()); + } + } + else { + warning("can't parse %s/%s section in %s", sect->segname(), sect->sectname(), _file->path()); + } + continue; + } + machOSects[count].sect = sect; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + if ( _stubsSectionNum == 0 ) { + _stubsSectionNum = i+1; + _stubsMachOSection = sect; + } + else + assert(1 && "multiple S_SYMBOL_STUBS sections"); + case S_LAZY_SYMBOL_POINTERS: + break; + case S_4BYTE_LITERALS: + totalSectionsSize += sizeof(Literal4Section); + machOSects[count++].type = sectionTypeLiteral4; + break; + case S_8BYTE_LITERALS: + totalSectionsSize += sizeof(Literal8Section); + machOSects[count++].type = sectionTypeLiteral8; + break; + case S_16BYTE_LITERALS: + totalSectionsSize += sizeof(Literal16Section); + machOSects[count++].type = sectionTypeLiteral16; + break; + case S_NON_LAZY_SYMBOL_POINTERS: + totalSectionsSize += sizeof(NonLazyPointerSection); + machOSects[count++].type = sectionTypeNonLazy; + break; + case S_LITERAL_POINTERS: + if ( (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__cls_refs") == 0) ) { + totalSectionsSize += sizeof(Objc1ClassReferences); + machOSects[count++].type = sectionTypeObjC1ClassRefs; + } + else { + totalSectionsSize += sizeof(PointerToCStringSection); + machOSects[count++].type = sectionTypeCStringPointer; + } + break; + case S_CSTRING_LITERALS: + totalSectionsSize += sizeof(CStringSection); + machOSects[count++].type = sectionTypeCString; + break; + case S_MOD_INIT_FUNC_POINTERS: + case S_MOD_TERM_FUNC_POINTERS: + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + case S_INTERPOSING: + case S_ZEROFILL: + case S_REGULAR: + case S_COALESCED: + case S_THREAD_LOCAL_REGULAR: + case S_THREAD_LOCAL_ZEROFILL: + if ( (strcmp(sect->segname(), "__TEXT") == 0) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { + totalSectionsSize += sizeof(CFISection); + machOSects[count++].type = sectionTypeCFI; + } + else if ( (strcmp(sect->segname(), "__DATA") == 0) && (strcmp(sect->sectname(), "__cfstring") == 0) ) { + totalSectionsSize += sizeof(CFStringSection); + machOSects[count++].type = sectionTypeCFString; + } + else if ( (strcmp(sect->segname(), "__TEXT") == 0) && (strcmp(sect->sectname(), "__ustring") == 0) ) { + totalSectionsSize += sizeof(UTF16StringSection); + machOSects[count++].type = sectionTypeUTF16Strings; + } + else if ( (strcmp(sect->segname(), "__DATA") == 0) && (strncmp(sect->sectname(), "__objc_classrefs", 16) == 0) ) { + totalSectionsSize += sizeof(ObjC2ClassRefsSection); + machOSects[count++].type = sectionTypeObjC2ClassRefs; + } + else if ( (strcmp(sect->segname(), "__DATA") == 0) && (strcmp(sect->sectname(), "__objc_catlist") == 0) ) { + totalSectionsSize += sizeof(ObjC2CategoryListSection); + machOSects[count++].type = typeObjC2CategoryList; + } + else if ( _AppleObjc && (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__class") == 0) ) { + totalSectionsSize += sizeof(ObjC1ClassSection); + machOSects[count++].type = sectionTypeObjC1Classes; + } + else { + totalSectionsSize += sizeof(SymboledSection); + machOSects[count++].type = sectionTypeSymboled; + } + break; + case S_THREAD_LOCAL_VARIABLES: + totalSectionsSize += sizeof(TLVDefsSection); + machOSects[count++].type = sectionTypeTLVDefs; + break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + default: + throwf("unknown section type %d", sect->flags() & SECTION_TYPE); + } + } + + // sort by address (mach-o object files don't aways have sections sorted) + ::qsort(machOSects, count, sizeof(MachOSectionAndSectionClass

), MachOSectionAndSectionClass

::sorter); + + // we will synthesize a dummy Section object for tentative definitions + if ( _tentativeDefinitionCount > 0 ) { + totalSectionsSize += sizeof(TentativeDefinitionSection); + machOSects[count++].type = sectionTypeTentativeDefinitions; + } + + // we will synthesize a dummy Section object for Absolute symbols + if ( _absoluteSymbolCount > 0 ) { + totalSectionsSize += sizeof(AbsoluteSymbolSection); + machOSects[count++].type = sectionTypeAbsoluteSymbols; + } + + // allocate one block for all Section objects as well as pointers to each + uint8_t* space = new uint8_t[totalSectionsSize+count*sizeof(Section*)]; + _file->_sectionsArray = (Section**)space; + _file->_sectionsArrayCount = count; + Section** objects = _file->_sectionsArray; + space += count*sizeof(Section*); + for (uint32_t i=0; i < count; ++i) { + switch ( machOSects[i].type ) { + case sectionTypeIgnore: + break; + case sectionTypeLiteral4: + *objects++ = new (space) Literal4Section(*this, *_file, machOSects[i].sect); + space += sizeof(Literal4Section); + break; + case sectionTypeLiteral8: + *objects++ = new (space) Literal8Section(*this, *_file, machOSects[i].sect); + space += sizeof(Literal8Section); + break; + case sectionTypeLiteral16: + *objects++ = new (space) Literal16Section(*this, *_file, machOSects[i].sect); + space += sizeof(Literal16Section); + break; + case sectionTypeNonLazy: + *objects++ = new (space) NonLazyPointerSection(*this, *_file, machOSects[i].sect); + space += sizeof(NonLazyPointerSection); + break; + case sectionTypeCFI: + _EHFrameSection = new (space) CFISection(*this, *_file, machOSects[i].sect); + *objects++ = _EHFrameSection; + space += sizeof(CFISection); + break; + case sectionTypeCString: + *objects++ = new (space) CStringSection(*this, *_file, machOSects[i].sect); + space += sizeof(CStringSection); + break; + case sectionTypeCStringPointer: + *objects++ = new (space) PointerToCStringSection(*this, *_file, machOSects[i].sect); + space += sizeof(PointerToCStringSection); + break; + case sectionTypeObjC1ClassRefs: + *objects++ = new (space) Objc1ClassReferences(*this, *_file, machOSects[i].sect); + space += sizeof(Objc1ClassReferences); + break; + case sectionTypeUTF16Strings: + *objects++ = new (space) UTF16StringSection(*this, *_file, machOSects[i].sect); + space += sizeof(UTF16StringSection); + break; + case sectionTypeCFString: + *objects++ = new (space) CFStringSection(*this, *_file, machOSects[i].sect); + space += sizeof(CFStringSection); + break; + case sectionTypeObjC2ClassRefs: + *objects++ = new (space) ObjC2ClassRefsSection(*this, *_file, machOSects[i].sect); + space += sizeof(ObjC2ClassRefsSection); + break; + case typeObjC2CategoryList: + *objects++ = new (space) ObjC2CategoryListSection(*this, *_file, machOSects[i].sect); + space += sizeof(ObjC2CategoryListSection); + break; + case sectionTypeObjC1Classes: + *objects++ = new (space) ObjC1ClassSection(*this, *_file, machOSects[i].sect); + space += sizeof(ObjC1ClassSection); + break; + case sectionTypeSymboled: + *objects++ = new (space) SymboledSection(*this, *_file, machOSects[i].sect); + space += sizeof(SymboledSection); + break; + case sectionTypeTLVDefs: + *objects++ = new (space) TLVDefsSection(*this, *_file, machOSects[i].sect); + space += sizeof(TLVDefsSection); + break; + case sectionTypeTentativeDefinitions: + *objects++ = new (space) TentativeDefinitionSection(*this, *_file); + space += sizeof(TentativeDefinitionSection); + break; + case sectionTypeAbsoluteSymbols: + _absoluteSection = new (space) AbsoluteSymbolSection(*this, *_file); + *objects++ = _absoluteSection; + space += sizeof(AbsoluteSymbolSection); + break; + default: + throw "internal error uknown SectionType"; + } + } +} + + +template +Section* Parser::sectionForAddress(typename A::P::uint_t addr) +{ + for (uint32_t i=0; i < _file->_sectionsArrayCount; ++i ) { + const macho_section* sect = _file->_sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + return _file->_sectionsArray[i]; + } + } + } + // not strictly in any section + // may be in a zero length section + for (uint32_t i=0; i < _file->_sectionsArrayCount; ++i ) { + const macho_section* sect = _file->_sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( (sect->addr() == addr) && (sect->size() == 0) ) { + return _file->_sectionsArray[i]; + } + } + } + + throwf("sectionForAddress(0x%llX) address not in any section", (uint64_t)addr); +} + +template +Section* Parser::sectionForNum(unsigned int num) +{ + for (uint32_t i=0; i < _file->_sectionsArrayCount; ++i ) { + const macho_section* sect = _file->_sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( num == (unsigned int)((sect - _sectionsStart)+1) ) + return _file->_sectionsArray[i]; + } + } + throwf("sectionForNum(%u) section number not for any section", num); +} + +template +Atom* Parser::findAtomByAddress(pint_t addr) +{ + Section* section = this->sectionForAddress(addr); + return section->findAtomByAddress(addr); +} + +template +Atom* Parser::findAtomByAddressOrNullIfStub(pint_t addr) +{ + if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) + return NULL; + return findAtomByAddress(addr); +} + +template +Atom* Parser::findAtomByAddressOrLocalTargetOfStub(pint_t addr, uint32_t* offsetInAtom) +{ + if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) { + // target is a stub, remove indirection + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); + const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); + // can't be to external weak symbol + assert( (this->combineFromSymbol(sym) != ld::Atom::combineByName) || (this->scopeFromSymbol(sym) != ld::Atom::scopeGlobal) ); + *offsetInAtom = 0; + return this->findAtomByName(this->nameFromSymbol(sym)); + } + Atom* target = this->findAtomByAddress(addr); + *offsetInAtom = addr - target->_objAddress; + return target; +} + +template +Atom* Parser::findAtomByName(const char* name) +{ + uint8_t* p = _file->_atomsArray; + for(int i=_file->_atomsArrayCount; i > 0; --i) { + Atom* atom = (Atom*)p; + if ( strcmp(name, atom->name()) == 0 ) + return atom; + p += sizeof(Atom); + } + return NULL; +} + +template +void Parser::findTargetFromAddress(pint_t addr, TargetDesc& target) +{ + if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) { + // target is a stub, remove indirection + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); + const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); + target.atom = NULL; + target.name = this->nameFromSymbol(sym); + target.weakImport = this->weakImportFromSymbol(sym); + target.addend = 0; + return; + } + Section* section = this->sectionForAddress(addr); + target.atom = section->findAtomByAddress(addr); + target.addend = addr - target.atom->_objAddress; + target.weakImport = false; + target.name = NULL; +} + +template +void Parser::findTargetFromAddress(pint_t baseAddr, pint_t addr, TargetDesc& target) +{ + findTargetFromAddress(baseAddr, target); + target.addend = addr - target.atom->_objAddress; +} + +template +void Parser::findTargetFromAddressAndSectionNum(pint_t addr, unsigned int sectNum, TargetDesc& target) +{ + if ( sectNum == R_ABS ) { + // target is absolute symbol that corresponds to addr + if ( _absoluteSection != NULL ) { + target.atom = _absoluteSection->findAbsAtomForValue(addr); + if ( target.atom != NULL ) { + target.name = NULL; + target.weakImport = false; + target.addend = 0; + return; + } + } + throwf("R_ABS reloc but no absolute symbol at target address"); + } + + if ( hasStubsSection() && (stubsSectionNum() == sectNum) ) { + // target is a stub, remove indirection + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); + const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); + // use direct reference when stub is to a static function + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (this->nameFromSymbol(sym)[0] == 'L')) ) { + this->findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + } + else { + target.atom = NULL; + target.name = this->nameFromSymbol(sym); + target.weakImport = this->weakImportFromSymbol(sym); + target.addend = 0; + } + return; + } + Section* section = this->sectionForNum(sectNum); + target.atom = section->findAtomByAddress(addr); + if ( target.atom == NULL ) { + typedef typename A::P::sint_t sint_t; + sint_t a = (sint_t)addr; + sint_t sectStart = (sint_t)(section->machoSection()->addr()); + sint_t sectEnd = sectStart + section->machoSection()->size(); + if ( a < sectStart ) { + // target address is before start of section, so must be negative addend + target.atom = section->findAtomByAddress(sectStart); + target.addend = a - sectStart; + target.weakImport = false; + target.name = NULL; + return; + } + else if ( a >= sectEnd ) { + target.atom = section->findAtomByAddress(sectEnd-1); + target.addend = a - sectEnd; + target.weakImport = false; + target.name = NULL; + return; + } + } + assert(target.atom != NULL); + target.addend = addr - target.atom->_objAddress; + target.weakImport = false; + target.name = NULL; +} + +template +void Parser::addDtraceExtraInfos(const SourceLocation& src, const char* providerName) +{ + // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with + // a matching provider name, add a by-name kDtraceTypeReference at probe site + const char* dollar = strchr(providerName, '$'); + if ( dollar != NULL ) { + int providerNameLen = dollar-providerName+1; + for ( std::vector::iterator it = _dtraceProviderInfo.begin(); it != _dtraceProviderInfo.end(); ++it) { + const char* typeDollar = strchr(*it, '$'); + if ( typeDollar != NULL ) { + if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) { + addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindDtraceExtra,false, *it); + } + } + } + } +} + +template +const char* Parser::scanSymbolTableForAddress(uint64_t addr) +{ + uint64_t closestSymAddr = 0; + const char* closestSymName = NULL; + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist

& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // only look at definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // return with exact match + if ( sym.n_value() == addr ) + return nameFromSymbol(sym); + + // record closest seen so far + if ( (sym.n_value() < addr) && ((sym.n_value() > closestSymAddr) || (closestSymName == NULL)) ) + closestSymName = nameFromSymbol(sym); + } + + return (closestSymName != NULL) ? closestSymName : "unknown"; +} + + +template +void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, const TargetDesc& target) +{ + // some fixup pairs can be combined + ld::Fixup::Cluster cl = ld::Fixup::k1of3; + ld::Fixup::Kind firstKind = ld::Fixup::kindSetTargetAddress; + bool combined = false; + if ( target.addend == 0 ) { + cl = ld::Fixup::k1of1; + combined = true; + switch ( setKind ) { + case ld::Fixup::kindStoreLittleEndian32: + firstKind = ld::Fixup::kindStoreTargetAddressLittleEndian32; + break; + case ld::Fixup::kindStoreLittleEndian64: + firstKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; + break; + case ld::Fixup::kindStoreBigEndian32: + firstKind = ld::Fixup::kindStoreTargetAddressBigEndian32; + break; + case ld::Fixup::kindStoreBigEndian64: + firstKind = ld::Fixup::kindStoreTargetAddressBigEndian64; + break; + case ld::Fixup::kindStoreX86BranchPCRel32: + firstKind = ld::Fixup::kindStoreTargetAddressX86BranchPCRel32; + break; + case ld::Fixup::kindStoreX86PCRel32: + firstKind = ld::Fixup::kindStoreTargetAddressX86PCRel32; + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + firstKind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad; + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + firstKind = ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad; + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + firstKind = ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad; + break; + case ld::Fixup::kindStoreARMBranch24: + firstKind = ld::Fixup::kindStoreTargetAddressARMBranch24; + break; + case ld::Fixup::kindStoreThumbBranch22: + firstKind = ld::Fixup::kindStoreTargetAddressThumbBranch22; + break; + case ld::Fixup::kindStorePPCBranch24: + firstKind = ld::Fixup::kindStoreTargetAddressPPCBranch24; + break; + default: + combined = false; + cl = ld::Fixup::k1of2; + break; + } + } + + if ( target.atom != NULL ) { + if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + addFixup(src, cl, firstKind, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + addFixup(src, cl, firstKind, ld::Fixup::bindingByContentBound, target.atom); + } + else if ( (src.atom->section().type() == ld::Section::typeCFString) && (src.offsetInAtom != 0) ) { + // backing string in CFStrings should always be direct + addFixup(src, cl, firstKind, target.atom); + } + else { + // change direct fixup to by-name fixup + addFixup(src, cl, firstKind, false, target.atom->name()); + } + } + else { + addFixup(src, cl, firstKind, target.weakImport, target.name); + } + if ( target.addend == 0 ) { + if ( ! combined ) + addFixup(src, ld::Fixup::k2of2, setKind); + } + else { + addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, target.addend); + addFixup(src, ld::Fixup::k3of3, setKind); + } +} + +template +void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase) +{ + ld::Fixup::Cluster cl = (target.addend == 0) ? ld::Fixup::k1of4 : ld::Fixup::k1of5; + if ( target.atom != NULL ) { + if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, target.atom); + } + else { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, false, target.atom->name()); + } + } + else { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, target.weakImport, target.name); + } + if ( target.addend == 0 ) { + assert(picBase.atom != NULL); + addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, picBase.atom); + addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, picBase.addend); + addFixup(src, ld::Fixup::k4of4, kind); + } + else { + addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, target.addend); + addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, picBase.atom); + addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, picBase.addend); + addFixup(src, ld::Fixup::k5of5, kind); + } +} + + + +template +uint32_t TentativeDefinitionSection::computeAtomCount(class Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + return parser.tentativeDefinitionCount(); +} + +template +uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uint8_t* p, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + this->_beginAtoms = (Atom*)p; + uint32_t count = 0; + for (uint32_t i=parser.undefinedStartIndex(); i < parser.undefinedEndIndex(); ++i) { + const macho_nlist

& sym = parser.symbolFromIndex(i); + if ( ((sym.n_type() & N_TYPE) == N_UNDF) && (sym.n_value() != 0) ) { + uint64_t size = sym.n_value(); + uint8_t alignP2 = GET_COMM_ALIGN(sym.n_desc()); + if ( alignP2 == 0 ) { + // common symbols align to their size + // that is, a 4-byte common aligns to 4-bytes + // if this size is not a power of two, + // then round up to the next power of two + alignP2 = 63 - (uint8_t)__builtin_clzll(size); + if ( size != (1ULL << alignP2) ) + ++alignP2; + } + // limit alignment of extremely large commons to 2^15 bytes (8-page) + if ( alignP2 > 12 ) + alignP2 = 12; + Atom* allocatedSpace = (Atom*)p; + new (allocatedSpace) Atom(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size, + ld::Atom::definitionTentative, ld::Atom::combineByName, + parser.scopeFromSymbol(sym), ld::Atom::typeZeroFill, ld::Atom::symbolTableIn, + parser.dontDeadStripFromSymbol(sym), false, false, ld::Atom::Alignment(alignP2) ); + p += sizeof(Atom); + ++count; + } + } + this->_endAtoms = (Atom*)p; + return count; +} + + +template +uint32_t AbsoluteSymbolSection::computeAtomCount(class Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + return parser.absoluteSymbolCount(); +} + +template +uint32_t AbsoluteSymbolSection::appendAtoms(class Parser& parser, uint8_t* p, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + this->_beginAtoms = (Atom*)p; + uint32_t count = 0; + for (uint32_t i=0; i < parser.symbolCount(); ++i) { + const macho_nlist

& sym = parser.symbolFromIndex(i); + if ( (sym.n_type() & N_TYPE) != N_ABS ) + continue; + const char* absName = parser.nameFromSymbol(sym); + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_class_name_", 17) == 0 ) + continue; + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_category_name_", 20) == 0 ) + continue; + // ignore empty *.eh symbols + if ( strcmp(&absName[strlen(absName)-3], ".eh") == 0 ) + continue; + + Atom* allocatedSpace = (Atom*)p; + new (allocatedSpace) Atom(*this, parser, sym, 0); + p += sizeof(Atom); + ++count; + } + this->_endAtoms = (Atom*)p; + return count; +} + +template +Atom* AbsoluteSymbolSection::findAbsAtomForValue(typename A::P::uint_t value) +{ + Atom* end = this->_endAtoms; + for(Atom* p = this->_beginAtoms; p < end; ++p) { + if ( p->_objAddress == value ) + return p; + } + return NULL; +} + + +template +uint32_t Parser::indirectSymbol(uint32_t indirectIndex) +{ + if ( indirectIndex >= _indirectTableCount ) + throw "indirect symbol index out of range"; + return E::get32(_indirectTable[indirectIndex]); +} + +template +const macho_nlist& Parser::symbolFromIndex(uint32_t index) +{ + if ( index > _symbolCount ) + throw "symbol index out of range"; + return _symbols[index]; +} + +template +const macho_section* Parser::machOSectionFromSectionIndex(uint32_t index) +{ + if ( index >= _machOSectionsCount ) + throw "section index out of range"; + return &_sectionsStart[index]; +} + +template +uint32_t Parser::symbolIndexFromIndirectSectionAddress(pint_t addr, const macho_section

* sect) +{ + uint32_t elementSize = 0; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + elementSize = sect->reserved2(); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + elementSize = sizeof(pint_t); + break; + default: + throw "section does not use inirect symbol table"; + } + uint32_t indexInSection = (addr - sect->addr()) / elementSize; + uint32_t indexIntoIndirectTable = sect->reserved1() + indexInSection; + return this->indirectSymbol(indexIntoIndirectTable); +} + + + +template +const char* Parser::nameFromSymbol(const macho_nlist

& sym) +{ + return &_strings[sym.n_strx()]; +} + +template +ld::Atom::Scope Parser::scopeFromSymbol(const macho_nlist

& sym) +{ + if ( (sym.n_type() & N_EXT) == 0 ) + return ld::Atom::scopeTranslationUnit; + else if ( (sym.n_type() & N_PEXT) != 0 ) + return ld::Atom::scopeLinkageUnit; + else if ( this->nameFromSymbol(sym)[0] == 'l' ) // since all 'l' symbols will be remove, don't make them global + return ld::Atom::scopeLinkageUnit; + else + return ld::Atom::scopeGlobal; +} + +template +ld::Atom::Definition Parser::definitionFromSymbol(const macho_nlist

& sym) +{ + switch ( sym.n_type() & N_TYPE ) { + case N_ABS: + return ld::Atom::definitionAbsolute; + case N_SECT: + return ld::Atom::definitionRegular; + case N_UNDF: + if ( sym.n_value() != 0 ) + return ld::Atom::definitionTentative; + } + throw "definitionFromSymbol() bad symbol"; +} + +template +ld::Atom::Combine Parser::combineFromSymbol(const macho_nlist

& sym) +{ + if ( sym.n_desc() & N_WEAK_DEF ) + return ld::Atom::combineByName; + else + return ld::Atom::combineNever; +} + + +template +ld::Atom::SymbolTableInclusion Parser::inclusionFromSymbol(const macho_nlist

& sym) +{ + const char* symbolName = nameFromSymbol(sym); + // labels beginning with 'l' (lowercase ell) are automatically removed in final linked images + // labels beginning with 'L' should have been stripped by the assembler, so are stripped now + if ( sym.n_desc() & REFERENCED_DYNAMICALLY ) + return ld::Atom::symbolTableInAndNeverStrip; + else if ( symbolName[0] == 'l' ) + return ld::Atom::symbolTableNotInFinalLinkedImages; + else if ( symbolName[0] == 'L' ) + return ld::Atom::symbolTableNotIn; + else + return ld::Atom::symbolTableIn; +} + +template +bool Parser::dontDeadStripFromSymbol(const macho_nlist

& sym) +{ + return ( (sym.n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 ); +} + +template +bool Parser::isThumbFromSymbol(const macho_nlist

& sym) +{ + return ( sym.n_desc() & N_ARM_THUMB_DEF ); +} + +template +bool Parser::weakImportFromSymbol(const macho_nlist

& sym) +{ + return ( ((sym.n_type() & N_TYPE) == N_UNDF) && ((sym.n_desc() & N_WEAK_REF) != 0) ); +} + +template +bool Parser::resolverFromSymbol(const macho_nlist

& sym) +{ + return ( sym.n_desc() & N_SYMBOL_RESOLVER ); +} + + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (const uint8_t ** offset, const uint8_t * end) +{ + while (*offset != end && **offset >= 0x80) + (*offset)++; + if (*offset != end) + (*offset)++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (const uint8_t ** offset, const uint8_t * end) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (*offset == end) + return (uint64_t) -1; + + b = **offset & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*(*offset)++ >= 0x80); + return result; +} + + +/* Skip over a DWARF attribute of form FORM. */ +template +bool Parser::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, + uint8_t addr_size, bool dwarf64) +{ + int64_t sz=0; + + switch (form) + { + case DW_FORM_addr: + sz = addr_size; + break; + + case DW_FORM_block2: + if (end - *offset < 2) + return false; + sz = 2 + A::P::E::get16(*(uint16_t*)offset); + break; + + case DW_FORM_block4: + if (end - *offset < 4) + return false; + sz = 2 + A::P::E::get32(*(uint32_t*)offset); + break; + + case DW_FORM_data2: + case DW_FORM_ref2: + sz = 2; + break; + + case DW_FORM_data4: + case DW_FORM_ref4: + sz = 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + sz = 8; + break; + + case DW_FORM_string: + while (*offset != end && **offset) + ++*offset; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + sz = 1; + break; + + case DW_FORM_block: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_block1: + if (*offset == end) + return false; + sz = 1 + **offset; + break; + + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + skip_leb128 (offset, end); + return true; + + case DW_FORM_strp: + case DW_FORM_ref_addr: + sz = 4; + break; + + default: + return false; + } + if (end - *offset < sz) + return false; + *offset += sz; + return true; +} + + +template +const char* Parser::getDwarfString(uint64_t form, const uint8_t* p) +{ + if ( form == DW_FORM_string ) + return (const char*)p; + else if ( form == DW_FORM_strp ) { + uint32_t offset = E::get32(*((uint32_t*)p)); + const char* dwarfStrings = (char*)_file->fileContent() + _file->_dwarfDebugStringSect->offset(); + if ( offset > _file->_dwarfDebugStringSect->size() ) { + warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->_path); + return NULL; + } + return &dwarfStrings[offset]; + } + warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->_path); + return NULL; +} + + +template +struct AtomAndLineInfo { + Atom* atom; + ld::Atom::LineInfo info; +}; + + +// Add support to ld64 for N_FUN stabs when used for symbolic constants +// Returns whether a stabStr belonging to an N_FUN stab represents a +// symbolic constant rather than a function +template +bool Parser::isConstFunStabs(const char *stabStr) +{ + const char* colon; + // N_FUN can be used for both constants and for functions. In case it's a constant, + // the format of the stabs string is "symname:c=;" + // ':' cannot appear in the symbol name, except if it's an Objective-C method + // (in which case the symbol name starts with + or -, and then it's definitely + // not a constant) + return (stabStr != NULL) && (stabStr[0] != '+') && (stabStr[0] != '-') + && ((colon = strchr(stabStr, ':')) != NULL) + && (colon[1] == 'c') && (colon[2] == '='); +} + + +template +void Parser::parseDebugInfo() +{ + // check for dwarf __debug_info section + if ( _file->_dwarfDebugInfoSect == NULL ) { + // if no DWARF debug info, look for stabs + this->parseStabs(); + return; + } + if ( _file->_dwarfDebugInfoSect->size() == 0 ) + return; + + uint64_t stmtList; + if ( !read_comp_unit(&_file->_dwarfTranslationUnitFile, &_file->_dwarfTranslationUnitDir, &stmtList) ) { + // if can't parse dwarf, warn and give up + _file->_dwarfTranslationUnitFile = NULL; + _file->_dwarfTranslationUnitDir = NULL; + warning("can't parse dwarf compilation unit info in %s", _path); + _file->_debugInfoKind = ld::relocatable::File::kDebugInfoNone; + return; + } + + // add line number info to atoms from dwarf + std::vector > entries; + entries.reserve(64); + if ( _file->_debugInfoKind == ld::relocatable::File::kDebugInfoDwarf ) { + // file with just data will have no __debug_line info + if ( (_file->_dwarfDebugLineSect != NULL) && (_file->_dwarfDebugLineSect->size() != 0) ) { + // validate stmt_list + if ( (stmtList != (uint64_t)-1) && (stmtList < _file->_dwarfDebugLineSect->size()) ) { + const uint8_t* debug_line = (uint8_t*)_file->fileContent() + _file->_dwarfDebugLineSect->offset(); + struct line_reader_data* lines = line_open(&debug_line[stmtList], + _file->_dwarfDebugLineSect->size() - stmtList, E::little_endian); + struct line_info result; + Atom* curAtom = NULL; + uint32_t curAtomOffset = 0; + uint32_t curAtomAddress = 0; + uint32_t curAtomSize = 0; + std::map dwarfIndexToFile; + if ( lines != NULL ) { + while ( line_next(lines, &result, line_stop_pc) ) { + //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d," + // " curAtomAddress=0x%X, curAtomSize=0x%X\n", + // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); + // work around weird debug line table compiler generates if no functions in __text section + if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) + continue; + // for performance, see if in next pc is in current atom + if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or pc at end of current atom + else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or only one function that is a one line function + else if ( result.end_of_sequence && (curAtom == NULL) && (this->findAtomByAddress(0) != NULL) && (result.pc == this->findAtomByAddress(0)->size()) ) { + curAtom = this->findAtomByAddress(0); + curAtomOffset = result.pc - curAtom->objectAddress(); + curAtomAddress = curAtom->objectAddress(); + curAtomSize = curAtom->size(); + } + else { + // do slow look up of atom by address + try { + curAtom = this->findAtomByAddress(result.pc); + } + catch (...) { + // in case of bug in debug info, don't abort link, just limp on + curAtom = NULL; + } + if ( curAtom == NULL ) + break; // file has line info but no functions + if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { + // a one line function can be returned by line_next() as one entry with pc at end of blob + // look for alt atom starting at end of previous atom + uint32_t previousEnd = curAtomAddress+curAtomSize; + Atom* alt = this->findAtomByAddressOrNullIfStub(previousEnd); + if ( alt == NULL ) + continue; // ignore spurious debug info for stubs + if ( result.pc <= alt->objectAddress() + alt->size() ) { + curAtom = alt; + curAtomOffset = result.pc - alt->objectAddress(); + curAtomAddress = alt->objectAddress(); + curAtomSize = alt->size(); + } + else { + curAtomOffset = result.pc - curAtom->objectAddress(); + curAtomAddress = curAtom->objectAddress(); + curAtomSize = curAtom->size(); + } + } + else { + curAtomOffset = result.pc - curAtom->objectAddress(); + curAtomAddress = curAtom->objectAddress(); + curAtomSize = curAtom->size(); + } + } + const char* filename; + std::map::iterator pos = dwarfIndexToFile.find(result.file); + if ( pos == dwarfIndexToFile.end() ) { + filename = line_file(lines, result.file); + dwarfIndexToFile[result.file] = filename; + } + else { + filename = pos->second; + } + // only record for ~8000 line info records per function + if ( curAtom->roomForMoreLineInfoCount() ) { + AtomAndLineInfo entry; + entry.atom = curAtom; + entry.info.atomOffset = curAtomOffset; + entry.info.fileName = filename; + entry.info.lineNumber = result.line; + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", + // result.pc, result.line, filename, curAtom->name(), curAtomSize, result.end_of_sequence); + entries.push_back(entry); + curAtom->incrementLineInfoCount(); + } + if ( result.end_of_sequence ) { + curAtom = NULL; + } + } + line_free(lines); + } + } + } + } + + // assign line info start offset for each atom + uint8_t* p = _file->_atomsArray; + uint32_t liOffset = 0; + for(int i=_file->_atomsArrayCount; i > 0; --i) { + Atom* atom = (Atom*)p; + atom->_lineInfoStartIndex = liOffset; + liOffset += atom->_lineInfoCount; + atom->_lineInfoCount = 0; + p += sizeof(Atom); + } + assert(liOffset == entries.size()); + _file->_lineInfos.reserve(liOffset); + + // copy each line info for each atom + for (typename std::vector >::iterator it = entries.begin(); it != entries.end(); ++it) { + uint32_t slot = it->atom->_lineInfoStartIndex + it->atom->_lineInfoCount; + _file->_lineInfos[slot] = it->info; + it->atom->_lineInfoCount++; + } + + // done with temp vector + entries.clear(); +} + +template +void Parser::parseStabs() +{ + // scan symbol table for stabs entries + Atom* currentAtom = NULL; + pint_t currentAtomAddress = 0; + enum { start, inBeginEnd, inFun } state = start; + for (uint32_t symbolIndex = 0; symbolIndex < _symbolCount; ++symbolIndex ) { + const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); + bool useStab = true; + uint8_t type = sym.n_type(); + const char* symString = (sym.n_strx() != 0) ? this->nameFromSymbol(sym) : NULL; + if ( (type & N_STAB) != 0 ) { + _file->_debugInfoKind = (_hasUUID ? ld::relocatable::File::kDebugInfoStabsUUID : ld::relocatable::File::kDebugInfoStabs); + ld::relocatable::File::Stab stab; + stab.atom = NULL; + stab.type = type; + stab.other = sym.n_sect(); + stab.desc = sym.n_desc(); + stab.value = sym.n_value(); + stab.string = NULL; + switch (state) { + case start: + switch (type) { + case N_BNSYM: + // beginning of function block + state = inBeginEnd; + // fall into case to lookup atom by addresss + case N_LCSYM: + case N_STSYM: + currentAtomAddress = sym.n_value(); + currentAtom = this->findAtomByAddress(currentAtomAddress); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", + (uint64_t)sym.n_value(), _path); + } + break; + case N_SO: + case N_OSO: + case N_OPT: + case N_LSYM: + case N_RSYM: + case N_PSYM: + // not associated with an atom, just copy + stab.string = symString; + break; + case N_GSYM: + { + // n_value field is NOT atom address ;-( + // need to find atom by name match + const char* colon = strchr(symString, ':'); + if ( colon != NULL ) { + // build underscore leading name + int nameLen = colon - symString; + char symName[nameLen+2]; + strlcpy(&symName[1], symString, nameLen+1); + symName[0] = '_'; + symName[nameLen+1] = '\0'; + currentAtom = this->findAtomByName(symName); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + else { + // might be a debug-note without trailing :G() + currentAtom = this->findAtomByName(symString); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + if ( stab.atom == NULL ) { + // ld_classic added bogus GSYM stabs for old style dtrace probes + if ( (strncmp(symString, "__dtrace_probe$", 15) != 0) ) + warning("can't find atom for N_GSYM stabs %s in %s", symString, _path); + useStab = false; + } + break; + } + case N_FUN: + if ( isConstFunStabs(symString) ) { + // constant not associated with a function + stab.string = symString; + } + else { + // old style stabs without BNSYM + state = inFun; + currentAtomAddress = sym.n_value(); + currentAtom = this->findAtomByAddress(currentAtomAddress); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, _path); + } + } + break; + case N_SOL: + case N_SLINE: + stab.string = symString; + // old stabs + break; + case N_BINCL: + case N_EINCL: + case N_EXCL: + stab.string = symString; + // -gfull built .o file + break; + default: + warning("unknown stabs type 0x%X in %s", type, _path); + } + break; + case inBeginEnd: + stab.atom = currentAtom; + switch (type) { + case N_ENSYM: + state = start; + currentAtom = NULL; + break; + case N_LCSYM: + case N_STSYM: + { + Atom* nestedAtom = this->findAtomByAddress(sym.n_value()); + if ( nestedAtom != NULL ) { + stab.atom = nestedAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs 0x%X at %08llX in %s", + type, (uint64_t)sym.n_value(), _path); + } + break; + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + default: + stab.string = symString; + break; + } + break; + case inFun: + switch (type) { + case N_FUN: + if ( isConstFunStabs(symString) ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + if ( sym.n_sect() != 0 ) { + // found another start stab, must be really old stabs... + currentAtomAddress = sym.n_value(); + currentAtom = this->findAtomByAddress(currentAtomAddress); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, _path); + } + } + else { + // found ending stab, switch back to start state + stab.string = symString; + stab.atom = currentAtom; + state = start; + currentAtom = NULL; + } + } + break; + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + stab.atom = currentAtom; + break; + case N_SO: + stab.string = symString; + state = start; + break; + default: + stab.atom = currentAtom; + stab.string = symString; + break; + } + break; + } + // add to list of stabs for this .o file + if ( useStab ) + _file->_stabs.push_back(stab); + } + } +} + + + +// Look at the compilation unit DIE and determine +// its NAME, compilation directory (in COMP_DIR) and its +// line number information offset (in STMT_LIST). NAME and COMP_DIR +// may be NULL (especially COMP_DIR) if they are not in the .o file; +// STMT_LIST will be (uint64_t) -1. +// +// At present this assumes that there's only one compilation unit DIE. +// +template +bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list) +{ + const uint8_t * debug_info; + const uint8_t * debug_abbrev; + const uint8_t * di; + const uint8_t * da; + const uint8_t * end; + const uint8_t * enda; + uint64_t sz; + uint16_t vers; + uint64_t abbrev_base; + uint64_t abbrev; + uint8_t address_size; + bool dwarf64; + + *name = NULL; + *comp_dir = NULL; + *stmt_list = (uint64_t) -1; + + if ( (_file->_dwarfDebugInfoSect == NULL) || (_file->_dwarfDebugAbbrevSect == NULL) ) + return false; + + debug_info = (uint8_t*)_file->fileContent() + _file->_dwarfDebugInfoSect->offset(); + debug_abbrev = (uint8_t*)_file->fileContent() + _file->_dwarfDebugAbbrevSect->offset(); + di = debug_info; + + if (_file->_dwarfDebugInfoSect->size() < 12) + /* Too small to be a real debug_info section. */ + return false; + sz = A::P::E::get32(*(uint32_t*)di); + di += 4; + dwarf64 = sz == 0xffffffff; + if (dwarf64) + sz = A::P::E::get64(*(uint64_t*)di), di += 8; + else if (sz > 0xffffff00) + /* Unknown dwarf format. */ + return false; + + /* Verify claimed size. */ + if (sz + (di - debug_info) > _file->_dwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + return false; + + vers = A::P::E::get16(*(uint16_t*)di); + if (vers < 2 || vers > 3) + /* DWARF version wrong for this code. + Chances are we could continue anyway, but we don't know for sure. */ + return false; + di += 2; + + /* Find the debug_abbrev section. */ + abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); + di += dwarf64 ? 8 : 4; + + if (abbrev_base > _file->_dwarfDebugAbbrevSect->size()) + return false; + da = debug_abbrev + abbrev_base; + enda = debug_abbrev + _file->_dwarfDebugAbbrevSect->size(); + + address_size = *di++; + + /* Find the abbrev number we're looking for. */ + end = di + sz; + abbrev = read_uleb128 (&di, end); + if (abbrev == (uint64_t) -1) + return false; + + /* Skip through the debug_abbrev section looking for that abbrev. */ + for (;;) + { + uint64_t this_abbrev = read_uleb128 (&da, enda); + uint64_t attr; + + if (this_abbrev == abbrev) + /* This is almost always taken. */ + break; + skip_leb128 (&da, enda); /* Skip the tag. */ + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + do { + attr = read_uleb128 (&da, enda); + skip_leb128 (&da, enda); + } while (attr != 0 && attr != (uint64_t) -1); + if (attr != 0) + return false; + } + + /* Check that the abbrev is one for a DW_TAG_compile_unit. */ + if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) + return false; + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + /* Now, go through the DIE looking for DW_AT_name, + DW_AT_comp_dir, and DW_AT_stmt_list. */ + for (;;) + { + uint64_t attr = read_uleb128 (&da, enda); + uint64_t form = read_uleb128 (&da, enda); + + if (attr == (uint64_t) -1) + return false; + else if (attr == 0) + return true; + + if (form == DW_FORM_indirect) + form = read_uleb128 (&di, end); + + if (attr == DW_AT_name) + *name = getDwarfString(form, di); + else if (attr == DW_AT_comp_dir) + *comp_dir = getDwarfString(form, di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) + *stmt_list = A::P::E::get32(*(uint32_t*)di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) + *stmt_list = A::P::E::get64(*(uint64_t*)di); + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } +} + + + +template +File::~File() +{ + free(_sectionsArray); + free(_atomsArray); +} + +template +bool File::translationUnitSource(const char** dir, const char** name) const +{ + if ( _debugInfoKind == ld::relocatable::File::kDebugInfoDwarf ) { + *dir = _dwarfTranslationUnitDir; + *name = _dwarfTranslationUnitFile; + return (_dwarfTranslationUnitFile != NULL); + } + return false; +} + + + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + uint8_t* p = _atomsArray; + for(int i=_atomsArrayCount; i > 0; --i) { + handler.doAtom(*((Atom*)p)); + p += sizeof(Atom); + } + return (_atomsArrayCount != 0); +} + +template +const char* Section::makeSegmentName(const macho_section* sect) +{ + // mach-o section record only has room for 16-byte seg/sect names + // so a 16-byte name has no trailing zero + const char* name = sect->segname(); + if ( strlen(name) < 16 ) + return name; + char* tmp = new char[17]; + strlcpy(tmp, name, 17); + return tmp; +} + +template +const char* Section::makeSectionName(const macho_section* sect) +{ + const char* name = sect->sectname(); + if ( strlen(name) < 16 ) + return name; + + // special case common long section names so we don't have to malloc + if ( strncmp(sect->sectname(), "__objc_classrefs", 16) == 0 ) + return "__objc_classrefs"; + if ( strncmp(sect->sectname(), "__objc_classlist", 16) == 0 ) + return "__objc_classlist"; + if ( strncmp(sect->sectname(), "__objc_nlclslist", 16) == 0 ) + return "__objc_nlclslist"; + if ( strncmp(sect->sectname(), "__objc_nlcatlist", 16) == 0 ) + return "__objc_nlcatlist"; + if ( strncmp(sect->sectname(), "__objc_protolist", 16) == 0 ) + return "__objc_protolist"; + if ( strncmp(sect->sectname(), "__objc_protorefs", 16) == 0 ) + return "__objc_protorefs"; + if ( strncmp(sect->sectname(), "__objc_superrefs", 16) == 0 ) + return "__objc_superrefs"; + if ( strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0 ) + return "__objc_imageinfo"; + if ( strncmp(sect->sectname(), "__objc_stringobj", 16) == 0 ) + return "__objc_stringobj"; + if ( strncmp(sect->sectname(), "__gcc_except_tab", 16) == 0 ) + return "__gcc_except_tab"; + + char* tmp = new char[17]; + strlcpy(tmp, name, 17); + return tmp; +} + +template +bool Section::readable(const macho_section* sect) +{ + return true; +} + +template +bool Section::writable(const macho_section* sect) +{ + // mach-o .o files do not contain segment permissions + // we just know TEXT is special + return ( strcmp(sect->segname(), "__TEXT") != 0 ); +} + +template +bool Section::exectuable(const macho_section* sect) +{ + // mach-o .o files do not contain segment permissions + // we just know TEXT is special + return ( strcmp(sect->segname(), "__TEXT") == 0 ); +} + + +template +ld::Section::Type Section::sectionType(const macho_section* sect) +{ + switch ( sect->flags() & SECTION_TYPE ) { + case S_ZEROFILL: + return ld::Section::typeZeroFill; + case S_CSTRING_LITERALS: + if ( (strcmp(sect->sectname(), "__cstring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) + return ld::Section::typeCString; + else + return ld::Section::typeNonStdCString; + case S_4BYTE_LITERALS: + return ld::Section::typeLiteral4; + case S_8BYTE_LITERALS: + return ld::Section::typeLiteral8; + case S_LITERAL_POINTERS: + return ld::Section::typeCStringPointer; + case S_NON_LAZY_SYMBOL_POINTERS: + return ld::Section::typeNonLazyPointer; + case S_LAZY_SYMBOL_POINTERS: + return ld::Section::typeLazyPointer; + case S_SYMBOL_STUBS: + return ld::Section::typeStub; + case S_MOD_INIT_FUNC_POINTERS: + return ld::Section::typeInitializerPointers; + case S_MOD_TERM_FUNC_POINTERS: + return ld::Section::typeTerminatorPointers; + case S_INTERPOSING: + return ld::Section::typeUnclassified; + case S_16BYTE_LITERALS: + return ld::Section::typeLiteral16; + case S_REGULAR: + case S_COALESCED: + if ( sect->flags() & S_ATTR_PURE_INSTRUCTIONS ) { + return ld::Section::typeCode; + } + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { + if ( strcmp(sect->sectname(), "__eh_frame") == 0 ) + return ld::Section::typeCFI; + else if ( strcmp(sect->sectname(), "__ustring") == 0 ) + return ld::Section::typeUTF16Strings; + else if ( strcmp(sect->sectname(), "__textcoal_nt") == 0 ) + return ld::Section::typeCode; + else if ( strcmp(sect->sectname(), "__StaticInit") == 0 ) + return ld::Section::typeCode; + } + else if ( strcmp(sect->segname(), "__DATA") == 0 ) { + if ( strcmp(sect->sectname(), "__cfstring") == 0 ) + return ld::Section::typeCFString; + else if ( strcmp(sect->sectname(), "__dyld") == 0 ) + return ld::Section::typeDyldInfo; + else if ( strcmp(sect->sectname(), "__program_vars") == 0 ) + return ld::Section::typeDyldInfo; + else if ( strncmp(sect->sectname(), "__objc_classrefs", 16) == 0 ) + return ld::Section::typeObjCClassRefs; + else if ( strcmp(sect->sectname(), "__objc_catlist") == 0 ) + return ld::Section::typeObjC2CategoryList; + } + else if ( strcmp(sect->segname(), "__OBJC") == 0 ) { + if ( strcmp(sect->sectname(), "__class") == 0 ) + return ld::Section::typeObjC1Classes; + } + break; + case S_THREAD_LOCAL_REGULAR: + return ld::Section::typeTLVInitialValues; + case S_THREAD_LOCAL_ZEROFILL: + return ld::Section::typeTLVZeroFill; + case S_THREAD_LOCAL_VARIABLES: + return ld::Section::typeTLVDefs; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + return ld::Section::typeTLVInitializerPointers; + } + return ld::Section::typeUnclassified; +} + + +template +Atom* Section::findContentAtomByAddress(pint_t addr, class Atom* start, class Atom* end) +{ + // do a binary search of atom array + uint32_t atomCount = end - start; + Atom* base = start; + for (uint32_t n = atomCount; n > 0; n /= 2) { + Atom* pivot = &base[n/2]; + pint_t atomStartAddr = pivot->_objAddress; + pint_t atomEndAddr = atomStartAddr + pivot->_size; + if ( atomStartAddr <= addr ) { + // address in normal atom + if (addr < atomEndAddr) + return pivot; + // address in "end" label (but not in alias) + if ( (pivot->_size == 0) && (addr == atomEndAddr) && !pivot->isAlias() ) + return pivot; + } + if ( addr >= atomEndAddr ) { + // key > pivot + // move base to atom after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + +template +ld::Atom::Alignment Section::alignmentForAddress(pint_t addr) +{ + const uint32_t sectionAlignment = this->_machOSection->align(); + return ld::Atom::Alignment(sectionAlignment, (addr % (1 << sectionAlignment))); +} + +template +uint32_t Section::sectionNum(class Parser& parser) const +{ + if ( _machOSection == NULL ) + return 0; + else + return 1 + (this->_machOSection - parser.firstMachOSection()); +} + +// libunwind does not support ppc64 +template <> uint32_t CFISection::cfiCount() { return 0; } +// arm does not have zero cost exceptions +template <> uint32_t CFISection::cfiCount() { return 0; } + +template +uint32_t CFISection::cfiCount() +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + return libunwind::CFI_Parser::getCFICount(oas, + this->_machOSection->addr(), this->_machOSection->size()); +} + +template +void CFISection::warnFunc(void* ref, uint64_t funcAddr, const char* msg) +{ + Parser* parser = (Parser*)ref; + if ( ! parser->convertUnwindInfo() ) + return; + if ( funcAddr != CFI_INVALID_ADDRESS ) { + // atoms are not constructed yet, so scan symbol table for labels + const char* name = parser->scanSymbolTableForAddress(funcAddr); + warning("could not create compact unwind for %s: %s", name, msg); + } + else { + warning("could not create compact unwind: %s", msg); + } +} + +template <> +bool CFISection::needsRelocating() +{ + return true; +} + +template +bool CFISection::needsRelocating() +{ + return false; +} + +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // copy __eh_frame data to buffer + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + + // and apply relocations + const macho_relocation_info

* relocs = (macho_relocation_info

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

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + uint64_t value = 0; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SUBTRACTOR: + value = 0 - parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + ++reloc; + if ( reloc->r_extern() ) + value += parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case X86_64_RELOC_UNSIGNED: + value = parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case X86_64_RELOC_GOT: + // this is used for the reference to the personality function in CIEs + // store the symbol number of the personality function for later use as a Fixup + value = reloc->r_symbolnum(); + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address()); + break; + } + uint64_t* p64; + uint32_t* p32; + switch ( reloc->r_length() ) { + case 3: + p64 = (uint64_t*)&buffer[reloc->r_address()]; + E::set64(*p64, value + E::get64(*p64)); + break; + case 2: + p32 = (uint32_t*)&buffer[reloc->r_address()]; + E::set32(*p32, value + E::get32(*p32)); + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation size at r_address=0x%08X\n", reloc->r_address()); + break; + } + } + + + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, buffer); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} + +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} + + +// need to change libunwind parseCFIs() to work for ppc +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} + +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // libunwind does not support ppc64 + assert(count == 0); +} + +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // arm does not use zero cost exceptions + assert(count == 0); +} + + + +template +uint32_t CFISection::computeAtomCount(class Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray& cfis) +{ + return cfis.count; +} + + + +template +uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray& 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]; + 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, + ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeCFI, ld::Atom::symbolTableNotInFinalLinkedImages, + false, false, false, ld::Atom::Alignment(0)); + p += sizeof(Atom); + } + this->_endAtoms = (Atom*)p; + return cfis.count; +} + + +template <> bool CFISection::bigEndian() { return false; } +template <> bool CFISection::bigEndian() { return false; } +template <> bool CFISection::bigEndian() { return false; } +template <> bool CFISection::bigEndian() { return true; } +template <> bool CFISection::bigEndian() { return true; } + + +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( personalityEncoding == 0x9B ) { + // compiler always produces X86_64_RELOC_GOT with addend of 4 to personality function + // CFISection::cfiParse() set targetAddress to be symbolIndex + 4 + addressInCIE + uint32_t symbolIndex = cieInfo->u.cieInfo.personality.targetAddress - 4 + - cieInfo->address - cieInfo->u.cieInfo.personality.offsetInCFI; + const macho_nlist

& sym = parser.symbolFromIndex(symbolIndex); + const char* personalityName = parser.nameFromSymbol(sym); + + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, false, personalityName); + parser.addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, 4); + parser.addFixup(src, ld::Fixup::k3of3, ld::Fixup::kindStoreX86PCRel32GOT); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} + +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", personalityEncoding); + } +} + + +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreBigEndian32); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} + + +template +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + // FIX ME + assert(0); +} + +template +void CFISection::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray& 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) { + if ( p->isCIE ) { + // add reference to personality function if used + if ( p->u.cieInfo.personality.targetAddress != CFI_INVALID_ADDRESS ) { + this->addCiePersonalityFixups(parser, p); + } + } + else { + // find FDE Atom + Atom* fdeAtom = this->findAtomByAddress(p->address); + // find function Atom + Atom* functionAtom = parser.findAtomByAddress(p->u.fdeInfo.function.targetAddress); + // find CIE Atom + Atom* cieAtom = this->findAtomByAddress(p->u.fdeInfo.cie.targetAddress); + // find LSDA Atom + Atom* lsdaAtom = NULL; + if ( p->u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) { + lsdaAtom = parser.findAtomByAddress(p->u.fdeInfo.lsda.targetAddress); + } + // add reference from FDE to CIE (always 32-bit pc-rel) + typename Parser::SourceLocation fdeToCieSrc(fdeAtom, p->u.fdeInfo.cie.offsetInCFI); + parser.addFixup(fdeToCieSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, fdeAtom); + parser.addFixup(fdeToCieSrc, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, p->u.fdeInfo.cie.offsetInCFI); + parser.addFixup(fdeToCieSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(fdeToCieSrc, ld::Fixup::k4of4, store32, cieAtom); + + // add reference from FDE to function + typename Parser::SourceLocation fdeToFuncSrc(fdeAtom, p->u.fdeInfo.function.offsetInCFI); + switch (p->u.fdeInfo.function.encodingOfTargetAddress) { + case DW_EH_PE_pcrel|DW_EH_PE_ptr: + if ( sizeof(typename A::P::uint_t) == 8 ) { + parser.addFixup(fdeToFuncSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, functionAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.function.offsetInCFI); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k4of4, store64); + break; + } + // else fall into 32-bit case + case DW_EH_PE_pcrel|DW_EH_PE_sdata4: + parser.addFixup(fdeToFuncSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, functionAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.function.offsetInCFI); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k4of4, store32); + break; + default: + throw "unsupported encoding in FDE of pointer to function"; + } + + // add reference from FDE to LSDA + typename Parser::SourceLocation fdeToLsdaSrc(fdeAtom, p->u.fdeInfo.lsda.offsetInCFI); + if ( lsdaAtom != NULL ) { + switch (p->u.fdeInfo.lsda.encodingOfTargetAddress) { + case DW_EH_PE_pcrel|DW_EH_PE_ptr: + if ( sizeof(typename A::P::uint_t) == 8 ) { + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, lsdaAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.lsda.offsetInCFI); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k4of4, store64); + break; + } + // else fall into 32-bit case + case DW_EH_PE_pcrel|DW_EH_PE_sdata4: + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, lsdaAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.lsda.offsetInCFI); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k4of4, store32); + break; + default: + throw "unsupported encoding in FDE of pointer to LSDA"; + } + } + + // FDE is in group lead by function atom + typename Parser::SourceLocation fdeSrc(functionAtom,0); + parser.addFixup(fdeSrc, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateFDE, fdeAtom); + + // LSDA is in group lead by function atom + if ( lsdaAtom != NULL ) { + parser.addFixup(fdeSrc, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, lsdaAtom); + } + } + } +} + + + + +template +const void* CFISection::OAS::mappedAddress(pint_t addr) +{ + if ( (_ehFrameStartAddr <= addr) && (addr < _ehFrameEndAddr) ) + return &_ehFrameContent[addr-_ehFrameStartAddr]; + else { + // requested bytes are not in __eh_frame section + // this can occur when examining the instruction bytes in the __text + File& file = _ehFrameSection.file(); + for (uint32_t i=0; i < file._sectionsArrayCount; ++i ) { + const macho_section* sect = file._sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + return file.fileContent() + sect->offset() + addr - sect->addr(); + } + } + } + throwf("__eh_frame parsing problem. Can't find target of reference to address 0x%08llX", (uint64_t)addr); + } +} + + +template +uint64_t CFISection::OAS::getULEB128(pint_t& logicalAddr, pint_t end) +{ + uintptr_t size = (end - logicalAddr); + libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); + libunwind::LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = libunwind::LocalAddressSpace::getULEB128(laddr, laddr+size); + logicalAddr += (laddr-sladdr); + return result; +} + +template +int64_t CFISection::OAS::getSLEB128(pint_t& logicalAddr, pint_t end) +{ + uintptr_t size = (end - logicalAddr); + libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); + libunwind::LocalAddressSpace::pint_t sladdr = laddr; + int64_t result = libunwind::LocalAddressSpace::getSLEB128(laddr, laddr+size); + logicalAddr += (laddr-sladdr); + return result; +} + +template +typename A::P::uint_t CFISection::OAS::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + pint_t p = addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + throwf("ObjectFileAddressSpace::getEncodedP() encoding 0x%08X not supported", encoding); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + throw "DW_EH_PE_textrel pointer encoding not supported"; + break; + case DW_EH_PE_datarel: + throw "DW_EH_PE_datarel pointer encoding not supported"; + break; + case DW_EH_PE_funcrel: + throw "DW_EH_PE_funcrel pointer encoding not supported"; + break; + case DW_EH_PE_aligned: + throw "DW_EH_PE_aligned pointer encoding not supported"; + break; + default: + throwf("ObjectFileAddressSpace::getEncodedP() encoding 0x%08X not supported", encoding); + break; + } + +// Note: DW_EH_PE_indirect is only used in CIEs to refernce the personality pointer +// When parsing .o files that pointer contains zero, so we don't to return that. +// Instead we skip the dereference and return the address of the pointer. +// if ( encoding & DW_EH_PE_indirect ) +// result = getP(result); + + return result; +} + +template +SymboledSection::SymboledSection(Parser& parser, File& f, const macho_section* s) + : Section(f, s), _type(ld::Atom::typeUnclassified) +{ + switch ( s->flags() & SECTION_TYPE ) { + case S_ZEROFILL: + _type = ld::Atom::typeZeroFill; + break; + case S_MOD_INIT_FUNC_POINTERS: + _type = ld::Atom::typeInitializerPointers; + break; + case S_MOD_TERM_FUNC_POINTERS: + _type = ld::Atom::typeTerminatorPointers; + break; + case S_THREAD_LOCAL_VARIABLES: + _type = ld::Atom::typeTLV; + break; + case S_THREAD_LOCAL_ZEROFILL: + _type = ld::Atom::typeTLVZeroFill; + break; + case S_THREAD_LOCAL_REGULAR: + _type = ld::Atom::typeTLVInitialValue; + break; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + _type = ld::Atom::typeTLVInitializerPointers; + break; + case S_REGULAR: + if ( strncmp(s->sectname(), "__gcc_except_tab", 16) == 0 ) + _type = ld::Atom::typeLSDA; + break; + } +} + + +template +bool SymboledSection::dontDeadStrip() +{ + switch ( _type ) { + case ld::Atom::typeInitializerPointers: + case ld::Atom::typeTerminatorPointers: + return true; + default: + // model an object file without MH_SUBSECTIONS_VIA_SYMBOLS as one in which nothing can be dead stripped + if ( ! this->_file.canScatterAtoms() ) + return true; + // call inherited + return Section::dontDeadStrip(); + } + return false; +} + + +template +uint32_t SymboledSection::computeAtomCount(class Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + const uint32_t sectNum = this->sectionNum(parser); + + uint32_t count = 0; + pint_t addr; + pint_t size; + const macho_nlist

* sym; + while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &sym) ) { + ++count; + } + //fprintf(stderr, "computeAtomCount(%s,%s) => %d\n", this->segmentName(), this->sectionName(), count); + return count; +} + +template +uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + this->_beginAtoms = (Atom*)p; + + //fprintf(stderr, "SymboledSection::appendAtoms() in section %s\n", this->_machOSection->sectname()); + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + const uint32_t sectNum = this->sectionNum(parser); + + uint32_t count = 0; + pint_t addr; + pint_t size; + const macho_nlist

* label; + while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &label) ) { + Atom* allocatedSpace = (Atom*)p; + // is break because of label or CFI? + if ( label != NULL ) { + // The size is computed based on the address of the next label (or the end of the section for the last label) + // If there are two labels at the same address, we want them one to be an alias of the other. + // If the label is at the end of a section, it is has zero size, but is not an alias + const bool isAlias = ( (size == 0) && (addr < endAddr) ); + new (allocatedSpace) Atom(*this, parser, *label, size, isAlias); + if ( isAlias ) + this->_hasAliases = true; + } + else { + new (allocatedSpace) Atom(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, this->contentType(), ld::Atom::symbolTableNotIn, + this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); + } + p += sizeof(Atom); + ++count; + } + + this->_endAtoms = (Atom*)p; + return count; +} + + +template +uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + uint32_t count = 0; + const macho_section

* sect = this->machoSection(); + const pint_t startAddr = sect->addr(); + const pint_t endAddr = startAddr + sect->size(); + for (pint_t addr = startAddr; addr < endAddr; addr += elementSizeAtAddress(addr) ) { + if ( useElementAt(parser, it, addr) ) + ++count; + } + if ( it.fileHasOverlappingSymbols && (sect->size() != 0) && (this->combine(parser, startAddr) == ld::Atom::combineByNameAndContent) ) { + // if there are multiple labels in this section for the same address, then clone them into multi atoms + pint_t prevSymbolAddr = (pint_t)(-1); + uint8_t prevSymbolSectNum = 0; + for(uint32_t i=0; i < it.sortedSymbolCount; ++i) { + const macho_nlist

& sym = parser.symbolFromIndex(it.sortedSymbolIndexes[i]); + const pint_t symbolAddr = sym.n_value(); + const pint_t symbolSectNum = sym.n_sect(); + if ( (symbolAddr == prevSymbolAddr) && (prevSymbolSectNum == symbolSectNum) && (symbolSectNum == this->sectionNum(parser)) ) { + ++count; + } + prevSymbolAddr = symbolAddr; + prevSymbolSectNum = symbolSectNum; + } + } + return count; +} + +template +uint32_t ImplicitSizeSection::appendAtoms(class Parser& parser, uint8_t* p, + struct Parser::LabelAndCFIBreakIterator& it, + const struct Parser::CFIInfoArray&) +{ + this->_beginAtoms = (Atom*)p; + + const macho_section

* sect = this->machoSection(); + const pint_t startAddr = sect->addr(); + const pint_t endAddr = startAddr + sect->size(); + const uint32_t sectNum = this->sectionNum(parser); + //fprintf(stderr, "ImplicitSizeSection::appendAtoms() in section %s\n", sect->sectname()); + uint32_t count = 0; + pint_t foundAddr; + pint_t size; + const macho_nlist

* foundLabel; + Atom* allocatedSpace; + while ( it.next(parser, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { + if ( foundLabel != NULL ) { + pint_t labeledAtomSize = this->elementSizeAtAddress(foundAddr); + allocatedSpace = (Atom*)p; + if ( this->ignoreLabel(parser.nameFromSymbol(*foundLabel)) ) { + //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)foundAddr); + new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, + this->elementSizeAtAddress(foundAddr), this->definition(), + this->combine(parser, foundAddr), this->scopeAtAddress(parser, foundAddr), + this->contentType(), this->symbolTableInclusion(), + this->dontDeadStrip(), false, false, this->alignmentForAddress(foundAddr)); + } + else { + // make named atom for label + //fprintf(stderr, " 0x%08llX make labeled\n", (uint64_t)foundAddr); + new (allocatedSpace) Atom(*this, parser, *foundLabel, labeledAtomSize); + } + ++count; + p += sizeof(Atom); + foundAddr += labeledAtomSize; + size -= labeledAtomSize; + } + // some number of anonymous atoms + for (pint_t addr = foundAddr; addr < (foundAddr+size); addr += elementSizeAtAddress(addr) ) { + // make anon atoms for area before label + if ( this->useElementAt(parser, it, addr) ) { + //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)addr); + allocatedSpace = (Atom*)p; + new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, addr), addr, this->elementSizeAtAddress(addr), + this->definition(), this->combine(parser, addr), this->scopeAtAddress(parser, addr), + this->contentType(), this->symbolTableInclusion(), + this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); + ++count; + p += sizeof(Atom); + } + } + } + + this->_endAtoms = (Atom*)p; + + return count; +} + + +template +unsigned long Literal4Section::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + const uint32_t* literalContent = (uint32_t*)atom->contentPointer(); + return *literalContent; +} + +template +bool Literal4Section::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + assert(this->type() == rhs.section().type()); + const uint32_t* literalContent = (uint32_t*)atom->contentPointer(); + + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + const uint32_t* rhsLiteralContent = (uint32_t*)rhsAtom->contentPointer(); + return (*literalContent == *rhsLiteralContent); + } + return false; +} + + +template +unsigned long Literal8Section::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ +#if __LP64__ + const uint64_t* literalContent = (uint64_t*)atom->contentPointer(); + return *literalContent; +#else + unsigned long hash = 5381; + const uint8_t* byteContent = atom->contentPointer(); + for (int i=0; i < 8; ++i) { + hash = hash * 33 + byteContent[i]; + } + return hash; +#endif +} + +template +bool Literal8Section::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeLiteral8 ) + return false; + assert(this->type() == rhs.section().type()); + const uint64_t* literalContent = (uint64_t*)atom->contentPointer(); + + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + const uint64_t* rhsLiteralContent = (uint64_t*)rhsAtom->contentPointer(); + return (*literalContent == *rhsLiteralContent); + } + return false; +} + + +template +unsigned long Literal16Section::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 5381; + const uint8_t* byteContent = atom->contentPointer(); + for (int i=0; i < 16; ++i) { + hash = hash * 33 + byteContent[i]; + } + return hash; +} + +template +bool Literal16Section::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeLiteral16 ) + return false; + assert(this->type() == rhs.section().type()); + const uint64_t* literalContent = (uint64_t*)atom->contentPointer(); + + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + const uint64_t* rhsLiteralContent = (uint64_t*)rhsAtom->contentPointer(); + return ((literalContent[0] == rhsLiteralContent[0]) && (literalContent[1] == rhsLiteralContent[1])); + } + return false; +} + + + +template +typename A::P::uint_t CStringSection::elementSizeAtAddress(pint_t addr) +{ + const macho_section

* sect = this->machoSection(); + const char* stringContent = (char*)(this->file().fileContent() + sect->offset() + addr - sect->addr()); + return strlen(stringContent) + 1; +} + +template +bool CStringSection::useElementAt(Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, pint_t addr) +{ + return true; +} + +template +Atom* CStringSection::findAtomByAddress(pint_t addr) +{ + Atom* result = this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); + return result; +} + +template +unsigned long CStringSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 5381; + const char* stringContent = (char*)atom->contentPointer(); + for (const char* s = stringContent; *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + + +template +bool CStringSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeCString ) + return false; + assert(this->type() == rhs.section().type()); + assert(strcmp(this->sectionName(), rhs.section().sectionName())== 0); + assert(strcmp(this->segmentName(), rhs.section().segmentName())== 0); + const char* stringContent = (char*)atom->contentPointer(); + + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + if ( atom->_size != rhsAtom->_size ) + return false; + const char* rhsStringContent = (char*)rhsAtom->contentPointer(); + return (strcmp(stringContent, rhsStringContent) == 0); + } + return false; +} + + +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreBigEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreBigEndian64; +} + +template <> +void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) +{ + 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&) +{ + // add references for each NLP atom based on indirect symbol table + const macho_section

* sect = this->machoSection(); + const pint_t endAddr = sect->addr() + sect->size(); + for( pint_t addr = sect->addr(); addr < endAddr; addr += sizeof(pint_t)) { + typename Parser::SourceLocation src; + typename Parser::TargetDesc target; + src.atom = this->findAtomByAddress(addr); + src.offsetInAtom = 0; + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + target.atom = NULL; + target.name = NULL; + target.weakImport = false; + target.addend = 0; + if ( symIndex == INDIRECT_SYMBOL_LOCAL ) { + // use direct reference for local symbols + const pint_t* nlpContent = (pint_t*)(this->file().fileContent() + sect->offset() + addr - sect->addr()); + pint_t targetAddr = P::getP(*nlpContent); + target.atom = parser.findAtomByAddress(targetAddr); + target.weakImport = false; + target.addend = (targetAddr - target.atom->objectAddress()); + // if pointer to thumb function, mask of thumb bit (not an addend of +1) + if ( target.atom->isThumb() ) + target.addend &= (-2); + assert(src.atom->combine() == ld::Atom::combineNever); + } + else { + const macho_nlist

& sym = parser.symbolFromIndex(symIndex); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + assert(src.atom->combine() == ld::Atom::combineNever); + } + else { + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + assert(src.atom->combine() == ld::Atom::combineByNameAndReferences); + } + } + parser.addFixups(src, this->fixupKind(), target); + } +} + +template +ld::Atom::Combine NonLazyPointerSection::combine(Parser& parser, pint_t addr) +{ + const macho_section

* sect = this->machoSection(); + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + if ( symIndex == INDIRECT_SYMBOL_LOCAL) + return ld::Atom::combineNever; + + // don't coalesce non-lazy-pointers to local symbols + const macho_nlist

& sym = parser.symbolFromIndex(symIndex); + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) + return ld::Atom::combineNever; + + return ld::Atom::combineByNameAndReferences; +} + +template +const char* NonLazyPointerSection::targetName(const class Atom* atom, const ld::IndirectBindingTable& ind) +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + assert(atom->fixupCount() == 1); + ld::Fixup::iterator fit = atom->fixupsBegin(); + const char* name = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + name = fit->u.name; + break; + case ld::Fixup::bindingByContentBound: + name = fit->u.target->name(); + break; + case ld::Fixup::bindingsIndirectlyBound: + name = ind.indirectName(fit->u.bindingIndex); + break; + default: + assert(0); + } + assert(name != NULL); + return name; +} + +template +unsigned long NonLazyPointerSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + unsigned long hash = 9508; + for (const char* s = this->targetName(atom, ind); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template +bool NonLazyPointerSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + if ( rhs.section().type() != ld::Section::typeNonLazyPointer ) + return false; + assert(this->type() == rhs.section().type()); + // there can be many non-lazy pointer in different section names + // we only want to coalesce in same section name + if ( *this != rhs.section() ) + return false; + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + const char* thisName = this->targetName(atom, indirectBindingTable); + const char* rhsName = this->targetName(rhsAtom, indirectBindingTable); + return (strcmp(thisName, rhsName) == 0); +} + +template +ld::Atom::Scope NonLazyPointerSection::scopeAtAddress(Parser& parser, pint_t addr) +{ + const macho_section

* sect = this->machoSection(); + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + if ( symIndex == INDIRECT_SYMBOL_LOCAL) + return ld::Atom::scopeTranslationUnit; + else + return ld::Atom::scopeLinkageUnit; +} + + +template +const uint8_t* CFStringSection::targetContent(const class Atom* atom, const ld::IndirectBindingTable& ind, + ContentType* ct, unsigned int* count) +{ + *ct = contentUnknown; + for (ld::Fixup::iterator fit=atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const ld::Atom* targetAtom = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + // ignore reference to ___CFConstantStringClassReference + // we are just looking for reference to backing string data + assert(fit->offsetInAtom == 0); + assert(strcmp(fit->u.name, "___CFConstantStringClassReference") == 0); + break; + case ld::Fixup::bindingDirectlyBound: + case ld::Fixup::bindingByContentBound: + targetAtom = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + targetAtom = ind.indirectAtom(fit->u.bindingIndex); + break; + default: + assert(0 && "bad binding type"); + } + assert(targetAtom != NULL); + const Atom* target = dynamic_cast*>(targetAtom); + if ( targetAtom->section().type() == ld::Section::typeCString ) { + *ct = contentUTF8; + *count = targetAtom->size(); + } + else if ( targetAtom->section().type() == ld::Section::typeUTF16Strings ) { + *ct = contentUTF16; + *count = (targetAtom->size()+1)/2; // round up incase of buggy compiler that has only one trailing zero byte + } + assert(target != NULL); + return target->contentPointer(); + } + assert(0); + return NULL; +} + +template +unsigned long CFStringSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + // base hash of CFString on hash of cstring it wraps + ContentType cType; + unsigned long hash; + unsigned int charCount; + const uint8_t* content = this->targetContent(atom, ind, &cType, &charCount); + switch ( cType ) { + case contentUTF8: + hash = 9408; + for (const char* s = (char*)content; *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; + case contentUTF16: + hash = 407955; + --charCount; // don't add last 0x0000 to hash because some buggy compilers only have trailing single byte + for (const uint16_t* s = (uint16_t*)content; charCount > 0; ++s, --charCount) { + hash = hash * 1025 + *s; + } + return hash; + case contentUnknown: + return 0; + } + return 0; +} + + +template +bool CFStringSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + if ( atom == &rhs ) + return true; + if ( rhs.section().type() != ld::Section::typeCFString) + return false; + assert(this->type() == rhs.section().type()); + assert(strcmp(this->sectionName(), "__cfstring") == 0); + + ContentType thisType; + unsigned int charCount; + const uint8_t* cstringContent = this->targetContent(atom, indirectBindingTable, &thisType, &charCount); + ContentType rhsType; + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + unsigned int rhsCharCount; + const uint8_t* rhsStringContent = this->targetContent(rhsAtom, indirectBindingTable, &rhsType, &rhsCharCount); + + if ( thisType != rhsType ) + return false; + + // no need to compare content of pointers are already the same + if ( cstringContent == rhsStringContent ) + return true; + + // no need to compare content if size is different + if ( charCount != rhsCharCount ) + return false; + + switch ( thisType ) { + case contentUTF8: + return (strcmp((char*)cstringContent, (char*)rhsStringContent) == 0); + case contentUTF16: + { + const uint16_t* cstringContent16 = (uint16_t*)cstringContent; + const uint16_t* rhsStringContent16 = (uint16_t*)rhsStringContent; + for (unsigned int i = 0; i < charCount; ++i) { + if ( cstringContent16[i] != rhsStringContent16[i] ) + return false; + } + return true; + } + case contentUnknown: + return false; + } + return false; +} + + +template +typename A::P::uint_t ObjC1ClassSection::elementSizeAtAddress(pint_t addr) +{ + // nominal size for each class is 48 bytes, but sometimes the compiler + // over aligns and there is padding after class data + const macho_section

* sct = this->machoSection(); + uint32_t align = 1 << sct->align(); + uint32_t size = ((12 * sizeof(pint_t)) + align-1) & (-align); + return size; +} + +template +const char* ObjC1ClassSection::unlabeledAtomName(Parser& parser, pint_t addr) +{ + // 8-bytes into class object is pointer to class name + const macho_section

* sct = this->machoSection(); + uint32_t classObjcFileOffset = sct->offset() - sct->addr() + addr; + const uint8_t* mappedFileContent = this->file().fileContent(); + pint_t nameAddr = P::getP(*((pint_t*)(mappedFileContent+classObjcFileOffset+2*sizeof(pint_t)))); + + // find section containing string address to get string bytes + const macho_section

* const sections = parser.firstMachOSection(); + const uint32_t sectionCount = parser.machOSectionCount(); + for (uint32_t i=0; i < sectionCount; ++i) { + const macho_section

* aSect = §ions[i]; + if ( (aSect->addr() <= nameAddr) && (nameAddr < (aSect->addr()+aSect->size())) ) { + assert((aSect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS); + uint32_t nameFileOffset = aSect->offset() - aSect->addr() + nameAddr; + const char* name = (char*)mappedFileContent + nameFileOffset; + // spin through symbol table to find absolute symbol corresponding to this class + for (uint32_t s=0; s < parser.symbolCount(); ++s) { + const macho_nlist

& sym = parser.symbolFromIndex(s); + if ( (sym.n_type() & N_TYPE) != N_ABS ) + continue; + const char* absName = parser.nameFromSymbol(sym); + if ( strncmp(absName, ".objc_class_name_", 17) == 0 ) { + if ( strcmp(&absName[17], name) == 0 ) + return absName; + } + } + assert(0 && "obj class name not found in symbol table"); + } + } + assert(0 && "obj class name not found"); + return "unknown objc class"; +} + + +template +const char* ObjC2ClassRefsSection::targetClassName(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->fixupCount() == 1); + ld::Fixup::iterator fit = atom->fixupsBegin(); + const char* className = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + className = fit->u.name; + break; + case ld::Fixup::bindingDirectlyBound: + case ld::Fixup::bindingByContentBound: + className = fit->u.target->name(); + break; + case ld::Fixup::bindingsIndirectlyBound: + className = ind.indirectName(fit->u.bindingIndex); + break; + default: + assert(0 && "unsupported binding in objc2 class ref section"); + } + assert(className != NULL); + return className; +} + + +template +unsigned long ObjC2ClassRefsSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 978; + for (const char* s = targetClassName(atom, ind); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template +bool ObjC2ClassRefsSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + assert(this->type() == rhs.section().type()); + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + const char* thisClassName = targetClassName(atom, indirectBindingTable); + const char* rhsClassName = targetClassName(rhsAtom, indirectBindingTable); + return (strcmp(thisClassName, rhsClassName) == 0); +} + + +template +const char* Objc1ClassReferences::targetCString(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->fixupCount() == 2); + ld::Fixup::iterator fit = atom->fixupsBegin(); + if ( fit->kind == ld::Fixup::kindSetTargetAddress ) + ++fit; + const ld::Atom* targetAtom = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByContentBound: + targetAtom = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + targetAtom = ind.indirectAtom(fit->u.bindingIndex); + if ( targetAtom == NULL ) { + fprintf(stderr, "missing target named %s\n", ind.indirectName(fit->u.bindingIndex)); + } + break; + default: + assert(0); + } + assert(targetAtom != NULL); + const Atom* target = dynamic_cast*>(targetAtom); + assert(target != NULL); + return (char*)target->contentPointer(); +} + + +template +const char* PointerToCStringSection::targetCString(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->fixupCount() == 1); + ld::Fixup::iterator fit = atom->fixupsBegin(); + const ld::Atom* targetAtom = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByContentBound: + targetAtom = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + targetAtom = ind.indirectAtom(fit->u.bindingIndex); + break; + default: + assert(0); + } + assert(targetAtom != NULL); + const Atom* target = dynamic_cast*>(targetAtom); + assert(target != NULL); + return (char*)target->contentPointer(); +} + +template +unsigned long PointerToCStringSection::contentHash(const class Atom* atom, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + // make hash from section name and target cstring name + unsigned long hash = 123; + for (const char* s = this->sectionName(); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + for (const char* s = this->targetCString(atom, indirectBindingTable); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template +bool PointerToCStringSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + assert(this->type() == rhs.section().type()); + // there can be pointers-to-cstrings in different section names + // we only want to coalesce in same section name + if ( *this != rhs.section() ) + return false; + + // get string content for this + const char* cstringContent = this->targetCString(atom, indirectBindingTable); + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + const char* rhsCstringContent = this->targetCString(rhsAtom, indirectBindingTable); + + assert(cstringContent != NULL); + assert(rhsCstringContent != NULL); + return (strcmp(cstringContent, rhsCstringContent) == 0); +} + + + +template +unsigned long UTF16StringSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 5381; + const uint16_t* stringContent = (uint16_t*)atom->contentPointer(); + // some buggy compilers end utf16 data with single byte, so don't use last word in hash computation + unsigned int count = (atom->size()/2) - 1; + for (const uint16_t* s = stringContent; count > 0; ++s, --count) { + hash = hash * 33 + *s; + } + return hash; +} + +template +bool UTF16StringSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeUTF16Strings ) + return false; + assert(0); + return false; +} + + + + + + + +template <> +uint32_t Section::x86_64PcRelOffset(uint8_t r_type) +{ + switch ( r_type ) { + case X86_64_RELOC_SIGNED: + return 4; + case X86_64_RELOC_SIGNED_1: + return 5; + case X86_64_RELOC_SIGNED_2: + return 6; + case X86_64_RELOC_SIGNED_4: + return 8; + } + return 0; +} + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + const macho_section

* sect = this->machoSection(); + uint64_t srcAddr = sect->addr() + reloc->r_address(); + Parser::SourceLocation src; + Parser::TargetDesc target; + Parser::TargetDesc toTarget; + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint64_t contentValue = 0; + const macho_relocation_info* nextReloc = &reloc[1]; + bool result = false; + bool useDirectBinding; + switch ( reloc->r_length() ) { + case 0: + contentValue = *fixUpPtr; + break; + case 1: + contentValue = (int64_t)(int16_t)E::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + contentValue = (int64_t)(int32_t)E::get32(*((uint32_t*)fixUpPtr)); + break; + case 3: + contentValue = E::get64(*((uint64_t*)fixUpPtr)); + break; + } + target.atom = NULL; + target.name = NULL; + target.weakImport = false; + target.addend = 0; + if ( reloc->r_extern() ) { + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(sym)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + target.addend += contentValue; + } + else { + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + target.addend = contentValue; + } + // cfstrings should always use direct reference to backing store + if ( (this->type() == ld::Section::typeCFString) && (src.offsetInAtom != 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + target.addend = contentValue; + } + } + else { + if ( reloc->r_pcrel() ) + contentValue += srcAddr + x86_64PcRelOffset(reloc->r_type()); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "length < 2 and X86_64_RELOC_UNSIGNED not supported"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian64, target); + break; + } + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32, target); + break; + case X86_64_RELOC_SIGNED_1: + if ( reloc->r_extern() ) + target.addend += 1; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32_1, target); + break; + case X86_64_RELOC_SIGNED_2: + if ( reloc->r_extern() ) + target.addend += 2; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32_2, target); + break; + case X86_64_RELOC_SIGNED_4: + if ( reloc->r_extern() ) + target.addend += 4; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32_4, target); + break; + } + break; + case X86_64_RELOC_BRANCH: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_BRANCH not supported"; + switch ( reloc->r_length() ) { + case 2: + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreX86BranchPCRel32, target); + } + break; + case 0: + parser.addFixups(src, ld::Fixup::kindStoreX86BranchPCRel8, target); + break; + default: + throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length()); + } + break; + case X86_64_RELOC_GOT: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT not supported"; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32GOT, target); + break; + case X86_64_RELOC_GOT_LOAD: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32GOTLoad, target); + break; + case X86_64_RELOC_SUBTRACTOR: + if ( reloc->r_pcrel() ) + throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; + if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; + if ( nextReloc->r_extern() ) { + const macho_nlist

& sym = parser.symbolFromIndex(nextReloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(sym)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), toTarget); + toTarget.addend = contentValue; + useDirectBinding = true; + } + else { + toTarget.name = parser.nameFromSymbol(sym); + toTarget.weakImport = parser.weakImportFromSymbol(sym); + toTarget.addend = contentValue; + useDirectBinding = false; + } + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget); + useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit); + } + if ( useDirectBinding ) + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); + else + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend); + if ( target.atom == NULL ) + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, false, target.name); + else + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, target.atom); + if ( reloc->r_length() == 2 ) + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + else + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian64); + break; + case X86_64_RELOC_TLV: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_TLV not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_TLV not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_TLV not supported"; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32TLVLoad, target); + break; + default: + throwf("unknown relocation type %d", reloc->r_type()); + } + return result; +} + + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + const macho_section

* sect = this->machoSection(); + uint32_t srcAddr; + const uint8_t* fixUpPtr; + uint32_t contentValue = 0; + ld::Fixup::Kind kind = ld::Fixup::kindNone; + Parser::SourceLocation src; + Parser::TargetDesc target; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + switch ( reloc->r_type() ) { + case GENERIC_RELOC_VANILLA: + switch ( reloc->r_length() ) { + case 0: + contentValue = (int32_t)(int8_t)*fixUpPtr; + if ( reloc->r_pcrel() ) { + kind = ld::Fixup::kindStoreX86BranchPCRel8; + contentValue += srcAddr + sizeof(uint8_t); + } + else + throw "r_length=0 and r_pcrel=0 not supported"; + break; + case 1: + contentValue = (int32_t)(int16_t)E::get16(*((uint16_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + kind = ld::Fixup::kindStoreX86PCRel16; + contentValue += srcAddr + sizeof(uint16_t); + } + else + kind = ld::Fixup::kindStoreLittleEndian16; + break; + case 2: + contentValue = E::get32(*((uint32_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + kind = ld::Fixup::kindStoreX86BranchPCRel32; + contentValue += srcAddr + sizeof(uint32_t); + } + else + kind = ld::Fixup::kindStoreLittleEndian32; + break; + case 3: + throw "r_length=3 not supported"; + } + 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); + target.addend = contentValue; + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + if ( (kind == ld::Fixup::kindStoreX86BranchPCRel32) && (target.name != NULL) ) { + if ( strncmp(target.name, "___dtrace_probe$", 16) == 0 ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + return false; + } + else if ( strncmp(target.name, "___dtrace_isenabled$", 20) == 0 ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + return false; + } + } + parser.addFixups(src, kind, target); + return false; + break; + case GENERIC_RLEOC_TLV: + { + if ( !reloc->r_extern() ) + throw "r_extern=0 and r_type=GENERIC_RLEOC_TLV not supported"; + if ( reloc->r_length() != 2 ) + throw "r_length!=2 and r_type=GENERIC_RLEOC_TLV not supported"; + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + } + else { + target.atom = NULL; + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + } + target.addend = (int64_t)(int32_t)E::get32(*((uint32_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32TLVLoad, target); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreX86Abs32TLVLoad, target); + } + return false; + } + break; + default: + throwf("unsupported i386 relocation type (%d)", reloc->r_type()); + } + } + else { + // scattered relocation + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + fixUpPtr = file().fileContent() + sect->offset() + sreloc->r_address(); + uint32_t relocValue = sreloc->r_value(); + bool result = false; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; // iterator should skip next reloc, since we've consumed it here + } + } + else { + if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + } + } + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + target.atom = parser.findAtomByAddress(relocValue); + if ( sreloc->r_pcrel() ) { + switch ( sreloc->r_length() ) { + case 0: + contentValue = srcAddr + 1 + *fixUpPtr; + target.addend = contentValue - relocValue; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel8, target); + break; + case 1: + contentValue = srcAddr + 2 + LittleEndian::get16(*((uint16_t*)fixUpPtr)); + target.addend = contentValue - relocValue; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel16, target); + break; + case 2: + contentValue = srcAddr + 4 + LittleEndian::get32(*((uint32_t*)fixUpPtr)); + target.addend = contentValue - relocValue; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32, target); + break; + case 3: + throw "unsupported r_length=3 for scattered pc-rel vanilla reloc"; + break; + } + } + else { + if ( sreloc->r_length() != 2 ) + throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length()); + contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + target.addend = contentValue - target.atom->objectAddress(); + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + } + break; + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + { + if ( !nextRelocIsPair ) + throw "GENERIC_RELOC_SECTDIFF missing following pair"; + switch ( sreloc->r_length() ) { + case 0: + case 3: + throw "bad length for GENERIC_RELOC_SECTDIFF"; + case 1: + contentValue = (int32_t)(int16_t)LittleEndian::get16(*((uint16_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreLittleEndian16; + break; + case 2: + contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreLittleEndian32; + break; + } + Atom* fromAtom = parser.findAtomByAddress(nextRelocValue); + 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); + if ( addend < 0 ) { + // switch binding base on coalescing + if ( target.atom == NULL ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.name); + } + else if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, target.atom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.atom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, target.addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom-addend); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + else { + // switch binding base on coalescing + if ( target.atom == NULL ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.name); + } + else if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, target.atom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.atom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, target.addend+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + } + break; + } + return result; + } +} + + + +// +// ppc and ppc64 both use the same relocations, so process them in one common routine +// +template +bool Section::addRelocFixup_powerpc(class Parser& parser, + const macho_relocation_info* reloc) +{ + const macho_section

* sect = this->machoSection(); + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + int16_t lowBits; + pint_t contentValue = 0; + typename Parser::SourceLocation src; + typename Parser::TargetDesc target; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const macho_relocation_info

* nextReloc = &reloc[1]; + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != PPC_RELOC_PAIR ) + instruction = BigEndian::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); + } + switch ( reloc->r_type() ) { + case PPC_RELOC_BR24: + assert((instruction & 0x4C000000) == 0x48000000); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStorePPCDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStorePPCDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + } + break; + case PPC_RELOC_BR14: + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_LO16 missing following pair"; + result = true; + lowBits = (instruction & 0x0000FFFF); + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); + break; + case PPC_RELOC_LO14: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_LO14 missing following pair"; + result = true; + lowBits = (instruction & 0xFFFC); + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); + break; + case PPC_RELOC_HI16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_HI16 missing following pair"; + result = true; + lowBits = (nextReloc->r_address() & 0xFFFF); + dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); + break; + case PPC_RELOC_HA16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_HA16 missing following pair"; + result = true; + lowBits = (nextReloc->r_address() & 0x0000FFFF); + dstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); + break; + case PPC_RELOC_VANILLA: + contentValue = P::getP(*((pint_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + target.addend = contentValue; + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "bad r_length in PPC_RELOC_VANILLA"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); + break; + } + break; + case PPC_RELOC_JBSR: + // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_JBSR missing following pair"; + if ( !parser._hasLongBranchStubs ) + warning("object file compiled with -mlong-branch which is no longer needed. " + "To remove this warning, recompile without -mlong-branch: %s", parser._path); + parser._hasLongBranchStubs = true; + result = true; + if ( reloc->r_extern() ) { + throw "PPC_RELOC_JBSR should not be using an external relocation"; + } + parser.findTargetFromAddressAndSectionNum(nextReloc->r_address(), reloc->r_symbolnum(), target); + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + sreloc->r_address()); + instruction = BigEndian::get32(*fixUpPtr); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + typename Parser::TargetDesc picBase; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch ( sreloc->r_type() ) { + case PPC_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + target.atom = parser.findAtomByAddress(sreloc->r_value()); + switch ( sreloc->r_length() ) { + case 0: + case 1: + throw "unsuppored r_length < 2 for scattered PPC_RELOC_VANILLA"; + case 2: + contentValue = BigEndian::get32(*(uint32_t*)fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); + break; + case 3: + contentValue = BigEndian::get64(*(uint64_t*)fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); + break; + } + break; + case PPC_RELOC_BR14: + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (srcAddr + displacement) - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); + break; + case PPC_RELOC_BR24: + assert((instruction & 0x4C000000) == 0x48000000); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (srcAddr + displacement) - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + break; + case PPC_RELOC_LO16_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; + lowBits = (instruction & 0xFFFF); + dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicLow16, target, picBase); + break; + case PPC_RELOC_LO14_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; + lowBits = (instruction & 0xFFFC); + dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicLow14, target, picBase); + break; + case PPC_RELOC_HA16_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; + lowBits = (nextRelocAddress & 0x0000FFFF); + dstAddr = nextRelocValue + (((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicHigh16AddLow, target, picBase); + break; + case PPC_RELOC_LO14: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO14 missing following pair"; + lowBits = (instruction & 0xFFFC); + dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); + break; + case PPC_RELOC_LO16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO16 missing following pair"; + lowBits = (instruction & 0xFFFF); + dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); + break; + case PPC_RELOC_HA16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HA16 missing following pair"; + lowBits = (nextRelocAddress & 0xFFFF); + dstAddr = (((instruction & 0xFFFF) << 16) + (int32_t)lowBits); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); + break; + case PPC_RELOC_HI16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HI16 missing following pair"; + lowBits = (nextRelocAddress & 0xFFFF); + dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_SECTDIFF missing following pair"; + ld::Fixup::Kind kind = ld::Fixup::kindNone; + switch ( sreloc->r_length() ) { + case 0: + throw "bad length for PPC_RELOC_SECTDIFF"; + case 1: + contentValue = (int32_t)(int16_t)BigEndian::get16(*((uint16_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian16; + break; + case 2: + contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian32; + break; + case 3: + contentValue = BigEndian::get64(*((uint64_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian64; + break; + break; + } + Atom* fromAtom = parser.findAtomByAddress(nextRelocValue); + Atom* targetAtom = parser.findAtomByAddress(sreloc->r_value()); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress; + // check for addend encoded in the section content + int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + if ( addend < 0 ) { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + 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); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + else { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + return addRelocFixup_powerpc(parser, reloc); +} + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + return addRelocFixup_powerpc(parser, reloc); +} + + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + const macho_section

* sect = this->machoSection(); + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + pint_t contentValue = 0; + Parser::SourceLocation src; + Parser::TargetDesc target; + const macho_relocation_info

* nextReloc; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + bool externSymbolIsThumbDef = false; + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + reloc->r_address()); + 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; + } + switch ( reloc->r_type() ) { + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + if ( externSymbolIsThumbDef ) + target.addend &= -2; // remove thumb bit + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreARMDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreARMDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreARMBranch24, target); + } + break; + case ARM_THUMB_RELOC_BR22: + // thumb2 added two more bits to displacement, complicating the displacement decoding + { + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + if ( s ) + sdis |= 0xFE000000; + displacement = sdis; + } + // The pc added will be +4 from the pc + displacement += 4; + // If the instruction was blx, force the low 2 bits to be clear + dstAddr = srcAddr + displacement; + if ((instruction & 0xF8000000) == 0xE8000000) + dstAddr &= 0xFFFFFFFC; + + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreThumbDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreThumbBranch22, target); + } + break; + case ARM_RELOC_VANILLA: + if ( reloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + contentValue = LittleEndian::get32(*fixUpPtr); + if ( reloc->r_extern() ) { + target.addend = contentValue; + if ( externSymbolIsThumbDef ) + target.addend &= -2; // remove thumb bit + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + // possible non-extern relocation turned into by-name ref because target is a weak-def + if ( target.atom != NULL ) { + if ( target.atom->isThumb() ) + target.addend &= -2; // remove thumb bit + // if reference to LSDA, add group subordinate fixup + if ( target.atom->contentType() == ld::Atom::typeLSDA ) { + Parser::SourceLocation src2; + src2.atom = src.atom; + src2.offsetInAtom = 0; + parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, target.atom); + } + } + } + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case ARM_THUMB_32BIT_BRANCH: + // silently ignore old unnecessary reloc + break; + case ARM_RELOC_HALF: + nextReloc = &reloc[1]; + if ( nextReloc->r_type() == ARM_RELOC_PAIR ) { + uint32_t instruction16; + uint32_t other16 = (nextReloc->r_address() & 0xFFFF); + bool isThumb; + if ( reloc->r_length() & 2 ) { + isThumb = true; + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + instruction16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + } + else { + isThumb = false; + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + instruction16 = (imm4 << 12) | imm12; + } + if ( reloc->r_length() & 1 ) { + // high 16 + dstAddr = ((instruction16 << 16) | other16); + parser.findTargetFromAddress(dstAddr, target); + parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16), target); + } + else { + // low 16 + dstAddr = (other16 << 16) | instruction16; + parser.findTargetFromAddress(dstAddr, target); + parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16), target); + } + result = true; + } + else + throw "for ARM_RELOC_HALF, next reloc is not ARM_RELOC_PAIR"; + break; + default: + throwf("unknown relocation type %d", reloc->r_type()); + break; + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + nextReloc = &reloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + sreloc->r_address()); + instruction = LittleEndian::get32(*fixUpPtr); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == ARM_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch ( sreloc->r_type() ) { + case ARM_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + contentValue = LittleEndian::get32(*fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + if ( target.atom->isThumb() ) + target.addend &= -2; // remove thumb bit + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (int64_t)(srcAddr + displacement) - (int64_t)(target.atom->_objAddress); + parser.addFixups(src, ld::Fixup::kindStoreARMBranch24, target); + break; + case ARM_THUMB_RELOC_BR22: + // thumb2 added two more bits to displacement, complicating the displacement decoding + { + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + if ( s ) + sdis |= 0xFE000000; + displacement = sdis; + } + // The pc added will be +4 from the pc + displacement += 4; + dstAddr = srcAddr+displacement; + // If the instruction was blx, force the low 2 bits to be clear + if ((instruction & 0xF8000000) == 0xE8000000) + dstAddr &= 0xFFFFFFFC; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = dstAddr - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreThumbBranch22, target); + break; + case ARM_RELOC_SECTDIFF: + case ARM_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) + throw "ARM_RELOC_SECTDIFF missing following pair"; + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_SECTDIFF"; + contentValue = LittleEndian::get32(*fixUpPtr); + Atom* fromAtom = parser.findAtomByAddress(nextRelocValue); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + 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); + if ( targetAtom->isThumb() ) + addend &= -2; // remove thumb bit + // if reference to LSDA, add group subordinate fixup + if ( targetAtom->contentType() == ld::Atom::typeLSDA ) { + Parser::SourceLocation src2; + src2.atom = src.atom; + src2.offsetInAtom = 0; + parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, targetAtom); + } + if ( addend < 0 ) { + // switch binding base on coalescing + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + 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); + parser.addFixup(src, ld::Fixup::k5of5, ld::Fixup::kindStoreLittleEndian32); + } + else { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, (uint32_t)(offsetInTarget+addend)); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, ld::Fixup::kindStoreLittleEndian32); + } + } + break; + case ARM_RELOC_HALF_SECTDIFF: + if ( nextRelocIsPair ) { + instruction = LittleEndian::get32(*fixUpPtr); + Atom* fromAtom = parser.findAtomByAddress(nextRelocValue); + 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; + if ( sreloc->r_length() & 2 ) { + isThumb = true; + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + instruction16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + } + else { + isThumb = false; + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + instruction16 = (imm4 << 12) | imm12; + } + if ( sreloc->r_length() & 1 ) + dstAddr = ((instruction16 << 16) | other16); + else + dstAddr = (other16 << 16) | instruction16; + int32_t addend = dstAddr - (sreloc->r_value() - nextRelocValue); + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, (uint32_t)offsetInTarget+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + if ( sreloc->r_length() & 1 ) { + // high 16 + parser.addFixup(src, ld::Fixup::k5of5, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16)); + } + else { + // low 16 + parser.addFixup(src, ld::Fixup::k5of5, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16)); + } + result = true; + } + else + throw "ARM_RELOC_HALF_SECTDIFF reloc missing following pair"; + break; + case ARM_RELOC_HALF: + if ( nextRelocIsPair ) { + instruction = LittleEndian::get32(*fixUpPtr); + Atom* targetAtom = parser.findAtomByAddress(sreloc->r_value()); + uint32_t instruction16; + uint32_t other16 = (nextRelocAddress & 0xFFFF); + bool isThumb; + if ( sreloc->r_length() & 2 ) { + isThumb = true; + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + instruction16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + } + else { + isThumb = false; + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + instruction16 = (imm4 << 12) | imm12; + } + if ( sreloc->r_length() & 1 ) + dstAddr = ((instruction16 << 16) | other16); + else + dstAddr = (other16 << 16) | instruction16; + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, dstAddr - targetAtom->_objAddress); + if ( sreloc->r_length() & 1 ) { + // high 16 + parser.addFixup(src, ld::Fixup::k3of3, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16)); + } + else { + // low 16 + parser.addFixup(src, ld::Fixup::k3of3, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16)); + } + result = true; + } + else + throw "scattered ARM_RELOC_HALF reloc missing following pair"; + break; + default: + throwf("unknown ARM scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + + + + +template +bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + // inherited + FixedSizeSection::addRelocFixup(parser, reloc); + + assert(0 && "needs template specialization"); + return false; +} + +template <> +bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) +{ + // if this is the reloc for the super class name string, add implicit reference to super class + if ( ((reloc->r_address() & R_SCATTERED) == 0) && (reloc->r_type() == GENERIC_RELOC_VANILLA) ) { + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section

* sect = this->machoSection(); + Parser::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + if ( src.offsetInAtom == 4 ) { + Parser::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* superClassBaseName = (char*)stringTarget.atom->rawContentPointer(); + char* superClassName = new char[strlen(superClassBaseName) + 20]; + strcpy(superClassName, ".objc_class_name_"); + strcat(superClassName, superClassBaseName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, superClassName); + } + } + // inherited + return FixedSizeSection::addRelocFixup(parser, reloc); +} + +template <> +bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) +{ + // if this is the reloc for the super class name string, add implicit reference to super class + if ( ((reloc->r_address() & R_SCATTERED) == 0) && (reloc->r_type() == PPC_RELOC_VANILLA) ) { + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section

* sect = this->machoSection(); + Parser::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + if ( src.offsetInAtom == 4 ) { + Parser::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* superClassBaseName = (char*)stringTarget.atom->rawContentPointer(); + char* superClassName = new char[strlen(superClassBaseName) + 20]; + strcpy(superClassName, ".objc_class_name_"); + strcat(superClassName, superClassBaseName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, superClassName); + } + } + + // inherited + return FixedSizeSection::addRelocFixup(parser, reloc); +} + + + + +template +bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + // inherited + PointerToCStringSection::addRelocFixup(parser, reloc); + + assert(0 && "needs template specialization"); + return false; +} + + +template <> +bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) +{ + // add implict class refs, fixups not usable yet, so look at relocations + assert( (reloc->r_address() & R_SCATTERED) == 0 ); + assert( reloc->r_type() == PPC_RELOC_VANILLA ); + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section

* sect = this->machoSection(); + Parser::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + Parser::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* baseClassName = (char*)stringTarget.atom->rawContentPointer(); + char* objcClassName = new char[strlen(baseClassName) + 20]; + strcpy(objcClassName, ".objc_class_name_"); + strcat(objcClassName, baseClassName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, objcClassName); + + // inherited + return PointerToCStringSection::addRelocFixup(parser, reloc); +} + + +template <> +bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) +{ + // add implict class refs, fixups not usable yet, so look at relocations + assert( (reloc->r_address() & R_SCATTERED) == 0 ); + assert( reloc->r_type() == GENERIC_RELOC_VANILLA ); + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section

* sect = this->machoSection(); + Parser::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + Parser::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* baseClassName = (char*)stringTarget.atom->rawContentPointer(); + char* objcClassName = new char[strlen(baseClassName) + 20]; + strcpy(objcClassName, ".objc_class_name_"); + strcat(objcClassName, baseClassName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, objcClassName); + + // inherited + return PointerToCStringSection::addRelocFixup(parser, reloc); +} + + +template +void Section::makeFixups(class Parser& parser, const struct Parser::CFIInfoArray&) +{ + const macho_section

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

* relocs = (macho_relocation_info

*)(file().fileContent() + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + for (uint32_t r = 0; r < relocCount; ++r) { + try { + if ( this->addRelocFixup(parser, &relocs[r]) ) + ++r; // skip next + } + catch (const char* msg) { + throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); + } + } + + // add follow-on fixups if .o file is missing .subsections_via_symbols + if ( this->addFollowOnFixups() ) { + Atom* end = &_endAtoms[-1]; + for(Atom* p = _beginAtoms; p < end; ++p) { + typename Parser::SourceLocation src(p, 0); + Atom* nextAtom = &p[1]; + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); + } + } + else if ( this->type() == ld::Section::typeCode ) { + // if FDE broke text not at a symbol, use followOn to keep code together + Atom* end = &_endAtoms[-1]; + for(Atom* p = _beginAtoms; p < end; ++p) { + typename Parser::SourceLocation src(p, 0); + Atom* nextAtom = &p[1]; + if ( (p->symbolTableInclusion() == ld::Atom::symbolTableIn) && (nextAtom->symbolTableInclusion() == ld::Atom::symbolTableNotIn) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); + } + } + } + + // add follow-on fixups for aliases + if ( _hasAliases ) { + for(Atom* p = _beginAtoms; p < _endAtoms; ++p) { + if ( p->isAlias() && ! this->addFollowOnFixups() ) { + Atom* targetOfAlias = &p[1]; + assert(p < &_endAtoms[-1]); + assert(p->_objAddress == targetOfAlias->_objAddress); + typename Parser::SourceLocation src(p, 0); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, targetOfAlias); + } + } + } +} + + + +// +// main function used by linker to instantiate ld::Files +// +ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts) +{ + switch ( opts.architecture ) { + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Parser::validFile(fileContent) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Parser::validFile(fileContent) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Parser::validFile(fileContent) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Parser::validFile(fileContent) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + +// +// used by archive reader to validate member object file +// +bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts) +{ + switch ( opts.architecture ) { + case CPU_TYPE_X86_64: + return ( mach_o::relocatable::Parser::validFile(fileContent) ); + case CPU_TYPE_I386: + return ( mach_o::relocatable::Parser::validFile(fileContent) ); + case CPU_TYPE_ARM: + return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); + case CPU_TYPE_POWERPC: + return ( mach_o::relocatable::Parser::validFile(fileContent) ); + case CPU_TYPE_POWERPC64: + return ( mach_o::relocatable::Parser::validFile(fileContent) ); + } + return false; +} + +// +// used by linker to infer architecture when no -arch is on command line +// +bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + *result = CPU_TYPE_X86_64; + *subResult = CPU_SUBTYPE_X86_64_ALL; + return true; + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + *result = CPU_TYPE_I386; + *subResult = CPU_SUBTYPE_X86_ALL; + return true; + } + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + *result = CPU_TYPE_ARM; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + *result = CPU_TYPE_POWERPC; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + *result = CPU_TYPE_POWERPC64; + *subResult = CPU_SUBTYPE_POWERPC_ALL; + return true; + } + return false; +} + +// +// used by linker is error messages to describe bad .o file +// +const char* archName(const uint8_t* fileContent) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } + return NULL; +} + +// +// Used by archive reader when -ObjC option is specified +// +bool hasObjC2Categories(const uint8_t* fileContent) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + 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; +} + + + +} // namespace relocatable +} // namespace mach_o + + diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h new file mode 100644 index 0000000..6d0e25e --- /dev/null +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -0,0 +1,58 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ + +#ifndef __MACHO_RELOCATABLE_FILE_H__ +#define __MACHO_RELOCATABLE_FILE_H__ + +#include "ld.hpp" +#include "Options.h" + +namespace mach_o { +namespace relocatable { + +struct ParserOptions { + uint32_t architecture; + bool objSubtypeMustMatch; + bool logAllFiles; + bool convertUnwindInfo; + uint32_t subType; +}; + +extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + const ParserOptions& opts); + +extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts); + +extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); + +extern bool hasObjC2Categories(const uint8_t* fileContent); + +extern const char* archName(const uint8_t* fileContent); + +} // namespace relocatable +} // namespace mach_o + + +#endif // __MACHO_RELOCATABLE_FILE_H__ diff --git a/ld64/src/ld/parsers/opaque_section_file.cpp b/ld64/src/ld/parsers/opaque_section_file.cpp new file mode 100644 index 0000000..660f66d --- /dev/null +++ b/ld64/src/ld/parsers/opaque_section_file.cpp @@ -0,0 +1,106 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include + +#include "ld.hpp" +#include "opaque_section_file.h" + + +namespace opaque_section { + + + +class Atom : public ld::Atom { +public: + virtual ld::File* file() const { return (ld::File*)&_file; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { memcpy(buffer, _content, _size); } + virtual void setScope(Scope) { } + +protected: + friend class File; + Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz); + virtual ~Atom() {} + + class File& _file; + const char* _name; + const uint8_t* _content; + uint64_t _size; +}; + + +class File : public ld::File +{ +public: + File(const char* segmentName, const char* sectionName, const char* pth, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ord, + const char* symbolName="sect_create") + : ld::File(pth, 0, ord), + _atom(*this, symbolName, fileContent, fileLength), + _section(segmentName, sectionName, ld::Section::typeUnclassified) { } + virtual ~File() { } + + virtual bool forEachAtom(ld::File::AtomHandler& h) const { h.doAtom(_atom); return true; } + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const { return false; } + + ld::Atom* atom() { return &_atom; } +private: + friend class Atom; + + Atom _atom; + ld::Section _section; +}; + +Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _file(f), _name(n), _content(content), _size(sz) {} + + +// +// main function used by linker for -sectcreate +// +ld::File* parse(const char* segmentName, const char* sectionName, const char* path, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, + const char* symbolName) +{ + return new File(segmentName, sectionName, path, fileContent, fileLength, ordinal, symbolName); +} + + +} // namespace opaque_section + + + + + + diff --git a/ld64/src/ld/parsers/opaque_section_file.h b/ld64/src/ld/parsers/opaque_section_file.h new file mode 100644 index 0000000..04db805 --- /dev/null +++ b/ld64/src/ld/parsers/opaque_section_file.h @@ -0,0 +1,46 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __SECTION_FILE_H__ +#define __SECTION_FILE_H__ + + + +#include "ld.hpp" + +namespace opaque_section { + +extern ld::File* parse(const char* segmentName, const char* sectionName, const char* path, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, + const char* symbolName="opaque_section"); + + +} // namespace opaque_section + + + +#endif // __SECTION_FILE_H__ + + + diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp new file mode 100644 index 0000000..d42fa54 --- /dev/null +++ b/ld64/src/ld/passes/branch_island.cpp @@ -0,0 +1,623 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ld.hpp" +#include "branch_island.h" + +namespace ld { +namespace passes { +namespace branch_island { + + + + +struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; }; +class TargetAndOffsetComparor +{ +public: + bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const + { + if ( left.atom != right.atom ) + return ( left.atom < right.atom ); + return ( left.offset < right.offset ); + } +}; + + +static bool _s_log = false; +static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); + +class PPCBranchIslandAtom : public ld::Atom { +public: + PPCBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + 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 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + int64_t displacement = _target->finalAddress() - this->finalAddress(); + const int64_t bl_sixteenMegLimit = 0x00FFFFFF; + if ( _target->contentType() == ld::Atom::typeBranchIsland ) { + // try optimizing away intermediate islands + int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress(); + if ( (skipToFinalDisplacement > bl_sixteenMegLimit) && (skipToFinalDisplacement < (-bl_sixteenMegLimit)) ) { + displacement = skipToFinalDisplacement; + } + } + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + +class ARMtoARMBranchIslandAtom : public ld::Atom { +public: + ARMtoARMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + 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 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + int64_t displacement = _target->finalAddress() - this->finalAddress() - 8; + if ( _target->contentType() == ld::Atom::typeBranchIsland ) { + // an ARM branch can branch farther than a thumb branch. The branch + // island generation was conservative and put islands every thumb + // branch distance apart. Check to see if this is a an island + // hopping branch that could be optimized to go directly to target. + int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 8; + if ( (skipToFinalDisplacement < 33554428LL) && (skipToFinalDisplacement > (-33554432LL)) ) { + // can skip branch island and jump straight to target + if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); + displacement = skipToFinalDisplacement; + } + else { + // ultimate target is too far, jump to island + if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + } + } + uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; + int32_t branchInstruction = 0xEA000000 | imm24; + OSWriteLittleInt32(buffer, 0, branchInstruction); + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + + +class ARMtoThumb1BranchIslandAtom : public ld::Atom { +public: + ARMtoThumb1BranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + 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 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to target which means no island hopping + int64_t displacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - (this->finalAddress() + 12); + if ( _finalTarget.atom->isThumb() ) + displacement |= 1; + if (_s_log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + + +class Thumb2toThumbBranchIslandAtom : public ld::Atom { +public: + Thumb2toThumbBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + 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 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + int64_t displacement = _target->finalAddress() - this->finalAddress() - 4; + if ( _target->contentType() == ld::Atom::typeBranchIsland ) { + // an ARM branch can branch farther than a thumb branch. The branch + // island generation was conservative and put islands every thumb + // branch distance apart. Check to see if this is a an island + // hopping branch that could be optimized to go directly to target. + int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 4; + if ( (skipToFinalDisplacement < 16777214) && (skipToFinalDisplacement > (-16777216LL)) ) { + // can skip branch island and jump straight to target + if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); + displacement = skipToFinalDisplacement; + } + else { + // ultimate target is too far for thumb2 branch, jump to island + if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + } + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + uint32_t opcode = 0x9000F000; + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + OSWriteLittleInt32(buffer, 0, newInstruction); + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + +class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { +public: + NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + 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 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to final target which means no island hopping + uint32_t targetAddr = _finalTarget.atom->finalAddress(); + if ( _finalTarget.atom->isThumb() ) + targetAddr |= 1; + if (_s_log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] + OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + +static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget) +{ + char* name; + if ( finalTarget.offset == 0 ) { + if ( islandRegion == 0 ) + asprintf(&name, "%s.island", finalTarget.atom->name()); + else + asprintf(&name, "%s.island.%d", finalTarget.atom->name(), islandRegion+1); + } + else { + asprintf(&name, "%s_plus_%d.island.%d", finalTarget.atom->name(), finalTarget.offset, islandRegion); + } + + switch ( kind ) { + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return new PPCBranchIslandAtom(name, nextTarget, finalTarget); + break; + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + if ( finalTarget.atom->isThumb() ) { + if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget); + } + else if ( opts.outputSlidable() ) { + return new ARMtoThumb1BranchIslandAtom(name, nextTarget, finalTarget); + } + else { + return new NoPicARMtoThumbMBranchIslandAtom(name, nextTarget, finalTarget); + } + } + else { + return new ARMtoARMBranchIslandAtom(name, nextTarget, finalTarget); + } + break; + default: + assert(0 && "unexpected branch kind"); + break; + } + return NULL; +} + + +static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool seenThumbBranch) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return 16000000; + break; + case CPU_TYPE_ARM: + if ( ! seenThumbBranch ) + return 32000000; // ARM can branch +/- 32MB + else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + return 16000000; // thumb2 can branch +/- 16MB + else + return 4000000; // thumb1 can branch +/- 4MB + break; + } + assert(0 && "unexpected architecture"); + return 0x100000000LL; +} + + +static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBranch) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return 14*1024*1024; + break; + 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 ) + return 14*1024*1024; // 2MB of branch islands per 16MB + else + return 3500000; // 0.5MB of branch islands per 4MB + break; + } + assert(0 && "unexpected architecture"); + return 0x100000000LL; +} + + +// +// PowerPC can do PC relative branches as far as +/-16MB. +// If a branch target is >16MB then we insert one or more +// "branch islands" between the branch and its target that +// allows island hopping to the target. +// +// Branch Island Algorithm +// +// If the __TEXT segment < 16MB, then no branch islands needed +// Otherwise, every 14MB into the __TEXT segment a region is +// added which can contain branch islands. Every out-of-range +// bl instruction is checked. If it crosses a region, an island +// is added to that region with the same target and the bl is +// adjusted to target the island instead. +// +// In theory, if too many islands are added to one region, it +// could grow the __TEXT enough that other previously in-range +// bl branches could be pushed out of range. We reduce the +// probability this could happen by placing the ranges every +// 14MB which means the region would have to be 2MB (512,000 islands) +// before any branches could be pushed out of range. +// + +void doPass(const Options& opts, ld::Internal& state) +{ + // only make branch islands in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // only PowerPC and ARM need branch islands + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + case CPU_TYPE_ARM: + break; + default: + return; + } + + // scan to find __text section + ld::Internal::FinalSection* textSection = NULL; + 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; + + // assign section offsets to each atom in __text section, watch for thumb branches, and find total size + const bool isARM = (opts.architecture() == CPU_TYPE_ARM); + bool hasThumbBranches = false; + uint64_t offset = 0; + for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // check for thumb branches + if ( isARM && ~hasThumbBranches ) { + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + hasThumbBranches = true; + break; + default: + break; + } + } + } + // align atom + ld::Atom::Alignment atomAlign = atom->alignment(); + uint64_t atomAlignP2 = (1 << atomAlign.powerOf2); + uint64_t currentModulus = (offset % atomAlignP2); + if ( currentModulus != atomAlign.modulus ) { + if ( atomAlign.modulus > currentModulus ) + offset += atomAlign.modulus-currentModulus; + else + offset += atomAlign.modulus+atomAlignP2-currentModulus; + } + (const_cast(atom))->setSectionOffset(offset); + offset += atom->size(); + } + uint64_t totalTextSize = offset; + if ( totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches) ) + return; + if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize); + + // figure out how many regions of branch islands will be needed + const uint32_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section + const int kIslandRegionsCount = totalTextSize / kBetweenRegions; + typedef std::map AtomToIsland; + AtomToIsland* regionsMap[kIslandRegionsCount]; + std::vector* regionsIslands[kIslandRegionsCount]; + for(int i=0; i < kIslandRegionsCount; ++i) { + regionsMap[i] = new AtomToIsland(); + regionsIslands[i] = new std::vector(); + } + unsigned int islandCount = 0; + if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + + // create islands for branches in __text that are out of range + for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target = NULL; + uint64_t addend = 0; + ld::Fixup* fixupWithTarget = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + fixupWithTarget = NULL; + addend = 0; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + fixupWithTarget = fit; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + fixupWithTarget = fit; + break; + } + bool haveBranch = false; + switch (fit->kind) { + case ld::Fixup::kindAddAddend: + addend = fit->u.addend; + break; + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + haveBranch = true; + break; + default: + break; + } + if ( haveBranch ) { + int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom; + int64_t dstAddr = target->sectionOffset() + addend; + if ( target->section().type() == ld::Section::typeStub ) + dstAddr = totalTextSize; + int64_t displacement = dstAddr - srcAddr; + TargetAndOffset finalTargetAndOffset = { target, addend }; + const int64_t kBranchLimit = kBetweenRegions; + if ( displacement > kBranchLimit ) { + // create forward branch chain + const ld::Atom* nextTarget = target; + for (int i=kIslandRegionsCount-1; i >=0 ; --i) { + AtomToIsland* region = regionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset); + (*region)[finalTargetAndOffset] = island; + if (_s_log) fprintf(stderr, "added island %s to region %d for %s\n", island->name(), i, atom->name()); + regionsIslands[i]->push_back(island); + ++islandCount; + nextTarget = island; + } + else { + nextTarget = pos->second; + } + } + } + if (_s_log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->name(), target->name(), atom->name()); + fixupWithTarget->u.target = nextTarget; + fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + } + else if ( displacement < (-kBranchLimit) ) { + // create back branching chain + const ld::Atom* prevTarget = target; + for (int i=0; i < kIslandRegionsCount ; ++i) { + AtomToIsland* region = regionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset); + (*region)[finalTargetAndOffset] = island; + if (_s_log) fprintf(stderr, "added back island %s to region %d for %s\n", island->name(), i, atom->name()); + regionsIslands[i]->push_back(island); + ++islandCount; + prevTarget = island; + } + else { + prevTarget = pos->second; + } + } + } + if (_s_log) fprintf(stderr, "using back island %s for %s\n", prevTarget->name(), atom->name()); + fixupWithTarget->u.target = prevTarget; + fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + } + } + } + } + + + // insert islands into __text section and adjust section offsets + if ( islandCount > 0 ) { + if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); + std::vector newAtomList; + newAtomList.reserve(textSection->atoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions;; + int regionIndex = 0; + for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { + const ld::Atom* atom = *it; + if ( (atom->sectionOffset()+atom->size()) > islandRegionAddr ) { + std::vector* regionIslands = regionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + const ld::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); + } + ++regionIndex; + islandRegionAddr += kBetweenRegions; + } + newAtomList.push_back(atom); + } + // put any remaining islands at end of __text section + if ( regionIndex < kIslandRegionsCount ) { + std::vector* regionIslands = regionsIslands[regionIndex]; + for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + const ld::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); + } + } + // swap in new list of atoms for __text section + textSection->atoms.clear(); + textSection->atoms = newAtomList; + } + +} + + +} // namespace branch_island +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/branch_island.h b/ld64/src/ld/passes/branch_island.h new file mode 100644 index 0000000..5daafe4 --- /dev/null +++ b/ld64/src/ld/passes/branch_island.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __BRANCH_ISLAND_H__ +#define __BRANCH_ISLAND_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace branch_island { + +// called by linker to branch islands if there are out of range branches +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace branch_island +} // namespace passes +} // namespace ld + +#endif // __BRANCH_ISLAND_H__ diff --git a/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp new file mode 100644 index 0000000..70ebcfc --- /dev/null +++ b/ld64/src/ld/passes/branch_shim.cpp @@ -0,0 +1,314 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#include +#include +#include +#include + +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "ld.hpp" +#include "branch_shim.h" + +namespace ld { +namespace passes { +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, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(NULL), + _target(target), + _fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), + _fixup2(8, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(8, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(8, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { 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, 0x44fc); // add ip, pc, ip + OSWriteLittleInt16(&buffer[6], 0, 0x4760); // bx ip + OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // .long target-this + } + + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; +}; + + + +class Thumb1ToArmShimAtom : public ld::Atom { +public: + Thumb1ToArmShimAtom(const ld::Atom* target) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(NULL), + _target(target), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { 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 16; } + 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, "6 Thumb1 instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt16(&buffer[ 0], 0, 0xb402); // push {r1} + OSWriteLittleInt16(&buffer[ 2], 0, 0x4902); // ldr r1, [pc, #8] + OSWriteLittleInt16(&buffer[ 4], 0, 0x4479); // add r1, pc + OSWriteLittleInt16(&buffer[ 6], 0, 0x468c); // mov ip, r1 + OSWriteLittleInt16(&buffer[ 8], 0, 0xbc02); // pop {r1} + OSWriteLittleInt16(&buffer[10], 0, 0x4760); // bx ip + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long target-this + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; +}; + + + + +class ARMtoThumbShimAtom : public ld::Atom { +public: + ARMtoThumbShimAtom(const ld::Atom* target) + : ld::Atom(_s_text_section, 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(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { 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 16; } + 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, "4 ARM instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[12], 0, 0); // .long target-this + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; +}; + + + + + + +static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const ld::Atom** target) +{ + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + *target = fixup->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + *target = state.indirectBindingTable[fixup->u.bindingIndex]; + break; + } +} + + + +// +// The tail-call optimzation may result in a function ending in a jump (b) +// to another functions. At compile time the compiler does not know +// if the target of the jump will be in the same mode (arm vs thumb). +// The arm/thumb instruction set has a way to change modes in a bl(x) +// insruction, but no instruction to change mode in a jump (b) instruction. +// In those rare cases, the linker needs to insert a shim of code to +// make the mode switch. +// +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; + + // only ARM need branch islands + if ( opts.architecture() != CPU_TYPE_ARM ) + return; + + // scan to find __text section + ld::Internal::FinalSection* textSection = NULL; + 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; + } + 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; + } + 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; + } + } + } + + // append all new shims to end of __text + textSection->atoms.insert(textSection->atoms.end(), shims.begin(), shims.end()); +} + + +} // namespace branch_shim +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/branch_shim.h b/ld64/src/ld/passes/branch_shim.h new file mode 100644 index 0000000..ab6b959 --- /dev/null +++ b/ld64/src/ld/passes/branch_shim.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#ifndef __BRANCH_SHIM_H__ +#define __BRANCH_SHIM_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace branch_shim { + +// called by linker to branch shims to support branch (but not bl) that switches mode +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace branch_shim +} // namespace passes +} // namespace ld + +#endif // __BRANCH_SHIM_H__ diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp new file mode 100644 index 0000000..5b9434e --- /dev/null +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -0,0 +1,794 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ld.hpp" +#include "compact_unwind.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + + +namespace ld { +namespace passes { +namespace compact_unwind { + + +struct UnwindEntry { + UnwindEntry(const ld::Atom* f, uint64_t a, uint32_t o, const ld::Atom* d, + const ld::Atom* l, const ld::Atom* p, uint32_t en) + : func(f), fde(d), lsda(l), personalityPointer(p), funcTentAddress(a), + functionOffset(o), encoding(en) { } + const ld::Atom* func; + const ld::Atom* fde; + const ld::Atom* lsda; + const ld::Atom* personalityPointer; + uint64_t funcTentAddress; + uint32_t functionOffset; + compact_unwind_encoding_t encoding; +}; + +struct LSDAEntry { + const ld::Atom* func; + const ld::Atom* lsda; +}; + + +template +class UnwindInfoAtom : public ld::Atom { +public: + UnwindInfoAtom(const std::vector& entries,uint64_t ehFrameSize); + ~UnwindInfoAtom(); + + 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 _headerSize+_pagesSize; } + 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; + + typedef macho_unwind_info_compressed_second_level_page_header

CSLP; + + bool encodingMeansUseDwarf(compact_unwind_encoding_t enc); + void compressDuplicates(const std::vector& entries, + std::vector& uniqueEntries); + void makePersonalityIndexes(std::vector& entries, + std::map& personalityIndexMap); + void findCommonEncoding(const std::vector& entries, + std::map& commonEncodings); + void makeLsdaIndex(const std::vector& entries, std::vector& lsdaIndex, + std::map& lsdaIndexOffsetMap); + unsigned int makeCompressedSecondLevelPage(const std::vector& uniqueInfos, + const std::map commonEncodings, + uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd); + unsigned int makeRegularSecondLevelPage(const std::vector& uniqueInfos, uint32_t pageSize, + unsigned int endIndex, uint8_t*& pageEnd); + void addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc); + void addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde); + void addRegularAddressFixup(uint32_t offset, const ld::Atom* func); + void addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde); + void addImageOffsetFixup(uint32_t offset, const ld::Atom* targ); + void addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend); + + uint8_t* _pagesForDelete; + uint8_t* _pages; + uint64_t _pagesSize; + uint8_t* _header; + uint64_t _headerSize; + std::vector _fixups; + + static bool _s_log; + static ld::Section _s_section; +}; + +template +bool UnwindInfoAtom::_s_log = false; + +template +ld::Section UnwindInfoAtom::_s_section("__TEXT", "__unwind_info", ld::Section::typeUnwindInfo); + + +template +UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint64_t ehFrameSize) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _pagesForDelete(NULL), _pages(NULL), _pagesSize(0), _header(NULL), _headerSize(0) +{ + // build new compressed list by removing entries where next function has same encoding + std::vector uniqueEntries; + compressDuplicates(entries, uniqueEntries); + + // reserve room so _fixups vector is not reallocated a bunch of times + _fixups.reserve(uniqueEntries.size()*3); + + // build personality index, update encodings with personality index + std::map personalityIndexMap; + makePersonalityIndexes(uniqueEntries, personalityIndexMap); + if ( personalityIndexMap.size() > 3 ) { + warning("too many personality routines for compact unwind to encode"); + return; + } + + // put the most common encodings into the common table, but at most 127 of them + std::map commonEncodings; + findCommonEncoding(uniqueEntries, commonEncodings); + + // build lsda index + std::map lsdaIndexOffsetMap; + std::vector lsdaIndex; + makeLsdaIndex(uniqueEntries, lsdaIndex, lsdaIndexOffsetMap); + + + // calculate worst case size for all unwind info pages when allocating buffer + const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + assert(uniqueEntries.size() > 0); + const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 1; + _pagesForDelete = (uint8_t*)calloc(pageCount,4096); + if ( _pagesForDelete == NULL ) { + warning("could not allocate space for compact unwind info"); + return; + } + + // make last second level page smaller so that all other second level pages can be page aligned + uint32_t maxLastPageSize = 4096 - (ehFrameSize % 4096); + uint32_t tailPad = 0; + if ( maxLastPageSize < 128 ) { + tailPad = maxLastPageSize; + maxLastPageSize = 4096; + } + + // fill in pages in reverse order + const ld::Atom* secondLevelFirstFuncs[pageCount*3]; + uint8_t* secondLevelPagesStarts[pageCount*3]; + unsigned int endIndex = uniqueEntries.size(); + unsigned int secondLevelPageCount = 0; + uint8_t* pageEnd = &_pagesForDelete[pageCount*4096]; + uint32_t pageSize = maxLastPageSize; + while ( endIndex > 0 ) { + endIndex = makeCompressedSecondLevelPage(uniqueEntries, commonEncodings, pageSize, endIndex, pageEnd); + secondLevelPagesStarts[secondLevelPageCount] = pageEnd; + secondLevelFirstFuncs[secondLevelPageCount] = uniqueEntries[endIndex].func; + ++secondLevelPageCount; + pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + } + _pages = pageEnd; + _pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd; + + + // calculate section layout + const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header

); + const uint32_t commonEncodingsArrayCount = commonEncodings.size(); + const uint32_t commonEncodingsArraySize = commonEncodingsArrayCount * sizeof(compact_unwind_encoding_t); + const uint32_t personalityArraySectionOffset = commonEncodingsArraySectionOffset + commonEncodingsArraySize; + const uint32_t personalityArrayCount = personalityIndexMap.size(); + const uint32_t personalityArraySize = personalityArrayCount * sizeof(uint32_t); + const uint32_t indexSectionOffset = personalityArraySectionOffset + personalityArraySize; + const uint32_t indexCount = secondLevelPageCount+1; + const uint32_t indexSize = indexCount * sizeof(macho_unwind_info_section_header_index_entry

); + const uint32_t lsdaIndexArraySectionOffset = indexSectionOffset + indexSize; + const uint32_t lsdaIndexArrayCount = lsdaIndex.size(); + const uint32_t lsdaIndexArraySize = lsdaIndexArrayCount * sizeof(macho_unwind_info_section_header_lsda_index_entry

); + const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; + + // now that we know the size of the header, slide all existing fixups on the pages + const int32_t fixupSlide = headerEndSectionOffset + (_pagesForDelete - _pages); + for(std::vector::iterator it = _fixups.begin(); it != _fixups.end(); ++it) { + it->offsetInAtom += fixupSlide; + } + + // allocate and fill in section header + _headerSize = headerEndSectionOffset; + _header = new uint8_t[_headerSize]; + bzero(_header, _headerSize); + macho_unwind_info_section_header

* sectionHeader = (macho_unwind_info_section_header

*)_header; + sectionHeader->set_version(UNWIND_SECTION_VERSION); + sectionHeader->set_commonEncodingsArraySectionOffset(commonEncodingsArraySectionOffset); + sectionHeader->set_commonEncodingsArrayCount(commonEncodingsArrayCount); + sectionHeader->set_personalityArraySectionOffset(personalityArraySectionOffset); + sectionHeader->set_personalityArrayCount(personalityArrayCount); + sectionHeader->set_indexSectionOffset(indexSectionOffset); + sectionHeader->set_indexCount(indexCount); + + // copy common encodings + uint32_t* commonEncodingsTable = (uint32_t*)&_header[commonEncodingsArraySectionOffset]; + for (std::map::iterator it=commonEncodings.begin(); it != commonEncodings.end(); ++it) + E::set32(commonEncodingsTable[it->second], it->first); + + // make references for personality entries + uint32_t* personalityArray = (uint32_t*)&_header[sectionHeader->personalityArraySectionOffset()]; + for (std::map::iterator it=personalityIndexMap.begin(); it != personalityIndexMap.end(); ++it) { + uint32_t offset = (uint8_t*)&personalityArray[it->second-1] - _header; + this->addImageOffsetFixup(offset, it->first); + } + + // build first level index and references + macho_unwind_info_section_header_index_entry

* indexTable = (macho_unwind_info_section_header_index_entry

*)&_header[indexSectionOffset]; + uint32_t refOffset; + for (unsigned int i=0; i < secondLevelPageCount; ++i) { + unsigned int reverseIndex = secondLevelPageCount - 1 - i; + indexTable[i].set_functionOffset(0); + indexTable[i].set_secondLevelPagesSectionOffset(secondLevelPagesStarts[reverseIndex]-_pages+headerEndSectionOffset); + indexTable[i].set_lsdaIndexArraySectionOffset(lsdaIndexOffsetMap[secondLevelFirstFuncs[reverseIndex]]+lsdaIndexArraySectionOffset); + refOffset = (uint8_t*)&indexTable[i] - _header; + this->addImageOffsetFixup(refOffset, secondLevelFirstFuncs[reverseIndex]); + } + indexTable[secondLevelPageCount].set_functionOffset(0); + indexTable[secondLevelPageCount].set_secondLevelPagesSectionOffset(0); + indexTable[secondLevelPageCount].set_lsdaIndexArraySectionOffset(lsdaIndexArraySectionOffset+lsdaIndexArraySize); + refOffset = (uint8_t*)&indexTable[secondLevelPageCount] - _header; + this->addImageOffsetFixupPlusAddend(refOffset, entries.back().func, entries.back().func->size()+1); + + // build lsda references + uint32_t lsdaEntrySectionOffset = lsdaIndexArraySectionOffset; + for (std::vector::iterator it = lsdaIndex.begin(); it != lsdaIndex.end(); ++it) { + this->addImageOffsetFixup(lsdaEntrySectionOffset, it->func); + this->addImageOffsetFixup(lsdaEntrySectionOffset+4, it->lsda); + lsdaEntrySectionOffset += sizeof(unwind_info_section_header_lsda_index_entry); + } + +} + +template +UnwindInfoAtom::~UnwindInfoAtom() +{ + free(_pagesForDelete); + free(_header); +} + +template +void UnwindInfoAtom::copyRawContent(uint8_t buffer[]) const +{ + // content is in two parts + memcpy(buffer, _header, _headerSize); + memcpy(&buffer[_headerSize], _pages, _pagesSize); +} + + +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); +} + +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); +} + +template +void UnwindInfoAtom::compressDuplicates(const std::vector& entries, std::vector& uniqueEntries) +{ + // build new list removing entries where next function has same encoding + uniqueEntries.reserve(entries.size()); + UnwindEntry last(NULL, 0, 0, NULL, NULL, NULL, 0xFFFFFFFF); + for(std::vector::const_iterator it=entries.begin(); it != entries.end(); ++it) { + const UnwindEntry& next = *it; + bool newNeedsDwarf = encodingMeansUseDwarf(next.encoding); + // remove entries which have same encoding and personalityPointer as last one + if ( newNeedsDwarf || (next.encoding != last.encoding) || (next.personalityPointer != last.personalityPointer) + || (next.lsda != NULL) || (last.lsda != NULL) ) { + uniqueEntries.push_back(next); + } + last = next; + } + if (_s_log) fprintf(stderr, "compressDuplicates() entries.size()=%lu, uniqueEntries.size()=%lu\n", + entries.size(), uniqueEntries.size()); +} + +template +void UnwindInfoAtom::makePersonalityIndexes(std::vector& entries, std::map& personalityIndexMap) +{ + for(std::vector::iterator it=entries.begin(); it != entries.end(); ++it) { + if ( it->personalityPointer != NULL ) { + std::map::iterator pos = personalityIndexMap.find(it->personalityPointer); + if ( pos == personalityIndexMap.end() ) { + const uint32_t nextIndex = personalityIndexMap.size() + 1; + personalityIndexMap[it->personalityPointer] = nextIndex; + } + uint32_t personalityIndex = personalityIndexMap[it->personalityPointer]; + it->encoding |= (personalityIndex << (__builtin_ctz(UNWIND_PERSONALITY_MASK)) ); + } + } + if (_s_log) fprintf(stderr, "makePersonalityIndexes() %lu personality routines used\n", personalityIndexMap.size()); +} + + +template +void UnwindInfoAtom::findCommonEncoding(const std::vector& entries, + std::map& commonEncodings) +{ + // scan infos to get frequency counts for each encoding + std::map encodingsUsed; + unsigned int mostCommonEncodingUsageCount = 0; + for(std::vector::const_iterator it=entries.begin(); it != entries.end(); ++it) { + // never put dwarf into common table + if ( encodingMeansUseDwarf(it->encoding) ) + continue; + std::map::iterator pos = encodingsUsed.find(it->encoding); + if ( pos == encodingsUsed.end() ) { + encodingsUsed[it->encoding] = 1; + } + else { + encodingsUsed[it->encoding] += 1; + if ( mostCommonEncodingUsageCount < encodingsUsed[it->encoding] ) + mostCommonEncodingUsageCount = encodingsUsed[it->encoding]; + } + } + // put the most common encodings into the common table, but at most 127 of them + for(unsigned int usages=mostCommonEncodingUsageCount; usages > 1; --usages) { + for (std::map::iterator euit=encodingsUsed.begin(); euit != encodingsUsed.end(); ++euit) { + if ( euit->second == usages ) { + unsigned int sz = commonEncodings.size(); + if ( sz < 127 ) { + commonEncodings[euit->first] = sz; + } + } + } + } + if (_s_log) fprintf(stderr, "findCommonEncoding() %lu common encodings found\n", commonEncodings.size()); +} + + +template +void UnwindInfoAtom::makeLsdaIndex(const std::vector& entries, std::vector& lsdaIndex, std::map& lsdaIndexOffsetMap) +{ + for(std::vector::const_iterator it=entries.begin(); it != entries.end(); ++it) { + lsdaIndexOffsetMap[it->func] = lsdaIndex.size() * sizeof(unwind_info_section_header_lsda_index_entry); + if ( it->lsda != NULL ) { + LSDAEntry entry; + entry.func = it->func; + entry.lsda = it->lsda; + lsdaIndex.push_back(entry); + } + } + if (_s_log) fprintf(stderr, "makeLsdaIndex() %lu LSDAs found\n", lsdaIndex.size()); +} + + +template <> +void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + + +template <> +void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} + + + + + +template +unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vector& uniqueInfos, uint32_t pageSize, + unsigned int endIndex, uint8_t*& pageEnd) +{ + const unsigned int maxEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + const unsigned int entriesToAdd = ((endIndex > maxEntriesPerPage) ? maxEntriesPerPage : endIndex); + uint8_t* pageStart = pageEnd + - entriesToAdd*sizeof(unwind_info_regular_second_level_entry) + - sizeof(unwind_info_regular_second_level_page_header); + macho_unwind_info_regular_second_level_page_header

* page = (macho_unwind_info_regular_second_level_page_header

*)pageStart; + page->set_kind(UNWIND_SECOND_LEVEL_REGULAR); + page->set_entryPageOffset(sizeof(macho_unwind_info_regular_second_level_page_header

)); + page->set_entryCount(entriesToAdd); + macho_unwind_info_regular_second_level_entry

* entryTable = (macho_unwind_info_regular_second_level_entry

*)(pageStart + page->entryPageOffset()); + for (unsigned int i=0; i < entriesToAdd; ++i) { + const UnwindEntry& info = uniqueInfos[endIndex-entriesToAdd+i]; + entryTable[i].set_functionOffset(0); + entryTable[i].set_encoding(info.encoding); + // add fixup for address part of entry + uint32_t offset = (uint8_t*)(&entryTable[i]) - _pagesForDelete; + this->addRegularAddressFixup(offset, info.func); + if ( encodingMeansUseDwarf(info.encoding) ) { + // add fixup for dwarf offset part of page specific encoding + uint32_t encOffset = (uint8_t*)(&entryTable[i]) - _pagesForDelete; + this->addRegularFDEOffsetFixup(encOffset, info.fde); + } + } + if (_s_log) fprintf(stderr, "regular page with %u entries\n", entriesToAdd); + pageEnd = pageStart; + return endIndex - entriesToAdd; +} + + +template +unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector& uniqueInfos, + const std::map commonEncodings, + uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) +{ + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); + // first pass calculates how many compressed entries we could fit in this sized page + // keep adding entries to page until: + // 1) encoding table plus entry table plus header exceed page size + // 2) the file offset delta from the first to last function > 24 bits + // 3) custom encoding index reachs 255 + // 4) run out of uniqueInfos to encode + std::map pageSpecificEncodings; + uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t); + std::vector encodingIndexes; + int index = endIndex-1; + int entryCount = 0; + uint64_t lastEntryAddress = uniqueInfos[index].funcTentAddress; + bool canDo = true; + while ( canDo && (index >= 0) ) { + const UnwindEntry& info = uniqueInfos[index--]; + // compute encoding index + unsigned int encodingIndex; + std::map::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) { + encodingIndex = pos->second; + } + else { + // no commmon entry, so add one on this page + uint32_t encoding = info.encoding; + if ( encodingMeansUseDwarf(encoding) ) { + // make unique pseudo encoding so this dwarf will gets is own encoding entry slot + encoding += (index+1); + } + std::map::iterator ppos = pageSpecificEncodings.find(encoding); + if ( ppos != pageSpecificEncodings.end() ) { + encodingIndex = pos->second; + } + else { + encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); + if ( encodingIndex <= 255 ) { + pageSpecificEncodings[encoding] = encodingIndex; + } + else { + canDo = false; // case 3) + if (_s_log) fprintf(stderr, "end of compressed page with %u entries, %lu custom encodings because too many custom encodings\n", + entryCount, pageSpecificEncodings.size()); + } + } + } + if ( canDo ) + encodingIndexes.push_back(encodingIndex); + // compute function offset + uint32_t funcOffsetWithInPage = lastEntryAddress - info.funcTentAddress; + if ( funcOffsetWithInPage > 0x00FFFF00 ) { + // don't use 0x00FFFFFF because addresses may vary after atoms are laid out again + canDo = false; // case 2) + if (_s_log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount); + } + else { + ++entryCount; + } + // check room for entry + if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { + canDo = false; // case 1) + --entryCount; + if (_s_log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); + } + //if (_s_log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); + } + + // check for cases where it would be better to use a regular (non-compressed) page + const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + + pageSpecificEncodings.size()*sizeof(uint32_t) + + entryCount*sizeof(uint32_t); + if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { + const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + if ( entryCount < regularEntriesPerPage ) { + return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); + } + } + + // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 + uint32_t pad = 0; + if ( compressPageUsed == (pageSize-4) ) + pad = 4; + + // second pass fills in page + uint8_t* pageStart = pageEnd - compressPageUsed - pad; + CSLP* page = (CSLP*)pageStart; + page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); + page->set_entryPageOffset(sizeof(CSLP)); + page->set_entryCount(entryCount); + page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); + page->set_encodingsCount(pageSpecificEncodings.size()); + uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; + // fill in entry table + uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; + const ld::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; + for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { + const UnwindEntry& info = uniqueInfos[i]; + uint8_t encodingIndex; + if ( encodingMeansUseDwarf(info.encoding) ) { + // dwarf entries are always in page specific encodings + encodingIndex = pageSpecificEncodings[info.encoding+i]; + } + else { + std::map::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) + encodingIndex = pos->second; + else + encodingIndex = pageSpecificEncodings[info.encoding]; + } + uint32_t entryIndex = i - endIndex + entryCount; + E::set32(entiresArray[entryIndex], encodingIndex << 24); + // add fixup for address part of entry + uint32_t offset = (uint8_t*)(&entiresArray[entryIndex]) - _pagesForDelete; + this->addCompressedAddressOffsetFixup(offset, info.func, firstFunc); + if ( encodingMeansUseDwarf(info.encoding) ) { + // add fixup for dwarf offset part of page specific encoding + uint32_t encOffset = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]) - _pagesForDelete; + this->addCompressedEncodingFixup(encOffset, info.fde); + } + } + // fill in encodings table + for(std::map::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { + E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); + } + + if (_s_log) fprintf(stderr, "compressed page with %u entries, %lu custom encodings\n", entryCount, pageSpecificEncodings.size()); + + // update pageEnd; + pageEnd = pageStart; + return endIndex-entryCount; // endIndex for next page +} + + + + + + +static uint64_t calculateEHFrameSize(const ld::Internal& state) +{ + uint64_t size = 0; + for (std::vector::const_iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeCFI ) { + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + size += (*ait)->size(); + } + } + } + return size; +} + +static void getAllUnwindInfos(const ld::Internal& state, std::vector& entries) +{ + uint64_t address = 0; + 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; + // adjust address for atom alignment + uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t currentModulus = (address % alignment); + uint64_t requiredModulus = atom->alignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + address += requiredModulus-currentModulus; + else + address += requiredModulus+alignment-currentModulus; + } + + if ( atom->beginUnwind() == atom->endUnwind() ) { + // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info + if ( atom->section().type() == ld::Section::typeCode ) { + entries.push_back(UnwindEntry(atom, address, 0, NULL, NULL, NULL, 0)); + } + } + else { + // atom has unwind info(s), add entry for each + const ld::Atom* fde = NULL; + const ld::Atom* lsda = NULL; + const ld::Atom* personalityPointer = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindNoneGroupSubordinateFDE: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + fde = fit->u.target; + break; + case ld::Fixup::kindNoneGroupSubordinateLSDA: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + lsda = fit->u.target; + break; + default: + break; + } + } + if ( fde != NULL ) { + // find CIE for this FDE + const ld::Atom* cie = NULL; + for (ld::Fixup::iterator fit = fde->fixupsBegin(), end=fde->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind != ld::Fixup::kindSubtractTargetAddress ) + continue; + if ( fit->binding != ld::Fixup::bindingDirectlyBound ) + continue; + cie = fit->u.target; + // CIE is only direct subtracted target in FDE + assert(cie->section().type() == ld::Section::typeCFI); + break; + } + if ( cie != NULL ) { + // if CIE can have just one fixup - to the personality pointer + for (ld::Fixup::iterator fit = cie->fixupsBegin(), end=cie->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind == ld::Fixup::kindSetTargetAddress ) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + personalityPointer = state.indirectBindingTable[fit->u.bindingIndex]; + assert(personalityPointer->section().type() == ld::Section::typeNonLazyPointer); + break; + case ld::Fixup::bindingDirectlyBound: + personalityPointer = fit->u.target; + assert(personalityPointer->section().type() == ld::Section::typeNonLazyPointer); + break; + default: + break; + } + } + } + } + } + for ( ld::Atom::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { + entries.push_back(UnwindEntry(atom, address, uit->startOffset, fde, lsda, personalityPointer, uit->unwindInfo)); + } + } + address += atom->size(); + } + } +} + + + + +void doPass(const Options& opts, ld::Internal& state) +{ + //const bool log = false; + + // only make make __unwind_info in final linked images + if ( !opts.needsUnwindInfoSection() ) + return; + + // walk every atom and gets its unwind info + std::vector entries; + entries.reserve(64); + getAllUnwindInfos(state, entries); + + // don't generate an __unwind_info section if there is no code in this linkage unit + if ( entries.size() == 0 ) + return; + + // calculate size of __eh_frame section, so __unwind_info can go before it and page align + uint64_t ehFrameSize = calculateEHFrameSize(state); + + // create atom that contains the whole compact unwind table + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); + break; + case CPU_TYPE_I386: + state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); + break; + default: + assert(0 && "no compact unwind for arch"); + } +} + + +} // namespace compact_unwind +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/compact_unwind.h b/ld64/src/ld/passes/compact_unwind.h new file mode 100644 index 0000000..c6dc9f5 --- /dev/null +++ b/ld64/src/ld/passes/compact_unwind.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __COMPACT_UNWIND_H__ +#define __COMPACT_UNWIND_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace compact_unwind { + +// called by linker to add __unwind_info section table +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace compact_unwind +} // namespace passes +} // namespace ld + +#endif // __COMPACT_UNWIND_H__ diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp new file mode 100644 index 0000000..f847cc0 --- /dev/null +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -0,0 +1,339 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include "ld.hpp" +#include "dtrace_dof.h" + +// prototype for entry point in libdtrace.dylib +typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); + + +namespace ld { +namespace passes { +namespace dtrace { + +class File; // forward reference + +class Atom : public ld::Atom { +public: + Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz); + + virtual ld::File* file() const { return (ld::File*)&_file; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { memcpy(buffer, _content, _size); } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_fixups[_fixups.size()]; } + +protected: + friend class File; + virtual ~Atom() {} + + class File& _file; + const char* _name; + const uint8_t* _content; + uint64_t _size; + mutable std::vector _fixups; +}; + + +class File : public ld::File +{ +public: + File(const char* segmentName, const char* sectionName, const char* pth, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ord, + const char* symbolName="dof") + : ld::File(pth, 0, ord), + _atom(*this, symbolName, fileContent, fileLength), + _section(segmentName, sectionName, ld::Section::typeDtraceDOF) { } + virtual ~File() {} + + virtual bool forEachAtom(AtomHandler& h) const { h.doAtom(_atom); return true; } + virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; } + + void reserveFixups(unsigned int count) { _atom._fixups.reserve(count); } + void addSectionFixup(const ld::Fixup& f) { _atom._fixups.push_back(f); } + ld::Atom& atom() { return _atom; } +private: + friend class Atom; + + Atom _atom; + ld::Section _section; +}; + +Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(strdup(n)), _content(content), _size(sz) {} + + + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +struct DTraceProbeInfo { + DTraceProbeInfo(const ld::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} + const ld::Atom* atom; + uint32_t offset; + const char* probeName; +}; +typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; +typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; + + + +void doPass(const Options& opts, ld::Internal& internal) +{ + static bool log = false; + + // only make __dof section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // scan all atoms looking for dtrace probes + std::vector probeSites; + std::vector isEnabledSites; + std::map atomToDtraceTypes; + for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeCode ) + continue; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); + break; + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); + break; + case ld::Fixup::kindDtraceExtra: + atomToDtraceTypes[atom].insert(fit->u.name); + break; + default: + break; + } + } + } + } + + // if no probes, we're done + if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) ) + return; + + ld::Fixup::Kind storeKind = ld::Fixup::kindNone; + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + storeKind = ld::Fixup::kindStoreBigEndian32; + break; + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: + storeKind = ld::Fixup::kindStoreLittleEndian32; + break; + default: + throw "unsupported arch for DOF"; + } + + // partition probes by provider name + // The symbol names looks like: + // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] + // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] + ProviderToProbes providerToProbes; + std::vector emptyList; + for(std::vector::iterator it = probeSites.begin(); it != probeSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( it->atom->coalescedAway() ) + continue; + const char* providerStart = &it->probeName[16]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + for(std::vector::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( it->atom->coalescedAway() ) + continue; + const char* providerStart = &it->probeName[20]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + + // create a DOF section for each provider + int dofIndex=1; + CStringSet sectionNamesUsed; + for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { + const char* providerName = pit->first; + const std::vector& probes = pit->second; + + // open library and find dtrace_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); + createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); + // build list of typedefs/stability infos for this provider + CStringSet types; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + std::map::iterator pos = atomToDtraceTypes.find(it->atom); + if ( pos != atomToDtraceTypes.end() ) { + for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { + const char* providerStart = strchr(*sit, '$')+1; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char aProviderName[providerEnd-providerStart+1]; + strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); + if ( strcmp(aProviderName, providerName) == 0 ) + types.insert(*sit); + } + } + } + } + int typeCount = types.size(); + const char* typeNames[typeCount]; + //fprintf(stderr, "types for %s:\n", providerName); + uint32_t index = 0; + for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { + typeNames[index] = *it; + //fprintf(stderr, "\t%s\n", *it); + ++index; + } + + // build list of probe/isenabled sites + const uint32_t probeCount = probes.size(); + const char* probeNames[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + index = 0; + for(std::vector::const_iterator it = probes.begin(); it != probes.end(); ++it) { + probeNames[index] = it->probeName; + funtionNames[index] = it->atom->name(); + offsetsInDOF[index] = 0; + ++index; + } + if ( log ) { + fprintf(stderr, "calling libtrace to create DOF:\n"); + fprintf(stderr, " types::\n"); + for(int i=0; i < typeCount; ++i) + fprintf(stderr, " [%u]\t %s\n", i, typeNames[i]); + fprintf(stderr, " probes::\n"); + for(uint32_t i=0; i < probeCount; ++i) + fprintf(stderr, " [%u]\t %s in %s\n", i, probeNames[i], funtionNames[i]); + } + + // call dtrace library to create DOF section + size_t dofSectionSize; + uint8_t* p = (*pCreateDOF)(opts.architecture(), typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + char* sectionName = new char[18]; // alloc new string, pass ownership to File() + strcpy(sectionName, "__dof_"); + strlcpy(§ionName[6], providerName, 10); + // create unique section name so each DOF is in its own section + if ( sectionNamesUsed.count(sectionName) != 0 ) { + sectionName[15] = '0'; + sectionName[16] = '\0'; + while ( sectionNamesUsed.count(sectionName) != 0 ) { + ++sectionName[15]; + } + } + sectionNamesUsed.insert(sectionName); + char symbolName[strlen(providerName)+64]; + sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); + File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, 0, symbolName); + if ( log ) { + fprintf(stderr, "libdtrace created DOF of size %ld\n", dofSectionSize); + } + // add references + f->reserveFixups(3*probeCount); + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, probes[i].atom)); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, probes[i].offset)); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, &f->atom())); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k4of4, storeKind)); + } + // insert new section + internal.addAtom(f->atom()); + } + else { + throw "error creating dtrace DOF section"; + } + } + + + +} + + +} // namespace dtrace +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/dtrace_dof.h b/ld64/src/ld/passes/dtrace_dof.h new file mode 100644 index 0000000..df69b87 --- /dev/null +++ b/ld64/src/ld/passes/dtrace_dof.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DTRACE_DOF_H__ +#define __DTRACE_DOF_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace dtrace { + +// called by linker to process atoms in Internal and add DOF sections as needed +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace dtrace +} // namespace passes +} // namespace ld + +#endif // __DTRACE_DOF_H__ diff --git a/ld64/src/ld/passes/dylibs.cpp b/ld64/src/ld/passes/dylibs.cpp new file mode 100644 index 0000000..2b4452b --- /dev/null +++ b/ld64/src/ld/passes/dylibs.cpp @@ -0,0 +1,86 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#include +#include +#include +#include +#include + +#include + +#include "ld.hpp" +#include "dylibs.h" + +namespace ld { +namespace passes { +namespace dylibs { + + +class WillBeUsed +{ +public: + bool operator()(ld::dylib::File* dylib) const { + return dylib->willRemoved(); + } +}; + + +void doPass(const Options& opts, ld::Internal& state) +{ +// const bool log = false; + + // only optimize dylibs in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // clear "willRemoved" bit on all dylibs + for (std::vector::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + aDylib->setWillBeRemoved(false); + } + for (std::vector::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + // set "willRemoved" bit on implicit dylibs that did not provide any exports + if ( aDylib->implicitlyLinked() && !aDylib->explicitlyLinked() && !aDylib->providedExportAtom() ) + aDylib->setWillBeRemoved(true); + // set "willRemoved" bit on dead strippable explicit dylibs that did not provide any exports + if ( aDylib->explicitlyLinked() && aDylib->deadStrippable() && !aDylib->providedExportAtom() ) + aDylib->setWillBeRemoved(true); + // 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()); + +} + + +} // namespace dylibs +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/dylibs.h b/ld64/src/ld/passes/dylibs.h new file mode 100644 index 0000000..3e4db11 --- /dev/null +++ b/ld64/src/ld/passes/dylibs.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#ifndef __DYLIBS_H__ +#define __DYLIBS_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace dylibs { + +// called by linker to optimize use of dylibs +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace dylibs +} // namespace passes +} // namespace ld + +#endif // __DYLIBS_H__ diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp new file mode 100644 index 0000000..ff18e00 --- /dev/null +++ b/ld64/src/ld/passes/got.cpp @@ -0,0 +1,276 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include "ld.hpp" +#include "got.h" + +namespace ld { +namespace passes { +namespace got { + +class File; // forward reference + +class GOTEntryAtom : public ld::Atom { +public: + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), + _target(target) + { _fixup.weakImport = weakImport; internal.addAtom(*this); } + + 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 _target->name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + const ld::Atom* _target; + + static ld::Section _s_section; +}; + +ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable) +{ + switch (fixup->kind) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + // start by assuming this can be optimized + *optimizable = true; + // cannot do LEA optimization if target is in another dylib + if ( targetOfGOT->definition() == ld::Atom::definitionProxy ) + *optimizable = false; + // cannot do LEA optimization if target in __huge section + if ( internal.usingHugeSections && (targetOfGOT->size() > 1024*1024) + && ( (targetOfGOT->section().type() == ld::Section::typeZeroFill) + || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) { + *optimizable = false; + } + if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { + // cannot do LEA optimization if target is weak exported symbol + if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) + *optimizable = false; + // cannot do LEA optimization if target is interposable + if ( opts.interposable(targetOfGOT->name()) ) + *optimizable = false; + // cannot do LEA optimization if target is resolver function + if ( targetOfGOT->contentType() == ld::Atom::typeResolver ) + *optimizable = false; + // cannot do LEA optimization for flat-namespace + if ( opts.nameSpace() != Options::kTwoLevelNameSpace ) + *optimizable = false; + } + return true; + case ld::Fixup::kindStoreX86PCRel32GOT: + *optimizable = false; + return true; + default: + break; + } + + return false; +} + +struct AtomByNameSorter +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } +}; + +void doPass(const Options& opts, ld::Internal& internal) +{ + const bool log = false; + + // only make got section in final linked images + 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 + std::vector atomsReferencingGOT; + std::map gotMap; + std::map weakImportMap; + atomsReferencingGOT.reserve(128); + for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + bool atomUsesGOT = false; + const ld::Atom* targetOfGOT = NULL; + 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]; + break; + case ld::Fixup::bindingDirectlyBound: + targetOfGOT = fit->u.target; + break; + default: + break; + } + bool optimizable; + if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + continue; + if ( optimizable ) { + // change from load of GOT entry to lea of target + if ( log ) fprintf(stderr, "optimized GOT usage in %s to %s\n", atom->name(), targetOfGOT->name()); + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + case ld::Fixup::bindingDirectlyBound: + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = targetOfGOT; + fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; + break; + default: + assert(0 && "unsupported GOT reference"); + break; + } + } + else { + // remember that we need to use GOT in this function + if ( log ) fprintf(stderr, "found GOT use in %s to %s\n", atom->name(), targetOfGOT->name()); + if ( !atomUsesGOT ) { + atomsReferencingGOT.push_back(atom); + atomUsesGOT = true; + } + gotMap[targetOfGOT] = NULL; + // record weak_import attribute + 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(); + } + } + else { + // target in weakImportMap, check for weakness mismatch + if ( pos->second != fit->weakImport ) { + // found mismatch + switch ( opts.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", targetOfGOT->name()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + } + } + } + + // make GOT entries + for (std::map::iterator it = gotMap.begin(); it != gotMap.end(); ++it) { + it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first]); + } + + // update atoms to use GOT entries + for (std::vector::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) { + const ld::Atom* atom = *it; + const ld::Atom* targetOfGOT = NULL; + ld::Fixup::iterator fitThatSetTarget = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + targetOfGOT = NULL; + fitThatSetTarget = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex]; + fitThatSetTarget = fit; + break; + case ld::Fixup::bindingDirectlyBound: + targetOfGOT = fit->u.target; + fitThatSetTarget = fit; + break; + default: + break; + } + bool optimizable; + if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + continue; + if ( !optimizable ) { + // GOT use not optimized away, update to bind to GOT entry + assert(fitThatSetTarget != NULL); + switch ( fitThatSetTarget->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + case ld::Fixup::bindingDirectlyBound: + fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound; + fitThatSetTarget->u.target = gotMap[targetOfGOT]; + break; + default: + assert(0 && "unsupported GOT reference"); + break; + } + } + } + } + + // sort new atoms so links are consistent + for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeNonLazyPointer ) { + std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); + } + } +} + + +} // namespace got +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/got.h b/ld64/src/ld/passes/got.h new file mode 100644 index 0000000..7aae0f8 --- /dev/null +++ b/ld64/src/ld/passes/got.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __GOT_H__ +#define __GOT_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace got { + +// called by linker to create GOT entries and optimize GOT loads into LEAs instead +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace got +} // namespace passes +} // namespace ld + +#endif // __GOT_H__ diff --git a/ld64/src/ld/passes/huge.cpp b/ld64/src/ld/passes/huge.cpp new file mode 100644 index 0000000..932122a --- /dev/null +++ b/ld64/src/ld/passes/huge.cpp @@ -0,0 +1,113 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +#include + +#include "ld.hpp" +#include "huge.h" + +namespace ld { +namespace passes { +namespace huge { + +class NullAtom +{ +public: + bool operator()(const ld::Atom* atom) const { + return (atom == NULL); + } +}; + +void doPass(const Options& opts, ld::Internal& state) +{ + const bool log = false; + + // only make make __huge section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // only make make __huge section for x86_64 + if ( opts.architecture() != CPU_TYPE_X86_64 ) + return; + + // only needed if some (non-linkedit) atoms have an addresss >2GB from base address + state.usingHugeSections = false; + uint64_t address = 0; + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typePageZero ) + continue; + if ( sect->type() == ld::Section::typeStack ) + continue; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( (address > 0x7FFFFFFFLL) && !sect->isSectionHidden() ) { + state.usingHugeSections = true; + if (log) fprintf(stderr, "atom: %s is >2GB (0x%09llX), so enabling huge mode\n", atom->name(), address); + break; + } + address += atom->size(); + } + if ( state.usingHugeSections ) + break; + } + if ( !state.usingHugeSections ) + return; + + // move all zero fill atoms that >1MB in size to a new __huge section + ld::Internal::FinalSection* hugeSection = state.getFinalSection(ld::Section("__DATA", "__huge", ld::Section::typeZeroFill)); + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect == hugeSection ) + continue; + if ( sect->type() == ld::Section::typeZeroFill ) { + bool movedSome = false; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->size() > 1024*1024 ) { + hugeSection->atoms.push_back(atom); + if (log) fprintf(stderr, "moved to __huge: %s, size=%llu\n", atom->name(), atom->size()); + *ait = NULL; // change atom to NULL for later bulk removal + movedSome = true; + } + } + if ( movedSome ) + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NullAtom()), sect->atoms.end()); + } + } + + +} + + +} // namespace huge +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/huge.h b/ld64/src/ld/passes/huge.h new file mode 100644 index 0000000..77dc43e --- /dev/null +++ b/ld64/src/ld/passes/huge.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __HUGE_H__ +#define __HUGE_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace huge { + +// called by linker to move large zero-fill atoms to the __huge section +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace huge +} // namespace passes +} // namespace ld + +#endif // __HUGE_H__ diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp new file mode 100644 index 0000000..f420b9f --- /dev/null +++ b/ld64/src/ld/passes/objc.cpp @@ -0,0 +1,1174 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +#include "ld.hpp" +#include "objc.h" + +namespace ld { +namespace passes { +namespace objc { + + + +struct objc_image_info { + uint32_t version; // initially 0 + uint32_t flags; +}; + +#define OBJC_IMAGE_IS_REPLACEMENT (1<<0) +#define OBJC_IMAGE_SUPPORTS_GC (1<<1) +#define OBJC_IMAGE_REQUIRES_GC (1<<2) +#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) +#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) + + + +// +// This class is the 8 byte section containing ObjC flags +// +template +class ObjCImageInfoAtom : public ld::Atom { +public: + ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, + bool compaction, bool objcReplacementClasses, bool abi2); + + 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 "objc image info"; } + virtual uint64_t size() const { return sizeof(objc_image_info); } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + memcpy(buffer, &_content, sizeof(objc_image_info)); + } + +private: + objc_image_info _content; + + static ld::Section _s_sectionABI1; + static ld::Section _s_sectionABI2; +}; + +template ld::Section ObjCImageInfoAtom::_s_sectionABI1("__OBJC", "__image_info", ld::Section::typeUnclassified); +template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", "__objc_imageinfo", ld::Section::typeUnclassified); + + +template +ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, + bool objcReplacementClasses, bool abi2) + : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) +{ + + uint32_t value = 0; + if ( objcReplacementClasses ) + value = OBJC_IMAGE_IS_REPLACEMENT; + switch ( objcConstraint ) { + case ld::File::objcConstraintNone: + case ld::File::objcConstraintRetainRelease: + if ( compaction ) + warning("ignoring -objc_gc_compaction because code not compiled for ObjC garbage collection"); + break; + case ld::File::objcConstraintRetainReleaseOrGC: + value |= OBJC_IMAGE_SUPPORTS_GC; + if ( compaction ) + value |= OBJC_IMAGE_SUPPORTS_COMPACTION; + break; + case ld::File::objcConstraintGC: + value |= OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC; + if ( compaction ) + value |= OBJC_IMAGE_SUPPORTS_COMPACTION; + break; + } + + _content.version = 0; + A::P::E::set32(_content.flags, value); +} + + + +// +// This class is for a new Atom which is an ObjC method list created by merging method lists from categories +// +template +class MethodListAtom : public ld::Atom { +public: + MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta, + const std::vector* categories, + std::set& deadAtoms); + + virtual const ld::File* file() const { return _file; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc merged method list"; } + virtual uint64_t size() const { return _methodCount*3*sizeof(pint_t) + 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + bzero(buffer, size()); + A::P::E::set32(*((uint32_t*)(&buffer[0])), 24); + A::P::E::set32(*((uint32_t*)(&buffer[4])), _methodCount); + } + 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::uint_t pint_t; + + const ld::File* _file; + unsigned int _methodCount; + std::vector _fixups; + + static ld::Section _s_section; +}; + +template +ld::Section MethodListAtom::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified); + + +// +// This class is for a new Atom which is an ObjC protocol list created by merging protocol lists from categories +// +template +class ProtocolListAtom : public ld::Atom { +public: + ProtocolListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, + const std::vector* categories, + std::set& deadAtoms); + + virtual const ld::File* file() const { return _file; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc merged protocol list"; } + virtual uint64_t size() const { return (_protocolCount+1)*sizeof(pint_t); } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + bzero(buffer, size()); + A::P::setP(*((pint_t*)(buffer)), _protocolCount); + } + 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::uint_t pint_t; + + const ld::File* _file; + unsigned int _protocolCount; + std::vector _fixups; + + static ld::Section _s_section; +}; + +template +ld::Section ProtocolListAtom::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified); + + + +// +// This class is for a new Atom which is an ObjC property list created by merging property lists from categories +// +template +class PropertyListAtom : public ld::Atom { +public: + PropertyListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, + const std::vector* categories, + std::set& deadAtoms); + + virtual const ld::File* file() const { return _file; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc merged property list"; } + virtual uint64_t size() const { return _propertyCount*2*sizeof(pint_t) + 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + bzero(buffer, size()); + A::P::E::set32(((uint32_t*)(buffer))[0], 2*sizeof(pint_t)); // sizeof(objc_property) + A::P::E::set32(((uint32_t*)(buffer))[1], _propertyCount); + } + 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::uint_t pint_t; + + const ld::File* _file; + unsigned int _propertyCount; + std::vector _fixups; + + static ld::Section _s_section; +}; + +template +ld::Section PropertyListAtom::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified); + + + + + +// +// This class is used to create an Atom that replaces an atom from a .o file that holds a class_ro_t. +// It is needed because there is no way to add Fixups to an existing atom. +// +template +class ClassROOverlayAtom : public ld::Atom { +public: + ClassROOverlayAtom(const ld::Atom* classROAtom); + + // overrides of ld::Atom + virtual const ld::File* file() const { return _atom->file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return _atom->translationUnitSource(dir, nm); } + virtual const char* name() const { return _atom->name(); } + virtual uint64_t size() const { return _atom->size(); } + virtual uint64_t objectAddress() const { return _atom->objectAddress(); } + virtual void copyRawContent(uint8_t buffer[]) const + { _atom->copyRawContent(buffer); } + virtual const uint8_t* rawContentPointer() const + { return _atom->rawContentPointer(); } + virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + { return _atom->contentHash(ibt); } + virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + { return _atom->canCoalesceWith(rhs,ibt); } + + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + + void addProtocolListFixup(); + void addPropertyListFixup(); + void addMethodListFixup(); + +private: + typedef typename A::P::uint_t pint_t; + + const ld::Atom* _atom; + std::vector _fixups; +}; + +template +ClassROOverlayAtom::ClassROOverlayAtom(const ld::Atom* classROAtom) + : ld::Atom(classROAtom->section(), ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + classROAtom->symbolTableInclusion(), false, false, false, classROAtom->alignment()), + _atom(classROAtom) +{ + // ensure all attributes are same as original + this->setAttributesFromAtom(*classROAtom); + + // copy fixups from orginal atom + for (ld::Fixup::iterator fit=classROAtom->fixupsBegin(); fit != classROAtom->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + _fixups.push_back(fixup); + } +} + + +// +// Base class for reading and updating existing ObjC atoms from .o files +// +template +class ObjCData { +public: + static const ld::Atom* getPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, unsigned int offset, bool* hasAddend=NULL); + static void setPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, + unsigned int offset, const ld::Atom* newAtom); + typedef typename A::P::uint_t pint_t; +}; + +template +const ld::Atom* ObjCData::getPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, unsigned int offset, bool* hasAddend) +{ + const ld::Atom* target = NULL; + if ( hasAddend != NULL ) + *hasAddend = false; + for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) { + if ( fit->offsetInAtom == offset ) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingNone: + if ( fit->kind == ld::Fixup::kindAddAddend ) { + if ( hasAddend != NULL ) + *hasAddend = true; + } + break; + default: + break; + } + } + } + return target; +} + +template +void ObjCData::setPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, + unsigned int offset, const ld::Atom* newAtom) +{ + for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) { + if ( fit->offsetInAtom == offset ) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + state.indirectBindingTable[fit->u.bindingIndex] = newAtom; + return; + case ld::Fixup::bindingDirectlyBound: + fit->u.target = newAtom; + return; + default: + break; + } + } + } + assert(0 && "could not update method list"); +} + + + +// +// Helper class for reading and updating existing ObjC category atoms from .o files +// +template +class Category : public ObjCData { +public: + static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getProperties(ld::Internal& state, const ld::Atom* contentAtom); + static uint32_t size() { return 6*sizeof(pint_t); } +private: + typedef typename A::P::uint_t pint_t; +}; + + +template +const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t)); // category_t.cls +} + +template +const ld::Atom* Category::getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData::getPointerInContent(state, contentAtom, 2*sizeof(pint_t)); // category_t.instanceMethods +} + +template +const ld::Atom* Category::getClassMethods(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData::getPointerInContent(state, contentAtom, 3*sizeof(pint_t)); // category_t.classMethods +} + +template +const ld::Atom* Category::getProtocols(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData::getPointerInContent(state, contentAtom, 4*sizeof(pint_t)); // category_t.protocols +} + +template +const ld::Atom* Category::getProperties(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData::getPointerInContent(state, contentAtom, 5*sizeof(pint_t)); // category_t.instanceProperties +} + + +template +class MethodList : public ObjCData { +public: + static uint32_t count(ld::Internal& state, const ld::Atom* methodListAtom) { + const uint32_t* methodListData = (uint32_t*)(methodListAtom->rawContentPointer()); + return A::P::E::get32(methodListData[1]); // method_list_t.count + } +}; + +template +class ProtocolList : public ObjCData { +public: + static uint32_t count(ld::Internal& state, const ld::Atom* protocolListAtom) { + pint_t* protocolListData = (pint_t*)(protocolListAtom->rawContentPointer()); + return A::P::getP(*protocolListData); // protocol_list_t.count + } +private: + typedef typename A::P::uint_t pint_t; +}; + +template +class PropertyList : public ObjCData { +public: + static uint32_t count(ld::Internal& state, const ld::Atom* protocolListAtom) { + uint32_t* protocolListData = (uint32_t*)(protocolListAtom->rawContentPointer()); + return A::P::E::get32(protocolListData[1]); // property_list_t.count + } +private: + typedef typename A::P::uint_t pint_t; +}; + + + +// +// Helper class for reading and updating existing ObjC class atoms from .o files +// +template +class Class : public ObjCData { +public: + static const ld::Atom* getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getClassMethodList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set& deadAtoms); + static const ld::Atom* setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set& deadAtoms); + static const ld::Atom* setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* propertyListAtom, std::set& deadAtoms); + static const ld::Atom* setClassMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set& deadAtoms); + static const ld::Atom* setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set& deadAtoms); + static uint32_t size() { return 5*sizeof(pint_t); } + static unsigned int class_ro_header_size(); +private: + typedef typename A::P::uint_t pint_t; + static const ld::Atom* getROData(ld::Internal& state, const ld::Atom* classAtom); +}; + +template <> unsigned int Class::class_ro_header_size() { return 16; } +template <> unsigned int Class::class_ro_header_size() { return 12;} +template <> unsigned int Class::class_ro_header_size() { return 12; } + + +template +const ld::Atom* Class::getROData(ld::Internal& state, const ld::Atom* classAtom) +{ + return ObjCData::getPointerInContent(state, classAtom, 4*sizeof(pint_t)); // class_t.data + +} + +template +const ld::Atom* Class::getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + return ObjCData::getPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t)); // class_ro_t.baseMethods +} + +template +const ld::Atom* Class::getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + return ObjCData::getPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t)); // class_ro_t.baseProtocols +} + +template +const ld::Atom* Class::getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + return ObjCData::getPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t)); // class_ro_t.baseProperties +} + +template +const ld::Atom* Class::getClassMethodList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* metaClassAtom = ObjCData::getPointerInContent(state, classAtom, 0); // class_t.isa + assert(metaClassAtom != NULL); + return Class::getInstanceMethodList(state, metaClassAtom); +} + +template +const ld::Atom* Class::setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set& deadAtoms) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + // if the base class does not already have a method list, we need to create an overlay + if ( getInstanceMethodList(state, classAtom) == NULL ) { + ClassROOverlayAtom* overlay = new ClassROOverlayAtom(classROAtom); + //fprintf(stderr, "replace class RO atom %p with %p for method list in class atom %s\n", classROAtom, overlay, classAtom->name()); + overlay->addMethodListFixup(); + ObjCData::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data + deadAtoms.insert(classROAtom); + ObjCData::setPointerInContent(state, overlay, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods + return overlay; + } + ObjCData::setPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods + return NULL; // means classRO atom was not replaced +} + +template +const ld::Atom* Class::setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set& deadAtoms) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + // if the base class does not already have a protocol list, we need to create an overlay + if ( getInstanceProtocolList(state, classAtom) == NULL ) { + ClassROOverlayAtom* overlay = new ClassROOverlayAtom(classROAtom); + //fprintf(stderr, "replace class RO atom %p with %p for protocol list in class atom %s\n", classROAtom, overlay, classAtom->name()); + overlay->addProtocolListFixup(); + ObjCData::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data + deadAtoms.insert(classROAtom); + ObjCData::setPointerInContent(state, overlay, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols + return overlay; + } + //fprintf(stderr, "set class RO atom %p protocol list in class atom %s\n", classROAtom, classAtom->name()); + ObjCData::setPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols + return NULL; // means classRO atom was not replaced +} + +template +const ld::Atom* Class::setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set& deadAtoms) +{ + // meta class also points to same protocol list as class + const ld::Atom* metaClassAtom = ObjCData::getPointerInContent(state, classAtom, 0); // class_t.isa + //fprintf(stderr, "setClassProtocolList(), classAtom=%p %s, metaClass=%p %s\n", classAtom, classAtom->name(), metaClassAtom, metaClassAtom->name()); + assert(metaClassAtom != NULL); + return setInstanceProtocolList(state, metaClassAtom, protocolListAtom, deadAtoms); +} + + + +template +const ld::Atom* Class::setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* propertyListAtom, std::set& deadAtoms) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + // if the base class does not already have a property list, we need to create an overlay + if ( getInstancePropertyList(state, classAtom) == NULL ) { + ClassROOverlayAtom* overlay = new ClassROOverlayAtom(classROAtom); + //fprintf(stderr, "replace class RO atom %p with %p for property list in class atom %s\n", classROAtom, overlay, classAtom->name()); + overlay->addPropertyListFixup(); + ObjCData::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data + deadAtoms.insert(classROAtom); + ObjCData::setPointerInContent(state, overlay, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties + return overlay; + } + ObjCData::setPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties + return NULL; // means classRO atom was not replaced +} + +template +const ld::Atom* Class::setClassMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set& deadAtoms) +{ + // class methods is just instance methods of metaClass + const ld::Atom* metaClassAtom = ObjCData::getPointerInContent(state, classAtom, 0); // class_t.isa + assert(metaClassAtom != NULL); + return setInstanceMethodList(state, metaClassAtom, methodListAtom, deadAtoms); +} + + + +template <> +void ClassROOverlayAtom::addMethodListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 2*8; // class_ro_t.baseMethods + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +} + +template <> +void ClassROOverlayAtom::addMethodListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 2*4; // class_ro_t.baseMethods + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + +template <> +void ClassROOverlayAtom::addMethodListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 2*4; // class_ro_t.baseMethods + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + + + +template <> +void ClassROOverlayAtom::addProtocolListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 3*8; // class_ro_t.baseProtocols + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +} + +template <> +void ClassROOverlayAtom::addProtocolListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + +template <> +void ClassROOverlayAtom::addProtocolListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + + +template <> +void ClassROOverlayAtom::addPropertyListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 6*8; // class_ro_t.baseProperties + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +} + +template <> +void ClassROOverlayAtom::addPropertyListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 6*4; // class_ro_t.baseProperties + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + +template <> +void ClassROOverlayAtom::addPropertyListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class::class_ro_header_size() + 6*4; // class_ro_t.baseProperties + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + + + + +// +// Encapsulates merging of ObjC categories +// +template +class OptimizeCategories { +public: + static void doit(const Options& opts, ld::Internal& state); + static bool hasInstanceMethods(ld::Internal& state, const std::vector* categories); + static bool hasClassMethods(ld::Internal& state, const std::vector* categories); + static bool hasProtocols(ld::Internal& state, const std::vector* categories); + static bool hasProperties(ld::Internal& state, const std::vector* categories); + + + static unsigned int class_ro_baseMethods_offset(); +private: + typedef typename A::P::uint_t pint_t; + +}; + + +template +bool OptimizeCategories::hasInstanceMethods(ld::Internal& state, const std::vector* categories) +{ + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* methodList = Category::getInstanceMethods(state, categoryAtom); + if ( methodList != NULL ) { + if ( MethodList::count(state, methodList) > 0 ) + return true; + } + } + return false; +} + + +template +bool OptimizeCategories::hasClassMethods(ld::Internal& state, const std::vector* categories) +{ + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* methodList = Category::getClassMethods(state, categoryAtom); + if ( methodList != NULL ) { + if ( MethodList::count(state, methodList) > 0 ) + return true; + } + } + return false; +} + +template +bool OptimizeCategories::hasProtocols(ld::Internal& state, const std::vector* categories) +{ + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* protocolListAtom = Category::getProtocols(state, categoryAtom); + if ( protocolListAtom != NULL ) { + if ( ProtocolList::count(state, protocolListAtom) > 0 ) { + return true; + } + } + } + return false; +} + + +template +bool OptimizeCategories::hasProperties(ld::Internal& state, const std::vector* categories) +{ + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* propertyListAtom = Category::getProperties(state, categoryAtom); + if ( propertyListAtom != NULL ) { + if ( PropertyList::count(state, propertyListAtom) > 0 ) + return true; + } + } + return false; +} + + + +// +// Helper for std::remove_if +// +class OptimizedAway { +public: + OptimizedAway(const std::set& oa) : _dead(oa) {} + bool operator()(const ld::Atom* atom) const { + return ( _dead.count(atom) != 0 ); + } +private: + const std::set& _dead; +}; + +template +void OptimizeCategories::doit(const Options& opts, ld::Internal& state) +{ + // first find all categories referenced by __objc_nlcatlist section + std::set nlcatListAtoms; + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) { + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* categoryListElementAtom = *ait; + for (unsigned int offset=0; offset < categoryListElementAtom->size(); offset += sizeof(pint_t)) { + const ld::Atom* categoryAtom = ObjCData::getPointerInContent(state, categoryListElementAtom, offset); + //fprintf(stderr, "offset=%d, cat=%p %s\n", offset, categoryAtom, categoryAtom->name()); + assert(categoryAtom != NULL); + nlcatListAtoms.insert(categoryAtom); + } + } + } + } + + // build map of all classes in this image that have categories on them + typedef std::map*> CatMap; + CatMap classToCategories; + std::set deadAtoms; + ld::Internal::FinalSection* methodListSection = NULL; + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeObjC2CategoryList ) { + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* categoryListElementAtom = *ait; + bool hasAddend; + const ld::Atom* categoryAtom = ObjCData::getPointerInContent(state, categoryListElementAtom, 0, &hasAddend); + if ( hasAddend || (categoryAtom->symbolTableInclusion() == ld::Atom::symbolTableNotIn)) { + // gcc-4.0 uses 'L' labels on categories which disables this optimization + //warning("__objc_catlist element does not point to start of category"); + continue; + } + assert(categoryAtom != NULL); + assert(categoryAtom->size() == Category::size()); + // ignore categories also in __objc_nlcatlist + if ( nlcatListAtoms.count(categoryAtom) != 0 ) + continue; + const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom); + assert(categoryOnClassAtom != NULL); + if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) { + // only look at classes defined in this image + CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); + if ( pos == classToCategories.end() ) { + classToCategories[categoryOnClassAtom] = new std::vector(); + } + classToCategories[categoryOnClassAtom]->push_back(categoryAtom); + // mark category atom and catlist atom as dead + deadAtoms.insert(categoryAtom); + deadAtoms.insert(categoryListElementAtom); + } + } + } + // record method list section + if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + methodListSection = sect; + } + + // if found some categories + if ( classToCategories.size() != 0 ) { + assert(methodListSection != NULL); + // alter each class definition to have new method list which includes all category methods + for (CatMap::iterator it=classToCategories.begin(); it != classToCategories.end(); ++it) { + const ld::Atom* classAtom = it->first; + const std::vector* categories = it->second; + assert(categories->size() != 0); + // if any category adds instance methods, generate new merged method list, and replace + if ( OptimizeCategories::hasInstanceMethods(state, categories) ) { + const ld::Atom* baseInstanceMethodListAtom = Class::getInstanceMethodList(state, classAtom); + const ld::Atom* newInstanceMethodListAtom = new MethodListAtom(state, baseInstanceMethodListAtom, false, categories, deadAtoms); + const ld::Atom* newClassRO = Class::setInstanceMethodList(state, classAtom, newInstanceMethodListAtom, deadAtoms); + // add new method list to final sections + methodListSection->atoms.push_back(newInstanceMethodListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + } + // if any category adds class methods, generate new merged method list, and replace + if ( OptimizeCategories::hasClassMethods(state, categories) ) { + const ld::Atom* baseClassMethodListAtom = Class::getClassMethodList(state, classAtom); + const ld::Atom* newClassMethodListAtom = new MethodListAtom(state, baseClassMethodListAtom, true, categories, deadAtoms); + const ld::Atom* newClassRO = Class::setClassMethodList(state, classAtom, newClassMethodListAtom, deadAtoms); + // add new method list to final sections + methodListSection->atoms.push_back(newClassMethodListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + } + // if any category adds protocols, generate new merged protocol list, and replace + if ( OptimizeCategories::hasProtocols(state, categories) ) { + const ld::Atom* baseProtocolListAtom = Class::getInstanceProtocolList(state, classAtom); + const ld::Atom* newProtocolListAtom = new ProtocolListAtom(state, baseProtocolListAtom, categories, deadAtoms); + const ld::Atom* newClassRO = Class::setInstanceProtocolList(state, classAtom, newProtocolListAtom, deadAtoms); + const ld::Atom* newMetaClassRO = Class::setClassProtocolList(state, classAtom, newProtocolListAtom, deadAtoms); + // add new protocol list to final sections + methodListSection->atoms.push_back(newProtocolListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + if ( newMetaClassRO != NULL ) { + assert(strcmp(newMetaClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newMetaClassRO); + } + } + // if any category adds properties, generate new merged property list, and replace + if ( OptimizeCategories::hasProperties(state, categories) ) { + const ld::Atom* basePropertyListAtom = Class::getInstancePropertyList(state, classAtom); + const ld::Atom* newPropertyListAtom = new PropertyListAtom(state, basePropertyListAtom, categories, deadAtoms); + const ld::Atom* newClassRO = Class::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms); + // add new property list to final sections + methodListSection->atoms.push_back(newPropertyListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + } + + } + + // remove dead atoms + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), OptimizedAway(deadAtoms)), sect->atoms.end()); + } + } +} + + +template +MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta, + const std::vector* categories, std::set& deadAtoms) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _methodCount(0) +{ + unsigned int fixupCount = 0; + // if base class has method list, then associate new method list with file defining class + if ( baseMethodList != NULL ) { + _file = baseMethodList->file(); + // calculate total size of merge method lists + _methodCount = MethodList::count(state, baseMethodList); + deadAtoms.insert(baseMethodList); + fixupCount = baseMethodList->fixupsEnd() - baseMethodList->fixupsBegin(); + } + for (std::vector::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { + const ld::Atom* categoryMethodListAtom; + if ( meta ) + categoryMethodListAtom = Category::getClassMethods(state, *ait); + else + categoryMethodListAtom = Category::getInstanceMethods(state, *ait); + if ( categoryMethodListAtom != NULL ) { + _methodCount += MethodList::count(state, categoryMethodListAtom); + fixupCount += (categoryMethodListAtom->fixupsEnd() - categoryMethodListAtom->fixupsBegin()); + deadAtoms.insert(categoryMethodListAtom); + // if base class did not have method list, associate new method list with file the defined category + if ( _file == NULL ) + _file = categoryMethodListAtom->file(); + } + } + //if ( baseMethodList != NULL ) + // fprintf(stderr, "total merged method count=%u for baseMethodList=%s\n", _methodCount, baseMethodList->name()); + //else + // fprintf(stderr, "total merged method count=%u\n", _methodCount); + //fprintf(stderr, "total merged fixup count=%u\n", fixupCount); + + // copy fixups and adjust offsets (in reverse order to simulator objc runtime) + _fixups.reserve(fixupCount); + uint32_t slide = 0; + for (std::vector::const_reverse_iterator rit=categories->rbegin(); rit != categories->rend(); ++rit) { + const ld::Atom* categoryMethodListAtom; + if ( meta ) + categoryMethodListAtom = Category::getClassMethods(state, *rit); + else + categoryMethodListAtom = Category::getInstanceMethods(state, *rit); + if ( categoryMethodListAtom != NULL ) { + for (ld::Fixup::iterator fit=categoryMethodListAtom->fixupsBegin(); fit != categoryMethodListAtom->fixupsEnd(); ++fit) { + 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()); + } + slide += 3*sizeof(pint_t) * MethodList::count(state, categoryMethodListAtom); + } + } + // add method list from base class last + if ( baseMethodList != NULL ) { + for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + } + } +} + + +template +ProtocolListAtom::ProtocolListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, + const std::vector* categories, std::set& deadAtoms) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _protocolCount(0) +{ + unsigned int fixupCount = 0; + if ( baseProtocolList != NULL ) { + // if base class has protocol list, then associate new protocol list with file defining class + _file = baseProtocolList->file(); + // calculate total size of merged protocol list + _protocolCount = ProtocolList::count(state, baseProtocolList); + deadAtoms.insert(baseProtocolList); + fixupCount = baseProtocolList->fixupsEnd() - baseProtocolList->fixupsBegin(); + } + for (std::vector::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { + const ld::Atom* categoryProtocolListAtom = Category::getProtocols(state, *ait); + if ( categoryProtocolListAtom != NULL ) { + _protocolCount += ProtocolList::count(state, categoryProtocolListAtom); + fixupCount += (categoryProtocolListAtom->fixupsEnd() - categoryProtocolListAtom->fixupsBegin()); + deadAtoms.insert(categoryProtocolListAtom); + // if base class did not have protocol list, associate new protocol list with file the defined category + if ( _file == NULL ) + _file = categoryProtocolListAtom->file(); + } + } + //fprintf(stderr, "total merged protocol count=%u\n", _protocolCount); + //fprintf(stderr, "total merged fixup count=%u\n", fixupCount); + + // copy fixups and adjust offsets + _fixups.reserve(fixupCount); + uint32_t slide = 0; + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryProtocolListAtom = Category::getProtocols(state, *it); + if ( categoryProtocolListAtom != NULL ) { + for (ld::Fixup::iterator fit=categoryProtocolListAtom->fixupsBegin(); fit != categoryProtocolListAtom->fixupsEnd(); ++fit) { + 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()); + } + slide += sizeof(pint_t) * ProtocolList::count(state, categoryProtocolListAtom); + } + } + // add method list from base class last + if ( baseProtocolList != NULL ) { + for (ld::Fixup::iterator fit=baseProtocolList->fixupsBegin(); fit != baseProtocolList->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + } + } +} + + +template +PropertyListAtom::PropertyListAtom(ld::Internal& state, const ld::Atom* basePropertyList, + const std::vector* categories, std::set& deadAtoms) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _propertyCount(0) +{ + unsigned int fixupCount = 0; + if ( basePropertyList != NULL ) { + // if base class has property list, then associate new property list with file defining class + _file = basePropertyList->file(); + // calculate total size of merged property list + _propertyCount = PropertyList::count(state, basePropertyList); + deadAtoms.insert(basePropertyList); + fixupCount = basePropertyList->fixupsEnd() - basePropertyList->fixupsBegin(); + } + for (std::vector::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { + const ld::Atom* categoryPropertyListAtom = Category::getProperties(state, *ait); + if ( categoryPropertyListAtom != NULL ) { + _propertyCount += PropertyList::count(state, categoryPropertyListAtom); + fixupCount += (categoryPropertyListAtom->fixupsEnd() - categoryPropertyListAtom->fixupsBegin()); + deadAtoms.insert(categoryPropertyListAtom); + // if base class did not have property list, associate new property list with file the defined category + if ( _file == NULL ) + _file = categoryPropertyListAtom->file(); + } + } + //fprintf(stderr, "total merged property count=%u\n", _propertyCount); + //fprintf(stderr, "total merged fixup count=%u\n", fixupCount); + + // copy fixups and adjust offsets + _fixups.reserve(fixupCount); + uint32_t slide = 0; + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryPropertyListAtom = Category::getProperties(state, *it); + if ( categoryPropertyListAtom != NULL ) { + for (ld::Fixup::iterator fit=categoryPropertyListAtom->fixupsBegin(); fit != categoryPropertyListAtom->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + //fprintf(stderr, "offset=0x%08X, binding=%d\n", fixup.offsetInAtom, fixup.binding); + //if ( fixup.binding == ld::Fixup::bindingDirectlyBound ) + // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name()); + //else if ( fixup.binding == ld::Fixup::bindingsIndirectlyBound ) + // fprintf(stderr, "offset=0x%08X, indirect index=%u, name=%s\n", fixup.offsetInAtom, fixup.u.bindingIndex, + // (char*)(state.indirectBindingTable[fixup.u.bindingIndex]->rawContentPointer())); + } + slide += 2*sizeof(pint_t) * PropertyList::count(state, categoryPropertyListAtom); + } + } + // add method list from base class last + if ( basePropertyList != NULL ) { + for (ld::Fixup::iterator fit=basePropertyList->fixupsBegin(); fit != basePropertyList->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + } + } +} + + + + +void doPass(const Options& opts, ld::Internal& state) +{ + // only make image info section if objc was used + if ( state.objcObjectConstraint != ld::File::objcConstraintNone ) { + + // verify dylibs are GC compatible with object files + if ( state.objcObjectConstraint != state.objcDylibConstraint ) { + if ( (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease) + && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) { + throw "Linked dylibs built for retain/release but object files built for GC-only"; + } + else if ( (state.objcDylibConstraint == ld::File::objcConstraintGC) + && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) { + throw "Linked dylibs built for GC-only but object files built for retain/release"; + } + } + + const bool compaction = opts.objcGcCompaction(); + + // add image info atom + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, true)); + break; + case CPU_TYPE_I386: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false)); + break; + case CPU_TYPE_POWERPC: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, false)); + break; + case CPU_TYPE_ARM: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, true)); + break; + case CPU_TYPE_POWERPC64: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, true)); + break; + default: + assert(0 && "unknown objc arch"); + } + } + + if ( opts.objcCategoryMerging() ) { + // optimize classes defined in this linkage unit by merging in categories also in this linkage unit + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + OptimizeCategories::doit(opts, state); + break; + case CPU_TYPE_I386: + // disable optimization until fully tested + //if ( opts.objCABIVersion2POverride() ) + // OptimizeCategories::doit(opts, state); + break; + case CPU_TYPE_ARM: + // disable optimization until fully tested + //OptimizeCategories::doit(opts, state); + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_POWERPC: + break; + default: + assert(0 && "unknown objc arch"); + } + } +} + + +} // namespace objc +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/objc.h b/ld64/src/ld/passes/objc.h new file mode 100644 index 0000000..d92def6 --- /dev/null +++ b/ld64/src/ld/passes/objc.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#ifndef __OBJC_H__ +#define __OBJC_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace objc { + +// called by linker to optimize ObjC data structures +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace objc +} // namespace passes +} // namespace ld + +#endif // __OBJC_H__ diff --git a/ld64/src/ld/passes/order_file.cpp b/ld64/src/ld/passes/order_file.cpp new file mode 100644 index 0000000..9e336ae --- /dev/null +++ b/ld64/src/ld/passes/order_file.cpp @@ -0,0 +1,531 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +#include +#include + +#include "ld.hpp" +#include "order_file.h" + +namespace ld { +namespace passes { +namespace order_file { + +// +// The purpose of this pass is to take the graph of all Atoms and produce an ordered +// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must +// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified +// in an order_file are sequenced as in the order_file and before Atoms not specified, +// 4) Atoms in the same section from the same .o file should be contiguous and sequenced +// in the same order they were in the .o file, 5) Atoms in the same Section but which came +// from different .o files should be sequenced in the same order that the .o files +// were passed to the linker (i.e. command line order). +// +// The way this is implemented is that the linker passes a "base ordinal" to each File +// as it is constructed. Add each atom has an objectAddress() method. Then +// sorting is just sorting by section, then by file ordinal, then by object address. +// +// If an order_file is specified, it gets more complicated. First, an override-ordinal map +// is created. It causes the sort routine to ignore the value returned by ordinal() and objectAddress() +// and use the override value instead. Next some Atoms must be layed out consecutively +// (e.g. hand written assembly that does not end with return, but rather falls into +// the next label). This is modeled in via a kindNoneFollowOn fixup. The use of +// kindNoneFollowOn fixups produces "clusters" of atoms that must stay together. +// If an order_file tries to move one atom, it may need to move a whole cluster. The +// algorithm to do this models clusters using two maps. The "starts" maps maps any +// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a +// cluster to the next Atom in the cluster. With this in place, while processing an +// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is +// given ordinal overrides. +// + +class Layout +{ +public: + Layout(const Options& opts, ld::Internal& state); + void doPass(); +private: + + class Comparer { + public: + Comparer(const Layout& l) : _layout(l) {} + bool operator()(const ld::Atom* left, const ld::Atom* right); + private: + const Layout& _layout; + }; + + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAtom; + + typedef std::map AtomToAtom; + + typedef std::map AtomToOrdinal; + + const ld::Atom* findAtom(const Options::OrderedSymbol& orderedSymbol); + void buildNameTable(); + void buildFollowOnTables(); + void buildOrdinalOverrideMap(); + const ld::Atom* follower(const ld::Atom* atom); + static bool matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName); + bool orderableSection(const ld::Internal::FinalSection*); + + const Options& _options; + ld::Internal& _state; + AtomToAtom _followOnStarts; + AtomToAtom _followOnNexts; + NameToAtom _nameTable; + std::vector _nameCollisionAtoms; + AtomToOrdinal _ordinalOverrideMap; + Comparer _comparer; + bool _haveOrderFile; + + static bool _s_log; +}; + +bool Layout::_s_log = false; + +Layout::Layout(const Options& opts, ld::Internal& state) + : _options(opts), _state(state), _comparer(*this), _haveOrderFile(opts.orderedSymbolsCount() != 0) +{ +} + + +bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) +{ + if ( left == right ) + return false; + + // magic section$start symbol always sorts to the start of its section + if ( left->contentType() == ld::Atom::typeSectionStart ) + return true; + if ( right->contentType() == ld::Atom::typeSectionStart ) + return false; + + // if a -order_file is specified, then sorting is altered to sort those symbols first + if ( _layout._haveOrderFile ) { + AtomToOrdinal::const_iterator leftPos = _layout._ordinalOverrideMap.find(left); + AtomToOrdinal::const_iterator rightPos = _layout._ordinalOverrideMap.find(right); + AtomToOrdinal::const_iterator end = _layout._ordinalOverrideMap.end(); + if ( leftPos != end ) { + if ( rightPos != end ) { + // both left and right are overridden, so compare overridden ordinals + return leftPos->second < rightPos->second; + } + else { + // left is overridden and right is not, so left < right + return true; + } + } + else { + if ( rightPos != end ) { + // right is overridden and left is not, so right < left + return false; + } + else { + // neither are overridden, + // fall into default sorting below + } + } + } + + // magic section$end symbol always sorts to the end of its section + if ( left->contentType() == ld::Atom::typeSectionEnd ) + return false; + if ( right->contentType() == ld::Atom::typeSectionEnd ) + return true; + + // the __common section can have real or tentative definitions + // we want the real ones to sort before tentative ones + bool leftIsTent = (left->definition() == ld::Atom::definitionTentative); + bool rightIsTent = (right->definition() == ld::Atom::definitionTentative); + if ( leftIsTent != rightIsTent ) + return rightIsTent; + +#if 0 + // initializers are auto sorted to start of section + if ( !fInitializerSet.empty() ) { + bool leftFirst = (fInitializerSet.count(left) != 0); + bool rightFirst = (fInitializerSet.count(right) != 0); + if ( leftFirst != rightFirst ) + return leftFirst; + } + + // terminators are auto sorted to end of section + if ( !fTerminatorSet.empty() ) { + bool leftLast = (fTerminatorSet.count(left) != 0); + bool rightLast = (fTerminatorSet.count(right) != 0); + if ( leftLast != rightLast ) + return rightLast; + } +#endif + + // sort by .o order + uint32_t leftFileOrdinal = left->file()->ordinal(); + uint32_t rightFileOrdinal = right->file()->ordinal(); + if ( leftFileOrdinal != rightFileOrdinal ) + return leftFileOrdinal< rightFileOrdinal; + + // tentative defintions have no address in .o file, they are traditionally laid out by name + if ( leftIsTent && rightIsTent ) + return (strcmp(left->name(), right->name()) < 0); + + // lastly sort by atom address + int64_t addrDiff = left->objectAddress() - right->objectAddress(); + if ( addrDiff == 0 ) { + // have same address so one might be an alias, and aliases need to sort before target + bool leftIsAlias = left->isAlias(); + bool rightIsAlias = right->isAlias(); + if ( leftIsAlias != rightIsAlias ) + return leftIsAlias; + + // both at same address, sort by name + return (strcmp(left->name(), right->name()) < 0); + } + return (addrDiff < 0); +} + +bool Layout::matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName) +{ + if ( objectFileLeafName == NULL ) + return true; + const char* atomFullPath = atom->file()->path(); + const char* lastSlash = strrchr(atomFullPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) + return true; + } + else { + if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) + return true; + } + return false; +} + + +bool Layout::orderableSection(const ld::Internal::FinalSection* sect) +{ + // atoms in only some sections are ordered + switch ( sect->type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeCode: + case ld::Section::typeZeroFill: + return true; + case ld::Section::typeImportProxies: + return false; + default: + // if section has command line aliases, then we must apply ordering so aliases layout before targets + if ( _options.haveCmdLineAliases() ) { + for (std::vector::const_iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->isAlias() ) + return true; + } + } + break; + } + return false; +} + +void Layout::buildNameTable() +{ + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + // atoms in some special sections are never ordered + if ( ! orderableSection(sect) ) + continue; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableIn ) { + const char* name = atom->name(); + if ( name != NULL) { + // static function or data + NameToAtom::iterator pos = _nameTable.find(name); + if ( pos == _nameTable.end() ) + _nameTable[name] = atom; + else { + _nameTable[name] = NULL; // collision, denote with NULL + _nameCollisionAtoms.push_back(atom); + } + } + } + } + } +} + + +const ld::Atom* Layout::findAtom(const Options::OrderedSymbol& orderedSymbol) +{ + // look for name in _nameTable + NameToAtom::iterator pos = _nameTable.find(orderedSymbol.symbolName); + if ( pos != _nameTable.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); + return pos->second; + } + if ( pos->second == NULL ) { + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector::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; + } + } + } + } + } + + return NULL; +} + +const ld::Atom* Layout::follower(const ld::Atom* atom) +{ + for (const ld::Atom* a = _followOnStarts[atom]; a != NULL; a = _followOnNexts[a]) { + assert(a != NULL); + if ( _followOnNexts[a] == atom ) { + return a; + } + } + // no follower, first in chain + return NULL; +} + +void Layout::buildFollowOnTables() +{ + // first make a pass to find all follow-on references and build start/next maps + // which are a way to represent clusters of atoms that must layout together + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( !orderableSection(sect) ) + continue; + 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) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + const ld::Atom* followOnAtom = fit->u.target; + if ( _s_log ) fprintf(stderr, "ref %p %s -> %p %s\n", atom, atom->name(), followOnAtom, followOnAtom->name()); + assert(_followOnNexts.count(atom) == 0); + _followOnNexts[atom] = followOnAtom; + if ( _followOnStarts.count(atom) == 0 ) { + // first time atom has been seen, make it start of chain + _followOnStarts[atom] = atom; + if ( _s_log ) fprintf(stderr, " start %s -> %s\n", atom->name(), atom->name()); + } + if ( _followOnStarts.count(followOnAtom) == 0 ) { + // first time followOnAtom has been seen, make atom start of chain + _followOnStarts[followOnAtom] = _followOnStarts[atom]; + if ( _s_log ) fprintf(stderr, " start %s -> %s\n", followOnAtom->name(), _followOnStarts[atom]->name()); + } + else { + if ( _followOnStarts[followOnAtom] == followOnAtom ) { + // followOnAtom atom already start of another chain, hook together + // and change all to use atom as start + const ld::Atom* a = followOnAtom; + while ( true ) { + assert(_followOnStarts[a] == followOnAtom); + _followOnStarts[a] = _followOnStarts[atom]; + if ( _s_log ) fprintf(stderr, " adjust start for %s -> %s\n", a->name(), _followOnStarts[atom]->name()); + AtomToAtom::iterator pos = _followOnNexts.find(a); + if ( pos != _followOnNexts.end() ) + a = pos->second; + else + break; + } + } + else { + // attempt to insert atom into existing followOn chain + const ld::Atom* curPrevToFollowOnAtom = this->follower(followOnAtom); + assert(curPrevToFollowOnAtom != NULL); + assert((atom->size() == 0) || (curPrevToFollowOnAtom->size() == 0)); + if ( atom->size() == 0 ) { + // insert alias into existing chain right before followOnAtom + _followOnNexts[curPrevToFollowOnAtom] = atom; + _followOnNexts[atom] = followOnAtom; + _followOnStarts[atom] = _followOnStarts[followOnAtom]; + } + else { + // insert real atom into existing chain right before alias of followOnAtom + const ld::Atom* curPrevPrevToFollowOn = this->follower(curPrevToFollowOnAtom); + if ( curPrevPrevToFollowOn == NULL ) { + // nothing previous, so make this a start of a new chain + _followOnNexts[atom] = curPrevToFollowOnAtom; + for (const ld::Atom* a = atom; a != NULL; a = _followOnNexts[a]) { + if ( _s_log ) fprintf(stderr, " adjust start for %s -> %s\n", a->name(), atom->name()); + _followOnStarts[a] = atom; + } + } + else { + // is previous, insert into existing chain before previous + _followOnNexts[curPrevPrevToFollowOn] = atom; + _followOnNexts[atom] = curPrevToFollowOnAtom; + _followOnStarts[atom] = _followOnStarts[curPrevToFollowOnAtom]; + } + } + } + } + } + } + } + } + + if ( _s_log ) { + for(AtomToAtom::iterator it = _followOnStarts.begin(); it != _followOnStarts.end(); ++it) + fprintf(stderr, "start %s -> %s\n", it->first->name(), it->second->name()); + + for(AtomToAtom::iterator it = _followOnNexts.begin(); it != _followOnNexts.end(); ++it) + fprintf(stderr, "next %s -> %s\n", it->first->name(), (it->second != NULL) ? it->second->name() : "null"); + } +} + +void Layout::buildOrdinalOverrideMap() +{ + // if no -order_file, then skip building override map + if ( ! _haveOrderFile ) + return; + + // build fast name->atom table + this->buildNameTable(); + + // handle .o files that cannot have their atoms rearranged + // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals + uint32_t index = 0; + uint32_t matchCount = 0; + for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) { + const ld::Atom* atom = this->findAtom(*it); + if ( atom != NULL ) { + AtomToAtom::iterator start = _followOnStarts.find(atom); + if ( start != _followOnStarts.end() ) { + // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together + for(const ld::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = _followOnNexts[nextAtom]) { + AtomToOrdinal::iterator pos = _ordinalOverrideMap.find(nextAtom); + if ( pos == _ordinalOverrideMap.end() ) { + _ordinalOverrideMap[nextAtom] = index++; + if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->file()->path()); + } + else { + if (_s_log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", + atom->name(), index, _followOnStarts[atom]->name(), _ordinalOverrideMap[atom] ); + } + } + } + else { + _ordinalOverrideMap[atom] = index; + if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->file()->path()); + } + ++matchCount; + } + else { + if ( _options.printOrderFileStatistics() ) { + if ( it->objectFileName == NULL ) + warning("can't find match for order_file entry: %s", it->symbolName); + else + warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName); + } + } + ++index; + } + if ( _options.printOrderFileStatistics() && (_options.orderedSymbolsCount() != matchCount) ) { + warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() ); + } + + +} + +void Layout::doPass() +{ + // handle .o files that cannot have their atoms rearranged + this->buildFollowOnTables(); + + // + this->buildOrdinalOverrideMap(); + + + // sort atoms in each section + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( orderableSection(sect) ) { + std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); + } + else { + // bubble sort any typeSectionStart atom to the beginning + bool moving = false; + for (int i=sect->atoms.size()-1; i >= 0; --i) { + if ( moving ) { + const ld::Atom* temp = sect->atoms[i]; + sect->atoms[i] = sect->atoms[i+1]; + sect->atoms[i+1] = temp; + } + if ( sect->atoms[i]->contentType() == ld::Atom::typeSectionStart ) + moving = true; + } + // bubble sort any typeSectionEnd atom to the end + moving = false; + for (unsigned int i=sect->atoms.size(); i < sect->atoms.size(); ++i) { + if ( moving ) { + const ld::Atom* temp = sect->atoms[i]; + sect->atoms[i] = sect->atoms[i-1]; + sect->atoms[i-1] = temp; + } + if ( sect->atoms[i]->contentType() == ld::Atom::typeSectionEnd ) + moving = true; + } + } + } + + //fprintf(stderr, "Sorted atoms:\n"); + //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; + // fprintf(stderr, "\t%s\t%s\n", sect->sectionName(), atom->name()); + // } + //} + +} + + +void doPass(const Options& opts, ld::Internal& state) +{ + Layout layout(opts, state); + layout.doPass(); +} + + +} // namespace order_file +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/order_file.h b/ld64/src/ld/passes/order_file.h new file mode 100644 index 0000000..7a2aa87 --- /dev/null +++ b/ld64/src/ld/passes/order_file.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __ORDER_FILE_H__ +#define __ORDER_FILE_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace order_file { + +// called by linker to re-arrange atoms to improve locality and performance +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace order_file +} // namespace passes +} // namespace ld + +#endif // __ORDER_FILE_H__ diff --git a/ld64/src/ld/passes/stubs/make_stubs.h b/ld64/src/ld/passes/stubs/make_stubs.h new file mode 100644 index 0000000..7330bee --- /dev/null +++ b/ld64/src/ld/passes/stubs/make_stubs.h @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace passes { +namespace stubs { + +// called by linker to process atoms in Internal and add stubs as needed +extern void doPass(const Options& opts, ld::Internal& internal); + +} // namespace stubs +} // namespace passes +} // namespace ld + diff --git a/ld64/src/ld/passes/stubs/stub_arm.hpp b/ld64/src/ld/passes/stubs/stub_arm.hpp new file mode 100644 index 0000000..e8dedda --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_arm.hpp @@ -0,0 +1,416 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ + +// already in ld::passes::stubs namespace +namespace arm { + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "non-lazy pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + + +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(28, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, compressedImageCache(pass)), + _fixup2(28, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(28, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 16), + _fixup4(28, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32), + _fixup5(32, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, compressedFastBinder(pass)), + _fixup6(32, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup7(32, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 28), + _fixup8(32, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return " stub helpers"; } + virtual uint64_t size() const { return 36; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // push lazy-info-offset + OSWriteLittleInt32(&buffer[ 0], 0, 0xe52dc004); // str ip, [sp, #-4]! + // push address of dyld_mageLoaderCache + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59fc010); // ldr ip, L1 + OSWriteLittleInt32(&buffer[ 8], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[12], 0, 0xe52dc004); // str ip, [sp, #-4]! + // jump through _fast_lazy_bind + OSWriteLittleInt32(&buffer[16], 0, 0xe59fc008); // ldr ip, L2 + OSWriteLittleInt32(&buffer[20], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[24], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[28], 0, 0x00000000); // L1: .long fFastStubGOTAtom - (helperhelper+16) + OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // L2: .long _fast_lazy_bind - (helperhelper+28) + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + ld::Fixup _fixup6; + ld::Fixup _fixup7; + ld::Fixup _fixup8; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom* lazyPointer) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, helperHelper(pass)), + _fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[4], 0, 0xea000000); // b _helperhelper + OSWriteLittleInt32(&buffer[8], 0, 0); // .long lazy-info-offset + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom* lazyPointer) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + ld::Atom::symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1( 4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, &stubTo), + _fixup2(32, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, lazyPointer), + _fixup3(32, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup4(32, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 20), + _fixup5(32, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 36; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe92d400f); // push {r0, r1, r2, r3, lr} + OSWriteLittleInt32(&buffer[ 4], 0, 0xebfffffd); // bl _foo + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59fc010); // ldr ip, [pc, #16] + OSWriteLittleInt32(&buffer[12], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[16], 0, 0xe58c0000); // str r0, [ip] + OSWriteLittleInt32(&buffer[20], 0, 0xe1a0c000); // mov ip, r0 + OSWriteLittleInt32(&buffer[24], 0, 0xe8bd400f); // pop {r0, r1, r2, r3, lr} + OSWriteLittleInt32(&buffer[28], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // .long foo$lazyptr - helper + 20 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup5)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, + bool weakImport, bool close) + : ld::Atom(close ? _s_sectionClose : _s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _helper(pass, stubTo, this), + _resolverHelper(pass, stubTo, this), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionClose; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionClose("__DATA", "__lazy_symbol", ld::Section::typeLazyPointerClose); + + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub4", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _fixup(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub4", ld::Section::typeStub); + + + + +class StubCloseAtom : public ld::Atom { +public: + StubCloseAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, true), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMLoad12, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xE59FF000); // ldr pc, [pc, #foo$lazy_ptr] + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubCloseAtom::_s_section("__TEXT", "__symbolstub1", ld::Section::typeStubClose); + + + + +} // namespace arm + diff --git a/ld64/src/ld/passes/stubs/stub_arm_classic.hpp b/ld64/src/ld/passes/stubs/stub_arm_classic.hpp new file mode 100644 index 0000000..50870f4 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_arm_classic.hpp @@ -0,0 +1,155 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// already in ld::passes::stubs namespace +namespace arm { +namespace classic { + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(forLazyDylib ? _s_sectionLazy : _s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub4", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub4", ld::Section::typeStub); + + + + +} // namespace classic +} // namespace arm + diff --git a/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp b/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp new file mode 100644 index 0000000..6862b60 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp @@ -0,0 +1,190 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace ppc { +namespace classic { + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, for64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, + for64 ? ld::Fixup::kindStoreTargetAddressBigEndian64 : ld::Fixup::kindStoreTargetAddressBigEndian32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo), + _for64(for64) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return _for64 ? 8 : 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + const bool _for64; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStorePPCPicHigh16AddLow), + _fixup5(20, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup6(20, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup7(20, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup8(20, ld::Fixup::k4of4, for64 ? ld::Fixup::kindStorePPCPicLow14 : ld::Fixup::kindStorePPCPicLow16), + _for64(for64) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 32; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + if ( _for64 ) + OSWriteBigInt32(&buffer[20], 0, 0xe98b0001);// ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + else + OSWriteBigInt32(&buffer[20], 0, 0x858b0000);// lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + mutable ld::Fixup _fixup4; + mutable ld::Fixup _fixup5; + mutable ld::Fixup _fixup6; + mutable ld::Fixup _fixup7; + mutable ld::Fixup _fixup8; + const bool _for64; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub1", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), + _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStorePPCAbsHigh16AddLow), + _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup4(4, ld::Fixup::k2of2, for64 ? ld::Fixup::kindStorePPCAbsLow14 : ld::Fixup::kindStorePPCAbsLow16), + _for64(for64) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_foo$lazy_ptr) + if ( _for64 ) + OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001);// ldu r12,lo16(L_foo$lazy_ptr)(r11) + else + OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000);// lwzu r12,lo16(L_foo$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + mutable ld::Fixup _fixup4; + const bool _for64; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub1", ld::Section::typeStub); + + +} // namespace classic +} // namespace ppc + diff --git a/ld64/src/ld/passes/stubs/stub_x86.hpp b/ld64/src/ld/passes/stubs/stub_x86.hpp new file mode 100644 index 0000000..0eda4ad --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_x86.hpp @@ -0,0 +1,347 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ + + +// already in ld::passes::stubs namespace +namespace x86 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + 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 "fast binder pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { pass.addAtom(*this); } + + 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 "image cache pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(1, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, compressedImageCache(pass)), + _fixup2(7, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, compressedFastBinder(pass)) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushl $dyld_ImageLoaderCache + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xFF; // jmp *_fast_lazy_bind + buffer[6] = 0x25; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x90; // nop + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(1, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup2(1, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32), + _fixup3(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, helperHelper(pass)) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushl $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, &stubTo), + _fixup2(9, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, lazyPointer), + _fixup3(18, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 22; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[ 0] = 0x50; // push %eax + buffer[ 1] = 0x51; // push %ecx + buffer[ 2] = 0x52; // push %edx + buffer[ 3] = 0xE8; // call foo + buffer[ 4] = 0x00; + buffer[ 5] = 0x00; + buffer[ 6] = 0x00; + buffer[ 7] = 0x00; + buffer[ 8] = 0xA3; // movl %eax,foo$lazy_ptr + buffer[ 9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x5A; // pop %edx + buffer[14] = 0x59; // pop %ecx + buffer[15] = 0x58; // pop %eax + buffer[16] = 0xFF; // jmp *foo$lazy_ptr + buffer[17] = 0x25; + buffer[18] = 0x00; + buffer[19] = 0x00; + buffer[20] = 0x00; + buffer[21] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__symbol_stub", ld::Section::typeStub); + + + +} // namespace x86 + diff --git a/ld64/src/ld/passes/stubs/stub_x86_64.hpp b/ld64/src/ld/passes/stubs/stub_x86_64.hpp new file mode 100644 index 0000000..e74b37d --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_x86_64.hpp @@ -0,0 +1,366 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86_64 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "image cache pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(3, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, compressedImageCache(pass)), + _fixup2(11, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, compressedFastBinder(pass)) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x4C; // leaq dyld_mageLoaderCache(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x41; // pushq %r11 + buffer[8] = 0x53; + buffer[9] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[10] = 0x25; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x90; // nop + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(1, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup2(1, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32), + _fixup3(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, helperHelper(pass)) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushq $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(10, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, &stubTo), + _fixup2(17, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, lazyPointer), + _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 36; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[ 0] = 0x50; // push %rax + buffer[ 1] = 0x57; // push %rdi + buffer[ 2] = 0x56; // push %rsi + buffer[ 3] = 0x52; // push %rdx + buffer[ 4] = 0x51; // push %rcx + buffer[ 5] = 0x41; // push %r8 + buffer[ 6] = 0x50; + buffer[ 7] = 0x41; // push %r9 + buffer[ 8] = 0x51; + buffer[ 9] = 0xE8; // call foo + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x48; // movq %rax,foo$lazy_pointer(%rip) + buffer[15] = 0x89; + buffer[16] = 0x05; + buffer[17] = 0x00; + buffer[18] = 0x00; + buffer[19] = 0x00; + buffer[20] = 0x00; + buffer[21] = 0x41; // pop %r9 + buffer[22] = 0x59; + buffer[23] = 0x41; // pop %r8 + buffer[24] = 0x58; + buffer[25] = 0x59; // pop %rcx + buffer[26] = 0x5A; // pop %rdx + buffer[27] = 0x5E; // pop %rsi + buffer[28] = 0x5F; // pop %rdi + buffer[29] = 0x58; // pop %rax + buffer[30] = 0xFF; // jmp *foo$lazy_ptr(%rip) + buffer[31] = 0x25; + buffer[32] = 0x00; + buffer[33] = 0x00; + buffer[34] = 0x00; + buffer[35] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + stubToResolver ? &_resolverHelper : + (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + + +} // namespace x86_64 + diff --git a/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp b/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp new file mode 100644 index 0000000..14507a2 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp @@ -0,0 +1,165 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86_64 { +namespace classic { + + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom& lazyPointer, bool forLazyDylib) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(3, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &lazyPointer), + _fixup2(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0xE9; // jmp dyld_stub_binding_helper + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, stubTo, *this, forLazyDylib), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &_helper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + +} // namespace classic +} // namespace x86_64 + diff --git a/ld64/src/ld/passes/stubs/stub_x86_classic.hpp b/ld64/src/ld/passes/stubs/stub_x86_classic.hpp new file mode 100644 index 0000000..0562218 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_x86_classic.hpp @@ -0,0 +1,163 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86 { +namespace classic { + + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom& lazyPointer, bool forLazyDylib) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(1, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &lazyPointer), + _fixup2(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushl $foo$lazy_ptr + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp dyld_stub_binding_helper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _helper(pass, stubTo, *this, forLazyDylib), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_helper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__symbol_stub", ld::Section::typeStub); + + +} // namespace classic +} // namespace x86 + diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp new file mode 100644 index 0000000..0e23c27 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -0,0 +1,385 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ + + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Options.h" +#include "ld.hpp" + +#include "make_stubs.h" + + +namespace ld { +namespace passes { +namespace stubs { + +class Pass { +public: + Pass(const Options& opts); + void process(ld::Internal& internal); + void addAtom(const ld::Atom& atom) { _internal->addAtom(atom); } + bool usingCompressedLINKEDIT() const { return _compressedLINKEDIT; } + ld::Internal* internal() { return _internal; } + + Atom* compressedHelperHelper; + Atom* compressedImageCache; + Atom* compressedFastBinderPointer; + +private: + + struct AtomByNameSorter + { + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } + }; + + const ld::Atom* stubableFixup(const ld::Fixup* fixup, ld::Internal&); + ld::Atom* makeStub(const ld::Atom& target, bool weakImport); + void verifyNoResolverFunctions(ld::Internal& state); + + const Options& _options; + const cpu_type_t _architecture; + const bool _lazyDylibsInUuse; + const bool _compressedLINKEDIT; + const bool _prebind; + const bool _mightBeInSharedRegion; + const bool _pic; + const bool _flatNamespace; + ld::Internal* _internal; + uint32_t _stubCount; + bool _largeText; +}; + +#include "stub_x86_64.hpp" +#include "stub_x86_64_classic.hpp" +#include "stub_x86.hpp" +#include "stub_x86_classic.hpp" +#include "stub_arm.hpp" +#include "stub_arm_classic.hpp" +#include "stub_ppc_classic.hpp" + + + +Pass::Pass(const Options& opts) + : compressedHelperHelper(NULL), + compressedImageCache(NULL), + compressedFastBinderPointer(NULL), + _options(opts), + _architecture(opts.architecture()), + _lazyDylibsInUuse(opts.usingLazyDylibLinking()), + _compressedLINKEDIT(opts.makeCompressedDyldInfo()), + _prebind(opts.prebind()), + _mightBeInSharedRegion(opts.sharedRegionEligible()), + _pic(opts.outputSlidable()), + _flatNamespace(opts.nameSpace() != Options::kTwoLevelNameSpace), + _internal(NULL), _stubCount(0), _largeText(false) +{ + +} + + +const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) +{ + if ( fixup->binding == ld::Fixup::bindingsIndirectlyBound ) { + const ld::Atom* target = state.indirectBindingTable[fixup->u.bindingIndex]; + switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + // create stub if target is in a dylib + if ( target->definition() == ld::Atom::definitionProxy ) + return target; + // use stub if target is a resolver function in same linkage unit + if ( target->contentType() == ld::Atom::typeResolver ) + return target; + if ( target->scope() == ld::Atom::scopeGlobal ) { + // create stub if target is global weak definition in symbol table + if ( (target->definition() == ld::Atom::definitionRegular) + && (target->combine() == ld::Atom::combineByName) + && ((target->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (target->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) + return target; + // create stub if target is interposable + if ( _options.interposable(target->name()) ) + return target; + if ( _flatNamespace ) { + // flat namespace does not indirect calls within main exectuables + if ( _options.outputKind() == Options::kDynamicExecutable ) + return NULL; + // create stub if target is global and building -flat dylib or bundle + return target; + } + } + break; + default: + if ( target->contentType() == ld::Atom::typeResolver ) { + // any pointer to a resolver needs to change to pointer to stub + return target; + } + break; + } + } + return NULL; +} + + + +ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) +{ + //fprintf(stderr, "makeStub(target=%p %s in sect %s)\n", &target, target.name(), target.section().sectionName()); + bool stubToGlobalWeakDef = ( (target.scope() == ld::Atom::scopeGlobal) + && (target.definition() == ld::Atom::definitionRegular) + && (target.combine() == ld::Atom::combineByName) ); + + bool forLazyDylib = false; + const ld::dylib::File* dylib = dynamic_cast(target.file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + forLazyDylib = true; + bool stubToResolver = (target.contentType() == ld::Atom::typeResolver); + + switch ( _architecture ) { + case CPU_TYPE_POWERPC: + if ( _pic ) + return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, false, weakImport); + else + return new ld::passes::stubs::ppc::classic::StubNoPICAtom(*this, target, forLazyDylib, false, weakImport); + break; + case CPU_TYPE_POWERPC64: + return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, true, weakImport); + break; + case CPU_TYPE_I386: + if ( usingCompressedLINKEDIT() && !forLazyDylib ) + return new ld::passes::stubs::x86::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else + return new ld::passes::stubs::x86::classic::StubAtom(*this, target, forLazyDylib, weakImport); + break; + case CPU_TYPE_X86_64: + if ( usingCompressedLINKEDIT() && !forLazyDylib ) + return new ld::passes::stubs::x86_64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else + return new ld::passes::stubs::x86_64::classic::StubAtom(*this, target, forLazyDylib, weakImport); + break; + case CPU_TYPE_ARM: + if ( usingCompressedLINKEDIT() && !forLazyDylib ) { + if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText ) + return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else if ( _pic ) + return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else + return new ld::passes::stubs::arm::StubNoPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + } + else { + if ( _pic ) + return new ld::passes::stubs::arm::classic::StubPICAtom(*this, target, forLazyDylib, weakImport); + else + return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport); + } + break; + } + throw "unsupported arch for stub"; +} + + +void Pass::verifyNoResolverFunctions(ld::Internal& state) +{ + 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; + if ( atom->contentType() == ld::Atom::typeResolver ) + throwf("resolver function '%s' not supported in type of output", atom->name()); + } + } +} + +void Pass::process(ld::Internal& state) +{ + switch ( _options.outputKind() ) { + case Options::kObjectFile: + // these kinds don't use stubs and can have resolver functions + return; + case Options::kKextBundle: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kDyld: + // these kinds don't use stubs and cannot have resolver functions + verifyNoResolverFunctions(state); + return; + case Options::kDynamicLibrary: + // uses stubs and can have resolver functions + break; + case Options::kDynamicExecutable: + case Options::kDynamicBundle: + // these kinds do use stubs and cannot have resolver functions + verifyNoResolverFunctions(state); + break; + } + + // walk all atoms and fixups looking for stubable references + // don't create stubs inline because that could invalidate the sections iterator + std::vector atomsCallingStubs; + std::map stubFor; + std::map weakImportMap; + atomsCallingStubs.reserve(128); + uint64_t codeSize = 0; + 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; + codeSize += atom->size(); + bool atomNeedsStub = false; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const ld::Atom* stubableTargetOfFixup = stubableFixup(fit, state); + if ( stubableTargetOfFixup != NULL ) { + if ( !atomNeedsStub ) { + atomsCallingStubs.push_back(atom); + atomNeedsStub = true; + } + stubFor[stubableTargetOfFixup] = NULL; + // record weak_import attribute + std::map::iterator pos = weakImportMap.find(stubableTargetOfFixup); + 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 + if ( pos->second != fit->weakImport ) { + // found mismatch + switch ( _options.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", stubableTargetOfFixup->name()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + } + // all resolver functions must have a corresponding stub + if ( atom->contentType() == ld::Atom::typeResolver ) { + if ( _options.outputKind() != Options::kDynamicLibrary ) + throwf("resolver functions (%s) can only be used in dylibs", atom->name()); + if ( !_options.makeCompressedDyldInfo() ) { + if ( _options.architecture() == CPU_TYPE_POWERPC ) + throwf("resolver functions (%s) not supported for PowerPC", atom->name()); + else if ( _options.architecture() == CPU_TYPE_ARM ) + throwf("resolver functions (%s) can only be used when targeting iOS 4.2 or later", atom->name()); + else + throwf("resolver functions (%s) can only be used when targeting Mac OS X 10.6 or later", atom->name()); + } + stubFor[atom] = NULL; + } + } + } + + // short circuit if no stubs needed + _internal = &state; + _stubCount = stubFor.size(); + if ( _stubCount == 0 ) + return; + + // lazily check for helper + 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; + + // make stub atoms + for (std::map::iterator it = stubFor.begin(); it != stubFor.end(); ++it) { + it->second = makeStub(*it->first, weakImportMap[it->first]); + } + + // updated atoms to use stubs + for (std::vector::iterator it=atomsCallingStubs.begin(); it != atomsCallingStubs.end(); ++it) { + const ld::Atom* atom = *it; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const ld::Atom* stubableTargetOfFixup = stubableFixup(fit, state); + if ( stubableTargetOfFixup != NULL ) { + ld::Atom* stub = stubFor[stubableTargetOfFixup]; + assert(stub != NULL && "stub not created"); + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = stub; + } + } + } + + // sort new atoms so links are consistent + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeStubHelper: + case ld::Section::typeStub: + case ld::Section::typeStubClose: + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyPointerClose: + std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); + break; + default: + break; + } + } + +} + + +void doPass(const Options& opts, ld::Internal& internal) +{ + Pass pass(opts); + pass.process(internal); +} + + + +} // namespace stubs +} // namespace passes +} // namespace ld + diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp new file mode 100644 index 0000000..f40d018 --- /dev/null +++ b/ld64/src/ld/passes/tlvp.cpp @@ -0,0 +1,303 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include "ld.hpp" +#include "tlvp.h" + +namespace ld { +namespace passes { +namespace tlvp { + +class File; // forward reference + +class TLVEntryAtom : public ld::Atom { +public: + TLVEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), + _target(target) + { _fixup.weakImport = weakImport; internal.addAtom(*this); } + + 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 _target->name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + const ld::Atom* _target; + + static ld::Section _s_section; +}; + +ld::Section TLVEntryAtom::_s_section("__DATA", "__thread_ptrs", ld::Section::typeTLVPointers); + + +static bool optimizable(const Options& opts, const ld::Atom* targetOfTLV) +{ + // cannot do LEA optimization if target is in another dylib + if ( targetOfTLV->definition() == ld::Atom::definitionProxy ) + return false; + if ( targetOfTLV->scope() == ld::Atom::scopeGlobal ) { + // cannot do LEA optimization if target is weak exported symbol + if ( (targetOfTLV->definition() == ld::Atom::definitionRegular) && (targetOfTLV->combine() == ld::Atom::combineByName) ) + return false; + // cannot do LEA optimization if target is interposable + if ( opts.interposable(targetOfTLV->name()) ) + return false; + // cannot do LEA optimization for flat-namespace + if ( opts.nameSpace() != Options::kTwoLevelNameSpace ) + return false; + } + return true; +} + +struct AtomByNameSorter +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } +}; + +struct TlVReferenceCluster +{ + const ld::Atom* targetOfTLV; + ld::Fixup* fixupWithTarget; + ld::Fixup* fixupWithTLVStore; + bool optimizable; +}; + +void doPass(const Options& opts, ld::Internal& internal) +{ + const bool log = false; + + // only make tlv section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // walk all atoms and fixups looking for TLV references and add them to list + std::vector references; + for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + TlVReferenceCluster ref; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + ref.targetOfTLV = NULL; + ref.fixupWithTarget = NULL; + ref.fixupWithTLVStore = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + ref.targetOfTLV = internal.indirectBindingTable[fit->u.bindingIndex]; + ref.fixupWithTarget = fit; + break; + case ld::Fixup::bindingDirectlyBound: + ref.targetOfTLV = fit->u.target; + ref.fixupWithTarget = fit; + break; + default: + break; + } + switch ( fit->kind ) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86Abs32TLVLoad: + ref.fixupWithTLVStore = fit; + break; + default: + break; + } + if ( fit->lastInCluster() && (ref.fixupWithTLVStore != NULL) ) { + ref.optimizable = optimizable(opts, ref.targetOfTLV); + if (log) fprintf(stderr, "found reference to TLV at %s+0x%X to %s\n", + atom->name(), ref.fixupWithTLVStore->offsetInAtom, ref.targetOfTLV->name()); + if ( ! opts.canUseThreadLocalVariables() ) { + throwf("targeted OS version does not support use of thread local variables in %s", atom->name()); + } + references.push_back(ref); + } + } + } + } + + // compute which TLV references will be weak_imports + std::map weakImportMap; + for(std::vector::iterator it=references.begin(); it != references.end(); ++it) { + if ( !it->optimizable ) { + // record weak_import attribute + std::map::iterator pos = weakImportMap.find(it->targetOfTLV); + 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 + if ( pos->second != it->fixupWithTarget->weakImport ) { + // found mismatch + switch ( opts.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", it->targetOfTLV->name()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + } + + // create TLV pointers for TLV references that cannot be optimized + std::map variableToPointerMap; + for(std::map::iterator it=weakImportMap.begin(); it != weakImportMap.end(); ++it) { + std::map::iterator pos = variableToPointerMap.find(it->first); + if ( pos == variableToPointerMap.end() ) { + if (log) fprintf(stderr, "make TLV pointer for %s\n", it->first->name()); + if ( it->first->contentType() != ld::Atom::typeTLV ) + throwf("illegal thread local variable reference to regular symbol %s", it->first->name()); + TLVEntryAtom* tlvp = new TLVEntryAtom(internal, it->first, it->second); + variableToPointerMap[it->first] = tlvp; + } + } + + // sort new tvlp atoms so links are consistent + for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeTLVPointers ) { + std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); + } + } + + // update references to use TLV pointers or TLV object directly + for(std::vector::iterator it=references.begin(); it != references.end(); ++it) { + if ( it->optimizable ) { + // change store to be LEA instead load (mov) + if (log) fprintf(stderr, "optimizing load of TLV to %s into an LEA\n", it->targetOfTLV->name()); + it->fixupWithTLVStore->binding = ld::Fixup::bindingDirectlyBound; + it->fixupWithTLVStore->u.target = it->targetOfTLV; + switch ( it->fixupWithTLVStore->kind ) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA; + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA; + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA; + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA; + break; + default: + assert(0 && "bad store kind for TLV optimization"); + } + } + else { + // change target to be new TLV pointer atom + if (log) fprintf(stderr, "updating load of TLV to %s to load from TLV pointer\n", it->targetOfTLV->name()); + const ld::Atom* tlvpAtom = variableToPointerMap[it->targetOfTLV]; + assert(tlvpAtom != NULL); + it->fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + it->fixupWithTarget->u.target = tlvpAtom; + } + } + + + + // alter tlv definitions to have an offset as third field + for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeTLVDefs ) + continue; + 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) { + if ( fit->offsetInAtom != 0 ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound && "thread variable def contains pointer to global"); + switch( fit->u.target->contentType() ) { + case ld::Atom::typeTLVZeroFill: + case ld::Atom::typeTLVInitialValue: + switch ( fit->kind ) { + case ld::Fixup::kindSetTargetAddress: + fit->kind = ld::Fixup::kindSetTargetTLVTemplateOffset; + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + fit->kind = ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32; + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + fit->kind = ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64; + break; + default: + assert(0 && "bad kind for target in tlv defs"); + break; + } + break; + default: + assert(0 && "wrong content type for target in tlv defs"); + break; + } + } + } + } + } + +} + + + +} // namespace tlvp +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/tlvp.h b/ld64/src/ld/passes/tlvp.h new file mode 100644 index 0000000..0bb8786 --- /dev/null +++ b/ld64/src/ld/passes/tlvp.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + +#ifndef __TLVP_H__ +#define __TLVP_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace tlvp { + +// called by linker to create TLVP entries and optimize TLV loads into LEAs instead +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace tlvp +} // namespace passes +} // namespace ld + +#endif // __TLVP_H__ diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 25352b1..abf7162 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,28 +26,30 @@ #include #include #include -#include - -#include "MachOReaderRelocatable.hpp" +#include +#include +#include +#include -#define LTO_SUPPORT 1 - -#if LTO_SUPPORT - #include "LTOReader.hpp" -#endif +#include "MachOFileAbstraction.hpp" +#include "parsers/macho_relocatable_file.h" +#include "parsers/lto_file.h" static bool sDumpContent= true; static bool sDumpStabs = false; static bool sSort = true; static bool sNMmode = false; +static bool sShowSection = true; +static bool sShowDefinitionKind = true; +static bool sShowCombineKind = true; + static cpu_type_t sPreferredArch = CPU_TYPE_I386; static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF; -static const char* sMatchName; +static const char* sMatchName = NULL; static int sPrintRestrict; static int sPrintAlign; static int sPrintName; - __attribute__((noreturn)) void throwf(const char* format, ...) { @@ -71,12 +73,12 @@ void warning(const char* format, ...) fprintf(stderr, "\n"); } -static void dumpStabs(std::vector* stabs) +static void dumpStabs(const std::vector* stabs) { // debug info printf("stabs: (%lu)\n", stabs->size()); - for (std::vector::iterator it = stabs->begin(); it != stabs->end(); ++it ) { - ObjectFile::Reader::Stab& stab = *it; + for (std::vector::const_iterator it = stabs->begin(); it != stabs->end(); ++it ) { + const ld::relocatable::File::Stab& stab = *it; const char* code = "?????"; switch (stab.type) { case N_GSYM: @@ -164,24 +166,24 @@ static void dumpStabs(std::vector* stabs) code = "LENG"; break; } - printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string); + printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->name() : ""), stab.other, stab.desc, code, stab.string); } } - -static void dumpAtomLikeNM(ObjectFile::Atom* atom) +#if 0 +static void dumpAtomLikeNM(ld::Atom* atom) { - uint32_t size = atom->getSize(); + uint32_t size = atom->size(); const char* visibility; - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: + switch ( atom->scope() ) { + case ld::Atom::scopeTranslationUnit: visibility = "internal"; break; - case ObjectFile::Atom::scopeLinkageUnit: + case ld::Atom::scopeLinkageUnit: visibility = "hidden "; break; - case ObjectFile::Atom::scopeGlobal: + case ld::Atom::scopeGlobal: visibility = "global "; break; default: @@ -190,17 +192,17 @@ static void dumpAtomLikeNM(ObjectFile::Atom* atom) } const char* kind; - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: + switch ( atom->definitionKind() ) { + case ld::Atom::kRegularDefinition: kind = "regular "; break; - case ObjectFile::Atom::kTentativeDefinition: + case ld::Atom::kTentativeDefinition: kind = "tentative"; break; - case ObjectFile::Atom::kWeakDefinition: + case ld::Atom::kWeakDefinition: kind = "weak "; break; - case ObjectFile::Atom::kAbsoluteSymbol: + case ld::Atom::kAbsoluteSymbol: kind = "absolute "; break; default: @@ -208,31 +210,31 @@ static void dumpAtomLikeNM(ObjectFile::Atom* atom) break; } - printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName()); + printf("0x%08X %s %s %s\n", size, visibility, kind, atom->name()); } -static void dumpAtom(ObjectFile::Atom* atom) +static void dumpAtom(ld::Atom* atom) { - if(sMatchName && strcmp(sMatchName, atom->getDisplayName())) + if(sMatchName && strcmp(sMatchName, atom->name())) return; //printf("atom: %p\n", atom); // name if(!sPrintRestrict || sPrintName) - printf("name: %s\n", atom->getDisplayName()); + printf("name: %s\n", atom->name()); // scope if(!sPrintRestrict) - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: + switch ( atom->scope() ) { + case ld::Atom::scopeTranslationUnit: printf("scope: translation unit\n"); break; - case ObjectFile::Atom::scopeLinkageUnit: + case ld::Atom::scopeLinkageUnit: printf("scope: linkage unit\n"); break; - case ObjectFile::Atom::scopeGlobal: + case ld::Atom::scopeGlobal: printf("scope: global\n"); break; default: @@ -241,23 +243,23 @@ static void dumpAtom(ObjectFile::Atom* atom) // kind if(!sPrintRestrict) - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: + switch ( atom->definitionKind() ) { + case ld::Atom::kRegularDefinition: printf("kind: regular\n"); break; - case ObjectFile::Atom::kWeakDefinition: + case ld::Atom::kWeakDefinition: printf("kind: weak\n"); break; - case ObjectFile::Atom::kTentativeDefinition: + case ld::Atom::kTentativeDefinition: printf("kind: tentative\n"); break; - case ObjectFile::Atom::kExternalDefinition: + case ld::Atom::kExternalDefinition: printf("kind: import\n"); break; - case ObjectFile::Atom::kExternalWeakDefinition: + case ld::Atom::kExternalWeakDefinition: printf("kind: weak import\n"); break; - case ObjectFile::Atom::kAbsoluteSymbol: + case ld::Atom::kAbsoluteSymbol: printf("kind: absolute symbol\n"); break; default: @@ -265,16 +267,14 @@ static void dumpAtom(ObjectFile::Atom* atom) } // segment and section - if(!sPrintRestrict && (atom->getSectionName() != NULL) ) - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + if(!sPrintRestrict && (atom->section().sectionName() != NULL) ) + printf("section: %s,%s\n", atom->section().segmentName(), atom->section().sectionName()); // attributes if(!sPrintRestrict) { printf("attrs: "); if ( atom->dontDeadStrip() ) printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); if ( atom->isThumb() ) printf("thumb "); printf("\n"); @@ -282,20 +282,20 @@ static void dumpAtom(ObjectFile::Atom* atom) // size if(!sPrintRestrict) - printf("size: 0x%012llX\n", atom->getSize()); + printf("size: 0x%012llX\n", atom->size()); // alignment if(!sPrintRestrict || sPrintAlign) - printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); + printf("align: %u mod %u\n", atom->alignment().modulus, (1 << atom->alignment().powerOf2) ); // content if (!sPrintRestrict && sDumpContent ) { - uint64_t size = atom->getSize(); + uint64_t size = atom->size(); if ( size < 4096 ) { uint8_t content[size]; atom->copyRawContent(content); printf("content: "); - if ( atom->getContentType() == ObjectFile::Atom::kCStringType ) { + if ( atom->contentType() == ld::Atom::typeCString ) { printf("\""); for (unsigned int i=0; i < size; ++i) { if(content[i]<'!' || content[i]>=127) @@ -317,12 +317,12 @@ static void dumpAtom(ObjectFile::Atom* atom) if(!sPrintRestrict) { if ( atom->beginUnwind() != atom->endUnwind() ) { printf("unwind encodings:\n"); - for (ObjectFile::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) { + for (ld::Atom::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) { printf("\t 0x%04X 0x%08X\n", it->startOffset, it->unwindInfo); } } } - +#if 0 // references if(!sPrintRestrict) { std::vector& references = atom->getReferences(); @@ -333,13 +333,12 @@ static void dumpAtom(ObjectFile::Atom* atom) printf(" %s\n", ref->getDescription()); } } - +#endif // line info if(!sPrintRestrict) { - std::vector* lineInfo = atom->getLineInfo(); - if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) { - printf("line info: (%lu)\n", lineInfo->size()); - for (std::vector::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + 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); } } @@ -348,31 +347,711 @@ static void dumpAtom(ObjectFile::Atom* atom) if(!sPrintRestrict) printf("\n"); } - +#endif struct AtomSorter { - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) + bool operator()(const ld::Atom* left, const ld::Atom* right) { if ( left == right ) return false; - int name = strcmp(left->getDisplayName(), right->getDisplayName()); - if ( name == 0 ) - return (left->getSize() < right->getSize()); - else - return ( name < 0); + // first sort by segment name + int diff = strcmp(left->section().segmentName(), right->section().segmentName()); + if ( diff != 0 ) + return (diff > 0); + + // then sort by section name + diff = strcmp(left->section().sectionName(), right->section().sectionName()); + if ( diff != 0 ) + return (diff < 0); + + // then sort by atom name + diff = strcmp(left->name(), right->name()); + if ( diff != 0 ) + return (diff < 0); + + // if cstring, sort by content + if ( left->contentType() == ld::Atom::typeCString ) { + diff = strcmp((char*)left->rawContentPointer(), (char*)right->rawContentPointer()); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeCStringPointer ) { + // if pointer to c-string sort by name + const char* leftString = NULL; + assert(left->fixupsBegin() != left->fixupsEnd()); + for (ld::Fixup::iterator fit = left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + const ld::Atom* cstringAtom = fit->u.target; + assert(cstringAtom->contentType() == ld::Atom::typeCString); + leftString = (char*)cstringAtom->rawContentPointer(); + } + } + const char* rightString = NULL; + assert(right->fixupsBegin() != right->fixupsEnd()); + for (ld::Fixup::iterator fit = right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + const ld::Atom* cstringAtom = fit->u.target; + assert(cstringAtom->contentType() == ld::Atom::typeCString); + rightString = (char*)cstringAtom->rawContentPointer(); + } + } + assert(leftString != NULL); + assert(rightString != NULL); + diff = strcmp(leftString, rightString); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeLiteral4 ) { + // if literal sort by content + uint32_t leftValue = *(uint32_t*)left->rawContentPointer(); + uint32_t rightValue = *(uint32_t*)right->rawContentPointer(); + diff = (leftValue - rightValue); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeCFI ) { + // if __he_frame sort by address + diff = (left->objectAddress() - right->objectAddress()); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeNonLazyPointer ) { + // if non-lazy-pointer sort by name + const char* leftString = NULL; + assert(left->fixupsBegin() != left->fixupsEnd()); + for (ld::Fixup::iterator fit = left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByNameUnbound ) { + leftString = fit->u.name; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + leftString = fit->u.target->name(); + } + } + const char* rightString = NULL; + assert(right->fixupsBegin() != right->fixupsEnd()); + for (ld::Fixup::iterator fit = right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByNameUnbound ) { + rightString = fit->u.name; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + rightString = fit->u.target->name(); + } + } + assert(leftString != NULL); + assert(rightString != NULL); + diff = strcmp(leftString, rightString); + if ( diff != 0 ) + return (diff < 0); + } + + // else sort by size + return (left->size() < right->size()); } }; -static void dumpFile(ObjectFile::Reader* reader) +class dumper : public ld::File::AtomHandler +{ +public: + void dump(); + virtual void doAtom(const ld::Atom&); + virtual void doFile(const ld::File&) {} +private: + void dumpAtom(const ld::Atom& atom); + const char* scopeString(const ld::Atom&); + const char* definitionString(const ld::Atom&); + const char* combineString(const ld::Atom&); + const char* inclusionString(const ld::Atom&); + const char* attributeString(const ld::Atom&); + const char* makeName(const ld::Atom& atom); + const char* referenceTargetAtomName(const ld::Fixup* ref); + void dumpFixup(const ld::Fixup* ref); + + uint64_t addressOfFirstAtomInSection(const ld::Section&); + + std::vector _atoms; +}; + +const char* dumper::scopeString(const ld::Atom& atom) +{ + switch ( (ld::Atom::Scope)atom.scope() ) { + case ld::Atom::scopeTranslationUnit: + return "translation-unit"; + case ld::Atom::scopeLinkageUnit: + return "hidden"; + case ld::Atom::scopeGlobal: + if ( atom.autoHide() ) + return "global but automatically hidden"; + else + return "global"; + } + return "UNKNOWN"; +} + +const char* dumper::definitionString(const ld::Atom& atom) +{ + switch ( (ld::Atom::Definition)atom.definition() ) { + case ld::Atom::definitionRegular: + return "regular"; + case ld::Atom::definitionTentative: + return "tentative"; + case ld::Atom::definitionAbsolute: + return "absolute"; + case ld::Atom::definitionProxy: + return "proxy"; + } + return "UNKNOWN"; +} + +const char* dumper::combineString(const ld::Atom& atom) +{ + switch ( (ld::Atom::Combine)atom.combine() ) { + case ld::Atom::combineNever: + return "never"; + case ld::Atom::combineByName: + return "by-name"; + case ld::Atom::combineByNameAndContent: + return "by-name-and-content"; + case ld::Atom::combineByNameAndReferences: + return "by-name-and-references"; + } + return "UNKNOWN"; +} + +const char* dumper::inclusionString(const ld::Atom& atom) +{ + switch ( (ld::Atom::SymbolTableInclusion)atom.symbolTableInclusion() ) { + case ld::Atom::symbolTableNotIn: + return "not in"; + case ld::Atom::symbolTableNotInFinalLinkedImages: + return "not in final linked images"; + case ld::Atom::symbolTableIn: + return "in"; + case ld::Atom::symbolTableInAndNeverStrip: + return "in and never strip"; + case ld::Atom::symbolTableInAsAbsolute: + return "in as absolute"; + case ld::Atom::symbolTableInWithRandomAutoStripLabel: + return "in as random auto-strip label"; + } + return "UNKNOWN"; +} + + + +const char* dumper::attributeString(const ld::Atom& atom) +{ + static char buffer[256]; + buffer[0] = '\0'; + + if ( atom.dontDeadStrip() ) + strcat(buffer, "dont-dead-strip "); + + if ( atom.isThumb() ) + strcat(buffer, "thumb "); + + if ( atom.isAlias() ) + strcat(buffer, "alias "); + + if ( atom.contentType() == ld::Atom::typeResolver ) + strcat(buffer, "resolver "); + + return buffer; +} + +const char* dumper::makeName(const ld::Atom& atom) +{ + static char buffer[4096]; + strcpy(buffer, "???"); + switch ( atom.symbolTableInclusion() ) { + case ld::Atom::symbolTableNotIn: + if ( atom.contentType() == ld::Atom::typeCString ) { + strcpy(buffer, "cstring="); + strlcat(buffer, (char*)atom.rawContentPointer(), 4096); + } + else if ( atom.section().type() == ld::Section::typeLiteral4 ) { + char temp[16]; + strcpy(buffer, "literal4="); + uint32_t value = *(uint32_t*)atom.rawContentPointer(); + sprintf(temp, "0x%08X", value); + strcat(buffer, temp); + } + else if ( atom.section().type() == ld::Section::typeLiteral8 ) { + char temp[32]; + strcpy(buffer, "literal8="); + uint32_t value1 = *(uint32_t*)atom.rawContentPointer(); + uint32_t value2 = ((uint32_t*)atom.rawContentPointer())[1]; + sprintf(temp, "0x%08X%08X", value1, value2); + strcat(buffer, temp); + } + else if ( atom.section().type() == ld::Section::typeLiteral16 ) { + char temp[64]; + strcpy(buffer, "literal16="); + uint32_t value1 = *(uint32_t*)atom.rawContentPointer(); + uint32_t value2 = ((uint32_t*)atom.rawContentPointer())[1]; + uint32_t value3 = ((uint32_t*)atom.rawContentPointer())[2]; + uint32_t value4 = ((uint32_t*)atom.rawContentPointer())[3]; + sprintf(temp, "0x%08X%08X%08X%08X", value1, value2, value3, value4); + strcat(buffer, temp); + } + else if ( atom.section().type() == ld::Section::typeCStringPointer ) { + assert(atom.fixupsBegin() != atom.fixupsEnd()); + for (ld::Fixup::iterator fit = atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + const ld::Atom* cstringAtom = fit->u.target; + if ( (cstringAtom != NULL) && (cstringAtom->contentType() == ld::Atom::typeCString) ) { + strlcpy(buffer, atom.name(), 4096); + strlcat(buffer, "=", 4096); + strlcat(buffer, (char*)cstringAtom->rawContentPointer(), 4096); + } + } + } + } + else if ( atom.section().type() == ld::Section::typeNonLazyPointer ) { + assert(atom.fixupsBegin() != atom.fixupsEnd()); + for (ld::Fixup::iterator fit = atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByNameUnbound ) { + strcpy(buffer, "non-lazy-pointer-to:"); + strlcat(buffer, fit->u.name, 4096); + return buffer; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + strcpy(buffer, "non-lazy-pointer-to-local:"); + strlcat(buffer, fit->u.target->name(), 4096); + return buffer; + } + } + strlcpy(buffer, atom.name(), 4096); + } + else { + uint64_t sectAddr = addressOfFirstAtomInSection(atom.section()); + sprintf(buffer, "%s@%s+0x%08llX", atom.name(), atom.section().sectionName(), atom.objectAddress()-sectAddr); + } + break; + case ld::Atom::symbolTableNotInFinalLinkedImages: + case ld::Atom::symbolTableIn: + case ld::Atom::symbolTableInAndNeverStrip: + case ld::Atom::symbolTableInAsAbsolute: + case ld::Atom::symbolTableInWithRandomAutoStripLabel: + strlcpy(buffer, atom.name(), 4096); + break; + } + return buffer; +} + +const char* dumper::referenceTargetAtomName(const ld::Fixup* ref) +{ + static char buffer[4096]; + switch ( ref->binding ) { + case ld::Fixup::bindingNone: + return "NO BINDING"; + case ld::Fixup::bindingByNameUnbound: + strcpy(buffer, "by-name("); + strlcat(buffer, ref->u.name, 4096); + strlcat(buffer, ")", 4096); + return buffer; + //return ref->u.name; + case ld::Fixup::bindingByContentBound: + strcpy(buffer, "by-content("); + strlcat(buffer, makeName(*ref->u.target), 4096); + strlcat(buffer, ")", 4096); + return buffer; + case ld::Fixup::bindingDirectlyBound: + strcpy(buffer, "direct("); + strlcat(buffer, makeName(*ref->u.target), 4096); + strlcat(buffer, ")", 4096); + return buffer; + case ld::Fixup::bindingsIndirectlyBound: + return "BOUND INDIRECTLY"; + } + return "BAD BINDING"; +} + + +void dumper::dumpFixup(const ld::Fixup* ref) +{ + if ( ref->weakImport ) { + printf("weak_import "); + } + switch ( (ld::Fixup::Kind)(ref->kind) ) { + case ld::Fixup::kindNone: + printf("none"); + break; + case ld::Fixup::kindNoneFollowOn: + printf("followed by %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinate: + printf("group subordinate %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinateFDE: + printf("group subordinate FDE %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinateLSDA: + printf("group subordinate LSDA %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinatePersonality: + printf("group subordinate personality %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetTargetAddress: + printf("%s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSubtractTargetAddress: + printf(" - %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindAddAddend: + printf(" + 0x%llX", ref->u.addend); + break; + case ld::Fixup::kindSubtractAddend: + printf(" - 0x%llX", ref->u.addend); + break; + case ld::Fixup::kindSetTargetImageOffset: + printf("imageOffset(%s)", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetTargetSectionOffset: + printf("sectionOffset(%s)", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStore8: + printf(", then store byte"); + break; + case ld::Fixup::kindStoreLittleEndian16: + printf(", then store 16-bit little endian"); + break; + case ld::Fixup::kindStoreLittleEndianLow24of32: + printf(", then store low 24-bit little endian"); + break; + case ld::Fixup::kindStoreLittleEndian32: + printf(", then store 32-bit little endian"); + break; + case ld::Fixup::kindStoreLittleEndian64: + printf(", then store 64-bit little endian"); + break; + case ld::Fixup::kindStoreBigEndian16: + printf(", then store 16-bit big endian"); + break; + case ld::Fixup::kindStoreBigEndianLow24of32: + printf(", then store low 24-bit big endian"); + break; + case ld::Fixup::kindStoreBigEndian32: + printf(", then store 32-bit big endian"); + break; + case ld::Fixup::kindStoreBigEndian64: + printf(", then store 64-bit big endian"); + break; + case ld::Fixup::kindStorePPCBranch24: + printf(", then store as PPC branch24"); + break; + case ld::Fixup::kindStorePPCBranch14: + printf(", then store as PPC branch14"); + break; + case ld::Fixup::kindStorePPCPicLow14: + printf(", then store as PPC low14 pic"); + break; + case ld::Fixup::kindStorePPCPicLow16: + printf(", then store as PPC low14 pic"); + break; + case ld::Fixup::kindStorePPCPicHigh16AddLow: + printf(", then store as PPC high16 pic"); + break; + case ld::Fixup::kindStorePPCAbsLow14: + printf(", then store as PPC low14 abs"); + break; + case ld::Fixup::kindStorePPCAbsLow16: + printf(", then store as PPC low14 abs"); + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + printf(", then store as PPC high16 abs"); + break; + case ld::Fixup::kindStorePPCAbsHigh16: + printf(", then store as PPC high16 abs, no carry"); + break; + case ld::Fixup::kindStoreX86BranchPCRel8: + printf(", then store as x86 8-bit pcrel branch"); + break; + case ld::Fixup::kindStoreX86BranchPCRel32: + printf(", then store as x86 32-bit pcrel branch"); + break; + case ld::Fixup::kindStoreX86PCRel8: + printf(", then store as x86 8-bit pcrel"); + break; + case ld::Fixup::kindStoreX86PCRel16: + printf(", then store as x86 16-bit pcrel"); + break; + case ld::Fixup::kindStoreX86PCRel32: + printf(", then store as x86 32-bit pcrel"); + break; + case ld::Fixup::kindStoreX86PCRel32_1: + printf(", then store as x86 32-bit pcrel from +1"); + break; + case ld::Fixup::kindStoreX86PCRel32_2: + printf(", then store as x86 32-bit pcrel from +2"); + break; + case ld::Fixup::kindStoreX86PCRel32_4: + printf(", then store as x86 32-bit pcrel from +4"); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + printf(", then store as x86 32-bit pcrel GOT load"); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + printf(", then store as x86 32-bit pcrel GOT load -> LEA"); + break; + case ld::Fixup::kindStoreX86PCRel32GOT: + printf(", then store as x86 32-bit pcrel GOT access"); + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + printf(", then store as x86 32-bit pcrel TLV load"); + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + printf(", then store as x86 32-bit pcrel TLV load"); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + printf(", then store as x86 32-bit absolute TLV load"); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA: + printf(", then store as x86 32-bit absolute TLV load -> LEA"); + break; + case ld::Fixup::kindStoreARMBranch24: + printf(", then store as ARM 24-bit pcrel branch"); + break; + case ld::Fixup::kindStoreThumbBranch22: + printf(", then store as Thumb 22-bit pcrel branch"); + break; + case ld::Fixup::kindStoreARMLoad12: + printf(", then store as ARM 12-bit pcrel load"); + break; + case ld::Fixup::kindStoreARMLow16: + printf(", then store low-16 in ARM movw"); + break; + case ld::Fixup::kindStoreARMHigh16: + printf(", then store high-16 in ARM movt"); + break; + case ld::Fixup::kindStoreThumbLow16: + printf(", then store low-16 in Thumb movw"); + break; + case ld::Fixup::kindStoreThumbHigh16: + printf(", then store high-16 in Thumb movt"); + break; + case ld::Fixup::kindDtraceExtra: + printf("dtrace static probe extra info"); + break; + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + printf("x86 dtrace static probe site"); + break; + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + printf("x86 dtrace static is-enabled site"); + break; + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + printf("ppc dtrace static probe site"); + break; + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + printf("ppc dtrace static is-enabled site"); + break; + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + printf("ARM dtrace static probe site"); + break; + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + printf("ARM dtrace static is-enabled site"); + break; + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + printf("Thumb dtrace static probe site"); + break; + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + printf("Thumb dtrace static is-enabled site"); + break; + case ld::Fixup::kindLazyTarget: + printf("lazy reference to external symbol %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetLazyOffset: + printf("offset of lazy binding info for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + printf("store 64-bit little endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian32: + printf("store 32-bit big endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian64: + printf("store 64-bit big endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + printf("x86 store 32-bit pc-rel address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + printf("x86 store 32-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + printf("x86 store 32-bit pc-rel GOT load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + printf("x86 store 32-bit pc-rel lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + printf("x86 store 32-bit pc-rel TLV load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: + printf("x86 store 32-bit pc-rel TLV lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + printf("x86 store 32-bit absolute TLV load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: + printf("x86 store 32-bit absolute TLV lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARMBranch24: + printf("ARM store 24-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + printf("Thumb store 22-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARMLoad12: + printf("ARM store 12-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + printf("PowerPC store 24-bit pc-rel load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffset: + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: + printf("tlv template offset of %s", referenceTargetAtomName(ref)); + //default: + // printf("unknown fixup"); + // break; + } +} + +uint64_t dumper::addressOfFirstAtomInSection(const ld::Section& sect) +{ + uint64_t lowestAddr = (uint64_t)(-1); + for (std::vector::iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( &atom->section() == § ) { + if ( atom->objectAddress() < lowestAddr ) + lowestAddr = atom->objectAddress(); + } + } + return lowestAddr; +} + +void dumper::doAtom(const ld::Atom& atom) +{ + if ( (sMatchName != NULL) && (strcmp(sMatchName, atom.name()) != 0) ) + return; + _atoms.push_back(&atom); +} + +void dumper::dump() +{ + if ( sSort ) + std::sort(_atoms.begin(), _atoms.end(), AtomSorter()); + + for (std::vector::iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + this->dumpAtom(**it); + } +} + +void dumper::dumpAtom(const ld::Atom& atom) +{ + printf("name: %s\n", makeName(atom)); + printf("size: 0x%0llX\n", atom.size()); + printf("align: %u mod %u\n", atom.alignment().modulus, (1 << atom.alignment().powerOf2) ); + printf("scope: %s\n", scopeString(atom)); + if ( sShowDefinitionKind ) + printf("def: %s\n", definitionString(atom)); + if ( sShowCombineKind ) + printf("combine: %s\n", combineString(atom)); + printf("symbol: %s\n", inclusionString(atom)); + printf("attrs: %s\n", attributeString(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); + } + if ( atom.contentType() == ld::Atom::typeCString ) { + uint8_t buffer[atom.size()+2]; + atom.copyRawContent(buffer); + buffer[atom.size()] = '\0'; + printf("content: \"%s\"\n", buffer); + } + if ( atom.fixupsBegin() != atom.fixupsEnd() ) { + printf("fixups:\n"); + for (unsigned int off=0; off < atom.size()+1; ++off) { + for (ld::Fixup::iterator it = atom.fixupsBegin(); it != atom.fixupsEnd(); ++it) { + if ( it->offsetInAtom == off ) { + switch ( it->clusterSize ) { + case ld::Fixup::k1of1: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + break; + case ld::Fixup::k1of2: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + break; + case ld::Fixup::k1of3: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + break; + case ld::Fixup::k1of4: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + break; + case ld::Fixup::k1of5: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + break; + default: + printf(" BAD CLUSTER SIZE: cluster=%d\n", it->clusterSize); + } + printf("\n"); + } + } + } + } + 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"); +} + +static void dumpFile(ld::relocatable::File* file) { // stabs debug info - if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) { - std::vector* stabs = reader->getStabs(); + if ( sDumpStabs && (file->debugInfo() == ld::relocatable::File::kDebugInfoStabs) ) { + const std::vector* stabs = file->stabs(); if ( stabs != NULL ) dumpStabs(stabs); } - + // dump atoms + dumper d; + file->forEachAtom(d); + d.dump(); + +#if 0 // get all atoms std::vector atoms = reader->getAtoms(); @@ -387,10 +1066,11 @@ static void dumpFile(ObjectFile::Reader* reader) else dumpAtom(*it); } +#endif } -static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) +static ld::relocatable::File* createReader(const char* path) { struct stat stat_buf; @@ -400,7 +1080,11 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read ::fstat(fd, &stat_buf); uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); ::close(fd); + if ( p == (uint8_t*)(-1) ) + throwf("cannot mmap file: %s", path); const mach_header* mh = (mach_header*)p; + uint64_t fileLen = stat_buf.st_size; + bool foundFatSlice = false; 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)); @@ -409,28 +1093,48 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read 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 ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader::validFile(p) ) - return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); -#if LTO_SUPPORT - if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) { - return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0); + + mach_o::relocatable::ParserOptions objOpts; + objOpts.architecture = sPreferredArch; + objOpts.objSubtypeMustMatch = false; + objOpts.logAllFiles = false; + objOpts.convertUnwindInfo = true; + objOpts.subType = sPreferredSubArch; +#if 1 + if ( ! foundFatSlice ) { + cpu_type_t archOfObj; + cpu_subtype_t subArchOfObj; + if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj) ) { + objOpts.architecture = archOfObj; + objOpts.subType = subArchOfObj; + } } -#endif - + + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts); + if ( objResult != NULL ) + return objResult; + + // see if it is an llvm object file + objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, 0, sPreferredArch, sPreferredSubArch, false); + if ( objResult != NULL ) + return objResult; + throwf("not a mach-o object file: %s", path); +#else + // for peformance testing + for (int i=0; i < 500; ++i ) { + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts); + delete objResult; + } + exit(0); +#endif } static @@ -439,6 +1143,9 @@ usage() { fprintf(stderr, "ObjectDump options:\n" "\t-no_content\tdon't dump contents\n" + "\t-no_section\tdon't dump section name\n" + "\t-no_defintion\tdon't dump definition kind\n" + "\t-no_combine\tdon't dump combine mode\n" "\t-stabs\t\tdump stabs\n" "\t-arch aaa\tonly dump info about arch aaa\n" "\t-only sym\tonly dump info about sym\n" @@ -454,8 +1161,6 @@ int main(int argc, const char* argv[]) return 0; } - ObjectFile::ReaderOptions options; - options.fAddCompactUnwindEncoding = true; try { for(int i=1; i < argc; ++i) { const char* arg = argv[i]; @@ -472,6 +1177,15 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-no_sort") == 0 ) { sSort = false; } + else if ( strcmp(arg, "-no_section") == 0 ) { + sShowSection = false; + } + else if ( strcmp(arg, "-no_definition") == 0 ) { + sShowDefinitionKind = false; + } + else if ( strcmp(arg, "-no_combine") == 0 ) { + sShowCombineKind = false; + } else if ( strcmp(arg, "-arch") == 0 ) { const char* arch = ++i& nodeStarts); const char* rebaseTypeName(uint8_t type); const char* bindTypeName(uint8_t type); pint_t segStartAddress(uint8_t segIndex); @@ -119,7 +130,7 @@ class DyldInfoPrinter const char* ordinalName(int libraryOrdinal); const char* classicOrdinalName(int libraryOrdinal); pint_t* mappedAddressForVMAddress(pint_t vmaddress); - + const char* symbolNameForAddress(uint64_t); const char* fPath; @@ -130,6 +141,8 @@ class DyldInfoPrinter const macho_nlist

* fSymbols; uint32_t fSymbolCount; const macho_dyld_info_command

* fInfo; + const macho_linkedit_data_command

* fSharedRegionInfo; + const macho_linkedit_data_command

* fFunctionStartsInfo; uint64_t fBaseAddress; const macho_dysymtab_command

* fDynamicSymbolTable; const macho_segment_command

* fFirstSegment; @@ -137,6 +150,7 @@ class DyldInfoPrinter bool fWriteableSegmentWithAddrOver4G; std::vector*>fSegments; std::vector fDylibs; + std::vector*> fDylibLoadCommands; }; @@ -235,6 +249,7 @@ template DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) : fHeader(NULL), fLength(fileLength), fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), + fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL), fBaseAddress(0), fDynamicSymbolTable(NULL), fFirstSegment(NULL), fFirstWritableSegment(NULL), fWriteableSegmentWithAddrOver4G(false) { @@ -281,9 +296,11 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: case LC_LAZY_LOAD_DYLIB: { const macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + fDylibLoadCommands.push_back(dylib); const char* lastSlash = strrchr(dylib->name(), '/'); const char* leafName = (lastSlash != NULL) ? lastSlash+1 : dylib->name(); const char* firstDot = strchr(leafName, '.'); @@ -309,6 +326,12 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen fStringsEnd = fStrings + symtab->strsize(); } break; + case LC_SEGMENT_SPLIT_INFO: + fSharedRegionInfo = (macho_linkedit_data_command

*)cmd; + break; + case LC_FUNCTION_STARTS: + fFunctionStartsInfo = (macho_linkedit_data_command

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

*)endOfCmd; } @@ -347,6 +370,14 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen } if ( printExportGraph ) printExportInfoGraph(); + if ( printExportNodes ) + printExportInfoNodes(); + if ( printSharedRegion ) + printSharedRegionInfo(); + if ( printFunctionStarts ) + printFunctionStartsInfo(); + if ( printDylibs ) + printDylibsInfo(); } static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) @@ -522,6 +553,8 @@ const char* DyldInfoPrinter::ordinalName(int libraryOrdinal) template const char* DyldInfoPrinter::classicOrdinalName(int libraryOrdinal) { + if ( (fHeader->flags() & MH_TWOLEVEL) == 0 ) + return "flat-namespace"; switch ( libraryOrdinal) { case SELF_LIBRARY_ORDINAL: return "this-image"; @@ -811,7 +844,7 @@ void DyldInfoPrinter::printBindingInfo() } else { printf("bind information:\n"); - printf("segment section address type weak addend dylib symbol\n"); + printf("segment section address type addend dylib symbol\n"); const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off(); const uint8_t* end = &p[fInfo->bind_size()]; @@ -861,7 +894,7 @@ void DyldInfoPrinter::printBindingInfo() ++p; ++p; if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) - weak_import = "weak"; + weak_import = " (weak import)"; else weak_import = ""; break; @@ -882,22 +915,22 @@ void DyldInfoPrinter::printBindingInfo() segOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += read_uleb128(p, end) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += skip + sizeof(pint_t); } break; @@ -1019,6 +1052,7 @@ void DyldInfoPrinter::printLazyBindingInfo() pint_t segStartAddr = 0; const char* segName = "??"; const char* typeName = "??"; + const char* weak_import = ""; for (const uint8_t* p=start; p < end; ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; uint8_t opcode = *p & BIND_OPCODE_MASK; @@ -1050,6 +1084,10 @@ void DyldInfoPrinter::printLazyBindingInfo() while (*p != '\0') ++p; ++p; + if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) + weak_import = " (weak import)"; + else + weak_import = ""; break; case BIND_OPCODE_SET_TYPE_IMM: type = immediate; @@ -1068,7 +1106,7 @@ void DyldInfoPrinter::printLazyBindingInfo() segOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, lazy_offset, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, lazy_offset, fromDylib, symbolName, weak_import); segOffset += sizeof(pint_t); break; default: @@ -1177,35 +1215,6 @@ void DyldInfoPrinter::printLazyBindingOpcodes() } - -template -void DyldInfoPrinter::processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, - char* cummulativeString, int curStrOffset) -{ - const uint8_t terminalSize = *p++; - const uint8_t* children = p + terminalSize; - if ( terminalSize != 0 ) { - uint32_t flags = read_uleb128(p, end); - uint64_t address = read_uleb128(p, end); - if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) - fprintf(stdout, "0x%08llX [weak_def] %s\n", address, cummulativeString); - else - fprintf(stdout, "0x%08llX %s\n", address, cummulativeString); - } - const uint8_t childrenCount = *children++; - const uint8_t* s = children; - for (uint8_t i=0; i < childrenCount; ++i) { - int edgeStrLen = 0; - while (*s != '\0') { - cummulativeString[curStrOffset+edgeStrLen] = *s++; - ++edgeStrLen; - } - cummulativeString[curStrOffset+edgeStrLen] = *s++; - uint32_t childNodeOffet = read_uleb128(s, end); - processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen); - } -} - struct SortExportsByAddress { bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) @@ -1228,10 +1237,26 @@ void DyldInfoPrinter::printExportInfo() parseTrie(start, end, list); //std::sort(list.begin(), list.end(), SortExportsByAddress()); for (std::vector::iterator it=list.begin(); it != list.end(); ++it) { - const char* flags = ""; - if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) - flags = "[weak_def] "; - fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name); + if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( it->importName[0] == '\0' ) + fprintf(stdout, "[re-export] %s from dylib=%llu\n", it->name, it->other); + else + fprintf(stdout, "[re-export] %s from dylib=%llu named=%s\n", it->name, it->other, it->importName); + } + else { + const char* flags = ""; + if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) + flags = "[weak_def] "; + else if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL ) + flags = "[per-thread] "; + if ( it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + flags = "[resolver] "; + fprintf(stdout, "0x%08llX %s%s (resolver=0x%08llX)\n", fBaseAddress+it->address, flags, it->name, it->other); + } + else { + fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name); + } + } } } } @@ -1243,13 +1268,27 @@ void DyldInfoPrinter::processExportGraphNode(const uint8_t* const start, cons char* cummulativeString, int curStrOffset) { const uint8_t* const me = p; - const uint8_t terminalSize = *p++; + const uint8_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { uint32_t flags = read_uleb128(p, end); - (void)flags; // currently unused - uint64_t address = read_uleb128(p, end); - printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + uint64_t ordinal = read_uleb128(p, end); + const char* importName = (const char*)p; + while (*p != '\0') + ++p; + ++p; + if ( *importName == '\0' ) + printf("\tnode%03ld [ label=%s,re-export from dylib=%llu ];\n", (long)(me-start), cummulativeString, ordinal); + else + printf("\tnode%03ld [ label=%s,re-export %s from dylib=%llu ];\n", (long)(me-start), cummulativeString, importName, ordinal); + } + else { + uint64_t address = read_uleb128(p, end); + printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address); + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + read_uleb128(p, end); + } } else { printf("\tnode%03ld;\n", (long)(me-start)); @@ -1286,6 +1325,222 @@ void DyldInfoPrinter::printExportInfoGraph() } } +template +void DyldInfoPrinter::gatherNodeStarts(const uint8_t* const start, const uint8_t* const end, + const uint8_t* parent, const uint8_t* p, + std::vector& nodeStarts) +{ + nodeStarts.push_back(p-start); + const uint8_t terminalSize = read_uleb128(p, end); + const uint8_t* children = p + terminalSize; + + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + // skip over edge string + while (*s != '\0') + ++s; + ++s; + uint32_t childNodeOffet = read_uleb128(s, end); + gatherNodeStarts(start, end, start, start+childNodeOffet, nodeStarts); + } +} + + +template +void DyldInfoPrinter::printExportInfoNodes() +{ + if ( (fInfo == NULL) || (fInfo->export_off() == 0) ) { + printf("no compressed export info\n"); + } + else { + const uint8_t* start = (uint8_t*)fHeader + fInfo->export_off(); + const uint8_t* end = &start[fInfo->export_size()]; + std::vector nodeStarts; + gatherNodeStarts(start, end, start, start, nodeStarts); + std::sort(nodeStarts.begin(), nodeStarts.end()); + for (std::vector::const_iterator it=nodeStarts.begin(); it != nodeStarts.end(); ++it) { + printf("0x%04X: ", *it); + const uint8_t* p = start + *it; + uint64_t exportInfoSize = read_uleb128(p, end); + if ( exportInfoSize != 0 ) { + // print terminal info + uint64_t flags = read_uleb128(p, end); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + uint64_t ordinal = read_uleb128(p, end); + const char* importName = (const char*)p; + while (*p != '\0') + ++p; + ++p; + if ( strlen(importName) == 0 ) + printf("[flags=REEXPORT ordinal=%llu] ", ordinal); + else + printf("[flags=REEXPORT ordinal=%llu import=%s] ", ordinal, importName); + } + else if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + uint64_t stub = read_uleb128(p, end); + uint64_t resolver = read_uleb128(p, end); + printf("[flags=STUB_AND_RESOLVER stub=0x%06llX resolver=0x%06llX] ", stub, resolver); + } + else { + uint64_t address = read_uleb128(p, end); + if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + printf("[addr=0x%06llX] ", address); + else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL) + printf("[flags=THREAD_LOCAL addr=0x%06llX] ", address); + else + printf("[flags=0x%llX addr=0x%06llX] ", flags, address); + } + } + // print child edges + const uint8_t childrenCount = *p++; + for (uint8_t i=0; i < childrenCount; ++i) { + const char* edgeName = (const char*)p; + while (*p != '\0') + ++p; + ++p; + uint32_t childNodeOffet = read_uleb128(p, end); + printf("%s->0x%04X", edgeName, childNodeOffet); + if ( i < (childrenCount-1) ) + printf(", "); + } + printf("\n"); + } + } +} + + + +template +const uint8_t* DyldInfoPrinter::printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind) +{ + const char* kindStr = "??"; + switch (kind) { + case 1: + kindStr = "32-bit pointer"; + break; + case 2: + kindStr = "64-bit pointer"; + break; + case 3: + kindStr = "ppc hi16"; + break; + case 4: + kindStr = "32-bit offset to IMPORT"; + break; + } + uint64_t address = 0; + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + if ( delta != 0 ) { + address += delta; + printf("0x%0llX %s\n", address+fBaseAddress, kindStr); + delta = 0; + shift = 0; + } + else { + more = false; + } + } + } while (more); + return p; +} + +template +void DyldInfoPrinter::printSharedRegionInfo() +{ + if ( (fSharedRegionInfo == NULL) || (fSharedRegionInfo->datasize() == 0) ) { + printf("no shared region info\n"); + } + else { + const uint8_t* infoStart = (uint8_t*)fHeader + fSharedRegionInfo->dataoff(); + const uint8_t* infoEnd = &infoStart[fSharedRegionInfo->datasize()]; + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + p = this->printSharedRegionInfoForEachULEB128Address(p, kind); + } + + } +} + +template <> +void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) +{ + if ( addr & 1 ) + printf("0x%0llX [thumb] %s\n", (addr & -2), symbolNameForAddress(addr & -2)); + else + printf("0x%0llX %s\n", addr, symbolNameForAddress(addr)); +} + +template +void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) +{ + printf("0x%0llX %s\n", addr, symbolNameForAddress(addr)); +} + + +template +void DyldInfoPrinter::printFunctionStartsInfo() +{ + if ( (fFunctionStartsInfo == NULL) || (fFunctionStartsInfo->datasize() == 0) ) { + printf("no function starts info\n"); + } + else { + const uint8_t* infoStart = (uint8_t*)fHeader + fFunctionStartsInfo->dataoff(); + const uint8_t* infoEnd = &infoStart[fFunctionStartsInfo->datasize()]; + uint64_t address = fBaseAddress; + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd); ) { + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + address += delta; + printFunctionStartLine(address); + more = false; + } + } while (more); + } + } +} + +template +void DyldInfoPrinter::printDylibsInfo() +{ + printf("attributes dependent dylibs\n"); + for(typename std::vector*>::iterator it = fDylibLoadCommands.begin(); it != fDylibLoadCommands.end(); ++it) { + const macho_dylib_command

* dylib = *it; + const char* attribute = ""; + switch ( dylib->cmd() ) { + case LC_LOAD_WEAK_DYLIB: + attribute = "weak_import"; + break; + case LC_REEXPORT_DYLIB: + attribute = "re-export"; + break; + case LC_LOAD_UPWARD_DYLIB: + attribute = "upward"; + break; + case LC_LAZY_LOAD_DYLIB: + attribute = "lazy_load"; + break; + case LC_LOAD_DYLIB: + default: + break; + } + printf(" %-12s %s\n", attribute, dylib->name()); + } +} + template <> ppc::P::uint_t DyldInfoPrinter::relocBase() @@ -1465,6 +1720,27 @@ 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()]; + } + } + // 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()]; + } + } + + + return "?"; +} template void DyldInfoPrinter::printClassicBindingInfo() @@ -1679,12 +1955,14 @@ static void dump(const char* path) static void usage() { fprintf(stderr, "Usage: dyldinfo [-arch ] \n" + "\t-dylibs print dependent dylibs\n" "\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n" "\t-bind print addresses dyld will set based on symbolic lookups\n" "\t-weak_bind print symbols which dyld must coalesce\n" "\t-lazy_bind print addresses dyld will lazily set on first use\n" "\t-export print addresses of all symbols this file exports\n" "\t-opcodes print opcodes used to generate the rebase and binding information\n" + "\t-function_starts print table of function start addresses\n" "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n" ); } @@ -1754,6 +2032,18 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-export_dot") == 0 ) { printExportGraph = true; } + else if ( strcmp(arg, "-export_trie_nodes") == 0 ) { + printExportNodes = true; + } + else if ( strcmp(arg, "-shared_region") == 0 ) { + printSharedRegion = true; + } + else if ( strcmp(arg, "-function_starts") == 0 ) { + printFunctionStarts = true; + } + else if ( strcmp(arg, "-dylibs") == 0 ) { + printDylibs = true; + } else { throwf("unknown option: %s\n", arg); } diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 68fdbb8..295fa19 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -52,6 +52,26 @@ void throwf(const char* format, ...) throw t; } +static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throwf("uleb128 too big"); + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} template class MachOChecker @@ -88,6 +108,8 @@ class MachOChecker void checkLocalReloation(const macho_relocation_info

* reloc); pint_t relocBase(); bool addressInWritableSegment(pint_t address); + bool hasTextRelocInRange(pint_t start, pint_t end); + pint_t segStartAddress(uint8_t segIndex); const char* fPath; const macho_header

* fHeader; @@ -106,7 +128,9 @@ class MachOChecker bool fWriteableSegmentWithAddrOver4G; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; + const macho_dyld_info_command

* fDyldInfo; uint32_t fSectionCount; + std::vector*>fSegments; }; @@ -211,7 +235,7 @@ 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), fSectionCount(0) + fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fDyldInfo(NULL), fSectionCount(0) { // sanity check if ( ! validFile(fileContent) ) @@ -241,7 +265,7 @@ void MachOChecker::checkMachHeader() throw "sizeofcmds in mach_header is larger than file"; uint32_t flags = fHeader->flags(); - const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000; + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFE000000; if ( flags & invalidBits ) throw "invalid bits in mach_header flags"; if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) @@ -287,8 +311,14 @@ void MachOChecker::checkLoadCommands() case LC_REEXPORT_DYLIB: case LC_SEGMENT_SPLIT_INFO: case LC_CODE_SIGNATURE: + case LC_LOAD_UPWARD_DYLIB: + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_FUNCTION_STARTS: + break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command

*)cmd; break; case LC_ENCRYPTION_INFO: encryption_info = (macho_encryption_info_command

*)cmd; @@ -312,6 +342,7 @@ void MachOChecker::checkLoadCommands() 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; + fSegments.push_back(segCmd); if ( segCmd->cmdsize() != (sizeof(macho_segment_command

) + segCmd->nsects() * sizeof(macho_section_content

)) ) throw "invalid segment load command size"; @@ -381,7 +412,9 @@ void MachOChecker::checkLoadCommands() throwf("section %s vm address not within segment", sect->sectname()); if ( (sect->addr()+sect->size()) > endAddr ) throwf("section %s vm address not within segment", sect->sectname()); - if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) { + if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) + && ((sect->flags() & SECTION_TYPE) != S_THREAD_LOCAL_ZEROFILL) + && (segCmd->filesize() != 0) ) { if ( sect->offset() < startOffset ) throwf("section %s file offset not within segment", sect->sectname()); if ( (sect->offset()+sect->size()) > endOffset ) @@ -470,7 +503,7 @@ void MachOChecker::checkLoadCommands() if ( isStaticExecutable ) throw "LC_DYSYMTAB should not be used in static executable"; foundDynamicSymTab = true; - fDynamicSymbolTable = (struct macho_dysymtab_command

*)cmd; + fDynamicSymbolTable = (macho_dysymtab_command

*)cmd; fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); if ( fIndirectTableCount != 0 ) { @@ -507,7 +540,7 @@ void MachOChecker::checkLoadCommands() { if ( isStaticExecutable ) throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; - const macho_linkedit_data_command

* info = (struct macho_linkedit_data_command

*)cmd; + const macho_linkedit_data_command

* info = (macho_linkedit_data_command

*)cmd; if ( info->dataoff() < linkEditSegment->fileoff() ) throw "split seg info not in __LINKEDIT"; if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) @@ -518,6 +551,19 @@ void MachOChecker::checkLoadCommands() throw "split seg info size not a multiple of pointer size"; } break; + case LC_FUNCTION_STARTS: + { + const macho_linkedit_data_command

* info = (macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "function starts data not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "function starts data not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "function starts data table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "function starts data size not a multiple of pointer size"; + } + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -543,6 +589,9 @@ void MachOChecker::checkSection(const macho_segment_command

* segCmd, const template void MachOChecker::checkIndirectSymbolTable() { + // static executables don't have indirect symbol table + if ( fDynamicSymbolTable == NULL ) + return; 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; @@ -596,7 +645,11 @@ void MachOChecker::checkSymbolTable() //fprintf(stderr, "sym[%d] = %s\n", i, symName); if ( externalNames.find(symName) != externalNames.end() ) throwf("duplicate external symbol: %s", symName); - externalNames.insert(symName); + if ( (p->n_type() & N_EXT) == 0 ) + throwf("non-external symbol in external symbol range: %s", symName); + // don't add N_INDR to externalNames because there is likely an undefine with same name + if ( (p->n_type() & N_INDR) == 0 ) + externalNames.insert(symName); } // verify no undefines with same name as an external symbol const macho_nlist

* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()]; @@ -876,8 +929,128 @@ void MachOChecker::checkRelocations() for (const macho_relocation_info

* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { this->checkLocalReloation(reloc); } + + // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs + 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; + // if segment is writable, we are fine + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) + continue; + // look at sections that have text reloc bit set + 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 ( (sect->flags() & S_ATTR_LOC_RELOC) != 0 ) { + if ( ! hasTextRelocInRange(sect->addr(), sect->addr()+sect->size()) ) { + throwf("section %s has attribute set that it has relocs, but it has none", sect->sectname()); + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } } +template +typename A::P::uint_t MachOChecker::segStartAddress(uint8_t segIndex) +{ + if ( segIndex > fSegments.size() ) + throw "segment index out of range"; + return fSegments[segIndex]->vmaddr(); +} + +template +bool MachOChecker::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) +{ + // 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 ( (rangeStart <= relocAddress) && (relocAddress < rangeEnd) ) + 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 ( (rangeStart <= addr) && (addr < rangeEnd) ) + 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 ( (rangeStart <= addr) && (addr < rangeEnd) ) + 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 ( (rangeStart <= addr) && (addr < rangeEnd) ) + 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 ( (rangeStart <= addr) && (addr < rangeEnd) ) + 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); + } + } + } +} static void check(const char* path) { diff --git a/ld64/src/other/rebase.cpp b/ld64/src/other/rebase.cpp index f8dc1ee..c04fb0a 100644 --- a/ld64/src/other/rebase.cpp +++ b/ld64/src/other/rebase.cpp @@ -302,7 +302,6 @@ void Rebaser::setBaseAddress(uint64_t addr) template void Rebaser::adjustLoadCommands() { - const macho_segment_command

* highestSegmentCmd = NULL; 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; @@ -317,6 +316,8 @@ void Rebaser::adjustLoadCommands() break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { // clear expected timestamps so that this image will load with invalid prebinding macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; @@ -326,7 +327,7 @@ void Rebaser::adjustLoadCommands() case macho_routines_command

::CMD: // update -init command { - struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; + macho_routines_command

* routines = (macho_routines_command

*)cmd; routines->set_init_address(routines->init_address() + fSlide); } break; @@ -374,6 +375,7 @@ void Rebaser::adjustSymbolTable() { const macho_dysymtab_command

* dysymtab = NULL; macho_nlist

* symbolTable = NULL; + const char* strings = NULL; // get symbol table info const macho_load_command

* const cmds = (macho_load_command

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

)); @@ -385,7 +387,8 @@ void Rebaser::adjustSymbolTable() { const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; symbolTable = (macho_nlist

*)(((uint8_t*)fHeader) + symtab->symoff()); - } + strings = (char*)(((uint8_t*)fHeader) + symtab->stroff()); + } break; case LC_DYSYMTAB: dysymtab = (macho_dysymtab_command

*)cmd; @@ -404,8 +407,23 @@ void Rebaser::adjustSymbolTable() // walk all local symbols and slide their n_value macho_nlist

* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; for (macho_nlist

* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { - if ( entry->n_sect() != NO_SECT ) + if ( ((entry->n_type() & N_STAB) == 0) && ((entry->n_type() & N_TYPE) == N_SECT) ) { entry->set_n_value(entry->n_value() + fSlide); + } + else if ( entry->n_type() & N_STAB ) { + // some stabs need to be slid too + switch ( entry->n_type() ) { + case N_FUN: + // don't slide end-of-function FUN which is FUN with no string + if ( (entry->n_strx() == 0) || (strings[entry->n_strx()] == '\0') ) + break; + case N_BNSYM: + case N_STSYM: + case N_LCSYM: + entry->set_n_value(entry->n_value() + fSlide); + break; + } + } } // FIXME ¥¥¥ adjust dylib_module if it exists @@ -448,7 +466,7 @@ void Rebaser::rebaseAt(int segIndex, uint64_t offset, uint8_t type) if ( segIndex == segCount ) { const macho_segment_command

* seg = (macho_segment_command

*)cmd; lastSegMappedStart = (uint8_t*)fHeader + seg->fileoff(); - lastSegIndex == segCount; + lastSegIndex = segCount; break; } ++segCount; diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 098d932..81e677d 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -59,8 +59,10 @@ class UnwindPrinter { public: static bool validFile(const uint8_t* fileContent); - static UnwindPrinter* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) - { return new UnwindPrinter(fileContent, fileLength, path); } + static UnwindPrinter* make(const uint8_t* fileContent, uint32_t fileLength, + const char* path, bool showFunctionNames) + { return new UnwindPrinter(fileContent, fileLength, + path, showFunctionNames); } virtual ~UnwindPrinter() {} @@ -77,9 +79,10 @@ class UnwindPrinter typedef __gnu_cxx::hash_set, CStringEquals> StringSet; - UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path); + UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, + const char* path, bool showFunctionNames); bool findUnwindSection(); - void printUnwindSection(); + void printUnwindSection(bool showFunctionNames); void getSymbolTableInfo(); const char* functionName(pint_t addr); static const char* archName(); @@ -195,7 +198,7 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) template -UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) +UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames) : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL), fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0) { @@ -209,7 +212,7 @@ UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, getSymbolTableInfo(); if ( findUnwindSection() ) - printUnwindSection(); + printUnwindSection(showFunctionNames); } @@ -664,7 +667,7 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* } template -void UnwindPrinter::printUnwindSection() +void UnwindPrinter::printUnwindSection(bool showFunctionNames) { const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset(); macho_unwind_info_section_header

* sectionHeader = (macho_unwind_info_section_header

*)(sectionContent); @@ -700,8 +703,8 @@ void UnwindPrinter::printUnwindSection() printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount); macho_unwind_info_section_header_lsda_index_entry

* lindex = (macho_unwind_info_section_header_lsda_index_entry

*)§ionContent[lsdaIndexArraySectionOffset]; for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) { - printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", - i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), functionName(lindex[i].functionOffset()+fMachHeaderAddress)); + const char* name = showFunctionNames ? functionName(lindex[i].functionOffset()+fMachHeaderAddress) : ""; + printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), name); if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF ) fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress)); } @@ -731,8 +734,9 @@ void UnwindPrinter::printUnwindSection() } char encodingString[100]; decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); + const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : ""; printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", - j, funcOffset, entry[j].encoding(), encodingString, functionName(funcOffset+fMachHeaderAddress)); + j, funcOffset, entry[j].encoding(), encodingString, name); } } else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) { @@ -755,7 +759,7 @@ void UnwindPrinter::printUnwindSection() char encodingString[100]; uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset; decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString); - const char* name = functionName(funcOff+fMachHeaderAddress); + const char* name = showFunctionNames ? functionName(funcOff+fMachHeaderAddress) : ""; if ( encoding & UNWIND_HAS_LSDA ) { // verify there is a corresponding entry in lsda table bool found = false; @@ -780,7 +784,7 @@ void UnwindPrinter::printUnwindSection() } -static void dump(const char* path, const std::set& onlyArchs) +static void dump(const char* path, const std::set& onlyArchs, bool showFunctionNames) { struct stat stat_buf; @@ -807,31 +811,31 @@ static void dump(const char* path, const std::set& onlyArchs) switch(cputype) { case CPU_TYPE_POWERPC: if ( UnwindPrinter::validFile(p + offset) ) - UnwindPrinter::make(p + offset, size, path); + 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); + 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); + 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); + 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); + UnwindPrinter::make(p + offset, size, path, showFunctionNames); else throw "in universal file, arm slice does not contain arm mach-o"; break; @@ -842,19 +846,19 @@ static void dump(const char* path, const std::set& onlyArchs) } } else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) { - UnwindPrinter::make(p, length, path); + UnwindPrinter::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) { - UnwindPrinter::make(p, length, path); + UnwindPrinter::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) { - UnwindPrinter::make(p, length, path); + UnwindPrinter::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) { - UnwindPrinter::make(p, length, path); + UnwindPrinter::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) { - UnwindPrinter::make(p, length, path); + UnwindPrinter::make(p, length, path, showFunctionNames); } else { throw "not a known file type"; @@ -870,6 +874,7 @@ int main(int argc, const char* argv[]) { std::set onlyArchs; std::vector files; + bool showFunctionNames = true; try { for(int i=1; i < argc; ++i) { @@ -890,6 +895,9 @@ int main(int argc, const char* argv[]) else throwf("unknown architecture %s", arch); } + else if ( strcmp(arg, "-no_symbols") == 0 ) { + showFunctionNames = false; + } else { throwf("unknown option: %s\n", arg); } @@ -910,7 +918,7 @@ int main(int argc, const char* argv[]) // process each file for(std::vector::iterator it=files.begin(); it != files.end(); ++it) { - dump(*it, onlyArchs); + dump(*it, onlyArchs, showFunctionNames); } } diff --git a/ld64/unit-tests/bin/result-filter.pl b/ld64/unit-tests/bin/result-filter.pl index 8168e79..9c92392 100755 --- a/ld64/unit-tests/bin/result-filter.pl +++ b/ld64/unit-tests/bin/result-filter.pl @@ -93,6 +93,15 @@ sub process_entry { printf "%-40s FAIL Makefile failure\n", $test_name; $total_count++; + #my $line1; + #foreach $line1 (@{$$tbl{stdout}}) + #{ + # printf "stdout: %s\n", $line1; + #} + #foreach $line1 (@{$$tbl{stderr}}) + #{ + # printf "stderr: %s\n", $line1; + #} return; } @@ -127,5 +136,14 @@ sub process_entry { printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; $total_count++; + #my $line1; + #foreach $line1 (@{$$tbl{stdout}}) + #{ + # printf "stdout: %s\n", $line1; + #} + #foreach $line1 (@{$$tbl{stderr}}) + #{ + # printf "stderr: %s\n", $line1; + #} } } diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 7222efa..642e491 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -6,7 +6,7 @@ SHELL = /bin/sh ARCH ?= $(shell arch) # set default to be all -VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6" +VALID_ARCHS ?= "i386 x86_64 armv6" MYDIR=$(shell cd ../../bin;pwd) LD = ld @@ -14,6 +14,7 @@ OBJECTDUMP = ObjectDump MACHOCHECK = machocheck OTOOL = otool REBASE = rebase +DYLDINFO = dyldinfo ifdef BUILT_PRODUCTS_DIR # if run within Xcode, add the just built tools to the command path @@ -23,17 +24,22 @@ ifdef BUILT_PRODUCTS_DIR OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck REBASE = ${BUILT_PRODUCTS_DIR}/rebase + UNWINDDUMP = ${BUILT_PRODUCTS_DIR}/unwinddump + DYLDINFO = ${BUILT_PRODUCTS_DIR}/dyldinfo 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 := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH} - LD = ${RELEASEDIR}/ld - OBJECTDUMP = ${RELEASEDIR}/ObjectDump - MACHOCHECK = ${RELEASEDIR}/machocheck - REBASE = ${RELEASEDIR}/rebase + PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH} + LD = ${DEBUGDIR}/ld + OBJECTDUMP = ${DEBUGDIR}/ObjectDump + MACHOCHECK = ${DEBUGDIR}/machocheck + REBASE = ${DEBUGDIR}/rebase + UNWINDDUMP = ${DEBUGDIR}/unwinddump + DYLDINFO = ${DEBUGDIR}/dyldinfo else PATH := ${MYDIR}:${PATH}: COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: @@ -42,12 +48,15 @@ endif export PATH export COMPILER_PATH +ifeq ($(ARCH),ppc) + SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk +endif -CC = gcc-4.2 -arch ${ARCH} ${SDKExtra} +CC = cc -arch ${ARCH} ${SDKExtra} CCFLAGS = -Wall -std=c99 ASMFLAGS = -CXX = g++-4.2 -arch ${ARCH} ${SDKExtra} +CXX = c++ -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall ifeq ($(ARCH),armv6) @@ -89,6 +98,7 @@ else FILEARCH = $(ARCH) endif + RM = rm RMFLAGS = -rf diff --git a/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests index 842ce77..f6275a7 100755 --- a/ld64/unit-tests/run-all-unit-tests +++ b/ld64/unit-tests/run-all-unit-tests @@ -6,6 +6,7 @@ unset LD_TRACE_DYLIBS unset LD_TRACE_ARCHIVES export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib +export MACOSX_DEPLOYMENT_TARGET=10.7 # cd into test-cases directory cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` diff --git a/ld64/unit-tests/test-cases/16-byte-alignment/Makefile b/ld64/unit-tests/test-cases/16-byte-alignment/Makefile index a3a256f..e178ddb 100644 --- a/ld64/unit-tests/test-cases/16-byte-alignment/Makefile +++ b/ld64/unit-tests/test-cases/16-byte-alignment/Makefile @@ -33,7 +33,7 @@ all: ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o # verify that the alignment is correct in the .o - ObjectDump -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null + ${OBJECTDUMP} -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null # now verify the executable ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH} diff --git a/ld64/unit-tests/test-cases/Lpath/Makefile b/ld64/unit-tests/test-cases/Lpath/Makefile new file mode 100644 index 0000000..b580706 --- /dev/null +++ b/ld64/unit-tests/test-cases/Lpath/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + mkdir -p hide + libtool foo.o -static -o hide/libfoo.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} main.o -lfoo -Lhide -r -o mainfoo.o + ${LD} main.o -lfoo -L hide -r -o mainfoo.o + ${PASS_IFF} true + +clean: + rm -rf foo.o hide main.o mainfoo.o diff --git a/ld64/unit-tests/test-cases/Lpath/foo.c b/ld64/unit-tests/test-cases/Lpath/foo.c new file mode 100644 index 0000000..8c5841a --- /dev/null +++ b/ld64/unit-tests/test-cases/Lpath/foo.c @@ -0,0 +1,2 @@ + +int foo() { return 0; } diff --git a/ld64/unit-tests/test-cases/Lpath/main.c b/ld64/unit-tests/test-cases/Lpath/main.c new file mode 100644 index 0000000..49deebf --- /dev/null +++ b/ld64/unit-tests/test-cases/Lpath/main.c @@ -0,0 +1,8 @@ + +extern int foo(); + +int main() +{ + foo(); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/absolute-symbol/abs.s b/ld64/unit-tests/test-cases/absolute-symbol/abs.s index 216867c..1aee96f 100644 --- a/ld64/unit-tests/test-cases/absolute-symbol/abs.s +++ b/ld64/unit-tests/test-cases/absolute-symbol/abs.s @@ -1,3 +1,7 @@ .globl _myAbs - .set _myAbs, 0xfe000000 +#if __LP64__ + _myAbs = 0x012345678 +#else + _myAbs = 0xfe000000 +#endif diff --git a/ld64/unit-tests/test-cases/alias-command-line/Makefile b/ld64/unit-tests/test-cases/alias-command-line/Makefile index 9e87329..f700dbf 100644 --- a/ld64/unit-tests/test-cases/alias-command-line/Makefile +++ b/ld64/unit-tests/test-cases/alias-command-line/Makefile @@ -41,11 +41,11 @@ all: ${CC} ${ASMFLAGS} aliases.s -c -o aliases.tmp.${ARCH}.o ${FAIL_IF_BAD_OBJ} aliases.tmp.${ARCH}.o - ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -o aliases.cmdline.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -alias _hidden _glob -o aliases.cmdline.${ARCH}.o ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.cmdline.${ARCH}.o > aliases.cmdline.${ARCH}.o.dump ${FAIL_IF_ERROR} diff aliases.source.${ARCH}.o.dump aliases.cmdline.${ARCH}.o.dump - ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.file.${ARCH}.o > aliases.file.${ARCH}.o.dump ${PASS_IFF} diff aliases.source.${ARCH}.o.dump aliases.file.${ARCH}.o.dump diff --git a/ld64/unit-tests/test-cases/alias-command-line/aliases.s b/ld64/unit-tests/test-cases/alias-command-line/aliases.s index ffab4a9..48e5961 100644 --- a/ld64/unit-tests/test-cases/alias-command-line/aliases.s +++ b/ld64/unit-tests/test-cases/alias-command-line/aliases.s @@ -29,13 +29,20 @@ _temp: nop .globl _foo _foo: nop nop + + .globl _hidden + .private_extern _hidden +_hidden: nop + nop #if ALIASES .globl _fooalt .globl _fooalt2 + .globl _glob /* this should make an alias "_fooalt" for "_foo" */ _fooalt = _foo _fooalt2 = _foo +_glob = _hidden #endif _bar: nop diff --git a/ld64/unit-tests/test-cases/alias-command-line/aliases.txt b/ld64/unit-tests/test-cases/alias-command-line/aliases.txt index 291f2f7..d0a3da9 100644 --- a/ld64/unit-tests/test-cases/alias-command-line/aliases.txt +++ b/ld64/unit-tests/test-cases/alias-command-line/aliases.txt @@ -2,5 +2,6 @@ _foo _fooalt # comment _foo _fooalt2 +_hidden _glob diff --git a/ld64/unit-tests/test-cases/allow_heap_execute/Makefile b/ld64/unit-tests/test-cases/allow_heap_execute/Makefile new file mode 100644 index 0000000..57c3da7 --- /dev/null +++ b/ld64/unit-tests/test-cases/allow_heap_execute/Makefile @@ -0,0 +1,35 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set the data execute bit for i386 +# + +run: run-${ARCH} + +run-x86_64: + ${PASS_IFF} true + +run-armv6: + ${PASS_IFF} true + +run-armv7: + ${PASS_IFF} true + +run-ppc: + ${PASS_IFF} true + + +run-i386: + # Test with the flag + ${CC} ${CCFLAGS} main.c -o main-allow -Wl,-allow_heap_execute + ${FAIL_IF_BAD_MACHO} main-allow + ${OTOOL} -hv main-allow | grep MH_NO_HEAP_EXECUTION | ${FAIL_IF_STDIN} + # Test without the flag + ${CC} ${CCFLAGS} main.c -o main + ${OTOOL} -hv main | grep MH_NO_HEAP_EXECUTION | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main-allow diff --git a/ld64/unit-tests/test-cases/allow_heap_execute/main.c b/ld64/unit-tests/test-cases/allow_heap_execute/main.c new file mode 100644 index 0000000..e0d6d5d --- /dev/null +++ b/ld64/unit-tests/test-cases/allow_heap_execute/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/archive-ObjC-unexported/Makefile b/ld64/unit-tests/test-cases/archive-ObjC-unexported/Makefile new file mode 100644 index 0000000..f94ee81 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC-unexported/Makefile @@ -0,0 +1,44 @@ +## +# 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 + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is to check that -ObjC loads all (and only) +# .o files that contain Objective-C code. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${CC} ${CCFLAGS} bar.m -c -o bar.o + libtool -static bar.o foo.o -o libfoo.a + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.m libfoo.a -o main -framework Foundation -dead_strip -Wl,-unexported_symbols_list,main.nexp 2>main.log + grep Foobar main.log | ${FAIL_IF_EMPTY} + ${PASS_IFF_SUCCESS} true + +clean: + rm -rf foo.o bar.o libfoo.a main.log diff --git a/ld64/unit-tests/test-cases/archive-ObjC-unexported/bar.m b/ld64/unit-tests/test-cases/archive-ObjC-unexported/bar.m new file mode 100644 index 0000000..ad598bc --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC-unexported/bar.m @@ -0,0 +1,14 @@ +#include + +@interface Foobar : NSObject +@end + +void other() +{ + [[Foobar alloc] init]; +} + +void bar() +{ +} + diff --git a/ld64/unit-tests/test-cases/archive-ObjC-unexported/foo.m b/ld64/unit-tests/test-cases/archive-ObjC-unexported/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC-unexported/foo.m @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/ld64/unit-tests/test-cases/archive-ObjC-unexported/main.m b/ld64/unit-tests/test-cases/archive-ObjC-unexported/main.m new file mode 100644 index 0000000..58a039a --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC-unexported/main.m @@ -0,0 +1,9 @@ +#include + +extern void bar(); + +int main() +{ + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/archive-ObjC-unexported/main.nexp b/ld64/unit-tests/test-cases/archive-ObjC-unexported/main.nexp new file mode 100644 index 0000000..0b864aa --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-ObjC-unexported/main.nexp @@ -0,0 +1 @@ +_bar diff --git a/ld64/unit-tests/test-cases/archive-image_info/Makefile b/ld64/unit-tests/test-cases/archive-image_info/Makefile new file mode 100644 index 0000000..5bb026f --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-image_info/Makefile @@ -0,0 +1,50 @@ +## +# 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 + +# +# Test that objc files loaded from archives set image info +# + +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + +run: all + +all: + ${CC} ${CCFLAGS} main.m -c -o main.o + libtool -static main.o -o libmain.a + ${CC} ${CCFLAGS} libmain.a -o main -framework Foundation + size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main.o libmain.a main diff --git a/ld64/unit-tests/test-cases/archive-image_info/main.m b/ld64/unit-tests/test-cases/archive-image_info/main.m new file mode 100644 index 0000000..b012f44 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-image_info/main.m @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/bind_at_load/Makefile b/ld64/unit-tests/test-cases/bind_at_load/Makefile new file mode 100644 index 0000000..007feb5 --- /dev/null +++ b/ld64/unit-tests/test-cases/bind_at_load/Makefile @@ -0,0 +1,41 @@ +## +# 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 + +# +# Test -bind-at-load option +# + +run: all + +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} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libfoo.dylib libbar.dylib main diff --git a/ld64/unit-tests/test-cases/bind_at_load/bar.c b/ld64/unit-tests/test-cases/bind_at_load/bar.c new file mode 100644 index 0000000..d4a1007 --- /dev/null +++ b/ld64/unit-tests/test-cases/bind_at_load/bar.c @@ -0,0 +1,2 @@ + +void bar() {} diff --git a/ld64/unit-tests/test-cases/bind_at_load/foo.c b/ld64/unit-tests/test-cases/bind_at_load/foo.c new file mode 100644 index 0000000..ed41e9b --- /dev/null +++ b/ld64/unit-tests/test-cases/bind_at_load/foo.c @@ -0,0 +1,3 @@ + +void foo() {} +void foo2() {} diff --git a/ld64/unit-tests/test-cases/bind_at_load/main.c b/ld64/unit-tests/test-cases/bind_at_load/main.c new file mode 100644 index 0000000..7aadb5c --- /dev/null +++ b/ld64/unit-tests/test-cases/bind_at_load/main.c @@ -0,0 +1,14 @@ +#include + +extern void foo(); +extern void foo2() __attribute__((weak_import)); +extern void bar(); + +int main() +{ + foo(); + bar(); + if ( &foo2 != NULL ) + foo2(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/blank-stubs/Makefile b/ld64/unit-tests/test-cases/blank-stubs/Makefile index 346e2b7..19f0af1 100644 --- a/ld64/unit-tests/test-cases/blank-stubs/Makefile +++ b/ld64/unit-tests/test-cases/blank-stubs/Makefile @@ -23,7 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(VALID_ARCHS)) +ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(subst ppc,,$(VALID_ARCHS)) ) # # Test that blank stubs are handled properly @@ -34,7 +34,7 @@ run: all all: # build example fully fat dylib - gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib + gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib ${FAIL_IF_BAD_MACHO} libfoo.dylib # handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo diff --git a/ld64/unit-tests/test-cases/branch-distance/Makefile b/ld64/unit-tests/test-cases/branch-distance/Makefile index 46472ee..e2f7c30 100644 --- a/ld64/unit-tests/test-cases/branch-distance/Makefile +++ b/ld64/unit-tests/test-cases/branch-distance/Makefile @@ -35,9 +35,9 @@ run: all all: ${CC} ${CCFLAGS} foo.s -c ${CC} ${CCFLAGS} bar.s -c - ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar ${ARCH_FLAGS} -nostdlib + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar ${ARCH_FLAGS} -nostdlib -lSystem ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o - ${CC} ${CCFLAGS} foobar.o -e _foo -o foobar2 ${ARCH_FLAGS} -nostdlib + ${CC} ${CCFLAGS} foobar.o -e _foo -o foobar2 ${ARCH_FLAGS} -nostdlib -lSystem ${PASS_IFF_GOOD_MACHO} foobar clean: diff --git a/ld64/unit-tests/test-cases/branch-interworking/Makefile b/ld64/unit-tests/test-cases/branch-interworking/Makefile new file mode 100644 index 0000000..c83e25e --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-interworking/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +# +# test thumb2 branch ranges +# + +run: all + + +all: + ${CC} ${CCFLAGS} mythumb.s -c + ${CC} ${CCFLAGS} myarm.s -c + ${CC} ${CCFLAGS} mythumb.o myarm.o -dynamiclib -o liball.dylib + ${PASS_IFF_GOOD_MACHO} liball.dylib + +clean: + rm *.o liball.dylib diff --git a/ld64/unit-tests/test-cases/branch-interworking/myarm.s b/ld64/unit-tests/test-cases/branch-interworking/myarm.s new file mode 100644 index 0000000..a3c7818 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-interworking/myarm.s @@ -0,0 +1,19 @@ + + .text +#if __arm__ + .code 32 + .align 2 +#endif + + .globl _myarm +_myarm: + nop +#if __arm__ + //bl _mythumb + b _mythumb +#elif __i386__ || __x86_64__ + jmp _mythumb +#endif + + + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/branch-interworking/mythumb.s b/ld64/unit-tests/test-cases/branch-interworking/mythumb.s new file mode 100644 index 0000000..a58c28f --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-interworking/mythumb.s @@ -0,0 +1,20 @@ + + .text +#if __arm__ + .syntax unified + .thumb_func _mythumb + .code 16 +#endif + + .globl _mythumb +_mythumb: + nop +#if __arm__ + //bl _myarm + b.w _myarm +#elif __i386__ || __x86_64__ + jmp _myarm +#endif + + + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/cfstring-and-cstring/Makefile b/ld64/unit-tests/test-cases/cfstring-and-cstring/Makefile new file mode 100644 index 0000000..6f9b150 --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-and-cstring/Makefile @@ -0,0 +1,14 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c bar.c -Os -o foo -framework CoreFoundation + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf foo diff --git a/ld64/unit-tests/test-cases/cfstring-and-cstring/bar.c b/ld64/unit-tests/test-cases/cfstring-and-cstring/bar.c new file mode 100644 index 0000000..3441ce9 --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-and-cstring/bar.c @@ -0,0 +1,14 @@ +#include + + +// +// llvm may make cfstring that has backing store of kTest +// + + +const char kTest[] = "test"; + +void bar() +{ + CFStringGetLength(CFSTR("test")); +} diff --git a/ld64/unit-tests/test-cases/cfstring-and-cstring/foo.c b/ld64/unit-tests/test-cases/cfstring-and-cstring/foo.c new file mode 100644 index 0000000..86b0d6e --- /dev/null +++ b/ld64/unit-tests/test-cases/cfstring-and-cstring/foo.c @@ -0,0 +1,12 @@ +#include + +extern void bar(); + +int main() +{ + CFStringGetLength(CFSTR("stuff")); + bar(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile index 3957151..272cb9f 100644 --- a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile +++ b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# Copyright (c) 2008-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -34,7 +34,7 @@ ifeq (,${findstring 64,$(ARCH)}) else CFSTRING_SIZE = 64 endif - USTRING_SIZE = 27 + USTRING_SIZE = 28 diff --git a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile index 72f365b..cdfe6ac 100644 --- a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile +++ b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile @@ -33,7 +33,7 @@ run: all-${ARCH} all-ppc: ${PASS_IFF} true -all-arm: +all-armv6: ${PASS_IFF} true all-i386: all-real diff --git a/ld64/unit-tests/test-cases/commons-order/Makefile b/ld64/unit-tests/test-cases/commons-order/Makefile index 68d809c..9c97867 100644 --- a/ld64/unit-tests/test-cases/commons-order/Makefile +++ b/ld64/unit-tests/test-cases/commons-order/Makefile @@ -31,7 +31,7 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} baz.c -fno-common -c -o baz.o - ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main nm -j -n main | grep _common > symbol.order ${FAIL_IF_ERROR} diff symbol.order expected.order ${PASS_IFF_GOOD_MACHO} main diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile index 4e55304..ff0919b 100644 --- a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile @@ -33,6 +33,7 @@ run: all all: ${CC} ${CCFLAGS} main.c custom.s -o main size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} + size -l main | grep -A2 __MYSEG | grep __cstring | grep " 10 " | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s index 33705e5..8871b8a 100644 --- a/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/custom.s @@ -2,7 +2,9 @@ .section __MYSEG, __cstring, cstring_literals -LC: .ascii "hello" +LC1: .ascii "hello\0" +LC2: .ascii "bye\0" +LC3: .ascii "hello\0" diff --git a/ld64/unit-tests/test-cases/cstring-empty-labeled/Makefile b/ld64/unit-tests/test-cases/cstring-empty-labeled/Makefile new file mode 100644 index 0000000..9c3504d --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-empty-labeled/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +# +# verify labled empty strings are preserved +# +# + + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.s -o foo.o + ${LD} -arch ${ARCH} -r foo.o -o foo-r.o + ${LD} -arch ${ARCH} -r foo-r.o -o foo-r2.o + ${OBJECTDUMP} foo-r.o > foo-r.dump + ${OBJECTDUMP} foo-r2.o > foo-r2.dump + ${PASS_IFF} diff foo-r.dump foo-r2.dump + +clean: + rm foo.o foo-r.o foo-r2.o foo.dump foo-r.dump foo-r2.dump diff --git a/ld64/unit-tests/test-cases/cstring-empty-labeled/foo.s b/ld64/unit-tests/test-cases/cstring-empty-labeled/foo.s new file mode 100644 index 0000000..9498b53 --- /dev/null +++ b/ld64/unit-tests/test-cases/cstring-empty-labeled/foo.s @@ -0,0 +1,21 @@ + .cstring +LC2: + .ascii "bye\0" + .ascii "\0" + .ascii "\0" + +.globl _empty +_empty: + .ascii "\0" + +LC0: + .ascii "hello\0" + .ascii "\0" + .ascii "\0" + .ascii "\0" + +LC1: + .ascii "\0" + + + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/custom-segment-layout/Makefile b/ld64/unit-tests/test-cases/custom-segment-layout/Makefile new file mode 100644 index 0000000..1dfb23f --- /dev/null +++ b/ld64/unit-tests/test-cases/custom-segment-layout/Makefile @@ -0,0 +1,16 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that a large page zero works +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c zero.s -o main -pagezero_size 0 -segaddr __TEXT 0xb8000000 -segaddr __MYZEROPAGE 0 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main diff --git a/ld64/unit-tests/test-cases/custom-segment-layout/main.c b/ld64/unit-tests/test-cases/custom-segment-layout/main.c new file mode 100644 index 0000000..7f59a23 --- /dev/null +++ b/ld64/unit-tests/test-cases/custom-segment-layout/main.c @@ -0,0 +1,8 @@ + +int x = 5; + +int main() +{ + return 0; +} + diff --git a/ld64/unit-tests/test-cases/custom-segment-layout/zero.s b/ld64/unit-tests/test-cases/custom-segment-layout/zero.s new file mode 100644 index 0000000..86bde83 --- /dev/null +++ b/ld64/unit-tests/test-cases/custom-segment-layout/zero.s @@ -0,0 +1,11 @@ + + .section __MYZEROPAGE,_data +_min: .long 0 + + + .zerofill __MYZEROPAGE,__zerofill,_padding,2147483644 + + + + + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile new file mode 100644 index 0000000..59cadd4 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +# +# Tests that if an unused function is pulled in from an archive +# because another function in the same .o file is needed, that the +# first does not cause a duplicate definition error. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static foo.o bar.o -o libfoobar.a + ${CC} ${CCFLAGS} main.c libfoobar.a -dead_strip -o main -Wl,-w + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main libfoobar.a foo.o bar.o diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c new file mode 100644 index 0000000..87526e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c @@ -0,0 +1,8 @@ + +void bar() { } + +int baz() +{ + return -1; +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c new file mode 100644 index 0000000..c8fcef2 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c @@ -0,0 +1,10 @@ + +void foo() { } + +extern void bar(); + +void deadwood() +{ + bar(); +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c new file mode 100644 index 0000000..1f74deb --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c @@ -0,0 +1,14 @@ + +extern void foo(); + +int baz() +{ + return 0; +} + +int main() +{ + foo(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile new file mode 100644 index 0000000..f4ed236 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +# +# Tests that a non-weak symbol in an archive cleanly overrides +# a weak symbol in a .o file with dead code stripping +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static foo.o bar.o -o libfoobar.a + ${CC} ${CCFLAGS} main.c libfoobar.a -dead_strip -o main + nm main | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main foo.o bar.o libfoobar.a diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c new file mode 100644 index 0000000..605e44e --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c @@ -0,0 +1,6 @@ + + +void bar() +{ +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c new file mode 100644 index 0000000..92bcb8f --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c @@ -0,0 +1,15 @@ + +extern void bar(); + + +// strong definition of foo overrides weak definition +// in main.c, but this foo needs bar() +void foo() +{ + bar(); +} + +void loadme() +{ +} + diff --git a/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/main.c b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/main.c new file mode 100644 index 0000000..81362f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-archive-weak-override/main.c @@ -0,0 +1,25 @@ + +extern void loadme(); + + +void bad() +{ +} + +// foo is first found be live here +// then the use of loadme causes libfoo.a(foo.o) +// to be loaded which overrides foo +__attribute__((weak)) void foo() +{ + bad(); +} + +int main() +{ + foo(); + loadme(); + foo(); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/dead_strip/Makefile b/ld64/unit-tests/test-cases/dead_strip/Makefile index 32ac1c5..a274694 100644 --- a/ld64/unit-tests/test-cases/dead_strip/Makefile +++ b/ld64/unit-tests/test-cases/dead_strip/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -35,6 +35,7 @@ run: all all: ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} + size -l main-${ARCH} | grep __PAGEZERO | ${FAIL_IF_EMPTY} ${FAIL_IF_BAD_MACHO} main-${ARCH} nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} diff --git a/ld64/unit-tests/test-cases/demangle/Makefile b/ld64/unit-tests/test-cases/demangle/Makefile new file mode 100644 index 0000000..6ed52b7 --- /dev/null +++ b/ld64/unit-tests/test-cases/demangle/Makefile @@ -0,0 +1,45 @@ +## +# 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 + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Test that linker option -demangle works +# + +run: all + +all: + ${CXX} ${CXXFLAGS} main.cxx -c + ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -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 + grep 'Foo::doit()' main2.fail | ${PASS_IFF_STDIN} + + +clean: + rm -f main.o main1.fail main2.fail main diff --git a/ld64/unit-tests/test-cases/demangle/main.cxx b/ld64/unit-tests/test-cases/demangle/main.cxx new file mode 100644 index 0000000..af42ca6 --- /dev/null +++ b/ld64/unit-tests/test-cases/demangle/main.cxx @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ +#include + +class Foo +{ +public: + Foo() {} + + void doit(); +}; + + + +int main() +{ + Foo f; + f.doit(); + + return 0; +} diff --git a/ld64/unit-tests/test-cases/dependency-logging/Makefile b/ld64/unit-tests/test-cases/dependency-logging/Makefile new file mode 100644 index 0000000..e861226 --- /dev/null +++ b/ld64/unit-tests/test-cases/dependency-logging/Makefile @@ -0,0 +1,46 @@ +## +# 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 LD_TRACE_* work +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + 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} + ${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 + grep 'libfoo.dylib' trace_dylibs | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf foo.o libfoo.a libfoo.dylib main trace_ars trace_dylibs diff --git a/ld64/unit-tests/test-cases/dependency-logging/foo.c b/ld64/unit-tests/test-cases/dependency-logging/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dependency-logging/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/ld64/unit-tests/test-cases/dependency-logging/main.c b/ld64/unit-tests/test-cases/dependency-logging/main.c new file mode 100644 index 0000000..a5a79d5 --- /dev/null +++ b/ld64/unit-tests/test-cases/dependency-logging/main.c @@ -0,0 +1,7 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dtrace-old-probes/Makefile b/ld64/unit-tests/test-cases/dtrace-old-probes/Makefile new file mode 100644 index 0000000..61ddb05 --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-old-probes/Makefile @@ -0,0 +1,36 @@ +## +# 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 + +# +# Test that old style _dtrace_probe$ labels are preserved +# + +all: + ${CC} ${CCFLAGS} main.c -o main + nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/ld64/unit-tests/test-cases/dtrace-old-probes/main.c b/ld64/unit-tests/test-cases/dtrace-old-probes/main.c new file mode 100644 index 0000000..811449a --- /dev/null +++ b/ld64/unit-tests/test-cases/dtrace-old-probes/main.c @@ -0,0 +1,49 @@ + +#include + +#define DTRACE_STRINGIFY(s) #s +#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s) + +#define DTRACE_NOPS \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" + + +#define DTRACE_LAB(p, n) \ + "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n) + +#define DTRACE_LABEL(p, n) \ + ".section __DATA, __data\n\t" \ + ".globl " DTRACE_LAB(p, n) "\n\t" \ + DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \ + ".text" "\n\t" \ + "1:" + +#define DTRACE_CALL(p,n) \ + DTRACE_LABEL(p,n) \ + DTRACE_NOPS + +#define DTRACE_CALL0ARGS(provider, name) \ + __asm volatile ( \ + DTRACE_CALL(provider, name) \ + : \ + : \ + ); + +int deadwood() +{ + DTRACE_CALL0ARGS(__foo__, test2) + return 0; +} + + +int main() { + int a = 1; + + while(a) { + DTRACE_CALL0ARGS(__foo__, test1) + } + + return 0; +} diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx index b756394..935ee12 100644 --- a/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx +++ b/ld64/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx @@ -1,6 +1,6 @@ #include "header.h" -void f() { LOTS_O_PROBES } +void bar() { LOTS_O_PROBES } diff --git a/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile b/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile index b59760f..1688f23 100644 --- a/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile +++ b/ld64/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -30,7 +30,7 @@ include ${TESTROOT}/include/common.makefile all: run -run: main main-r main-dead_strip libmain.dylib +run: main main-dead_strip libmain.dylib main-r ${FAIL_IF_BAD_MACHO} main ${PASS_IFF_GOOD_MACHO} main-r diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile index 1eb6310..9770686 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -33,27 +33,16 @@ include ${TESTROOT}/include/common.makefile run: all -all: foobar.o main.o - ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-test-${ARCH} - nm -ap dwarf-test-${ARCH} | ./stabs-filter.pl > dwarf-test-${ARCH}.stabs - ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs - -foobar.o : foo.o bar.o +all: + ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o foo.o -mdynamic-no-pic + ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o bar.o -mdynamic-no-pic ${LD} -r -arch ${ARCH} foo.o bar.o -o foobar.o - ${FAIL_IF_BAD_OBJ} foobar.o - -foo.o : foo.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -bar.o : bar.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -main.o : main.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ + ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o main.o -mdynamic-no-pic + ${CXX} ${CCXXFLAGS} foobar.o main.o -o main + ${FAIL_IF_BAD_MACHO} main + nm -ap main | ./stabs-filter.pl > main.stabs + ${PASS_IFF} diff main.stabs expected-stabs + clean: - rm -rf dwarf-test-* *.o *.stabs + rm -rf foo.o bar.o main.o foobar.o main main.stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile new file mode 100644 index 0000000..e70c6b6 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile @@ -0,0 +1,45 @@ +## +# 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 + +# +# Verify that UUID is the same for the two binaries built +# from the same source file but with different intermediate +# object file paths. +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 main.c -c -o main1.o + ${CC} ${CCFLAGS} -gdwarf-2 main.c -c -o main2.o + ${CC} ${CCFLAGS} main1.o -o main1 + ${CC} ${CCFLAGS} main2.o -o main2 + otool -lv main1 | grep -A3 UUID > main1.uuid + otool -lv main2 | grep -A3 UUID > main2.uuid + ${PASS_IFF} diff main1.uuid main2.uuid + + +clean: + rm -rf main1.o main2.o main1 main2 main1.uuid main2.uuid diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c b/ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c new file mode 100644 index 0000000..6ff8d4b --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c @@ -0,0 +1,23 @@ + + +void foo() +{ + +} + + +void bar() +{ + foo(); +} + + + +int main() +{ + bar(); + return 0; +} + + + diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile index 42f1cb7..b4fa7ab 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,19 +32,13 @@ include ${TESTROOT}/include/common.makefile run: all -all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - nm -ap dwarf-hello-${ARCH} | ./stabs-filter.pl > dwarf-hello-${ARCH}.stabs - ${PASS_IFF} diff dwarf-hello-${ARCH}.stabs expected-stabs - -hello.o : hello.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ +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 + ${FAIL_IF_BAD_MACHO} hello + nm -ap hello | ./stabs-filter.pl > hello.stabs + ${PASS_IFF} diff hello.stabs expected-stabs clean: - rm -rf dwarf-hello-* *.o *.stabs + rm -rf hello hello.o other.o hello.stabs diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index 0ef639d..95eb33d 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -7,7 +7,7 @@ 0000 ENSYM 0000 BNSYM 0000 FUN __Z3fooi -0000 SOL header.h +0000 SOL CWD//header.h 0000 FUN 0000 ENSYM 0000 SO @@ -22,12 +22,12 @@ 0000 FUN .my_non_standard_function_name 0000 FUN 0000 ENSYM -0000 GSYM _init 0000 STSYM .my_non_standard_name_static 0000 STSYM .my_non_standard_name 0000 STSYM __ZZ3bariE8bar_init +0000 GSYM _init 0000 GSYM _uninit +0000 STSYM __ZZ3bariE10bar_uninit 0000 STSYM __ZL7suninit 0000 STSYM __ZL5sinit -0000 STSYM __ZZ3bariE10bar_uninit 0000 SO diff --git a/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile b/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile new file mode 100644 index 0000000..3812933 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile @@ -0,0 +1,29 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that ld -r -S preserves the __objc_imageinfo section +# + +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.m -c -o hello.o + ${LD} -r -S hello.o -o hello-r.o + size -l hello-r.o | grep ${IMAGE_INFO} | ${PASS_IFF_STDIN} + + + +clean: + rm -rf hello.o hello-r.o diff --git a/ld64/unit-tests/test-cases/dwarf-strip-objc/hello.m b/ld64/unit-tests/test-cases/dwarf-strip-objc/hello.m new file mode 100644 index 0000000..ffecb41 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-strip-objc/hello.m @@ -0,0 +1,8 @@ +#include + + +int main() +{ + [NSString stringWithUTF8String: "hello"]; + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dwarf-strip/Makefile b/ld64/unit-tests/test-cases/dwarf-strip/Makefile index 947afa7..8174f11 100644 --- a/ld64/unit-tests/test-cases/dwarf-strip/Makefile +++ b/ld64/unit-tests/test-cases/dwarf-strip/Makefile @@ -31,10 +31,10 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o dwarf-hello-${ARCH} - #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - nm -ap dwarf-hello-${ARCH} | grep -e " - " | ${PASS_IFF_EMPTY} + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o hello + #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o hello + ${FAIL_IF_BAD_MACHO} hello + nm -ap hello | grep -e " - " | ${PASS_IFF_EMPTY} clean: - rm -rf dwarf-hello-* + rm -rf hello hello.dSYM diff --git a/ld64/unit-tests/test-cases/dylib-aliases/Makefile b/ld64/unit-tests/test-cases/dylib-aliases/Makefile index 1cf9fa0..5ed2e91 100644 --- a/ld64/unit-tests/test-cases/dylib-aliases/Makefile +++ b/ld64/unit-tests/test-cases/dylib-aliases/Makefile @@ -31,7 +31,7 @@ include ${TESTROOT}/include/common.makefile run: all all: libfoo.dylib libbar.dylib libbaz.dylib - ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. + ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. -Wl,-w ${PASS_IFF_GOOD_MACHO} main libfoo.dylib : foo.c diff --git a/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile index 617fbfc..057ac03 100644 --- a/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile +++ b/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,19 +32,14 @@ SHELL = bash # use bash shell so we can redirect just stderr run: all -all: libfoo.dylib +all: + 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 + ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 ${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true grep "cycle" errmsg | ${PASS_IFF_STDIN} -libfoo.dylib : foo.c libbar.dylib - ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 - -libbar.dylib : bar.c other/libfoo.dylib - ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 - -other/libfoo.dylib : foo.c - mkdir -p other - ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 diff --git a/ld64/unit-tests/test-cases/dylib-upward/Makefile b/ld64/unit-tests/test-cases/dylib-upward/Makefile new file mode 100644 index 0000000..5248a33 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-upward/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +# Verify upward dylib options work + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-upward-lbar -L. + otool -lv libfoo.dylib | grep -A2 LC_LOAD_UPWARD_DYLIB | grep libbar.dylib | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-upward_library,libbar.dylib + otool -lv libfoo.dylib | grep -A2 LC_LOAD_UPWARD_DYLIB | grep libbar.dylib | ${FAIL_IF_EMPTY} + mkdir -p Bar.framework + ${CC} ${CCFLAGS} bar.c -dynamiclib -o Bar.framework/Bar + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-upward_framework,Bar -F. + otool -lv libfoo.dylib | grep -A2 LC_LOAD_UPWARD_DYLIB | grep Bar.framework/Bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf libfoo.dylib libbar.dylib Bar.framework diff --git a/ld64/unit-tests/test-cases/dylib-upward/bar.c b/ld64/unit-tests/test-cases/dylib-upward/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-upward/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/unit-tests/test-cases/dylib-upward/foo.c b/ld64/unit-tests/test-cases/dylib-upward/foo.c new file mode 100644 index 0000000..bf74f09 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-upward/foo.c @@ -0,0 +1,6 @@ +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/ld64/unit-tests/test-cases/efi-basic/LibTest.c b/ld64/unit-tests/test-cases/efi-basic/LibTest.c new file mode 100644 index 0000000..de051c8 --- /dev/null +++ b/ld64/unit-tests/test-cases/efi-basic/LibTest.c @@ -0,0 +1,13 @@ + +char *gS = (void *)0; + +void LibInit(void) +{ +} + + +void OutputString(char *String) +{ + gS = String; +} + diff --git a/ld64/unit-tests/test-cases/efi-basic/Makefile b/ld64/unit-tests/test-cases/efi-basic/Makefile new file mode 100644 index 0000000..1f392c5 --- /dev/null +++ b/ld64/unit-tests/test-cases/efi-basic/Makefile @@ -0,0 +1,73 @@ +## +# Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## + +# +# This test verifies ld -preload support required for mtoc +# to create a PE/COFF image that will work with EFI. +# The mach-o file and pecoff files are parsed with the +# test.py script to make sure the images look OK. +# +# Note: We should fail with an error condition if any of the all: +# commands fail. +# Note: Currently does not support ARM due to no ARM support in +# /usr/local/efi/bin/objdump +# +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +LOCAL_CC_FLAGS = -g -Os -fshort-wchar -fno-strict-aliasing -Wall -Werror -Wno-missing-braces + +#ARCH=x86_64 +#ARCH=i386 + + +ifeq (${ARCH},x86_64) + OBJ_DUMP_ARCH=efi-app-x86-64 + LD_ARG= +else + OBJ_DUMP_ARCH=efi-app-ia32 + LD_ARG=-read_only_relocs suppress +endif + + +# +# Compile the file +# + +all: + ${CC} -arch $(ARCH) -o LibTest1.obj $(LOCAL_CC_FLAGS) -c LibTest.c + 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 + /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 + /usr/local/efi/bin/objdump -b $(OBJ_DUMP_ARCH) -x MtocTest1.pecoff > objdump-raw.log + ${PASS_IFF_SUCCESS} ./mtoctest.py --arch $(ARCH) + + +clean: + rm -rf *.pecoff *.macho *.obj *.log *.lib *.map + + diff --git a/ld64/unit-tests/test-cases/efi-basic/MtocTest.c b/ld64/unit-tests/test-cases/efi-basic/MtocTest.c new file mode 100644 index 0000000..5e3ebd5 --- /dev/null +++ b/ld64/unit-tests/test-cases/efi-basic/MtocTest.c @@ -0,0 +1,21 @@ + +extern void LibInit(void); + +extern void OutputString(char *String); + + + +char * gString = "\nHello world via mtoc - reloc\n"; + +int gWhyAreUninitializedGlobalsBad; + + +void _ModuleEntryPoint(void) +{ + __asm__ __volatile__ ("int $3"); + + LibInit (); + gWhyAreUninitializedGlobalsBad =1; + OutputString(gString); +} + diff --git a/ld64/unit-tests/test-cases/efi-basic/mtoctest.py b/ld64/unit-tests/test-cases/efi-basic/mtoctest.py new file mode 100755 index 0000000..460c8f5 --- /dev/null +++ b/ld64/unit-tests/test-cases/efi-basic/mtoctest.py @@ -0,0 +1,135 @@ +#!/usr/bin/python + +import sys +import getopt + +def Usage(): + print "Unkown command line args" + + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:],"hav:",["help","arch=","verbose"]) + except getopt.GetoptError: + print "Unkown command line args" + sys.exit(2) + + Verbose = 0 + for o,a in opts: + if o in ("-h", "--help"): + Usage () + sys.exit () + elif o in ("-a", "--arch"): + Arch = a + else: + Usage () + sys.exit () + + + if Verbose: + print "\nmach-o load commands:" + otoolload = open("otool-load.log", "r") + data = otoolload.read() + otoolload.close() + + + # extract extry point from ' ss 0x00000000 eflags 0x00000000 eip 0x00000259 cs 0x00000000' + if Arch == "i386": + eip = data.find("eip") + if eip != -1: + EntryPoint = int (data[eip + 4:eip + 4 + 10], 16) + + if Arch == "arm": + r15 = data.find("r15") + if r15 != -1: + EntryPoint = int (data[r15 + 4:r15 + 4 + 10], 16) + + # extract entry point from ' r15 0x0000000000000000 rip 0x0000000000000253' + if Arch == "x86_64": + rip = data.find("rip") + if rip != -1: + EntryPoint = int (data[rip + 4:rip + 4 + 18], 16) + + if EntryPoint == 0: + print "FAIL - no entry point for PE/COFF image" + sys.exit(-1) + else: + if Verbose: + print "Entry Point = 0x%08x" % EntryPoint + + + if Verbose: + print "\nPE/COFF dump:" + objdump = open("objdump-raw.log", "r") + data = objdump.read() + objdump.close() + + # Extract 'SizeOfImage 00000360' + Index = data.find("SizeOfImage") + End = data[Index:].find("\n") + SizeOfImage = int (data[Index+11:Index + End], 16); + if Verbose: + print "SizeOfImage = 0x%08x" % SizeOfImage + + #Parse ' 0 .text 00000080 00000240 00000240 00000240 2**2' + # ' CONTENTS, ALLOC, LOAD, READONLY, CODE ' + EndOfTable = data.find("SYMBOL TABLE:") + Index = data.find("Idx Name") + End = data[Index:].find("\n") + Index = Index + End + 1 + + PeCoffEnd = 0 + while Index < EndOfTable: + End = data[Index:].find("\n") + Split = data[Index:Index+End].split() + # Split[0] Indx + # Split[1] Name i.e. .text + # Split[2] Size + # Split[3] VMA + # Split[4] LMA + # Split[5] File Off + # Split[6] Align + if int(Split[3],16) != int(Split[5],16): + print "FAIL - %s VMA %08x not equal File off %08x XIP will not work" % (Split[1], int(Split[3],16), int(Split[5],16)) + sys.exit(-1) + + if int(Split[3],16) + int(Split[2],16) > PeCoffEnd: + PeCoffEnd = int(Split[3],16) + int(Split[2],16) + + if Split[1] == ".text": + SecStart = int(Split[3],16) + SecEnd = int(Split[3],16) + int(Split[2],16) + if (EntryPoint < SecStart) or (EntryPoint > SecEnd): + print "FAIL - Entry point (0x%x) not in .text section (0x%x - 0x%x)" % (EntryPoint, SecStart, SecEnd) + sys.exit(-1) + + if Verbose: + print "%10s" % Split[1] + ' ' + Split[2] + ' ' + Split[3] + ' ' + Split[4] + ' ' + Split[5] + " End = %x" % PeCoffEnd + Index += data[Index:].find("\n") + 1 + Index += data[Index:].find("\n") + 1 + + if SizeOfImage < PeCoffEnd: + print "FAIL - PE/COFF Header SizeOfImage (0x%x) is not correct. Image larger than size (0x%x)." % (SizeOfImage, PeCoffEnd) + sys.exit(-1) + + if Verbose: + print "\nmach-o relocations:" + otoolreloc = open("otool-reloc.log", "r") + lines = otoolreloc.readlines() + otoolreloc.close() + + found = False + for line in lines: + if found: + chunk = line.split() + if Verbose: + print chunk[0] + if line.find ("address") > -1: + found = True + + if Verbose: + print + + +if __name__ == "__main__": + main() diff --git a/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx b/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx index 83d1845..59aa60d 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx +++ b/ld64/unit-tests/test-cases/eh-coalescing/bar.cxx @@ -23,7 +23,7 @@ */ #include -// this non-seak func() will have no unwind info and no LSDA +// this non-weak func() will have no unwind info and no LSDA int func() { return 0; } void foo(int x) {} diff --git a/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile b/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile index 107400e..15819f0 100644 --- a/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile +++ b/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile @@ -10,13 +10,13 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CXX} ${CCXXFLAGS} main.cxx -c -o main1.o -Os + ${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 - ${CXX} ${CCXXFLAGS} main1.o -Wl,-x -o main1 -exported_symbols_list keep.exp + ${CXX} ${CCXXFLAGS} main1.o -o main1 ${CXX} ${CCXXFLAGS} main2.o -o main2 - unwinddump -arch ${ARCH} main1 > main1.unwind - unwinddump -arch ${ARCH} main2 > main2.unwind + ${UNWINDDUMP} -arch ${ARCH} -no_symbols main1 > main1.unwind + ${UNWINDDUMP} -arch ${ARCH} -no_symbols main2 > main2.unwind ${PASS_IFF} diff main1.unwind main2.unwind clean: diff --git a/ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx b/ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx index 325937d..90cb581 100644 --- a/ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx +++ b/ld64/unit-tests/test-cases/eh-stripped-symbols/main.cxx @@ -24,29 +24,48 @@ #include #include -void bar() -{ -} - +int global = 0; -void foo2() -{ - int a = arc4random(); - int b = arc4random(); - fprintf(stderr, "hello %d %d\n", a, b); +int bar() +{ + global = 1; + throw 10; + } -void foo1() + +void foo() { - foo2(); - fprintf(stderr, "world\n"); + try { + bar(); + } + catch(int x) { + global = 2; + throw x; + } } int main() { - foo1(); - bar(); - return 0; -} \ No newline at end of file + int state = 1; + try { + state = 2; + foo(); + state = 3; + } + catch (int x) { + if ( state != 2 ) + return 1; + if ( x != 10 ) + return 1; + state = 4; + } + + if ( (state == 4) && (global == 2) ) + return 0; + else + return 1; +} + diff --git a/ld64/unit-tests/test-cases/empty-dylib/Makefile b/ld64/unit-tests/test-cases/empty-dylib/Makefile index a12e7c1..504fb97 100644 --- a/ld64/unit-tests/test-cases/empty-dylib/Makefile +++ b/ld64/unit-tests/test-cases/empty-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -25,15 +25,23 @@ include ${TESTROOT}/include/common.makefile # # Check that the n_sect number for __mh_dylib_header is valid when there is no __text section +# Also check that codesigning works on empty dylib. # +CODESIGN_ARCH = ${ARCH} +ifeq (${ARCH},ppc) + CODESIGN_ARCH = ppc7400 +endif + + run: all all: ${CC} ${CCFLAGS} -dynamiclib justdata.c -o libjustdata.dylib -Wl,-no_compact_unwind ${PASS_IFF_GOOD_MACHO} libjustdata.dylib ${CC} ${CCFLAGS} -dynamiclib empty.c -o libempty.dylib -Wl,-no_compact_unwind - ${PASS_IFF_GOOD_MACHO} libempty.dylib + codesign_allocate -i libempty.dylib -a ${CODESIGN_ARCH} 1024 -o libsigned.dylib + ${PASS_IFF_GOOD_MACHO} libsigned.dylib clean: - rm -rf libempty.dylib libjustdata.dylib + rm -rf libempty.dylib libjustdata.dylib libsigned.dylib diff --git a/ld64/unit-tests/test-cases/exported-symbols-dead_strip/Makefile b/ld64/unit-tests/test-cases/exported-symbols-dead_strip/Makefile new file mode 100644 index 0000000..52c0d43 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-dead_strip/Makefile @@ -0,0 +1,39 @@ +## +# 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 + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Tests that link fails if undefined symbol is in export list +# + +run: all + +all: + ${PASS_IFF_ERROR} ${CC} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list foo.exp -dead_strip 2>/dev/null + +clean: + rm libfoo.dylib diff --git a/ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.c b/ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.c new file mode 100644 index 0000000..6bb8d95 --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void good() {} +void bad() {} + + +void ABC() {} +void ABD() { good(); } +void DEF() {} +void DEG() { bad(); } + diff --git a/ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp b/ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp new file mode 100644 index 0000000..565684e --- /dev/null +++ b/ld64/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp @@ -0,0 +1,3 @@ +_ABC +_ABCD +_DEF diff --git a/ld64/unit-tests/test-cases/function-starts/Makefile b/ld64/unit-tests/test-cases/function-starts/Makefile new file mode 100644 index 0000000..0b43cd5 --- /dev/null +++ b/ld64/unit-tests/test-cases/function-starts/Makefile @@ -0,0 +1,29 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check -function_starts +# + +run: all + +all: + # as main executable + ${CC} ${CCFLAGS} main.c -o main -Wl,-function_starts + ${DYLDINFO} -function_starts main | grep _bar | ${FAIL_IF_EMPTY} + # as dylib + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib -Wl,-function_starts + ${DYLDINFO} -function_starts libmain.dylib | grep _bar | ${FAIL_IF_EMPTY} + # as dylib with prefered load address + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain2.dylib -seg1addr 0x200000 -Wl,-function_starts + ${DYLDINFO} -function_starts libmain2.dylib | grep _bar | ${FAIL_IF_EMPTY} + # 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} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm main libmain.dylib libmain2.dylib libmain3.dylib diff --git a/ld64/unit-tests/test-cases/function-starts/main.c b/ld64/unit-tests/test-cases/function-starts/main.c new file mode 100644 index 0000000..4ff1717 --- /dev/null +++ b/ld64/unit-tests/test-cases/function-starts/main.c @@ -0,0 +1,10 @@ + +void foo() {} + +void mid() {} + +static void bar() { foo(); } + +int main() { bar(); return 0; } + + diff --git a/ld64/unit-tests/test-cases/hidden-r/Makefile b/ld64/unit-tests/test-cases/hidden-r/Makefile new file mode 100644 index 0000000..702038c --- /dev/null +++ b/ld64/unit-tests/test-cases/hidden-r/Makefile @@ -0,0 +1,41 @@ +## +# 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 + + +# +# Tests that a hidden tentative or weak hidden symbol survives -r +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r main.o foo.o -o all.o + ${CC} ${CCFLAGS} all.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main.o foo.o all.o main diff --git a/ld64/unit-tests/test-cases/hidden-r/foo.c b/ld64/unit-tests/test-cases/hidden-r/foo.c new file mode 100644 index 0000000..8c03a7f --- /dev/null +++ b/ld64/unit-tests/test-cases/hidden-r/foo.c @@ -0,0 +1,5 @@ +void __attribute__((weak,visibility("hidden"))) my_weak() +{ +} + +int __attribute__((visibility("hidden"))) my_tent; diff --git a/ld64/unit-tests/test-cases/hidden-r/main.c b/ld64/unit-tests/test-cases/hidden-r/main.c new file mode 100644 index 0000000..352c2f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/hidden-r/main.c @@ -0,0 +1,12 @@ +#include + +extern void my_weak(); +extern int my_tent; + +int main() +{ + my_tent = 0; + my_weak(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/kext-basic/mykext.c b/ld64/unit-tests/test-cases/kext-basic/mykext.c index 51de101..6dfeb5c 100644 --- a/ld64/unit-tests/test-cases/kext-basic/mykext.c +++ b/ld64/unit-tests/test-cases/kext-basic/mykext.c @@ -1,5 +1,6 @@ #include +extern void extern_func(); int my_global = 3; extern int extern_global; @@ -7,6 +8,7 @@ extern int extern_global; kern_return_t mykext_start (kmod_info_t * ki, void * d) { ++my_global; ++extern_global; + extern_func(); return KERN_SUCCESS; } diff --git a/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile b/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile index a6f1537..bd9a8ff 100755 --- a/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile +++ b/ld64/unit-tests/test-cases/label-on-end-of-section/Makefile @@ -31,7 +31,7 @@ run: all all: ${CC} ${CCFLAGS} foo.s -c -o foo.o - ${OBJECTDUMP} foo.o | grep "pointer to _end" | ${PASS_IFF_STDIN} + ${OBJECTDUMP} foo.o | grep "address of direct(_end)" | ${PASS_IFF_STDIN} clean: rm foo.o diff --git a/ld64/unit-tests/test-cases/large-bss/Makefile b/ld64/unit-tests/test-cases/large-bss/Makefile new file mode 100644 index 0000000..0e5a155 --- /dev/null +++ b/ld64/unit-tests/test-cases/large-bss/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld can link > 4GB zero fill section +# + + +run: all + +all: + ${CC} ${CCFLAGS} test.s -dynamiclib -o libtest.dylib + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib diff --git a/ld64/unit-tests/test-cases/large-bss/test.s b/ld64/unit-tests/test-cases/large-bss/test.s new file mode 100644 index 0000000..c09a83c --- /dev/null +++ b/ld64/unit-tests/test-cases/large-bss/test.s @@ -0,0 +1,33 @@ + +#if __x86_64__ + + +.lcomm _mediumarray1,4000,5 +.lcomm _bigarray1,2000000000,5 +.lcomm _bigarray2,2000000000,5 +.lcomm _bigarray3,2000000000,5 +.lcomm _small1,4,2 +.lcomm _small2,4,2 +.lcomm _small3,4,2 + + + .text +.globl _test +_test: + pushq %rbp + movq %rsp, %rbp + movq _bigarray1@GOTPCREL(%rip), %rax + movq _bigarray2@GOTPCREL(%rip), %rax + movq _bigarray3@GOTPCREL(%rip), %rax + leaq _small1(%rip),%rax + leaq _small2(%rip),%rax + leaq _small3(%rip),%rax + leaq _mediumarray1(%rip),%rax + leave + ret + + +#endif + + + diff --git a/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile b/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile index 975ad29..0eb717b 100644 --- a/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile +++ b/ld64/unit-tests/test-cases/lazy-dylib-objc/Makefile @@ -42,4 +42,4 @@ all: clean: - rm libfoo.dylib main rm fail.log + rm -f libfoo.dylib main rm fail.log diff --git a/ld64/unit-tests/test-cases/lazy-dylib/Makefile b/ld64/unit-tests/test-cases/lazy-dylib/Makefile index e3d0d6a..4f1b14d 100644 --- a/ld64/unit-tests/test-cases/lazy-dylib/Makefile +++ b/ld64/unit-tests/test-cases/lazy-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2008-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -35,10 +35,10 @@ run: all all: ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + ${CC} ${CCFLAGS} main.c -lazy-lfoo -L. -o main ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad.c -Wl,-lazy_library,libfoo.dylib -o bad 2> fail.log ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad2.c -Wl,-lazy_library,libfoo.dylib -o bad2 2> fail.log - ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/literals-labels/Makefile b/ld64/unit-tests/test-cases/literals-labels/Makefile new file mode 100644 index 0000000..92b3535 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-labels/Makefile @@ -0,0 +1,21 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Support mulitple labels on the same literal by cloning the literal +# into an atom per label. +# + +run: all + +all: + ${CC} ${ASMFLAGS} literals.s -c -o literals.o + ${OBJECTDUMP} literals.o > literals.o.dump + ${LD} -arch ${ARCH} -r literals.o -o literals-r.o + ${OBJECTDUMP} literals-r.o > literals-r.o.dump + ${PASS_IFF} diff literals.o.dump literals-r.o.dump + +clean: + rm -rf literals.o literals-r.o literals.o.dump literals-r.o.dump diff --git a/ld64/unit-tests/test-cases/literals-labels/literals.s b/ld64/unit-tests/test-cases/literals-labels/literals.s new file mode 100644 index 0000000..1fde088 --- /dev/null +++ b/ld64/unit-tests/test-cases/literals-labels/literals.s @@ -0,0 +1,77 @@ +/* + * 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@ + */ + + .literal16 +L01:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654321 + +L02:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654322 + +_foo16: +_bar16: + .long 22345678 + .long 87654321 + .long 12345678 + .long 87654323 + +L04:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654324 + + + .literal8 +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +_foo8: +_bar8: + .long 22345678 + .long 87654323 + +L4: .long 12345678 + .long 87654324 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +_foo4: +_bar4: + .long 22345670 +L14:.long 12345671 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +_string1: +_string2: + .ascii "there\0" +L24: .ascii "bye\0" diff --git a/ld64/unit-tests/test-cases/llvm-integration/Makefile b/ld64/unit-tests/test-cases/llvm-integration/Makefile index 30376df..513dccc 100644 --- a/ld64/unit-tests/test-cases/llvm-integration/Makefile +++ b/ld64/unit-tests/test-cases/llvm-integration/Makefile @@ -251,16 +251,16 @@ seventeen: ${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 b17.o - ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o - file ab17.o | grep "Mach-O" | ${PASS_IFF_STDIN} + ${LLVMGCC} ${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 ${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o - ObjectDump -nm a18-rkpe.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} - ObjectDump -nm a18-rkpe.o | grep _func_hidden2 | grep " hidden" | ${FAIL_IF_EMPTY} + 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} #echo verify ld -r makes hidden symbols internal (except for commons) ${LD} -arch ${ARCH} -r a18.o -o a18-r.o #ObjectDump -nm a18-r.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} diff --git a/ld64/unit-tests/test-cases/llvm-integration/a17.c b/ld64/unit-tests/test-cases/llvm-integration/a17.c index 3cd06fd..1b8d4bf 100644 --- a/ld64/unit-tests/test-cases/llvm-integration/a17.c +++ b/ld64/unit-tests/test-cases/llvm-integration/a17.c @@ -2,3 +2,11 @@ int a = 0; int func_a() { return a; } +// add code that will cause stack canary +extern void fill(char*); +void test() +{ + char buf[100]; + fill(buf); +} + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile new file mode 100644 index 0000000..ccd5b59 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# 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 + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -f foo.o libfoo.dylib diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-objc/foo.m b/ld64/unit-tests/test-cases/lto-dead_strip-objc/foo.m new file mode 100644 index 0000000..6ee5735 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-objc/foo.m @@ -0,0 +1,27 @@ +#include + +@interface Foo : NSObject +- (NSString*) foo; +@end + + +@implementation Foo +- (NSString*) foo +{ + return [NSString stringWithUTF8String:"hello"]; +} +@end + + +@interface Bar : NSData +- (NSArray*) bar; +@end + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile new file mode 100644 index 0000000..d563c92 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# 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 + ${PASS_IFF_GOOD_MACHO} libbar.dylib + +clean: + rm bar.o libbar.dylib diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c b/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c new file mode 100644 index 0000000..480ac85 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c @@ -0,0 +1,19 @@ + +int data[] = { 4, 5, 6 }; +int deaddata[] = { 7, 8, 9 }; + +int func() { return 0; } +int deadfunc() { return 0; } + +__attribute__((visibility("default"))) +int* foo() +{ + return data; +} + +__attribute__((visibility("default"))) +void* foo2() +{ + return func; +} + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile new file mode 100644 index 0000000..fa0a02b --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile @@ -0,0 +1,44 @@ +## +# 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 + +# +# 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 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf foo.o bar.o baz.o main.o main diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-tentative/bar.c b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/bar.c new file mode 100644 index 0000000..d8fb526 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/bar.c @@ -0,0 +1,4 @@ + +int tent; + +int bar() { return tent; } diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-tentative/baz.c b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/baz.c new file mode 100644 index 0000000..d1cca6e --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/baz.c @@ -0,0 +1,5 @@ + +int tent; + +int baz() { return tent; } + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-tentative/foo.c b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/foo.c new file mode 100644 index 0000000..983fe9c --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/foo.c @@ -0,0 +1,4 @@ + +int tent; + +int foo() { return tent; } diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-tentative/main.c b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/main.c new file mode 100644 index 0000000..4138be7 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/main.c @@ -0,0 +1,12 @@ + +extern int foo(); +extern int bar(); +extern int baz(); + +int main() +{ + foo(); + bar(); + baz(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile new file mode 100644 index 0000000..a518d82 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +# +# 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 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f bar.o main.o main + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-unused/bar.c b/ld64/unit-tests/test-cases/lto-dead_strip-unused/bar.c new file mode 100644 index 0000000..f5f5823 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-unused/bar.c @@ -0,0 +1,15 @@ + + +void bar() +{ +} + + +extern void unused1(); + +void unused2() +{ + unused1(); +} + + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-unused/main.c b/ld64/unit-tests/test-cases/lto-dead_strip-unused/main.c new file mode 100644 index 0000000..f04d475 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-unused/main.c @@ -0,0 +1,8 @@ +extern void bar(); + +int main() +{ + bar(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile b/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile new file mode 100644 index 0000000..8b13444 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile @@ -0,0 +1,53 @@ +## +# 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 + +# +# 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) + IMAGE_INFO = "__objc_imageinfo" +endif + + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.m -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o -o main -framework Foundation + size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main.o diff --git a/ld64/unit-tests/test-cases/lto-objc-image-info/main.m b/ld64/unit-tests/test-cases/lto-objc-image-info/main.m new file mode 100644 index 0000000..7b88000 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-objc-image-info/main.m @@ -0,0 +1,9 @@ + +#include + +int main() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pool drain]; + return 0; +} + diff --git a/ld64/unit-tests/test-cases/lto-object_path/Makefile b/ld64/unit-tests/test-cases/lto-object_path/Makefile new file mode 100644 index 0000000..aeda85d --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-object_path/Makefile @@ -0,0 +1,42 @@ +## +# 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 linker option -object_path_lto results in tmp .o file +# 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 + nm main.tmp.o | grep _main | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main main.o main.tmp.o main diff --git a/ld64/unit-tests/test-cases/lto-object_path/main.c b/ld64/unit-tests/test-cases/lto-object_path/main.c new file mode 100644 index 0000000..578d24b --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-object_path/main.c @@ -0,0 +1,15 @@ + +#include + + +void foo(int x) +{ + printf("hello, world %d\n", x); +} + +int main() +{ + foo(10); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/main-stripped/Makefile b/ld64/unit-tests/test-cases/main-stripped/Makefile index 03756ff..784ba44 100644 --- a/ld64/unit-tests/test-cases/main-stripped/Makefile +++ b/ld64/unit-tests/test-cases/main-stripped/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,9 +30,11 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} main.c -o main-${ARCH} -exported_symbols_list main.exp - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm -m main-${ARCH} | grep _magicSymbol | grep "referenced dynamically" | ${PASS_IFF_STDIN} + ${CC} main.c -o main -exported_symbols_list main.exp + nm -m main | grep _magicSymbol | grep "referenced dynamically" | ${FAIL_IF_EMPTY} + nm -m main | grep _hiddenSymbol | grep "referenced dynamically" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + clean: - rm main-* + rm main diff --git a/ld64/unit-tests/test-cases/main-stripped/main.c b/ld64/unit-tests/test-cases/main-stripped/main.c index 1e71f1b..1304615 100644 --- a/ld64/unit-tests/test-cases/main-stripped/main.c +++ b/ld64/unit-tests/test-cases/main-stripped/main.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,6 +27,9 @@ int magicSymbol = 1; asm(".desc _magicSymbol, 0x10"); +// this symbol will be suppressed by .exp file +int hiddenSymbol = 1; +asm(".desc _hiddenSymbol, 0x10"); int main() { diff --git a/ld64/unit-tests/test-cases/main-stripped/main.exp b/ld64/unit-tests/test-cases/main-stripped/main.exp index 4eb9e89..1c7daa3 100644 --- a/ld64/unit-tests/test-cases/main-stripped/main.exp +++ b/ld64/unit-tests/test-cases/main-stripped/main.exp @@ -1 +1,2 @@ _main +_magicSymbol diff --git a/ld64/unit-tests/test-cases/no-uuid/Makefile b/ld64/unit-tests/test-cases/no-uuid/Makefile index 5d52553..7c5304b 100644 --- a/ld64/unit-tests/test-cases/no-uuid/Makefile +++ b/ld64/unit-tests/test-cases/no-uuid/Makefile @@ -47,8 +47,8 @@ all: ${FAIL_IF_BAD_MACHO} foo ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN} -# Test ld -r of stabs file has no uuid - ${CC} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ +# Test ld -r of stabs file has no uuid (llvm does not support stabs, so use gcc) + gcc-4.2 -arch ${ARCH} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ ${LD} -arch ${ARCH} foo.o -r -o foo2.o ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN} diff --git a/ld64/unit-tests/test-cases/non-lazy-r/Makefile b/ld64/unit-tests/test-cases/non-lazy-r/Makefile index d581048..3d594fd 100644 --- a/ld64/unit-tests/test-cases/non-lazy-r/Makefile +++ b/ld64/unit-tests/test-cases/non-lazy-r/Makefile @@ -38,6 +38,8 @@ all-i386: hasnl all-armv6: hasnl +all-armv7: hasnl + all-x86_64: all-true all-true: @@ -49,15 +51,18 @@ hasnl: ${CC} ${CCFLAGS} -c other.c -o other.o ${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo # make sure there are two indirect symbols: _foo and LOCAL - otool -Iv fooall.o | grep "3 entries" | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep "4 entries" | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _tent | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN} - # make sure re-parsed correctly - ${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${OBJECTDUMP} fooall.o | grep name: | grep '_tent$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${PASS_IFF} true + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to:_foo' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to-local:_other' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to:_tent' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to:_foo' | ${FAIL_IF_EMPTY} + ${LD} -r -arch ${ARCH} fooall.o -o fooall2.o + ${OBJECTDUMP} fooall.o > fooall.dump + ${OBJECTDUMP} fooall2.o > fooall2.dump + ${PASS_IFF} diff fooall.dump fooall2.dump clean: - rm -rf *.o + rm -rf *.o fooall.dump fooall2.dump diff --git a/ld64/unit-tests/test-cases/non-lazy-r/foo.c b/ld64/unit-tests/test-cases/non-lazy-r/foo.c index 9d21475..bfde3de 100644 --- a/ld64/unit-tests/test-cases/non-lazy-r/foo.c +++ b/ld64/unit-tests/test-cases/non-lazy-r/foo.c @@ -15,3 +15,6 @@ extern int tent; int gettent() { return tent; } + +extern void* func; +void* getfunc() { return func; } diff --git a/ld64/unit-tests/test-cases/non-lazy-r/other.c b/ld64/unit-tests/test-cases/non-lazy-r/other.c index 68eb01d..8ee4784 100644 --- a/ld64/unit-tests/test-cases/non-lazy-r/other.c +++ b/ld64/unit-tests/test-cases/non-lazy-r/other.c @@ -1,3 +1,4 @@ int foo = 2; int other = 3; int tent; +void func() {} diff --git a/ld64/unit-tests/test-cases/non-lazy-sections-r/Makefile b/ld64/unit-tests/test-cases/non-lazy-sections-r/Makefile new file mode 100644 index 0000000..020cd0a --- /dev/null +++ b/ld64/unit-tests/test-cases/non-lazy-sections-r/Makefile @@ -0,0 +1,55 @@ +## +# 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 non-lazy-pointers in different section are properly handled by -r +# + + +all: all-${ARCH} + +all-ppc: hasnl + +all-i386: hasnl + +all-armv6: hasnl + +all-armv7: hasnl + +all-x86_64: all-true + +all-true: + ${PASS_IFF} true + + +hasnl: + ${CC} ${CCFLAGS} -c foo.s -o foo.o + ${LD} -r -arch ${ARCH} foo.o -o foo-r.o + ${OBJECTDUMP} foo.o > foo.o.dump + ${OBJECTDUMP} foo-r.o > foo-r.o.dump + ${PASS_IFF} diff foo.o.dump foo-r.o.dump + +clean: + rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s b/ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s new file mode 100644 index 0000000..d307880 --- /dev/null +++ b/ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s @@ -0,0 +1,34 @@ + .text + .globl _test +_test: +#if __i386__ + movl L_foo$non_lazy_ptr, %eax + movl L_bar$non_lazy_ptr, %eax + movl L_other$non_lazy_ptr, %eax + ret +#endif +#if __arm__ || __ppc__ + .long L_foo$non_lazy_ptr + .long L_bar$non_lazy_ptr + .long L_other$non_lazy_ptr +#endif + + + + .section __IMPORT,__pointers,non_lazy_symbol_pointers +L_foo$non_lazy_ptr: +.indirect_symbol _foo + .long 0 + + .section __DATA,__one,non_lazy_symbol_pointers +L_bar$non_lazy_ptr: +.indirect_symbol _bar + .long 0 + + .section __DATA,__two,non_lazy_symbol_pointers +L_other$non_lazy_ptr: +.indirect_symbol _other + .long 0 + + +.subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/objc-abi/Makefile b/ld64/unit-tests/test-cases/objc-abi/Makefile new file mode 100644 index 0000000..d7eb660 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-abi/Makefile @@ -0,0 +1,49 @@ +## +# 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 + +# +# Verify -objc_abi_version 2 works for i386 +# +ifeq (${ARCH},i386) + ALL = all-i386 +else + ALL = all +endif + +run: ${ALL} + +all: + ${PASS_IFF_GOOD_MACHO} /usr/bin/true + +all-i386: + ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 + size -l test1 | grep __image_info | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} test.m -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 + size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} test2 + + +clean: + rm -rf test1 test2 diff --git a/ld64/unit-tests/test-cases/objc-abi/test.m b/ld64/unit-tests/test-cases/objc-abi/test.m new file mode 100644 index 0000000..8b4a295 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-abi/test.m @@ -0,0 +1,17 @@ + +@interface Foo +@end + +@implementation Foo +@end + + +int main() +{ + return 0; +} + + +#if __i386__ && __OBJC2__ + int _objc_empty_vtable = 1; +#endif diff --git a/ld64/unit-tests/test-cases/objc-category-archive/Makefile b/ld64/unit-tests/test-cases/objc-category-archive/Makefile new file mode 100644 index 0000000..b56bacf --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-archive/Makefile @@ -0,0 +1,27 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify -ObjC works with x86_64 categories +# Verify stripped archives keep don't-strip bit +# + +run: all + +all: + ${CC} ${CCFLAGS} -g test.m -c -o test.o + libtool -static test.o -o libtest.a + ${CC} ${CCFLAGS} main.m libtest.a -ObjC -o main -framework Foundation + nm main | grep mycatmethod1 | ${FAIL_IF_EMPTY} + nm main | grep mycatmethod2 | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r -S -keep_private_externs test.o -o test-stripped.o + libtool -static test-stripped.o -o libtest-stripped.a + ${CC} ${CCFLAGS} main.m libtest-stripped.a -all_load -dead_strip -o main2 -framework Foundation + nm main2 | grep mycatmethod1 | ${FAIL_IF_EMPTY} + nm main2 | grep mycatmethod2 | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main2 + +clean: + rm -rf test.o test-stripped.o libtest.a libtest-stripped.a main main2 diff --git a/ld64/unit-tests/test-cases/objc-category-archive/main.m b/ld64/unit-tests/test-cases/objc-category-archive/main.m new file mode 100644 index 0000000..1105885 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-archive/main.m @@ -0,0 +1,6 @@ + +int main() +{ + return 0; +} + diff --git a/ld64/unit-tests/test-cases/objc-category-archive/test.m b/ld64/unit-tests/test-cases/objc-category-archive/test.m new file mode 100644 index 0000000..5347856 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-archive/test.m @@ -0,0 +1,21 @@ + +#include + +int some_global_to_stop_libtool_warning = 5; + +@interface NSObject (stuff) +- (void) mycatmethod1; +@end + +@implementation NSObject (stuff) +- (void) mycatmethod1 { } +@end + +@interface NSObject (other) +- (void) mycatmethod2; +@end + +@implementation NSObject (other) +- (void) mycatmethod2 { } +@end + diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile new file mode 100644 index 0000000..5652312 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -0,0 +1,52 @@ +## +# 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 + +# +# Verify optimization where categories are merged into classes +# +OPTIONS = + +ifeq ($(ARCH),i386) + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 +endif + +all: all-${ARCH} + +all-ppc: + ${PASS_IFF} true + +all-i386: all-rest +all-x86_64: all-rest +all-armv6: all-rest + +all-rest: + # check optimzation of category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m -framework Foundation -o libfoo.dylib + size -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + + +clean: + rm -rf libfoo.dylib diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/cat1.m b/ld64/unit-tests/test-cases/objc-category-optimize-load/cat1.m new file mode 100644 index 0000000..06a78f0 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/cat1.m @@ -0,0 +1,15 @@ +#include + +@interface Foo : NSObject +-(void) method1; +@end + + +@interface Foo(mycat) ++(void) load; +@end + +@implementation Foo(mycat) ++(void) load {} +@end + diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/foo.m b/ld64/unit-tests/test-cases/objc-category-optimize-load/foo.m new file mode 100644 index 0000000..065450c --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/foo.m @@ -0,0 +1,9 @@ +#include + +@interface Foo : NSObject +@end + + +@implementation Foo +@end + diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile new file mode 100644 index 0000000..c452bec --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile @@ -0,0 +1,71 @@ +## +# 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 + +# +# Verify optimization where categories are merged into classes +# +OPTIONS = + +ifeq ($(ARCH),i386) + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 +endif + +all: all-${ARCH} + +all-ppc: + ${PASS_IFF} true + +all-i386: all-rest +all-x86_64: all-rest +all-armv6: all-rest + +all-rest: + # check optimization can be turned off + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -Wl,-no_objc_category_merging -o libno.dylib + size -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN} + otool -ov libno.dylib | grep -A17 __objc_classlist | grep -A16 '_OBJC_CLASS_$$_Foo' | grep "count 1" | ${FAIL_IF_EMPTY} + # check optimzation of category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -o libfoo.dylib + size -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 4" | ${FAIL_IF_EMPTY} + # check optimzation of protocol and category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROTOCOLS -framework Foundation -o libfoo2.dylib + size -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo2.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} + # check optimzation of properties and category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROPERTIES -framework Foundation -o libfoo3.dylib + size -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo3.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} + # check optimzation of category methods and no base methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DNO_BASE_METHODS -framework Foundation -o libfoo4.dylib + size -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo4.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 3" | ${FAIL_IF_EMPTY} + otool -ov libfoo4.dylib | grep -A20 "Meta Class" | grep "count 2" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo3.dylib + + + +clean: + rm -rf lib*.dylib diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m b/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m new file mode 100644 index 0000000..11c170b --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m @@ -0,0 +1,29 @@ +#include + +@interface Foo : NSObject +-(void) method1; +@end + + + + +@interface Foo(mycat) +-(void) instance_method_mycat1; +-(void) instance_method_mycat2; ++(void) class_method_mycat; +#if PROPERTIES + @property(readonly) int property1; + @property(readonly) int property2; +#endif +@end + +@implementation Foo(mycat) +-(void) instance_method_mycat1 {} +-(void) instance_method_mycat2 {} ++(void) class_method_mycat {} +#if PROPERTIES + -(int) property1 { return 0; } + -(int) property2 { return 0; } +#endif +@end + diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/cat2.m b/ld64/unit-tests/test-cases/objc-category-optimize/cat2.m new file mode 100644 index 0000000..7b51291 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize/cat2.m @@ -0,0 +1,37 @@ +#include + +@interface Foo : NSObject +-(void) method1; +@end + +#if PROTOCOLS + @protocol myotherprotocol + - (void) instance_method_myotherprotocol1; + - (void) instance_method_myotherprotocol2; + @end + + @interface Foo(myothercat) < myotherprotocol > + - (void) instance_method_myothercat; + + (void) class_method_myothercat; + @end + + @implementation Foo(myothercat) + - (void) instance_method_myothercat {} + + (void) class_method_myothercat {} + - (void) instance_method_myotherprotocol1 {} + - (void) instance_method_myotherprotocol2 {} + @end + +#else + @interface Foo(myothercat) + - (void) instance_method_myothercat; + + (void) class_method_myothercat; + @end + + @implementation Foo(myothercat) + - (void) instance_method_myothercat {} + + (void) class_method_myothercat {} + @end +#endif + + diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/foo.m b/ld64/unit-tests/test-cases/objc-category-optimize/foo.m new file mode 100644 index 0000000..0509927 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-optimize/foo.m @@ -0,0 +1,17 @@ +#include + +@interface Foo : NSObject +#ifndef NO_BASE_METHODS +-(void) instance_method; ++(void) class_method; +#endif +@end + + +@implementation Foo +#ifndef NO_BASE_METHODS +-(void) instance_method {} ++(void) class_method {} +#endif +@end + diff --git a/ld64/unit-tests/test-cases/objc-class-alias/Makefile b/ld64/unit-tests/test-cases/objc-class-alias/Makefile new file mode 100644 index 0000000..c8c1b27 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-class-alias/Makefile @@ -0,0 +1,44 @@ +## +# 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 + +# +# Verify -alias works with ObjC classes +# +CLASS_NAME_FOO = .objc_class_name_Foo +ifeq (${ARCH},x86_64) + CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo' +endif + + +run: all + +all: + ${CC} ${CCFLAGS} test.m -Wl,-alias,${CLASS_NAME_FOO},_MagicName -framework Foundation -o test + ${DYLDINFO} -export test | grep ${CLASS_NAME_FOO} | awk '{ print $$1}' > foo.addr + ${DYLDINFO} -export test | grep _MagicName | awk '{ print $$1}' > magic.addr + ${PASS_IFF} diff foo.addr magic.addr + +clean: + rm -rf test foo.addr magic.addr diff --git a/ld64/unit-tests/test-cases/objc-class-alias/test.m b/ld64/unit-tests/test-cases/objc-class-alias/test.m new file mode 100644 index 0000000..7eba08f --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-class-alias/test.m @@ -0,0 +1,13 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile index 0abe7d5..b18012a 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile @@ -26,6 +26,16 @@ include ${TESTROOT}/include/common.makefile SHELL = bash # use bash shell so we can redirect just stderr +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + # # Validate that the linker catches illegal combinations of .o files @@ -70,6 +80,11 @@ test: ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + # check GC/RR + RR -> RR + ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + # check GC + GC/RR -> GC ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib ${FAIL_IF_BAD_MACHO} libfoobar.dylib @@ -78,7 +93,42 @@ test: # check RR + GC -> error ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log + # check cmd line GC/RR, GC/RR + RR -> error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc 2> fail.log + + # check GC/RR + compaction + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12 | ${FAIL_IF_EMPTY} + + # check GC + compaction + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16 | ${FAIL_IF_EMPTY} + + # none + GC/RR-dylib -> none + ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + + # none + GC-dylib -> none + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + + # none + RR-dylib -> none + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + + # check RR + GC-dylib -> error + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + + # check GC + RR-dylib -> error + ${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 clean: - rm -rf foo*.o bar*.o libfoobar.dylib fail.log + rm -rf foo*.o bar*.o libfoobar.dylib fail.log libfoo.dylib libnone.dylib diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/none.c b/ld64/unit-tests/test-cases/objc-gc-checks/none.c new file mode 100644 index 0000000..44506fa --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-gc-checks/none.c @@ -0,0 +1 @@ +void none() {} diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile index c0b9081..6596f83 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile +++ b/ld64/unit-tests/test-cases/objc-literal-pointers/Makefile @@ -36,10 +36,10 @@ run: all all: ${CC} ${CCFLAGS} test.m -c -o test.o - ${OBJECTDUMP} -no_content test.o | grep -v zero-fill-at> test.dump + ${OBJECTDUMP} -no_content test.o > test.dump ${LD} -arch ${ARCH} -r test.o -keep_private_externs -o test-r.o - ${OBJECTDUMP} -no_content test-r.o | grep -v zero-fill-at > test-r.dump + ${OBJECTDUMP} -no_content test-r.o > test-r.dump diff test.dump test-r.dump | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/objc-properties/Makefile b/ld64/unit-tests/test-cases/objc-properties/Makefile new file mode 100644 index 0000000..999bf2f --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-properties/Makefile @@ -0,0 +1,19 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify an Objective-C object file when run through ld -r is unaltered. +# +# + +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: + rm -rf libtest.dylib diff --git a/ld64/unit-tests/test-cases/objc-properties/test.m b/ld64/unit-tests/test-cases/objc-properties/test.m new file mode 100644 index 0000000..25ca8c8 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-properties/test.m @@ -0,0 +1,49 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +#include + +@interface Test +{ + BOOL one; + NSString* two; + BOOL three; +} +@property BOOL one; +@property (retain) NSString* two; +@property BOOL three; +@end + + +@implementation Test +@synthesize one; +@synthesize two; +@synthesize three; +@end + + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/objc-visibility/Makefile b/ld64/unit-tests/test-cases/objc-visibility/Makefile new file mode 100644 index 0000000..bd0eb15 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-visibility/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Tests when a hidden class is made file scoped (via ld -r) +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${CC} ${CCFLAGS} bar.m -c -o bar.o + ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libtfoobar.dylib -framework Foundation + ${LD} -r -arch ${ARCH} foo.o bar.o -o bar2.o + ${CC} ${CCFLAGS} bar2.o -dynamiclib -o liball.dylib -framework Foundation + ${PASS_IFF_GOOD_MACHO} liball.dylib + +clean: + rm -rf *.o *.dylib diff --git a/ld64/unit-tests/test-cases/objc-visibility/bar.h b/ld64/unit-tests/test-cases/objc-visibility/bar.h new file mode 100644 index 0000000..dc37fe1 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-visibility/bar.h @@ -0,0 +1,8 @@ + +#include + +__attribute__((visibility("hidden"))) +@interface Bar : NSData +- (NSArray*) bar; +@end + diff --git a/ld64/unit-tests/test-cases/objc-visibility/bar.m b/ld64/unit-tests/test-cases/objc-visibility/bar.m new file mode 100644 index 0000000..997ac26 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-visibility/bar.m @@ -0,0 +1,13 @@ +#include + + +#include "bar.h" + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/ld64/unit-tests/test-cases/objc-visibility/foo.h b/ld64/unit-tests/test-cases/objc-visibility/foo.h new file mode 100644 index 0000000..e22d744 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-visibility/foo.h @@ -0,0 +1,8 @@ + +#include + + +@interface Foo : NSObject +- (NSString*) foo; +@end + diff --git a/ld64/unit-tests/test-cases/objc-visibility/foo.m b/ld64/unit-tests/test-cases/objc-visibility/foo.m new file mode 100644 index 0000000..4882e7b --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-visibility/foo.m @@ -0,0 +1,14 @@ + +#include + +#include "foo.h" +#include "bar.h" + +@implementation Foo +- (NSString*) foo +{ + [Bar alloc]; + return [NSString stringWithUTF8String:"hello"]; +} +@end + diff --git a/ld64/unit-tests/test-cases/order_file-ans/main.cxx b/ld64/unit-tests/test-cases/order_file-ans/main.cxx index b3c1edd..4ae8026 100644 --- a/ld64/unit-tests/test-cases/order_file-ans/main.cxx +++ b/ld64/unit-tests/test-cases/order_file-ans/main.cxx @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -55,5 +55,9 @@ namespace wow { int main() { + wow::inner(); + baz(NULL); + bar(NULL); + foo(); return 0; } diff --git a/ld64/unit-tests/test-cases/order_file/Makefile b/ld64/unit-tests/test-cases/order_file/Makefile index 21ae0ea..1aec6f1 100644 --- a/ld64/unit-tests/test-cases/order_file/Makefile +++ b/ld64/unit-tests/test-cases/order_file/Makefile @@ -33,25 +33,29 @@ include ${TESTROOT}/include/common.makefile run: all all: - as -arch ${ARCH} -L extra.s -o extra.o - ${CC} ${CCFLAGS} main.c extra.o -o main1 -Wl,-order_file -Wl,main1.order + ${CC} ${CCFLAGS} main.c extra.s -o main1 -Wl,-order_file -Wl,main1.order ${FAIL_IF_BAD_MACHO} main1 nm -n -g -j main1 | grep "_main" > main1.nm ${PASS_IFF} diff main1.nm main1.expected - ${CC} ${CCFLAGS} main.c extra.o -o main2 -Wl,-order_file -Wl,main2.order + ${CC} ${CCFLAGS} main.c extra.s -o main2 -Wl,-order_file -Wl,main2.order ${FAIL_IF_BAD_MACHO} main2 nm -n -j main2 | egrep '^_[a-z]+[0-9]$$' > main2.nm ${PASS_IFF} diff main2.nm main2.expected - ${CC} -arch ${ARCH} -c main.c -o main.o - ${CC} ${CCFLAGS} main.o extra.o -o main3 -Wl,-order_file -Wl,main3.order + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o extra.s -o main3 -Wl,-order_file -Wl,main3.order ${FAIL_IF_BAD_MACHO} main3 nm -n -g -j main3 | grep "_main" > main3.nm ${PASS_IFF} diff main3.nm main3.expected + ${CC} ${CCFLAGS} main.c extra.s -DSUBSECTIONS=1 -o main4 -Wl,-order_file -Wl,main4.order + ${FAIL_IF_BAD_MACHO} main4 + nm -n -g -j main4 | nm -njg main4 | egrep '*[1-9]' > main4.nm + ${PASS_IFF} diff main4.nm main4.expected + clean: - rm -rf main1 *.nm main2 *.o warnings.log main3 + rm -rf main1 main2 main3 main4 main.o *.nm diff --git a/ld64/unit-tests/test-cases/order_file/extra.s b/ld64/unit-tests/test-cases/order_file/extra.s index 90166ce..eba52c0 100644 --- a/ld64/unit-tests/test-cases/order_file/extra.s +++ b/ld64/unit-tests/test-cases/order_file/extra.s @@ -7,6 +7,8 @@ _foo1: nop .globl _aaa2 _aaa2: + .globl _bbb2 + .private_extern _bbb2 _bbb2: _ccc2: nop @@ -20,5 +22,7 @@ _ccc3: _aaa4: nop - - \ No newline at end of file + +#if SUBSECTIONS + .subsections_via_symbols +#endif diff --git a/ld64/unit-tests/test-cases/order_file/main4.expected b/ld64/unit-tests/test-cases/order_file/main4.expected new file mode 100644 index 0000000..b82a23d --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main4.expected @@ -0,0 +1,6 @@ +_aaa2 +_main3 +_main2 +_main4 +_foo1 +_bbb3 diff --git a/ld64/unit-tests/test-cases/order_file/main4.order b/ld64/unit-tests/test-cases/order_file/main4.order new file mode 100644 index 0000000..fe07188 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file/main4.order @@ -0,0 +1,4 @@ + + +_bbb2 +_main3 diff --git a/ld64/unit-tests/test-cases/re-export-and-use/Makefile b/ld64/unit-tests/test-cases/re-export-and-use/Makefile new file mode 100644 index 0000000..b220580 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-and-use/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +# +# Test that a public re-exported library is automatically added as a dependent +# unless nothing is used from it. +# + + +run: all + +all: + + ${CC} ${CCFLAGS} -dynamiclib pub.c -o libpub.dylib -install_name /usr/lib/libpub.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${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} + # 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} + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + + +clean: + + rm -rf libbar.dylib libbaz.dylib libpub.dylib libfoo1.dylib libfoo2.dylib diff --git a/ld64/unit-tests/test-cases/re-export-and-use/bar.c b/ld64/unit-tests/test-cases/re-export-and-use/bar.c new file mode 100644 index 0000000..642a280 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-and-use/bar.c @@ -0,0 +1,5 @@ + +int bar() +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-and-use/baz.c b/ld64/unit-tests/test-cases/re-export-and-use/baz.c new file mode 100644 index 0000000..98b7832 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-and-use/baz.c @@ -0,0 +1,5 @@ + +int baz() +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-and-use/foo.c b/ld64/unit-tests/test-cases/re-export-and-use/foo.c new file mode 100644 index 0000000..ad634d3 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-and-use/foo.c @@ -0,0 +1,16 @@ +extern int bar(); +extern int baz(); + +void* pbar = &bar; + +#if USE_BAZ +void* pbaz = &baz; +#endif + +int foo() +{ +#if USE_BAZ + baz(); +#endif + return bar(); +} diff --git a/ld64/unit-tests/test-cases/re-export-and-use/pub.c b/ld64/unit-tests/test-cases/re-export-and-use/pub.c new file mode 100644 index 0000000..03ffc1b --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-and-use/pub.c @@ -0,0 +1,5 @@ + +int pub() +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-layers/Makefile b/ld64/unit-tests/test-cases/re-export-layers/Makefile new file mode 100644 index 0000000..fc62273 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-layers/Makefile @@ -0,0 +1,61 @@ +## +# 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 + +# +# Test all the different ways that re-exports can be specified and implemented +# + + +run: all + +all: + + +# -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 + ${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 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${PASS_IFF_GOOD_MACHO} main + + +clean: + + rm -rf libbaz.dylib libbar.dylib libfoo.dylib main + diff --git a/ld64/unit-tests/test-cases/re-export-layers/bar.c b/ld64/unit-tests/test-cases/re-export-layers/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-layers/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-layers/baz.c b/ld64/unit-tests/test-cases/re-export-layers/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-layers/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-layers/foo.c b/ld64/unit-tests/test-cases/re-export-layers/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-layers/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-layers/main.c b/ld64/unit-tests/test-cases/re-export-layers/main.c new file mode 100644 index 0000000..5f1fae9 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-layers/main.c @@ -0,0 +1,15 @@ + + +extern void baz(); +extern void bar(); +extern void foo(); + + +int main() +{ + baz(); + bar(); + foo(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/re-export-symbol/Makefile b/ld64/unit-tests/test-cases/re-export-symbol/Makefile new file mode 100644 index 0000000..6de347c --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/Makefile @@ -0,0 +1,56 @@ +## +# 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 + +# +# Test that fine grain re-exports works +# + +run: all + +all: + # build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + + # build library the re-exports _bar from base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -exported_symbols_list foo.exp + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${DYLDINFO} -export libfoo.dylib | grep _bar | grep 're-export' | ${FAIL_IF_EMPTY} + # link against dylib and verify _bar is marked as coming from libfoo + ${CC} ${CCFLAGS} main1.c libfoo.dylib -o main1 + ${DYLDINFO} -bind -lazy_bind main1 | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + + # build library the re-exports _bar from base library as _mybar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp + ${FAIL_IF_BAD_MACHO} libfoo2.dylib + ${DYLDINFO} -export libfoo2.dylib | grep _mybar | grep 're-export' | grep _bar | ${FAIL_IF_EMPTY} + # link against dylib and verify _mybar is marked as coming from libfoo + ${CC} ${CCFLAGS} main2.c libfoo2.dylib -o main2 + ${DYLDINFO} -bind -lazy_bind main2 | grep _mybar | grep libfoo2 | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + +clean: + rm -rf libbar.dylib libfoo.dylib libfoo2.dylib main1 main2 diff --git a/ld64/unit-tests/test-cases/re-export-symbol/bar.c b/ld64/unit-tests/test-cases/re-export-symbol/bar.c new file mode 100644 index 0000000..34e5666 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/foo.c b/ld64/unit-tests/test-cases/re-export-symbol/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/foo.exp b/ld64/unit-tests/test-cases/re-export-symbol/foo.exp new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/foo.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/ld64/unit-tests/test-cases/re-export-symbol/foo2.exp b/ld64/unit-tests/test-cases/re-export-symbol/foo2.exp new file mode 100644 index 0000000..d55b748 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/foo2.exp @@ -0,0 +1,2 @@ +_foo +_mybar diff --git a/ld64/unit-tests/test-cases/re-export-symbol/main1.c b/ld64/unit-tests/test-cases/re-export-symbol/main1.c new file mode 100644 index 0000000..4c0b25e --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/main1.c @@ -0,0 +1,9 @@ +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/main2.c b/ld64/unit-tests/test-cases/re-export-symbol/main2.c new file mode 100644 index 0000000..edac057 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-symbol/main2.c @@ -0,0 +1,10 @@ +extern int foo(); +extern int mybar(); + +int main() +{ + foo(); + mybar(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/read-only-relocs/Makefile b/ld64/unit-tests/test-cases/read-only-relocs/Makefile index 02ed1df..8db5b03 100644 --- a/ld64/unit-tests/test-cases/read-only-relocs/Makefile +++ b/ld64/unit-tests/test-cases/read-only-relocs/Makefile @@ -32,15 +32,24 @@ SHELL = bash # use bash shell so we can redirect just stderr NO_PIC = STATIC = +RELOC_FAIL = ${FAIL_IF_SUCCESS} +LRELOCS_NEEDED = ${FAIL_IF_EMPTY} +XRELOCS_NEEDED = ${FAIL_IF_EMPTY} ifeq (${ARCH},i386) NO_PIC = -mdynamic-no-pic STATIC = -static -else - ifeq (${ARCH},ppc) - NO_PIC = -mdynamic-no-pic - STATIC = -mdynamic-no-pic - endif +endif +ifeq (${ARCH},ppc) + NO_PIC = -mdynamic-no-pic + STATIC = -mdynamic-no-pic + XRELOCS_NEEDED = ${FAIL_IF_STDIN} + LRELOCS_NEEDED = ${FAIL_IF_EMPTY} +endif +ifeq (${ARCH},x86_64) + RELOC_FAIL = + XRELOCS_NEEDED = ${FAIL_IF_STDIN} + LRELOCS_NEEDED = ${FAIL_IF_STDIN} endif @@ -48,16 +57,25 @@ endif all: # build libfoo.dylib as regular dylib ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + # build libtest.dylib using -mdynamic-no-pic, should fail + ${CC} ${CCFLAGS} test_rebase.c -c ${NO_PIC} + ${RELOC_FAIL} ${CC} ${CCFLAGS} test_rebase.o libfoo.dylib -dynamiclib -o libtestrebase.dylib -read_only_relocs error 2>/dev/null + ${CC} ${CCFLAGS} test_bind.c -c ${NO_PIC} + ${RELOC_FAIL} ${CC} ${CCFLAGS} test_bind.o libfoo.dylib -dynamiclib -o libtestbind.dylib -read_only_relocs error 2>/dev/null # build libtest.dylib using -mdynamic-no-pic and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${NO_PIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + ${CC} ${CCFLAGS} test_rebase.c -c ${NO_PIC} + ${CC} ${CCFLAGS} test_bind.c -c ${NO_PIC} + ${CC} ${CCFLAGS} test_rebase.o test_bind.o libfoo.dylib -dynamiclib -o libtest-no-pic.dylib -read_only_relocs suppress -Wl,-w # build libtest.dylib using -static and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${STATIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + ${CC} ${CCFLAGS} test_rebase.c -c ${STATIC} + ${CC} ${CCFLAGS} test_bind.c -c ${STATIC} + ${CC} ${CCFLAGS} test_rebase.o test_bind.o libfoo.dylib -dynamiclib -o libtest-static.dylib -read_only_relocs suppress -Wl,-w + otool -lv libtest-static.dylib | grep -A9 "sectname __text" | grep attributes | grep EXT_RELOC | ${XRELOCS_NEEDED} + otool -lv libtest-static.dylib | grep -A9 "sectname __text" | grep attributes | grep LOC_RELOC | ${LRELOCS_NEEDED} # build main using -static and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${STATIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -o foo -read_only_relocs suppress -Wl,-w - ${PASS_IFF_GOOD_MACHO} foo + ${CC} ${CCFLAGS} main.c -c ${STATIC} + ${CC} ${CCFLAGS} main.o libfoo.dylib -o main -read_only_relocs suppress -Wl,-w + ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf test.o libfoo.dylib libtest.dylib foo + rm -rf test_bind.o test_rebase.o libfoo.dylib libtestrebase.dylib libtestbind.dylib libtest-no-pic.dylib libtest-static.dylib main.o main diff --git a/ld64/unit-tests/test-cases/read-only-relocs/main.c b/ld64/unit-tests/test-cases/read-only-relocs/main.c new file mode 100644 index 0000000..d5fe9c2 --- /dev/null +++ b/ld64/unit-tests/test-cases/read-only-relocs/main.c @@ -0,0 +1,10 @@ + +extern int b; +extern void func(); + +int main() +{ + func(); + return b; +} + diff --git a/ld64/unit-tests/test-cases/read-only-relocs/test_bind.c b/ld64/unit-tests/test-cases/read-only-relocs/test_bind.c new file mode 100644 index 0000000..8ec6b8d --- /dev/null +++ b/ld64/unit-tests/test-cases/read-only-relocs/test_bind.c @@ -0,0 +1,10 @@ + +extern int b; +extern void func(); + +int test_bind() +{ + func(); + return b; +} + diff --git a/ld64/unit-tests/test-cases/read-only-relocs/test_rebase.c b/ld64/unit-tests/test-cases/read-only-relocs/test_rebase.c new file mode 100644 index 0000000..1ddf43c --- /dev/null +++ b/ld64/unit-tests/test-cases/read-only-relocs/test_rebase.c @@ -0,0 +1,8 @@ + +int a=0; + +int test_rebase() +{ + return a; +} + diff --git a/ld64/unit-tests/test-cases/rebase-basic/Makefile b/ld64/unit-tests/test-cases/rebase-basic/Makefile index 9373aa3..cc7407b 100644 --- a/ld64/unit-tests/test-cases/rebase-basic/Makefile +++ b/ld64/unit-tests/test-cases/rebase-basic/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006-2008 Apple Inc. All rights reserved. +# Copyright (c) 2006-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,10 +32,10 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -c foo.c -o foo.${ARCH}.o + ${CC} ${CCFLAGS} -c -g foo.c -o foo.${ARCH}.o ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o - ${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o + ${CC} ${CCFLAGS} -c -g bar.m -o bar.${ARCH}.o ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -Wl,-no_order_data -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/Makefile b/ld64/unit-tests/test-cases/reexport_symbols_list/Makefile new file mode 100644 index 0000000..abc560c --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/Makefile @@ -0,0 +1,61 @@ +## +# 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 + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Test -reexport_symbols_list +# + +run: all + +all: + # build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + + # verify re-export fails with -bundle + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -bundle foo.c -o libfoo.bundle libbar.dylib -Wl,-reexported_symbols_list,bart.exp 2>/dev/null + + # verify failure if re-exported symbol does not exist + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -Wl,-reexported_symbols_list,junk.exp 2>/dev/null + + # verify failure if re-exported symbol is from .o file + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -Wl,-reexported_symbols_list,foo.exp 2>/dev/null + + # build library the re-exports _bart from base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -Wl,-reexported_symbols_list,bart.exp + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${DYLDINFO} -export libfoo.dylib | grep _barb | ${FAIL_IF_STDIN} + ${DYLDINFO} -export libfoo.dylib | grep _bart | grep 're-export' | ${FAIL_IF_EMPTY} + + # link against dylib and verify _bart is marked as coming from libfoo + ${CC} ${CCFLAGS} main1.c libfoo.dylib -o main1 + ${DYLDINFO} -bind -lazy_bind main1 | grep _bart | grep libfoo | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf libbar.dylib libfoo.dylib main1 diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/bar.c b/ld64/unit-tests/test-cases/reexport_symbols_list/bar.c new file mode 100644 index 0000000..b0f6fc9 --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/bar.c @@ -0,0 +1,10 @@ + +int barb(void) +{ + return 1; +} + +int bart(void) +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/bart.exp b/ld64/unit-tests/test-cases/reexport_symbols_list/bart.exp new file mode 100644 index 0000000..da8ec30 --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/bart.exp @@ -0,0 +1 @@ +_bart diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/foo.c b/ld64/unit-tests/test-cases/reexport_symbols_list/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/foo.exp b/ld64/unit-tests/test-cases/reexport_symbols_list/foo.exp new file mode 100644 index 0000000..70eefe9 --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/foo.exp @@ -0,0 +1 @@ +_foo diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/junk.exp b/ld64/unit-tests/test-cases/reexport_symbols_list/junk.exp new file mode 100644 index 0000000..8b925f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/junk.exp @@ -0,0 +1 @@ +_junk diff --git a/ld64/unit-tests/test-cases/reexport_symbols_list/main1.c b/ld64/unit-tests/test-cases/reexport_symbols_list/main1.c new file mode 100644 index 0000000..4655cb6 --- /dev/null +++ b/ld64/unit-tests/test-cases/reexport_symbols_list/main1.c @@ -0,0 +1,9 @@ +extern int foo(); +extern int bart(); + +int main() +{ + foo(); + bart(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s index 4d38f2d..86716da 100644 --- a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -82,6 +82,9 @@ _test_branches: @ call internal + addend bne _test_calls+16 + @ call internal - addend + bne _test_calls-16 + @ call external bne _external @@ -92,6 +95,35 @@ _test_branches: bl 1f 1: nop + + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + + .globl _test_hidden_weak + .private_extern _test_hidden_weak + .weak_definition _test_hidden_weak +_test_hidden_weak: + nop + nop + + +_test_weak_call: + bl _test_weak + bl _test_weak+4 + + +_test_weak_hidden_pointer_call: + ldr r12,L3 + add r12, pc, r12 + nop + bx r12 +L101: + .long _test_hidden_weak - L101 + + .text _pointer_diffs: .long _foo-1b @@ -101,6 +133,7 @@ _pointer_diffs: .long (_test_branches - _test_loads) + -2097152 .long (_test_calls - _test_loads) + -2097152 + .text .code 32 _arm1: @@ -178,8 +211,8 @@ L15:add r3, pc .align 2 L16: .long _thumb1-(L12+8) L17: .long _thumb2-(L13+8) -L17a: .long _thumb3-(L3+8) -L17b: .long _thumb4-(L3+8) +L17a: .long _thumb3-(L13+8) +L17b: .long _thumb4-(L13+8) L18: .long _arm1-(L14+8) L19: .long _arm2-(L15+8) L19a: .long _arm3-(L15+8) @@ -192,6 +225,81 @@ _myVTable: .long _thumb3 .long _arm1 .long _arm2 + +#if __ARM_ARCH_7A__ + .text + .align 2 +_arm16tests: + movw r0, :lower16:_datahilo16 + movt r0, :upper16:_datahilo16 + movw r0, :lower16:_datahilo16+4 + movt r0, :upper16:_datahilo16+4 + movw r0, :lower16:_datahilo16alt + movt r0, :upper16:_datahilo16alt + movw r0, :lower16:_datahilo16alt+61440 + movt r0, :upper16:_datahilo16alt+61440 + movw r0, :lower16:_datahilo16alt+2048 + movt r0, :upper16:_datahilo16alt+2048 + movw r0, :lower16:_datahilo16alt+1792 + movt r0, :upper16:_datahilo16alt+1792 + movw r0, :lower16:_datahilo16alt+165 + movt r0, :upper16:_datahilo16alt+165 +Lpicbase: + movw r0, :lower16:_datahilo16 - Lpicbase + movt r0, :upper16:_datahilo16 - Lpicbase + movw r0, :lower16:_datahilo16+4 - Lpicbase + movt r0, :upper16:_datahilo16+4 - Lpicbase + movw r0, :lower16:_datahilo16alt - Lpicbase + movt r0, :upper16:_datahilo16alt - Lpicbase + movw r0, :lower16:_datahilo16alt+61440 - Lpicbase + movt r0, :upper16:_datahilo16alt+61440 - Lpicbase + movw r0, :lower16:_datahilo16alt+2048 - Lpicbase + movt r0, :upper16:_datahilo16alt+2048 - Lpicbase + movw r0, :lower16:_datahilo16alt+1792 - Lpicbase + movt r0, :upper16:_datahilo16alt+1792 - Lpicbase + movw r0, :lower16:_datahilo16alt+165 - Lpicbase + movt r0, :upper16:_datahilo16alt+165 - Lpicbase + bx lr + + .code 16 + .thumb_func _thumb16tests +_thumb16tests: + movw r0, :lower16:_datahilo16 + movt r0, :upper16:_datahilo16 + movw r0, :lower16:_datahilo16+4 + movt r0, :upper16:_datahilo16+4 + movw r0, :lower16:_datahilo16alt + movt r0, :upper16:_datahilo16alt + movw r0, :lower16:_datahilo16alt+61440 + movt r0, :upper16:_datahilo16alt+61440 + movw r0, :lower16:_datahilo16alt+2048 + movt r0, :upper16:_datahilo16alt+2048 + movw r0, :lower16:_datahilo16alt+1792 + movt r0, :upper16:_datahilo16alt+1792 + movw r0, :lower16:_datahilo16alt+165 + movt r0, :upper16:_datahilo16alt+165 +Lpicbase2: + movw r0, :lower16:_datahilo16 - Lpicbase2 + movt r0, :upper16:_datahilo16 - Lpicbase2 + movw r0, :lower16:_datahilo16+4 - Lpicbase2 + movt r0, :upper16:_datahilo16+4 - Lpicbase2 + movw r0, :lower16:_datahilo16alt - Lpicbase2 + movt r0, :upper16:_datahilo16alt - Lpicbase2 + movw r0, :lower16:_datahilo16alt+61440 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+61440 - Lpicbase2 + movw r0, :lower16:_datahilo16alt+2048 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+2048 - Lpicbase2 + movw r0, :lower16:_datahilo16alt+1792 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+1792 - Lpicbase2 + movw r0, :lower16:_datahilo16alt+165 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+165 - Lpicbase2 + bx lr + + .data +_datahilo16: .long 0 +_datahilo16alt: .long 0 + +#endif #endif @@ -309,6 +417,17 @@ _test_branches: ; call external + addend bne _external+16 + + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + +_test_weak_call: + bl _test_weak + bl _test_weak+4 + #endif @@ -316,7 +435,12 @@ _test_branches: #if __i386__ .text .align 2 - + +Ltest_data: + .long 1 + .long 2 + .long 3 + .globl _test_loads _test_loads: pushl %ebp @@ -347,6 +471,10 @@ Lpicbase: # absolute lea of external + addend leal _ax+0x1900, %eax + # absolute load of _test_data with negative addend and local label + movl Ltest_data-16(%edi),%eax + movq Ltest_data-16(%edi),%mm4 + ret @@ -406,6 +534,16 @@ c_2: sub $(1), %ecx jcxz c_2 + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + +_test_weak_call: + call _test_weak + call _test_weak+1 + #endif @@ -483,19 +621,30 @@ _test_branches: jne _external+16 _byte_relocs: + # nonsense loop that creates byte branch relocation mov $100, %ecx -c_1: +c_1: loop _byte_relocs nop + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + +_test_weak_call: + call _test_weak + call _test_weak+1 + #endif # test that pointer-diff relocs are preserved .text -_test_diffs: .align 2 +_test_diffs: Llocal2: .long 0 .long Llocal2-_test_branches @@ -511,6 +660,7 @@ Llocal2: #endif _foo: nop +Lfoo: nop .align 2 _distance_from_foo: @@ -528,6 +678,10 @@ _distance_to_here: .long _foo - _distance_to_here .long _foo - _distance_to_here - 4 .long _foo - _distance_to_here - 12 + .long Lfoo - _distance_to_here +Ltohere: + .long Lfoo - Ltohere + .long Lfoo - Ltohere - 4 .long 0 @@ -545,6 +699,7 @@ L1: .quad _test_branches - _test_diffs .quad _test_branches - . .quad _test_branches - L1 .quad L1 - _prev + .quad _prev+100 - _test_branches #tests support for 32-bit absolute pointers .long _prev .long L1 @@ -570,11 +725,15 @@ _b: .long _test_calls+16 .long _external .long _external+16 + .long _test_weak + .long _test_weak+16 #elif __ppc64__ || __x86_64__ .quad _test_calls .quad _test_calls+16 .quad _external .quad _external+16 + .quad _test_weak + .quad _test_weak+16 #endif # test that reloc sizes are the same diff --git a/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile index 36fb47b..d484458 100644 --- a/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile +++ b/ld64/unit-tests/test-cases/relocs-neg-from-local/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Inc. All rights reserved. +# Copyright (c) 2009-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -41,10 +41,10 @@ run-other: run-i386: ${CC} ${ASMFLAGS} test.s -c -o test.o - ${OBJECTDUMP} test.o | grep "__data@0 plus 0xFFFFFFE2" | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} test.o | grep "direct(anon@__data+0x00000000) + 0xFFFFFFFFFFFFFFE2" | ${FAIL_IF_EMPTY} ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o - ${OBJECTDUMP} test-r.o | grep "__data@0 plus 0xFFFFFFE2" | ${PASS_IFF_STDIN} + ${OBJECTDUMP} test-r.o | grep "direct(anon@__data+0x00000000) + 0xFFFFFFFFFFFFFFE2" | ${PASS_IFF_STDIN} clean: diff --git a/ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile b/ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile new file mode 100644 index 0000000..5b1f246 --- /dev/null +++ b/ld64/unit-tests/test-cases/sectcreate-dead_strip/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 + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -sectcreate __MYSEG __mysect sect_content -dead_strip + size -l main | grep __MYSEG | ${FAIL_IF_EMPTY} + size -l main | grep __mysect | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main diff --git a/ld64/unit-tests/test-cases/sectcreate-dead_strip/main.c b/ld64/unit-tests/test-cases/sectcreate-dead_strip/main.c new file mode 100644 index 0000000..fc047f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/sectcreate-dead_strip/main.c @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/sectcreate-dead_strip/sect_content b/ld64/unit-tests/test-cases/sectcreate-dead_strip/sect_content new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/ld64/unit-tests/test-cases/sectcreate-dead_strip/sect_content @@ -0,0 +1 @@ +hello diff --git a/ld64/unit-tests/test-cases/segment-labels/Makefile b/ld64/unit-tests/test-cases/segment-labels/Makefile new file mode 100644 index 0000000..2a4a95b --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-labels/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2009-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 ld resolves the magic segment start/end symbols. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib + ${CC} ${CCFLAGS} main.c -o main + ${CC} ${CCFLAGS} test.c -o test.preload -Wl,-preload -nostartfiles -nodefaultlibs -e _start + ${CC} ${CCFLAGS} test.c -o test.preload-pie -Wl,-preload -Wl,-pie -nostartfiles -nodefaultlibs -e _start + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libmain.dylib main test.preload test.preload-pie diff --git a/ld64/unit-tests/test-cases/segment-labels/main.c b/ld64/unit-tests/test-cases/segment-labels/main.c new file mode 100644 index 0000000..441860f --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-labels/main.c @@ -0,0 +1,50 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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@ + */ +#include + + +extern void* data_start __asm("segment$start$__DATA"); +extern void* data_end __asm("segment$end$__DATA"); +extern void* text_start __asm("segment$start$__TEXT"); +extern void* text_end __asm("segment$end$__TEXT"); +extern void* other_start __asm("segment$start$__OTHER"); +extern void* other_end __asm("segment$end$__OTHER"); + + +int other[100] __attribute__ ((section ("__OTHER,__my"))) = { 1, 2 }; + +int mytent[1000]; +static int mybss[1000]; + +int main() +{ + mytent[0] = 0; + mybss[0] = 0; + printf("text %p -> %p\n", &text_start, &text_end); + printf("data %p -> %p\n", &data_start, &data_end); + printf("other %p -> %p\n", &other_start, &other_end); + return 0; +} + + diff --git a/ld64/unit-tests/test-cases/segment-labels/test.c b/ld64/unit-tests/test-cases/segment-labels/test.c new file mode 100644 index 0000000..247b989 --- /dev/null +++ b/ld64/unit-tests/test-cases/segment-labels/test.c @@ -0,0 +1,12 @@ +extern char text_start[] __asm("segment$start$__TEXT"); +extern char text_end[] __asm("segment$end$__TEXT"); +extern char text_text_start[] __asm("section$start$__TEXT$__text"); + +void* a = &text_start; +void* b = &text_end; +void* c = &text_text_start; + +void start(void) +{ +} + diff --git a/ld64/unit-tests/test-cases/stabs-coalesce/Makefile b/ld64/unit-tests/test-cases/stabs-coalesce/Makefile index 6e11c59..806f406 100644 --- a/ld64/unit-tests/test-cases/stabs-coalesce/Makefile +++ b/ld64/unit-tests/test-cases/stabs-coalesce/Makefile @@ -34,18 +34,18 @@ include ${TESTROOT}/include/common.makefile run: all all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} + g++-4.2 ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH} nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count echo " 1" > one ${PASS_IFF} diff stabs-hello-foo-count one hello.o : hello.cxx - ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ + g++-4.2 ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ ${FAIL_IF_BAD_OBJ} $@ other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ + g++-4.2 ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ ${FAIL_IF_BAD_OBJ} $@ clean: diff --git a/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile b/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile index 05588b5..4e61937 100644 --- a/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile +++ b/ld64/unit-tests/test-cases/static-executable-weak-defines/Makefile @@ -29,7 +29,8 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker + strip -S test -o test.stripped ${PASS_IFF_GOOD_MACHO} test clean: - rm -rf test + rm -rf test test.stripped diff --git a/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c b/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c index 3e03861..687cb3d 100644 --- a/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c +++ b/ld64/unit-tests/test-cases/static-executable-weak-defines/test.c @@ -11,3 +11,6 @@ int entry() { return foo(); } + +// pointer to weak function might trigger external relocation +void* pfoo = &foo; diff --git a/ld64/unit-tests/test-cases/static-executable/test.c b/ld64/unit-tests/test-cases/static-executable/test.c index 27fe88d..57b349a 100644 --- a/ld64/unit-tests/test-cases/static-executable/test.c +++ b/ld64/unit-tests/test-cases/static-executable/test.c @@ -1,7 +1,10 @@ +int a; +int b = 5; + int foo() { - return 0; + return a+b; } @@ -9,3 +12,5 @@ int entry() { return foo(); } + + diff --git a/ld64/unit-tests/test-cases/symbol-hiding-umbrella/Makefile b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/Makefile new file mode 100644 index 0000000..3fc3aa0 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/Makefile @@ -0,0 +1,48 @@ +## +# 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 + +# +# Test magic $ld$hide works with public sub library +# RedGarnet's linker does not honor $ld$hide for umbrella libraries +# + + +run: all + +all: + # In this test case aaa and bbb both moved between libfoo and libar + # between 10.4 and 10.5. + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -Wl,-reexport_library,libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar_alt.dylib + ${CC} ${CCFLAGS} main.c -o main-10.5 libfoo.dylib -L. -mmacosx-version-min=10.5 + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main-10.6 libfoo.dylib -L. -mmacosx-version-min=10.6 2>/dev/null + ${CC} ${CCFLAGS} main.c -o main-10.7 libfoo.dylib -L. -mmacosx-version-min=10.7 + ${PASS_IFF_GOOD_MACHO} main-10.7 + + +clean: + + rm -rf libbar.dylib libfoo.dylib main-10.5 main-10.6 main-10.7 diff --git a/ld64/unit-tests/test-cases/symbol-hiding-umbrella/bar.c b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/unit-tests/test-cases/symbol-hiding-umbrella/foo.c b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/foo.c new file mode 100644 index 0000000..d946cb4 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/foo.c @@ -0,0 +1,7 @@ + +void foo() {} + +#define SYMBOL_NOT_HERE_IN_10_6(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.6$_" #sym ); const char sym##_tmp = 0; + +SYMBOL_NOT_HERE_IN_10_6(bar) diff --git a/ld64/unit-tests/test-cases/symbol-hiding-umbrella/main.c b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/main.c new file mode 100644 index 0000000..8c9c95c --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-hiding-umbrella/main.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); + + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/symbol-resolver-basic/Makefile b/ld64/unit-tests/test-cases/symbol-resolver-basic/Makefile new file mode 100644 index 0000000..15cb555 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-resolver-basic/Makefile @@ -0,0 +1,29 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test a simple symbol resolver function +# +TARGET = all +ifeq (${ARCH},ppc) + TARGET = all-ppc +endif + + +run: ${TARGET} + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} foo.o -dynamiclib -o libfoo.dylib + ${DYLDINFO} -export libfoo.dylib | grep _foo | grep resolver | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r foo.o -o foo-r.o + ${CC} ${CCFLAGS} foo-r.o -dynamiclib -o libfoo-r.dylib + ${DYLDINFO} -export libfoo-r.dylib | grep _foo | grep resolver | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo-r.dylib + +all-ppc: + echo "PASS" + +clean: + rm -f foo.o libfoo.dylib foo-r.o libfoo-r.dylib diff --git a/ld64/unit-tests/test-cases/symbol-resolver-basic/foo.c b/ld64/unit-tests/test-cases/symbol-resolver-basic/foo.c new file mode 100644 index 0000000..c6a39d9 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-resolver-basic/foo.c @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + + + +int foo_real() +{ + return 10; +} + +// This foo is a "resolver" function that return the actual address of "foo" +void* foo() +{ + __asm__(".desc _foo, 0x100"); + return &foo_real; +} + diff --git a/ld64/unit-tests/test-cases/symbol-resolver-hidden/Makefile b/ld64/unit-tests/test-cases/symbol-resolver-hidden/Makefile new file mode 100644 index 0000000..754dad4 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-resolver-hidden/Makefile @@ -0,0 +1,25 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test a simple symbol resolver function +# +TARGET = all +ifeq (${ARCH},ppc) + TARGET = all-ppc +endif + + +run: ${TARGET} + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-w + ${DYLDINFO} -lazy_bind libfoo.dylib | grep _a | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +all-ppc: + echo "PASS" + +clean: + rm -f libfoo.dylib diff --git a/ld64/unit-tests/test-cases/symbol-resolver-hidden/foo.c b/ld64/unit-tests/test-cases/symbol-resolver-hidden/foo.c new file mode 100644 index 0000000..c8e29e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-resolver-hidden/foo.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +#include + +extern void a(); + +void b() { + a(); +} + +static void a_impl() { + printf("Hello World!\n"); +} + +void *a_chooser() __asm__("_a") __attribute__((visibility("hidden"), noinline)); +void *a_chooser() { + __asm__(".symbol_resolver _a"); + return a_impl; +} + diff --git a/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile b/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile index df0ae07..6a7d3a6 100644 --- a/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile +++ b/ld64/unit-tests/test-cases/tentative-to-real-r/Makefile @@ -32,10 +32,10 @@ run: all all: ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o - ${OBJECTDUMP} -no_content test.${ARCH}.o | grep -v kind: | grep -v section: > test.${ARCH}.o.dump + ${OBJECTDUMP} -no_content -no_definition -no_section -no_combine test.${ARCH}.o > test.${ARCH}.o.dump ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o - ${OBJECTDUMP} -no_content test-r-d.${ARCH}.o | grep -v kind: | grep -v section: > test-r-d.${ARCH}.o.dump + ${OBJECTDUMP} -no_content -no_definition -no_section -no_combine test-r-d.${ARCH}.o > test-r-d.${ARCH}.o.dump ${PASS_IFF} diff test.${ARCH}.o.dump test-r-d.${ARCH}.o.dump diff --git a/ld64/unit-tests/test-cases/tlv-basic/Makefile b/ld64/unit-tests/test-cases/tlv-basic/Makefile new file mode 100644 index 0000000..f5be591 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-basic/Makefile @@ -0,0 +1,39 @@ +## +# 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 main executable can use TLVs +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c get.s -o main + 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/ld64/unit-tests/test-cases/tlv-basic/get.s b/ld64/unit-tests/test-cases/tlv-basic/get.s new file mode 100644 index 0000000..9255841 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-basic/get.s @@ -0,0 +1,162 @@ + + # _a is zerofill global TLV + .tbss _a$tlv$init,4,2 + + # _b is an initialized global TLV + .tdata +_b$tlv$init: + .long 5 + + # _c is zerofill non-external TLV + .tbss _c$tlv$init,4,2 + + # _d is an initialized non-external TLV + .tdata +_d$tlv$init: + .long 5 + +#if __x86_64__ + + # _a is global TLV + .tlv + .globl _a +_a: .quad __tlv_bootstrap + .quad 0 + .quad _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .quad __tlv_bootstrap + .quad 0 + .quad _b$tlv$init + + # _c is a non-external TLV + .tlv +_c: .quad __tlv_bootstrap + .quad 0 + .quad _c$tlv$init + + # _d is a non-external TLV + .tlv +_d: .quad __tlv_bootstrap + .quad 0 + .quad _d$tlv$init + + + .text + .globl _get_a +_get_a: + pushq %rbp + movq %rsp, %rbp + movq _a@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_b +_get_b: + pushq %rbp + movq %rsp, %rbp + movq _b@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_c +_get_c: + pushq %rbp + movq %rsp, %rbp + movq _c@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_d +_get_d: + pushq %rbp + movq %rsp, %rbp + movq _d@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + +#endif + +#if __i386__ + + # _a is global TLV + .tlv + .globl _a +_a: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .long __tlv_bootstrap + .long 0 + .long _b$tlv$init + + # _c is a non-external TLV + .tlv +_c: .long __tlv_bootstrap + .long 0 + .long _c$tlv$init + + # _d is a non-external TLV + .tlv +_d: .long __tlv_bootstrap + .long 0 + .long _d$tlv$init + + + .text + .globl _get_a +_get_a: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _a@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_b +_get_b: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _b@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_c +_get_c: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _c@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_d +_get_d: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _d@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + +#endif + +.subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-basic/main.c b/ld64/unit-tests/test-cases/tlv-basic/main.c new file mode 100644 index 0000000..fc56a13 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-basic/main.c @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +// work around until compiler supports __thread +extern int* get_a(); +extern int* get_b(); +extern int* get_c(); +extern int* get_d(); + +int main() +{ + get_a(); + get_b(); + get_c(); + get_d(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/tlv-dylib/Makefile b/ld64/unit-tests/test-cases/tlv-dylib/Makefile new file mode 100644 index 0000000..5017692 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-dylib/Makefile @@ -0,0 +1,46 @@ +## +# 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 + + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Check that dylibs can export TLVs +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.s -dynamiclib -o libfoo.dylib + # check trying to access TLV _foo as regular variable is an error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DUSE_FOO libfoo.dylib -o main 2>/dev/null + # check trying to access regular variable _bar as TLV is an error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c getbar.s libfoo.dylib -o main 2>/dev/null + # check can link with TLV _foo in dylib + ${CC} ${CCFLAGS} main.c getfoo.s libfoo.dylib -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf libfoo.dylib main diff --git a/ld64/unit-tests/test-cases/tlv-dylib/foo.s b/ld64/unit-tests/test-cases/tlv-dylib/foo.s new file mode 100644 index 0000000..fd2f23d --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-dylib/foo.s @@ -0,0 +1,33 @@ + +// _foo is an exported thread local variable +// _bar is an exported regular variable + + # _a is zerofill global TLV + .tbss _a$tlv$init,4,2 + +#if __x86_64__ + .tlv + .globl _foo +_foo: .quad __tlv_bootstrap + .quad 0 + .quad _a$tlv$init + + +#endif + +#if __i386__ + .tlv + .globl _foo +_foo: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init +#endif + + + .data + .globl _bar +_bar: .long 0 + + + + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-dylib/getbar.s b/ld64/unit-tests/test-cases/tlv-dylib/getbar.s new file mode 100644 index 0000000..33b0418 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-dylib/getbar.s @@ -0,0 +1,29 @@ + +#if __x86_64__ + .text + .globl _get_bar +_get_bar: + pushq %rbp + movq %rsp, %rbp + movq _bar@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret +#endif + + +#if __i386__ + .text + .globl _get_bar +_get_bar: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _bar@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret +#endif + +.subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-dylib/getfoo.s b/ld64/unit-tests/test-cases/tlv-dylib/getfoo.s new file mode 100644 index 0000000..21db32c --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-dylib/getfoo.s @@ -0,0 +1,29 @@ + +#if __x86_64__ + .text + .globl _get_foo +_get_foo: + pushq %rbp + movq %rsp, %rbp + movq _foo@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret +#endif + + +#if __i386__ + .text + .globl _get_foo +_get_foo: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _foo@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret +#endif + +.subsections_via_symbols diff --git a/ld64/src/ld/SectCreate.h b/ld64/unit-tests/test-cases/tlv-dylib/main.c similarity index 74% rename from ld64/src/ld/SectCreate.h rename to ld64/unit-tests/test-cases/tlv-dylib/main.c index d5c7f4e..d5ec3b8 100644 --- a/ld64/src/ld/SectCreate.h +++ b/ld64/unit-tests/test-cases/tlv-dylib/main.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,22 +22,15 @@ * @APPLE_LICENSE_HEADER_END@ */ - -#ifndef __SECTCREATE__ -#define __SECTCREATE__ - - -#include "ObjectFile.h" - -namespace SectCreate { - - extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); - -}; - - +#if USE_FOO + extern int foo; #endif - - +int main() +{ +#if USE_FOO + foo = 1; +#endif + return 0; +} diff --git a/ld64/unit-tests/test-cases/umbrella-dylib/Makefile b/ld64/unit-tests/test-cases/umbrella-dylib/Makefile new file mode 100644 index 0000000..b20f368 --- /dev/null +++ b/ld64/unit-tests/test-cases/umbrella-dylib/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +# +# Test out building an umbrella dylib. a and c are co-dependent. b depends on c. +# + + +run: all + +all: + # build bootstrap dylib of with all code + ${CC} ${CCFLAGS} -dynamiclib a.c b.c c.c -o libBig.stub -install_name /usr/lib/libBig.dylib + # link each sub library against bootstrap to intra-Big symbols + ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libBig.stub -install_name /usr/local/lib/big/liba.dylib -umbrella Big + ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libBig.stub -install_name /usr/local/lib/big/libb.dylib -umbrella Big + ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib libBig.stub -install_name /usr/local/lib/big/libc.dylib -umbrella Big + ${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} + ${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} + ${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 + + +clean: + + rm -rf libBig.stub liba.dylib libb.dylib libc.dylib libBig.dylib main diff --git a/ld64/unit-tests/test-cases/umbrella-dylib/a.c b/ld64/unit-tests/test-cases/umbrella-dylib/a.c new file mode 100644 index 0000000..83ff3fc --- /dev/null +++ b/ld64/unit-tests/test-cases/umbrella-dylib/a.c @@ -0,0 +1,11 @@ + +extern void c2(); + +void a1(void) +{ + c2(); +} + +void a2(void) +{ +} diff --git a/ld64/unit-tests/test-cases/umbrella-dylib/b.c b/ld64/unit-tests/test-cases/umbrella-dylib/b.c new file mode 100644 index 0000000..cdb9dfa --- /dev/null +++ b/ld64/unit-tests/test-cases/umbrella-dylib/b.c @@ -0,0 +1,7 @@ + +extern void c1(); + +void b1(void) +{ + c1(); +} diff --git a/ld64/unit-tests/test-cases/umbrella-dylib/c.c b/ld64/unit-tests/test-cases/umbrella-dylib/c.c new file mode 100644 index 0000000..01747b6 --- /dev/null +++ b/ld64/unit-tests/test-cases/umbrella-dylib/c.c @@ -0,0 +1,11 @@ + +extern void a2(); + +void c1(void) +{ + a2(); +} + +void c2(void) +{ +} diff --git a/ld64/unit-tests/test-cases/umbrella-dylib/main.c b/ld64/unit-tests/test-cases/umbrella-dylib/main.c new file mode 100644 index 0000000..8d0b5b7 --- /dev/null +++ b/ld64/unit-tests/test-cases/umbrella-dylib/main.c @@ -0,0 +1,8 @@ +extern void c1(); +extern void a1(); +int main() +{ + a1(); + c1(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile index eebcc37..69b40c7 100644 --- a/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile +++ b/ld64/unit-tests/test-cases/undefined-dynamic-lookup/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,18 +30,31 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup + ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} main ${CC} ${CCFLAGS} main.c -o main -Wl,-U,_foo nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} main ${CC} ${CCFLAGS} main.c -o main -flat_namespace -Wl,-U,_foo nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main + ${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} main.c -bundle -o main.bundle -nodefaultlibs -undefined dynamic_lookup + nm -m main.bundle | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main.bundle | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main.bundle | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main.bundle + clean: - rm main + rm -f main main.bundle diff --git a/ld64/unit-tests/test-cases/unstrippable-symbols/Makefile b/ld64/unit-tests/test-cases/unstrippable-symbols/Makefile new file mode 100644 index 0000000..39937ba --- /dev/null +++ b/ld64/unit-tests/test-cases/unstrippable-symbols/Makefile @@ -0,0 +1,17 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a dynamically referenced symbol is always exported +# + +run: all + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _keep_global | grep "referenced dynamically" | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _keep_hidden | grep "referenced dynamically" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/ld64/unit-tests/test-cases/unstrippable-symbols/foo.c b/ld64/unit-tests/test-cases/unstrippable-symbols/foo.c new file mode 100644 index 0000000..140f8c6 --- /dev/null +++ b/ld64/unit-tests/test-cases/unstrippable-symbols/foo.c @@ -0,0 +1,24 @@ +#include + +int keep_global = 1; +asm(".desc _keep_global, 0x10"); + +__attribute__((visibility("hidden"))) int keep_hidden = 1; +asm(".desc _keep_hidden, 0x10"); + +static int keep_static = 1; +asm(".desc _keep_static, 0x10"); + + +int lose_global = 1; + +__attribute__((visibility("hidden"))) int lose_hidden = 1; + +static int lose_static = 1; + + + +int get() +{ + return keep_global + keep_hidden + keep_static + lose_global + lose_hidden + lose_static; +} diff --git a/ld64/unit-tests/test-cases/utf16-nul/Makefile b/ld64/unit-tests/test-cases/utf16-nul/Makefile new file mode 100644 index 0000000..3d845e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/utf16-nul/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2008-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 + +# +# Test that utf16 cfstring literals are coalesced. +# There is 3 CFSTR in foo.m and 1 CFSTR in bar.m +# After coalescing and dead stripping there should be only two CFSTR in the output +# + + +all: + ${CC} ${CCFLAGS} withnul.s -c -o withnul.o + ${CC} ${CCFLAGS} other.s -c -o other.o + ${LD} -r -arch ${ARCH} withnul.o other.o -o all.o + size -l all.o | grep "__ustring): 44" | ${PASS_IFF_STDIN} + + +clean: + rm -rf withnul.o other.o all.o diff --git a/ld64/unit-tests/test-cases/utf16-nul/other.s b/ld64/unit-tests/test-cases/utf16-nul/other.s new file mode 100644 index 0000000..f78d014 --- /dev/null +++ b/ld64/unit-tests/test-cases/utf16-nul/other.s @@ -0,0 +1,12 @@ + + + .section __TEXT,__ustring + .align 1 +___utf16_string_3: + .short 0x00fc, 0x0062, 0x0065, 0x0072, 0x0000 + +___utf16_string_4: + .short 0x0073, 0x0074, 0x00fc, 0x0066, 0x0066, 0x0000 + + + diff --git a/ld64/unit-tests/test-cases/utf16-nul/withnul.s b/ld64/unit-tests/test-cases/utf16-nul/withnul.s new file mode 100644 index 0000000..a1bc7ce --- /dev/null +++ b/ld64/unit-tests/test-cases/utf16-nul/withnul.s @@ -0,0 +1,8 @@ + + + .section __TEXT,__ustring + .align 1 +___utf16_string_1: + .short 0x00fc, 0x0062, 0x0065, 0x0072, 0x0000, 0x0073, 0x0074, 0x00fc, 0x0066, 0x0066, 0x0000 + + diff --git a/ld64/unit-tests/test-cases/visibility-warning/Makefile b/ld64/unit-tests/test-cases/visibility-warning/Makefile index 43bdcd8..64b0363 100644 --- a/ld64/unit-tests/test-cases/visibility-warning/Makefile +++ b/ld64/unit-tests/test-cases/visibility-warning/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -39,18 +39,24 @@ all: ${CC} ${CCFLAGS} -c foo_weak.c -o foo_weak.o ${CC} ${CCFLAGS} -c foo.c -o foo.o ${CC} ${CCFLAGS} -c foo_hidden.c -o foo_hidden.o - # weak default and weak hidden should warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_EMPTY} - # weak hidden and strong should not warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_STDIN} - # weak default and strong hidden should not warn - ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_STDIN} - # weak default and weak hidden but -w should not warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -w -o libfoo.dylib 2> warnings.log - cat warnings.log | ${FAIL_IF_STDIN} + # weak default and weak hidden should pick default + ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_STDIN} + # weak default and weak hidden should pick default + ${CC} ${CCFLAGS} foo_weak.o foo_weak_hidden.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_STDIN} + # weak hidden and strong should not warn and pick strong + ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_STDIN} + # weak default and strong hidden should not warn and pick hidden + ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_EMPTY} + # weak default and strong hidden should not warn and pick hidden + ${CC} ${CCFLAGS} foo_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} libfoo.dylib clean: diff --git a/ld64/unit-tests/test-cases/weak-def-auto-hide/Makefile b/ld64/unit-tests/test-cases/weak-def-auto-hide/Makefile new file mode 100644 index 0000000..7b74b81 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-auto-hide/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2010Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Tests weak-external and weak-can-be-hidden symbols work. +# + +run: all + +all: + # test that _my_other_weak is hidden + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} other.s -c -o other.o + ${CC} ${CCFLAGS} main.o other.o -o main + nm -m main | grep _my_weak | grep "weak external" | ${FAIL_IF_EMPTY} + nm -m main | grep _my_other_weak | grep "non-external" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + # test that .exp file can override auto-hide + ${CC} ${CCFLAGS} main.o other.o -o main2 -Wl,-exported_symbol,_my_other_weak + nm -m main2 | grep _my_weak | grep "non-external" | ${FAIL_IF_EMPTY} + nm -m main2 | grep _my_other_weak | grep "weak external" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main2 + ${CC} ${CCFLAGS} main.o other.o -o main2 -Wl,-exported_symbol,_main + nm -m main2 | grep _my_weak | grep "non-external" | ${FAIL_IF_EMPTY} + nm -m main2 | grep _my_other_weak | grep "non-external" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main2 + # test that auto-hide bit survives ld -r + ${LD} -r -arch ${ARCH} other.o -o other-r.o + ${OBJECTDUMP} other.o > other.o.dump + ${OBJECTDUMP} other-r.o > other-r.o.dump + ${PASS_IFF} diff other.o.dump other-r.o.dump + +clean: + rm -f main.o other.o main main2 other-r.o other.o.dump other-r.o.dump diff --git a/ld64/unit-tests/test-cases/weak-def-auto-hide/main.c b/ld64/unit-tests/test-cases/weak-def-auto-hide/main.c new file mode 100644 index 0000000..dada6bd --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-auto-hide/main.c @@ -0,0 +1,15 @@ +#include + +void __attribute__((weak)) my_weak() +{ +} + +extern void my_other_weak(); + +int main() +{ + my_weak(); + my_other_weak(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak-def-auto-hide/other.s b/ld64/unit-tests/test-cases/weak-def-auto-hide/other.s new file mode 100644 index 0000000..5fd7ec4 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-auto-hide/other.s @@ -0,0 +1,15 @@ + + + .text + + .globl _my_weak + .weak_def_can_be_hidden _my_weak +_my_weak: nop + nop + + + .globl _my_other_weak + .weak_def_can_be_hidden _my_other_weak +_my_other_weak: nop + nop + diff --git a/ld64/unit-tests/test-cases/weak-def-flag/Makefile b/ld64/unit-tests/test-cases/weak-def-flag/Makefile index e813f49..a8d30a9 100644 --- a/ld64/unit-tests/test-cases/weak-def-flag/Makefile +++ b/ld64/unit-tests/test-cases/weak-def-flag/Makefile @@ -45,4 +45,4 @@ all: ${PASS_IFF_GOOD_MACHO} main clean: - rm main main-strip-weak \ No newline at end of file + rm -f main main-strip-weak \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/weak-force/Makefile b/ld64/unit-tests/test-cases/weak-force/Makefile new file mode 100644 index 0000000..57712a6 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-force/Makefile @@ -0,0 +1,50 @@ +## +# 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 + +# +# Test +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib -Wl,-force_symbols_weak_list foo1.exp foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + nm -m libfoo.dylib | grep _foo1 | grep weak | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _wildcheck | grep weak | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _foo3 | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _willnot | grep weak | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib -Wl,-force_symbols_not_weak_list foo2.exp foo.c -o libfoo.dylib + nm -m libfoo.dylib | grep _foo2 | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _patterncheck | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo4 | grep weak | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _patnot | grep weak | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + + + +clean: + rm -rf libfoo.dylib diff --git a/ld64/unit-tests/test-cases/weak-force/foo.c b/ld64/unit-tests/test-cases/weak-force/foo.c new file mode 100644 index 0000000..baa5755 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-force/foo.c @@ -0,0 +1,17 @@ + + +void foo1() {} +void foo3() {} + + +__attribute__((weak)) void foo2() {} +__attribute__((weak)) void foo4() {} + + +void wildcheck() {} +void willnot() {} + + + +__attribute__((weak)) void patterncheck() {} +__attribute__((weak)) void patnot() {} diff --git a/ld64/unit-tests/test-cases/weak-force/foo1.exp b/ld64/unit-tests/test-cases/weak-force/foo1.exp new file mode 100644 index 0000000..7268896 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-force/foo1.exp @@ -0,0 +1,2 @@ +_foo1 +_wild* diff --git a/ld64/unit-tests/test-cases/weak-force/foo2.exp b/ld64/unit-tests/test-cases/weak-force/foo2.exp new file mode 100644 index 0000000..1ad4c0d --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-force/foo2.exp @@ -0,0 +1,2 @@ +_foo2 +_pattern* diff --git a/ld64/unit-tests/test-cases/weak_import-force/main.c b/ld64/unit-tests/test-cases/weak_import-force/main.c index 3b9cdff..76c68ba 100644 --- a/ld64/unit-tests/test-cases/weak_import-force/main.c +++ b/ld64/unit-tests/test-cases/weak_import-force/main.c @@ -15,15 +15,18 @@ extern int bar_data2; int* pfoo = &foo_data1; int* pbar = &bar_data1; +void* pfoo1; +void* pbar1; int main (void) { // make non-lazy reference to foo1 and bar1 - if ( &foo1 == &bar1 ) { - // make lazy reference to foo2 and bar2 - foo2(); - bar2(); - } + pfoo1 = &foo1; + pbar1 = &bar1; + + // make lazy reference to foo2 and bar2 + foo2(); + bar2(); // make non-lazy reference to foo_data2 and bar_data2 return *pfoo + *pbar + foo_data2 + bar_data2; diff --git a/ld64/unit-tests/test-cases/weak_import-local/Makefile b/ld64/unit-tests/test-cases/weak_import-local/Makefile new file mode 100644 index 0000000..4d21410 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-local/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that setting weak_import on symbols in a linkage unit works +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c -o main + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} main.c foo.c -o main -mmacosx-version-min=10.4 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/ld64/unit-tests/test-cases/weak_import-local/foo.c b/ld64/unit-tests/test-cases/weak_import-local/foo.c new file mode 100644 index 0000000..6eabcc7 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-local/foo.c @@ -0,0 +1,7 @@ + + +#include "foo.h" + +void func2() {} +int data2 = 0; // weak_import initialized + diff --git a/ld64/unit-tests/test-cases/weak_import-local/foo.h b/ld64/unit-tests/test-cases/weak_import-local/foo.h new file mode 100644 index 0000000..1dd4e82 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-local/foo.h @@ -0,0 +1,6 @@ + + +extern void func2() __attribute__((weak_import)); + +extern int data2 __attribute__((weak_import)); + diff --git a/ld64/unit-tests/test-cases/weak_import-local/main.c b/ld64/unit-tests/test-cases/weak_import-local/main.c new file mode 100644 index 0000000..ce044ad --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-local/main.c @@ -0,0 +1,18 @@ +#include + +#include "foo.h" + +void* pf2 = &func2; +int* pd2 = &data2; + +int main (void) +{ + if ( &func2 != NULL ) + func2(); + + if ( &data2 != NULL ) + data2 = 1; + + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak_import/main.c b/ld64/unit-tests/test-cases/weak_import/main.c index 3266aed..a08ed86 100644 --- a/ld64/unit-tests/test-cases/weak_import/main.c +++ b/ld64/unit-tests/test-cases/weak_import/main.c @@ -1,3 +1,4 @@ +#include #include "foo.h" @@ -5,12 +6,14 @@ int* pdata5 = &data5; int* pdata6 = &data6; +void* pf3; int main (void) { // make non-lazy reference to func3 and func4 - if ( &func3 == &func4 ) { - // make lazy reference to func3 and func4 + pf3 = &func3; + if ( &func4 == NULL ) { + // make lazy reference to func1 and func2 func1(); func2(); } diff --git a/ld64/unit-tests/test-cases/zero-fill2/test.c b/ld64/unit-tests/test-cases/zero-fill2/test.c index 219cdc2..94bf64d 100644 --- a/ld64/unit-tests/test-cases/zero-fill2/test.c +++ b/ld64/unit-tests/test-cases/zero-fill2/test.c @@ -37,14 +37,17 @@ int bigarray2[2560]; int bigarray3[25600]; int bigarray4[256000]; int bigarray5[2560000]; +#ifndef __arm__ int bigarray6[256000000*BOOST]; +#endif static int staticbigarray1[256]; static int staticbigarray2[2560]; static int staticbigarray3[25600]; static int staticbigarray4[256000]; static int staticbigarray5[2560000]; +#ifndef __arm__ static int staticbigarray6[25600000*BOOST]; - +#endif int main() { staticbigarray1[10] = 4; @@ -52,7 +55,9 @@ int main() staticbigarray3[10] = 4; staticbigarray4[10] = 4; staticbigarray5[10] = 4; +#ifndef __arm__ staticbigarray6[10] = 4; +#endif return 0; } From e552f4d994879b62d1504cdc9a6fdb4fa226cc25 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 13 May 2015 23:37:03 +0100 Subject: [PATCH 09/48] 123.2.1 --- ld64/ChangeLog | 12 ++++++++++-- ld64/doc/man/man1/ld.1 | 8 ++++---- ld64/src/ld/Options.cpp | 6 ++++-- ld64/src/ld/OutputFile.cpp | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ld64/ChangeLog b/ld64/ChangeLog index bc918bb..e2d1d2c 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,4 +1,12 @@ +-------- tagged ld64-123.2.1 + +2010-03-07 Nick Kledzik + + enable i386 ASLR + +-------- tagged ld64-123.2 + 2010-12-10 Nick Kledzik Man page typo: "dysmutil" under object_path_lto @@ -71,7 +79,7 @@ 2010-11-01 Nick Kledzik - Durango is missing dof sections for armv7 slice + -------- tagged ld64-120.3 @@ -544,7 +552,7 @@ 2010-06-09 Nick Kledzik - Barolo: 'rebase' makes timestamps invalid/unreadable for GDB + 2010-06-09 Nick Kledzik diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index d4d1329..c1aa2bc 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -1,4 +1,4 @@ -.Dd Sept 2, 2010 +.Dd March 7, 2011 .Dt ld 1 .Os Darwin .Sh NAME @@ -331,9 +331,9 @@ This option is also called -dylib_current_version for compatibility. This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5 and later, the OS the OS will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some -security. When targeting Mac OS X 10.7 or later PIE is the default for x86_64 main executables. +security. When targeting Mac OS X 10.7 or later PIE is the default for main executables. .It Fl no_pie -Do not make a position independent executable (PIE). This is the default, except for x86_64 for 10.7 or later. +Do not make a position independent executable (PIE). This is the default, when targeting 10.6 and earlier. .It Fl pagezero_size Ar size By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence will cause a bus error if a NULL pointer is dereferenced. The argument @@ -516,7 +516,7 @@ This option causes the linker to instead produce traditional relocation informat .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 -behavior and allows instructions on any page to be run. +behavior and allows instructions on any page to be executed. .It Fl no_eh_labels Normally in -r mode, the linker produces .eh labels on all FDEs in the __eh_frame section. This option suppresses those labels. Those labels are not needed by the Mac OS X 10.6 diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 1de3379..c06e1f3 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -3454,8 +3454,10 @@ void Options::reconfigureDefaults() fCanUseUpwardDylib = true; // x86_64 for MacOSX 10.7 defaults to PIE - if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_7) ) { - fPositionIndependentExecutable = true; + if ( ((fArchitecture == CPU_TYPE_X86_64) || (fArchitecture == CPU_TYPE_I386)) + && (fOutputKind == kDynamicExecutable) + && (fMacVersionMin >= ld::mac10_7) ) { + fPositionIndependentExecutable = true; } // armv7 for iOS4.3 defaults to PIE diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index bb368c2..963e9e5 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -2583,7 +2583,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) ) { + else if ( _options.positionIndependentExecutable() && ((_options.iphoneOSVersionMin() >= ld::iPhone4_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. " From 9c31a73be3f736f0160794c0c199d855a26f5a06 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:08:18 +0100 Subject: [PATCH 10/48] 127-2 --- ld64/ChangeLog | 405 +++- ld64/doc/man/man1/ld.1 | 10 +- ld64/ld64.xcodeproj/project.pbxproj | 23 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 64 + ld64/src/ld/HeaderAndLoadCommands.hpp | 18 +- ld64/src/ld/InputFiles.cpp | 64 +- ld64/src/ld/InputFiles.h | 7 +- ld64/src/ld/LinkEdit.hpp | 68 +- ld64/src/ld/LinkEditClassic.hpp | 28 +- ld64/src/ld/Options.cpp | 300 +-- ld64/src/ld/Options.h | 18 +- ld64/src/ld/OutputFile.cpp | 312 ++- ld64/src/ld/OutputFile.h | 7 +- ld64/src/ld/Resolver.cpp | 76 +- ld64/src/ld/SymbolTable.cpp | 43 +- ld64/src/ld/debugline.c | 10 +- ld64/src/ld/dwarf2.h | 219 +++ ld64/src/ld/ld.cpp | 19 +- ld64/src/ld/ld.hpp | 53 +- ld64/src/ld/parsers/archive_file.cpp | 156 +- ld64/src/ld/parsers/archive_file.h | 5 +- .../src/ld/parsers/libunwind/AddressSpace.hpp | 439 +++++ .../parsers/libunwind/DwarfInstructions.hpp | 1726 +++++++++++++++++ ld64/src/ld/parsers/libunwind/DwarfParser.hpp | 819 ++++++++ .../src/ld/parsers/libunwind/InternalMacros.h | 105 + ld64/src/ld/parsers/libunwind/Registers.hpp | 1050 ++++++++++ ld64/src/ld/parsers/lto_file.cpp | 19 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 105 +- ld64/src/ld/parsers/macho_dylib_file.h | 5 +- .../src/ld/parsers/macho_relocatable_file.cpp | 617 ++++-- ld64/src/ld/passes/branch_island.cpp | 6 +- ld64/src/ld/passes/branch_shim.cpp | 258 ++- ld64/src/ld/passes/compact_unwind.cpp | 155 +- ld64/src/ld/passes/dylibs.cpp | 55 +- ld64/src/ld/passes/got.cpp | 23 +- ld64/src/ld/passes/objc.cpp | 32 +- ld64/src/ld/passes/order_file.cpp | 21 +- ld64/src/ld/passes/stubs/stubs.cpp | 40 +- ld64/src/ld/passes/tlvp.cpp | 8 - ld64/src/other/ObjectDump.cpp | 93 +- ld64/src/other/dyldinfo.cpp | 176 +- ld64/src/other/machochecker.cpp | 470 ++++- ld64/src/other/rebase.cpp | 18 +- ld64/src/other/unwinddump.cpp | 224 ++- ld64/unit-tests/include/common.makefile | 52 +- ld64/unit-tests/run-all-unit-tests | 12 +- .../test-cases/absolute-symbol/Makefile | 6 +- .../test-cases/absolute-symbol/abs.s | 2 +- .../test-cases/alias-command-line/aliases.s | 3 +- .../test-cases/alias-objects/aliases.s | 3 +- .../test-cases/archive-image_info/Makefile | 2 +- .../test-cases/archive-order/Makefile | 47 + .../unit-tests/test-cases/archive-order/bar.c | 1 + .../test-cases/archive-order/bar2.c | 1 + .../test-cases/archive-order/bar3.c | 1 + .../test-cases/archive-order/expected.order | 6 + .../unit-tests/test-cases/archive-order/foo.c | 1 + .../test-cases/archive-order/foo2.c | 1 + .../test-cases/archive-order/foo3.c | 1 + .../test-cases/archive-order/main.c | 41 + ld64/unit-tests/test-cases/auto-arch/Makefile | 12 +- ld64/unit-tests/test-cases/auto-arch/hello.c | 1 + .../test-cases/bind_at_load/Makefile | 4 +- .../test-cases/branch-distance/bar.s | 2 + .../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 + .../test-cases/branch-islands/hello.c | 1 + .../test-cases/check-init-abs/.mod_init_func | 2 + .../test-cases/check-init-abs/Makefile | 21 + .../test-cases/check-init-abs/init.s | 10 + .../{prebound-main => check-init-abs}/main.c | 1 - .../test-cases/check-init-abs/term.s | 10 + .../test-cases/check-init-bind/.mod_init_func | 2 + .../test-cases/check-init-bind/Makefile | 21 + .../test-cases/check-init-bind/init.s | 10 + .../test-cases/check-init-bind/main.c | 2 + .../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 +- .../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 +- .../test-cases/cpu-sub-types/Makefile | 66 +- .../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 +- ld64/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 +- .../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 +- ld64/unit-tests/test-cases/efi-basic/Makefile | 2 +- .../test-cases/efi-basic/MtocTest.c | 2 +- .../test-cases/eh-stripped-symbols/Makefile | 15 +- ld64/unit-tests/test-cases/filelist/hello.c | 1 + ld64/unit-tests/test-cases/flat-dylib/main.c | 1 + ld64/unit-tests/test-cases/flat-main/main.c | 1 + .../test-cases/function-starts/Makefile | 9 +- .../test-cases/function-starts/main.c | 4 + .../test-cases/got-elimination/Makefile | 4 +- ld64/unit-tests/test-cases/header-pad/hello.c | 1 + .../unit-tests/test-cases/hello-world/hello.c | 1 + .../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 + .../test-cases/objc-gc-checks/Makefile | 11 +- .../test-cases/objc-properties/Makefile | 1 - ld64/unit-tests/test-cases/order_file/extra.s | 1 + .../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 +- .../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 + .../test-cases/tlv-dead_strip/Makefile | 39 + .../test-cases/tlv-dead_strip/main.c | 33 + .../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 +- ld64/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 +- .../test-cases/weak_import3/Makefile | 15 +- .../unit-tests/test-cases/zero-fill3/Makefile | 10 +- 191 files changed, 9195 insertions(+), 1362 deletions(-) create mode 100644 ld64/src/ld/parsers/libunwind/AddressSpace.hpp create mode 100644 ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp create mode 100644 ld64/src/ld/parsers/libunwind/DwarfParser.hpp create mode 100644 ld64/src/ld/parsers/libunwind/InternalMacros.h create mode 100644 ld64/src/ld/parsers/libunwind/Registers.hpp create mode 100644 ld64/unit-tests/test-cases/archive-order/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-order/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-order/bar2.c create mode 100644 ld64/unit-tests/test-cases/archive-order/bar3.c create mode 100644 ld64/unit-tests/test-cases/archive-order/expected.order create mode 100644 ld64/unit-tests/test-cases/archive-order/foo.c create mode 100644 ld64/unit-tests/test-cases/archive-order/foo2.c create mode 100644 ld64/unit-tests/test-cases/archive-order/foo3.c create mode 100644 ld64/unit-tests/test-cases/archive-order/main.c create mode 100644 ld64/unit-tests/test-cases/check-init-abs/.mod_init_func create mode 100644 ld64/unit-tests/test-cases/check-init-abs/Makefile create mode 100644 ld64/unit-tests/test-cases/check-init-abs/init.s rename ld64/unit-tests/test-cases/{prebound-main => check-init-abs}/main.c (96%) create mode 100644 ld64/unit-tests/test-cases/check-init-abs/term.s create mode 100644 ld64/unit-tests/test-cases/check-init-bind/.mod_init_func create mode 100644 ld64/unit-tests/test-cases/check-init-bind/Makefile create mode 100644 ld64/unit-tests/test-cases/check-init-bind/init.s create mode 100644 ld64/unit-tests/test-cases/check-init-bind/main.c create mode 100644 ld64/unit-tests/test-cases/check-init-bind/term.s create mode 100644 ld64/unit-tests/test-cases/check-init-no-rebase/.mod_init_func create mode 100644 ld64/unit-tests/test-cases/check-init-no-rebase/Makefile create mode 100644 ld64/unit-tests/test-cases/check-init-no-rebase/init.s create mode 100644 ld64/unit-tests/test-cases/check-init-no-rebase/main.c create mode 100644 ld64/unit-tests/test-cases/check-init-no-rebase/term.s create mode 100644 ld64/unit-tests/test-cases/compact-unwind-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/compact-unwind-basic/test.s create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes/hello.order create mode 100644 ld64/unit-tests/test-cases/kext-undefined-export/Makefile create mode 100644 ld64/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp create mode 100644 ld64/unit-tests/test-cases/kext-undefined-export/mykext.c create mode 100644 ld64/unit-tests/test-cases/kext-undefined-export/mykext.exp create mode 100644 ld64/unit-tests/test-cases/kext-undefined-export/mykextinfo.c create mode 100755 ld64/unit-tests/test-cases/label-on-end-of-section-order/Makefile create mode 100755 ld64/unit-tests/test-cases/label-on-end-of-section-order/foo.s create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-coalesce/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-coalesce/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c create mode 100755 ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile create mode 100755 ld64/unit-tests/test-cases/merge_zero_fill_sections/main.c create mode 100644 ld64/unit-tests/test-cases/objc-category-warning/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-category-warning/cat.m create mode 100644 ld64/unit-tests/test-cases/objc-category-warning/copycat.m create mode 100644 ld64/unit-tests/test-cases/objc-category-warning/foo.m delete mode 100644 ld64/unit-tests/test-cases/prebound-main/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive-code/foo_code.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive-code/foo_data.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive-code/junk.c create mode 100644 ld64/unit-tests/test-cases/tentative-and-archive-code/main.c create mode 100644 ld64/unit-tests/test-cases/tlv-dead_strip/Makefile create mode 100644 ld64/unit-tests/test-cases/tlv-dead_strip/main.c create mode 100644 ld64/unit-tests/test-cases/weak-def-hidden-and-global/Makefile create mode 100644 ld64/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c create mode 100644 ld64/unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s create mode 100644 ld64/unit-tests/test-cases/weak_dylib/data.c create mode 100644 ld64/unit-tests/test-cases/weak_import-addend/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import-addend/test.s diff --git a/ld64/ChangeLog b/ld64/ChangeLog index e2d1d2c..fd449c8 100644 --- a/ld64/ChangeLog +++ b/ld64/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/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index c1aa2bc..03f257a 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/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/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index cfb54eb..ad73394 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/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/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index a9a69a7..f5f073b 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/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 @@ class macho_version_min_command { }; +// +// 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/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 2c97dba..395fc99 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/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/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index bc292bb..84a2297 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/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/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index 1565515..1842c8e 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/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 @@ class InputFiles : public ld::dylib::File::DylibHandler // 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 @@ class InputFiles : public ld::dylib::File::DylibHandler 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/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index d905a3d..bf9026b 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -1058,12 +1058,16 @@ class SplitSegInfoAtom : public LinkEditAtom 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/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index d5438ac..1429d95 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/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/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index c06e1f3..3f5b054 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/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/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 7ce5766..d9becf3 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -150,6 +150,7 @@ class Options 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 @@ class Options 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 @@ class Options 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 @@ class Options 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 @@ class Options 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 @@ class Options const char* fArchitectureName; OutputKind fOutputKind; bool fHasPreferredSubType; + bool fArchSupportsThumb2; bool fPrebind; bool fBindAtLoad; bool fKeepPrivateExterns; @@ -476,6 +480,7 @@ class Options bool fRemoveDwarfUnwindIfCompactExists; bool fAutoOrderInitializers; bool fOptimizeZeroFill; + bool fMergeZeroFill; bool fLogObjectFiles; bool fLogAllFiles; bool fTraceDylibs; @@ -489,13 +494,18 @@ class Options 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/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 963e9e5..65e8b8c 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/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 @@ class DebugNoteSorter 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/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index 9b49289..415d24b 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -130,9 +130,10 @@ class OutputFile }; 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 @@ class OutputFile 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 @@ class OutputFile 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/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index e17e632..a56cefe 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/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/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index f9ed04c..2716ccf 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/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/ld64/src/ld/debugline.c b/ld64/src/ld/debugline.c index c367d00..c4e151d 100644 --- a/ld64/src/ld/debugline.c +++ b/ld64/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/ld64/src/ld/dwarf2.h b/ld64/src/ld/dwarf2.h index 7a7b4f2..411dbbc 100644 --- a/ld64/src/ld/dwarf2.h +++ b/ld64/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/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index d9d66a6..5b21c63 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -103,7 +103,7 @@ class InternalState : public ld::Internal 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 @@ class InternalState : public ld::Internal 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/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index e5319bb..96075a7 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -83,8 +83,8 @@ class File // 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 @@ class Section 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 @@ class Atom 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 @@ class Atom _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 @@ class Atom 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 @@ class Atom 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 @@ class Atom _mode = a._mode; _overridesADylibsWeakDef = a._overridesADylibsWeakDef; _coalescedAway = a._coalescedAway; - _weakImport = a._weakImport; + _weakImportState = a._weakImportState; } const Section * _section; @@ -626,9 +641,9 @@ class Atom AddressMode _mode: 2; bool _overridesADylibsWeakDef : 1; bool _coalescedAway : 1; - bool _weakImport : 1; bool _live : 1; unsigned _machoSection : 8; + WeakImportState _weakImportState : 2; }; diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 8c866cd..66f9432 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/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 @@ class Parser }; 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 @@ class File : public ld::File 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 @@ class File : public ld::File const mach_o::relocatable::ParserOptions& opts); static cpu_type_t architecture(); - class Entry : ar_hdr { public: @@ -116,8 +119,12 @@ class File : public ld::File 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 @@ class File : public ld::File 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/ld64/src/ld/parsers/archive_file.h b/ld64/src/ld/parsers/archive_file.h index 4dcbc8b..31bab29 100644 --- a/ld64/src/ld/parsers/archive_file.h +++ b/ld64/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/ld64/src/ld/parsers/libunwind/AddressSpace.hpp b/ld64/src/ld/parsers/libunwind/AddressSpace.hpp new file mode 100644 index 0000000..bebd3e6 --- /dev/null +++ b/ld64/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/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp new file mode 100644 index 0000000..b04582d --- /dev/null +++ b/ld64/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/ld64/src/ld/parsers/libunwind/DwarfParser.hpp b/ld64/src/ld/parsers/libunwind/DwarfParser.hpp new file mode 100644 index 0000000..3824d2e --- /dev/null +++ b/ld64/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/ld64/src/ld/parsers/libunwind/InternalMacros.h b/ld64/src/ld/parsers/libunwind/InternalMacros.h new file mode 100644 index 0000000..25c1631 --- /dev/null +++ b/ld64/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/ld64/src/ld/parsers/libunwind/Registers.hpp b/ld64/src/ld/parsers/libunwind/Registers.hpp new file mode 100644 index 0000000..7d39fd7 --- /dev/null +++ b/ld64/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/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 6221c90..46d350d 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/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/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 40dad13..436f3e6 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/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 @@ class File : public ld::dylib::File 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 @@ class File : public ld::dylib::File 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 @@ class File : public ld::dylib::File 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 @@ class File : public ld::dylib::File 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 @@ class Parser 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/ld64/src/ld/parsers/macho_dylib_file.h b/ld64/src/ld/parsers/macho_dylib_file.h index 5c858e2..d26f50e 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.h +++ b/ld64/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/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 25c1965..abd44fa 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/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 @@ class Section : public ld::Section 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 @@ class Section : public ld::Section 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 @@ class CFISection : public Section 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 @@ class CFISection : public Section }; +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 @@ class TentativeDefinitionSection : public Section 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 @@ class AbsoluteSymbolSection : public Section 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 @@ class SymboledSection : public Section 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 @@ class NonLazyPointerSection : public FixedSizeSection 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 @@ class CStringSection : public ImplicitSizeSection 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 @@ class Atom : public ld::Atom 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 @@ class Parser 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 @@ class Parser 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 @@ class Parser 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 @@ class Parser 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 @@ class Parser // 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/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index d42fa54..99b3eb8 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/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/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp index 70ebcfc..7a2bd50 100644 --- a/ld64/src/ld/passes/branch_shim.cpp +++ b/ld64/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 @@ class Thumb2ToArmShimAtom : public ld::Atom { }; +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 @@ class Thumb1ToArmShimAtom : public ld::Atom { 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 @@ class ARMtoThumbShimAtom : public ld::Atom { }; +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/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 5b9434e..86c8eec 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/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/ld64/src/ld/passes/dylibs.cpp b/ld64/src/ld/passes/dylibs.cpp index 2b4452b..47b1647 100644 --- a/ld64/src/ld/passes/dylibs.cpp +++ b/ld64/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/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index ff18e00..bb94ddf 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/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/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index f420b9f..ed59193 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/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/ld64/src/ld/passes/order_file.cpp b/ld64/src/ld/passes/order_file.cpp index 9e336ae..5e814bd 100644 --- a/ld64/src/ld/passes/order_file.cpp +++ b/ld64/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/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index 0e23c27..274878b 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/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/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index f40d018..a102e97 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/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/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index abf7162..3d7ac5e 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/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 @@ class DyldInfoPrinter 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/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 295fa19..4841c7c 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/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 @@ class MachOChecker 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 @@ class MachOChecker 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 @@ class MachOChecker 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/ld64/src/other/rebase.cpp b/ld64/src/other/rebase.cpp index c04fb0a..d69fc3c 100644 --- a/ld64/src/other/rebase.cpp +++ b/ld64/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/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 81e677d..d24e62c 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/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 @@ class UnwindPrinter 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/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 642e491..adb5468 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/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/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests index f6275a7..c07c947 100755 --- a/ld64/unit-tests/run-all-unit-tests +++ b/ld64/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/ld64/unit-tests/test-cases/absolute-symbol/Makefile b/ld64/unit-tests/test-cases/absolute-symbol/Makefile index 806c64e..bbdfd11 100644 --- a/ld64/unit-tests/test-cases/absolute-symbol/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/absolute-symbol/abs.s b/ld64/unit-tests/test-cases/absolute-symbol/abs.s index 1aee96f..dc5496f 100644 --- a/ld64/unit-tests/test-cases/absolute-symbol/abs.s +++ b/ld64/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/ld64/unit-tests/test-cases/alias-command-line/aliases.s b/ld64/unit-tests/test-cases/alias-command-line/aliases.s index 48e5961..3f3a9a6 100644 --- a/ld64/unit-tests/test-cases/alias-command-line/aliases.s +++ b/ld64/unit-tests/test-cases/alias-command-line/aliases.s @@ -22,7 +22,8 @@ */ .text - + .align 4 + _temp: nop nop diff --git a/ld64/unit-tests/test-cases/alias-objects/aliases.s b/ld64/unit-tests/test-cases/alias-objects/aliases.s index b21f8c7..5e92d8d 100644 --- a/ld64/unit-tests/test-cases/alias-objects/aliases.s +++ b/ld64/unit-tests/test-cases/alias-objects/aliases.s @@ -22,7 +22,8 @@ */ .text - + .align 4 + _temp: nop nop diff --git a/ld64/unit-tests/test-cases/archive-image_info/Makefile b/ld64/unit-tests/test-cases/archive-image_info/Makefile index 5bb026f..ae87948 100644 --- a/ld64/unit-tests/test-cases/archive-image_info/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/archive-order/Makefile b/ld64/unit-tests/test-cases/archive-order/Makefile new file mode 100644 index 0000000..a1b6a3f --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/archive-order/bar.c b/ld64/unit-tests/test-cases/archive-order/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-order/bar2.c b/ld64/unit-tests/test-cases/archive-order/bar2.c new file mode 100644 index 0000000..54e8ace --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/bar2.c @@ -0,0 +1 @@ +int bar2() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-order/bar3.c b/ld64/unit-tests/test-cases/archive-order/bar3.c new file mode 100644 index 0000000..0b44057 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/bar3.c @@ -0,0 +1 @@ +int bar3() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-order/expected.order b/ld64/unit-tests/test-cases/archive-order/expected.order new file mode 100644 index 0000000..f060170 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/expected.order @@ -0,0 +1,6 @@ +_bar3 +_bar2 +_bar +_foo +_foo2 +_foo3 diff --git a/ld64/unit-tests/test-cases/archive-order/foo.c b/ld64/unit-tests/test-cases/archive-order/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/ld64/unit-tests/test-cases/archive-order/foo2.c b/ld64/unit-tests/test-cases/archive-order/foo2.c new file mode 100644 index 0000000..492879f --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/foo2.c @@ -0,0 +1 @@ +int foo2() { return 1; } diff --git a/ld64/unit-tests/test-cases/archive-order/foo3.c b/ld64/unit-tests/test-cases/archive-order/foo3.c new file mode 100644 index 0000000..1de0bce --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-order/foo3.c @@ -0,0 +1 @@ +int foo3() { return 1; } diff --git a/ld64/unit-tests/test-cases/archive-order/main.c b/ld64/unit-tests/test-cases/archive-order/main.c new file mode 100644 index 0000000..242b32a --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/auto-arch/Makefile b/ld64/unit-tests/test-cases/auto-arch/Makefile index 3b1cc7c..06ca6ed 100644 --- a/ld64/unit-tests/test-cases/auto-arch/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/auto-arch/hello.c b/ld64/unit-tests/test-cases/auto-arch/hello.c index 14d9363..463663a 100644 --- a/ld64/unit-tests/test-cases/auto-arch/hello.c +++ b/ld64/unit-tests/test-cases/auto-arch/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } diff --git a/ld64/unit-tests/test-cases/bind_at_load/Makefile b/ld64/unit-tests/test-cases/bind_at_load/Makefile index 007feb5..3cc0bf2 100644 --- a/ld64/unit-tests/test-cases/bind_at_load/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/branch-distance/bar.s b/ld64/unit-tests/test-cases/branch-distance/bar.s index 5b1f6dd..999166a 100644 --- a/ld64/unit-tests/test-cases/branch-distance/bar.s +++ b/ld64/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/ld64/unit-tests/test-cases/branch-distance/foo.s b/ld64/unit-tests/test-cases/branch-distance/foo.s index 18f3a81..73d0140 100644 --- a/ld64/unit-tests/test-cases/branch-distance/foo.s +++ b/ld64/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/ld64/unit-tests/test-cases/branch-interworking/Makefile b/ld64/unit-tests/test-cases/branch-interworking/Makefile index c83e25e..d5ad615 100644 --- a/ld64/unit-tests/test-cases/branch-interworking/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/branch-interworking/myarm.s b/ld64/unit-tests/test-cases/branch-interworking/myarm.s index a3c7818..df759e6 100644 --- a/ld64/unit-tests/test-cases/branch-interworking/myarm.s +++ b/ld64/unit-tests/test-cases/branch-interworking/myarm.s @@ -1,5 +1,6 @@ .text + .align 4 #if __arm__ .code 32 .align 2 diff --git a/ld64/unit-tests/test-cases/branch-interworking/mythumb.s b/ld64/unit-tests/test-cases/branch-interworking/mythumb.s index a58c28f..32e3d4b 100644 --- a/ld64/unit-tests/test-cases/branch-interworking/mythumb.s +++ b/ld64/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/ld64/unit-tests/test-cases/branch-islands/hello.c b/ld64/unit-tests/test-cases/branch-islands/hello.c index 89caa9d..3037663 100644 --- a/ld64/unit-tests/test-cases/branch-islands/hello.c +++ b/ld64/unit-tests/test-cases/branch-islands/hello.c @@ -6,5 +6,6 @@ int main() { fprintf(stdout, "hello\n"); foo(); + return 0; } diff --git a/ld64/unit-tests/test-cases/check-init-abs/.mod_init_func b/ld64/unit-tests/test-cases/check-init-abs/.mod_init_func new file mode 100644 index 0000000..f0e588d --- /dev/null +++ b/ld64/unit-tests/test-cases/check-init-abs/.mod_init_func @@ -0,0 +1,2 @@ + .long 0x12340000 + diff --git a/ld64/unit-tests/test-cases/check-init-abs/Makefile b/ld64/unit-tests/test-cases/check-init-abs/Makefile new file mode 100644 index 0000000..f0dbf19 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-abs/init.s b/ld64/unit-tests/test-cases/check-init-abs/init.s new file mode 100644 index 0000000..55e93c6 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/prebound-main/main.c b/ld64/unit-tests/test-cases/check-init-abs/main.c similarity index 96% rename from ld64/unit-tests/test-cases/prebound-main/main.c rename to ld64/unit-tests/test-cases/check-init-abs/main.c index 251979e..b54a220 100644 --- a/ld64/unit-tests/test-cases/prebound-main/main.c +++ b/ld64/unit-tests/test-cases/check-init-abs/main.c @@ -1,3 +1,2 @@ - int main() { return 0; } diff --git a/ld64/unit-tests/test-cases/check-init-abs/term.s b/ld64/unit-tests/test-cases/check-init-abs/term.s new file mode 100644 index 0000000..c69be4f --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-bind/.mod_init_func b/ld64/unit-tests/test-cases/check-init-bind/.mod_init_func new file mode 100644 index 0000000..f0e588d --- /dev/null +++ b/ld64/unit-tests/test-cases/check-init-bind/.mod_init_func @@ -0,0 +1,2 @@ + .long 0x12340000 + diff --git a/ld64/unit-tests/test-cases/check-init-bind/Makefile b/ld64/unit-tests/test-cases/check-init-bind/Makefile new file mode 100644 index 0000000..95c3ce8 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-bind/init.s b/ld64/unit-tests/test-cases/check-init-bind/init.s new file mode 100644 index 0000000..f9dce33 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-bind/main.c b/ld64/unit-tests/test-cases/check-init-bind/main.c new file mode 100644 index 0000000..b54a220 --- /dev/null +++ b/ld64/unit-tests/test-cases/check-init-bind/main.c @@ -0,0 +1,2 @@ +int main() { return 0; } + diff --git a/ld64/unit-tests/test-cases/check-init-bind/term.s b/ld64/unit-tests/test-cases/check-init-bind/term.s new file mode 100644 index 0000000..bbdd265 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-no-rebase/.mod_init_func b/ld64/unit-tests/test-cases/check-init-no-rebase/.mod_init_func new file mode 100644 index 0000000..f0e588d --- /dev/null +++ b/ld64/unit-tests/test-cases/check-init-no-rebase/.mod_init_func @@ -0,0 +1,2 @@ + .long 0x12340000 + diff --git a/ld64/unit-tests/test-cases/check-init-no-rebase/Makefile b/ld64/unit-tests/test-cases/check-init-no-rebase/Makefile new file mode 100644 index 0000000..ff7d09a --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-no-rebase/init.s b/ld64/unit-tests/test-cases/check-init-no-rebase/init.s new file mode 100644 index 0000000..5b282d0 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/check-init-no-rebase/main.c b/ld64/unit-tests/test-cases/check-init-no-rebase/main.c new file mode 100644 index 0000000..b54a220 --- /dev/null +++ b/ld64/unit-tests/test-cases/check-init-no-rebase/main.c @@ -0,0 +1,2 @@ +int main() { return 0; } + diff --git a/ld64/unit-tests/test-cases/check-init-no-rebase/term.s b/ld64/unit-tests/test-cases/check-init-no-rebase/term.s new file mode 100644 index 0000000..cfe0b3a --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile b/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile index cdfe6ac..1f9f2e4 100644 --- a/ld64/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/commons-alignment/Makefile b/ld64/unit-tests/test-cases/commons-alignment/Makefile index d073c0e..15e1abd 100644 --- a/ld64/unit-tests/test-cases/commons-alignment/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/commons-alignment/foo.s b/ld64/unit-tests/test-cases/commons-alignment/foo.s index 03c28fc..2b11717 100644 --- a/ld64/unit-tests/test-cases/commons-alignment/foo.s +++ b/ld64/unit-tests/test-cases/commons-alignment/foo.s @@ -1,2 +1,3 @@ .comm _mycomm64aligned,15,6 + .comm _mycomm16kaligned,256,14 diff --git a/ld64/unit-tests/test-cases/compact-unwind-basic/Makefile b/ld64/unit-tests/test-cases/compact-unwind-basic/Makefile new file mode 100644 index 0000000..10d741e --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/compact-unwind-basic/test.s b/ld64/unit-tests/test-cases/compact-unwind-basic/test.s new file mode 100644 index 0000000..e694feb --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile b/ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile index 7586a51..5f3ca5d 100644 --- a/ld64/unit-tests/test-cases/cpu-sub-types-preference/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/cpu-sub-types/Makefile b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile index 695ffba..f773c11 100644 --- a/ld64/unit-tests/test-cases/cpu-sub-types/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/cstring-labels/Makefile b/ld64/unit-tests/test-cases/cstring-labels/Makefile index 1c6ee7d..76db33a 100644 --- a/ld64/unit-tests/test-cases/cstring-labels/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/custom-segment-layout/Makefile b/ld64/unit-tests/test-cases/custom-segment-layout/Makefile index 1dfb23f..e5b13c2 100644 --- a/ld64/unit-tests/test-cases/custom-segment-layout/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/custom-segment-layout/zero.s b/ld64/unit-tests/test-cases/custom-segment-layout/zero.s index 86bde83..94e1a50 100644 --- a/ld64/unit-tests/test-cases/custom-segment-layout/zero.s +++ b/ld64/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/ld64/unit-tests/test-cases/dead_strip-archive/Makefile b/ld64/unit-tests/test-cases/dead_strip-archive/Makefile index 1542d78..54812f3 100644 --- a/ld64/unit-tests/test-cases/dead_strip-archive/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile b/ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile index 1317c48..75b7988 100644 --- a/ld64/unit-tests/test-cases/dead_strip-entry-archive/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/demangle/Makefile b/ld64/unit-tests/test-cases/demangle/Makefile index 6ed52b7..018bd4c 100644 --- a/ld64/unit-tests/test-cases/demangle/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/dependency-logging/Makefile b/ld64/unit-tests/test-cases/dependency-logging/Makefile index e861226..7f4cf64 100644 --- a/ld64/unit-tests/test-cases/dependency-logging/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile index b4fa7ab..ea9dc56 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index 95eb33d..f7eb45a 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-debug-notes/hello.order b/ld64/unit-tests/test-cases/dwarf-debug-notes/hello.order new file mode 100644 index 0000000..2860890 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx index b1b0e77..a6b403b 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/other.cxx +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-ignore/hello.c b/ld64/unit-tests/test-cases/dwarf-ignore/hello.c index e2f0fe9..4750b51 100644 --- a/ld64/unit-tests/test-cases/dwarf-ignore/hello.c +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile b/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile index 3812933..866d178 100644 --- a/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/dwarf-strip/hello.c b/ld64/unit-tests/test-cases/dwarf-strip/hello.c index ddca819..68509d7 100644 --- a/ld64/unit-tests/test-cases/dwarf-strip/hello.c +++ b/ld64/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/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile index 057ac03..86248c2 100644 --- a/ld64/unit-tests/test-cases/dylib-re-export-cycle/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/efi-basic/Makefile b/ld64/unit-tests/test-cases/efi-basic/Makefile index 1f392c5..99f2fa6 100644 --- a/ld64/unit-tests/test-cases/efi-basic/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/efi-basic/MtocTest.c b/ld64/unit-tests/test-cases/efi-basic/MtocTest.c index 5e3ebd5..1d076ba 100644 --- a/ld64/unit-tests/test-cases/efi-basic/MtocTest.c +++ b/ld64/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/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile b/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile index 15819f0..404cc41 100644 --- a/ld64/unit-tests/test-cases/eh-stripped-symbols/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/filelist/hello.c b/ld64/unit-tests/test-cases/filelist/hello.c index 14d9363..463663a 100644 --- a/ld64/unit-tests/test-cases/filelist/hello.c +++ b/ld64/unit-tests/test-cases/filelist/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } diff --git a/ld64/unit-tests/test-cases/flat-dylib/main.c b/ld64/unit-tests/test-cases/flat-dylib/main.c index 6014829..affbf90 100644 --- a/ld64/unit-tests/test-cases/flat-dylib/main.c +++ b/ld64/unit-tests/test-cases/flat-dylib/main.c @@ -30,4 +30,5 @@ int main() { foo(); fprintf(stdout, "hello\n"); + return 0; } diff --git a/ld64/unit-tests/test-cases/flat-main/main.c b/ld64/unit-tests/test-cases/flat-main/main.c index 6014829..affbf90 100644 --- a/ld64/unit-tests/test-cases/flat-main/main.c +++ b/ld64/unit-tests/test-cases/flat-main/main.c @@ -30,4 +30,5 @@ int main() { foo(); fprintf(stdout, "hello\n"); + return 0; } diff --git a/ld64/unit-tests/test-cases/function-starts/Makefile b/ld64/unit-tests/test-cases/function-starts/Makefile index 0b43cd5..531bb7a 100644 --- a/ld64/unit-tests/test-cases/function-starts/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/function-starts/main.c b/ld64/unit-tests/test-cases/function-starts/main.c index 4ff1717..be69aaa 100644 --- a/ld64/unit-tests/test-cases/function-starts/main.c +++ b/ld64/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/ld64/unit-tests/test-cases/got-elimination/Makefile b/ld64/unit-tests/test-cases/got-elimination/Makefile index 6fae5ee..e3b687b 100644 --- a/ld64/unit-tests/test-cases/got-elimination/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/header-pad/hello.c b/ld64/unit-tests/test-cases/header-pad/hello.c index 14d9363..463663a 100644 --- a/ld64/unit-tests/test-cases/header-pad/hello.c +++ b/ld64/unit-tests/test-cases/header-pad/hello.c @@ -26,4 +26,5 @@ int main() { fprintf(stdout, "hello\n"); + return 0; } diff --git a/ld64/unit-tests/test-cases/hello-world/hello.c b/ld64/unit-tests/test-cases/hello-world/hello.c index e2f0fe9..4750b51 100644 --- a/ld64/unit-tests/test-cases/hello-world/hello.c +++ b/ld64/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/ld64/unit-tests/test-cases/implicit_dylib/Makefile b/ld64/unit-tests/test-cases/implicit_dylib/Makefile index c982885..b2cfe02 100644 --- a/ld64/unit-tests/test-cases/implicit_dylib/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/kext-basic/Makefile b/ld64/unit-tests/test-cases/kext-basic/Makefile index 4844cd7..e33b66c 100644 --- a/ld64/unit-tests/test-cases/kext-basic/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/kext-undefined-export/Makefile b/ld64/unit-tests/test-cases/kext-undefined-export/Makefile new file mode 100644 index 0000000..b6acfd4 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp b/ld64/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp new file mode 100644 index 0000000..2449aba --- /dev/null +++ b/ld64/unit-tests/test-cases/kext-undefined-export/mykext-i386.exp @@ -0,0 +1 @@ +_kmod_info diff --git a/ld64/unit-tests/test-cases/kext-undefined-export/mykext.c b/ld64/unit-tests/test-cases/kext-undefined-export/mykext.c new file mode 100644 index 0000000..d01996f --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/kext-undefined-export/mykext.exp b/ld64/unit-tests/test-cases/kext-undefined-export/mykext.exp new file mode 100644 index 0000000..eccfe2a --- /dev/null +++ b/ld64/unit-tests/test-cases/kext-undefined-export/mykext.exp @@ -0,0 +1,3 @@ +_kmod_info +_foo +_my_used_external_global diff --git a/ld64/unit-tests/test-cases/kext-undefined-export/mykextinfo.c b/ld64/unit-tests/test-cases/kext-undefined-export/mykextinfo.c new file mode 100644 index 0000000..fe31e7b --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/label-on-end-of-section-order/Makefile b/ld64/unit-tests/test-cases/label-on-end-of-section-order/Makefile new file mode 100755 index 0000000..7ec72bc --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/label-on-end-of-section-order/foo.s b/ld64/unit-tests/test-cases/label-on-end-of-section-order/foo.s new file mode 100755 index 0000000..e0fd49c --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/llvm-integration/Makefile b/ld64/unit-tests/test-cases/llvm-integration/Makefile index 513dccc..8f33f36 100644 --- a/ld64/unit-tests/test-cases/llvm-integration/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-archive-dylib/Makefile b/ld64/unit-tests/test-cases/lto-archive-dylib/Makefile index e538ee1..ba8eed7 100644 --- a/ld64/unit-tests/test-cases/lto-archive-dylib/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile index 060e5a1..379a282 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-all-hidden/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/Makefile new file mode 100644 index 0000000..2f3f504 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/foo.c b/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/foo.c new file mode 100644 index 0000000..f47f84d --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c b/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c new file mode 100644 index 0000000..ecbd4f2 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile index ccd5b59..afd6948 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-objc/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile index d563c92..ad1fe8b 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile index fa0a02b..5a54bcf 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-tentative/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile index a518d82..b27291a 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-llvm-options/Makefile b/ld64/unit-tests/test-cases/lto-llvm-options/Makefile index 1edf08a..095405a 100644 --- a/ld64/unit-tests/test-cases/lto-llvm-options/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-objc-archive/Makefile b/ld64/unit-tests/test-cases/lto-objc-archive/Makefile index 57fae1d..968671a 100644 --- a/ld64/unit-tests/test-cases/lto-objc-archive/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile b/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile index 8b13444..1a6f059 100644 --- a/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-object_path/Makefile b/ld64/unit-tests/test-cases/lto-object_path/Makefile index aeda85d..180cfb3 100644 --- a/ld64/unit-tests/test-cases/lto-object_path/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-weak-native-override/Makefile b/ld64/unit-tests/test-cases/lto-weak-native-override/Makefile index 55ac0f1..2be86f3 100644 --- a/ld64/unit-tests/test-cases/lto-weak-native-override/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/lto-weak-native-override/main.c b/ld64/unit-tests/test-cases/lto-weak-native-override/main.c index 6a433db..5f5d498 100644 --- a/ld64/unit-tests/test-cases/lto-weak-native-override/main.c +++ b/ld64/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/ld64/unit-tests/test-cases/lto-weak_import/Makefile b/ld64/unit-tests/test-cases/lto-weak_import/Makefile index e53ad48..ff4f7cc 100644 --- a/ld64/unit-tests/test-cases/lto-weak_import/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile b/ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile new file mode 100755 index 0000000..bec9e76 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/merge_zero_fill_sections/main.c b/ld64/unit-tests/test-cases/merge_zero_fill_sections/main.c new file mode 100755 index 0000000..87528ec --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s b/ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s index d307880..1749c7e 100644 --- a/ld64/unit-tests/test-cases/non-lazy-sections-r/foo.s +++ b/ld64/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/ld64/unit-tests/test-cases/objc-category-warning/Makefile b/ld64/unit-tests/test-cases/objc-category-warning/Makefile new file mode 100644 index 0000000..95cc91d --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/objc-category-warning/cat.m b/ld64/unit-tests/test-cases/objc-category-warning/cat.m new file mode 100644 index 0000000..e60f37b --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/objc-category-warning/copycat.m b/ld64/unit-tests/test-cases/objc-category-warning/copycat.m new file mode 100644 index 0000000..309be02 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/objc-category-warning/foo.m b/ld64/unit-tests/test-cases/objc-category-warning/foo.m new file mode 100644 index 0000000..2a75794 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/objc-class-alias/Makefile b/ld64/unit-tests/test-cases/objc-class-alias/Makefile index c8c1b27..52840c9 100644 --- a/ld64/unit-tests/test-cases/objc-class-alias/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile index b18012a..67e04a3 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/objc-properties/Makefile b/ld64/unit-tests/test-cases/objc-properties/Makefile index 999bf2f..8788364 100644 --- a/ld64/unit-tests/test-cases/objc-properties/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/order_file/extra.s b/ld64/unit-tests/test-cases/order_file/extra.s index eba52c0..9a6b194 100644 --- a/ld64/unit-tests/test-cases/order_file/extra.s +++ b/ld64/unit-tests/test-cases/order_file/extra.s @@ -1,6 +1,7 @@ .text + .align 4 .globl _foo1 _foo1: nop diff --git a/ld64/unit-tests/test-cases/prebound-main/Makefile b/ld64/unit-tests/test-cases/prebound-main/Makefile deleted file mode 100644 index 285d767..0000000 --- a/ld64/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/ld64/unit-tests/test-cases/prebound-split-seg/Makefile b/ld64/unit-tests/test-cases/prebound-split-seg/Makefile index 07bce59..67ed3fa 100644 --- a/ld64/unit-tests/test-cases/prebound-split-seg/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/re-export-and-use/Makefile b/ld64/unit-tests/test-cases/re-export-and-use/Makefile index b220580..b4de574 100644 --- a/ld64/unit-tests/test-cases/re-export-and-use/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/re-export-cases/Makefile b/ld64/unit-tests/test-cases/re-export-cases/Makefile index 64ec112..59887aa 100644 --- a/ld64/unit-tests/test-cases/re-export-cases/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/re-export-layers/Makefile b/ld64/unit-tests/test-cases/re-export-layers/Makefile index fc62273..3ec1fca 100644 --- a/ld64/unit-tests/test-cases/re-export-layers/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile b/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile index fdb13d5..4eb7b43 100644 --- a/ld64/unit-tests/test-cases/re-export-optimizations-indirect/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/re-export-optimizations/Makefile b/ld64/unit-tests/test-cases/re-export-optimizations/Makefile index a1dfd88..e71b8cc 100644 --- a/ld64/unit-tests/test-cases/re-export-optimizations/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/read-only-relocs/Makefile b/ld64/unit-tests/test-cases/read-only-relocs/Makefile index 8db5b03..e4b8086 100644 --- a/ld64/unit-tests/test-cases/read-only-relocs/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s b/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s index 86716da..67ff5e5 100644 --- a/ld64/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/ld64/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/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c b/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c index 6924ac6..68edc41 100644 --- a/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c +++ b/ld64/unit-tests/test-cases/shared-cache-dylib/foo.c @@ -1,3 +1,6 @@ -void foo() {} + +int x; + +int foo() { return x; } diff --git a/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile b/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile index 5318933..d6c5b82 100644 --- a/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/stabs-directory-slash/main.c b/ld64/unit-tests/test-cases/stabs-directory-slash/main.c index 54dc4c5..a46866d 100644 --- a/ld64/unit-tests/test-cases/stabs-directory-slash/main.c +++ b/ld64/unit-tests/test-cases/stabs-directory-slash/main.c @@ -1,3 +1,4 @@ -main() +int main() { + return 0; } diff --git a/ld64/unit-tests/test-cases/stack_addr_size/Makefile b/ld64/unit-tests/test-cases/stack_addr_size/Makefile index 670f014..c88f66b 100644 --- a/ld64/unit-tests/test-cases/stack_addr_size/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/stack_size_no_addr/Makefile b/ld64/unit-tests/test-cases/stack_size_no_addr/Makefile index 6134c7e..cbb6d49 100644 --- a/ld64/unit-tests/test-cases/stack_size_no_addr/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/strip_local/hello.c b/ld64/unit-tests/test-cases/strip_local/hello.c index 2deed42..09e802f 100644 --- a/ld64/unit-tests/test-cases/strip_local/hello.c +++ b/ld64/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/ld64/unit-tests/test-cases/switch-jump-table/Makefile b/ld64/unit-tests/test-cases/switch-jump-table/Makefile index 847dc78..72bc68b 100644 --- a/ld64/unit-tests/test-cases/switch-jump-table/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/switch-jump-table/switch.s b/ld64/unit-tests/test-cases/switch-jump-table/switch.s index 12b559f..b2b0249 100644 --- a/ld64/unit-tests/test-cases/switch-jump-table/switch.s +++ b/ld64/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/ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile b/ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile new file mode 100644 index 0000000..7c2c28f --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_code.c b/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_code.c new file mode 100644 index 0000000..277b5ae --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_code.c @@ -0,0 +1,3 @@ + +int foo() { return 10; } + diff --git a/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_data.c b/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_data.c new file mode 100644 index 0000000..e7d3845 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_data.c @@ -0,0 +1,2 @@ + +int foo = 5; diff --git a/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c b/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c new file mode 100644 index 0000000..42e0a8e --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive-code/foo_tent.c @@ -0,0 +1,2 @@ + +int foo; diff --git a/ld64/unit-tests/test-cases/tentative-and-archive-code/junk.c b/ld64/unit-tests/test-cases/tentative-and-archive-code/junk.c new file mode 100644 index 0000000..6ecfcf1 --- /dev/null +++ b/ld64/unit-tests/test-cases/tentative-and-archive-code/junk.c @@ -0,0 +1 @@ +void junk() {} diff --git a/ld64/unit-tests/test-cases/tentative-and-archive-code/main.c b/ld64/unit-tests/test-cases/tentative-and-archive-code/main.c new file mode 100644 index 0000000..08ed143 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile b/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile new file mode 100644 index 0000000..46e695c --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/tlv-dead_strip/main.c b/ld64/unit-tests/test-cases/tlv-dead_strip/main.c new file mode 100644 index 0000000..a3e3fee --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/umbrella-dylib/Makefile b/ld64/unit-tests/test-cases/umbrella-dylib/Makefile index b20f368..0835fdb 100644 --- a/ld64/unit-tests/test-cases/umbrella-dylib/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/weak-def-auto-hide/other.s b/ld64/unit-tests/test-cases/weak-def-auto-hide/other.s index 5fd7ec4..c524ebf 100644 --- a/ld64/unit-tests/test-cases/weak-def-auto-hide/other.s +++ b/ld64/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/ld64/unit-tests/test-cases/weak-def-hidden-and-global/Makefile b/ld64/unit-tests/test-cases/weak-def-hidden-and-global/Makefile new file mode 100644 index 0000000..b275b62 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c b/ld64/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c new file mode 100644 index 0000000..c671809 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak-def-hidden-and-global/myglobal.c @@ -0,0 +1,4 @@ + +__attribute__((weak)) +int myweak = 10; + diff --git a/ld64/unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s b/ld64/unit-tests/test-cases/weak-def-hidden-and-global/myhidden.s new file mode 100644 index 0000000..9b6a57e --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/weak_dylib/Makefile b/ld64/unit-tests/test-cases/weak_dylib/Makefile index 0a00a39..6fd999e 100644 --- a/ld64/unit-tests/test-cases/weak_dylib/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/weak_dylib/data.c b/ld64/unit-tests/test-cases/weak_dylib/data.c new file mode 100644 index 0000000..98cd752 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/weak_import-addend/Makefile b/ld64/unit-tests/test-cases/weak_import-addend/Makefile new file mode 100644 index 0000000..898424b --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/weak_import-addend/test.s b/ld64/unit-tests/test-cases/weak_import-addend/test.s new file mode 100644 index 0000000..fb39453 --- /dev/null +++ b/ld64/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/ld64/unit-tests/test-cases/weak_import-local/Makefile b/ld64/unit-tests/test-cases/weak_import-local/Makefile index 4d21410..bc94bd0 100644 --- a/ld64/unit-tests/test-cases/weak_import-local/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/weak_import3/Makefile b/ld64/unit-tests/test-cases/weak_import3/Makefile index 98a2779..2c8806d 100644 --- a/ld64/unit-tests/test-cases/weak_import3/Makefile +++ b/ld64/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/ld64/unit-tests/test-cases/zero-fill3/Makefile b/ld64/unit-tests/test-cases/zero-fill3/Makefile index 6266019..d16b9d0 100644 --- a/ld64/unit-tests/test-cases/zero-fill3/Makefile +++ b/ld64/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: From cb63a9987adf30b9cc99ec3016b3e824a2685b37 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:13:56 +0100 Subject: [PATCH 11/48] 128-2 --- ld64/ChangeLog | 84 +++ ld64/doc/man/man1/ld.1 | 4 +- ld64/ld64.xcodeproj/project.pbxproj | 20 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 10 +- ld64/src/ld/HeaderAndLoadCommands.hpp | 68 +- ld64/src/ld/InputFiles.cpp | 7 +- ld64/src/ld/LinkEdit.hpp | 40 -- ld64/src/ld/LinkEditClassic.hpp | 624 ----------------- ld64/src/ld/Options.cpp | 222 +++--- ld64/src/ld/Options.h | 9 +- ld64/src/ld/OutputFile.cpp | 260 +------ ld64/src/ld/OutputFile.h | 6 +- ld64/src/ld/Resolver.cpp | 52 +- ld64/src/ld/Resolver.h | 1 - ld64/src/ld/SymbolTable.cpp | 68 +- ld64/src/ld/ld.cpp | 163 +++-- ld64/src/ld/ld.hpp | 7 - ld64/src/ld/parsers/archive_file.cpp | 16 - ld64/src/ld/parsers/lto_file.cpp | 20 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 62 -- .../src/ld/parsers/macho_relocatable_file.cpp | 634 +----------------- ld64/src/ld/passes/branch_island.cpp | 54 +- ld64/src/ld/passes/dtrace_dof.cpp | 6 - ld64/src/ld/passes/got.cpp | 17 +- ld64/src/ld/passes/objc.cpp | 11 - .../ld/passes/{order_file.cpp => order.cpp} | 114 ++-- ld64/src/ld/passes/{order_file.h => order.h} | 12 +- ld64/src/ld/passes/stubs/stub_ppc_classic.hpp | 190 ------ ld64/src/ld/passes/stubs/stubs.cpp | 15 +- ld64/src/other/ObjectDump.cpp | 42 +- ld64/src/other/dyldinfo.cpp | 2 - ld64/src/other/machochecker.cpp | 3 - ld64/src/other/rebase.cpp | 8 +- ld64/src/other/unwinddump.cpp | 2 - .../test-cases/archive-init-order/Makefile | 48 ++ .../test-cases/archive-init-order/bar.c | 4 + .../test-cases/archive-init-order/bar2.c | 4 + .../test-cases/archive-init-order/bar3.c | 4 + .../test-cases/archive-init-order/foo.c | 4 + .../test-cases/archive-init-order/foo2.c | 4 + .../test-cases/archive-init-order/foo3.c | 4 + .../test-cases/archive-init-order/main.c | 41 ++ .../dead_strip-initializers/Makefile | 47 ++ .../dead_strip-initializers/main.cxx | 24 + .../dead_strip-initializers/other.cxx | 18 + .../unit-tests/test-cases/dso_handle/Makefile | 23 + ld64/unit-tests/test-cases/dso_handle/test.c | 16 + .../test-cases/fatal_warning/Makefile | 40 ++ .../test-cases/fatal_warning/main.c | 5 + .../test-cases/lto-dead_strip-unused/Makefile | 7 +- .../test-cases/order_file-zero-fill/Makefile | 21 + .../test-cases/order_file-zero-fill/foo.c | 4 + .../test-cases/order_file-zero-fill/main.c | 38 ++ .../order_file-zero-fill/main.expected | 7 + .../order_file-zero-fill/main.order | 7 + .../test-cases/tlv-dead_strip/Makefile | 2 +- 56 files changed, 893 insertions(+), 2332 deletions(-) rename ld64/src/ld/passes/{order_file.cpp => order.cpp} (87%) rename ld64/src/ld/passes/{order_file.h => order.h} (87%) delete mode 100644 ld64/src/ld/passes/stubs/stub_ppc_classic.hpp create mode 100644 ld64/unit-tests/test-cases/archive-init-order/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-init-order/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-init-order/bar2.c create mode 100644 ld64/unit-tests/test-cases/archive-init-order/bar3.c create mode 100644 ld64/unit-tests/test-cases/archive-init-order/foo.c create mode 100644 ld64/unit-tests/test-cases/archive-init-order/foo2.c create mode 100644 ld64/unit-tests/test-cases/archive-init-order/foo3.c create mode 100644 ld64/unit-tests/test-cases/archive-init-order/main.c create mode 100644 ld64/unit-tests/test-cases/dead_strip-initializers/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-initializers/main.cxx create mode 100644 ld64/unit-tests/test-cases/dead_strip-initializers/other.cxx create mode 100644 ld64/unit-tests/test-cases/dso_handle/Makefile create mode 100644 ld64/unit-tests/test-cases/dso_handle/test.c create mode 100644 ld64/unit-tests/test-cases/fatal_warning/Makefile create mode 100644 ld64/unit-tests/test-cases/fatal_warning/main.c create mode 100644 ld64/unit-tests/test-cases/order_file-zero-fill/Makefile create mode 100644 ld64/unit-tests/test-cases/order_file-zero-fill/foo.c create mode 100644 ld64/unit-tests/test-cases/order_file-zero-fill/main.c create mode 100644 ld64/unit-tests/test-cases/order_file-zero-fill/main.expected create mode 100644 ld64/unit-tests/test-cases/order_file-zero-fill/main.order diff --git a/ld64/ChangeLog b/ld64/ChangeLog index fd449c8..0514d7f 100644 --- a/ld64/ChangeLog +++ b/ld64/ChangeLog @@ -1,4 +1,88 @@ +-------- tagged ld64-128.1 + +2011-09-13 Nick Kledzik + + Enable dyld to be put into the dyld shared cache + +2011-09-13 Nick Kledzik + + Fix "using ld_classic" warning for i386 kexts + +2011-09-13 Nick Kledzik + + LTO many have eliminated need for some undefines + +-------- tagged ld64-128 + +2011-09-08 Nick Kledzik + + Rework 8924157 fix to sort all sections + +2011-09-07 Nick Kledzik + + ER: -current_version should allow 64-bit a.b.c.d.e tuple + +2011-09-06 Nick Kledzik + + -mdynamic-no-pic broke with movw of weak thumb symbol + +2011-09-01 Nick Kledzik + + Turn crash into a warning if __dso_handle is defined in user code + Added test case: unit-tests/test-cases/dso_handle + +2011-08-31 Nick Kledzik + + Add -fatal_warnings + +2011-08-30 Nick Kledzik + + Support .weak_def_can_be_hidden via LTO interface + +2011-08-29 Nick Kledzik + + improve performance of zerofill->data ordering + +2011-08-29 Nick Kledzik + + Implement -print_statistics + +2011-08-25 Nick Kledzik + + check for overlaps between pinned segments and regular segments + +2011-08-23 Nick Kledzik + + do got elimination more aggressively in static and preload mode + +2011-08-22 Nick Kledzik + + enable __dso_handle in -preload + +2011-08-22 Nick Kledzik + + fix section$end to sort to end of __mod_init_func section + +2011-08-18 Nick Kledzik + + __constructor section removed with -dead_strip + +2011-08-11 Nick Kledzik + + remove ld support for PowerPC + +2011-08-11 Nick Kledzik + + Fix spurious -segaddr alignment warning + +-------- tagged ld64-127.3 + +2011-08-31 Nick Kledzik + + [regression] C++ Initializers from archives not sorted + Added test case: unit-tests/test-cases/archive-init-order + -------- tagged ld64-127.2 2011-08-15 Nick Kledzik diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index 03f257a..dc2a501 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -515,6 +515,8 @@ Prints the version of the linker. 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 behavior and allows instructions on any page to be executed. +.It Fl fatal_warnings +Causes the linker to exit with a non-zero value if any warnings were emitted. .It Fl no_eh_labels Normally in -r mode, the linker produces .eh labels on all FDEs in the __eh_frame section. This option suppresses those labels. Those labels are not needed by the Mac OS X 10.6 @@ -576,7 +578,7 @@ Specifies the minimum space for future expansion of the load commands. Only use install_name_tool to alter the load commands later. Size is a hexadecimal number. .It Fl headerpad_max_install_names Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. -Only useful if intend to run install_name_tool to alter the load commands later. Size is a hexadecimal number. +Only useful if intend to run install_name_tool to alter the load commands later. .It Fl bind_at_load Sets a bit in the mach header of the resulting binary which tells dyld to bind all symbols when the binary is loaded, rather than lazily. .It Fl force_flat_namespace diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index ad73394..65f20a7 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -49,7 +49,7 @@ F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; F98498A310AE2159009E9878 /* compact_unwind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA963310A2545C0097A440 /* compact_unwind.cpp */; }; F98498A410AE2159009E9878 /* got.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AB1063107D380700E54C9E /* got.cpp */; }; - F9849E3610B38EF5009E9878 /* order_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9849E3410B38EF5009E9878 /* order_file.cpp */; }; + F9849E3610B38EF5009E9878 /* order.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9849E3410B38EF5009E9878 /* order.cpp */; }; F984A38210BB4B0D009E9878 /* branch_island.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F984A38010BB4B0D009E9878 /* branch_island.cpp */; }; F989D30D106826020014B60C /* OutputFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F989D30B106826020014B60C /* OutputFile.cpp */; }; F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */; }; @@ -251,9 +251,8 @@ F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = ""; }; F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; - F984963310AB9318009E9878 /* stub_ppc_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_ppc_classic.hpp; sourceTree = ""; }; - F9849E3410B38EF5009E9878 /* order_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = order_file.cpp; sourceTree = ""; }; - F9849E3510B38EF5009E9878 /* order_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = order_file.h; sourceTree = ""; }; + F9849E3410B38EF5009E9878 /* order.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = order.cpp; sourceTree = ""; }; + F9849E3510B38EF5009E9878 /* order.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = order.h; sourceTree = ""; }; F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm_classic.hpp; sourceTree = ""; }; F984A38010BB4B0D009E9878 /* branch_island.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_island.cpp; sourceTree = ""; }; F984A38110BB4B0D009E9878 /* branch_island.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_island.h; sourceTree = ""; }; @@ -399,8 +398,8 @@ F984A38110BB4B0D009E9878 /* branch_island.h */, F9AA44DA1294885F00CB8390 /* branch_shim.cpp */, F9AA44DB1294885F00CB8390 /* branch_shim.h */, - F9849E3410B38EF5009E9878 /* order_file.cpp */, - F9849E3510B38EF5009E9878 /* order_file.h */, + F9849E3410B38EF5009E9878 /* order.cpp */, + F9849E3510B38EF5009E9878 /* order.h */, F9BA963310A2545C0097A440 /* compact_unwind.cpp */, F9BA963410A2545C0097A440 /* compact_unwind.h */, F9AA67B410570C41003E3539 /* dtrace_dof.h */, @@ -432,7 +431,6 @@ F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */, F989D0391062E6350014B60C /* stub_x86_64.hpp */, F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */, - F984963310AB9318009E9878 /* stub_ppc_classic.hpp */, ); path = stubs; sourceTree = ""; @@ -702,7 +700,7 @@ ); 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"; + shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\tfi\nfi\n\n"; showEnvVarsInLog = 0; }; F9E8DB4D11921594007B4D6A /* make config.h */ = { @@ -745,7 +743,7 @@ F98498A310AE2159009E9878 /* compact_unwind.cpp in Sources */, F98498A410AE2159009E9878 /* got.cpp in Sources */, F9BA955E10A233000097A440 /* huge.cpp in Sources */, - F9849E3610B38EF5009E9878 /* order_file.cpp in Sources */, + F9849E3610B38EF5009E9878 /* order.cpp in Sources */, F984A38210BB4B0D009E9878 /* branch_island.cpp in Sources */, F9A4DB9110F816FF00BD8423 /* objc.cpp in Sources */, F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */, @@ -873,7 +871,7 @@ ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEAD_CODE_STRIPPING = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -928,6 +926,7 @@ ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1106,6 +1105,7 @@ ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index f5f073b..f4d136f 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -1279,8 +1278,13 @@ class macho_version_min_command { uint32_t version() const INLINE { return fields.version; } void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } - uint32_t reserved() const INLINE { return fields.reserved; } - void set_reserved(uint32_t value) INLINE { E::set32(fields.reserved, value); } +#ifdef LC_SOURCE_VERSION + uint32_t sdk() const INLINE { return fields.sdk; } + void set_sdk(uint32_t value) INLINE { E::set32(fields.sdk, value); } +#else + uint32_t sdk() const INLINE { return fields.reserved; } + void set_sdk(uint32_t value) INLINE { E::set32(fields.reserved, value); } +#endif typedef typename P::E E; private: diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 395fc99..903a2bf 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -519,33 +519,15 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const return bits; } -template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } -template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } -template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_POWERPC; } -template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_POWERPC64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_I386; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_X86_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM; } -template <> -uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const -{ - return _state.cpuSubType; -} - -template <> -uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const -{ - if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) - return (CPU_SUBTYPE_POWERPC_ALL | 0x80000000); - else - return CPU_SUBTYPE_POWERPC_ALL; -} template <> uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const @@ -973,7 +955,7 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDylibIDLoadCommand(uint8_t* p) const cmd->set_cmdsize(sz); cmd->set_name_offset(); cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses - cmd->set_current_version(_options.currentVersion()); + cmd->set_current_version(_options.currentVersion32()); cmd->set_compatibility_version(_options.compatibilityVersion()); strcpy((char*)&p[sizeof(macho_dylib_command

)], _options.installPath()); return p + sz; @@ -1024,62 +1006,18 @@ uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const cmd->set_cmd(LC_VERSION_MIN_MACOSX); cmd->set_cmdsize(sizeof(macho_version_min_command

)); cmd->set_version((uint32_t)macVersion); - cmd->set_reserved(0); + cmd->set_sdk(0); } else { cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); cmd->set_cmdsize(sizeof(macho_version_min_command

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

); } -template <> -uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const -{ - return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 -} - - -template <> -uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const -{ - assert(_state.entryPoint != NULL); - pint_t start = _state.entryPoint->finalAddress(); - macho_thread_command* cmd = (macho_thread_command*)p; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(threadLoadCommandSize()); - cmd->set_flavor(1); // PPC_THREAD_STATE - cmd->set_count(40); // PPC_THREAD_STATE_COUNT; - cmd->set_thread_register(0, start); - if ( _options.hasCustomStack() ) - cmd->set_thread_register(3, _options.customStackAddr()); // r1 - return p + threadLoadCommandSize(); -} - -template <> -uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const -{ - return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 -} - -template <> -uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const -{ - assert(_state.entryPoint != NULL); - pint_t start = _state.entryPoint->finalAddress(); - macho_thread_command* cmd = (macho_thread_command*)p; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(threadLoadCommandSize()); - cmd->set_flavor(5); // PPC_THREAD_STATE64 - cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; - cmd->set_thread_register(0, start); - if ( _options.hasCustomStack() ) - cmd->set_thread_register(3, _options.customStackAddr()); // r1 - return p + threadLoadCommandSize(); -} template <> uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 84a2297..7b05a96 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -627,12 +627,8 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) // no thin .o files found, so default to same architecture this tool was built as warning("-arch not specified"); -#if __ppc__ - opts.setArchitecture(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); -#elif __i386__ +#if __i386__ opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL); -#elif __ppc64__ - opts.setArchitecture(CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL); #elif __x86_64__ opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL); #elif __arm__ @@ -811,6 +807,7 @@ bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const case Options::kPreload: // add implicit __mh_preload_header label handler.doAtom(DSOHandleAtom::_s_atomPreload); + handler.doAtom(DSOHandleAtom::_s_atomAll); break; case Options::kObjectFile: handler.doAtom(DSOHandleAtom::_s_atomObjectFile); diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index bf9026b..7bca2e3 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -1063,7 +1063,6 @@ class SplitSegInfoAtom : public LinkEditAtom 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; @@ -1146,36 +1145,6 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki } -template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const -{ - switch (kind) { - case ld::Fixup::kindStorePPCPicHigh16AddLow: - _ppcHi16Locations.push_back(address); - break; - case ld::Fixup::kindStoreBigEndian32: - _32bitPointerLocations.push_back(address); - break; - default: - warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); - break; - } -} - - -template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const -{ - switch (kind) { - case ld::Fixup::kindStorePPCPicHigh16AddLow: - _ppcHi16Locations.push_back(address); - break; - default: - warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); - break; - } -} - template void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& locations) const @@ -1231,14 +1200,6 @@ void SplitSegInfoAtom::encode() const this->_encodedData.append_byte(0); // terminator } - if ( _ppcHi16Locations.size() != 0 ) { - this->_encodedData.append_byte(3); - //fprintf(stderr, "type 3:\n"); - std::sort(_ppcHi16Locations.begin(), _ppcHi16Locations.end()); - this->uleb128EncodeAddresses(_ppcHi16Locations); - this->_encodedData.append_byte(0); // terminator - } - if ( _thumbLo16Locations.size() != 0 ) { this->_encodedData.append_byte(5); //fprintf(stderr, "type 5:\n"); @@ -1286,7 +1247,6 @@ void SplitSegInfoAtom::encode() const // clean up temporaries _32bitPointerLocations.clear(); _64bitPointerLocations.clear(); - _ppcHi16Locations.clear(); } diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 1429d95..7fbc755 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -798,52 +798,6 @@ void LocalRelocationsAtom::addPointerReloc(uint64_t addr, uint32_t symNum) template void LocalRelocationsAtom::addTextReloc(uint64_t addr, ld::Fixup::Kind kind, uint64_t targetAddr, uint32_t symNum) { - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - switch ( kind ) { - case ld::Fixup::kindStorePPCAbsLow14: - case ld::Fixup::kindStorePPCAbsLow16: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( _options.outputSlidable() ) { - reloc1.set_r_address(addr); - reloc1.set_r_symbolnum(symNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - reloc2.set_r_address(targetAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - _relocs.push_back(reloc1); - _relocs.push_back(reloc2); - } - break; - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - case ld::Fixup::kindStorePPCAbsHigh16: - if ( _options.outputSlidable() ) { - reloc1.set_r_address(addr); - reloc1.set_r_symbolnum(symNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); - reloc2.set_r_address(targetAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - _relocs.push_back(reloc1); - _relocs.push_back(reloc2); - } - break; - default: - break; - } } @@ -967,9 +921,7 @@ uint64_t ExternalRelocationsAtom::size() const template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return GENERIC_RELOC_VANILLA; } -template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return PPC_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X86_64_RELOC_UNSIGNED; } -template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return PPC_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::callReloc() { return X86_64_RELOC_BRANCH; } @@ -1664,582 +1616,6 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } -template <> -void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, - const Entry& entry, std::vector >& relocs) -{ - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; - bool external = entry.toTargetUsesExternalReloc; - uint32_t symbolNum = sectSymNum(external, entry.toTarget); - bool fromExternal = false; - uint32_t fromSymbolNum = 0; - if ( entry.fromTarget != NULL ) { - fromExternal = entry.fromTargetUsesExternalReloc; - fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); - } - uint32_t toAddr; - uint32_t fromAddr; - - switch ( entry.kind ) { - - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_BR24); - } - relocs.push_back(reloc1); - break; - - case ld::Fixup::kindStorePPCBranch14: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_BR14); - } - relocs.push_back(reloc1); - break; - - case ld::Fixup::kindStoreBigEndian32: - case ld::Fixup::kindStoreTargetAddressBigEndian32: - if ( entry.fromTarget != NULL ) { - // this is a pointer-diff - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(PPC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - if ( entry.toTarget == entry.inAtom ) - 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); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( entry.fromTarget == entry.inAtom ) { - if ( entry.fromAddend > entry.fromTarget->size() ) - sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); - else - sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); - } - else - sreloc2->set_r_value(entry.fromTarget->finalAddress()); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - } - else { - // regular pointer - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - relocs.push_back(reloc1); - } - break; - - case ld::Fixup::kindStorePPCAbsLow14: - case ld::Fixup::kindStorePPCAbsLow16: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - } - if ( external ) - reloc2.set_r_address(entry.toAddend >> 16); - else - reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCAbsHigh16: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HI16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_HI16); - } - if ( external ) - reloc2.set_r_address(entry.toAddend & 0x0000FFFF); - else - reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_HA16); - } - if ( external ) - reloc2.set_r_address(entry.toAddend & 0x0000FFFF); - else - reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCPicLow14: - case ld::Fixup::kindStorePPCPicLow16: - fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; - toAddr = entry.toTarget->finalAddress() + entry.toAddend; - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCPicHigh16AddLow: - fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; - toAddr = entry.toTarget->finalAddress() + entry.toAddend; - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - default: - assert(0 && "need to handle -r reloc"); - - } -} - -template <> -void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, - const Entry& entry, std::vector >& relocs) -{ - macho_relocation_info

reloc1; - macho_relocation_info

reloc2; - macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; - macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; - uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; - bool external = entry.toTargetUsesExternalReloc; - uint32_t symbolNum = sectSymNum(external, entry.toTarget); - bool fromExternal = false; - uint32_t fromSymbolNum = 0; - if ( entry.fromTarget != NULL ) { - fromExternal = entry.fromTargetUsesExternalReloc; - fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); - } - uint32_t toAddr; - uint32_t fromAddr; - - switch ( entry.kind ) { - - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_BR24); - } - relocs.push_back(reloc1); - break; - - case ld::Fixup::kindStorePPCBranch14: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_BR14); - } - relocs.push_back(reloc1); - break; - - case ld::Fixup::kindStoreBigEndian32: - case ld::Fixup::kindStoreTargetAddressBigEndian32: - if ( entry.fromTarget != NULL ) { - // this is a pointer-diff - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(PPC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - if ( entry.toTarget == entry.inAtom ) - 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); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( entry.fromTarget == entry.inAtom ) { - if ( entry.fromAddend > entry.fromTarget->size() ) - sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); - else - sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); - } - else - sreloc2->set_r_value(entry.fromTarget->finalAddress()); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - } - else { - // regular pointer - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - relocs.push_back(reloc1); - } - break; - - case ld::Fixup::kindStoreBigEndian64: - case ld::Fixup::kindStoreTargetAddressBigEndian64: - if ( entry.fromTarget != NULL ) { - // this is a pointer-diff - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(3); - if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(PPC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - if ( entry.toTarget == entry.inAtom ) - 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(3); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( entry.fromTarget == entry.inAtom ) { - if ( entry.fromAddend > entry.fromTarget->size() ) - sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); - else - sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); - } - else - sreloc2->set_r_value(entry.fromTarget->finalAddress()); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - } - else { - // regular pointer - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(3); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(3); - reloc1.set_r_extern(external); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - relocs.push_back(reloc1); - } - break; - - case ld::Fixup::kindStorePPCAbsLow14: - case ld::Fixup::kindStorePPCAbsLow16: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - } - if ( external ) - reloc2.set_r_address(entry.toAddend >> 16); - else - reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCAbsHigh16: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HI16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_HI16); - } - if ( external ) - reloc2.set_r_address(entry.toAddend & 0x0000FFFF); - else - reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(PPC_RELOC_HA16); - } - if ( external ) - reloc2.set_r_address(entry.toAddend & 0x0000FFFF); - else - reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCPicLow14: - case ld::Fixup::kindStorePPCPicLow16: - fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; - toAddr = entry.toTarget->finalAddress() + entry.toAddend; - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - case ld::Fixup::kindStorePPCPicHigh16AddLow: - fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; - toAddr = entry.toTarget->finalAddress() + entry.toAddend; - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(entry.toTarget->finalAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - relocs.push_back(reloc1); - relocs.push_back(reloc2); - break; - - default: - assert(0 && "need to handle -r reloc"); - - } -} template void SectionRelocationsAtom::addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind kind, diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 3f5b054..629903f 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -48,11 +48,14 @@ static char crashreporterBuffer[crashreporterBufferSize]; char* __crashreporter_info__ = crashreporterBuffer; static bool sEmitWarnings = true; +static bool sFatalWarnings = false; static const char* sWarningsSideFilePath = NULL; static FILE* sWarningsSideFile = NULL; +static int sWarningsCount = 0; void warning(const char* format, ...) { + ++sWarningsCount; if ( sEmitWarnings ) { va_list list; if ( sWarningsSideFilePath != NULL ) { @@ -146,6 +149,10 @@ Options::~Options() } +bool Options::errorBecauseOfWarnings() const +{ + return (sFatalWarnings && (sWarningsCount > 0)); +} const char* Options::installPath() const @@ -475,50 +482,6 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fArchitecture = type; fSubArchitecture = subtype; switch ( type ) { - case CPU_TYPE_POWERPC: - switch ( subtype ) { - case CPU_SUBTYPE_POWERPC_750: - fArchitectureName = "ppc750"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_7400: - fArchitectureName = "ppc7400"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_7450: - fArchitectureName = "ppc7450"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_970: - fArchitectureName = "ppc970"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_ALL: - fArchitectureName = "ppc"; - fHasPreferredSubType = false; - break; - default: - assert(0 && "unknown ppc subtype"); - fArchitectureName = "ppc"; - break; - } - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); - #else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; - #endif - } - break; - case CPU_TYPE_POWERPC64: - fArchitectureName = "ppc64"; - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - warning("-macosx_version_min not specificed, assuming 10.5"); - fMacVersionMin = ld::mac10_5; - } - break; case CPU_TYPE_I386: fArchitectureName = "i386"; if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { @@ -583,15 +546,7 @@ void Options::parseArch(const char* arch) if ( arch == NULL ) throw "-arch must be followed by an architecture string"; fArchitectureName = arch; - if ( strcmp(arch, "ppc") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; - } - else if ( strcmp(arch, "ppc64") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC64; - fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; - } - else if ( strcmp(arch, "i386") == 0 ) { + if ( strcmp(arch, "i386") == 0 ) { fArchitecture = CPU_TYPE_I386; fSubArchitecture = CPU_SUBTYPE_I386_ALL; } @@ -603,27 +558,6 @@ void Options::parseArch(const char* arch) fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_ALL; } - // compatibility support for cpu-sub-types - else if ( strcmp(arch, "ppc750") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_750; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "ppc7400") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "ppc7450") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "ppc970") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_970; - fHasPreferredSubType = true; - } else { for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { if ( strcmp(t->subTypeName,arch) == 0 ) { @@ -1418,16 +1352,67 @@ uint32_t Options::parseProtection(const char* prot) } +// +// Parses number of form A[.B[.B[.D[.E]]]] into a uint64_t where the bits are a24.b10.c10.d10.e10 +// +uint64_t Options::parseVersionNumber64(const char* versionString) +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + uint64_t e = 0; + char* end; + a = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + b = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + c = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + d = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + e = strtoul(&end[1], &end, 10); + } + } + } + } + if ( (*end != '\0') || (a > 0xFFFFFF) || (b > 0x3FF) || (c > 0x3FF) || (d > 0x3FF) || (e > 0x3FF) ) + throwf("malformed 64-bit a.b.c.d.e version number: %s", versionString); + + return (a << 40) | ( b << 30 ) | ( c << 20 ) | ( d << 10 ) | e; +} + + +uint32_t Options::currentVersion32() const +{ + // warn if it does not fit into 32 bit vers number + uint32_t a = (fDylibCurrentVersion >> 40) & 0xFFFF; + uint32_t b = (fDylibCurrentVersion >> 30) & 0xFF; + uint32_t c = (fDylibCurrentVersion >> 20) & 0xFF; + uint64_t rep32 = ((uint64_t)a << 40) | ((uint64_t)b << 30) | ((uint64_t)c << 20); + if ( rep32 != fDylibCurrentVersion ) { + warning("truncating -current_version to fit in 32-bit space used by old mach-o format"); + a = (fDylibCurrentVersion >> 40) & 0xFFFFFF; + if ( a > 0xFFFF ) + a = 0xFFFF; + b = (fDylibCurrentVersion >> 30) & 0x3FF; + if ( b > 0xFF ) + b = 0xFF; + c = (fDylibCurrentVersion >> 20) & 0x3FF; + if ( c > 0xFF ) + c = 0xFF; + } + return (a << 16) | ( b << 8 ) | c; +} // // Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz // -// -uint32_t Options::parseVersionNumber(const char* versionString) +uint32_t Options::parseVersionNumber32(const char* versionString) { - unsigned long x = 0; - unsigned long y = 0; - unsigned long z = 0; + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; char* end; x = strtoul(versionString, &end, 10); if ( *end == '.' ) { @@ -1437,7 +1422,7 @@ uint32_t Options::parseVersionNumber(const char* versionString) } } if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed version number: %s", versionString); + throwf("malformed 32-bit x.y.z version number: %s", versionString); return (x << 16) | ( y << 8 ) | z; } @@ -1565,18 +1550,12 @@ void Options::parseOrderFile(const char* path, bool cstring) *last = '\0'; --last; } + // if there is an architecture prefix, only use this symbol it if matches current arch if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { - if ( fArchitecture == CPU_TYPE_POWERPC ) - symbolStart = &symbolStart[4]; - else - symbolStart = NULL; + symbolStart = NULL; } - // if there is an architecture prefix, only use this symbol it if matches current arch else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { - if ( fArchitecture == CPU_TYPE_POWERPC64 ) - symbolStart = &symbolStart[6]; - else - symbolStart = NULL; + symbolStart = NULL; } else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { if ( fArchitecture == CPU_TYPE_I386 ) @@ -1860,14 +1839,14 @@ void Options::parse(int argc, const char* argv[]) const char* vers = argv[++i]; if ( vers == NULL ) throw "-dylib_compatibility_version missing "; - fDylibCompatVersion = parseVersionNumber(vers); + fDylibCompatVersion = parseVersionNumber32(vers); } else if ( (strcmp(arg, "-dylib_current_version") == 0) || (strcmp(arg, "-current_version") == 0)) { const char* vers = argv[++i]; if ( vers == NULL ) throw "-dylib_current_version missing "; - fDylibCurrentVersion = parseVersionNumber(vers); + fDylibCurrentVersion = parseVersionNumber64(vers); } else if ( strcmp(arg, "-sectorder") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) @@ -2127,9 +2106,9 @@ void Options::parse(int argc, const char* argv[]) if ( (seg.name == NULL) || (argv[i+1] == NULL) ) throw "-segaddr missing segName Adddress"; seg.address = parseAddress(argv[++i]); - uint64_t temp = seg.address & (-4096); // page align - if ( (seg.address != temp) ) - warning("-segaddr %s not page aligned, rounding down", seg.name); + uint64_t temp = ((seg.address+fSegmentAlignment-1) & (-fSegmentAlignment)); + if ( seg.address != temp ) + warning("-segaddr %s not %lld byte aligned", seg.name, fSegmentAlignment); fCustomSegmentAddresses.push_back(seg); } // ??? Deprecate when we deprecate split-seg. @@ -2313,6 +2292,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-w") == 0 ) { // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-fatal_warnings") == 0 ) { + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { fErrorOnOtherArchFiles = true; } @@ -2787,6 +2769,9 @@ void Options::buildSearchPaths(int argc, const char* argv[]) else if ( strcmp(argv[i], "-w") == 0 ) { sEmitWarnings = false; } + else if ( strcmp(argv[i], "-fatal_warnings") == 0 ) { + sFatalWarnings = true; + } } int standardLibraryPathsStartIndex = libraryPaths.size(); int standardFrameworkPathsStartIndex = frameworkPaths.size(); @@ -3032,7 +3017,6 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - case CPU_TYPE_POWERPC: if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); @@ -3073,12 +3057,6 @@ void Options::reconfigureDefaults() fMacVersionMin = ld::mac10_4; } break; - case CPU_TYPE_POWERPC64: - if ( fMacVersionMin < ld::mac10_4 ) { - //warning("-macosx_version_min should be 10.4 or later for ppc64"); - fMacVersionMin = ld::mac10_4; - } - break; case CPU_TYPE_X86_64: if ( fMacVersionMin < ld::mac10_4 ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); @@ -3107,7 +3085,6 @@ void Options::reconfigureDefaults() break; } // else use object file - case CPU_TYPE_POWERPC: case CPU_TYPE_I386: // use .o files fOutputKind = kObjectFile; @@ -3150,9 +3127,8 @@ void Options::reconfigureDefaults() // split segs only allowed for dylibs if ( fSplitSegs ) { - // split seg only supported for ppc, i386, and arm. + // split seg only supported for i386, and arm. switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: case CPU_TYPE_I386: if ( fOutputKind != Options::kDynamicLibrary ) fSplitSegs = false; @@ -3179,11 +3155,9 @@ void Options::reconfigureDefaults() // set too-large size switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: case CPU_TYPE_I386: fMaxAddress = 0xFFFFFFFF; break; - case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: break; case CPU_TYPE_ARM: @@ -3217,7 +3191,6 @@ void Options::reconfigureDefaults() // disable prebinding depending on arch and min OS version if ( fPrebind ) { switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: case CPU_TYPE_I386: if ( fMacVersionMin == ld::mac10_4 ) { // in 10.4 only split seg dylibs are prebound @@ -3251,7 +3224,6 @@ void Options::reconfigureDefaults() } } break; - case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: fPrebind = false; break; @@ -3287,17 +3259,17 @@ void Options::reconfigureDefaults() || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) fSharedRegionEligible = true; } - + else if ( fOutputKind == Options::kDyld ) { + // Enable dyld to be put into the dyld shared cache + fSharedRegionEligible = true; + } + // 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 - if ( fMacVersionMin <= ld::mac10_5 ) - fNeedsModuleTable = true; - break; case CPU_TYPE_ARM: if ( fPrebind ) fNeedsModuleTable = true; // redo_prebinding requires a module table @@ -3329,8 +3301,6 @@ void Options::reconfigureDefaults() break; } break; - case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: case CPU_TYPE_ARM: fAddCompactUnwindEncoding = false; fRemoveDwarfUnwindIfCompactExists = false; @@ -3402,8 +3372,6 @@ void Options::reconfigureDefaults() if ( !minOS(ld::mac10_6, ld::iOS_3_1) ) fMakeCompressedDyldInfo = false; break; - case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: default: fMakeCompressedDyldInfo = false; } @@ -3629,12 +3597,10 @@ void Options::checkIllegalOptionCombinations() if ( fStackAddr != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: case CPU_TYPE_ARM: if ( fStackAddr > 0xFFFFFFFF ) throw "-stack_addr must be < 4G for 32-bit processes"; break; - case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: break; } @@ -3648,7 +3614,6 @@ void Options::checkIllegalOptionCombinations() if ( fStackSize != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: if ( fStackSize > 0xFFFFFFFF ) throw "-stack_size must be < 4G for 32-bit processes"; if ( fStackAddr == 0 ) { @@ -3664,7 +3629,6 @@ void Options::checkIllegalOptionCombinations() fStackAddr = 0x2F000000; if ( fStackAddr > 0x30000000) throw "-stack_addr must be < 0x30000000 for arm"; - case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: if ( fStackAddr == 0 ) { fStackAddr = 0x00007FFF5C000000LL; @@ -3774,7 +3738,6 @@ void Options::checkIllegalOptionCombinations() if ( fObjCABIVersion2Override ) alterObjC1ClassNamesToObjC2 = true; break; - case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: alterObjC1ClassNamesToObjC2 = true; @@ -3868,18 +3831,10 @@ void Options::checkIllegalOptionCombinations() // zero page size not specified on command line, set default switch (fArchitecture) { case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: case CPU_TYPE_ARM: // first 4KB for 32-bit architectures fZeroPageSize = 0x1000; break; - case CPU_TYPE_POWERPC64: - // first 4GB for ppc64 on 10.5 - if ( fMacVersionMin >= ld::mac10_5 ) - fZeroPageSize = 0x100000000ULL; - else - fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page - break; case CPU_TYPE_X86_64: // first 4GB for x86_64 on all OS's fZeroPageSize = 0x100000000ULL; @@ -4053,6 +4008,7 @@ void Options::checkForClassic(int argc, const char* argv[]) // ld_classic does not understand this option, so remove it for(int j=i; j < argc; ++j) argv[j] = argv[j+1]; + warning("using ld_classic"); this->gotoClassicLinker(argc-1, argv); } else if ( strcmp(arg, "-o") == 0 ) { @@ -4070,7 +4026,6 @@ void Options::checkForClassic(int argc, const char* argv[]) if( archFound ) { switch ( fArchitecture ) { case CPU_TYPE_I386: - case CPU_TYPE_POWERPC: if ( (staticFound || kextFound) && !newLinker ) { // this environment variable will disable use of ld_classic for -static links if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { @@ -4082,15 +4037,16 @@ void Options::checkForClassic(int argc, const char* argv[]) } else { // work around for VSPTool - if ( staticFound ) + if ( staticFound ) { + warning("using ld_classic"); this->gotoClassicLinker(argc, 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) { diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index d9becf3..5a8a7e0 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -156,7 +156,8 @@ class Options bool bindAtLoad() const { return fBindAtLoad; } NameSpace nameSpace() const { return fNameSpace; } const char* installPath() const; // only for kDynamicLibrary - uint32_t currentVersion() const { return fDylibCurrentVersion; } // only for kDynamicLibrary + uint64_t currentVersion() const { return fDylibCurrentVersion; } // only for kDynamicLibrary + uint32_t currentVersion32() const; // only for kDynamicLibrary uint32_t compatibilityVersion() const { return fDylibCompatVersion; } // only for kDynamicLibrary const char* entryName() const { return fEntryName; } // only for kDynamicExecutable or kStaticExecutable const char* executablePath(); @@ -293,6 +294,7 @@ class Options bool forceNotWeak(const char* symbolName) const; bool forceWeakNonWildCard(const char* symbolName) const; bool forceNotWeakNonWildcard(const char* symbolName) const; + bool errorBecauseOfWarnings() const; private: class CStringEquals @@ -335,7 +337,8 @@ class Options FileInfo findFramework(const char* rootName, const char* suffix); bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const; - uint32_t parseVersionNumber(const char*); + uint64_t parseVersionNumber64(const char*); + uint32_t parseVersionNumber32(const char*); void parseSectionOrderFile(const char* segment, const char* section, const char* path); void parseOrderFile(const char* path, bool cstring); void addSection(const char* segment, const char* section, const char* path); @@ -384,7 +387,7 @@ class Options bool fDeadStrip; NameSpace fNameSpace; uint32_t fDylibCompatVersion; - uint32_t fDylibCurrentVersion; + uint64_t fDylibCurrentVersion; const char* fDylibInstallName; const char* fFinalName; const char* fEntryName; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 65e8b8c..f217eb0 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -516,6 +516,8 @@ void OutputFile::assignFileOffsets(ld::Internal& state) // second pass, assign section address to sections in segments that are contiguous with previous segment address = floatingAddressStart; lastSegName = ""; + ld::Internal::FinalSection* overlappingFixedSection = NULL; + ld::Internal::FinalSection* overlappingFlowSection = NULL; if ( log ) fprintf(stderr, "Regular layout segments:\n"); for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; @@ -550,6 +552,25 @@ void OutputFile::assignFileOffsets(ld::Internal& state) && (_options.outputKind() != Options::kStaticExecutable) ) throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", sect->sectionName(), address, sect->size); + + // sanity check it does not overlap a fixed address segment + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* otherSect = *sit; + if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) + continue; + if ( sect->address > otherSect->address ) { + if ( (otherSect->address+otherSect->size) > sect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + else { + if ( (sect->address+sect->size) > otherSect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + } if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", sect->address, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, @@ -558,6 +579,21 @@ void OutputFile::assignFileOffsets(ld::Internal& state) if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) address += sect->size; } + if ( overlappingFixedSection != NULL ) { + fprintf(stderr, "Section layout:\n"); + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->isSectionHidden() ) + continue; + fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", + sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + + } + throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", + overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), + overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); + } // third pass, assign section file offsets @@ -898,33 +934,6 @@ void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& sta } } -void OutputFile::rangeCheckPPCBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) -{ - const int64_t bl_eightMegLimit = 0x00FFFFFF; - if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("bl PPC branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); - } -} - -void OutputFile::rangeCheckPPCBranch14(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) -{ - const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; - if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("bcc PPC branch out of range (%lld max is +/-64KB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); - } -} @@ -956,7 +965,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: int64_t delta; uint32_t instruction; uint32_t newInstruction; - uint16_t instructionLowHalf; bool is_bl; bool is_blx; bool is_b; @@ -1145,41 +1153,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } set32LE(fixUpLocation, newInstruction); break; - case ld::Fixup::kindStorePPCBranch14: - delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); - rangeCheckPPCBranch14(delta, state, atom, fit); - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)delta & 0x0000FFFC); - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCPicLow14: - case ld::Fixup::kindStorePPCAbsLow14: - instruction = get32BE(fixUpLocation); - if ( (accumulator & 0x3) != 0 ) - throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)accumulator); - newInstruction = (instruction & 0xFFFF0003) | (accumulator & 0xFFFC); - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCAbsLow16: - case ld::Fixup::kindStorePPCPicLow16: - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0000) | (accumulator & 0xFFFF); - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - case ld::Fixup::kindStorePPCPicHigh16AddLow: - instructionLowHalf = (accumulator >> 16) & 0xFFFF; - if ( accumulator & 0x00008000 ) - ++instructionLowHalf; - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - set32BE(fixUpLocation, newInstruction); - break; - case ld::Fixup::kindStorePPCAbsHigh16: - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFFFF0000) | ((accumulator >> 16) & 0xFFFF); - set32BE(fixUpLocation, newInstruction); - break; case ld::Fixup::kindDtraceExtra: break; case ld::Fixup::kindStoreX86DtraceCallSiteNop: @@ -1202,18 +1175,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: fixUpLocation[3] = 0x90; // 1-byte nop } break; - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - if ( _options.outputKind() != Options::kObjectFile ) { - // change call site to a NOP - set32BE(fixUpLocation, 0x60000000); - } - break; - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: - if ( _options.outputKind() != Options::kObjectFile ) { - // change call site to a li r3,0 - set32BE(fixUpLocation, 0x38600000); - } - break; case ld::Fixup::kindStoreARMDtraceCallSiteNop: if ( _options.outputKind() != Options::kObjectFile ) { // change call site to a NOP @@ -1506,18 +1467,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, newInstruction); } break; - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - accumulator = addressOf(state, fit, &toTarget); - if ( fit->contentDetlaToAddendOnly ) - accumulator = 0; - // fall into kindStorePPCBranch24 case - case ld::Fixup::kindStorePPCBranch24: - delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); - rangeCheckPPCBranch24(delta, state, atom, fit); - instruction = get32BE(fixUpLocation); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)delta & 0x03FFFFFC); - set32BE(fixUpLocation, newInstruction); - break; } } } @@ -1525,10 +1474,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb) { switch ( _options.architecture() ) { - case CPU_TYPE_POWERPC: - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); - break; case CPU_TYPE_I386: case CPU_TYPE_X86_64: for (uint8_t* p=from; p < to; ++p) @@ -2002,54 +1947,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) return addPreloadLinkEdit(state); switch ( _options.architecture() ) { - case CPU_TYPE_POWERPC: - if ( _hasSectionRelocations ) { - _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); - sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); - } - if ( _hasDyldInfo ) { - _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); - rebaseSection = state.addAtom(*_rebasingInfoAtom); - - _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); - bindingSection = state.addAtom(*_bindingInfoAtom); - - _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); - weakBindingSection = state.addAtom(*_weakBindingInfoAtom); - - _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); - lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); - - _exportInfoAtom = new ExportInfoAtom(_options, state, *this); - exportSection = state.addAtom(*_exportInfoAtom); - } - if ( _hasLocalRelocations ) { - _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); - localRelocationsSection = state.addAtom(*_localRelocsAtom); - } - if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); - splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); - } - if ( _hasFunctionStartsInfo ) { - _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); - functionStartsSection = state.addAtom(*_functionStartsAtom); - } - if ( _hasSymbolTable ) { - _symbolTableAtom = new SymbolTableAtom(_options, state, *this); - symbolTableSection = state.addAtom(*_symbolTableAtom); - } - if ( _hasExternalRelocations ) { - _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); - externalRelocationsSection = state.addAtom(*_externalRelocsAtom); - } - if ( _hasSymbolTable ) { - _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); - indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); - _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); - stringPoolSection = state.addAtom(*_stringPoolAtom); - } - break; case CPU_TYPE_I386: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2194,54 +2091,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; - case CPU_TYPE_POWERPC64: - if ( _hasSectionRelocations ) { - _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); - sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); - } - if ( _hasDyldInfo ) { - _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); - rebaseSection = state.addAtom(*_rebasingInfoAtom); - - _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); - bindingSection = state.addAtom(*_bindingInfoAtom); - - _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); - weakBindingSection = state.addAtom(*_weakBindingInfoAtom); - - _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); - lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); - - _exportInfoAtom = new ExportInfoAtom(_options, state, *this); - exportSection = state.addAtom(*_exportInfoAtom); - } - if ( _hasLocalRelocations ) { - _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); - localRelocationsSection = state.addAtom(*_localRelocsAtom); - } - if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); - splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); - } - if ( _hasFunctionStartsInfo ) { - _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); - functionStartsSection = state.addAtom(*_functionStartsAtom); - } - if ( _hasSymbolTable ) { - _symbolTableAtom = new SymbolTableAtom(_options, state, *this); - symbolTableSection = state.addAtom(*_symbolTableAtom); - } - if ( _hasExternalRelocations ) { - _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); - externalRelocationsSection = state.addAtom(*_externalRelocsAtom); - } - if ( _hasSymbolTable ) { - _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); - indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); - _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); - stringPoolSection = state.addAtom(*_stringPoolAtom); - } - break; default: throw "unknown architecture"; } @@ -2262,14 +2111,6 @@ void OutputFile::addLoadCommands(ld::Internal& state) _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; - case CPU_TYPE_POWERPC: - _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); - headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); - break; - case CPU_TYPE_POWERPC64: - _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); - headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); - break; default: throw "unknown architecture"; } @@ -2425,18 +2266,12 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreARMLoad12: - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStorePPCBranch14: - case ld::Fixup::kindStorePPCPicLow14: - case ld::Fixup::kindStorePPCPicLow16: - case ld::Fixup::kindStorePPCPicHigh16AddLow: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: return true; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: return (_options.outputKind() != Options::kKextBundle); @@ -2485,7 +2320,6 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: return true; case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: @@ -2493,8 +2327,6 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: return (_options.outputKind() == Options::kObjectFile); default: break; @@ -3026,25 +2858,6 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio sect->hasLocalRelocs = true; } break; - case ld::Fixup::kindStorePPCAbsLow14: - case ld::Fixup::kindStorePPCAbsLow16: - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - case ld::Fixup::kindStorePPCAbsHigh16: - { - assert(target != NULL); - if ( target->definition() == ld::Atom::definitionProxy ) - throwf("half word text relocs not supported in %s", atom->name()); - if ( _options.outputSlidable() ) { - if ( inReadOnlySeg ) - noteTextReloc(atom, target); - uint32_t machoSectionIndex = (target->definition() == ld::Atom::definitionAbsolute) - ? R_ABS : target->machoSection(); - _localRelocsAtom->addTextReloc(relocAddress, fixupWithTarget->kind, - target->finalAddress(), machoSectionIndex); - sect->hasLocalRelocs = true; - } - } - break; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: if ( _options.outputKind() == Options::kKextBundle ) { assert(target != NULL); @@ -3228,7 +3041,6 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreX86PCRel32GOTLoad: case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreX86PCRel32GOT: - case ld::Fixup::kindStorePPCPicHigh16AddLow: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index 415d24b..8401716 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -65,7 +65,7 @@ class OutputFile uint32_t encryptedTextStartOffset() { return _encryptedTEXTstartOffset; } uint32_t encryptedTextEndOffset() { return _encryptedTEXTendOffset; } int compressedOrdinalForAtom(const ld::Atom* target); - + uint64_t fileSize() const { return _fileSize; } bool hasWeakExternalSymbols; @@ -210,10 +210,6 @@ class OutputFile const ld::Fixup* fixup); void rangeCheckThumbBranch22(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); - void rangeCheckPPCBranch24(int64_t delta, ld::Internal& state, const ld::Atom* atom, - const ld::Fixup* fixup); - void rangeCheckPPCBranch14(int64_t delta, ld::Internal& state, const ld::Atom* atom, - const ld::Fixup* fixup); uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); void dumpAtomsBySection(ld::Internal& state, bool); diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index a56cefe..2502820 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -297,26 +297,6 @@ void Resolver::buildAtomList() //_symbolTable.printStatistics(); } -unsigned int Resolver::ppcSubTypeIndex(uint32_t subtype) -{ - switch ( subtype ) { - case CPU_SUBTYPE_POWERPC_ALL: - return 0; - case CPU_SUBTYPE_POWERPC_750: - // G3 - return 1; - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - // G4 - return 2; - case CPU_SUBTYPE_POWERPC_970: - // G5 can run everything - return 3; - default: - throw "Unhandled PPC cpu subtype!"; - break; - } -} void Resolver::doFile(const ld::File& file) { @@ -363,29 +343,6 @@ void Resolver::doFile(const ld::File& file) // update cpu-sub-type cpu_subtype_t nextObjectSubType = file.cpuSubType(); switch ( _options.architecture() ) { - case CPU_TYPE_POWERPC: - // no checking when -force_cpusubtype_ALL is used - if ( _options.forceCpuSubtypeAll() ) - return; - if ( _options.preferSubArchitecture() ) { - // warn if some .o file is not compatible with desired output sub-type - if ( _options.subArchitecture() != nextObjectSubType ) { - if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_options.subArchitecture()) ) { - if ( !_inputFiles.inferredArch() ) - warning("cpu-sub-type of %s is not compatible with command line cpu-sub-type", file.path()); - _internal.cpuSubType = nextObjectSubType; - } - } - } - else { - // command line to linker just had -arch ppc - // figure out final sub-type based on sub-type of all .o files - if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_internal.cpuSubType) ) { - _internal.cpuSubType = nextObjectSubType; - } - } - break; - case CPU_TYPE_ARM: if ( _options.subArchitecture() != nextObjectSubType ) { if ( (_options.subArchitecture() == CPU_SUBTYPE_ARM_ALL) && _options.forceCpuSubtypeAll() ) { @@ -405,9 +362,6 @@ void Resolver::doFile(const ld::File& file) } break; - case CPU_TYPE_POWERPC64: - break; - case CPU_TYPE_I386: _internal.cpuSubType = CPU_SUBTYPE_I386_ALL; break; @@ -567,8 +521,6 @@ bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: case ld::Fixup::kindDtraceExtra: return true; default: @@ -770,7 +722,6 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() // but a archive loaded .o file may have a forward reference @@ -1078,7 +1029,8 @@ void Resolver::checkUndefines(bool force) break; } std::vector unresolvableUndefines; - if ( _options.deadCodeStrip() ) + // LTO many have eliminated need for some undefines + if ( _options.deadCodeStrip() || _haveLLVMObjs ) this->liveUndefines(unresolvableUndefines); else _symbolTable.undefines(unresolvableUndefines); diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 1202fc7..65e2006 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -94,7 +94,6 @@ class Resolver : public ld::File::AtomHandler void markLive(const ld::Atom& atom, WhyLiveBackChain* previous); bool isDtraceProbe(ld::Fixup::Kind kind); void liveUndefines(std::vector&); - static unsigned int ppcSubTypeIndex(uint32_t subtype); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index 2716ccf..66ff358 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -179,22 +179,32 @@ bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) } else { // existing not-weak, new is not-weak - if ( ignoreDuplicates ) { + if ( newAtom.section().type() == ld::Section::typeMachHeader ) { + warning("ignoring override of built-in symbol %s from %s", newAtom.name(), existingAtom->file()->path()); + useNew = true; + } + else if ( existingAtom->section().type() == ld::Section::typeMachHeader ) { + warning("ignoring override of built-in symbol %s from %s", newAtom.name(), newAtom.file()->path()); useNew = false; - static bool fullWarning = false; - if ( ! fullWarning ) { - warning("-dead_strip with lazy loaded static (library) archives " - "has resulted in a duplicate symbol. You can change your " - "source code to rename symbols to avoid the collision. " - "This will be an error in a future linker."); - fullWarning = true; - } - warning("duplicate symbol %s originally in %s now lazily loaded from %s", - SymbolTable::demangle(name), existingAtom->file()->path(), newAtom.file()->path()); - } + } else { - throwf("duplicate symbol %s in %s and %s", - SymbolTable::demangle(name), newAtom.file()->path(), existingAtom->file()->path()); + if ( ignoreDuplicates ) { + useNew = false; + static bool fullWarning = false; + if ( ! fullWarning ) { + warning("-dead_strip with lazy loaded static (library) archives " + "has resulted in a duplicate symbol. You can change your " + "source code to rename symbols to avoid the collision. " + "This will be an error in a future linker."); + fullWarning = true; + } + warning("duplicate symbol %s originally in %s now lazily loaded from %s", + SymbolTable::demangle(name), existingAtom->file()->path(), newAtom.file()->path()); + } + else { + throwf("duplicate symbol %s in %s and %s", + SymbolTable::demangle(name), newAtom.file()->path(), existingAtom->file()->path()); + } } } } @@ -222,17 +232,27 @@ bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) switch ( newAtom.definition() ) { case ld::Atom::definitionRegular: // replace existing tentative atom with regular one - checkVisibilityMismatch = true; - if ( newAtom.size() < existingAtom->size() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "being replaced by a real definition of size %llu from %s", - newAtom.name(), existingAtom->size(), existingAtom->file()->path(), - newAtom.size(), newAtom.file()->path()); + if ( newAtom.section().type() == ld::Section::typeMachHeader ) { + // silently replace tentative __dso_handle with real linker created symbol + useNew = true; + } + else if ( existingAtom->section().type() == ld::Section::typeMachHeader ) { + // silently replace tentative __dso_handle with real linker created symbol + useNew = false; } - 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()); + else { + checkVisibilityMismatch = true; + if ( newAtom.size() < existingAtom->size() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s", + newAtom.name(), existingAtom->size(), existingAtom->file()->path(), + newAtom.size(), newAtom.file()->path()); + } + if ( newAtom.section().type() == ld::Section::typeCode ) { + warning("for symbol %s tentative (data) defintion from %s is " + "being replaced by code from %s", newAtom.name(), existingAtom->file()->path(), + newAtom.file()->path()); + } } break; case ld::Atom::definitionTentative: diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 5b21c63..ce49e73 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.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@ * @@ -74,7 +74,7 @@ extern "C" double log2 ( double ); #include "passes/tlvp.h" #include "passes/huge.h" #include "passes/compact_unwind.h" -#include "passes/order_file.h" +#include "passes/order.h" #include "passes/branch_island.h" #include "passes/branch_shim.h" #include "passes/objc.h" @@ -87,14 +87,31 @@ extern "C" double log2 ( double ); #include "parsers/opaque_section_file.h" +struct PerformanceStatistics { + uint64_t startTool; + uint64_t startInputFileProcessing; + uint64_t startResolver; + uint64_t startDylibs; + uint64_t startPasses; + uint64_t startOutput; + uint64_t startDone; + vm_statistics_data_t vmStart; + vm_statistics_data_t vmEnd; +}; + + + + + class InternalState : public ld::Internal { public: - InternalState(const Options& opts) : _options(opts) { } + InternalState(const Options& opts) : _options(opts), _atomsOrderedInSections(false) { } virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); void sortSections(); + void markAtomsOrdered() { _atomsOrderedInSections = true; } virtual ~InternalState() {} private: @@ -134,6 +151,7 @@ class InternalState : public ld::Internal SectionInToOut _sectionInToFinalMap; const Options& _options; + bool _atomsOrderedInSections; }; ld::Section InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified); @@ -440,32 +458,28 @@ static void validateFixups(const ld::Atom& atom) ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) { ld::Internal::FinalSection* fs = this->getFinalSection(atom.section()); - - // When order file used on data, turn ordered zero fill symbols into zero data - switch ( atom.section().type() ) { - case ld::Section::typeZeroFill: - case ld::Section::typeTentativeDefs: - 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) ) { - // found in order file, move to __data section - fs = getFinalSection(InternalState::FinalSection::_s_DATA_data);\ - //fprintf(stderr, "moved %s to __data section\n", atom.name()); - break; - } - } - } - break; - default: - break; - } - //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs); #ifndef NDEBUG validateFixups(atom); #endif - fs->atoms.push_back(&atom); + if ( _atomsOrderedInSections ) { + // make sure this atom is placed before any trailing section$end$ atom + if ( (fs->atoms.size() > 1) && (fs->atoms.back()->contentType() == ld::Atom::typeSectionEnd) ) { + // last atom in section$end$ atom, insert before it + const ld::Atom* endAtom = fs->atoms.back(); + fs->atoms.pop_back(); + fs->atoms.push_back(&atom); + fs->atoms.push_back(endAtom); + } + else { + // not end atom, just append new atom + fs->atoms.push_back(&atom); + } + } + else { + // normal case + fs->atoms.push_back(&atom); + } return fs; } @@ -552,6 +566,49 @@ void InternalState::sortSections() } +static char* commatize(uint64_t in, char* out) +{ + char* result = out; + char rawNum[30]; + sprintf(rawNum, "%llu", in); + const int rawNumLen = strlen(rawNum); + for(int i=0; i < rawNumLen-1; ++i) { + *out++ = rawNum[i]; + if ( ((rawNumLen-i) % 3) == 1 ) + *out++ = ','; + } + *out++ = rawNum[rawNumLen-1]; + *out = '\0'; + return result; +} + +static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; + //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); + } + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; + uint32_t milliSeconds = milliSecondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%24s: % 4d.%d milliseconds (% 4d.%d%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percent = percentTimesTen/10; + fprintf(stderr, "%24s: % 4d.%d seconds (% 4d.%d%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + + static void getVMInfo(vm_statistics_data_t& info) { mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); @@ -564,23 +621,20 @@ static void getVMInfo(vm_statistics_data_t& info) int main(int argc, const char* argv[]) { -#if DEBUG - usleep(1000000); -#endif const char* archName = NULL; bool showArch = false; bool archInferred = false; try { - vm_statistics_data_t vmStart; - vm_statistics_data_t vmEnd; - getVMInfo(vmStart); - + PerformanceStatistics statistics; + statistics.startTool = mach_absolute_time(); + // create object to track command line arguments Options options(argc, argv); + InternalState state(options); - // gather stats + // gather vm stats if ( options.printStatistics() ) - getVMInfo(vmStart); + getVMInfo(statistics.vmStart); // update strings for error messages showArch = options.printArchPrefix(); @@ -588,46 +642,71 @@ int main(int argc, const char* argv[]) archInferred = (options.architecture() == 0); // open and parse input files + statistics.startInputFileProcessing = mach_absolute_time(); ld::tool::InputFiles inputFiles(options, &archName); // load and resolve all references - InternalState state(options); + statistics.startResolver = mach_absolute_time(); ld::tool::Resolver resolver(options, inputFiles, state); resolver.resolve(); // add dylibs used + statistics.startDylibs = mach_absolute_time(); inputFiles.dylibs(state); // do initial section sorting so passes have rough idea of the layout state.sortSections(); // run passes + statistics.startPasses = mach_absolute_time(); ld::passes::objc::doPass(options, state); ld::passes::stubs::doPass(options, state); ld::passes::huge::doPass(options, state); ld::passes::got::doPass(options, state); ld::passes::tlvp::doPass(options, state); ld::passes::dylibs::doPass(options, state); // must be after stubs and GOT passes - ld::passes::order_file::doPass(options, state); + ld::passes::order::doPass(options, state); + state.markAtomsOrdered(); ld::passes::branch_shim::doPass(options, state); // must be after stubs - ld::passes::branch_island::doPass(options, state); // must be after stubs and order_file pass + ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); - ld::passes::compact_unwind::doPass(options, state); // must be after order-file pass + ld::passes::compact_unwind::doPass(options, state); // must be after order pass // sort final sections state.sortSections(); // write output file + statistics.startOutput = mach_absolute_time(); ld::tool::OutputFile out(options); out.write(state); + statistics.startDone = mach_absolute_time(); // print statistics //mach_o::relocatable::printCounts(); if ( options.printStatistics() ) { - getVMInfo(vmEnd); - fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", vmEnd.pageins-vmStart.pageins, - vmEnd.pageouts-vmStart.pageouts, vmEnd.faults-vmStart.faults); - + getVMInfo(statistics.vmEnd); + uint64_t totalTime = statistics.startDone - statistics.startTool; + printTime("ld total time", totalTime, totalTime); + printTime(" option parsing time", statistics.startInputFileProcessing - statistics.startTool, totalTime); + printTime(" object file processing", statistics.startResolver - statistics.startInputFileProcessing,totalTime); + printTime(" resolve symbols", statistics.startDylibs - statistics.startResolver, totalTime); + printTime(" build atom list", statistics.startPasses - statistics.startDylibs, totalTime); + printTime(" passess", statistics.startOutput - statistics.startPasses, totalTime); + printTime(" write output", statistics.startDone - statistics.startOutput, totalTime); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", + statistics.vmEnd.pageins-statistics.vmStart.pageins, + statistics.vmEnd.pageouts-statistics.vmStart.pageouts, + statistics.vmEnd.faults-statistics.vmStart.faults); + char temp[40]; + fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", inputFiles._totalObjectLoaded, commatize(inputFiles._totalObjectSize, temp)); + fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", inputFiles._totalArchivesLoaded, commatize(inputFiles._totalArchiveSize, temp)); + fprintf(stderr, "processed %3u dylib files\n", inputFiles._totalDylibsLoaded); + fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(out.fileSize(), temp)); + } + // Would like linker warning to be build error. + if ( options.errorBecauseOfWarnings() ) { + fprintf(stderr, "ld: fatal warning(s) induced error (-fatal_warnings)\n"); + return 1; } } catch (const char* msg) { diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 96075a7..b615046 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -318,16 +318,11 @@ struct Fixup kindStoreARMLoad12, kindStoreARMLow16, kindStoreARMHigh16, kindStoreThumbLow16, kindStoreThumbHigh16, - // PowerPC specific store kinds - kindStorePPCBranch24, kindStorePPCBranch14, - kindStorePPCPicLow14, kindStorePPCPicLow16, kindStorePPCPicHigh16AddLow, - kindStorePPCAbsLow14, kindStorePPCAbsLow16, kindStorePPCAbsHigh16AddLow, kindStorePPCAbsHigh16, // dtrace probes kindDtraceExtra, kindStoreX86DtraceCallSiteNop, kindStoreX86DtraceIsEnableSiteClear, kindStoreARMDtraceCallSiteNop, kindStoreARMDtraceIsEnableSiteClear, kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, - kindStorePPCDtraceCallSiteNop, kindStorePPCDtraceIsEnableSiteClear, // lazy binding kindLazyTarget, kindSetLazyOffset, // pointer store combinations @@ -350,8 +345,6 @@ struct Fixup kindStoreTargetAddressARMBranch24, // kindSetTargetAddress + kindStoreARMBranch24 kindStoreTargetAddressThumbBranch22, // kindSetTargetAddress + kindStoreThumbBranch22 kindStoreTargetAddressARMLoad12, // kindSetTargetAddress + kindStoreARMLoad12 - // PowerPC value calculation and store combinations - kindStoreTargetAddressPPCBranch24, // kindSetTargetAddress + kindStorePPCBranch24 }; union { diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 66f9432..cf0a058 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -226,8 +226,6 @@ const class File::Entry* File::Entry::next() const } -template <> cpu_type_t File::architecture() { return CPU_TYPE_POWERPC; } -template <> cpu_type_t File::architecture() { return CPU_TYPE_POWERPC64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_I386; } template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } @@ -314,12 +312,6 @@ bool File::memberHasObjCCategories(const Entry* member) const } } -template <> -bool File::memberHasObjCCategories(const Entry* member) const -{ - // ppc uses ObjC1 ABI which has .objc_category* global symbols - return false; -} template @@ -581,14 +573,6 @@ ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; - case CPU_TYPE_POWERPC: - if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) - return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); - break; - case CPU_TYPE_POWERPC64: - if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) - return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); - break; } return NULL; } diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 46d350d..3a11c18 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -147,7 +147,7 @@ class Atom : public ld::Atom { public: Atom(File& f, const char* name, ld::Atom::Scope s, - ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a); + ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a, bool ah); // overrides of ld::Atom virtual ld::File* file() const { return &_file; } @@ -257,8 +257,6 @@ bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix); } break; - case CPU_TYPE_POWERPC: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "powerpc-"); } return false; } @@ -268,8 +266,6 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); switch (arch) { - case CPU_TYPE_POWERPC: - return "ppc"; case CPU_TYPE_I386: return "i386"; case CPU_TYPE_X86_64: @@ -377,6 +373,7 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte // make LLVM atoms for definitions and a reference for undefines if ( def != ld::Atom::definitionProxy ) { ld::Atom::Scope scope; + bool autohide = false; switch ( attr & LTO_SYMBOL_SCOPE_MASK) { case LTO_SYMBOL_SCOPE_INTERNAL: scope = ld::Atom::scopeTranslationUnit; @@ -387,6 +384,12 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte case LTO_SYMBOL_SCOPE_DEFAULT: scope = ld::Atom::scopeGlobal; break; +#if LTO_API_VERSION >= 4 + case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN: + scope = ld::Atom::scopeGlobal; + autohide = true; + break; +#endif default: throwf("unknown scope for symbol %s in bitcode file %s", name, pth); } @@ -395,7 +398,7 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte continue; uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); // make Atom using placement new operator - new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment); + new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment, autohide); if ( scope == ld::Atom::scopeLinkageUnit ) _internalAtom.addReference(name); if ( log ) fprintf(stderr, "\t0x%08X %s\n", attr, name); @@ -430,11 +433,14 @@ InternalAtom::InternalAtom(File& f) { } -Atom::Atom(File& f, const char* nm, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a) +Atom::Atom(File& f, const char* nm, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Combine c, + ld::Atom::Alignment a, bool ah) : ld::Atom(f._section, d, c, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, false, a), _file(f), _name(nm), _compiledAtom(NULL) { + if ( ah ) + this->setAutoHide(); } void Atom::setCompiledAtom(const ld::Atom& atom) diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 436f3e6..39cebfb 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -855,60 +855,6 @@ class Parser -template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - 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_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - template <> bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) { @@ -1012,14 +958,6 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( Parser::validFile(fileContent, bundleLoader) ) 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, indirectDylib); - break; - case CPU_TYPE_POWERPC64: - if ( Parser::validFile(fileContent, bundleLoader) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; } return NULL; } diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index abd44fa..e79d261 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -168,7 +168,6 @@ class Section : public ld::Section _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } - bool addRelocFixup_powerpc(class Parser& parser,const macho_relocation_info* reloc); 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); @@ -1128,31 +1127,6 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p { } -template <> -bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} template <> bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) @@ -1202,39 +1176,6 @@ bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, c } -template <> -const char* Parser::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return NULL; - switch ( header->cpusubtype() ) { - case CPU_SUBTYPE_POWERPC_750: - return "ppc750"; - case CPU_SUBTYPE_POWERPC_7400: - return "ppc7400"; - case CPU_SUBTYPE_POWERPC_7450: - return "ppc7450"; - case CPU_SUBTYPE_POWERPC_970: - return "ppc970"; - case CPU_SUBTYPE_POWERPC_ALL: - return "ppc"; - } - return "ppc???"; -} - -template <> -const char* Parser::fileKind(const uint8_t* fileContent) -{ - const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return NULL; - return "ppc64"; -} template <> const char* Parser::fileKind(const uint8_t* fileContent) @@ -1688,8 +1629,6 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) -template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } -template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } @@ -2524,9 +2463,6 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co case ld::Fixup::kindStoreThumbBranch22: firstKind = ld::Fixup::kindStoreTargetAddressThumbBranch22; break; - case ld::Fixup::kindStorePPCBranch24: - firstKind = ld::Fixup::kindStoreTargetAddressPPCBranch24; - break; default: combined = false; cl = ld::Fixup::k1of2; @@ -3626,6 +3562,8 @@ ld::Section::Type Section::sectionType(const macho_section* se return ld::Section::typeCode; else if ( strcmp(sect->sectname(), "__StaticInit") == 0 ) return ld::Section::typeCode; + else if ( strcmp(sect->sectname(), "__constructor") == 0 ) + return ld::Section::typeInitializerPointers; } else if ( strcmp(sect->segname(), "__DATA") == 0 ) { if ( strcmp(sect->sectname(), "__cfstring") == 0 ) @@ -3705,8 +3643,6 @@ uint32_t Section::sectionNum(class Parser& parser) const return 1 + (this->_machOSection - parser.firstMachOSection()); } -// libunwind does not support ppc64 -template <> uint32_t CFISection::cfiCount() { return 0; } // arm does not have zero cost exceptions template <> uint32_t CFISection::cfiCount() { return 0; } @@ -3827,32 +3763,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, } -// need to change libunwind parseCFIs() to work for ppc -template <> -void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) -{ - // create ObjectAddressSpace object for use by libunwind - OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); - - // use libuwind to parse __eh_frame data into array of CFI_Atom_Info - const char* msg; - msg = libunwind::DwarfInstructions::parseCFIs( - oas, this->_machOSection->addr(), this->_machOSection->size(), - cfiArray, count, (void*)&parser, warnFunc); - if ( msg != NULL ) - throwf("malformed __eh_frame section: %s", msg); -} -template <> -void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) -{ - // libunwind does not support ppc64 - assert(count == 0); -} template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, @@ -3900,8 +3811,6 @@ uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } -template <> bool CFISection::bigEndian() { return true; } -template <> bool CFISection::bigEndian() { return true; } template <> @@ -3951,30 +3860,6 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const C } -template <> -void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) -{ - uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; - if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { - uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; - uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; - Atom* cieAtom = this->findAtomByAddress(cieInfo->address); - Atom* nlpAtom = parser.findAtomByAddress(nlpAddr); - assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); - Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); - - parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); - parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); - parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); - parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreBigEndian32); - } - else if ( personalityEncoding != 0 ) { - throwf("unsupported address encoding (%02X) of personality function in CIE", - personalityEncoding); - } -} - - template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { @@ -4369,6 +4254,8 @@ SymboledSection::SymboledSection(Parser& parser, File& f, const macho_s case S_REGULAR: if ( strncmp(s->sectname(), "__gcc_except_tab", 16) == 0 ) _type = ld::Atom::typeLSDA; + else if ( this->type() == ld::Section::typeInitializerPointers ) + _type = ld::Atom::typeInitializerPointers; break; } } @@ -4714,17 +4601,6 @@ ld::Fixup::Kind NonLazyPointerSection::fixupKind() return ld::Fixup::kindStoreLittleEndian32; } -template <> -ld::Fixup::Kind NonLazyPointerSection::fixupKind() -{ - return ld::Fixup::kindStoreBigEndian32; -} - -template <> -ld::Fixup::Kind NonLazyPointerSection::fixupKind() -{ - return ld::Fixup::kindStoreBigEndian64; -} template <> void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -5654,406 +5530,6 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati -// -// ppc and ppc64 both use the same relocations, so process them in one common routine -// -template -bool Section::addRelocFixup_powerpc(class Parser& parser, - const macho_relocation_info* reloc) -{ - const macho_section

* sect = this->machoSection(); - bool result = false; - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - int32_t displacement = 0; - uint32_t instruction = 0; - int16_t lowBits; - pint_t contentValue = 0; - typename Parser::SourceLocation src; - typename Parser::TargetDesc target; - - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - srcAddr = sect->addr() + reloc->r_address(); - src.atom = this->findAtomByAddress(srcAddr); - src.offsetInAtom = srcAddr - src.atom->_objAddress; - const macho_relocation_info

* nextReloc = &reloc[1]; - fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != PPC_RELOC_PAIR ) - instruction = BigEndian::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); - } - switch ( reloc->r_type() ) { - case PPC_RELOC_BR24: - assert((instruction & 0x4C000000) == 0x48000000); - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - if ( reloc->r_extern() ) { - target.addend = srcAddr + displacement; - } - else { - dstAddr = srcAddr + displacement; - parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); - } - // special case "calls" for dtrace - if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { - parser.addFixup(src, ld::Fixup::k1of1, - ld::Fixup::kindStorePPCDtraceCallSiteNop, false, target.name); - parser.addDtraceExtraInfos(src, &target.name[16]); - } - else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { - parser.addFixup(src, ld::Fixup::k1of1, - ld::Fixup::kindStorePPCDtraceIsEnableSiteClear, false, target.name); - parser.addDtraceExtraInfos(src, &target.name[20]); - } - else { - parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); - } - break; - case PPC_RELOC_BR14: - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - if ( reloc->r_extern() ) { - target.addend = srcAddr + displacement; - } - else { - dstAddr = srcAddr + displacement; - parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); - } - parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); - break; - case PPC_RELOC_PAIR: - // skip, processed by a previous look ahead - break; - case PPC_RELOC_LO16: - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) - throw "PPC_RELOC_LO16 missing following pair"; - result = true; - lowBits = (instruction & 0x0000FFFF); - dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_extern() ) { - target.addend = dstAddr; - } - else { - parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); - } - parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); - break; - case PPC_RELOC_LO14: - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) - throw "PPC_RELOC_LO14 missing following pair"; - result = true; - lowBits = (instruction & 0xFFFC); - dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_extern() ) { - target.addend = dstAddr; - } - else { - parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); - } - parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); - break; - case PPC_RELOC_HI16: - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) - throw "PPC_RELOC_HI16 missing following pair"; - result = true; - lowBits = (nextReloc->r_address() & 0xFFFF); - dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); - if ( reloc->r_extern() ) { - target.addend = dstAddr; - } - else { - parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); - } - parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); - break; - case PPC_RELOC_HA16: - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) - throw "PPC_RELOC_HA16 missing following pair"; - result = true; - lowBits = (nextReloc->r_address() & 0x0000FFFF); - dstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; - if ( reloc->r_extern() ) { - target.addend = dstAddr; - } - else { - parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); - } - parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); - break; - case PPC_RELOC_VANILLA: - contentValue = P::getP(*((pint_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - target.addend = contentValue; - } - else { - parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); - } - switch ( reloc->r_length() ) { - case 0: - case 1: - throw "bad r_length in PPC_RELOC_VANILLA"; - case 2: - parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); - break; - case 3: - parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); - break; - } - break; - case PPC_RELOC_JBSR: - // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) - throw "PPC_RELOC_JBSR missing following pair"; - if ( !parser._hasLongBranchStubs ) - warning("object file compiled with -mlong-branch which is no longer needed. " - "To remove this warning, recompile without -mlong-branch: %s", parser._path); - parser._hasLongBranchStubs = true; - result = true; - if ( reloc->r_extern() ) { - throw "PPC_RELOC_JBSR should not be using an external relocation"; - } - parser.findTargetFromAddressAndSectionNum(nextReloc->r_address(), reloc->r_symbolnum(), target); - parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - // file format allows pair to be scattered or not - const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; - const macho_relocation_info

* nextReloc = &reloc[1]; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + sreloc->r_address()); - instruction = BigEndian::get32(*fixUpPtr); - src.atom = this->findAtomByAddress(srcAddr); - src.offsetInAtom = srcAddr - src.atom->_objAddress; - typename Parser::TargetDesc picBase; - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - } - switch ( sreloc->r_type() ) { - case PPC_RELOC_VANILLA: - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - target.atom = parser.findAtomByAddress(sreloc->r_value()); - switch ( sreloc->r_length() ) { - case 0: - case 1: - throw "unsuppored r_length < 2 for scattered PPC_RELOC_VANILLA"; - case 2: - contentValue = BigEndian::get32(*(uint32_t*)fixUpPtr); - target.addend = contentValue - target.atom->_objAddress; - parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); - break; - case 3: - contentValue = BigEndian::get64(*(uint64_t*)fixUpPtr); - target.addend = contentValue - target.atom->_objAddress; - parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); - break; - } - break; - case PPC_RELOC_BR14: - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - target.atom = parser.findAtomByAddress(sreloc->r_value()); - target.addend = (srcAddr + displacement) - target.atom->_objAddress; - parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); - break; - case PPC_RELOC_BR24: - assert((instruction & 0x4C000000) == 0x48000000); - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - target.atom = parser.findAtomByAddress(sreloc->r_value()); - target.addend = (srcAddr + displacement) - target.atom->_objAddress; - parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); - break; - case PPC_RELOC_LO16_SECTDIFF: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; - lowBits = (instruction & 0xFFFF); - dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); - parser.findTargetFromAddress(sreloc->r_value(), target); - if ( target.atom != NULL ) - target.addend = dstAddr - target.atom->_objAddress; - picBase.atom = parser.findAtomByAddress(nextRelocValue); - picBase.addend = nextRelocValue - picBase.atom->_objAddress; - picBase.weakImport = false; - picBase.name = NULL; - parser.addFixups(src, ld::Fixup::kindStorePPCPicLow16, target, picBase); - break; - case PPC_RELOC_LO14_SECTDIFF: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; - lowBits = (instruction & 0xFFFC); - dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); - parser.findTargetFromAddress(sreloc->r_value(), target); - if ( target.atom != NULL ) - target.addend = dstAddr - target.atom->_objAddress; - picBase.atom = parser.findAtomByAddress(nextRelocValue); - picBase.addend = nextRelocValue - picBase.atom->_objAddress; - picBase.weakImport = false; - picBase.name = NULL; - parser.addFixups(src, ld::Fixup::kindStorePPCPicLow14, target, picBase); - break; - case PPC_RELOC_HA16_SECTDIFF: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; - lowBits = (nextRelocAddress & 0x0000FFFF); - dstAddr = nextRelocValue + (((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits); - parser.findTargetFromAddress(sreloc->r_value(), target); - if ( target.atom != NULL ) - target.addend = dstAddr - target.atom->_objAddress; - picBase.atom = parser.findAtomByAddress(nextRelocValue); - picBase.addend = nextRelocValue - picBase.atom->_objAddress; - picBase.weakImport = false; - picBase.name = NULL; - parser.addFixups(src, ld::Fixup::kindStorePPCPicHigh16AddLow, target, picBase); - break; - case PPC_RELOC_LO14: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_LO14 missing following pair"; - lowBits = (instruction & 0xFFFC); - dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); - parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); - parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); - break; - case PPC_RELOC_LO16: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_LO16 missing following pair"; - lowBits = (instruction & 0xFFFF); - dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); - parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); - parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); - break; - case PPC_RELOC_HA16: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_HA16 missing following pair"; - lowBits = (nextRelocAddress & 0xFFFF); - dstAddr = (((instruction & 0xFFFF) << 16) + (int32_t)lowBits); - parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); - parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); - break; - case PPC_RELOC_HI16: - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_HI16 missing following pair"; - lowBits = (nextRelocAddress & 0xFFFF); - dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); - parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); - parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); - break; - case PPC_RELOC_SECTDIFF: - case PPC_RELOC_LOCAL_SECTDIFF: - { - if ( ! nextRelocIsPair ) - throw "PPC_RELOC_SECTDIFF missing following pair"; - ld::Fixup::Kind kind = ld::Fixup::kindNone; - switch ( sreloc->r_length() ) { - case 0: - throw "bad length for PPC_RELOC_SECTDIFF"; - case 1: - contentValue = (int32_t)(int16_t)BigEndian::get16(*((uint16_t*)fixUpPtr)); - kind = ld::Fixup::kindStoreBigEndian16; - break; - case 2: - contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); - kind = ld::Fixup::kindStoreBigEndian32; - break; - case 3: - contentValue = BigEndian::get64(*((uint64_t*)fixUpPtr)); - kind = ld::Fixup::kindStoreBigEndian64; - break; - break; - } - Atom* fromAtom = parser.findAtomByAddress(nextRelocValue); - Atom* targetAtom = parser.findAtomByAddress(sreloc->r_value()); - uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; - uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress; - // check for addend encoded in the section content - int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue); - if ( addend < 0 ) { - if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); - } - else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); - } - else { - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); - } - 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); - parser.addFixup(src, ld::Fixup::k5of5, kind); - } - else { - if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); - } - else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); - } - else { - parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); - } - parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget+addend); - parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); - parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); - parser.addFixup(src, ld::Fixup::k5of5, kind); - } - } - break; - case PPC_RELOC_PAIR: - break; - case PPC_RELOC_HI16_SECTDIFF: - warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - - -template <> -bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) -{ - return addRelocFixup_powerpc(parser, reloc); -} - - -template <> -bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) -{ - return addRelocFixup_powerpc(parser, reloc); -} - template <> @@ -6226,7 +5702,9 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati dstAddr = ((instruction16 << 16) | other16); if ( reloc->r_extern() ) { target.addend = dstAddr; - } + if ( externSymbolIsThumbDef ) + target.addend &= -2; // remove thumb bit + } else { parser.findTargetFromAddress(dstAddr, target); if ( target.atom->isThumb() ) @@ -6239,6 +5717,8 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati dstAddr = (other16 << 16) | instruction16; if ( reloc->r_extern() ) { target.addend = dstAddr; + if ( externSymbolIsThumbDef ) + target.addend &= -2; // remove thumb bit } else { parser.findTargetFromAddress(dstAddr, target); @@ -6552,41 +6032,6 @@ bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const mach return FixedSizeSection::addRelocFixup(parser, reloc); } -template <> -bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) -{ - // if this is the reloc for the super class name string, add implicit reference to super class - if ( ((reloc->r_address() & R_SCATTERED) == 0) && (reloc->r_type() == PPC_RELOC_VANILLA) ) { - assert( reloc->r_length() == 2 ); - assert( ! reloc->r_pcrel() ); - - const macho_section

* sect = this->machoSection(); - Parser::SourceLocation src; - uint32_t srcAddr = sect->addr() + reloc->r_address(); - src.atom = this->findAtomByAddress(srcAddr); - src.offsetInAtom = srcAddr - src.atom->objectAddress(); - if ( src.offsetInAtom == 4 ) { - Parser::TargetDesc stringTarget; - const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); - uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); - parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); - - assert(stringTarget.atom != NULL); - assert(stringTarget.atom->contentType() == ld::Atom::typeCString); - const char* superClassBaseName = (char*)stringTarget.atom->rawContentPointer(); - char* superClassName = new char[strlen(superClassBaseName) + 20]; - strcpy(superClassName, ".objc_class_name_"); - strcat(superClassName, superClassBaseName); - - parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, superClassName); - } - } - - // inherited - return FixedSizeSection::addRelocFixup(parser, reloc); -} - - template @@ -6600,38 +6045,6 @@ bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho } -template <> -bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) -{ - // add implict class refs, fixups not usable yet, so look at relocations - assert( (reloc->r_address() & R_SCATTERED) == 0 ); - assert( reloc->r_type() == PPC_RELOC_VANILLA ); - assert( reloc->r_length() == 2 ); - assert( ! reloc->r_pcrel() ); - - const macho_section

* sect = this->machoSection(); - Parser::SourceLocation src; - uint32_t srcAddr = sect->addr() + reloc->r_address(); - src.atom = this->findAtomByAddress(srcAddr); - src.offsetInAtom = srcAddr - src.atom->objectAddress(); - Parser::TargetDesc stringTarget; - const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); - uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); - parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); - - assert(stringTarget.atom != NULL); - assert(stringTarget.atom->contentType() == ld::Atom::typeCString); - const char* baseClassName = (char*)stringTarget.atom->rawContentPointer(); - char* objcClassName = new char[strlen(baseClassName) + 20]; - strcpy(objcClassName, ".objc_class_name_"); - strcat(objcClassName, baseClassName); - - parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, objcClassName); - - // inherited - return PointerToCStringSection::addRelocFixup(parser, reloc); -} - template <> bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) @@ -6738,14 +6151,6 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Parser::validFile(fileContent) ) - return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Parser::validFile(fileContent) ) - return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); - break; } return NULL; } @@ -6762,10 +6167,6 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO return ( mach_o::relocatable::Parser::validFile(fileContent) ); case CPU_TYPE_ARM: return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); - case CPU_TYPE_POWERPC: - return ( mach_o::relocatable::Parser::validFile(fileContent) ); - case CPU_TYPE_POWERPC64: - return ( mach_o::relocatable::Parser::validFile(fileContent) ); } return false; } @@ -6791,17 +6192,6 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = header->cpusubtype(); return true; } - if ( mach_o::relocatable::Parser::validFile(fileContent) ) { - *result = CPU_TYPE_POWERPC; - const macho_header >* header = (const macho_header >*)fileContent; - *subResult = header->cpusubtype(); - return true; - } - if ( mach_o::relocatable::Parser::validFile(fileContent) ) { - *result = CPU_TYPE_POWERPC64; - *subResult = CPU_SUBTYPE_POWERPC_ALL; - return true; - } return false; } @@ -6819,12 +6209,6 @@ const char* archName(const uint8_t* fileContent) if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::fileKind(fileContent); } - if ( mach_o::relocatable::Parser::validFile(fileContent) ) { - return mach_o::relocatable::Parser::fileKind(fileContent); - } - if ( mach_o::relocatable::Parser::validFile(fileContent) ) { - return mach_o::relocatable::Parser::fileKind(fileContent); - } return NULL; } diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 99b3eb8..773031e 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -59,42 +59,6 @@ class TargetAndOffsetComparor static bool _s_log = false; static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); -class PPCBranchIslandAtom : public ld::Atom { -public: - PPCBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, - ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, - ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), - _name(nm), - _target(target), - _finalTarget(finalTarget) { } - - 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 4; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { - int64_t displacement = _target->finalAddress() - this->finalAddress(); - const int64_t bl_sixteenMegLimit = 0x00FFFFFF; - if ( _target->contentType() == ld::Atom::typeBranchIsland ) { - // try optimizing away intermediate islands - int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress(); - if ( (skipToFinalDisplacement > bl_sixteenMegLimit) && (skipToFinalDisplacement < (-bl_sixteenMegLimit)) ) { - displacement = skipToFinalDisplacement; - } - } - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); - } - virtual void setScope(Scope) { } - -private: - const char* _name; - const ld::Atom* _target; - TargetAndOffset _finalTarget; -}; class ARMtoARMBranchIslandAtom : public ld::Atom { @@ -303,10 +267,6 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int } switch ( kind ) { - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - return new PPCBranchIslandAtom(name, nextTarget, finalTarget); - break; case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: @@ -337,10 +297,6 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool seenThumbBranch) { switch ( opts.architecture() ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: - return 16000000; - break; case CPU_TYPE_ARM: if ( ! seenThumbBranch ) return 32000000; // ARM can branch +/- 32MB @@ -358,10 +314,6 @@ static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool see static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBranch) { switch ( opts.architecture() ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: - return 14*1024*1024; - break; case CPU_TYPE_ARM: if ( ! seenThumbBranch ) return 30*1024*1024; // 2MB of branch islands per 32MB @@ -405,10 +357,8 @@ void doPass(const Options& opts, ld::Internal& state) if ( opts.outputKind() == Options::kObjectFile ) return; - // only PowerPC and ARM need branch islands + // only ARM needs branch islands switch ( opts.architecture() ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: case CPU_TYPE_ARM: break; default: @@ -506,8 +456,6 @@ void doPass(const Options& opts, ld::Internal& state) case ld::Fixup::kindAddAddend: addend = fit->u.addend; break; - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index f847cc0..665e3ae 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -143,13 +143,11 @@ void doPass(const Options& opts, ld::Internal& internal) for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { switch ( fit->kind ) { case ld::Fixup::kindStoreX86DtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); @@ -170,10 +168,6 @@ void doPass(const Options& opts, ld::Internal& internal) ld::Fixup::Kind storeKind = ld::Fixup::kindNone; switch ( opts.architecture() ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: - storeKind = ld::Fixup::kindStoreBigEndian32; - break; case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index bb94ddf..08eeb73 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -89,8 +89,21 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom } if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { // cannot do LEA optimization if target is weak exported symbol - if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) - *optimizable = false; + if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) { + switch ( opts.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kKextBundle: + *optimizable = false; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + break; + } + } // cannot do LEA optimization if target is interposable if ( opts.interposable(targetOfGOT->name()) ) *optimizable = false; diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index ed59193..b209b1d 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -1153,18 +1153,10 @@ void doPass(const Options& opts, ld::Internal& state) state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false)); break; - case CPU_TYPE_POWERPC: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, false)); - break; case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, state.hasObjcReplacementClasses, true)); break; - case CPU_TYPE_POWERPC64: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); - break; default: assert(0 && "unknown objc arch"); } @@ -1185,9 +1177,6 @@ void doPass(const Options& opts, ld::Internal& state) // disable optimization until fully tested //OptimizeCategories::doit(opts, state); break; - case CPU_TYPE_POWERPC64: - case CPU_TYPE_POWERPC: - break; default: assert(0 && "unknown objc arch"); } diff --git a/ld64/src/ld/passes/order_file.cpp b/ld64/src/ld/passes/order.cpp similarity index 87% rename from ld64/src/ld/passes/order_file.cpp rename to ld64/src/ld/passes/order.cpp index 5e814bd..ce17a35 100644 --- a/ld64/src/ld/passes/order_file.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -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@ * @@ -33,17 +33,17 @@ #include #include "ld.hpp" -#include "order_file.h" +#include "order.h" namespace ld { namespace passes { -namespace order_file { +namespace order { // // The purpose of this pass is to take the graph of all Atoms and produce an ordered // sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must // be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified -// in an order_file are sequenced as in the order_file and before Atoms not specified, +// in an order are sequenced as in the order file and before Atoms not specified, // 4) Atoms in the same section from the same .o file should be contiguous and sequenced // in the same order they were in the .o file, 5) Atoms in the same Section but which came // from different .o files should be sequenced in the same order that the .o files @@ -53,9 +53,9 @@ namespace order_file { // as it is constructed. Add each atom has an objectAddress() method. Then // sorting is just sorting by section, then by file ordinal, then by object address. // -// If an order_file is specified, it gets more complicated. First, an override-ordinal map +// If an -order_file is specified, it gets more complicated. First, an override-ordinal map // is created. It causes the sort routine to ignore the value returned by ordinal() and objectAddress() -// and use the override value instead. Next some Atoms must be layed out consecutively +// and use the override value instead. Next some Atoms must be laid out consecutively // (e.g. hand written assembly that does not end with return, but rather falls into // the next label). This is modeled in via a kindNoneFollowOn fixup. The use of // kindNoneFollowOn fixups produces "clusters" of atoms that must stay together. @@ -98,7 +98,7 @@ class Layout void buildOrdinalOverrideMap(); const ld::Atom* follower(const ld::Atom* atom); static bool matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName); - bool orderableSection(const ld::Internal::FinalSection*); + bool possibleToOrder(const ld::Internal::FinalSection*); const Options& _options; ld::Internal& _state; @@ -132,7 +132,7 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) if ( right->contentType() == ld::Atom::typeSectionStart ) return false; - // if a -order_file is specified, then sorting is altered to sort those symbols first + // if an -order_file is specified, then sorting is altered to sort those symbols first if ( _layout._haveOrderFile ) { AtomToOrdinal::const_iterator leftPos = _layout._ordinalOverrideMap.find(left); AtomToOrdinal::const_iterator rightPos = _layout._ordinalOverrideMap.find(right); @@ -191,8 +191,10 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) #endif // sort by .o order - uint32_t leftFileOrdinal = left->file()->ordinal(); - uint32_t rightFileOrdinal = right->file()->ordinal(); + const ld::File* leftFile = left->file(); + const ld::File* rightFile = right->file(); + uint32_t leftFileOrdinal = (leftFile != NULL) ? leftFile->ordinal() : 0; + uint32_t rightFileOrdinal = (rightFile != NULL) ? rightFile->ordinal() : 0; if ( leftFileOrdinal != rightFileOrdinal ) return leftFileOrdinal< rightFileOrdinal; @@ -233,9 +235,9 @@ bool Layout::matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafN } -bool Layout::orderableSection(const ld::Internal::FinalSection* sect) +bool Layout::possibleToOrder(const ld::Internal::FinalSection* sect) { - // atoms in only some sections are ordered + // atoms in only some sections can have order_file applied switch ( sect->type() ) { case ld::Section::typeUnclassified: case ld::Section::typeCode: @@ -261,8 +263,8 @@ void Layout::buildNameTable() { for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - // atoms in some special sections are never ordered - if ( ! orderableSection(sect) ) + // some sections are not worth scanning for names + if ( ! possibleToOrder(sect) ) continue; for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; @@ -339,11 +341,15 @@ const ld::Atom* Layout::follower(const ld::Atom* atom) void Layout::buildFollowOnTables() { + // if no -order_file, then skip building follow on table + if ( ! _haveOrderFile ) + return; + // first make a pass to find all follow-on references and build start/next maps // which are a way to represent clusters of atoms that must layout together for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( !orderableSection(sect) ) + if ( !possibleToOrder(sect) ) continue; for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; @@ -425,6 +431,20 @@ void Layout::buildFollowOnTables() } } + +class InSet +{ +public: + InSet(const std::set& theSet) : _set(theSet) {} + + bool operator()(const ld::Atom* atom) const { + return ( _set.count(atom) != 0 ); + } +private: + const std::set& _set; +}; + + void Layout::buildOrdinalOverrideMap() { // if no -order_file, then skip building override map @@ -438,9 +458,21 @@ void Layout::buildOrdinalOverrideMap() // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals uint32_t index = 0; uint32_t matchCount = 0; + std::set moveToData; for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) { const ld::Atom* atom = this->findAtom(*it); if ( atom != NULL ) { + // When order file used on data, turn ordered zero fill symbols into zero data + switch ( atom->section().type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTentativeDefs: + if ( atom->size() <= 512 ) + moveToData.insert(atom); + break; + default: + break; + } + AtomToAtom::iterator start = _followOnStarts.find(atom); if ( start != _followOnStarts.end() ) { // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together @@ -477,6 +509,25 @@ void Layout::buildOrdinalOverrideMap() } + // When order file used on data, turn ordered zero fill symbols into zeroed data + if ( ! moveToData.empty() ) { + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTentativeDefs: + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end()); + break; + case ld::Section::typeUnclassified: + if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + sect->atoms.insert(sect->atoms.end(), moveToData.begin(), moveToData.end()); + break; + default: + break; + } + } + } + } void Layout::doPass() @@ -484,42 +535,15 @@ void Layout::doPass() // handle .o files that cannot have their atoms rearranged this->buildFollowOnTables(); - // + // assign new ordinal value to all ordered atoms this->buildOrdinalOverrideMap(); - // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( orderableSection(sect) ) { - std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); - } - else { - // bubble sort any typeSectionStart atom to the beginning - bool moving = false; - for (int i=sect->atoms.size()-1; i >= 0; --i) { - if ( moving ) { - const ld::Atom* temp = sect->atoms[i]; - sect->atoms[i] = sect->atoms[i+1]; - sect->atoms[i+1] = temp; - } - if ( sect->atoms[i]->contentType() == ld::Atom::typeSectionStart ) - moving = true; - } - // bubble sort any typeSectionEnd atom to the end - moving = false; - for (unsigned int i=sect->atoms.size(); i < sect->atoms.size(); ++i) { - if ( moving ) { - const ld::Atom* temp = sect->atoms[i]; - sect->atoms[i] = sect->atoms[i-1]; - sect->atoms[i-1] = temp; - } - if ( sect->atoms[i]->contentType() == ld::Atom::typeSectionEnd ) - moving = true; - } - } + std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); } - + //fprintf(stderr, "Sorted atoms:\n"); //for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { // ld::Internal::FinalSection* sect = *sit; diff --git a/ld64/src/ld/passes/order_file.h b/ld64/src/ld/passes/order.h similarity index 87% rename from ld64/src/ld/passes/order_file.h rename to ld64/src/ld/passes/order.h index 7a2aa87..a3608e1 100644 --- a/ld64/src/ld/passes/order_file.h +++ b/ld64/src/ld/passes/order.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@ * @@ -23,8 +23,8 @@ */ -#ifndef __ORDER_FILE_H__ -#define __ORDER_FILE_H__ +#ifndef __ORDER_H__ +#define __ORDER_H__ #include "Options.h" #include "ld.hpp" @@ -32,14 +32,14 @@ namespace ld { namespace passes { -namespace order_file { +namespace order { // called by linker to re-arrange atoms to improve locality and performance extern void doPass(const Options& opts, ld::Internal& internal); -} // namespace order_file +} // namespace order } // namespace passes } // namespace ld -#endif // __ORDER_FILE_H__ +#endif // __ORDER_H__ diff --git a/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp b/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp deleted file mode 100644 index 6862b60..0000000 --- a/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp +++ /dev/null @@ -1,190 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -// already in ld::passes::stubs namespace -namespace ppc { -namespace classic { - - - -class LazyPointerAtom : public ld::Atom { -public: - LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool forLazyDylib, bool for64, bool weakImport) - : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, - ld::Atom::definitionRegular, ld::Atom::combineNever, - ld::Atom::scopeTranslationUnit, - forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, - symbolTableNotIn, false, false, false, for64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2)), - _stubTo(stubTo), - _fixup1(0, ld::Fixup::k1of1, - for64 ? ld::Fixup::kindStoreTargetAddressBigEndian64 : ld::Fixup::kindStoreTargetAddressBigEndian32, - forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper), - _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo), - _for64(for64) - { _fixup2.weakImport = weakImport; pass.addAtom(*this); } - - virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } - virtual const char* name() const { return _stubTo.name(); } - virtual uint64_t size() const { return _for64 ? 8 : 4; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } - virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } - -private: - const ld::Atom& _stubTo; - mutable ld::Fixup _fixup1; - mutable ld::Fixup _fixup2; - const bool _for64; - - static ld::Section _s_section; - static ld::Section _s_sectionLazy; -}; - -ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); -ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); - - - -class StubPICAtom : public ld::Atom { -public: - StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool forLazyDylib, bool for64, bool weakImport) - : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, - ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), - _stubTo(stubTo), - _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), - _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), - _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), - _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), - _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStorePPCPicHigh16AddLow), - _fixup5(20, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), - _fixup6(20, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), - _fixup7(20, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), - _fixup8(20, ld::Fixup::k4of4, for64 ? ld::Fixup::kindStorePPCPicLow14 : ld::Fixup::kindStorePPCPicLow16), - _for64(for64) - { pass.addAtom(*this); } - - virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } - virtual const char* name() const { return _stubTo.name(); } - virtual uint64_t size() const { return 32; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { - OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - if ( _for64 ) - OSWriteBigInt32(&buffer[20], 0, 0xe98b0001);// ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - else - OSWriteBigInt32(&buffer[20], 0, 0x858b0000);// lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } - virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } - -private: - const ld::Atom& _stubTo; - LazyPointerAtom _lazyPointer; - mutable ld::Fixup _fixup1; - mutable ld::Fixup _fixup2; - mutable ld::Fixup _fixup3; - mutable ld::Fixup _fixup4; - mutable ld::Fixup _fixup5; - mutable ld::Fixup _fixup6; - mutable ld::Fixup _fixup7; - mutable ld::Fixup _fixup8; - const bool _for64; - - static ld::Section _s_section; -}; - -ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub1", ld::Section::typeStub); - - - -class StubNoPICAtom : public ld::Atom { -public: - StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool forLazyDylib, bool for64, bool weakImport) - : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, - ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), - _stubTo(stubTo), - _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), - _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), - _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStorePPCAbsHigh16AddLow), - _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), - _fixup4(4, ld::Fixup::k2of2, for64 ? ld::Fixup::kindStorePPCAbsLow14 : ld::Fixup::kindStorePPCAbsLow16), - _for64(for64) - { pass.addAtom(*this); } - - virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } - virtual const char* name() const { return _stubTo.name(); } - virtual uint64_t size() const { return 16; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_foo$lazy_ptr) - if ( _for64 ) - OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001);// ldu r12,lo16(L_foo$lazy_ptr)(r11) - else - OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000);// lwzu r12,lo16(L_foo$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } - virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } - virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } - -private: - const ld::Atom& _stubTo; - LazyPointerAtom _lazyPointer; - mutable ld::Fixup _fixup1; - mutable ld::Fixup _fixup2; - mutable ld::Fixup _fixup3; - mutable ld::Fixup _fixup4; - const bool _for64; - - static ld::Section _s_section; -}; - -ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub1", ld::Section::typeStub); - - -} // namespace classic -} // namespace ppc - diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index 274878b..aa4aaf1 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -89,7 +89,6 @@ class Pass { #include "stub_x86_classic.hpp" #include "stub_arm.hpp" #include "stub_arm_classic.hpp" -#include "stub_ppc_classic.hpp" @@ -116,7 +115,6 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) if ( fixup->binding == ld::Fixup::bindingsIndirectlyBound ) { const ld::Atom* target = state.indirectBindingTable[fixup->u.bindingIndex]; switch ( fixup->kind ) { - case ld::Fixup::kindStoreTargetAddressPPCBranch24: case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: @@ -177,15 +175,6 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) } switch ( _architecture ) { - case CPU_TYPE_POWERPC: - if ( _pic ) - return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, false, weakImport); - else - return new ld::passes::stubs::ppc::classic::StubNoPICAtom(*this, target, forLazyDylib, false, weakImport); - break; - case CPU_TYPE_POWERPC64: - return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, true, weakImport); - break; case CPU_TYPE_I386: if ( usingCompressedLINKEDIT() && !forLazyDylib ) return new ld::passes::stubs::x86::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); @@ -304,9 +293,7 @@ void Pass::process(ld::Internal& state) if ( _options.outputKind() != Options::kDynamicLibrary ) throwf("resolver functions (%s) can only be used in dylibs", atom->name()); if ( !_options.makeCompressedDyldInfo() ) { - if ( _options.architecture() == CPU_TYPE_POWERPC ) - throwf("resolver functions (%s) not supported for PowerPC", atom->name()); - else if ( _options.architecture() == CPU_TYPE_ARM ) + if ( _options.architecture() == CPU_TYPE_ARM ) throwf("resolver functions (%s) can only be used when targeting iOS 4.2 or later", atom->name()); else throwf("resolver functions (%s) can only be used when targeting Mac OS X 10.6 or later", atom->name()); diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 3d7ac5e..44d8f53 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -738,33 +738,6 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreBigEndian64: printf(", then store 64-bit big endian"); break; - case ld::Fixup::kindStorePPCBranch24: - printf(", then store as PPC branch24"); - break; - case ld::Fixup::kindStorePPCBranch14: - printf(", then store as PPC branch14"); - break; - case ld::Fixup::kindStorePPCPicLow14: - printf(", then store as PPC low14 pic"); - break; - case ld::Fixup::kindStorePPCPicLow16: - printf(", then store as PPC low14 pic"); - break; - case ld::Fixup::kindStorePPCPicHigh16AddLow: - printf(", then store as PPC high16 pic"); - break; - case ld::Fixup::kindStorePPCAbsLow14: - printf(", then store as PPC low14 abs"); - break; - case ld::Fixup::kindStorePPCAbsLow16: - printf(", then store as PPC low14 abs"); - break; - case ld::Fixup::kindStorePPCAbsHigh16AddLow: - printf(", then store as PPC high16 abs"); - break; - case ld::Fixup::kindStorePPCAbsHigh16: - printf(", then store as PPC high16 abs, no carry"); - break; case ld::Fixup::kindStoreX86BranchPCRel8: printf(", then store as x86 8-bit pcrel branch"); break; @@ -840,12 +813,6 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: printf("x86 dtrace static is-enabled site"); break; - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - printf("ppc dtrace static probe site"); - break; - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: - printf("ppc dtrace static is-enabled site"); - break; case ld::Fixup::kindStoreARMDtraceCallSiteNop: printf("ARM dtrace static probe site"); break; @@ -909,9 +876,6 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreTargetAddressARMLoad12: printf("ARM store 12-bit pc-rel branch to %s", referenceTargetAtomName(ref)); break; - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - printf("PowerPC store 24-bit pc-rel load of %s", referenceTargetAtomName(ref)); - break; case ld::Fixup::kindSetTargetTLVTemplateOffset: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: @@ -1220,11 +1184,7 @@ int main(int argc, const char* argv[]) } else if ( strcmp(arg, "-arch") == 0 ) { const char* arch = ++i::relocTypeName(uint8_t r_type) { if ( r_type == GENERIC_RELOC_VANILLA ) return "pointer"; - else if ( r_type == PPC_RELOC_PB_LA_PTR ) - return "pb pointer"; else return "??"; } diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 4841c7c..54dc560 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -1066,9 +1066,6 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* relo } else { - // ignore pair relocs - if ( reloc->r_type() == PPC_RELOC_PAIR ) - return; // FIX if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); diff --git a/ld64/src/other/rebase.cpp b/ld64/src/other/rebase.cpp index d69fc3c..2255789 100644 --- a/ld64/src/other/rebase.cpp +++ b/ld64/src/other/rebase.cpp @@ -650,13 +650,7 @@ void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) } } else { - macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; - if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { - sreloc->set_r_value( sreloc->r_value() + fSlide ); - } - else { - throw "cannot rebase final linked image with scattered relocations"; - } + throw "cannot rebase final linked image with scattered relocations"; } } diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index d24e62c..9060557 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -104,8 +104,6 @@ class UnwindPrinter }; -template <> const char* UnwindPrinter::archName() { return "ppc"; } -template <> const char* UnwindPrinter::archName() { return "ppc64"; } template <> const char* UnwindPrinter::archName() { return "i386"; } template <> const char* UnwindPrinter::archName() { return "x86_64"; } template <> const char* UnwindPrinter::archName() { return "arm"; } diff --git a/ld64/unit-tests/test-cases/archive-init-order/Makefile b/ld64/unit-tests/test-cases/archive-init-order/Makefile new file mode 100644 index 0000000..83364e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/Makefile @@ -0,0 +1,48 @@ +## +# 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 -Wl,-map,main.map + grep anon main.map | awk '{ print $$4}' > main-actual.txt + sort main-actual.txt > main-should.txt + diff main-actual.txt main-should.txt + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.o *.a main main.map main*.txt diff --git a/ld64/unit-tests/test-cases/archive-init-order/bar.c b/ld64/unit-tests/test-cases/archive-init-order/bar.c new file mode 100644 index 0000000..850c211 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/bar.c @@ -0,0 +1,4 @@ +int bar() { return 0; } + +__attribute__((constructor)) +void bar_init() { } diff --git a/ld64/unit-tests/test-cases/archive-init-order/bar2.c b/ld64/unit-tests/test-cases/archive-init-order/bar2.c new file mode 100644 index 0000000..4c268ba --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/bar2.c @@ -0,0 +1,4 @@ +int bar2() { return 0; } + +__attribute__((constructor)) +void bar2_init() { } diff --git a/ld64/unit-tests/test-cases/archive-init-order/bar3.c b/ld64/unit-tests/test-cases/archive-init-order/bar3.c new file mode 100644 index 0000000..a55167f --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/bar3.c @@ -0,0 +1,4 @@ +int bar3() { return 0; } + +__attribute__((constructor)) +void bar3_init() { } diff --git a/ld64/unit-tests/test-cases/archive-init-order/foo.c b/ld64/unit-tests/test-cases/archive-init-order/foo.c new file mode 100644 index 0000000..6149aa1 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/foo.c @@ -0,0 +1,4 @@ +int foo() { return 1; } + +__attribute__((constructor)) +void foo_init() { } diff --git a/ld64/unit-tests/test-cases/archive-init-order/foo2.c b/ld64/unit-tests/test-cases/archive-init-order/foo2.c new file mode 100644 index 0000000..a429fe5 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/foo2.c @@ -0,0 +1,4 @@ +int foo2() { return 1; } + +__attribute__((constructor)) +void foo2_init() { } diff --git a/ld64/unit-tests/test-cases/archive-init-order/foo3.c b/ld64/unit-tests/test-cases/archive-init-order/foo3.c new file mode 100644 index 0000000..b097ebf --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-order/foo3.c @@ -0,0 +1,4 @@ +int foo3() { return 1; } + +__attribute__((constructor)) +void foo3_init() { } diff --git a/ld64/unit-tests/test-cases/archive-init-order/main.c b/ld64/unit-tests/test-cases/archive-init-order/main.c new file mode 100644 index 0000000..242b32a --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-init-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/ld64/unit-tests/test-cases/dead_strip-initializers/Makefile b/ld64/unit-tests/test-cases/dead_strip-initializers/Makefile new file mode 100644 index 0000000..4292550 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-initializers/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 + +# +# Sanity check -dead_strip does not remove initializers and terminators +# + + +run: all + +all: + ${CXX} ${CCFLAGS} main.cxx other.cxx -dead_strip -o main + ${FAIL_IF_BAD_MACHO} main + nm main | grep dead_door_knob | ${FAIL_IF_STDIN} + nm main | grep ctr | ${FAIL_IF_EMPTY} + nm main | grep dtr | ${FAIL_IF_EMPTY} + ${CXX} ${CCFLAGS} -static main.cxx other.cxx -dead_strip -nostdlib -e _main -o main-static -Wl,-new_linker + ${FAIL_IF_BAD_MACHO} main-static + nm main-static | grep dead_door_knob | ${FAIL_IF_STDIN} + nm main-static | grep ctr | ${FAIL_IF_EMPTY} + nm main-static | grep dtr | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main-static + +clean: + rm -rf main main-static diff --git a/ld64/unit-tests/test-cases/dead_strip-initializers/main.cxx b/ld64/unit-tests/test-cases/dead_strip-initializers/main.cxx new file mode 100644 index 0000000..59af7c2 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-initializers/main.cxx @@ -0,0 +1,24 @@ + +int main() +{ + return 0; +} + + + +void dead_door_knob() { } + + +extern "C" int ctr(); +extern "C" void dtr(); + + +int ctr() { return 10; } +void dtr() { } + + +#if __STATIC__ +extern "C" void __cxa_atexit(); +void __cxa_atexit() {} +#endif + diff --git a/ld64/unit-tests/test-cases/dead_strip-initializers/other.cxx b/ld64/unit-tests/test-cases/dead_strip-initializers/other.cxx new file mode 100644 index 0000000..1c6534c --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-initializers/other.cxx @@ -0,0 +1,18 @@ + +extern "C" int ctr(); +extern "C" void dtr(); + +class Foo +{ +public: + Foo() : field(ctr()) { } + ~Foo() { dtr(); } +private: + int field; +}; + + +Foo f1; +Foo f2; + + diff --git a/ld64/unit-tests/test-cases/dso_handle/Makefile b/ld64/unit-tests/test-cases/dso_handle/Makefile new file mode 100644 index 0000000..f6c40e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/dso_handle/Makefile @@ -0,0 +1,23 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Verify the linker errors out nicely when __dso_handle is defined in source. +# + +run: all + +all: + ${CC} ${CCFLAGS} test.c -o test + ${FAIL_IF_BAD_MACHO} test + ${CC} ${CCFLAGS} test.c -DDSO_DEF=1 -o test-def 2>warnings.txt + ${FAIL_IF_BAD_MACHO} test-def + ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent + ${PASS_IFF_GOOD_MACHO} test-tent + + +clean: + rm -f test test-def test-tent warnings.txt diff --git a/ld64/unit-tests/test-cases/dso_handle/test.c b/ld64/unit-tests/test-cases/dso_handle/test.c new file mode 100644 index 0000000..0b984b0 --- /dev/null +++ b/ld64/unit-tests/test-cases/dso_handle/test.c @@ -0,0 +1,16 @@ +#include + +#if DSO_DEF + void* __dso_handle = NULL; +#elif DSO_TENT + void* __dso_handle; +#else + extern void* __dso_handle; +#endif + +int main() +{ + printf("dso_handle=%p\n", __dso_handle); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/fatal_warning/Makefile b/ld64/unit-tests/test-cases/fatal_warning/Makefile new file mode 100644 index 0000000..6758bc0 --- /dev/null +++ b/ld64/unit-tests/test-cases/fatal_warning/Makefile @@ -0,0 +1,40 @@ +## +# 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 + + +# +# Check that warning under -fatal_warnings causes error +# + +run: all + +all: + ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} main.c -o main -pagezero_size 123 -Wl,-fatal_warnings 2>msgs.txt + + +clean: + rm -f main msgs.txt diff --git a/ld64/unit-tests/test-cases/fatal_warning/main.c b/ld64/unit-tests/test-cases/fatal_warning/main.c new file mode 100644 index 0000000..fc047f7 --- /dev/null +++ b/ld64/unit-tests/test-cases/fatal_warning/main.c @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile index b27291a..82192f8 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile +++ b/ld64/unit-tests/test-cases/lto-dead_strip-unused/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@ # @@ -23,8 +23,10 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile + # # LTO with 'dead code strip' can't ignore unused functions with undefined references +# LTO many have eliminated need for some undefines # run: all @@ -32,7 +34,8 @@ run: all all: ${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 + ${CC} ${CCFLAGS} main.o bar.o -o main + ${CC} ${CCFLAGS} main.o bar.o -o main -dead_strip ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/order_file-zero-fill/Makefile b/ld64/unit-tests/test-cases/order_file-zero-fill/Makefile new file mode 100644 index 0000000..de2283e --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-zero-fill/Makefile @@ -0,0 +1,21 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -order_file will transform zero-fill (tentative def) +# symbols to __data symbols to achieve ordering. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c -Wl,-order_file,main.order -o main + nm -nj main | grep _xyz_ > main.actual + diff main.expected main.actual + nm -nm main | grep _xyz_f4 | grep __data | ${FAIL_IF_EMPTY} + nm -nm main | grep _other | grep __common | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main main.actual diff --git a/ld64/unit-tests/test-cases/order_file-zero-fill/foo.c b/ld64/unit-tests/test-cases/order_file-zero-fill/foo.c new file mode 100644 index 0000000..8bfc7d2 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-zero-fill/foo.c @@ -0,0 +1,4 @@ + +int xyz_f2; +int xyz_f4; +int other; diff --git a/ld64/unit-tests/test-cases/order_file-zero-fill/main.c b/ld64/unit-tests/test-cases/order_file-zero-fill/main.c new file mode 100644 index 0000000..6c37d8e --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-zero-fill/main.c @@ -0,0 +1,38 @@ +/* -*- 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 + +int xyz_m1; +int xyz_m3; +static int xyz_ms1; +int xyz_mi3 = 4; +int xyz_mi4 = 4; +int main_common; + + +int main() +{ + fprintf(stdout, "hello %p\n", &xyz_ms1); + return 0; +} diff --git a/ld64/unit-tests/test-cases/order_file-zero-fill/main.expected b/ld64/unit-tests/test-cases/order_file-zero-fill/main.expected new file mode 100644 index 0000000..8b9edb4 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-zero-fill/main.expected @@ -0,0 +1,7 @@ +_xyz_f4 +_xyz_mi4 +_xyz_f2 +_xyz_m3 +_xyz_m1 +_xyz_ms1 +_xyz_mi3 diff --git a/ld64/unit-tests/test-cases/order_file-zero-fill/main.order b/ld64/unit-tests/test-cases/order_file-zero-fill/main.order new file mode 100644 index 0000000..fa6c623 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-zero-fill/main.order @@ -0,0 +1,7 @@ +_xyz_f4 +_xyz_mi4 +_xyz_f2 +_xyz_m3 +_xyz_m1 +_xyz_ms1 + diff --git a/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile b/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile index 46e695c..568ebc6 100644 --- a/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile +++ b/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile @@ -32,7 +32,7 @@ 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} + otool -lv main | egrep 'S_THREAD_LOCAL_REGULAR|S_THREAD_LOCAL_ZEROFILL' | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: From a8b343c14b31d7930f3121d7adb0b1b16dd94e97 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:15:03 +0100 Subject: [PATCH 12/48] 133-3 --- ld64/ChangeLog | 1681 ----------------- ld64/compile_stubs | 51 + ld64/doc/design/bindings.png | Bin 0 -> 25309 bytes ld64/doc/design/hello.png | Bin 0 -> 11577 bytes ld64/doc/design/linker.html | 423 +++++ ld64/doc/man/man1/ld.1 | 2 +- ld64/ld64.xcodeproj/project.pbxproj | 199 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 161 +- ld64/src/abstraction/MachOTrie.hpp | 6 +- ld64/src/create_configure | 45 + ld64/src/ld/HeaderAndLoadCommands.hpp | 130 +- ld64/src/ld/InputFiles.cpp | 596 ++++-- ld64/src/ld/InputFiles.h | 66 +- ld64/src/ld/LinkEdit.hpp | 193 +- ld64/src/ld/LinkEditClassic.hpp | 96 +- ld64/src/ld/Options.cpp | 651 +++++-- ld64/src/ld/Options.h | 73 +- ld64/src/ld/OutputFile.cpp | 427 +++-- ld64/src/ld/OutputFile.h | 9 + ld64/src/ld/Resolver.cpp | 178 +- ld64/src/ld/Resolver.h | 10 +- ld64/src/ld/Snapshot.cpp | 538 ++++++ ld64/src/ld/Snapshot.h | 153 ++ ld64/src/ld/SymbolTable.cpp | 482 ++--- ld64/src/ld/SymbolTable.h | 14 +- ld64/src/ld/code-sign-blobs/blob.cpp | 129 ++ ld64/src/ld/code-sign-blobs/blob.h | 217 +++ ld64/src/ld/code-sign-blobs/endian.h | 138 ++ ld64/src/ld/code-sign-blobs/memutils.h | 124 ++ ld64/src/ld/code-sign-blobs/superblob.h | 249 +++ ld64/src/ld/ld.cpp | 34 +- ld64/src/ld/ld.hpp | 120 +- ld64/src/ld/parsers/archive_file.cpp | 179 +- ld64/src/ld/parsers/archive_file.h | 2 +- ld64/src/ld/parsers/lto_file.cpp | 86 +- ld64/src/ld/parsers/lto_file.h | 5 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 52 +- ld64/src/ld/parsers/macho_dylib_file.h | 2 +- .../src/ld/parsers/macho_relocatable_file.cpp | 125 +- ld64/src/ld/parsers/macho_relocatable_file.h | 4 +- ld64/src/ld/parsers/opaque_section_file.cpp | 8 +- ld64/src/ld/parsers/opaque_section_file.h | 2 +- ld64/src/ld/passes/branch_island.cpp | 72 +- ld64/src/ld/passes/branch_shim.cpp | 4 +- ld64/src/ld/passes/compact_unwind.cpp | 8 + ld64/src/ld/passes/dtrace_dof.cpp | 6 +- ld64/src/ld/passes/objc.cpp | 71 +- ld64/src/ld/passes/order.cpp | 5 +- ld64/src/ld/passes/stubs/stub_arm.hpp | 89 + ld64/src/ld/passes/stubs/stub_x86_64.hpp | 75 +- ld64/src/ld/passes/stubs/stubs.cpp | 43 +- ld64/src/other/ObjectDump.cpp | 52 +- ld64/src/other/dyldinfo.cpp | 166 +- ld64/src/other/machochecker.cpp | 57 +- ld64/src/other/rebase.cpp | 31 +- ld64/unit-tests/bin/make-recursive.pl | 153 +- ld64/unit-tests/include/common.makefile | 23 +- ld64/unit-tests/run-all-unit-tests | 2 +- .../test-cases/16-byte-alignment/Makefile | 4 +- .../test-cases/archive-r-ObjC/Makefile | 52 + .../test-cases/archive-r-ObjC/bar.c | 2 + .../test-cases/archive-r-ObjC/baz.m | 8 + .../test-cases/archive-r-ObjC/cat.m | 16 + .../test-cases/archive-r-ObjC/foo.m | 8 + .../test-cases/archive-r-ObjC/main.c | 31 + .../test-cases/branch-islands/Makefile | 4 + .../test-cases/branch-islands/atomic_space.s | 76 + .../test-cases/data-in-code/Makefile | 37 + .../unit-tests/test-cases/data-in-code/main.c | 49 + .../unit-tests/test-cases/data-in-code/test.s | 25 + .../test-cases/duplicate_symbols/Makefile | 52 + .../test-cases/duplicate_symbols/duplicates.c | 10 + .../duplicate_symbols/main_extern.c | 16 + .../duplicate_symbols/main_no_extern.c | 13 + .../{weak_import3 => dylib-main}/Makefile | 28 +- ld64/unit-tests/test-cases/dylib-main/foo.c | 1 + ld64/unit-tests/test-cases/dylib-main/main.c | 7 + .../unit-tests/test-cases/force-weak/Makefile | 38 + ld64/unit-tests/test-cases/force-weak/foo.c | 2 + ld64/unit-tests/test-cases/force-weak/test.c | 5 + .../unit-tests/test-cases/force-weak/weak.exp | 2 + .../test-cases/install-name-override/Makefile | 50 + .../test-cases/install-name-override/foo.c | 18 + .../test-cases/install-name-override/main.c | 9 + .../test-cases/llvm-integration/Makefile | 6 - .../lto-dead_strip-inline-asm/Makefile | 41 + .../lto-dead_strip-inline-asm/bar.c | 14 + .../test-cases/lto-dead_strip-unused/Makefile | 3 + .../test-cases/lto-preload-pie/Makefile | 11 +- ld64/unit-tests/test-cases/no-uuid/Makefile | 26 +- .../test-cases/objc-category-archive/Makefile | 9 +- .../test-cases/objc-category-archive/test2.m | 11 + .../objc-category-debug-notes/Makefile | 2 +- .../objc-category-optimize-load/Makefile | 3 +- .../objc-category-optimize/Makefile | 7 +- .../test-cases/objc-category-warning/Makefile | 4 +- .../unit-tests/test-cases/order_file/Makefile | 2 +- .../test-cases/pipelined-linking/Makefile | 74 + .../test-cases/pipelined-linking/bar.c | 4 + .../test-cases/pipelined-linking/cat.c | 4 + .../test-cases/pipelined-linking/foo.c | 10 + .../test-cases/static-executable-pie/Makefile | 50 + .../test-cases/static-executable-pie/bad.c | 9 + .../test-cases/static-executable-pie/test.c | 19 + .../tentative-and-archive-code/Makefile | 4 +- .../test-cases/tlv-dead_strip/Makefile | 2 +- .../test-cases/weak_import-undefined/Makefile | 40 + .../test-cases/weak_import-undefined/weak.c | 13 + .../test-cases/weak_import3/comment.txt | 1 - ld64/unit-tests/test-cases/weak_import3/foo.c | 17 - ld64/unit-tests/test-cases/weak_import3/foo.h | 16 - .../unit-tests/test-cases/weak_import3/foo1.c | 4 - .../unit-tests/test-cases/weak_import3/main.c | 20 - 113 files changed, 6482 insertions(+), 3024 deletions(-) delete mode 100644 ld64/ChangeLog create mode 100755 ld64/compile_stubs create mode 100644 ld64/doc/design/bindings.png create mode 100644 ld64/doc/design/hello.png create mode 100644 ld64/doc/design/linker.html create mode 100755 ld64/src/create_configure create mode 100644 ld64/src/ld/Snapshot.cpp create mode 100644 ld64/src/ld/Snapshot.h create mode 100644 ld64/src/ld/code-sign-blobs/blob.cpp create mode 100644 ld64/src/ld/code-sign-blobs/blob.h create mode 100644 ld64/src/ld/code-sign-blobs/endian.h create mode 100644 ld64/src/ld/code-sign-blobs/memutils.h create mode 100644 ld64/src/ld/code-sign-blobs/superblob.h create mode 100644 ld64/unit-tests/test-cases/archive-r-ObjC/Makefile create mode 100644 ld64/unit-tests/test-cases/archive-r-ObjC/bar.c create mode 100644 ld64/unit-tests/test-cases/archive-r-ObjC/baz.m create mode 100644 ld64/unit-tests/test-cases/archive-r-ObjC/cat.m create mode 100644 ld64/unit-tests/test-cases/archive-r-ObjC/foo.m create mode 100644 ld64/unit-tests/test-cases/archive-r-ObjC/main.c create mode 100644 ld64/unit-tests/test-cases/branch-islands/atomic_space.s create mode 100644 ld64/unit-tests/test-cases/data-in-code/Makefile create mode 100644 ld64/unit-tests/test-cases/data-in-code/main.c create mode 100644 ld64/unit-tests/test-cases/data-in-code/test.s create mode 100644 ld64/unit-tests/test-cases/duplicate_symbols/Makefile create mode 100644 ld64/unit-tests/test-cases/duplicate_symbols/duplicates.c create mode 100644 ld64/unit-tests/test-cases/duplicate_symbols/main_extern.c create mode 100644 ld64/unit-tests/test-cases/duplicate_symbols/main_no_extern.c rename ld64/unit-tests/test-cases/{weak_import3 => dylib-main}/Makefile (72%) create mode 100644 ld64/unit-tests/test-cases/dylib-main/foo.c create mode 100644 ld64/unit-tests/test-cases/dylib-main/main.c create mode 100644 ld64/unit-tests/test-cases/force-weak/Makefile create mode 100644 ld64/unit-tests/test-cases/force-weak/foo.c create mode 100644 ld64/unit-tests/test-cases/force-weak/test.c create mode 100644 ld64/unit-tests/test-cases/force-weak/weak.exp create mode 100644 ld64/unit-tests/test-cases/install-name-override/Makefile create mode 100644 ld64/unit-tests/test-cases/install-name-override/foo.c create mode 100644 ld64/unit-tests/test-cases/install-name-override/main.c create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c create mode 100644 ld64/unit-tests/test-cases/objc-category-archive/test2.m create mode 100644 ld64/unit-tests/test-cases/pipelined-linking/Makefile create mode 100644 ld64/unit-tests/test-cases/pipelined-linking/bar.c create mode 100644 ld64/unit-tests/test-cases/pipelined-linking/cat.c create mode 100644 ld64/unit-tests/test-cases/pipelined-linking/foo.c create mode 100644 ld64/unit-tests/test-cases/static-executable-pie/Makefile create mode 100644 ld64/unit-tests/test-cases/static-executable-pie/bad.c create mode 100644 ld64/unit-tests/test-cases/static-executable-pie/test.c create mode 100644 ld64/unit-tests/test-cases/weak_import-undefined/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import-undefined/weak.c delete mode 100644 ld64/unit-tests/test-cases/weak_import3/comment.txt delete mode 100644 ld64/unit-tests/test-cases/weak_import3/foo.c delete mode 100644 ld64/unit-tests/test-cases/weak_import3/foo.h delete mode 100644 ld64/unit-tests/test-cases/weak_import3/foo1.c delete mode 100644 ld64/unit-tests/test-cases/weak_import3/main.c diff --git a/ld64/ChangeLog b/ld64/ChangeLog deleted file mode 100644 index 0514d7f..0000000 --- a/ld64/ChangeLog +++ /dev/null @@ -1,1681 +0,0 @@ - --------- tagged ld64-128.1 - -2011-09-13 Nick Kledzik - - Enable dyld to be put into the dyld shared cache - -2011-09-13 Nick Kledzik - - Fix "using ld_classic" warning for i386 kexts - -2011-09-13 Nick Kledzik - - LTO many have eliminated need for some undefines - --------- tagged ld64-128 - -2011-09-08 Nick Kledzik - - Rework 8924157 fix to sort all sections - -2011-09-07 Nick Kledzik - - ER: -current_version should allow 64-bit a.b.c.d.e tuple - -2011-09-06 Nick Kledzik - - -mdynamic-no-pic broke with movw of weak thumb symbol - -2011-09-01 Nick Kledzik - - Turn crash into a warning if __dso_handle is defined in user code - Added test case: unit-tests/test-cases/dso_handle - -2011-08-31 Nick Kledzik - - Add -fatal_warnings - -2011-08-30 Nick Kledzik - - Support .weak_def_can_be_hidden via LTO interface - -2011-08-29 Nick Kledzik - - improve performance of zerofill->data ordering - -2011-08-29 Nick Kledzik - - Implement -print_statistics - -2011-08-25 Nick Kledzik - - check for overlaps between pinned segments and regular segments - -2011-08-23 Nick Kledzik - - do got elimination more aggressively in static and preload mode - -2011-08-22 Nick Kledzik - - enable __dso_handle in -preload - -2011-08-22 Nick Kledzik - - fix section$end to sort to end of __mod_init_func section - -2011-08-18 Nick Kledzik - - __constructor section removed with -dead_strip - -2011-08-11 Nick Kledzik - - remove ld support for PowerPC - -2011-08-11 Nick Kledzik - - Fix spurious -segaddr alignment warning - --------- tagged ld64-127.3 - -2011-08-31 Nick Kledzik - - [regression] C++ Initializers from archives not sorted - Added test case: unit-tests/test-cases/archive-init-order - --------- tagged ld64-127.2 - -2011-08-15 Nick Kledzik - - suppress version load command for simulator builds - --------- tagged ld64-127.1 - -2011-07-26 Nick Kledzik - - Csu needs to support for armv7 variants - --------- tagged ld64-127 - -2011-07-26 Nick Kledzik - - crash with TLS + -dead_strip - -2011-07-20 Nick Kledzik - - ld64-123.2.1/ChangeLog contains internal train names and radar titles - -2011-07-17 Nick Kledzik - - ld crashes with an assertion failure when linking WebKit with LTO - -2011-07-14 Nick Kledzik - - Personalities missing when using compact unwind - -2011-07-13 Nick Kledzik - - force loaded archives not listed in LD_TRACE - -2011-07-05 Nick Kledzik - - spurious warning: Codegen prevents image from working in dyld shared cache - -2011-07-01 Nick Kledzik - - Fix -classic_linker option - --------- tagged ld64-126.5 - -2011-06-15 Nick Kledzik - - ld64-124.6: ld -r introduces duplicate symbols - -2011-06-15 Nick Kledzik - - loosen check for 32-bit absolute address out of range - --------- tagged ld64-126.3.1 - -2011-06-15 Nick Kledzik - - Update armv7 variants - --------- tagged ld64-126.2 - -2011-06-13 Nick Kledzik - - iOS ld -r loses dont-dead-strip attribute on __objc_nlclslist section - -2011-06-13 Nick Kledzik - - LC_ENCRYPTION_INFO size can be wrong - - --------- tagged ld64-126.1 - -2011-06-10 Nick Kledzik - - Add back support for armv7 variants - --------- tagged ld64-126 - -2011-06-09 Nick Kledzik - - -ObjC does not work for simulator - -2011-06-09 Nick Kledzik - - clang ld: bad codegen, pointer diff - Added test case: unit-tests/test-cases/weak-def-hidden-and-global - -2011-06-03 Nick Kledzik - - warning then assertion when libSystem.dylib is missing - -2011-06-02 Nick Kledzik - - ld crash with resolver functions - -2011-06-01 Nick Kledzik - - define way for compilers to specify compact unwind info - Added test case: unit-tests/test-cases/compact-unwind-basic - Updated unwinddump tool to display compact unwind info in .o files - -2011-06-01 Nick Kledzik - - Allow 8612550 (turn ordered zero fill symbols into zero data) to work not just for dyld - -2011-06-01 Nick Kledzik - - Remove trailing /. in dwarf source dirs to cannoicalize paths - -2011-06-01 Nick Kledzik - - Sort debug notes by output order instead of input order. - -2011-06-01 Nick Kledzik - - remove support for invoking ld_classic in iOS - -2011-06-01 Nick Kledzik - - Fix arm branch interworking in -r for armv6 - -2011-06-01 Nick Kledzik - - i386 regression with pointer-diff of same pointer - -2011-05-27 Nick Kledzik - - Canonicalize dwarf source file dirname to always end in / - -2011-05-27 Nick Kledzik - - support arm branch interworking in -r mode (use extern relocs) - -2011-05-27 Nick Kledzik - - Add -page_align_data_atoms option - -2011-05-24 Nick Kledzik - - align(16384) doesn't produce 16K aligned globals on ARMv7 - -2011-05-24 Nick Kledzik - - support arm shims in sections other than __text - -2011-05-23 Nick Kledzik - - ld64 should only install to the platform in iOS - -2011-05-19 Nick Kledzik - - Ld assertion with unusual section order - -2011-05-17 Nick Kledzik - - Linker is not automatically weak loading dylibs when all references are weak - --------- tagged ld64-125.3 - -2011-05-12 Nick Kledzik - - Fix missing split-seg-info for kindSetTargetImageOffset - -2011-05-12 Nick Kledzik - - Linker crashes with __gcc_except_tab data belonging to no FDE - -2011-05-11 Nick Kledzik - - Fix nop padding for arm code - -2011-05-05 Nick Kledzik - - x86_64: cmp of GOT slot loses weak_import bit - --------- tagged ld64-125.2 - -2011-05-02 Nick Kledzik - - Fix -flat_namespace issue with not all indirect dylibs being processed - -2011-04-29 Nick Kledzik - - Fix sign extention on i386 addends of extern vanilla relocs - -2011-04-29 Nick Kledzik - - Don't let -ObjC double load any archive members - -2011-04-29 Nick Kledzik - - better warning about unaligned ARM functions - --------- tagged ld64-125.1 - -2011-04-28 Nick Kledzik - - Fix sign extention on arm sect-diff relocs so as to not trip rangeCheckAbsolute32() - --------- tagged ld64-125 - -2011-04-24 Nick Kledzik - - the entry point should start out initially undefined - -2011-04-24 Nick Kledzik - - ld should never have a symbol in the non-lazy indirect symbol table with index 0 - -2011-04-24 Nick Kledzik - - ld adds undefined symbol from .exp file to kext bundle - -2011-04-24 Nick Kledzik - - Linker typo suggestions should ignore l- and L- symbols - -2011-04-24 Nick Kledzik - - -order_file_statistics warns about syms in multiple .o files even when names in order file are prefixed - -2011-04-23 Nick Kledzik - - warning when a method is overridden in a category in the same link unit - Add test case: unit-tests/test-cases/objc-category-warning - -2011-04-23 Nick Kledzik - - don't let function from archive override a tentative definition - Add test case: unit-tests/test-cases/tentative-and-archive-code - -2011-04-23 Nick Kledzik - - x86_64 -- lossy relocation at static link time (push/mov $imm) - -2011-04-23 Nick Kledzik - - Add comment to error message when __ZTV symbols are undefined - -2011-04-23 Nick Kledzik - - obsolete -no_compact_linkedit - -2011-04-23 Nick Kledzik - - sect->sectname() passed to "%s" formats - -2011-04-14 Nick Kledzik - - linking a sub library of libSystem should not warn about common symbols - -2011-04-14 Nick Kledzik - - support movw/movt in static executables - -2011-04-12 Nick Kledzik - - Rework ARM subtype handling to be table driven - -2011-04-11 Nick Kledzik - - Error if -init or -e function not in image being linked - -2011-04-01 Nick Kledzik - - -static and -stack_addr don't work together - -2011-03-31 Nick Kledzik - - ld assert in LTO mode if libLTO suppresses a weak symbol it should have perserved - --------- tagged ld64-124.1 - -2011-03-30 Nick Kledzik - - log warning if ld_classic is invoked - -2011-03-30 Nick Kledzik - - Support "-arch arm -force_cpusubtype_ALL" to keep gcc building - --------- tagged ld64-124 - -2011-03-24 Nick Kledzik - - make libgcc_s and libSystem work for any link order - -2011-03-18 Nick Kledzik - - ld64 should only install to the platform in iOS trains - -2011-03-18 Nick Kledzik - - ld64 should build stand-alone and not need libunwind headers - -2011-03-18 Nick Kledzik - - add LC_VERSION_MIN_IPHONEOS to iOS targets, warn on mismatches - -2011-03-18 Nick Kledzik - - Make iOS simulator a real platform with command line versioning - -2011-03-15 Nick Kledzik - - static executables don't get function start information - -2011-03-15 Nick Kledzik - - allow_sub_type_mismatches linker flag broken - -2011-03-15 Nick Kledzik - - Add option to support merging zero fill sections - Add test case: unit-tests/test-cases/merge_zero_fill_sections - -2011-03-15 Nick Kledzik - - Improve error message about text-relocs caused by direct access to global weak symbols. - -2011-03-10 Nick Kledzik - - ld assert linking armv7 kext bundle on b/bl to external function - --------- tagged ld64-123.10 - -2011-03-03 Nick Kledzik - - linking x86_64 causes assert from changes in ld64-123.9 - --------- tagged ld64-123.9 - -2011-03-03 Nick Kledzik - - movw/movt don't work in dyld shared cache - -2011-03-03 Nick Kledzik - - classic linkedit does not match compact for non-lazy pointers - -2011-02-24 Nick Kledzik - - Support armv7 variants - --------- tagged ld64-123.8 - -2011-02-10 Nick Kledzik - - Switch arm32 kexts to MH_KEXT_BUNDLE - --------- tagged ld64-123.7 - -2011-02-10 Nick Kledzik - - Switch arm32 kexts to MH_KEXT_BUNDLE, if LD_KEXT_BUNDLE is set - -2011-01-28 Nick Kledzik - - spurious 'found branch-22 without store' warning - --------- tagged ld64-123.6 - -2011-01-26 Nick Kledzik - - crash with arm hi16/lo16 to external symbols - --------- tagged ld64-123.5 - -2011-01-24 Nick Kledzik - - dyld synthesized tail call stubs don't always work - --------- tagged ld64-123.4 - -2011-01-19 Nick Kledzik - - __text with >10 alignment should disable close-stub optimization - -2011-01-18 Nick Kledzik - - :upper16: / :lower16: not working when targeting thumb functions - --------- tagged ld64-123.3 - -2010-12-14 Nick Kledzik - - ld64 making shims when not necessary - -2010-12-14 Nick Kledzik - - Add work around for latest llvm-c/lto.h - --------- tagged ld64-123.2.1 - -2011-03-07 Nick Kledzik - - enable i386 ASLR - --------- tagged ld64-123.2 - -2010-12-10 Nick Kledzik - - Man page typo: "dysmutil" under object_path_lto - -2010-12-10 Nick Kledzik - - ld64 crashes when warning about re-exported symbol - --------- tagged ld64-123.1 - -2010-12-07 Nick Kledzik - - assertion if symbol from re-exported dylib is in -exported_symbols_list - --------- tagged ld64-123 - -2010-12-06 Nick Kledzik - - Change default search order and add -search_dylibs_first to restore old behavior - -2010-12-06 Nick Kledzik - - ld should consistently warn when resolvers are not exported - -2010-12-02 Nick Kledzik - - data segment has file offset 0 - --------- tagged ld64-122 - -2010-12-01 Nick Kledzik - - Add #define ARM_RELOC_HALF in case trying to build with old mach-o/arm/reloc.h header - -2010-11-30 Nick Kledzik - - Linker should synthesize interworking stubs for tail calls - added test case: unit-tests/test-cases/branch-interworking - -2010-11-30 Nick Kledzik - - Link Time Optimization error with tentative defs and -dead_strip - added test case: unit-tests/test-cases/lto-dead_strip-tentative - --------- tagged ld64-121 - -2010-11-10 Nick Kledzik - - Add -dylibs option to dyldinfo tool - -2010-11-03 Nick Kledzik - - Need support for ARM/thumb upper/lower 16 bit relocation - -2010-11-03 Nick Kledzik - - Spelling typo in linker warning - -2010-11-02 Nick Kledzik - - Xcode 4: ld -r doesn't work on files compiled with llvm-gcc -flto - -2010-11-01 Nick Kledzik - - ld wrongly complaining about tlv relocs for i386 - -2010-11-01 Nick Kledzik - - Fix -why_live to list all references, not just first - -2010-11-01 Nick Kledzik - - iOS is missing dof sections for armv7 slice - --------- tagged ld64-120.3 - -2010-11-09 Nick Kledzik - - revert default search order - --------- tagged ld64-120.2 - -2010-11-09 Nick Kledzik - - ld -r corrupts multiple non-lazy sections - --------- tagged ld64-120.1 - -2010-10-25 Nick Kledzik - - When order file used on data, turn ordered zero fill symbols into zero data - --------- tagged ld64-120 - -2010-10-25 Nick Kledzik - - ld should use this-image ordinal for symbols from re-exported non-public dylibs - added test case: unit-tests/test-cases/re-export-and-use - -2010-10-25 Nick Kledzik - - Value of N_SO stabs should be address of first atom from translation unit - -2010-10-25 Nick Kledzik - - Always print arch name on undefined symbols error - -2010-10-25 Nick Kledzik - - Add ld64 version number to crash logs - -2010-10-22 Nick Kledzik - - -objc_abi_version 1 not supported - -2010-10-22 Nick Kledzik - - Add support to ld64 for N_FUN stabs when used for symbolic constants - -2010-10-22 Nick Kledzik - - Change default search order and add -search_dylibs_first to restore old behavior - -2010-10-22 Nick Kledzik - - -L flag should support a space between it and its argument - -2010-10-22 Nick Kledzik - - Hidden resolver functions don't work with DYLD_BIND_AT_LAUNCH - -2010-10-22 Nick Kledzik - - Support resolver functions for function pointer use from same linkage unit - -2010-10-19 Nick Kledzik - - section$start$ does not always point to start of built in section types - --------- tagged ld64-119.2 - -2010-10-18 Nick Kledzik - - make having an ObjC2 class symbol in an export list be a warning instead of an error - -2010-10-15 Nick Kledzik - - lazily produce (crt1.o missing) error - --------- tagged ld64-119.1 - -2010-10-05 Nick Kledzik - - ld -r can produce output with LC_DYLD_INFO load command - -2010-10-05 Nick Kledzik - - ld doesn't pass stabs debug info through to the final executable any longer - -2010-10-05 Nick Kledzik - - __UNIXSTACK placed incorrectly when -stack_addr < 0x4000000 - -2010-10-05 Nick Kledzik - - ld mishandles -lazy-l option - Updated test case: unit-tests/test-cases/lazy-dylib - -2010-10-04 Nick Kledzik - - -no_compact_unwind should suppress dwarf->CUE warnings - --------- tagged ld64-119 - -2010-10-01 Nick Kledzik - - use ld64 to link iBoot - -2010-10-01 Nick Kledzik - - crash when entrypoint is thumb - -2010-10-01 Nick Kledzik - - If -ios_version_min is used with -arch i386, assume simulator - -2010-10-01 Nick Kledzik - - crash with multiple re-exported dylibs with same install_name - -2010-09-28 Nick Kledzik - - Linker complains about resolver functions when architecture is inferred. - -2010-09-28 Nick Kledzik - - ARM subtype not set on LTO programs - -2010-09-28 Nick Kledzik - - Link-Time Optimization assertion - Added test cases: - unit-tests/test-cases/lto-dead_strip-objc - unit-tests/test-cases/lto-dead_strip-some-hidden - -2010-09-24 Nick Kledzik - - Support -dyld_env NAME=value - -2010-09-23 Nick Kledzik - - Previous BranchIsland code changes to make buildable with clang++ were bad. Fix. - -2010-09-23 Nick Kledzik - - ld64 objc category merging asserts on category from old framework - -2010-09-23 Nick Kledzik - - ASLR Sidebuild: Many Projects Fail checksyms_read_only_relocs Verifier - -2010-09-22 Nick Kledzik - - Fix DOF section name bug - -2010-09-22 Nick Kledzik - - Fixes to build with clang++ - -2010-09-21 Nick Kledzik - - In Resolver::fillInHelpersInInternalState(), dyld never needs stubs - -2010-09-21 Ivan Krstic - - ld: support non-executable heap Mach-O header flag - -2010-09-21 Nick Kledzik - - Xcode 4 linker fails with "address not in any section" - -2010-09-20 Nick Kledzik - - assertion failure reading i386 yasm .o (not using scattered reloc) - -2010-09-20 Nick Kledzik - - ld crashes when parsing dwarf and all code is not in __text - -2010-09-17 Nick Kledzik - - magic segment symbol names don't work with preload executables - Update test case: unit-tests/test-cases/segment-labels - -2010-09-17 Nick Kledzik - - OSO in DebugNotes for LTO should point to generated mach-o not, bitcode .o file - -2010-09-16 Nick Kledzik - - FUN in debug map not rebased - Update test case: unit-tests/test-cases/rebase-basic - -2010-09-16 Nick Kledzik - - Relocation failure with i386 32-bit diff to stub - -2010-09-16 Nick Kledzik - - assert when targeting 10.5 and crt1.o/dylib1.o is not supplied - --------- tagged ld64-118.1 - -2010-09-15 Nick Kledzik - - Fix missing rebase commands that broke perl - -2010-09-15 Nick Kledzik - - assert when .objc_class_name_* symbol missing - Add test case: unit-tests/test-cases/archive-ObjC-unexported - -2010-09-13 Nick Kledzik - - linker does not honor $ld$hide for umbrella libraries - Added test case: unit-tests/test-cases/symbol-hiding-umbrella - -2010-09-09 Nick Kledzik - - LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats - -2010-09-09 Nick Kledzik - - support -bind_at_load - -2010-09-07 Nick Kledzik - - ld mis-handling std::tr1::anonymous symbols - Remove support for ordering gcc-4.0 compiled anonymous namespace symbols - - --------- tagged ld64-118 - -2010-09-02 Nick Kledzik - - -preload should not have LINKEDIT segment - Added test case: unit-tests/test-cases/efi-basic - -2010-09-02 Nick Kledzik - - trivial Objective-C app fails when using libLTO - Added test case: unit-tests/test-cases/lto-objc-image-info - -2010-09-02 Nick Kledzik - - Add -reexport_symbols_list option to re-export certain symbols. - Added test case: unit-tests/test-cases/reexport_symbols_list - -2010-09-01 Nick Kledzik - - LTO with 'dead code strip' can't ignore unused functions with undefined references - Add test case: unit-tests/test-cases/lto-dead_strip-unused - -2010-09-01 Nick Kledzik - - Warn if unaligned ARM code is detected - -2010-09-01 Nick Kledzik - - Mach-O linked by current linker don't load in VIrtualBox any more - -2010-09-01 Nick Kledzik - - Linker should pick default visibility instead of warning about conflicts - Updated test case: unit-tests/test-cases/visibility-warning - -2010-09-01 Nick Kledzik - - Enable new load commands - -2010-09-01 Nick Kledzik - - Do not pass -demangle to ld_classic - -2010-09-01 Nick Kledzik - - iOS 4.3 armv7 should be PIE by default - better error message for direct access to external globals when not linking read_only_relocs - linker does not error on direct (static) data references to a dylib symbol - --------- tagged ld64-117.11 - -2010-09-03 Nick Kledzik - - mask thumb bit off non lazy pointers content when parsing arm .o files - --------- tagged ld64-117.10 - -2010-08-26 Nick Kledzik - - 8F64 kills installtest devices - Don't clear thumb bit on pointers inside thumb functions if addend is negative - --------- tagged ld64-117.9 - -2010-08-25 Nick Kledzik - - no more audio because of broken thunk - Update of thumb22 b.w instruction was not clearing bits before or'ing in new ones - --------- tagged ld64-117.8 - -2010-08-25 Nick Kledzik - - prefetch abort in kernel mode: fault_addr=0xe58024e4 - Don't set thumb bit on .long pointers in thumb functions that point to some offset in same function - --------- tagged ld64-117.7 - -2010-08-24 Nick Kledzik - - dyld-179.4 fails to link, assert in setLoadCommandsPadding() - Fix linker to always put __text first before other code-only sections - --------- tagged ld64-117.6 - -2010-08-23 Nick Kledzik - - ld no longer output static archive dependencies for dyld for B&I - Add test case unit-tests/test-cases/dependency-logging - --------- tagged ld64-117.5 - -2010-08-20 Nick Kledzik - - SWB: ld64-117.1 on 8F54: Assertion failed: - UTF16 CFStrings were not coalesced correctly when gcc built the .o files and the - last string in the __ustring section only had a single zero byte at the end. - --------- tagged ld64-117.4 - -2010-08-19 Nick Kledzik - - DTSJ1J105: SpringBoard crashes on boot - Fix order file to move aliases even when subsections_via_symbols it used - Update test case unit-tests/test-cases/order_file - -2010-08-17 Nick Kledzik - - Fix resolver functions to survive ld -r. - Warn if resolver functions are made non-external. - -2010-08-17 Nick Kledzik - - Make it an error for resolver functions to be used in anything but a dylib. - --------- tagged ld64-117.3 - -2010-08-17 Nick Kledzik - - Fix thumb resolver functions - Enable updward dylibs and symbol re-exports for iOS 4.2 - -2010-08-16 Nick Kledzik - - Latest ld no longer supports EFI -preload - Rearrange LINKEDIT chunks in -preload mode - --------- tagged ld64-117.2 - -2010-08-14 Nick Kledzik - - Latest ld no longer supports EFI -preload - Add LC_UNIXTHREAD to -preload - -2010-08-14 Nick Kledzik - - SWB: ld64-117.1 on 8F54: Assertion failed: (categoryAtom->size() == Category::size()) - gcc-4.0 uses 'L' labels on categories. This merges them onto previous data and disable category optimzation - -2010-08-14 Nick Kledzik - - SWB: ld64-117.1 on 8F54: bad category optimization - Disable category optimization for i386 and arm until further testing - -2010-08-14 Nick Kledzik - - SWB: ld64-117.1 on 8F54: address not in any section - Handle pointer diff to stub for weak hidden function - -2010-08-13 Nick Kledzik - - Latest ld no longer supports EFI -preload - --------- tagged ld64-117.1 - -2010-08-11 Nick Kledzik - - Make missing exported symbols a warning to help adoption of new linker - -2010-08-11 Nick Kledzik - - Add ExternalRelocationsAtom<>::pointerReloc() to more easily support kext bundles - -2010-08-09 Nick Kledzik - - SWB: ld64-116.2 fix branch to label-4 - -2010-08-09 Nick Kledzik - - Error with empty non_lazy_symbol_pointers section - -2010-08-06 Nick Kledzik - - Add command line options to control symbol weak-def-bit on exported symbols - --------- tagged ld64-117 - -2010-07-28 Nick Kledzik - - split seg info wrong for x86_64 stub helpers - -2010-07-26 Nick Kledzik - - __nlcatlist categories should not be optimized - -2010-07-23 Nick Kledzik - - ld64 assertion on object file - -2010-07-21 Nick Kledzik - - Reorder sections to reduce page faults in object files - -2010-06-30 Nick Kledzik - - Support resolver functions in iOS dylibs - --------- tagged ld64-116.2 - -2010-06-30 Nick Kledzik - - C programs get objc GCness from dylibs - Update: unit-tests/test-cases/objc-gc-checks - --------- tagged ld64-116.1 - -2010-06-22 Nick Kledzik - - address range check should not apply to preload executables - -2010-06-22 Nick Kledzik - - Warn instead of error when CPU_SUBTYPE_ARM_ALL .o files used. - -2010-06-22 Nick Kledzik - - Fix assert in objc category optimzation. Metaclass also has copy of propery list to update. - -2010-06-22 Nick Kledzik - - Fix crash in -r mode with -alias. - --------- tagged ld64-116 - -2010-06-21 Nick Kledzik - - Add support for -ios_version_min as an alias for -iphoneos_version_min - -2010-06-21 Nick Kledzik - - linker could merge method lists from class and its categories - Added test case: unit-tests/test-cases/objc-category-optimize - Added option: -no_objc_category_merging to disable - -2010-06-21 Nick Kledzik - - i386 TLV PIC reloc content is negated - -2010-06-15 Nick Kledzik - - Added better error messages and asserts for bad thread local object files - -2010-06-09 Nick Kledzik - - 'rebase' makes timestamps invalid/unreadable for GDB - -2010-06-09 Nick Kledzik - - executable has no debug symbols when compiled with LTO - Added test case: unit-tests/test-cases/lto-object_path - -2010-06-09 Nick Kledzik - - stop promoting hidden referenced dynamically symbols to global - Updated test case: unit-tests/test-cases/main-stripped - -2010-06-04 Nick Kledzik - - ER: individual symbol re-exports - Added test case: unit-tests/test-cases/re-export-symbo - -2010-06-03 Nick Kledzik - - add functions start info to LINKEDIT - * Support added but not on by default. Use -function_starts to enable. - * Added test case: unit-tests/test-cases/function-start - -2010-06-02 Nick Kledzik - - ER: add load command for min OS version - * Support added but not on by default. Use -version_load_command to enable. - -2010-06-02 Nick Kledzik - - provide better undefined symbol error message - -2010-05-28 Nick Kledzik - - * ld should also merge file attributes from lazy loaded archives - * Move attribute gathering from InputFiles to Resolver - -2010-05-28 Nick Kledzik - - * SWB: ld64-115.3: dylib on kext link line causes malformed kext - * allow -static after -kext on command line - --------- tagged ld64-115.3 - -2010-05-26 Nick Kledzik - - * strip of .o files removes __objc_imageinfo section - * Added test case: unit-tests/test-cases/dwarf-strip-objc - -2010-05-25 Nick Kledzik - - * crash when parsing local vanilla reloc to weak def - --------- tagged ld64-115.2 - -2010-05-21 Nick Kledzik - - * switch back to using ld_classic for -static arm code - - -2010-05-21 Nick Kledzik - - * warn instead of error when seg1addr is out of range for ARM - - -2010-05-21 Nick Kledzik - - * fix -undefined dynamic_lookup -nodefaults to not error about missing dyld_stub_binder - --------- tagged ld64-115.1 - -2010-05-19 Nick Kledzik - - * Fix trie nodes for resolver functions to have second address be stub not helper - -2010-05-19 Nick Kledzik - - * work around for old checksyms tools - * Make i386 stub section named "__symbol_stub" instead of "__stubs" - -2010-05-10 Nick Kledzik - - * linking with LTO prints "/tmp/lto.o" - --------- tagged ld64-115 - -2010-05-06 Nick Kledzik - - * linker loses x86_64 addend to 'L' symbols - * properly handle addend to 'L' symbols that are ignored - -2010-05-05 Nick Kledzik - - * rework min OS version parsing to enable the linker to handle unknown OS versions - - -2010-05-05 Nick Kledzik - - * Implement magic section$start$xxx$yyyy and section$end$xxx$yyyy symbols - * Implement magic segment$start$xxx and segment$end$xxx symbols - * Add test case: unit-tests/test-cases/segment-labels - - -2010-05-03 Nick Kledzik - - * implement optional demangling in linker - * Add option: -demangle - * Add test case: unit-tests/test-cases/demangle - - -2010-05-03 Nick Kledzik - - * ld64 doesn't grok the modern-objc-ABI-on-i386 - * Add support for -objc_abi_version command line option - * Added test case: unit-tests/test-cases/objc-abi - - -2010-05-03 Nick Kledzik - - * -alias does not work with __OBJC sections - * sort contents of sections with aliases - * Added test case: unit-tests/test-cases/objc-class-alias - - -2010-04-28 Nick Kledzik - - * Feature: Thread local storage - * Add test case: unit-tests/test-cases/tlv-basic - * Add test case: unit-tests/test-cases/tlv-dylib - - -2010-04-27 Nick Kledzik - - * Accelerate needs way to dispatch based on instruction execution time characteristics. - * Add support for "symbol resolver" functions - * Add test case unit-tests/test-cases/symbol-resolver-basic - - -2010-04-26 Nick Kledzik - - * range check fat archives - * check that fat file slice being used does not extend beyond end of file - * check that member being used does not extend beyond end of slice/file - -2010-04-26 Nick Kledzik - - * The documentation for the -allowable_client option doesn't say enough about it - -2010-04-26 Nick Kledzik - - * back out LD_NO_PIE - -2010-04-22 Nick Kledzik - - * More ICU make check failures with 0-terminated UTF16 strings - * Change UTF16StringSection to break into atoms just on label boundaries - * Added test case: unit-tests/test-cases/utf16-nul - - --------- tagged ld64-114.12 - -2010-04-14 Nick Kledzik - - * Crash with messed up BNSYM - - -2010-04-07 Nick Kledzik - - * Fix crash with blank dylib stubs - --------- tagged ld64-114.11 - -2010-04-07 Nick Kledzik - - * for ppc, add split-seg info for TEXT pointers to DATA - - -2010-04-07 Nick Kledzik - - * Cannot build ppc64 target with ObjC code - - -2010-04-01 Nick Kledzik - - * let .exp files override auto-hide so that they can be exported if needed - - -2010-04-01 Nick Kledzik - - * support auto hidden weak symbols: .weak_def_can_be_hidden - * added test case: unit-tests/test-cases/weak-def-auto-hide - - -2010-04-01 Nick Kledzik - - * 'l' symbols not being automatically removed - - -2010-03-31 Nick Kledzik - - * weak defs should not cause indirection in static executables - * Update test case: unit-tests/test-cases/static-executable-weak-defines - --------- tagged ld64-114.10 - -2010-03-31 Nick Kledzik - - * assert with .o file with two LSDA sections - - --------- tagged ld64-114.9 - -2010-03-30 Nick Kledzik - - * L4 locks up starting a second processor, works fine with old linker - * properly get addend from content in x86_64 substractor when target is direct - - -2010-03-29 Nick Kledzik - - * ld should excludes debug notes when computing UUID - * added test case: unit-tests/test-cases/dwarf-debug-notes-uuid - --------- tagged ld64-114.8 - -2010-03-26 Nick Kledzik - - * __objc_catlist section loses don't dead strip bit in ld -r mode - * Update test case unit-tests/test-cases/static-executable - --------- tagged ld64-114.7 - -2010-03-25 Nick Kledzik - - * Support LD_NO_PIE again - -2010-03-25 Nick Kledzik - - * Rosetta crashes on launch in 11A133a - * Fix -segaddr __TEXT to cause other floating segments to be contiguous with TEXT - -2010-03-24 Nick Kledzik - - * Page Zero segment seems to be getting dead stripped - -2010-03-24 Nick Kledzik - - * kernel sdt dtrace probes not visible - --------- tagged ld64-114.6 - -2010-03-23 Nick Kledzik - - * new linker makes dylibs with no __text section, causing codesign_allocate tool to fail - * make sure there is always a __text section in dylibs and bundles - --------- tagged ld64-114.5 - -2010-03-22 Nick Kledzik - - * missing __objc_imageinfo section - * Real fix will be in 7780438. For now have Resolver also accumulate objc constraint info - - -2010-03-19 Nick Kledzik - - * ld64-114 does not error on missing exported symbols with -dead_strip - --------- tagged ld64-114.4 - -2010-03-16 Nick Kledzik - - * dyld missing LC_ID_DYLINKER - --------- tagged ld64-114.3 - -2010-03-15 Nick Kledzik - - * force i386 kexts to be built with ld_classic - * preserve 'l' labels in static executables - * sync section offsets and addresses in segments with command line addresses - - --------- tagged ld64-114.2 - -2010-03-13 Nick Kledzik - - * ld64-114 generates x86_64 kext external call sites with incorrect addend - --------- tagged ld64-114.1 - -2010-03-12 Nick Kledzik - - * Fix dyldinfo tool to correct show ordinal info for classic linkedit - -2010-03-12 Nick Kledzik - - ld64-114 is causing read_only_reloc verification errors for ppc - * Update machochecker to check this. - --------- tagged ld64-114 - -2010-03-11 Nick Kledzik - - * i386 dylibs built with ld64-112 cause runtime errors when incorporated into the dyld shared cache - * Add -shared_region option to dyldinfo tool - --------- tagged ld64-113 - -2010-03-11 Nick Kledzik - - * Allow CPU_SUBTYPE_ARM_ALL .o files to be linked into any arm arch linkage - -2010-03-11 Nick Kledzik - - ld64-112 with -undefined dynamic_lookup marks all symbols as being flat - * update test case at: unit-tests/test-cases/undefined-dynamic-lookup - -2010-03-10 Nick Kledzik - - * prevent possible crash in warning about can't export hidden symbols - -2010-03-10 Nick Kledzik - - * make sure split-info data is zero terminated - --------- tagged ld64-112 - -2010-03-09 Nick Kledzik - - * Never dead strip sections added with -sectcreate - * Added test case: unit-tests/test-cases/sectcreate-dead_strip - - --------- tagged ld64-111 - -2010-03-03 Nick Kledzik - - * Add support for "-arch arm -force_cpusubtype_ALL" to keep gcc building - -2010-03-02 Nick Kledzik - - * Add some checking to the use of upward dylibs - --------- tagged ld64-110 - -2010-03-01 Nick Kledzik - - * Don't coalesce cstrings across segments - -2010-03-01 Nick Kledzik - - * Emulate previous linker bug where hidden symbols with dynamically-referenced - bit were promoted to global. - * Added test case: unit-tests/test-cases/unstrippable-symbols - --------- tagged ld64-109.1 - -2010-02-26 Nick Kledzik - - * Make sure building dyld results in a thread load command - --------- tagged ld64-109 - -2010-02-26 Nick Kledzik - - * Better sorting of zero-fill sections to preserve discovery order - * Zero out file offsets in dynamic symbol table entries that are not used - -2010-02-26 Nick Kledzik - - * Support pointer-diffs to zero sized atom in zero sized section - -2010-02-25 Nick Kledzik - - * Add support for -r mode with ppc64 - -2010-02-25 Nick Kledzik - - * Handle multiple labels on the same coalesable literal by making an - atom for each label, each with the same content. - * Add test case: unit-tests/test-cases/literals-labels - - -2010-02-25 Nick Kledzik - - * Handle old ppc .o files that have stubs to static functions - -2010-02-25 Nick Kledzik - - * Add basic ppc64 support - -2010-02-24 Nick Kledzik - - * Range check TOC entries in archives - - -2010-02-23 Nick Kledzik - - * Fix spurious dylib re-export warnings that are just regular linkage cycles - - -2010-02-23 Nick Kledzik - - * re-partition bits in mach_o::relocatable::Atom ivars to allow more functions per file - - -2010-02-22 Nick Kledzik - - * re-partition bits in mach_o::relocatable::Atom ivars to allow more fixups per function - - -2010-02-22 Nick Kledzik - - * Handle re-exported dylibs that are re-exported again - * Added test case: unit-tests/test-cases/re-export-layers - - -2010-02-22 Nick Kledzik - - * Properly handle X86_64_RELOC_SUBTRACTOR with non-external target symbol - - --------- tagged ld64-108 - -2010-02-17 Nick Kledzik - - * ER: Support upward dylib dependencies - * Add test case: unit-tests/test-cases/dylib-upward - - -2010-02-17 Nick Kledzik - - * ld(1) man page typo - * Linker (ld) man page typo: "unredable" in -pagezero_size option description - * Typo in ld(1) man page, "-x" option - * man ld: Change "if" -> "is" - * DOC: ld(1) mentions -dynamiclib when it means -dylib - - -2010-02-17 Nick Kledzik - - * Wordsmith ld warning about missing directories - - -2010-02-17 Nick Kledzik - - * Fix -umbrella to work when umbrella is a dylib instead of a framework - * Add test case: unit-tests/test-cases/umbrella-dylib - - --------- tagged ld64-107 - -2010-02-16 Nick Kledzik - - * Fix bugs with -preload - - -2010-02-16 Nick Kledzik - - * Fix dylib re-export cylce detection - - -2010-02-16 Nick Kledzik - - * -ObjC not pulling in members with categories only - * scan for non-zero __objc_catlist section in archive members when -ObjC is used - * Added test case: unit-tests/test-cases/objc-category-archive - - -2010-02-15 Nick Kledzik - - * ld glibly removes /dev/null - - -2010-02-15 Nick Kledzik - - * Linker should be able to validate GC intentions - * Add -objc_gc and -objc_gc_only. Error when used and RR based .o file is linked in - * Update test case: unit-tests/test-cases/objc-gc-checks - - -2010-02-15 Nick Kledzik - - * Linker should provide a way to mark binaries that support compaction - * Added -objc_gc_compaction option - * Update test case: unit-tests/test-cases/objc-gc-checks - - -2010-02-15 Nick Kledzik - - * ER: Need a way to detect weak exports in dev tools - * implement -warn_weak_exports - -2010-02-15 Nick Kledzik - - * Add support for LD_DYLD_PATH - - -2010-02-15 Nick Kledzik - - * cfstring backing store points to global cstring - * Force all by-name references in cfstring to be direct references - * add test case: unit-tests/test-cases/cfstring-and-cstring - - --------- tagged ld64-106 - -2010-02-12 Nick Kledzik - - * Assertion failed: when class is translation unit scoped - * added test case unit-tests/test-cases/objc-visibility - - -2010-02-12 Nick Kledzik - - * crash with missing crt? - - -2010-02-12 Nick Kledzik - - * Suppress indirect symbol table in static executables - - -2010-02-12 Nick Kledzik - - * Rework CIE parsing to work with icc generated code - - -2010-02-11 Nick Kledzik - - * Fix creation of debug notes - * Tweak unit-tests/test-cases/dwarf-debug-notes to match llvm symbol layout - - -2010-02-11 Nick Kledzik - - * Don't assert when infering ppc subtype that is ALL. - * Fix spurious warning about mismatched subtypes, when subtype is inferred - - --------- tagged ld64-105 - -2010-02-11 Nick Kledzik - - * Use symbolic constants for bit field sizes - - -2010-02-10 Nick Kledzik - - * Handle out of order sections in .o files - - --------- tagged ld64-104 - -2010-02-10 Nick Kledzik - - * Rename __tentative internal section name to __comm/tent to sort like __common - * Fix test case in unit-tests/test-cases/tentative-to-real-r - - -2010-02-10 Nick Kledzik - - * Better warning messages about mismatched architectures - - -2010-02-10 Nick Kledzik - - * Gracefully ignore if there are >8000 line info per function in debug info - - -2010-02-10 Nick Kledzik - - * Properly handle when regular definition is weak_imported - * Add unit-tests/test-cases/weak_import-local - - -2010-02-10 Nick Kledzik - - * Don't try to coalesce zero length cstrings in mach-o parser. - - -2010-02-10 Nick Kledzik - - * Add work around for llvm using L labels for backing string of CFString with -fwritable-strings - - -2010-02-09 Nick Kledzik - - * Ignore labels in __gcc_except_tab section - * Properly apply local relocations to __eh_frame section so CFI parser works - * Update unit-tests/test-cases/eh-stripped-symbols to reproduce problem - - -2010-02-09 Nick Kledzik - - * Fix file offset computation with large zero-fill sections - - -2010-02-09 Nick Kledzik - - * Force global 'l' to be hidden - * Add test case with objc properties: unit-tests/test-cases/objc-properties - - --------- tagged ld64-103 - -2010-02-04 Nick Kledzik - - * Temporarily change assert() to call exit(1) instead of abort() - - -2010-02-04 Nick Kledzik - - * Fix another case in -r mode where the vmsize was less that filesize - - -2010-02-04 Nick Kledzik - - * Fix assert when generating GSYM stab debug notes - - -2010-02-04 Nick Kledzik - - * Add SRCROOT to crash logs - - -2010-02-04 Nick Kledzik - - * Remove architectureName() from InputFiles - - --------- tagged ld64-102 - -2010-02-03 Nick Kledzik - - * Add follow-on reference from symbol text atom to non-symboled text atom - - --------- tagged ld64-101 - -2010-01-29 Nick Kledzik - - * fix -alias symbols to be global by default - - --------- tagged ld64-100 - -2010-01-28 Nick Kledzik - - * Merge new/refactored linker to trunk - diff --git a/ld64/compile_stubs b/ld64/compile_stubs new file mode 100755 index 0000000..e142e4d --- /dev/null +++ b/ld64/compile_stubs @@ -0,0 +1,51 @@ +#!/bin/csh + +# Attempt to find the architecture. +# First look through the command line args. +set arch=unknown +set link_cmd=(`cat link_command`) +while ( $#link_cmd > 0 ) + if ( "$link_cmd[1]" == "-arch" ) then + set arch=$link_cmd[2] + endif + shift link_cmd +end + +# look for an explicit arch file +if ( "$arch" == "unknown" ) then + if ( -e arch ) then + set arch=`cat arch` + endif +endif + +if ( "$arch" == "unknown" ) then +echo "***** Unable to determine architecture." +exit 1 +endif + +# Create .dylibs for each file in the dylib_stubs directory. +if ( -e dylib_stubs ) then + set files=`cd dylib_stubs ; echo *` + mkdir -p dylibs + foreach file ($files) + if ( ! -e dylibs/$file ) then + clang -arch $arch -c -fno-builtin -o tmp_object.o -x c dylib_stubs/$file + ld -arch $arch -dylib -macosx_version_min 10.1 -no_version_load_command -o dylibs/$file tmp_object.o + endif + end +endif + +# Create .frameworks for each file in the framework_stubs directory. +if ( -e framework_stubs ) then + set files=`cd framework_stubs ; echo *` + foreach file ($files) + if ( ! -e frameworks/$file.framework ) then + clang -arch $arch -c -fno-builtin -o tmp_object.o -x c framework_stubs/$file + mkdir -p frameworks/$file.framework + ld -arch $arch -dylib -macosx_version_min 10.1 -no_version_load_command -o frameworks/$file.framework/$file tmp_object.o + endif + end +endif + +# Clean up. +rm -f tmp_object.o diff --git a/ld64/doc/design/bindings.png b/ld64/doc/design/bindings.png new file mode 100644 index 0000000000000000000000000000000000000000..7a563888edbf49ee4c6304cadf8bd98b789fd739 GIT binary patch literal 25309 zcmeFZWmHt(8}|(eIFt-MG|bSQA`L@#3PX1zjf8Y}mqAHOhk|scNQfXvhm;}=APvvK zpa1(=>v{dWyw{S&;&^7CeRf=X@9XpZPOPT70v-wVp%Ef5f|1cf-)Yi7s#`cF%)b~H! z{QUf1*gN_7fq%!Gz#sgcug&roa`#$3E2;lvQ0HsRf(_9nez18q4iZxwiRb4<$3>Ot z2J@ifhK6((M1|9vgCJ;8A;7Qe@DPFxNyN9j|6N*5fK-G3{U-#C08KuD0P?}i>SdyvVw`<<5zj#Q3GNL>sP`0qf^+R6Pzb2771`j7f;C?(Vlz+7z&%MYy zM75FUzsGuZ^}8#(T9LqkUFJl()+$+iHt=FA+ocDC%!~PrTiDe>+waBNu3HKuyi@jM z)Rmbyyx*(S4U zLQ7r;hkf`yR!PFPS=O)of0yM4?adl{s`+f^`iN955hzei;YpmYTJ**B_geFh)mJ1l z3Y01mOvlQM=8alB(JG+zgXrpQX6Ym7o~n@mjeJfSY8=DMBv&;+8+6{2$p86?OXK)s z&$2v!$M0bs7e8LNDbNPam;|1Fb%`L5cawP}Y>n>`!JtNw91@cZ)}9GAusI5DS_`g} z>sJ_N3RAtEJJQ>C+NA_WgCxirI@kj5RnEZi9-{W~f+Y&1i5|3U+$vS5odNCqKxi$3 z2s4e=vAmFqa<=G__9^NIhd z_O(sPQ;u^sO~PF=pX#}LjtlCuNM?}x(a0v~CB^+YQO$Rer}LzRk&tld5^MmSzgE-n z$FOq-u$(=}0pLrk~Bp`9?B+)JUM6Wg4GCAj5_GNsvgY!;3Guvdats! zmHPR&9yN)J%|iSZ`Z()^j82CW8I6-)D_vc$ZC^Q1C01<~aKVNW%)kGNl1c*2s3q(a zWE*hln*ISguCMyTf}`-eZtXQ;V6=@_YrOWC7F62xTNqTiA#H*%~S=o5!4v@ZDlS&tP%)7k@qd03T~&D!}Or`p3!n?`}F~*W*E9>qQq=mCLsG+ z^mjWC;||aXZ0SW5h8ZR8qdJV@KU;ultE*7pVoA(8{i+NZM&-`GF7e81TB)*ZQC>eK zKUwm&MK$5wu=V(+)=LxLwa6aASHZUzeRQ-G5Kyrg(16hr>X+}9gYRzKsL{_CoN6N_ zs_=vsd5p)Xro!ti;^6bd zSXI>zVlzZhW=K=1aMa5xVyh6|bl&{=ei#f4BQK}w_j=!k@Y)ARjnK7ahk&^xL`Cb; zvPNa3g<<006~ddyxj1}cvf}kcP44MO4S#8*fGR{P!s!W3h*h}TC3=a8Ge`hEv^9o6 zP1kRL6jp6^YsVNHwJ9J|ycr#!rPPQgeljV3+0_k=7dz~J@Mw-A3*8~`&%9lacwC9B zVLi{eb&~K87FvoIKkFZijO32)i4!VPox#vTj|1X3Rw3E!f*)$y4n(3$1pC1<#0EF7 zykWEg+6XMiSpz#xjqMO(xdee$Rbc5V5Cme1JX`%Y%VOxhbtth7C!uyvEHT#pGq5?Q zjKt;rKkEP zB^*AoiYo`mF>cy+@X!3ARD%0FaKhZkvs(jE7=Rgmrw-SrBc1$s(HF2nTOY2ZD6#RHT0aJb z=>_S{KmY!W8pb9GguN;)VZ9o^A203rhKfMeoM{@5pRPxoVtW#k&(fwt-KAqvXjJw& zKYX)1ki5IlK!irG-QiBYv`$l6M8n|#V}9pJDO!?)Xea6Bb-#{!a8rg0*<-L+mz*G7 zw<{83U2$}Nt{ue59VI17uQ@j=1eG?mH*9ZT)ipH>Jk2z+PL<;`&`jxvC5x@QkC4z$>z(AWJwXE>PJi6nwUoFV*@3-(J`Pa_gFMCUetB# zz`OXiur#D$W4eqTz|@}n1+s2{peAF-t8w0NgaEO9!2%>2_)Pb@1;>Ch;==z^U)y%x zi_t0lS7SpM8WI=Q<+{4NS+1ikMOp2=2~(%L&*e)8Yix|&1!xvBiATgHyy93(-GGm{ z(7}@`s78oFzkVADlcRb8)%_q+Pd_;_F@>BpxF2|_(3JkGQ+Pj>*rn0=CK1u(%RZrB zO<$sHy8nE$N?tX+-b~l<&mB`(;7}M6scz~&CKg1=(2w*hol>|&)iQMH`0D-OXoH5{p-^{f-om_0bp(Hg zw|+guKrLFDS!mHsCTCRBfs2-_LLLhSt6y}>Uq@PsL@b&jTo7J66HG#gg}ZZO&a07g z$B)dNrR8Tbm@%X%UBG(PcUf(YtJJ{6)@qk}I%l@R*?!oo9#3Z-8~@^^+2{;JZ)HTL zTWWJW$n>{1O{)P_lvFb`B^E-7MR9zJ7{%4jkjl|j?ik;4V&K(w(28BVg_jT8AIZR- z-Ake}wReL9YO(^?E4Esd=ZH^lH_?9Vj4+RVvq!IQ#pgPP4g*U$6@^WJQO0}2W5QAJ zgqp=Q?y+*;`u&2~Nw0~pEWX1fEN;m78uA@a1x=;8`C~v}c)_1aa@M%`Y=TJeu; zZfRQM?&-!qkhUvj9$nZ2hPu}I9Z8$D=cR@H+xv3y80`jx$zMIsufG7hGNL>(Pbr@T zhq6hZ#IZca6Np$8&rnL|0kDnf&jsSK1+`Ju;%oCTti7kOQwf4XIM>@xWNMwamrg>+ zVc1+XY52)uLorsR&5`;RX9o4+X%)p&fsr`7%H!~>{Ik%hBLlfaIOZD?Ilfcg$9zP( zQdR43ZMw_*>H7S(bA>)_S#fG%ZqwxoWY?zVkaD`D(%|7Lrfuy*%m8tQHETSNR**FK{T-Skx*OF$lJ%XGK+#2$S6+&heb z$|p7HKs$Y02J^tY!p9bTlN zR}*1=HP0RJys!oKyKV=g?6uBMk{m8(c;StZt3RNeTIG1p%IXi;Hz66U95|(piV?N9 zb3_g0BBU&EL){caTs(JY%WIn~gRrfMj1)4n0NxWHM*)7PS1zr)rKb!A$KspZ%Z{D( zN@NYHYrqmfae1@vFm+5Wn8%7J!$LSMtu!5<(iG*t3iVHLRM@GdvB}WdORNc?RS}w2 z%(u()8y&iANGF--EdHA9wA+vaY>N84nhX!DoH2523EG`G43zr#T{TomJ6f7uEOmSR zD=zXC7)kZxI9?CH7fx8UL$$v#Hlbf6Xt;V_EC*dZGs#mazh<4l_Ka=Ef%UMZOU1cc zsSF*13_#Lo1bIO4UE}6vg5{HAz5XKy8E!C&Bo!;_tKf0xcl|JLYf(b8EXu~aTn%jZ zgu464yr+{q2?;GCX{+a7_Y+OvClY^bs$VAPgvz|peI>@-Njb_daxy8?2l}DH^^xHd zYGN6WJFRDq`%8Y(kYsp~Xdaq;yX4*V@!A(BG0!ZEewft8>)Kg+)VsEW)=Fau$hksb zL|GgorNbM3klVQ69B!g#J6;T?RecmMDZMwL+56ZHv7LhO+eqfET<*oytbS4X4ODgx za(w>{CBL8bLt##|;`Xa+>)WFB%}dXXSo(H)3*;z*S~_CQ%!*>YwJx^(BWh<09-3{= z`)(xmk)^STPjnre?|<dnHiCc-Z%=*#N-mIQetzJmtrn?Esnw=6eEP`l$4p-9bem7 z_llTHnawIDTABM-OWmf3o{KUOMHY9*iT1hMF>rdKVe_80(0a&r^6bO>V7&lzu6!*_ zCF`W3JH2?Azh7|Zgm%%Z!M=rzuWa3R>iI$Vw_VA>w~_rj@ewI`z=ot?@2kGqFPgxU zsa1y%6jm6R<)%2`Ot*M=AYcHqS&~87Dvw}t2>WAo49Ch+7##$Ikd`a@QTz;L(Y_RE9CRa&;6Bfj5e4sm{3vD{7 zN2#MSsQq9+%_8PFz76cKsg1{tC$`7!HSJSyAFSEC>59|{_*b6C)&%$=Iv@;65*nR+0XdYYhsTWa)WLzBN^GJVoy?@(123J zrWCuiOGQ!AwkdwxsPm5tgci`zMA*Q{t>t})!pY87g6}53DSlyGJUqicKfK^CUO3*Q z7(_XAw%3KON=sUlY-jZwhqurnBv{Mrh@kAl8n*ESmSABP4#i~;eVGfLJdz)In%VL! zP`ZS(#$IWaHzdz@z7%)GM}BiS(pg*ODp}vc7=ZVBIFy>x)4S(+SH1KvE*a=1H=Y2I zX2f7Rp+etU)_A>nF+7y(vrMj-bf>gNRTM%`+^JT9P1u}wN$viApg$%v+K3Q`J1g|T zLrf>^^|u5Or{$x0amEK;F{E$?wFr44>u^!X90No6KH-_|^bO?@d0Ap5q*6^bm6UmI z$aj4?Qfi7!h}W|y{b-f1qzx6ve~hykH@^f%gY0g>85P*lyC^1jtM%15T6cP@wlVky zo`CBp>;EDz*yJXglEf9~ly(*x_|g37?ng#meX&)ni*3PS^`jpIvr9uPX~QzH>M?zX z!)L5etQ*Vm}KPKOq`}{FN~F97In7b@0KkDOks?*0o#SJ zOg<8_u*}FYyuyZ(U|N;7Y4@6<-zc)5Bbe(25dy35#5BU zJCt(W5xy;?9lNG*mZ5$FrEIsc8YPN$VxSRjjbZyV#=@P zmF1%z`^Rj4iL=#SRL!tVra3-32G$f}Yo>T&AIbxRD!Rpx1khkF{9#SsWdtrAz8Fe_ zDG7HmNJ&#kM)L%N&ps-SCX9hAh6)GABXd=Gdjmxv4dUF#V$co()7D5Qg<_!9Pnjzd zijxuq7N#`;5rfYjgFQtaGZA$!+gi+*=gDU}dbgopI4+wiiNu%WJxLy8-MPSljN4cx zC{@6un3?`e=U{h37Ko^B(|4L=Zy(^Pk*;-57uOQEPv=qkGVu<*wbLYPId*PBW=^xf zb9VPBT1vj1E2D7|8%iWBuj^=RBnp`lAhY8iao8tShMemAvHH#*)gPl-KKq7#_mfBG zXv+UPqu zltOJ$MZ(GZig81=kDol4@y5T6>ofmZx~o^Qvr<~?PD`(Kr=wpUVs<9HzF55ZJV5jp zexBSsB;e0HO1$1vW&+rSg|r8fOds3*wsF<|>^4#=!LEHp?Y#dY>FjkRstY%pHV1K4 zErKl-Ub1o2@%4{fobiw-CM#6rkwg7cLviNLtiV+0RYxjxn|**h9XfZ)m><_DB$j@Z zF8}J}dG6sx47na{!n2vPz+f_$DOfWCumy^Mv_F^Wy*jzaL^l->_NSll|4x&lQr#tK( zumZ-CSre5Wet6NdlFXfV{f-{I-A05ooDp07(VID|6lRoy)D`F{8AJagHrvf%xNKOM zyf%+L(^zIe#>-|vP&^z)maC2snK8I5CyKBb!Br{ilVplDO5(`R*67$18?-oZd18F9^zCLr(eP!GiDN z>8HGw8#oBe1b0@tg1>)!>YmTn6_;wQdR`j-I-p)UWivBTS(U`#jI{9DldFLa-R#nV ztLqwhP2^~i5RmaQc8g6W!oQ68gQmF30(i#}osXc%)G-xS+{5E3FFoZxktT~vTZ$IK z@-n<1n@En*KUrcYG6)g7Gjj=4nv-!e^i zI*$wyfrf>AdyJG}yN^ ziXn8LwjOf$F|D$x#DPDtimJk%oGp5e)wn2@nnZ9z>*8?ztP1UrGM&14PipXRtBl`| zr3;FS5`?zKYGpzq2IHyHv5T3tbr}^fy5lGsAU107glalDno?xJD1xM}%5<2(=biC8 zU0dgOO&=%N>aR_4ER;ZlY&zzj`PcjoSqs{37BX6(Y_x+;(neS`i-O>z_Ex z$6uvYMUOP!?dUVm?sDvTve3#W$@#r1RcHoM5{4CMSa2dNxECVax$6B8MUXRY zXDE1n&aXkM6e8i%`#+(6rJUe84 z8ua+j`zW?}k;VePyWQp=w{JmP*sc#uK&n}GTMV7%Qm}X{=TBDF>H2W%3ZzXxBBRW~ z-wPO8MhR%j)kdHsG75|Qjiw-B%&Eg^D^Bg*k#S|YS#0f$1Ly!kf0vyd!$UUsagw_P zlWb*sgzXG7%jzQnZW*ltr2k&cHD5hUa1Ys$W+28Rjwk>)UTqw+9_8~ z2u#hp4B9kIltEUgU1$tC-R&!&IJ4qJu!*|+t-Z-3-`-1F=l^w4 zx#*cBrulrPu6LhqPRv3ziS5N=Ux$PHnd;IlIa-ud5g15(u?*m-+JNJe1~Ol>)d)E8 z)-y9I4&9u0SvzE(bs@j8A*`x@h{0>E`BQnr2>NYD1$kkqlOW9g7u(4rueEPZH z#m4g#@oo{wG?M7^gO2i@@7^<;lyLinoP#Q6EYmh2ltW%-d=gjr_-MT^kv*OvR0QJ8 z1dptXP)&TH(-bBb_PRzKY-AY(nbfI5rbMR(MDHeEwY#e=uD;_GY#Rkyl_FA#t3Sr$ zT@&4tUpm54VeKcn9v-poiHfN=0@|vgStRHuadIRtlZsqbLym&Cl+OB(z6Aw=Kz4bO z=rQFSZb=RO?gWWH2H|nMyn|LegnQC@kp$hyHV4gOS?~P z5Tkp*8M4E-<<*?Fk$OKk|$aWZ){eH29R?-+hoBDR$J|&K{8fe0q7^#|m z#aBuVg<)VU57R66fq^5v*b~OUr;hFzB)^-Lhg?+1KC#k56_B;t-Mj?Yq|k_A`HyMw zolk9Ws7u&wSQDwHa@8VOwP#N0(XQ^Rodr#Cps_L&sGV`F^=jvKx9iX&d9|)|JLit` zIzD7H3Z${goLvF`xRah(V@qIdY)bd!)`{qdH32KtN%-^opRWPvC&df4r{E=pwUx@X z(bIWEoO~lwa?HQ`>1jH*LlZgvsS%`ZtpK`k7gLI$Qt;B?KW%EdPxJBtG&8flo*x4) z#lTFMAoHt;>&jV4Ni?gybonkNpJ6%r@SN7Q0sL_pq*ZQU4UqDv7W1Xe=g&&DeMec&lTVk_a+WdGx#M8JzKoiuUQ{`{OF~gmqF4sPOauTY>+7TL81xUv3abxkTF$H#4csbg?)*9G6cqIV0`v#hWI{ ze?37RDCct6%pU|tMdT_BDK0AU6ZP>MC7e>K>llwp>+uJPYqQuCp2V)6~`OPYj;}<>KukDIz zPvIu$Kz_%<=mvIMe@E~nQK)nPW4?q)>9#k?-R^UE>O{xWpmmZ3jEo3^}h z#Py{0!~i!o7W(eo_z_^RWdK=Mq(8bRkwE&#)&)T8KKC59j*33OAhG%<<@oJ4PRxJ8 z0p)v0P)|5dhovaPHAC&QIF%?ctq9QPq{knjx>ETG(q^peay2-hP&BwhQjKH~*lo<2#X- zm(43dFML)4&prYeIM@+!;Ac1RQW#w$@t=cH$iiiwVey8 zK$ivzA##Biw}hlbbTa*&jZK1Rot9N90XAkxbgfHZ^s5BL$?O6kmZjrzud;-95K?pk zJYtT-PRkYQ8kA?X3*5uqfgEM0uLbDU(_LU7f*`(O`%h1S#CqqZ!gf~s>qf6WJfVyz za@(9VpYbfQ`Al1Y2uvHfc0Bbz8X!~AQRS5zNw&#N*r_Is$PF?Tzu+X z?&Gdxpo(~%^tCMt!h@Kk9kI=zr5PO!lTPcR#K#_A)J3Z|TAG(fhwg;}XCut{8Fw3| znYaK7?a3SiO{Z%}%&+mC&VKw32yI4HW_K5?b5!#M-0@+ml@<}4aEj#z;6E?h?h|HF zUqdFpzCi7DzsVWXRYceWZI~Mc?5rcebf84VNFEUbI(cOIb(&A=+uo+@7i1O3q_!8t zff0Zt#)57vS>m!hV6p9OZu6(nyDz0-DSy&bxOUq!cOa3YH!ps7_*~>QQBT`Vp<$g# z0Mux$R#9u4c)DzO9kU7~`{ei&|Gq9rwEE}^%=6EEd~2l$Rqbbqj|Ha<$3qP0LeAru z5dC1H)&alYiNqrh*im>5$pR1*_)gDS)}lOt(4mTu$=c{gow^9>HEkSE^G3L(2xP59 zQMy1BvtH9_CoxB!kc*+mrhR@S`V$-@=x1`hawdn_QLkK2`dWNJaJFPYVq)OGaMKP3 zPWW0O%A{#vEh!2EoA1w>y<3Ru%lAl1ajO7$?gI2J{y{gOQEFW-_g`doN)2d84Spuh zrFw0V8Zt!l;?uo&#DZZPnAfDFQV5?V@1+O$V&J3+o<7_jj6}h2uXzYFod#)MIB#FU z`i8X@D?r7U`FGdM36R7Ma{;z(McQmGk=l0T=n>=H!ixWXbzh9ZY@!JuQ9yf1>%UWI zf&R0L%Vizaco;GKkjA&*JHWqWMqvp&aKtm#N(vx(m3JIiZ_zphNH7RT-~YDrzSoW% zhu5G58GNyyyV?(o_O+X)aN>O=KzdBL@|%_-#BV^q$#*aCk=&R<6~4{pxX?-h{@3@( z(wVQ%l^Z73V|j2zEDmFT6X6uW@&k|~X=X+lpFcGlOUz~m%3JgC&1xut z!fnK)qo!U3#=?FXBE6^N$TF|JG6?-namX_yZqGN6C{(V}rAxCLIr$5kG&^sJ^2}v| zu0+!2&L~UQ`^{O_Rf)_{Gq2+_pR=7pU`gatY-+Y^@u7n1`CZ1|)0-xo5SIl?Ivj z(in-Y3m{bE71jIE1_pcYbtusISvvsnR$Y8NvM2m?W+(X8#K$t6|ItAj;K`F4>oBP%8#a&J!#xdg4xor-g z;#X1q&_h;KRuQ&o%>S>}Z-THtqPF>Qo|Hes4r{EZ|3HjB9_Cbodcph77*A zS5Ed)w(q*r5txrDtez-?apfCZzPd_nKtpyeFvGPOZhgD5PyEHVj2#<-?-?T`q>Cm8)vo`1i126rYL~4{GR3r zF1vKBXb56W;5TzlfU}>y@B9SBa7i$AVY9&Kq~AWn$=qd@6$?%VdENxDhalAZVNpjg zp}v3SkHgav=ipV{Fv`}$*fGNNI%A_~@e?4KvM4@K7yU(i8G+R%XP?nf1;f*f!Si7W zoM|s=dE@S&(#_}hW_x%0dz1wZnVE6&w@0JSO>%=`)LB)?mMjbCvE zNqC%lfc?&nL#UUa0+{y2U!FpbJYUE>a0Y~VciHi0vCMfOJmFEMOTI@a9}jzRa?jL1 zCJHS-fjk`9?rwatS6+lhN@naP&4Z1|ji0!zHwP_gDf0bVMuOB=1o2zf7?xKZz*#YvW+$3ld#PrSH?>fG3P#g4t6Er2$)SE2M{R%%BI#}~^( zI;jHPw@qw*vGtD}>@P4V;RCco6LC^0tVb6Z;NFLo8^=pc$%1Y`L$)+NGtf%OK`&HNjz9E3{j!7V(LZkeNU+_^-9Wq@E9w7B* zkY_?cCn@96(6#ZZX1yn^>{wt1DhF2{vlGqLMfcsFl^x0)uoI-9*g6?HJEkF*Pt%e3 z;uQ*Mc|<5DIZCvtaMLX%3X1fO-+fqCs2)zOf$7i>zmsIwIO19rFP!Rmfl`XtOEI{n zDt|$b-%g!v7UXCDGnAG=nd;30{pDEe1>dS^B&aZ)v0px%P<#rDBfYi1RmM6@U{gOW4UAJhhfQ7X`zZo&OEuCmAH9=Ve(58 z55fR(aB-hZNr7k5)-XfUW60Ksu&J(yCwV|$yj0;^xmVGetM*`fVunmk{H)%-LBD58 zV|=$ObAjtW_r;G+#@r^T? z9r>7JtTF({klQlAVPzjz$L*1d|71n4?I0Zo+LSHI?x6-G)C8rQPwBhtWpdR(L?X~g8;#0WffwWvXi#hJSEUx!g@2kISVh#_i=^f%*V-2=D1qI_Y#yp;3Ze zNTj-0du6?oFxLa6PsGeMHe%v4Lmy+fSprG#v?H`C65LK~JrS_4%?M>0^H*nqN!*=1 zWHWV(6hjE2nJ%6+f!*_2^${a-dhsmA;|R#-da+kN7!iT;#Fu1`IdVxKZJx+<2E?-@ zAQ)<^UdE{>gvl{$3=5ach)?+nd9iTkO)7UbuwB%86(==SN5!~iM5Mkm9%`-bJkmxU zwD1smRFq_4H|yBmtZ#2Io(0J1AP;IiW;)+;`pxNFbp6UuzPUVQubsdt?c~F)^K~k1 zXD~0>5Q_#x4C2Q4e>jA4h&7BEB@UPyW3i zZvxl{a=g#lNqOITJ>r)a9&KM7C0-*tpO-g6cDd#O8H7sSd7l|_q9I-JnyQ^$I3Z@c zrFpb>`pt4L?wGU(?onV?NmJ7oq$0Kab!R=%flaJZaT?cJ@!b*LTOPuUtA~%M5Xc9M zy{tTkcy9<{hfV}1CwN(I6=y#)#OR}^ZZq99EC&;-xT{mkm-#h6t8nh>x0brZPf51$ z@m>v&UHKjGm?ef1G;P{rtQq!+^yT)h;sjisz}(ayxYQBW5!bI;7Rn8r8ZdC?i^$uP zTCGglobXIU+{zl;HA(H0RG}KaLZG!o*$Znwuk)xwxEW0GmAFO)H5RW<_uxp=Mxy&r zRIj-Z8(W+xt#QeoH$lSn4zpI1ihg%ayAM7z4}1}Z{hQ+(^Fymu)&hL>8pA4~Jn9E| zhooz_gL_gBO%wuG**_gVC6}id3VSc@*K24Yh5tZt^Us#Z9#~BCM2iW-#P`n(E67A} zM`p$T6<5C;8Fsit-9a&t6?J+3@I!a4yxr}-_i^5d&8(zVDqTO^F2H7^J%od;#V>}1 z_QF*7f*zghTZFFX*=9U}Sjh3-O2cz=R4LhoIg?k%Ll`=XWiB@{oWFQ!T}LE5{!uA0 zqqN}qzVyw3##fEHOHv;Z>#}JYlhmKjD*HYGGlrW{uo9z`q9+K>B6I1~ zbdOxRIY?f>ynko4xK|vD`97|nWO)lOPS8|$5a6{RE#^ZrbV-Rb>}^Zh=Qa4uPCXa+ zK0-GMYC*9y!z!jgJ-1Gg!WB5+0IY1Ma<9Iph0+IbRcCG22q?kSvYVQ`aXECAJQI+l z_@Ei@O!%cVYuT&MDSxOO6C0cN*TvrAv=sU*wrK1T?teO#DrUawC>gp?4l~Lc)Z+*o z5bqR9zUd{hqIvlV_6Rm8rrGo)E(xVNh^^nLwqy1}BRz7^Y|8sUO%n4?N)@3s(!<@c zG`HuO`-jRf(a!8k?ROuNW{&lnPPR$)xw0+(UkK5y5idb>OLR8{{)LQCTwmD*lRCJ2 zNiz{^m|vNA^mBHFmp94a@L_3-wouK=&e42iRo(2>FAodMP3V@ z_RV%F@dMvnUb;kdLdWI%pSl0bkPF}gtgt>U&g;{*@Oi1W$heq#P}+k>uU>YR*!-V- zCm)g3w6^6mXOWz!666;o*-L=Tky+eiEdFnnT>M^j&C43c0eL=lc&<)}ERu7y8sq&O z%)B*+yjREqd3RU}4lkTE1)V6o9DKbnuzWpemV>S1*8*$;ac${Wf6rZO%0! zIb$`$x66e~ob~RJZ^o-_DhAoGo<+>uIkQ@f2Xd=GtzEG4@8epZbH)ERt-MqLN9Imk3j&woSFAA zPnsJqh1shbj~Jf^+JtMQ*3CjG+1HNq{SpJ*DUcQz&p$v@VYA#gcrsxD2ay_ilK?IM z!ObV6L&<*TNySdsn-GhnMLe7MFZo?yLQwvqTKBZxg4crAk`qpd&`ZJ0FIng30q+1Q zS4t2j+SU{6QL=8~XclLt7!QoGb(J5eK0Ns!En$*Sc$NG|9UETu8rwZrhkx%YfC)4g zfj1pG0X753LCQY``;J}`9j)hP*spU9B<%61QmgnIHV$Ej<&EqCA`B{TkOr8I@>AO> zSusJs{>vjLNdw7MAjd-rO5|Z8QPd~~`ANlru~+=k2Uwg4EuAq~k?5HZPZTt~BHC?E z;YlrO^WV0qC@TV`Adj2k8> z@h&HCeDb8dk#$^9&_AUnjH9!W8{C$Si-Tz_ad7=hoX`c{i>-L!RY3dx)PcMu3(G@}TaU9;Ne$#>O#vfe&O>1({jChYE=T*w<#~+of2(9xI zot`A`Tv`WEO&HNNA!OY|9udHUpxx(^b8g1>NVW6-eNlE^w0MwOKXVvEf|BO8x9XW7 z&O**AotW&9Y>4}?pUF_?|60Wknj*@JU7E8a8}{Z)CrE3qnJogB5h_8zFtib9VpsUT zGNF?^ma1@4BAbAk>7(;k^1p{R6%9x364h7Mh5j=^vOGdlgAJ}*&kF#s^RFusoeehW znJ2fk|1;sgVt`poau47JtUjpzi*GY=0MV;focI2RiA+L)zxBmz$BO?wx%wV4#$}lN zkM8{60`40TcbVkl&jQsf<&v32U6DM0_t73{Omc>;a?g7L=ll@wz5{kp06L0u3#J!K z&HHmo`*q_+tbve-{%Rpcba?{C8Q=F8nLwfD{fPdub?-Q(!wJbd+`A3%`wb`La1`K2 zBnPGe&(gA4voX{D)Fm9YesB-9DS{9=f+e(Tnb7bU2XhX;=||Gq zY0j}EOb9wkDurBXlqWWA+{o{yCFnoFVjoVe&J4qO>fg^bV=K|9_haS$twImr1@!&p z*!(rJ^T~G)`0ofzXm15B6pGm8!mg9gyiG0mXCB}`GuI`yOvpE5&o|)D3oWq#LeNq? zAb=9^}<8%4U5;m>G@^-)_bnm8G1)PxV zLi<&Xt#>!SgDoym=g4fV@>T&t_RIFA4QccEdHwI8{4^A`@NX*c=6f9 zH{`uT`;HFSzQhrK#b?wO1N=i_7?`Y!N z&4@961|0x0=ySii?rslCvQ0fUl0;qdM1;g@0H<>|XTvb`FF(Gwczp*n{tvY9c>BTh z&tiz#-x;`kjk5bj_|G!V0t1!>;7kgI(c;>b?`NZm2@UBhSw zGIeeDdhrx_lz0bN3ha)&-0Ht2lzvheWbIJ`$3>_+r@&^RH{LIPz0Yp@@yZFUu!&z6 z;DCvu01gY^8f$QF(fv}&l`?csxy1o_T8AxJySm73F<7CNY+=Nd0A;92&8A+_LVLoL z-=={FKwo%w(9<;1<1m>i8GXa zEg^4QalHbNs>M_Vnq7q3)jej{Q4`D*1x$)doGLB%`h{y>3?%++>9$e`#~wAgX{S@ZxPU|5iQ59Ph5 z9)IX{k-@1hUFVbnt$53~hq#bcdt=@lm8~eTfno=A18SYNUa&psJpfLJr zD1pbnE(z9|?h#ZSz&UbqDO$90m>r3MB|)kNE6VZ~el?779o&10{nf>e`sv>|H;gz> zs%J8jr9%;8;+HpPE72cI|GysmipeKBFFD}DqM@d~_?_2Cm1e*<-AbhXqzA|f`E>kw zsvkO_4$KiLB5-*;G?1wU%WoqB+IEbt27u@1*Y7uW5r9rLsy1Kv`ur zBauZ7x}wU@OYTPv_Pv4D<^)Ql?XWB7n(-DeU#v6dZ|ajgzEIG8*_hn+i2)datZ+I+ z9C+Nhahx=Z)LKec6zg*OE-C*0dj#+qXtNndQ%AK$x5j{DoXEW@lYqxJRSq1XYo5#D zQ4|LmktFd|jqBK=We>v}`@%qtx>nI3_tNq^@a{_=zPnIj3Y4tO(>tF01c5f|KYXf5Lk?l1zt#9?3i z)=>d+Zp_*2y(9>g8zsu=IvS)r01zw2-Sf&9@f6P-Uo1?X8+Sx=9`|m0sl)3=+S+}aWSNgQH7dkMhL^K{Lz{eL=2q+HX*lSque`wEZf82`*01j`V&*ciZEOjyDOP|Ib!5eKzB-8`}AG=TwI?)Wt zSKw4l%BIzsQ4h?!h{$e(XH587G?{j5Qs2nP{WeqYyf1+DgOHThZgUU#)`tTw;7bB{ z!j8ZWOBi&voSd%IFyclSMU zmGF@pdoyz37Za1JnVH>Nb+jQe*boV#@!KRAT2!c-0L;Ho-2#IR*XXMrZHNqE3at1D zkZ`mpG3Pc>H)u8{ZAK$GEfY8gTLQjP6`I_0@Q?JGKD023Lp0dC$%eFk;O?I_0G;Jo zaZ2DFbOTfhh;u4pv%PLd^R1kF|DJA+YL0E#86Y2^QjD08U2^NgbE$qmi%luBzB?oe z74&FOawbV!qX&f8) zK;e(<)fyW{fRRmxX<|;YuH46neNi9NZR|Ntg4vLAfclVXwcH*s!5a;2`bTt%RdFiD z2X%^B149VkY_0%m)i+8B@a7j0RNIIW@Xd~ua$cDkF*GrfabuEpj*WfWt5q{=W;kUa z24lQloiYI)G@MPLlR=KGy57Dp_4fZ*e@%|&cfTqg>nR@He^Z9bC~KKl%3Ji_2x8%( zhslgpO?~F1pJO59Rj4l;aK8iQNvf*0s?7cST|%9;0_C%&0)PhErc6(9un zE{520Q%gt;awpFm6?)_f$jS;tH-}yo{XcD;cQo7W|Hc(1R>dAQ zV%3V>mMSqKR%=RMjQvW*?61G`V^_50vDt$^RXW=zlZWnr zJ)X7OXuJ6~BLje_$jS=a4=%@_S5u*ImZ9EHNYA(hL5#g>YKJ?CpY;xFQ#oU z*Q46Uz{_*U8882{Y^FesrQa7RllQNb8rdR-T0Yr0L^H=%_N=dNc%E5ESN;BMumhr5 zBz(<qcC@?Z)e9(_=$GoZK`jK>QBp2bP3hB<{vit+Wtb@7|-wtq}6aB8qAUDbv_(f zEG(mWYam2rt%_4jqV<(&*Tb@>4D@UPl&9`qcwMd-#p2!MYen z{s|<`q!O*(3ND6et2Gtu^B9CkfqQ1cIHMaGO5m6YJKSB2%+U=C8U-llmG8aaP9aB> zQOH5zowl)T#h9){lum) z*fVa^oKUn`P!QkRwd$S8%~_{mb;c^oo}vxSCGbTu$F$N5YM_(21C!ls-T@%vUQU1o z>ft`;baa7|psfg9rmyP-n3tU9UJVGBX`f13L>1O_sZMR)!2Gt0*6MZ``AuogM)*t` z$tlG}2RK!i!ZZz;)Z~Op{4?W4b3+a`dgRT|(ca$sfiuw;7k7yB2M-Vni&!(T-_7ql z+sRsiEx3Q`E9($Uzc2VsecaszUv$lIg59`9GR|R(fcpIa4FO7@xZc3f^ zq#nD%P&K&UW85&0NOYb9JEpb}i8(RDrjV+rULAL1yKV(?afr=XS}yvU4!UlmuN%ge zik|D6Uu_|BysUH(c6b%U&PG{4+9S1l8>CzCK~fHowLzN2?7f#%Mf_tjJ)xE(o?1jk zty`GghR3pu!_|;AdN#JE(5ml2u`+K=u3*0gn-llIE=1@%cJhv(?Hmp`_n0E3XII9G ztn}15*A%jsJAV)27;I)nigIG=#n^5qeX>5(&zIGSmgvq;(zaC}P7ooM2gj&x&pQo|ZlmW;zZTOGG$A6bVM0v~TNkT|B|1UBh;(}#*!F?+&c)89 z@#Jk2H=#8i{SYEFQQ<7_+7j?y%A9vGJ=fWkH(|mD*NUSd$h{Q%aIb-PFp`a+ARz7e zv6x#CD$aJi937WnsIMx*aCrkk-i)b$gy;ql&)S@#KaR^g&#Ry>k-9%b;ZuwYYXfC< zfeGhwg<6!S;qx*#>y&&M&VR>!XYAw5;u%W21Ep{un!Lp~KceuJe0Hms&fC)Xv@hz! z8wKsE6Ajedl726}o3uHZV|QX~CVHyC03vYCWF?(1wxciD?EJul*`b%SYv{#&w4&Sv zl0_(5&buKH1}@0SwD@DwT}@!`*dd2(4nm7J>tYNVKEkdaN?;U1B6c6%jUCZa2s7pq zCj=~Jv@+vhCf36nt=*3ox1kYocFHRNX*(ApXC7BPv_Xjui#1K3UrXsogJ-GP@Xa3g z{o|^=THPa(V?7r_=IlqYxs~%RoN~iS+9EZY5&YqallK8?Qc%2wzJrK#@ zU>(P$A0_z$h^pInvv=(w%yB-;;oBdfzOf%O{!*Jg^Jm{*z*^&XgUksN>pKI zJUT2vAI^BC>_5e;2)NcCez4c$6;QX&LeqF-g2s!LKTDHdws#-RA$oi-42oN1Ep{0< zy@p+Bruh>pe{zLm)QT7HtR<&bZXIaP*S2PhdRW4cm0&mV2i9#43L(t|Fet_*I4ZKDEN0?Px*F!&`wr-s2|gKBd; zBWJWm3ET~vUsGTeO-K$?X|h^kA+fZ#%2~<{-j6!iI4pMN)80tR??MU`%18P^@K%opVKiQGaSW`9s45bwyea8uPTh=8fMbdF zbr5C%A0z!CwfhXeu1{~bl9TuljZ#I=y(AVsd4addWfe_wQO&7ob&?BpX>Xu<(b+)z z(hm)6pyPNUWr8v$u&eVZ;TLXMx6M9&Q8`wWk~1Yi>b<`wzqH@^2Zc&W=f@-UyE+Lb znp^2su9P7wS>5sKV%n*KyzG-+bnQ|`C}YQ9s9WaMXKO!$x0&{M zUUapajn8Y&XS2z@&(WdlyD@DS9xleiaLG3Tp@OFin@If|nBrR8iXIdyt7uH0B6XAD@zgj_ zd$AiiODiv=8nZ>#kw_i{`;q7v#V6Fvwj7bROS;+0ktwk5zZ~>@++z1W>2~;A*mW&V z%u#PZwHVHE9x9h8Eod}>+t}GHQK@6+Wt4llb{6s8A``7Smhr zXWD9+*8Ht??q0LhnuyG9U%}kF9Yl}a+(|O~E`jgdGE2p+*h6Tl>4%#t-0?q zkNZj~&d}QqK`Rd1J(AvJL5xS0dik;xnj|+9-`kPu-)~3(i+I_bQATvAbU*r0^FDLq z9KLGqimuc*B{U`7q4!~%{4Ou9*Wt9(x9@DoLx~sELO&;T9sV(@#`t-Z@68Of<|i*lFD-<(jCv!7~h zi)}UCGYuVkYlgC4-csUFQnK_uk6?^*hpi;l34m{|`c<<&FPNLE_xKU@^!g>ytTj6o+h zywmAfjO0BoHv#P?zh2i``?sg6u9Y{@L!!qwp*`_rQOgrzNsgm@!2`QzMf`xm3s5Be<7!Tw$?Mjg}4Z zL1RHyEnQYwI_W=lq96hd$Zsrr3sN{l`Yqvp`NUQdou0B~bPQdEeCmvZ?l&2qS>TVZ z`rY>$AM-RWSxY}=9L8K|qntr(GgUptkm z;B&=@+&KBZXY}FK;5R3 z+(c78)v zj`N<2gBv6?2tSP_X;qraJ8=uIMud^iMk+ix+cx03;I5bCJ!kvuviq*KN!96H#$%nc zqHymIP#_I}Hl-<$S0MVvbhJ5i+4w?CV#0x&xU#%^RRSlSP`nHv!I$u@n3lIiZncuT zgZTjz>3%CVy0SLGeS`MKbUJInAvMD1D`=IcVNbosrZ5Q7dEM!3SMhc#)? z?C-8AN?aZUOs}2~tApXSm>spUOT8cqd>h5Y%F7IFpsY#7#)KEfgyRn5Qw_GFofI9J39=AmQyI zHe2Df;3rs|eBIT>9IlTaZ{tr2mtW}9#BXF2{mi;kczO_n-X$O@r-a6}vkTvd4g3vp zbbr)$;n5?=@s8928EaD0jFvOnWmb?3q6NL|rAkB7*!|rjX~x_Y;)QS={T@ON3Gqdj zcbB64QLJ;QM7Cs!75SP4iPfNqiJyvaP8yY(3h6cWU9?mBefdj257W$!{!&cCgnkwU zKN2?T$7l4Q9*oFj(CK%HcU`Wx=dW-db%SFh>gMFXD z=4Dm{v^?EgFg4^hw%{lO^;~TC34h%;Aq6-!qO>?l_baIwQbI0ntEz?Cg_jPyN9e_4 z2t@2e&u2N?QXt?jn|4LD5=BSE+$$ms#$!)6#(0@gjKZ(ws2=aW5v6SU8~8z4C7#b# zvd&fNxw*OLpLOPS*;VZ0ct$A>EyE39%;16lj&uq;On|F$_bxY-qd2e=Z)DUA{^wE}C1=X?@I!bo{clJ@kRxgWIiA z{&*h~9&xYL**1Ss@9EWeraX&3*DCb0#Y1L*4Wyb;fBr(SG~C)rvt`2WkTpV*_9;G@ z?};hwlu^rPaRH~$Lf83J2j2E9?%ff*y+SJW&maGo){Z^dEC>?y~VR{IfepAb>^cyffUgh0nX?+`s zV7?PkRETRJlwOrfLT;0mAT-9;yPo1EOc_Ex5cKV=DC}K_qi$3JBZE5;^4-9UpL)!?I=v_fAbdB6kvI59A8eLk>9WT_$ zrf`@%SvowRU6H@_CizARgTr8cih1I|V-1uH5tiQ`u3(IEVqBjdb9r|=3*o)(Tvm=V zlkc;2Od7BtLH^5D*eH(U@4YM4+?z^uDV!FGn256~{a(OZVtKCQ0jHSX%Ou(1Kkrxz z%7*6NB$Ygvb!@KmIw_pXxGd-GAJWog;UT;to)Gd+QP%A~d5;HMSnKvLZ80y{^XYP` zNH5@qcByHvY|`cDkNp|s5*E8<*Fuz7MK_|)^_+UFy~DH;qFj3ysIIW{w2fSOXzTeB z%k<~(xn7r2;sXX+x909QS8l#zO-JO&eYW||V>@2$lh_vZdkn@CXO$=$EyOpidlo|w z+=gTG9qS$lZ8vjw^XA0?-5HXi5Ll3+lvPjtO|`rcF&u8H4rtzgA?z=Qk$5?qWO1G! zRIv?lbZ~nIqzT4WhEq4P?e-72v3zbVK~}P2>4(%(Ye4>c8 zSUsaSDgf8h>u=cMw-5`E_xe?_ebNs|96SM%R?34A!1Vo-5Q^Zgmg+^$iS^@emBOsj_yatDz=eyrARZ z*CHr4Z{|c?&7+zW8lG2jH;1HzhdcO_yd{+LQ-OU-9@!I zglBd3|Ig{eO-JSVwPI^{OSQzZ-Mh@Cn9l)!6s_HbUax+Rk%m0xOn(!jIf$NqQvOM< zJh|V3c^~`*n#9C8^pjydb?gjwI9O}B_&vOlCW~_tK(N)Rfd*J`xR~-qj@q1=eEsNr zNuk*{jzu-xV$scH-DPk(B4MbMC~@6@(c7Y^!mZIe?Z&*Yl7PQC6SONF?G-%isi`5d{}?O zRKJwy0M{(^=T15N8|~PZ1=LJhF?~bOKXQz70~D&vieS&^+k5_Isir+2E9%_9P%aQj zjwQPbr#_4y#YlwTRkcfA(J_cZk?*(8st+1)MMo&Jw&r}Zc$C~RnI@Hm;=3|T&B>nc zPw7ch6gw#ir~Tn4iUR-v7y{7&P1~wIwh<8$gDeP+b_G#yT{r8$jtGbSo=&>^*wN3V9{3?N5exr( zKzG^ZaX@&Wm)|IV z_1-U5B&7J6U!Uw(YFH

w!BOuKD5vz-wozjIHjtLd?gMzkV&48m;yu>+CQORGR) z_XEIKt^<`>1h#_NtWb){4rpRG%SAp)~lAH)kIPD#GfvEeH+HO6a384S}5y6U!kQ*StPO>zijak+?l>w`sy!=yQ?a<$}jE zmt37CscE(h{TtNQ{`ntO$f_wrpi)(R*5IQC{T?-`2TuXwhgorgn#pdaF&!1b-N>+V ziJE~d@N2i-l!UsF!6@u+x%38}T`T<8A2rSo&H>B8`qFtvpzeHq;+ABWRra`W)yCoG z{MpH%S>>8^tHOy_pqbwYrjj$EJGTI@c)~_3@+K9B_stA~*$8(g61vDsw&i31+D`%s zEQo49zya0a7XOdP`CeND5x4}-H8|l)u&(;Vf01#2TH46p?I#P&$;L$s5$qfKfuXde z>Jd{Q9G{LV_ogY$BF-9Ci*K3LsBqeFigs$w3ysdEcyQq6fH7CrLVEBX^Rk4;y4le9 zr(o(7)CRq-4(w@Pe&V0D{=K^Vv}~xhJ^qu-l_(z|q=L@|z^lsZFI;N({(c|Sda79v zIbBEo@BKI%C~JXtu^`3iZ-9a>62yIY%QW*g{`VeObV;9mEZ%ON`PY(oT?P~zw&+pu t|2&KT-yy_R=d9b${dY(|W7>}BI7K&S&y^|+ga50bF+rIdROq`r_#bn|7qb8W literal 0 HcmV?d00001 diff --git a/ld64/doc/design/hello.png b/ld64/doc/design/hello.png new file mode 100644 index 0000000000000000000000000000000000000000..9140e1691c246c3cc47aca957863d061f7224ba8 GIT binary patch literal 11577 zcma)iRZv_}v?UOn;I6@%#@#)*OCUgScM0wqtZ^E52?Pih+$Feca0#xByHDr6H&Zh; zZ>r|wcHipSXVvX}&RKh%wJ%6bMHU^E1QiAb23=lG>J#+)33@dnBSJp`otfq^FsSx6 zl9Fojl9H5aE>4y<_7*TOUa)hiR+<`11cQ?uhp}LB`cD#Pa*3=R;jHBLq3{y)9HDXK z#G1xZ*x0bri3N=O1mXiBk<=rU#?|jQ>FFmy2&9Z>h!kV(H|u`BM(c*h*;k`)5Qs0> ze^NIPW~k%~+(#T{1aVF=Wul$Kp2)};(+vt(;e0sxd`wy$h-R>>aX#Dq%KcYD@vU(^ znJwMGtlPhQMFKTt_AvS4=0f2~BPufY+&$Ed;^~Mm`qAgcmYFanc$`5oSOSziLVaU7 z`-~ieV^^S->;y_}+7cuf;f#J97g$PkYC)QBYv&=7>EQ5HA9xP3utf zq@)E<3$n>tde@)L*RU@Y>HV3^1fGfefoPY?JHH*$T<*YenNoj--3GLGzFw2^(EaE`Fo*UEC_ER4AqGNEY3D$3-XGr2_!gLw~2 zU|iB~33|fNUqGo16L$0?=_U3aHAN>@5?xtLFtIeXWn6#lLb-U!6oUvou66_;EWG{s z)C%*9LHso6e(qJ9Lh)Tt-6U+j|GmbnS9E;EYXci>kr?{H;C(6Zb7CpFI0r zTKDI#@7{+9ZD13@Zg1dY1!Ec`hES481zRV;6`|UEL`)zyM;8!3O<===mzJ2uBHWTZ zoIe6?ZlRY3pUhi@wzis$I}7{YK}AtmZCP*`BXOfXILY77>s zAVeM{5xoo>@4wzQ!;qn+g^v!Bca!%T^r3GLIOme%=k&vUg$p2h zgAv2db1Lm4vE98Tq_n&05i=l=i4>m38SVpl9NTgum+acn(pxfGDuclC({c{tw|| zKXAT0ks>>QA14dI2w0{K2N){i&^FNK02bu-)5r_HA1u4kiGU21#N@ja->6HN_n8Tqnl!4U z9d%B>KjU}B2jYdE*y z5CAYuSTiK$Iwd4|o{9YPk#?q*minzG0sRNH&I;jT32o<3@@fp9Tk<)}6g1q`cFR(K z8fy9vCs^=vfr)lwD8l8G<;N9S3{E1KgO+1j<9tees(i{HanTFI8)UL&%4C|tbH5qBYW1npD$DZ3(iSL$Y zg2$0BphMHu=Fg0XqHkINUk$rFXBm&TxdP7(?>Ns1x4+#Frvct_?luP%jyv|h_Uk6E zmKncm7cC-E*P8Ob+<&oeI%vGNw6S2Qx39^rpQ(>({8`&-#bQQoZD8&{j%c=RTDP>9 zJ#M9^xWc`X)EKx(yaZV0SyEY`FrRt}OmvEPy zm2B&!?bQn73HN{RiN=QV0i;7x!?7h(WJ=3cOM^;?Pw<(Lr(?!{w=-qCoLOtPy0uN$ z%6S;WI(e&a&vggFXXnR2@#(W84-x}yY7BIU#Il~XafC|-pf^}W-Ftr+A0qKWr2CS8V{i7 zTzAIf+Ie)8_wWxPiFhl?{tVOw@xNH{6%2Bg6Y0JYWxTqrU4NL7IQUo}$~5HaZfm9kgEz(OGMZ{Wu*b$-3f=ce+@e`IXYl z<9uA*Le~OlIco}YIv&%QSON3he%r?!f53U@dG=j7Y1BRwKifDVTD0lB>Yg}5$PPIU zqYJYLGm2i6UI^rVsd*k;GH`A~jADt#JQ+CMJL_y4bXJuFxyh>xFB*1rI26asgv{JM z&7U4K>1kYc@M&lziY$vtBkB});RpMq+6`nP=Pb59K9w$aKv2NxNQK-@w|}vKp0|s$ZviB3%JRRHX$a(?4r!nQXcPfZbVnsY!a*29*f1qe&p;0-j=n$rea z7d*sT$($cNCZlqLnMAd#3~rizS{7RFws@ZG_8;w^CJdW8*8_SlW%RnF-!hC?0z|ls z1*SY^k1PlC$Bp-=E(_3l<9gE^Th>~Avb>)L$lrt{Jtfc9yPA&Hhx{jAKuGeK>%wDR z{8!eaW@FnSz)z|pxr`!}56Wjv^Vp+_SKl4Kzf6YYiIAAZcRY&R+*NOTPclr78kTli zXR~MV^Br;s+`H_KUTBACb7?%hCA`Yc*{v;6z&M-?yZFkxTtf0L+aFOsFGlcN1EW)-l?`NHb z)fheaq^d^;%}sxi;47nAEIB?4^45@qfwA&@)ZCxPclFajwBm3@9+R$MzLu5%s)y4% z{|A-RyWFv1fxFeKr}i_~b=Nb`vzE12OR_% z;S_H1uu@sa*EvW4kA8(wR{SnX7P{C<}3Be9UtTMa{j!N&i{r?8wv@a4t#ys zZxm9x%ZpB)|J@luos~9(la}zVeIo>o*%C}=LXTMe?LZTLOTmLJQT{7cZ9+}~-|2_4 zruH{|o%oKO6n>Nm{tiA?8=P=_TykwwtVpz_4qAK(F6FKFZ;uOIx|dw6`~0*FqiS0q z;-9_^kMfG9vmAJn!i0uiCRfh1mu-BUCyCL>eM%lwr3s%S$A1+R{C>m~d&W`~d*N<$ zSPAcqz}%iy6?14jXAJ?5i6)NuKi&3k{Qkz_u*|*cvEo1^=vE34md{H~owq;9NQ=V_ z5tPmsfj4a>Jp;5RJg>I6=8L)Ry|=Zs{bfFkgNKK=(FelSX>kEyPzYNCVKT_L?e(XT zF|sK^*frFaeWw?LADVP>MSKaa$9hx16<{fh6BND45?;8qm_qpVG8w#N1z>8nR0${e zfXi}B+|ZChK~ZtK+NkS~jNryW`OlwU2gyB+2jj@^w?Di^R#Y(SyG={a_sq`trrpD4 zO_?%;h#QZt6HLo{=oW4EMr<$JRrf#M9H$&`(BlG!Pga_~H#ZYCt^1VcNVV8fPt>29 zZVD{@)PSFefR1A|PXhqb(lonUTLo7G-(H4>hkyV4Tt2bTm>Z1~Tv$l0?{iq3dcW4@ zi6m0JIXgRh;x1g-4owIPJY1qd*?&JZ?odkm13Rwym8{qWV;5PZ2Qul?^XS?@kaKXc@sC4H0oVg=+N9EO~w~!^1--9jAV;NjX zZ5}7JI<6rpcAg{O;)=9OkfkIjv6lLyA}t7uzgvj;T$dV{%GfH+n#u-^dYUR#yomLp zY??Vq+ac4ysV5mQM_{elEtUDr&CL;UTdNV(xR@%vL&E5*F&oTeY1^1gs8#O~qM`!| zVe>2rpmtg8yVHPFR4rzKtvEnCQ^gnrr!!7NP%i&!ubQiIt?PL`o#~g*a}$_irkG$_ z;NG?AoW&f#M4Kq$w&n^8S;<2e`wK7XW?9gYAQCK9n^H41CctNZ<1fA*ij)UNvI4-o zq;Oi};*UE9T>U{v+t5eQy1-)F9|eC05fZ`0f>LN6`qkR842IV~{@G4YX1Ai}G-(jl zT4=!H_s;*by3g2u;nBMGwH@aPLz6f0iGQ9j{-b5J((_>FtpcwW8PC#}F+~@3-teE+ zCvr@2B`?jR&SY6~89(pMO*st)iup`jmz0O>t8qU#^}BC3@`5`y&7Msi1SnQ7fX`ng zJbVEs58~!`^GaLMh2AVNeDMR@@h}{B+g0~Sz249F7ehls-MyG%G0oUruUHCEAG&|A zl&cvBd_0`5gehFo&%58J*RKa2)%^AB~ZFg8j@=3x`I(k=GJGMp<|T7T~N`*b=J#_o>_ zPVe09D{Fc`;Db~FGcUJIUH$7Fz79rRS2<@JZGT@_@7Xo5?l$7`wo|8Dx(`09lz4Gq>c7yN>e6dKyXu&nc+4#c4P<$Yz@=KuL@z2oEGIxE%dgX!MR+mWUX zoEzh$=P|~v3t8NOPg=P^V>v}KBc^N!9u z(eh^Tt)gWlR|c$0>NpQ^c($IGa%^0Zvl%e;XF<*u z_MTx~^7aO2Wko++?VIJc1P>hF>FBd*c+K~csx|DfZgf{)lx{wb1)AgpVrW%byrO8% z@luxCbVmLt6gMw%0p0QbP}rBIxo{R+r^XEj=?(w`G;{v;6v-!@r&RkPc>eWoTI1r# z6&VU_LVjt*Ut+vEW9!949aA*DnIjrYcEfy;;cGnF|8)l~WJc_Ip^1FgGUJ`3Bxq6> zVn;c)_pX>u)?b%;?MgICRF?dwMV}yCPXj9zU+Q#4cCe+5xb2XzDHFZNCn~#Y>*}V< zwb}IjF8V$8Nm#2bvVLM{l`P=|i@(6)nFo9jEAvIrp7RgUHK*ij`$o91f2jJc1+-}F z7-LEFp)&fRq{*7pO(ak@&(g)8Y`Ke;#`yh~%9ZB`a|c(g>$Zej9R2vJDzb!|9`A4h z|AOE4tdjMc4c)#^6}by)@Yv>J&4YA|^<7Q##Ibubv+rz@JfK{G!zsqw)<%hHzUz{N zS43^`o_nVKlP2$LVaW1`_PO{bQPzatrJIK2_~41?_#f}B>C{$cIGYywbOH2lg+>lQ z`=L$he^8?Y4t>e8o@NFg(W8DS^{gJjLDoBn)0}jo^cKk3jpt3*h8T2m8?biIIwHuU z+4xlX{7Ik2q^8tdOV>&=aOlu0b+F|q;V{O9oQW)ED@Tz$odyl~_6kj=?D0epAx7S8 zotd!L`iWL~GFxZa4{WI>Z_Q;yIXU5~D)Ah#T}z9>*8_j!9X>kjMf&Dt9O2^|&fi7z};O?4cavJTlt&`(zneW);{i{Z1FLWM9X=+xf zl60Czn(_iUG#OGEj0;w@l^o~59q%n=il7oY;m>_)I$K#lP@R*_r7g-1j}q6Gp)@yF zZBnIkzZ1nnyJEVZW~@&?pUeSo4rIlWs`a4M;K824{?IF*Lve1$ z@JtWSmz5kZWo~K78%W!Ouc!cafbUt}^(3ACc`ac!c*3RT$hgSgm8ddW|2?E(paXcNGZI{*(<^Gu8>0wfdq{Lp> zRka|hirgd%(Y*hn%ImNk3I$y$7UsB1T4l>K$21zwwMVJ1|G=|UOrN5VJWiU&?@SF9 z!=FEcKlk!ei7SClZWfH3YVNjJlN_=$qx5)MW8z7ZIkfR2A)1^Qnw+ooJ%2wv;Ay|T zzHoRzbeO-?((X;zAXH*+(A4&MbO*tXr{e~ivRxVXp%IaZNWY7-mb6Kb|NEdLmyyCjZ)(<p5a$T) zuVLy*IjGXRg*4M)5YsuX`xonN`Fk)JCtK8?ghn>5F9ZQ?Y!Kgb`+T$aQRvybumA7# zf6DTzii-DltFH0>7`c;OxJDWJ2!Yxt)eDe)Y!6@_>NVMStXx#O5t(QefG||-mnQht zMTM|3Gs6dxih7+>?u}g$f~u`GwQG%QoyCd(ntXT0T1d8^KLgEoZQs0 zr@kCMN3@9hJkKQ-FC3=dNkw$I`mLkK6|_RLJ8SM|pVp;IlOd;a07vtZ%fxoPDy)p& zqRSDSyfcN_t>`&mP1yOV_{@XI1&+Zvb?!B-@bJXlyu8QJ1mzH3a z2uG+zD@|>PRjLGfbosZE6^v`qro&b>)dbZA87r0G^j&-^1&37%qpbwWA}%ytB|J=J z$E+(sB?-Cr!{}`-nO;d&^|$5Kee15q#Bb`pSeH0t03tE1xe%(H2$$VoNWB zpFD{qGOz$U>F~1E?!X_k0+Qe?=M0$8gT6(*c)R;j3r|^UmiOpWKy@{6ssdvuVc0H* z(}23VK=0^f4yrh39a&B&Z0po+G_pE~n0$D&S;s4b9RvVPBp6<@o0ETvw+s3Y435N{ z-bg7g#(a7j{G^>9Q}VXez|H;;#RCj(&>1K#UqUC`u!oDa?{16HnIY|d9ntP1i!b{s zjyj`dZJmZDKVu#rtN9&RkGWOX9qsd#*p{s9SZI4l8J5uyOTkVP0{LqRG{u{JVGy!7 zZIj*fC`NNaCRVDe^X$LH;4s3m$lyNWnWR5p)}){!d225ISJmOuxS%Z=4`MKVC4G^$ zbB65DDX`&9=LpS}UdMZIh+Fq>hqUw`mH% z+4arfHAQnA{YYkWtAp652nfrW7~#Abn>RougiGZtRxvZw02a3ay73tP&7CIlI5&uE z=RKPZ4i4f!H{!HAX)*U+o~iAL9qLBB`X3Z!hk{Nl>>J80;?cXjQsXip5^7ZCSXoq7 z?*F?VVJRm2d{~mlt9S2(LHFK0ap%9YBicRFzTgutjUatM zNYBh9fig5a0s_LV#yFF9Rehf054fS7e3R`cnXi<*&+R-lTlz&)U1J6CHOBrbnHw0I1eBd=^|+oQ9u5w zj$7Rdqb`b<#~W9xJ_BfBb9}lzffk2>%BFQ&7C+h^4GdO6K%3Af6eSO0xRe+Inbb3P zcXyped*aT!jx&>+qdBm0FA+9ns@Y&%f2;eku0L(k;%FbK87>M#)#!a_H%aM?INyO( z%d=Jv!T!tL-@&U%v3SpQZ=hO4z1j+Lg-#9JcbV88WxlmgY#JHvH|G5gb>Ky#-Qg-1 zv{VzGT{RFSSrC@11kcniEG(Ffrm{3|#|gO|28jN7m%9yqy~h#y%4AEG@ZrS)^GA535+X zK7d#~^D*afq3#5TM@0KGt>COajiBsM=f$v?`>-q}J2V&-oNG?l0}PZan|=q@|620i z9B%Kx;${~P2V$nHoBzRI{~)0B5i1>1PhD$|*P{VD2X!PQJTksYQ?O#^$k8te$$k)= zQJ}1?Gp(Vm^)r|2*H{ohPLK-eP-?A$2B-nui2n)*P8%{oJr+ELA=W55f*&1-H-3It z3BxG`L!mrZi-3PbXCtNT;Q=+1nwYB?GKJU&yMPidiNR!pUB!#Gnxhn?0T;~Vk&tza zNQ*R-ZeNvckfgZWX@8FMLN=7FXmJQ@N5AT8@*e6fN@~|=L-ojBKXfHUm5^G!_J@4NacPF)5Z&h4(};AZz%? zf;dn$#Cow_&|KlR9uN*V9n>@5QdQmanyUrkzRWuyQTrYq+(I~us}rf4r?y7lQAG4IaR4u%-|jnbt+h_z#+ z1KiPT!{Y|7h0Ri96=m6yyDpg(qR=3|7YO}!=O4Ej@?YOo1m;iu@MKDlP=1Q5m)I`U zlHH3fSH;LTUr!%+HtS5!Tf%hEjR`@@{pPTB56jF--{)iIe;C)^h#JB1TJW+o&O-*yY7r%$yG_xwcRMbEJ>+gs=;Z3}Nsk|`p z#>f$#=3VcY^X-XWzux#ScKOo$@_>z+2TKjs>>c;2T|e)7IR^@i{FpO6b^lhg`%PxX zA3GKESH)j`=JK5A7wGAdunQq8aF*&*Q~wQd!nd4JzU^P~KVN!1gd6MLcP+jK@=890 zOVhWxAC`b9oSuh;8_J7)$_?b*8Wbb@y^%z33vAvl?T8E->kskyMJvV-BZD|} zTq9Yn_3B?;9>8;R(Nn>zGq0CDzA2_N6iB5vF7DX#?_txqbTVN9PqFpqO>I4xinR!i zj;h{x%!SQSy*)}N^^BH!o!Ct6SL{*cmv^@xdeK*aW~;fyrM|auGNXRaJTb!=q5Vz^ z#;fJCiFf5obb%a;W3aB1K__n$^?8R5T?KGqhu-w;H)~IGkks3Eea)?l?cR0$uT^cQ z5_hL-mBP=T6CqG#JTx)U$eEaC@l5r(JCAN~UuYpwnE69-)I&tUptD4G3yYK&Aj|Wn zc)V9}^1v*$WAE1?j_3T)*q&>QlGD}l!YXFBzTYrTjsO=2{Up2x`S>xahIOTr)dzOT$mEweiJLVR1Ou65_+vwagk zXIT&?a|WLFN`DP{Z`j#(E!XGS)qP&FyDitV1qsZ5m&Ih(Z^jaPoQ{XwJPgE=UW{=~ zE;{5l$COC#E*Q(kQ;@sr>3&?0r5s*>kR!1lN1@c$>D*=UO(mp+EqexjYnSKSeh_QY zugkm9Oyk)h;o`CH-k#J75{PTRHPSKGdO*8EclCE%jOEM1gKo;eC@N3h0m8kN-iKKr zkzH=^#b0*Z-?a~36NYCCrYn~Zba(zk9{`ps+64k*B$t4zz<2Y){@wL|gQ8eed5!7< zIXoc@+XJz^lL5C}&%11aH47&X4-Xz~hbFz@{e~^`Sg7z5W|?5FPB){v@xd>+=T{x& z(`T*ni@)+YX0P#uzP^Rp|It2gCiL-dKI?Ci2~5l<36CW`;!b2NhTUcMAKO2x7#@$Ap00ZAT#^Vb&Q1zJ+jv)-%5-jek{HAOH z?Fp7K`;+@BP(ho549>^K8pN;wUq(fv!Q4;(+JXwJf{U6m;={QkGYd5(cu+4W8K((@ zHw9{nl+<3kLm0xumCVx?2mwkAJ7h!^8>|KI3}#sQW5g+IHCp<72md!;HPC0vw~)mR z0oq6EV0y3gREkUwR|&;w8jy^~3!b9J@~m|&r(Sbqc7Bww2h|8lQ|iK-&Xcr=Ccg}^ zw(Q`L3H11D&jH|qzstsl5TR`X%=M_FrRvMwJ^Z1NYrZs*&g%T(P%$kH-Ifco1Jl@8 zdZ(h>g9hds)=dq17?fB%OE2@1k*XGE&zW{rzco}vcDtu~mco>?c$d&=K}}v~axa$` zxwBrjG|*54R>l3`IELh+*u+h9cSgZx@+C^tlxlcg`cN*E))xTrY-@4 zO2Ea%#hX27Z1ui*H)nr8{8O%AfT9Glwo63c%U{q*5bV254&ZB_;m&Wh9BM2S=rAkk zLVeRUc`u)(HMjq?PvZXk_<=k1esZ@ zM28avmkz6h6}n6(9!Pl991#=m)u9d~odW=XTi>t$<(>TUy>lcJ_0tKA5Q~?%@N_ek zs)!`6+1}5&&j30SkDoPR|A%t|c=i^b(;x^gy^FPCV5IGuVEEuwQC?p6mqf_>l2#>~ z-$d+kC|-nfViN13Y4aNr=GMP{g3483lG5&UB#ithsMhmry%|zF3gLO|4u0oAGCc8j z(L2t0?|bn+eD}+i&53+KhoiPbRH*od|8{>5bTN&nZI*gv!`~Y?`Uo}yOv#F=loCegdp1sJVnbZ2LJo4&3pL7U<56kgb^+gE0T0c-VK~akY`RXs=S*5 z5bfd|0g2PS!_hKZ~Stq1L)b2GR17dHO6U(F=!nMnS$p zsIt{_No5RFonV0S!BQFIZy!9wI*xiM{ZZb~NASn=l^|HEqz8OpSR4KME;XrDg8>iB zSp)47mgxReep?Td-qvCwC3S4!^#OIgXWc@x!Ql$DIWD|{zM z1Q#4&b$2Hd$wW2GM4&J6Hk0F>o|mg45x8a|dW)lpJUXT5H4t`B+nBbaaLsNf`#eZM zSX-wjp+&_Z*+m#V(Jm|tED({K4wUd=^Ul}<^2Yb$G7ii!z>!N+I+~{;b`)76c!i3B@+Zm~;D|3wIDC!GNd~QqMff4UvoAKnzz2X4W2nT} zGo3JqQqmQeZgBmXL>pc(O1x+2^i15>-0`fvtCO9)NqVbsrkC4;TydXj%EtVxoytZr zDhRrXVLN(pQSg;D;3XoVj2bF2N5ueLX==5y64JAH!B|i~9J4^pT`o$T7Rs}UYSY}( zl%Y^om891s2mc!m@TJw{R>nrAg7PXB>gR|eXdhdAnBxD2ybtt^@1BrtIy-{ClL8|z Mts+$=VI1;50NeYjJpcdz literal 0 HcmV?d00001 diff --git a/ld64/doc/design/linker.html b/ld64/doc/design/linker.html new file mode 100644 index 0000000..0e39f2f --- /dev/null +++ b/ld64/doc/design/linker.html @@ -0,0 +1,423 @@ + + + Linker + + + + +

+ Inside the Linker +

+ + + +

+ Introduction +

+ +

The Darwin linker is a new generation of linker. It is not "section" based +like traditional linkers which mostly just interlace sections from multiple +object files into the output file. The Darwin linker is based on "Atoms". +Traditional section based linking work well for simple linking, but their model +makes advanced linking features difficult to implement. Features like dead code +stripping, reordering functions for locality, and C++ coalescing require the +linker to work at a finer grain. +

+ +

An atom is an indivisible chunk of code or data. An atom has a set of +attributes, such as: name, scope, content-type, alignment, etc. An atom also +has a list of Fixups. A Fixup contains: a kind, an optional offset, an optional +addend, and an optional target atom.

+ +

The Atom model allows the linker to use standard graph theory models for +linking data structures. Each atom is a node, and each Fixup is an edge. +The feature of dead code stripping is implemented by following edges to mark +all live atoms, and then delete the non-live atoms.

+
+

+ Atom model +

+ +

An atom is an indivisible chuck of code or data. Typically each user +written function or global variable is an atom. In addition, the compiler may +emit other atoms, such as for literal c-strings or floating point constants, or +for runtime data structures like dwarf unwind info or pointers to initializers. +

+ +

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

+hello world graphic +

There are two atoms: main and an anonymous atom containing the c-string +literal "hello world". The Atom "main" has two fixups. One is the call site +for the call to printf, and the other is a fixup for the instruction that loads +the address of the c-string literal.

+ +
+

+ File model +

+ +

The linker views the input files as basically containers of Atoms and Fixups, + and just a few attributes of their own. The linker works with three kinds +of files: object files, static libraries, and dynamic libraries. Each kind +of file has reader object which presents the file in the model expected by +the linker.

+

Object File +

+An object file is just a container of atoms. When linking with +an object file, all atoms are added to the initial graph of atoms. + +

Static Library (Archive) +

+This is the traditional unix static archive which is just a collection of +object files with a "table of contents". When linking with a static library, +by default nothing is added to the initial graph of atoms. Instead, if there +are unresolved references (dangling edges) in the master graph of all atoms, +and the table of contents for a static library says that one of the object files +in the library defines one of the missing symbols (dangling edge), +the set of atoms from the specified object file in the static library is added +to the master graph of atoms. + +

Dynamic Library (Shared Object) +

+Dynamic libraries are unique in that the don't directly add add any atoms. +Their purpose is to check at build time that all references are resolved and +provide a list of dynamic libraries (SO_NEEDED) that will be needed at runtime. +The way this is modeled in the linker is that a dynamic library contributes +no atoms to the initial graph of atoms. Instead, (like static libraries) if +there are unresolved references (dangling edges) in the master graph of all atoms, +if a dynamic library exports a required symbol, then a "proxy" atom is +instantiated by the linker. The proxy atom allows the master atom graph to have +all edges resolved and also records from which dynamic library a symbol came.

+ +
+

+ Linking Steps +

+

Through the use of abstract Atoms, the core of linking is architecture +independent and file format independent. All command line parsing is factored +out into a separate "options" abstraction which enables the linker to be driven +with different command line sets.

+

The overall steps in linking are:

+

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

The Resolving and Passes steps are done purely on the master graph of atoms, +so they have no notion of file formats such as mach-o or ELF.

+ +

Resolving +

+

The resolving step takes all the atoms graphs from each object file and +combines them into one master object graph. Unfortunately, it is not as simple +as appending the atom list from each file into one big list. There are many +cases where atoms need to be coalesced. That is, two or more atoms need to +be coalesced into one atom. This is necessary to support: C language + "tentative definitions", C++ weak symbols for templates and inlines defined +in headers, and for merging copies of constants like c-strings and floating +point constants.

+ +

The linker support coalescing by-name and by-content. By-name is used for +tentative definitions and weak symbols. By-content is used for constant data +that can be merged.

+ +

When one atom has a reference (FixUp) to another atom, there is also a binding +type: by-name, direct, or indirect. A Fixup contains a tagged union that if +the binding type is by-name, the union field is a pointer to a c-string. If +the binding type is direct, the union is a pointer to an Atom. If the binding +type is indirect, the union is a index into a table of pointers to Atoms. Below +is a graphical representation of the binding types:

+binding types graphic + +

Input file Atoms contain only direct and by-name references. Direct +references are used for atoms defined in the same object file for which the +target atom is either unnamed or cannot change. For instance, calling +a static function in a translation unit will result in a direct reference +to the static functions's atom. Also the FDE (dwarf unwind info) for a function +has a direct reference to its function. On the other hand references to +global symbols (e.g. call to printf) use by-name binding in object files. +

+ +

The resolving process maintains some global linking "state", including: +a "symbol table" which is a map from c-string to Atom*, an indirect symbol +table which is a growable array of Atom*, and for each kind of coalesable +constants there is a content to Atom* map. With these data structures, +the linker walks all atoms in all input files. For each +atom, it checks if the atom should be in one symbol table or one of the +coalescing tables. If so, it attempts to add the atom. If there already is +a matching atom in that table, that means the current atom needs to be +coalesced with the found atom. +

+ +

To support coalescing, all references to coalesable atoms are changed to +indirect binding and an entry is added to the indirect table which points +to the current chosen atom. When all input atoms have been processed by +the resolver, there should be only direct and indirect bindings left. If +there are any NULL entries in the indirect table, that means there are +undefined references. The linker then looks to the supplied libraries (both +static and dynamic) to resolve those references. +

+ +

Dead code stripping (if requested) is done at the end of resolving. The +linker does a simple mark-and-sweep. It starts with "root" atoms (like "main" +in a main executable) and follows each references and marks each Atom that +it visits as "live". When done, all atoms not marked "live" are removed. +

+ +

Passes +

+

The Passes step +is an open ended set of routines that each get a change to modify or enhance +the master graph of atoms. Passes are only run if the master graph of +atoms is completely resolved (no dangling edges). +The current set of Passes in the Darwin linker are:

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

Some of these passes are specific to Apple's runtime environments. But many +of the passes are applicable to any OS (such as generating branch island for +out of range branch instructions).

+ +

The general structure of a pass is to walk the master graph inspecting each +atom and doing something. For instance, the stub pass, walks the graph looking +for atoms with call sites to proxy atoms (e.g. call to printf). It then +instantiates a "stub" atom (PLT entry) and a "lazy pointer" atom for each +proxy atom needed, and these new atoms are added to the master graph. Next +all the noted call sites to proxy atoms are replaced with calls to the +corresponding stub atom.

+ +

Generate Output File +

+

Once the passes are done, the output file generator is given a sorted list +of atoms. Its job is to create the executable content file wrapper and place +the content of the atoms into it. +

+ + +

+ Future Directions +

+ +

Sections +

+

The current use of sections in mach-o .o files over-constrains the linker. +By default, the linker should preserve the section an atom is in. But since +all sections must be contiguous in the output, that limits the ability of +the linker to order atoms for locality. It would be helpful to enrich the +object file with with reason something is in the section it is. For instance, +is the section found at runtime? Or was the use of a section just a quick +way to group some content together? +

+

The ELF model for sections is a little better than mach-o because ELF +sections have write and execute bits, whereas mach-o sections must be in some +segment and the segment has the write and execute bits. +

+ +

Mach-o Object File Format +

+

+The messiest part of the linker is the mach-o parser. This is because mach-o +is a traditional section and symbols based file format. The parser must infer +atom boundaries using two approaches. The first is that some section types have +well defined content which the linker can parse into atoms (e.g. __cstring, +__eh_frame). The other approach is a naming convention (which the compiler follows) +by which the linker breaks sections into atoms at any non-local (not starting +with 'L') symbol. The processing the linker has to do parse mach-o .o files is a +significant part of the link time. +

+ +

Given that the assembler writes object files once, whereas the linker reads +them many times (during development), it would make sense to optimize the object +file format to be something the linker can read/parse efficiently.

+ +

New Object File Model +

+

LLVM has a nice model for its IR. There are three representations: +the binary bit code file, the in-memory object model, and a textual +representation. LLVM contains utility possible code for converting between these +representations. The same model makes sense for atoms too. There should be +three representations for atoms: binary file, in-memory, and textual. The Darwin +linker already has an in-memory C++ object model for Atoms. All we need is a +textual representation and binary file format. +

+

Note: in the darwin linker the binary format for input object files is +independent of the output executable format. That is, we could have one +universal object file format which the linker could use as input to produce +mach-o, ELF, or PE executables.

+

+The object file binary format should be designed to instantiate into atoms +as fast as possible. The obvious way to do that is that the +file format would be an array of atoms. The linker just mmaps in the file and +looks at the header to see how many atoms there and instantiate that many atoms +with the atom attribute information coming from that array. The trick is +designing this in a way that can be extended as the Atom mode evolves and new +attributes are added. +

+

+In designing a textual format we want something easy for humans to read and +easy for the linker to parse. Since an atom has lots of attributes most of +which are usually just the default, we should define default values for +every attribute so that those can be omitted from the text representation. +One possile format is YAML. Here is the atoms for a simple hello world +program expressed in YAML. +

+
+---
+target-triple:   x86_64-apple-darwin11
+source:
+
+atoms:
+    - name:    _main
+      scope:   linkage-unit
+      type:    code
+      alignment: 
+          power: 4
+      content: [ 55, 48, 89, e5, 48, 8d, 3d, 00, 00, 00, 00, 30, c0, e8, 00, 00,
+                 00, 00, 31, c0, 5d, c3 ]
+      fixups:
+      - offset: 07
+        kind:   pcrel32
+        target: 2
+      - offset: 0E
+        kind:   call32
+        target: _fprintf
+
+    - type:    c-string
+      merge:   by-content
+      content: [ 73, 5A, 00 ]
+
+...
+
+ +

One big use for the textual format will be writing test cases. The Darwin +linker test suite test cases are written mostly in C/C++ and a few assembly +files. The use of C means the same test case can be compiled for different +architectures. But writing test cases in C is problematic because the compiler +may vary its output over time for its own optimization reasons which my +inadvertently disable or break the linker feature trying to be tested. By +writing test cases in the linkers own textual format, we can exactly specify +every attribute of every atom and thus target specific linker logic. +

+ +

Debug Info +

+

Around 2005 when Apple switched from using STABS to using DWARF for debug +information, we made a design decision to have the linker ignore DWARF in +.o files. This improves linking performance because the linker is not +copying tons of debug info. Instead, the linker adds "debug notes" into +output binary that contain the paths of the original .o files. During development +the Darwin debugger will notice the debug notes and the load the dwarf +debug information from the original object files. For release builds, +a tool named dsymutil is run on the program. It finds the debug notes and +then the original object files, then reads, merges and optimizes all the dwarf +debug information into one .dSYM file which can be loaded by the debugger +if needed.

+ +

The current way DWARF is generated is that all debug information for all +functions in a translation unit are merged and optimized into sections based +on debug info kind. For instance the mapping of instructions to source line +numbers for all functions is compressed and put in one section. This does not +play well in an Atom based file format. One idea is to have the compiler +emit some intermediate representation debug information (one which is +partitioned per atom) into the Atom based file format. The linker could +then have code to convert that intermediate debug into to final dwarf. +This is still an open question.

+ +

Extending Atom attributes to ELF and XCOFF +

+

The current set of attributes defined for Atoms in the darwin linker +were chosen to meet the requirements of developing code to run on iOS and +Mac OS X. Below is a list of the attributes and their possible values. +It may just require adding more values to support ELF and XCOFF. Or there +may need to be new attributes added to capture new functionality. +

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

Where does dllexport fit in here? Where does visibility protected and +internal fit? Protected seems like scope=global plus the rule to not +indirect references to it. Internal is like hidden plus enables some +compiler optimizations. I'm not sure the linker needs to know about internal. +

+ + + + diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index dc2a501..a0850f6 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -743,7 +743,7 @@ Optimize stabs debug symbols to remove duplicates. This is the default. This o Write minimal stabs which causes the debugger to open and read the original .o file for full stabs. This style of debugging is obsolete in Mac OS X 10.5. This option is obsolete. .It Fl X -Strip local symbols that being the 'L'. This is the default. This option is obsolete. +Strip local symbols that begin with 'L'. This is the default. This option is obsolete. .It Fl s Completely strip the output, including removing the symbol table. This file format variant is no longer supported. This option is obsolete. diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 65f20a7..11e716e 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; }; F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93CB246116E69EB003233B8 /* tlvp.cpp */; }; @@ -76,6 +77,7 @@ F9BA955E10A233000097A440 /* huge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA955C10A233000097A440 /* huge.cpp */; }; F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */; }; + F9CC24191461FB4300A92174 /* blob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CC24141461FB4300A92174 /* blob.cpp */; }; F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; @@ -239,7 +241,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; }; + B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; }; + B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; }; @@ -311,6 +315,12 @@ F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; }; F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; }; + F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; }; + F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; }; + F9CC24161461FB4300A92174 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = ""; }; + F9CC24171461FB4300A92174 /* memutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memutils.h; sourceTree = ""; }; + F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = ""; }; + F9CCF761144CE1AD007CB524 /* create_configure */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = create_configure; path = src/create_configure; sourceTree = ""; }; F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = ""; }; F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/ld/debugline.c; sourceTree = ""; tabWidth = 8; usesTabs = 1; }; @@ -368,7 +378,6 @@ F9023C2C06D5A227001BBF46 = { isa = PBXGroup; children = ( - C02A29DE0953B26E001FB8C1 /* ChangeLog */, F9B813A80EC27B6300F94C13 /* abstraction */, F9B813AD0EC27B8500F94C13 /* ld */, F9B813B00EC27B9E00F94C13 /* other */, @@ -492,11 +501,14 @@ F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */, F989D3AA10684F5B0014B60C /* LinkEdit.hpp */, F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */, + F9CC24131461FB4300A92174 /* code-sign-blobs */, F9AA650B1051BD2B003E3539 /* passes */, F9AA65861051E750003E3539 /* parsers */, F933DC37092A82480083EAC8 /* Architectures.hpp */, F9EA7582097882F3008B4F1D /* debugline.c */, F9EA7583097882F3008B4F1D /* debugline.h */, + B3B672411406D42800A376BB /* Snapshot.cpp */, + B3B672441406D44300A376BB /* Snapshot.h */, ); name = ld; sourceTree = ""; @@ -504,6 +516,8 @@ F9B813B00EC27B9E00F94C13 /* other */ = { isa = PBXGroup; children = ( + B3C7A09914295B9C005FC714 /* compile_stubs */, + F9CCF761144CE1AD007CB524 /* create_configure */, F9EA72D4097454FF008B4F1D /* machochecker.cpp */, F971EED706D5AD240041D381 /* ObjectDump.cpp */, F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */, @@ -515,6 +529,19 @@ name = other; sourceTree = ""; }; + F9CC24131461FB4300A92174 /* code-sign-blobs */ = { + isa = PBXGroup; + children = ( + F9CC24141461FB4300A92174 /* blob.cpp */, + F9CC24151461FB4300A92174 /* blob.h */, + F9CC24161461FB4300A92174 /* endian.h */, + F9CC24171461FB4300A92174 /* memutils.h */, + F9CC24181461FB4300A92174 /* superblob.h */, + ); + name = "code-sign-blobs"; + path = "src/ld/code-sign-blobs"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -522,7 +549,8 @@ isa = PBXNativeTarget; buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; buildPhases = ( - F9E8DB4D11921594007B4D6A /* make config.h */, + F9E8DB4D11921594007B4D6A /* make configure.h */, + B3C7A09714295B60005FC714 /* make compile_stub string */, F9023C3606D5A23E001BBF46 /* Sources */, F9023C3706D5A23E001BBF46 /* Frameworks */, F97F5025070D0B6300B9FCD7 /* copy man page */, @@ -542,6 +570,7 @@ isa = PBXNativeTarget; buildConfigurationList = F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */; buildPhases = ( + F9CCF773144CE304007CB524 /* make configure.h */, F971EED006D5ACF60041D381 /* Sources */, F971EED106D5ACF60041D381 /* Frameworks */, ); @@ -558,6 +587,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */; buildPhases = ( + F9CCF781144CE3DF007CB524 /* make configure.h */, F9A3DDC70ED762B700C590B9 /* Sources */, F9A3DE140ED76D7700C590B9 /* CopyFiles */, ); @@ -574,6 +604,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */; buildPhases = ( + F9CCF77C144CE36B007CB524 /* make configure.h */, F9B670020DDA176100E6D0DA /* Sources */, F9B670040DDA176100E6D0DA /* Frameworks */, F9B813870EC2659600F94C13 /* install man page */, @@ -591,6 +622,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */; buildPhases = ( + F9CCF76B144CE2AD007CB524 /* make configure.h */, F9BA515E0ECE58BE00D1D62E /* Sources */, F9BA515F0ECE58BE00D1D62E /* Frameworks */, F9C12EA50ED63E05005BC69D /* install man page */, @@ -608,6 +640,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */; buildPhases = ( + F9CCF76F144CE2D6007CB524 /* make configure.h */, F9EA72C8097454A6008B4F1D /* Sources */, F9EA72C9097454A6008B4F1D /* Frameworks */, ); @@ -624,6 +657,7 @@ isa = PBXNativeTarget; buildConfigurationList = F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */; buildPhases = ( + F9CCF765144CE244007CB524 /* make configure.h */, F9EC77EB0A2F85F6002A3E39 /* Sources */, F9EC77EC0A2F85F6002A3E39 /* Frameworks */, F9B1A25E0A3A44CB00DA8FAB /* install man page */, @@ -674,6 +708,23 @@ /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ + B3C7A09714295B60005FC714 /* make compile_stub string */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/compile_stubs", + ); + name = "make compile_stub string"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/compile_stubs.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/csh; + shellScript = "echo \"static const char *compile_stubs = \" > $DERIVED_FILE_DIR/compile_stubs.h\ncat compile_stubs | sed s/\\\"/\\\\\\\\\\\"/g | sed s/^/\\\"/ | sed s/\\$/\\\\\\\\n\\\"/ >> $DERIVED_FILE_DIR/compile_stubs.h\necho \";\" >> $DERIVED_FILE_DIR/compile_stubs.h"; + showEnvVarsInLog = 0; + }; F96D5367094A2754008E9EE8 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -685,7 +736,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/csh; - shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Developer/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; + shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; showEnvVarsInLog = 0; }; F9871A3413340B4600DB3F24 /* Platform install */ = { @@ -700,23 +751,119 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\tfi\nfi\n\n"; + shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n"; + showEnvVarsInLog = 0; + }; + F9CCF765144CE244007CB524 /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/src/create_configure\n"; + showEnvVarsInLog = 0; + }; + F9CCF76B144CE2AD007CB524 /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/src/create_configure\n"; + showEnvVarsInLog = 0; + }; + F9CCF76F144CE2D6007CB524 /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/src/create_configure\n"; + showEnvVarsInLog = 0; + }; + F9CCF773144CE304007CB524 /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/src/create_configure\n"; + showEnvVarsInLog = 0; + }; + F9CCF77C144CE36B007CB524 /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/src/create_configure\n"; showEnvVarsInLog = 0; }; - F9E8DB4D11921594007B4D6A /* make config.h */ = { + F9CCF781144CE3DF007CB524 /* make configure.h */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "make config.h"; + name = "make configure.h"; outputPaths = ( "$(DERIVED_FILE_DIR)/configure.h", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/configure.h\n\nif [ -n \"${IPHONEOS_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_IPHONEOS_MIN_VERSION \\\"${IPHONEOS_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\nelse\n if [ -n \"${MACOSX_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_MACOSX_MIN_VERSION \\\"${MACOSX_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\n fi\nfi\n"; + shellScript = "${SRCROOT}/src/create_configure\n"; + showEnvVarsInLog = 0; + }; + F9E8DB4D11921594007B4D6A /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "${SRCROOT}/src/create_configure\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -749,6 +896,8 @@ F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */, F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */, F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */, + B3B672421406D42800A376BB /* Snapshot.cpp in Sources */, + F9CC24191461FB4300A92174 /* blob.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -867,8 +1016,6 @@ F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; @@ -892,7 +1039,7 @@ GCC_WARN_MISSING_PARENTHESES = YES; GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; GCC_WARN_PEDANTIC = NO; - GCC_WARN_SHADOW = YES; + GCC_WARN_SHADOW = NO; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = NO; @@ -903,6 +1050,7 @@ GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( + "$(DT_TOOLCHAIN_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/include", ); @@ -910,7 +1058,10 @@ LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_LDFLAGS = ( + "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-exported_symbol,__mh_execute_header", + ); PREBINDING = NO; PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; @@ -922,8 +1073,6 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; @@ -962,13 +1111,14 @@ GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( + "$(DT_TOOLCHAIN_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ( - "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib", + "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -985,8 +1135,6 @@ F933D92009291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1006,7 +1154,7 @@ "$(DEVELOPER_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt"; OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; @@ -1033,7 +1181,7 @@ "$(DEVELOPER_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt"; OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; @@ -1048,6 +1196,8 @@ F933D92409291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; }; @@ -1056,6 +1206,8 @@ F933D92509291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; }; @@ -1082,6 +1234,8 @@ F9849FF810B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; }; @@ -1101,8 +1255,6 @@ F9849FFA10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; @@ -1140,13 +1292,14 @@ GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( + "$(DT_TOOLCHAIN_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ( - "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib", + "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1213,7 +1366,7 @@ "$(DEVELOPER_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt"; OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index f4d136f..80a4e81 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -31,13 +31,13 @@ #include #include #include -#include #include #include #include #include "FileAbstraction.hpp" +#include "configure.h" // stuff that will eventually go away once newer cctools headers are widespread #ifndef LC_LOAD_UPWARD_DYLIB @@ -54,9 +54,6 @@ #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) #endif -#ifndef ARM_THUMB_32BIT_BRANCH - #define ARM_THUMB_32BIT_BRANCH 7 -#endif #ifndef N_ARM_THUMB_DEF #define N_ARM_THUMB_DEF 0x0008 #endif @@ -206,9 +203,18 @@ #define LC_DYLD_ENVIRONMENT 0x27 #endif -// hack until newer everywhere -#define ARM_RELOC_HALF 8 -#define ARM_RELOC_HALF_SECTDIFF 9 +#ifndef LC_DATA_IN_CODE + #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */ + struct data_in_code_entry { + uint32_t offset; + uint16_t length; + uint16_t kind; + }; +#endif + +#ifndef LC_DYLIB_CODE_SIGN_DRS + #define LC_DYLIB_CODE_SIGN_DRS 0x2B +#endif #ifndef CPU_SUBTYPE_ARM_V7F #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) @@ -217,24 +223,77 @@ #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) #endif -struct ARMSubType { - const char* subTypeName; + +#ifndef LC_SOURCE_VERSION + #define LC_SOURCE_VERSION 0x2A + struct source_version_command { + uint32_t cmd; /* LC_SOURCE_VERSION */ + uint32_t cmdsize; /* 16 */ + uint64_t version; /* A.B.C.D.E packed as a24.b10.c10.d10.e10 */ + }; +#endif + +#ifndef LC_MAIN + #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */ + struct entry_point_command { + uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */ + uint32_t cmdsize; /* 24 */ + uint64_t entryoff; /* file (__TEXT) offset of main() */ + uint64_t stacksize;/* if not zero, initial stack size */ + }; +#endif + +#ifndef LC_DYLIB_CODE_SIGN_DRS + #define LC_DYLIB_CODE_SIGN_DRS 0x2B +#endif + + +struct ArchInfo { + const char* archName; + cpu_type_t cpuType; + cpu_subtype_t cpuSubType; const char* llvmTriplePrefix; - cpu_subtype_t subType; + const char* llvmTriplePrefixAlt; + bool isSubType; bool supportsThumb2; }; -static const ARMSubType ARMSubTypes[] = { - { "armv4t","armv4t-", CPU_SUBTYPE_ARM_V4T, false }, - { "armv5", "armv5e-", CPU_SUBTYPE_ARM_V5TEJ, false }, - { "armv6", "armv6-", CPU_SUBTYPE_ARM_V6, false }, - { "armv7", "thumbv7-", CPU_SUBTYPE_ARM_V7, true }, - { "armv7f", "thumbv7f-", CPU_SUBTYPE_ARM_V7F, true }, - { "armv7k", "thumbv7k-", CPU_SUBTYPE_ARM_V7K, true }, - { 0, NULL, false } +static const ArchInfo archInfoArray[] = { +#if SUPPORT_ARCH_x86_64 + { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", false, false }, +#endif +#if SUPPORT_ARCH_i386 + { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, "i386-", "", false, false }, +#endif +#if SUPPORT_ARCH_armv4t + { "armv4t", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T, "armv4t-", "", true, false }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv5 + { "armv5", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ, "armv5e-", "", true, false }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv6 + { "armv6", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, "armv6-", "", true, false }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7 + { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, "thumbv7-", "armv7-", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif + { NULL, 0, 0, NULL, NULL, false, false } }; +// weird, but this include must wait until after SUPPORT_ARCH_arm_any is set up +#if SUPPORT_ARCH_arm_any +#include +#endif + +// hack until newer everywhere +#define ARM_RELOC_HALF 8 +#define ARM_RELOC_HALF_SECTDIFF 9 + // @@ -1278,7 +1337,7 @@ class macho_version_min_command { uint32_t version() const INLINE { return fields.version; } void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } -#ifdef LC_SOURCE_VERSION +#ifdef DICE_KIND_DATA uint32_t sdk() const INLINE { return fields.sdk; } void set_sdk(uint32_t value) INLINE { E::set32(fields.sdk, value); } #else @@ -1329,6 +1388,70 @@ class macho_compact_unwind_entry { }; +// +// mach-o source version load command +// +template +class macho_source_version_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint64_t version() const INLINE { return fields.version; } + void set_version(uint64_t value) INLINE { E::set64(fields.version, value); } + + typedef typename P::E E; +private: + source_version_command fields; +}; + + +// +// mach-o source version load command +// +template +class macho_entry_point_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint64_t entryoff() const INLINE { return fields.entryoff; } + void set_entryoff(uint64_t value) INLINE { E::set64(fields.entryoff, value); } + + uint64_t stacksize() const INLINE { return fields.stacksize; } + void set_stacksize(uint64_t value) INLINE { E::set64(fields.stacksize, value); } + + typedef typename P::E E; +private: + entry_point_command fields; +}; + + + +template +class macho_data_in_code_entry { +public: + uint32_t offset() const INLINE { return E::get32(fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } + + uint16_t length() const INLINE { return E::get16(fields.length); } + void set_length(uint16_t value) INLINE { E::set16((uint16_t&)fields.length, value); } + + uint16_t kind() const INLINE { return E::get16(fields.kind); } + void set_kind(uint16_t value) INLINE { E::set16((uint16_t&)fields.kind, value); } + + typedef typename P::E E; +private: + data_in_code_entry fields; +}; + + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp index 7b30cad..76f575a 100644 --- a/ld64/src/abstraction/MachOTrie.hpp +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -360,8 +360,10 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* ++edgeStrLen; } cummulativeString[curStrOffset+edgeStrLen] = *s++; - uint32_t childNodeOffet = read_uleb128(s, end); - processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); + uint32_t childNodeOffset = read_uleb128(s, end); + if (childNodeOffset == 0) + throw "malformed trie, childNodeOffset==0"; + processExportNode(start, start+childNodeOffset, end, cummulativeString, curStrOffset+edgeStrLen, output); } } diff --git a/ld64/src/create_configure b/ld64/src/create_configure new file mode 100755 index 0000000..5375911 --- /dev/null +++ b/ld64/src/create_configure @@ -0,0 +1,45 @@ +#!/bin/bash + +echo "" > ${DERIVED_FILE_DIR}/configure.h + +if [ -n "${IPHONEOS_DEPLOYMENT_TARGET}" ]; then + echo "#define DEFAULT_IPHONEOS_MIN_VERSION \"${IPHONEOS_DEPLOYMENT_TARGET}\"" >> ${DERIVED_FILE_DIR}/configure.h +else + if [ -n "${MACOSX_DEPLOYMENT_TARGET}" ]; then + echo "#define DEFAULT_MACOSX_MIN_VERSION \"${MACOSX_DEPLOYMENT_TARGET}\"" >> ${DERIVED_FILE_DIR}/configure.h + fi +fi + +if [ -z "${RC_SUPPORTED_ARCHS}" ]; then + RC_SUPPORTED_ARCHS="i386 x86_64" +fi + +for ANARCH in ${RC_SUPPORTED_ARCHS} +do + KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,i386,x86_64," + FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` + if [ $FOUND ]; then + echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h + else + echo "#error uknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h + fi +done + +echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h + + +# ld64 hardcodes a reference to /Developer/usr/lib/libLTO.dylib +if [ -n "${DT_TOOLCHAIN_DIR}" ] +then + echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt +else + if [ -e "/Developer/usr/lib/libLTO.dylib" ] + then + echo "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt + else + echo "-Wl,-lazy_library,${BUILT_PRODUCTS_DIR}/../lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt + fi +fi + + + diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 903a2bf..e425fd4 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2009-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -96,7 +96,9 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract uint8_t* copyRoutinesLoadCommand(uint8_t* p) const; uint8_t* copyUUIDLoadCommand(uint8_t* p) const; uint8_t* copyVersionLoadCommand(uint8_t* p) const; + uint8_t* copySourceVersionLoadCommand(uint8_t* p) const; uint8_t* copyThreadsLoadCommand(uint8_t* p) const; + uint8_t* copyEntryPointLoadCommand(uint8_t* p) const; uint8_t* copyEncryptionLoadCommand(uint8_t* p) const; uint8_t* copySplitSegInfoLoadCommand(uint8_t* p) const; uint8_t* copyDylibLoadCommand(uint8_t* p, const ld::dylib::File*) const; @@ -106,6 +108,8 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract uint8_t* copySubLibraryLoadCommand(uint8_t* p, const char* name) const; uint8_t* copySubUmbrellaLoadCommand(uint8_t* p, const char* name) const; uint8_t* copyFunctionStartsLoadCommand(uint8_t* p) const; + uint8_t* copyDataInCodeLoadCommand(uint8_t* p) const; + uint8_t* copyDependentDRLoadCommand(uint8_t* p) const; uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; @@ -120,6 +124,7 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract bool _hasDyldLoadCommand; bool _hasDylibIDLoadCommand; bool _hasThreadLoadCommand; + bool _hasEntryPointLoadCommand; bool _hasEncryptionLoadCommand; bool _hasSplitSegInfoLoadCommand; bool _hasRoutinesLoadCommand; @@ -130,6 +135,9 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract bool _hasSubFrameworkLoadCommand; bool _hasVersionLoadCommand; bool _hasFunctionStartsLoadCommand; + bool _hasDataInCodeLoadCommand; + bool _hasSourceVersionLoadCommand; + bool _hasDependentDRInfo; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; uint32_t _dyldEnvironExrasCount; @@ -161,9 +169,8 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _hasDyldInfoLoadCommand = opts.makeCompressedDyldInfo(); _hasDyldLoadCommand = ((opts.outputKind() == Options::kDynamicExecutable) || (_options.outputKind() == Options::kDyld)); _hasDylibIDLoadCommand = (opts.outputKind() == Options::kDynamicLibrary); - _hasThreadLoadCommand = _hasDyldLoadCommand || (opts.outputKind() == Options::kStaticExecutable) - || (opts.outputKind() == Options::kPreload) - || (opts.outputKind() == Options::kDyld); + _hasThreadLoadCommand = _options.needsThreadLoadCommand(); + _hasEntryPointLoadCommand = _options.needsEntryPointLoadCommand(); _hasEncryptionLoadCommand = opts.makeEncryptable(); _hasSplitSegInfoLoadCommand = opts.sharedRegionEligible(); _hasRoutinesLoadCommand = (opts.initFunctionName() != NULL); @@ -189,7 +196,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: } break; case Options::kStaticExecutable: - _hasDynamicSymbolTableLoadCommand = false; + _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); break; case Options::kPreload: _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); @@ -199,6 +206,9 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL); _hasVersionLoadCommand = _options.addVersionLoadCommand(); _hasFunctionStartsLoadCommand = _options.addFunctionStarts(); + _hasDataInCodeLoadCommand = _options.addDataInCodeInfo(); + _hasSourceVersionLoadCommand = _options.needsSourceVersionLoadCommand(); + _hasDependentDRInfo = _options.needsDependentDRInfo(); _dylibLoadCommmandsCount = _writer.dylibCount(); _allowableClientLoadCommmandsCount = _options.allowableClients().size(); _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); @@ -328,9 +338,15 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasVersionLoadCommand ) sz += sizeof(macho_version_min_command

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

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

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

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

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

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

); + return sz; } @@ -409,8 +431,14 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasVersionLoadCommand ) ++count; + if ( _hasSourceVersionLoadCommand ) + ++count; + if ( _hasThreadLoadCommand ) ++count; + + if ( _hasEntryPointLoadCommand ) + ++count; if ( _hasEncryptionLoadCommand ) ++count; @@ -436,6 +464,12 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasFunctionStartsLoadCommand ) ++count; + if ( _hasDataInCodeLoadCommand ) + ++count; + + if ( _hasDependentDRInfo ) + ++count; + return count; } @@ -473,6 +507,8 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const else { if ( _options.outputKind() == Options::kStaticExecutable ) { bits |= MH_NOUNDEFS; + if ( _options.positionIndependentExecutable() ) + bits |= MH_PIE; } else if ( _options.outputKind() == Options::kPreload ) { bits |= MH_NOUNDEFS; @@ -704,7 +740,12 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* else return S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; case ld::Section::typeNonLazyPointer: - return S_NON_LAZY_SYMBOL_POINTERS; + if ( _options.outputKind() == Options::kKextBundle ) + return S_REGULAR; + else if ( (_options.outputKind() == Options::kStaticExecutable) && _options.positionIndependentExecutable() ) + return S_REGULAR; + else + return S_NON_LAZY_SYMBOL_POINTERS; case ld::Section::typeDyldInfo: return S_REGULAR; case ld::Section::typeLazyDylibPointer: @@ -715,7 +756,13 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* else return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; case ld::Section::typeInitializerPointers: - return S_MOD_INIT_FUNC_POINTERS; + // i386 kexts need different section type + if ( (_options.outputKind() == Options::kObjectFile) + && (strcmp(sect->sectionName(), "__constructor") == 0) + && (strcmp(sect->segmentName(), "__TEXT") == 0) ) + return S_REGULAR; + else + return S_MOD_INIT_FUNC_POINTERS; case ld::Section::typeTerminatorPointers: return S_MOD_TERM_FUNC_POINTERS; case ld::Section::typeTLVInitialValues: @@ -1006,17 +1053,26 @@ uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const cmd->set_cmd(LC_VERSION_MIN_MACOSX); cmd->set_cmdsize(sizeof(macho_version_min_command

)); cmd->set_version((uint32_t)macVersion); - cmd->set_sdk(0); + cmd->set_sdk(_options.sdkVersion()); } else { cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); cmd->set_cmdsize(sizeof(macho_version_min_command

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

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

* cmd = (macho_source_version_command

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

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

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

* cmd = (macho_entry_point_command

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

)); + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + if ( _state.entryPoint->isThumb() ) + start |= 1ULL; + cmd->set_entryoff(start - this->finalAddress()); + cmd->set_stacksize(_options.hasCustomStack() ? _options.customStackSize() : 0 ); + return p + sizeof(macho_entry_point_command

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

* cmd = (macho_linkedit_data_command

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

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

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

* cmd = (macho_linkedit_data_command

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

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

); +} + + template void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { @@ -1271,8 +1369,14 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasVersionLoadCommand ) p = this->copyVersionLoadCommand(p); + if ( _hasSourceVersionLoadCommand ) + p = this->copySourceVersionLoadCommand(p); + if ( _hasThreadLoadCommand ) p = this->copyThreadsLoadCommand(p); + + if ( _hasEntryPointLoadCommand ) + p = this->copyEntryPointLoadCommand(p); if ( _hasEncryptionLoadCommand ) p = this->copyEncryptionLoadCommand(p); @@ -1318,7 +1422,13 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasFunctionStartsLoadCommand ) p = this->copyFunctionStartsLoadCommand(p); - + + if ( _hasDataInCodeLoadCommand ) + p = this->copyDataInCodeLoadCommand(p); + + if ( _hasDependentDRInfo ) + p = this->copyDependentDRLoadCommand(p); + } diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 7b05a96..766b17c 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -60,12 +62,19 @@ #include "archive_file.h" #include "lto_file.h" #include "opaque_section_file.h" +#include "Snapshot.h" +const bool _s_logPThreads = false; namespace ld { namespace tool { - +class IgnoredFile : public ld::File { +public: + IgnoredFile(const char* pth, time_t modTime, Ordinal ord, Type type) : ld::File(pth, modTime, ord, type) {}; + virtual bool forEachAtom(AtomHandler&) const { return false; }; + virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; }; +}; class DSOHandleAtom : public ld::Atom { @@ -182,7 +191,15 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) if ( strncmp((const char*)p, "!\n", 8) == 0 ) return "archive"; - return "unsupported file format"; + char *unsupported = (char *)malloc(128); + strcpy(unsupported, "unsupported file format ("); + for (unsigned i=0; imagic == OSSwapBigToHostInt32(FAT_MAGIC) ) { isFatFile = true; const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - uint32_t sliceToUse; bool sliceFound = false; + sliceCount = OSSwapBigToHostInt32(fh->nfat_arch); if ( _options.preferSubArchitecture() ) { // first try to find a slice that match cpu-type and cpu-sub-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + for (uint32_t i=0; i < sliceCount; ++i) { if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture()) && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) { sliceToUse = i; @@ -222,7 +240,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } if ( !sliceFound ) { // look for any slice that matches just cpu-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + for (uint32_t i=0; i < sliceCount; ++i) { if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) { sliceToUse = i; sliceFound = true; @@ -260,19 +278,26 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.logAllFiles = _options.logAllFiles(); objOpts.convertUnwindInfo = _options.needsUnwindInfoSection(); objOpts.subType = _options.subArchitecture(); - ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, _nextInputOrdinal, objOpts); - if ( objResult != NULL ) - return this->addObject(objResult, info, len); + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); + if ( objResult != NULL ) { + OSAtomicAdd64(len, &_totalObjectSize); + OSAtomicIncrement32(&_totalObjectLoaded); + return objResult; + } // see if it is an llvm object file - objResult = lto::parse(p, len, info.path, info.modTime, _nextInputOrdinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles()); - if ( objResult != NULL ) - return this->addObject(objResult, info, len); - + objResult = lto::parse(p, len, info.path, info.modTime, _options.architecture(), _options.subArchitecture(), _options.logAllFiles()); + if ( objResult != NULL ) { + OSAtomicAdd64(len, &_totalObjectSize); + OSAtomicIncrement32(&_totalObjectLoaded); + return objResult; + } + // see if it is a dynamic library - ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, _nextInputOrdinal, info.options.fBundleLoader, indirectDylib); - if ( dylibResult != NULL ) - return this->addDylib(dylibResult, info, len); + ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); + if ( dylibResult != NULL ) { + return dylibResult; + } // see if it is a static library ::archive::ParserOptions archOpts; @@ -283,18 +308,17 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib archOpts.objcABI2 = _options.objCABIVersion2POverride(); archOpts.verboseLoad = _options.whyLoad(); archOpts.logAllFiles = _options.logAllFiles(); - ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts); + ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts); if ( archiveResult != NULL ) { - // force loaded archives should be in LD_TRACE - if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) - logArchive(archiveResult); - return this->addArchive(archiveResult, info, len); + OSAtomicAdd64(len, &_totalArchiveSize); + OSAtomicIncrement32(&_totalArchivesLoaded); + return archiveResult; } // does not seem to be any valid linker input file, check LTO misconfiguration problems if ( lto::archName((uint8_t*)p, len) != NULL ) { if ( lto::libLTOisLoaded() ) { - throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + throwf("lto file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path); } else { const char* libLTO = "libLTO.dylib"; @@ -302,7 +326,10 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib char tmpPath[PATH_MAX]; char libLTOPath[PATH_MAX]; uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( _options.overridePathlibLTO() != NULL ) { + libLTO = _options.overridePathlibLTO(); + } + else if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { if ( realpath(ldPath, tmpPath) != NULL ) { char* lastSlash = strrchr(tmpPath, '/'); if ( lastSlash != NULL ) @@ -318,13 +345,13 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib // error handling if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - throwf("missing required architecture %s in file", _options.architectureName()); + throwf("missing required architecture %s in file %s (%u slices)", _options.architectureName(), info.path, sliceCount); } else { if ( isFatFile ) - throwf("file is universal but does not contain a(n) %s slice", _options.architectureName()); + throwf("file is universal (%u slices) but does not contain a(n) %s slice: %s", sliceCount, _options.architectureName(), info.path); else - throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + throwf("file was built for %s which is not the architecture being linked (%s): %s", fileArch(p, len), _options.architectureName(), info.path); } } @@ -409,9 +436,12 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from if ( strcmp(dit->installName,installPath) == 0 ) { try { Options::FileInfo info = _options.findFile(dit->useInstead); + _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); + info.ordinal = _indirectDylibOrdinal; ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { + addDylib(dylibReader, info); //_installPathToDylibs[strdup(installPath)] = dylibReader; this->logDylib(dylibReader, true); return dylibReader; @@ -438,12 +468,15 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from // note: @executable_path case is handled inside findFileUsingPaths() // search for dylib using -F and -L paths Options::FileInfo info = _options.findFileUsingPaths(installPath); + _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); + info.ordinal = _indirectDylibOrdinal; try { ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); //_installPathToDylibs[strdup(installPath)] = dylibReader; + addDylib(dylibReader, info); this->logDylib(dylibReader, true); return dylibReader; } @@ -461,7 +494,8 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from void InputFiles::createIndirectDylibs() { _allDirectDylibsLoaded = true; - + _indirectDylibOrdinal = ld::File::Ordinal::indirectDylibBase(); + // mark all dylibs initially specified as required and check if they can be used for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) { it->second->setExplicitlyLinked(); @@ -510,10 +544,7 @@ void InputFiles::createOpaqueFileSections() { // extra command line section always at end for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { - _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, - it->dataLen, _nextInputOrdinal)); - // bump ordinal - _nextInputOrdinal++; + _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)); } } @@ -643,71 +674,158 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) InputFiles::InputFiles(Options& opts, const char** archName) : _totalObjectSize(0), _totalArchiveSize(0), _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), - _options(opts), _bundleLoader(NULL), _nextInputOrdinal(1), - _allDirectDylibsLoaded(false), _inferredArch(false) + _options(opts), _bundleLoader(NULL), + _allDirectDylibsLoaded(false), _inferredArch(false), _fileMonitor(-1), + _exception(NULL) { // fStartCreateReadersTime = mach_absolute_time(); if ( opts.architecture() == 0 ) { // command line missing -arch, so guess arch inferArchitecture(opts, archName); } - - const std::vector& files = opts.getInputFiles(); +#if HAVE_PTHREADS + pthread_mutex_init(&_parseLock, NULL); + pthread_cond_init(&_parseWorkReady, NULL); + pthread_cond_init(&_newFileAvailable, NULL); +#endif + const std::vector& files = _options.getInputFiles(); if ( files.size() == 0 ) throw "no object files specified"; - // add all direct object, archives, and dylibs + _inputFiles.reserve(files.size()); +#if HAVE_PTHREADS + unsigned int inputFileSlot = 0; + _availableInputFiles = 0; + _parseCursor = 0; +#endif + Options::FileInfo* entry; for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { - const Options::FileInfo& entry = *it; - try { - _inputFiles.push_back(this->makeFile(entry, false)); - } - catch (const char* msg) { - if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { - if ( opts.ignoreOtherArchInputFiles() ) { - // ignore, because this is about an architecture not in use - } - else { - warning("ignoring file %s, %s", entry.path, msg); - } - } - else { - throwf("in %s, %s", entry.path, msg); - } - } + entry = (Options::FileInfo*)&(*it); +#if HAVE_PTHREADS + // Assign input file slots to all the FileInfos. + // Also chain all FileInfos into one big list to set up for worker threads to do parsing. + entry->inputFileSlot = inputFileSlot; + entry->readyToParse = !entry->fromFileList || !_options.pipelineEnabled(); + if (entry->readyToParse) + _availableInputFiles++; + _inputFiles.push_back(NULL); + inputFileSlot++; +#else + // In the non-threaded case just parse the file now. + _inputFiles.push_back(makeFile(*entry, false)); +#endif + } + +#if HAVE_PTHREADS + _remainingInputFiles = files.size(); + + // initialize info for parsing input files on worker threads + unsigned int ncpus; + int mib[2]; + size_t len = sizeof(ncpus); + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + if (sysctl(mib, 2, &ncpus, &len, NULL, 0) != 0) { + ncpus = 1; + } + _availableWorkers = MIN(ncpus, files.size()); // max # workers we permit + _idleWorkers = 0; + + if (_options.pipelineEnabled()) { + // start up a thread to listen for available input files + startThread(InputFiles::waitForInputFiles); } - this->createIndirectDylibs(); - this->createOpaqueFileSections(); + // Start up one parser thread. More start on demand as parsed input files get consumed. + startThread(InputFiles::parseWorkerThread); + _availableWorkers--; +#else + if (_options.pipelineEnabled()) { + throwf("pipelined linking not supported on this platform"); + } +#endif } - -ld::File* InputFiles::addArchive(ld::File* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - // bump ordinal - _nextInputOrdinal += reader->subFileCount(); - - // update stats - _totalArchiveSize += mappedLen; - _totalArchivesLoaded++; - return reader; +#if HAVE_PTHREADS +void InputFiles::startThread(void (*threadFunc)(InputFiles *)) const { + pthread_t thread; + pthread_attr_t attr; + pthread_attr_init(&attr); + // set a nice big stack (same as main thread) because some code uses potentially large stack buffers + pthread_attr_setstacksize(&attr, 8 * 1024 * 1024); + pthread_create(&thread, &attr, (void *(*)(void*))threadFunc, (void *)this); + pthread_detach(thread); + pthread_attr_destroy(&attr); } +// Work loop for input file parsing threads +void InputFiles::parseWorkerThread() { + ld::File *file; + const char *exception = NULL; + pthread_mutex_lock(&_parseLock); + const std::vector& files = _options.getInputFiles(); + if (_s_logPThreads) printf("worker starting\n"); + do { + if (_availableInputFiles == 0) { + _idleWorkers++; + pthread_cond_wait(&_parseWorkReady, &_parseLock); + _idleWorkers--; + } else { + int slot = _parseCursor; + while (slot < (int)files.size() && (_inputFiles[slot] != NULL || !files[slot].readyToParse)) + slot++; + assert(slot < (int)files.size()); + Options::FileInfo& entry = (Options::FileInfo&)files[slot]; + _parseCursor = slot+1; + _availableInputFiles--; + entry.readyToParse = false; // to avoid multiple threads finding this file + pthread_mutex_unlock(&_parseLock); + if (_s_logPThreads) printf("parsing index %u\n", slot); + try { + file = makeFile(entry, false); + } catch (const char *msg) { + if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { + if ( _options.ignoreOtherArchInputFiles() ) { + // ignore, because this is about an architecture not in use + } + else { + warning("ignoring file %s, %s", entry.path, msg); + } + } else { + exception = msg; + } + file = new IgnoredFile(entry.path, entry.modTime, entry.ordinal, ld::File::Other); + } + pthread_mutex_lock(&_parseLock); + if (_remainingInputFiles > 0) + _remainingInputFiles--; + if (_s_logPThreads) printf("done with index %u, %d remaining\n", slot, _remainingInputFiles); + if (exception) { + // We are about to die, so set to zero to stop other threads from doing unneeded work. + _remainingInputFiles = 0; + _exception = exception; + } else { + _inputFiles[slot] = file; + if (_neededFileSlot == slot) + pthread_cond_signal(&_newFileAvailable); + } + } + } while (_remainingInputFiles); + if (_s_logPThreads) printf("worker exiting\n"); + pthread_cond_broadcast(&_parseWorkReady); + pthread_cond_signal(&_newFileAvailable); + pthread_mutex_unlock(&_parseLock); +} -ld::File* InputFiles::addObject(ld::relocatable::File* file, const Options::FileInfo& info, uint64_t mappedLen) -{ - // bump ordinal - _nextInputOrdinal++; - // update stats - _totalObjectSize += mappedLen; - _totalObjectLoaded++; - return file; +void InputFiles::parseWorkerThread(InputFiles *inputFiles) { + inputFiles->parseWorkerThread(); } +#endif -ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info, uint64_t mappedLen) +ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info) { _allDylibs.insert(reader); @@ -750,8 +868,9 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& } } } - if ( !dylibOnCommandLineTwice && !isSymlink ) - warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + // remove warning for Same install name for CoreServices and CFNetwork? + //if ( !dylibOnCommandLineTwice && !isSymlink ) + // warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); } } else if ( info.options.fBundleLoader ) @@ -761,106 +880,240 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& if ( !_allDirectDylibsLoaded ) this->logDylib(reader, false); - // bump ordinal - _nextInputOrdinal++; - // update stats _totalDylibsLoaded++; + _searchLibraries.push_back(LibraryInfo(reader)); return reader; } -bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const +#if HAVE_PTHREADS +// Called during pipelined linking to listen for available input files. +// Available files are enqueued for parsing. +void InputFiles::waitForInputFiles() { - bool didSomething = false; - for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { - didSomething |= (*it)->forEachAtom(handler); - } - if ( didSomething || true ) { - switch ( _options.outputKind() ) { - case Options::kStaticExecutable: - case Options::kDynamicExecutable: - // add implicit __dso_handle label - handler.doAtom(DSOHandleAtom::_s_atomExecutable); - handler.doAtom(DSOHandleAtom::_s_atomAll); - if ( _options.pageZeroSize() != 0 ) - handler.doAtom(*new PageZeroAtom(_options.pageZeroSize())); - if ( _options.hasCustomStack() ) - handler.doAtom(*new CustomStackAtom(_options.customStackSize())); - break; - case Options::kDynamicLibrary: - // add implicit __dso_handle label - handler.doAtom(DSOHandleAtom::_s_atomDylib); - handler.doAtom(DSOHandleAtom::_s_atomAll); - break; - case Options::kDynamicBundle: - // add implicit __dso_handle label - handler.doAtom(DSOHandleAtom::_s_atomBundle); - handler.doAtom(DSOHandleAtom::_s_atomAll); - break; - case Options::kDyld: - // add implicit __dso_handle label - handler.doAtom(DSOHandleAtom::_s_atomDyld); - handler.doAtom(DSOHandleAtom::_s_atomAll); - break; - case Options::kPreload: - // add implicit __mh_preload_header label - handler.doAtom(DSOHandleAtom::_s_atomPreload); - handler.doAtom(DSOHandleAtom::_s_atomAll); - break; - case Options::kObjectFile: - handler.doAtom(DSOHandleAtom::_s_atomObjectFile); - break; - case Options::kKextBundle: - // add implicit __dso_handle label - handler.doAtom(DSOHandleAtom::_s_atomAll); - break; + if (_s_logPThreads) printf("starting pipeline listener\n"); + try { + const char *fifo = _options.pipelineFifo(); + assert(fifo); + std::map fileMap; + const std::vector& files = _options.getInputFiles(); + for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { + const Options::FileInfo& entry = *it; + if (entry.fromFileList) { + fileMap[entry.path] = &entry; + } + } + FILE *fileStream = fopen(fifo, "r"); + if (!fileStream) + throwf("pipelined linking error - failed to open stream. fopen() returns %s for \"%s\"\n", strerror(errno), fifo); + while (fileMap.size() > 0) { + char path_buf[PATH_MAX+1]; + if (fgets(path_buf, PATH_MAX, fileStream) == NULL) + throwf("pipelined linking error - %lu missing input files", fileMap.size()); + int len = strlen(path_buf); + if (path_buf[len-1] == '\n') + path_buf[len-1] = 0; + std::map::iterator it = fileMap.find(path_buf); + if (it == fileMap.end()) + throwf("pipelined linking error - not in file list: %s\n", path_buf); + Options::FileInfo* inputInfo = (Options::FileInfo*)it->second; + if (!inputInfo->checkFileExists()) + throwf("pipelined linking error - file does not exist: %s\n", inputInfo->path); + pthread_mutex_lock(&_parseLock); + if (_idleWorkers) + pthread_cond_signal(&_parseWorkReady); + inputInfo->readyToParse = true; + if (_parseCursor > inputInfo->inputFileSlot) + _parseCursor = inputInfo->inputFileSlot; + _availableInputFiles++; + if (_s_logPThreads) printf("pipeline listener: %s slot=%d, _parseCursor=%d, _availableInputFiles = %d remaining = %ld\n", path_buf, inputInfo->inputFileSlot, _parseCursor, _availableInputFiles, fileMap.size()-1); + pthread_mutex_unlock(&_parseLock); + fileMap.erase(it); } + } catch (const char *msg) { + pthread_mutex_lock(&_parseLock); + _exception = msg; + pthread_cond_signal(&_newFileAvailable); + pthread_mutex_unlock(&_parseLock); } - return didSomething; } -bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const +void InputFiles::waitForInputFiles(InputFiles *inputFiles) { + inputFiles->waitForInputFiles(); +} +#endif + + +void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) { - // check each input file - for (std::vector::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { - ld::File* file = *it; - // if this reader is a static archive that has the symbol we need, pull in all atoms in that module - // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. - ld::dylib::File* dylibFile = dynamic_cast(file); - ld::archive::File* archiveFile = dynamic_cast(file); - if ( searchDylibs && (dylibFile != NULL) ) { - //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() ); - if ( dylibFile->justInTimeforEachAtom(name, handler) ) { - // we found a definition in this dylib - // done, unless it is a weak definition in which case we keep searching - if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) - return true; - // else continue search for a non-weak definition + // add all direct object, archives, and dylibs + const std::vector& files = _options.getInputFiles(); + size_t fileIndex; + for (fileIndex=0; fileIndex<_inputFiles.size(); fileIndex++) { + ld::File *file; +#if HAVE_PTHREADS + pthread_mutex_lock(&_parseLock); + + // this loop waits for the needed file to be ready (parsed by worker thread) + while (_inputFiles[fileIndex] == NULL && _exception == NULL) { + // We are starved for input. If there are still files to parse and we have + // not maxed out the worker thread count start a new worker thread. + if (_availableInputFiles > 0 && _availableWorkers > 0) { + if (_s_logPThreads) printf("starting worker\n"); + startThread(InputFiles::parseWorkerThread); + _availableWorkers--; } + _neededFileSlot = fileIndex; + if (_s_logPThreads) printf("consumer blocking for %lu: %s\n", fileIndex, files[fileIndex].path); + pthread_cond_wait(&_newFileAvailable, &_parseLock); } - else if ( searchArchives && (archiveFile != NULL) ) { - if ( dataSymbolOnly ) { - if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) { - if ( _options.traceArchives() ) - logArchive(file); - // found data definition in static library, done - return true; - } + + if (_exception) + throw _exception; + + // The input file is parsed. Assimilate it and call its atom iterator. + if (_s_logPThreads) printf("consuming slot %lu\n", fileIndex); + file = _inputFiles[fileIndex]; + pthread_mutex_unlock(&_parseLock); +#else + file = _inputFiles[fileIndex]; +#endif + const Options::FileInfo& info = files[fileIndex]; + switch (file->type()) { + case ld::File::Reloc: + { + ld::relocatable::File* reloc = (ld::relocatable::File*)file; + _options.snapshot().recordObjectFile(reloc->path()); } - else { - if ( archiveFile->justInTimeforEachAtom(name, handler) ) { - if ( _options.traceArchives() ) - logArchive(file); - // found definition in static library, done - return true; - } + break; + case ld::File::Dylib: + { + ld::dylib::File* dylib = (ld::dylib::File*)file; + addDylib(dylib, info); } + break; + case ld::File::Archive: + { + ld::archive::File* archive = (ld::archive::File*)file; + // force loaded archives should be in LD_TRACE + if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) + logArchive(archive); + _searchLibraries.push_back(LibraryInfo(archive)); + } + break; + case ld::File::Other: + break; + default: + { + throwf("Unknown file type for %s", file->path()); + } + break; } + file->forEachAtom(handler); } + createIndirectDylibs(); + createOpaqueFileSections(); + + while (fileIndex < _inputFiles.size()) { + ld::File *file = _inputFiles[fileIndex]; + file->forEachAtom(handler); + fileIndex++; + } + + switch ( _options.outputKind() ) { + case Options::kStaticExecutable: + case Options::kDynamicExecutable: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomExecutable); + handler.doAtom(DSOHandleAtom::_s_atomAll); + if ( _options.pageZeroSize() != 0 ) + handler.doAtom(*new PageZeroAtom(_options.pageZeroSize())); + if ( _options.hasCustomStack() && !_options.needsEntryPointLoadCommand() ) + handler.doAtom(*new CustomStackAtom(_options.customStackSize())); + break; + case Options::kDynamicLibrary: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomDylib); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kDynamicBundle: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomBundle); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kDyld: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomDyld); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kPreload: + // add implicit __mh_preload_header label + handler.doAtom(DSOHandleAtom::_s_atomPreload); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kObjectFile: + handler.doAtom(DSOHandleAtom::_s_atomObjectFile); + break; + case Options::kKextBundle: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + } +} + + +bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const +{ + // Check each input library. + std::vector::const_iterator libIterator = _searchLibraries.begin(); + + + while (libIterator != _searchLibraries.end()) { + LibraryInfo lib = *libIterator; + if (lib.isDylib()) { + if (searchDylibs) { + ld::dylib::File *dylibFile = lib.dylib(); + //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() ); + if ( dylibFile->justInTimeforEachAtom(name, handler) ) { + // we found a definition in this dylib + // done, unless it is a weak definition in which case we keep searching + _options.snapshot().recordDylibSymbol(dylibFile, name); + if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) { + return true; + } + // else continue search for a non-weak definition + } + } + } else { + if (searchArchives) { + ld::archive::File *archiveFile = lib.archive(); + if ( dataSymbolOnly ) { + if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) { + if ( _options.traceArchives() ) + logArchive(archiveFile); + _options.snapshot().recordArchive(archiveFile->path()); + // found data definition in static library, done + return true; + } + } + else { + if ( archiveFile->justInTimeforEachAtom(name, handler) ) { + if ( _options.traceArchives() ) + logArchive(archiveFile); + _options.snapshot().recordArchive(archiveFile->path()); + // found definition in static library, done + return true; + } + } + } + } + libIterator++; + } + // search indirect dylibs if ( searchDylibs ) { for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { @@ -879,8 +1132,10 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc if ( dylibFile->justInTimeforEachAtom(name, handler) ) { // we found a definition in this dylib // done, unless it is a weak definition in which case we keep searching - if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) + _options.snapshot().recordDylibSymbol(dylibFile, name); + if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) { return true; + } // else continue search for a non-weak definition } } @@ -904,6 +1159,11 @@ bool InputFiles::searchWeakDefInDylib(const char* name) const } return false; } + +static bool vectorContains(const std::vector& vec, ld::dylib::File* key) +{ + return std::find(vec.begin(), vec.end(), key) != vec.end(); +} void InputFiles::dylibs(ld::Internal& state) { @@ -928,8 +1188,11 @@ void InputFiles::dylibs(ld::Internal& state) ld::dylib::File* dylibFile = dynamic_cast(*it); // only add dylibs that are not "blank" dylib stubs if ( (dylibFile != NULL) && ((dylibFile->installPath() != NULL) || (dylibFile == _bundleLoader)) ) { - if ( dylibsOK ) - state.dylibs.push_back(dylibFile); + if ( dylibsOK ) { + if ( ! vectorContains(state.dylibs, dylibFile) ) { + state.dylibs.push_back(dylibFile); + } + } else warning("unexpected dylib (%s) on link line", dylibFile->path()); } @@ -938,15 +1201,30 @@ void InputFiles::dylibs(ld::Internal& state) if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { ld::dylib::File* dylibFile = it->second; - if ( dylibFile->implicitlyLinked() && dylibsOK ) - state.dylibs.push_back(dylibFile); + if ( dylibFile->implicitlyLinked() && dylibsOK ) { + if ( ! vectorContains(state.dylibs, dylibFile) ) { + state.dylibs.push_back(dylibFile); + } + } } } + + //fprintf(stderr, "all dylibs:\n"); + //for(std::vector::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it) { + // const ld::dylib::File* dylib = *it; + // fprintf(stderr, " %p %s\n", dylib, dylib->path()); + //} + // and -bundle_loader state.bundleLoader = _bundleLoader; + + // give an error when -nostdlib is used and libSystem is missing + if ( (state.dylibs.size() == 0) && _options.needsEntryPointLoadCommand() ) + throw "dynamic main executables must link with libSystem.dylib"; } } // namespace tool } // namespace ld + diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index 1842c8e..afc2077 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -25,6 +25,8 @@ #ifndef __INPUT_FILES_H__ #define __INPUT_FILES_H__ +#define HAVE_PTHREADS 1 + #include #include #include @@ -40,6 +42,9 @@ #include #include #include +#if HAVE_PTHREADS +#include +#endif #include @@ -58,7 +63,7 @@ class InputFiles : public ld::dylib::File::DylibHandler virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath); // iterates all atoms in initial files - bool forEachInitialAtom(ld::File::AtomHandler&) const; + void forEachInitialAtom(ld::File::AtomHandler&); // searches libraries for name bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler&) const; @@ -69,29 +74,34 @@ class InputFiles : public ld::dylib::File::DylibHandler bool inferredArch() const { return _inferredArch; } - uint32_t nextInputOrdinal() const { return _nextInputOrdinal++; } - // for -print_statistics - uint64_t _totalObjectSize; - uint64_t _totalArchiveSize; - uint32_t _totalObjectLoaded; - uint32_t _totalArchivesLoaded; - uint32_t _totalDylibsLoaded; + volatile int64_t _totalObjectSize; + volatile int64_t _totalArchiveSize; + volatile int32_t _totalObjectLoaded; + volatile int32_t _totalArchivesLoaded; + volatile int32_t _totalDylibsLoaded; private: void inferArchitecture(Options& opts, const char** archName); const char* fileArch(const uint8_t* p, unsigned len); ld::File* makeFile(const Options::FileInfo& info, bool indirectDylib); - ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info, uint64_t mappedLen); - ld::File* addObject(ld::relocatable::File* f, const Options::FileInfo& info, uint64_t mappedLen); - ld::File* addArchive(ld::File* f, const Options::FileInfo& info, uint64_t mappedLen); + ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info); void logTraceInfo (const char* format, ...) const; void logDylib(ld::File*, bool indirect); void logArchive(ld::File*) const; void createIndirectDylibs(); void checkDylibClientRestrictions(ld::dylib::File*); void createOpaqueFileSections(); + + // for pipelined linking + void waitForInputFiles(); + static void waitForInputFiles(InputFiles *inputFiles); + + // for threaded input file processing + void parseWorkerThread(); + static void parseWorkerThread(InputFiles *inputFiles); + void startThread(void (*threadFunc)(InputFiles *)) const; class CStringEquals { public: @@ -105,9 +115,41 @@ class InputFiles : public ld::dylib::File::DylibHandler InstallNameToDylib _installPathToDylibs; std::set _allDylibs; ld::dylib::File* _bundleLoader; - mutable uint32_t _nextInputOrdinal; bool _allDirectDylibsLoaded; bool _inferredArch; + int _fileMonitor; + struct strcompclass { + bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } + }; + + // for threaded input file processing +#if HAVE_PTHREADS + pthread_mutex_t _parseLock; + pthread_cond_t _parseWorkReady; // used by parse threads to block for work + pthread_cond_t _newFileAvailable; // used by main thread to block for parsed input files + int _availableWorkers; // number of remaining unstarted parse threads + int _idleWorkers; // number of running parse threads that are idle + int _neededFileSlot; // input file the resolver is currently blocked waiting for + int _parseCursor; // slot to begin searching for a file to parse + int _availableInputFiles; // number of input fileinfos with readyToParse==true +#endif + const char * _exception; // passes an exception message from parse thread to main thread + int _remainingInputFiles; // number of input files still to parse + + ld::File::Ordinal _indirectDylibOrdinal; + + class LibraryInfo { + ld::File* _lib; + bool _isDylib; + public: + LibraryInfo(ld::dylib::File* dylib) : _lib(dylib), _isDylib(true) {}; + LibraryInfo(ld::archive::File* dylib) : _lib(dylib), _isDylib(false) {}; + + bool isDylib() { return _isDylib; } + ld::dylib::File *dylib() { return (ld::dylib::File*)_lib; } + ld::archive::File *archive() { return (ld::archive::File*)_lib; } + }; + std::vector _searchLibraries; }; } // namespace tool diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index 7bca2e3..e436082 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -37,6 +37,7 @@ #include "ld.hpp" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" +#include "code-sign-blobs/superblob.h" namespace ld { namespace tool { @@ -1249,7 +1250,6 @@ void SplitSegInfoAtom::encode() const _64bitPointerLocations.clear(); } - template class FunctionStartsAtom : public LinkEditAtom { @@ -1292,6 +1292,9 @@ void FunctionStartsAtom::encode() const std::vector& atoms = sect->atoms; for (std::vector::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { const ld::Atom* atom = *ait; + // filter out zero-length atoms, so LC_FUNCTION_STARTS address can't spill into next section + if ( atom->size() == 0 ) + continue; uint64_t nextAddr = atom->finalAddress(); if ( atom->isThumb() ) nextAddr |= 1; @@ -1313,6 +1316,194 @@ void FunctionStartsAtom::encode() const } +// Need way to formalize data in code +template +class DataInCodeAtom : public LinkEditAtom +{ +public: + DataInCodeAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "data-in-code info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct FixupByAddressSorter + { + bool operator()(const ld::Fixup* left, const ld::Fixup* right) + { + return (left->offsetInAtom < right->offsetInAtom); + } + }; + + void encodeEntry(uint32_t startImageOffset, int len, ld::Fixup::Kind kind) const { + //fprintf(stderr, "encodeEntry(start=0x%08X, len=0x%04X, kind=%04X\n", startImageOffset, len, kind); + do { + macho_data_in_code_entry

entry; + entry.set_offset(startImageOffset); + entry.set_length(len); + switch ( kind ) { + case ld::Fixup::kindDataInCodeStartData: + entry.set_kind(1); + break; + case ld::Fixup::kindDataInCodeStartJT8: + entry.set_kind(2); + break; + case ld::Fixup::kindDataInCodeStartJT16: + entry.set_kind(3); + break; + case ld::Fixup::kindDataInCodeStartJT32: + entry.set_kind(4); + break; + case ld::Fixup::kindDataInCodeStartJTA32: + entry.set_kind(5); + break; + default: + assert(0 && "bad L$start$ label to encode"); + } + uint8_t* bp = (uint8_t*)&entry; + this->_encodedData.append_byte(bp[0]); + this->_encodedData.append_byte(bp[1]); + this->_encodedData.append_byte(bp[2]); + this->_encodedData.append_byte(bp[3]); + this->_encodedData.append_byte(bp[4]); + this->_encodedData.append_byte(bp[5]); + this->_encodedData.append_byte(bp[6]); + this->_encodedData.append_byte(bp[7]); + // in rare case data range is huge, create multiple entries + len -= 0xFFF8; + startImageOffset += 0xFFF8; + } while ( len > 0 ); + } + + static ld::Section _s_section; +}; + +template +ld::Section DataInCodeAtom::_s_section("__LINKEDIT", "__dataInCode", ld::Section::typeLinkEdit, true); + + +template +void DataInCodeAtom::encode() const +{ + if ( this->_writer.hasDataInCode ) { + uint64_t mhAddress = 0; + for (std::vector::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeMachHeader ) + mhAddress = sect->address; + if ( sect->type() != ld::Section::typeCode ) + continue; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // gather all code-in-data labels + std::vector dataInCodeLabels; + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindDataInCodeStartData: + case ld::Fixup::kindDataInCodeStartJT8: + case ld::Fixup::kindDataInCodeStartJT16: + case ld::Fixup::kindDataInCodeStartJT32: + case ld::Fixup::kindDataInCodeStartJTA32: + case ld::Fixup::kindDataInCodeEnd: + dataInCodeLabels.push_back(fit); + break; + default: + break; + } + } + // to do: sort labels by address + std::sort(dataInCodeLabels.begin(), dataInCodeLabels.end(), FixupByAddressSorter()); + + // convert to array of struct data_in_code_entry + ld::Fixup::Kind prevKind = ld::Fixup::kindDataInCodeEnd; + uint32_t prevOffset = 0; + for ( std::vector::iterator sfit = dataInCodeLabels.begin(); sfit != dataInCodeLabels.end(); ++sfit) { + if ( ((*sfit)->kind != prevKind) && (prevKind != ld::Fixup::kindDataInCodeEnd) ) { + int len = (*sfit)->offsetInAtom - prevOffset; + if ( len == 0 ) + warning("multiple L$start$ labels found at same address in %s at offset 0x%04X", atom->name(), prevOffset); + this->encodeEntry(atom->finalAddress()+prevOffset-mhAddress, (*sfit)->offsetInAtom - prevOffset, prevKind); + } + prevKind = (*sfit)->kind; + prevOffset = (*sfit)->offsetInAtom; + } + if ( prevKind != ld::Fixup::kindDataInCodeEnd ) { + // add entry if function ends with data + this->encodeEntry(atom->finalAddress()+prevOffset-mhAddress, atom->size() - prevOffset, prevKind); + } + } + } + } + + this->_encoded = true; +} + + + + + +// linker needs to cache "Designated Requirements" in linked binary +template +class DependentDRAtom : public LinkEditAtom +{ +public: + DependentDRAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "dependent dylib DR info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; + +}; + +template +ld::Section DependentDRAtom::_s_section("__LINKEDIT", "__dependentDR", ld::Section::typeLinkEdit, true); + + +template +void DependentDRAtom::encode() const +{ + Security::SuperBlobCore, Security::kSecCodeMagicDRList, uint32_t>::Maker maker; + + uint32_t index = 0; + for(std::vector::iterator it=_state.dylibs.begin(); it != _state.dylibs.end(); ++it) { + const ld::dylib::File* dylib = *it; + Security::BlobCore* dylibDR = (Security::BlobCore*)dylib->codeSignatureDR(); + void* dup = NULL; + if ( dylibDR != NULL ) { + // Maker takes ownership of every blob added + // We need to make a copy here because dylib still owns the pointer returned by codeSignatureDR() + dup = ::malloc(dylibDR->length()); + ::memcpy(dup, dylibDR, dylibDR->length()); + } + maker.add(index, (Security::BlobCore*)dup); + ++index; + } + + Security::SuperBlob* topBlob = maker.make(); + const uint8_t* data = (uint8_t*)topBlob->data(); + for(size_t i=0; i < topBlob->length(); ++i) + _encodedData.append_byte(data[i]); + + this->_encoded = true; +} + + } // namespace tool } // namespace ld diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 7fbc755..172bb4c 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -230,6 +230,7 @@ class SymbolTableAtom : public ClassicLinkEditAtom uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool); uint64_t valueForStab(const ld::relocatable::File::Stab& stab); uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab); + void addDataInCodeLabels(const ld::Atom* atom, uint32_t& symbolIndex); mutable std::vector > _globals; @@ -242,18 +243,21 @@ class SymbolTableAtom : public ClassicLinkEditAtom uint32_t _stabsIndexEnd; static ld::Section _s_section; + static int _s_anonNameIndex; + }; template ld::Section SymbolTableAtom::_s_section("__LINKEDIT", "__symbol_table", ld::Section::typeLinkEdit, true); +template +int SymbolTableAtom::_s_anonNameIndex = 1; template bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) { macho_nlist

entry; - static int s_anonNameIndex = 1; assert(atom->symbolTableInclusion() != ld::Atom::symbolTableNotIn); // set n_strx @@ -264,7 +268,7 @@ bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) if ( atom->combine() == ld::Atom::combineByNameAndContent ) { // don't use 'l' labels for x86_64 strings // x86_64 obj-c runtime confused when static lib is stripped - sprintf(anonName, "LC%u", s_anonNameIndex++); + sprintf(anonName, "LC%u", _s_anonNameIndex++); symbolName = anonName; } } @@ -279,7 +283,7 @@ bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) } else if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) { // make auto-strip anonymous name for symbol - sprintf(anonName, "l%03u", s_anonNameIndex++); + sprintf(anonName, "l%03u", _s_anonNameIndex++); symbolName = anonName; } } @@ -335,7 +339,16 @@ void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) macho_nlist

entry; // set n_strx - entry.set_n_strx(pool->add(atom->name())); + const char* symbolName = atom->name(); + char anonName[32]; + if ( this->_options.outputKind() == Options::kObjectFile ) { + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) { + // make auto-strip anonymous name for symbol + sprintf(anonName, "l%03u", _s_anonNameIndex++); + symbolName = anonName; + } + } + entry.set_n_strx(pool->add(symbolName)); // set n_type if ( atom->definition() == ld::Atom::definitionAbsolute ) { @@ -356,8 +369,9 @@ void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) entry.set_n_type(N_EXT | N_SECT | N_PEXT); } else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) - && (atom->section().type() == ld::Section::typeMachHeader) ) { - // the __mh_execute_header is historical magic and must be an absolute symbol + && (atom->section().type() == ld::Section::typeMachHeader) + && !_options.positionIndependentExecutable() ) { + // the __mh_execute_header is historical magic in non-pie executabls and must be an absolute symbol entry.set_n_type(N_EXT | N_ABS); } } @@ -609,6 +623,49 @@ bool SymbolTableAtom::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, return ( (_stabsIndexStart != _stabsIndexEnd) || (_stabsStringsOffsetStart != _stabsStringsOffsetEnd) ); } + +template +void SymbolTableAtom::addDataInCodeLabels(const ld::Atom* atom, uint32_t& symbolIndex) +{ + char label[64]; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + label[0] = '\0'; + switch ( fit->kind ) { + case ld::Fixup::kindDataInCodeStartData: + sprintf(label, "L$start$data$%03u", symbolIndex); + break; + case ld::Fixup::kindDataInCodeStartJT8: + sprintf(label, "L$start$jt8$%03u", symbolIndex); + break; + case ld::Fixup::kindDataInCodeStartJT16: + sprintf(label, "L$start$jt16$%03u", symbolIndex); + break; + case ld::Fixup::kindDataInCodeStartJT32: + sprintf(label, "L$start$jt32$%03u", symbolIndex); + break; + case ld::Fixup::kindDataInCodeStartJTA32: + sprintf(label, "L$start$jta32$%03u", symbolIndex); + break; + case ld::Fixup::kindDataInCodeEnd: + sprintf(label, "L$start$code$%03u", symbolIndex); + break; + default: + break; + } + if ( label[0] != '\0' ) { + macho_nlist

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

); } +#if SUPPORT_ARCH_arm_any template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } +#endif template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return GENERIC_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X86_64_RELOC_UNSIGNED; } @@ -1363,8 +1431,8 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } else { // regular pointer - if ( !external && (entry.toAddend != 0) ) { - // use scattered reloc is target offset is non-zero + if ( !external && (entry.toAddend != 0) && (entry.toTarget->symbolTableInclusion() != ld::Atom::symbolTableNotIn) ) { + // use scattered reloc if target offset is non-zero into named atom (5658046) sreloc1->set_r_scattered(true); sreloc1->set_r_pcrel(false); sreloc1->set_r_length(2); @@ -1390,6 +1458,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } +#if SUPPORT_ARCH_arm_any template <> void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, const Entry& entry, std::vector >& relocs) @@ -1614,6 +1683,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } } +#endif @@ -1866,14 +1936,18 @@ bool IndirectSymbolTableAtom::kextBundlesDontHaveIndirectSymbolTable() template void IndirectSymbolTableAtom::encode() { - // static executables should not have an indirect symbol table - if ( this->_options.outputKind() == Options::kStaticExecutable ) + // static executables should not have an indirect symbol table, unless PIE + if ( (this->_options.outputKind() == Options::kStaticExecutable) && !_options.positionIndependentExecutable() ) return; // x86_64 kext bundles should not have an indirect symbol table if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() ) return; + // slidable static executables (-static -pie) should not have an indirect symbol table + if ( (this->_options.outputKind() == Options::kStaticExecutable) && this->_options.positionIndependentExecutable() ) + return; + // find all special sections that need a range of the indirect symbol table section for (std::vector::iterator sit = this->_state.sections.begin(); sit != this->_state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 629903f..58d49a7 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -26,15 +26,21 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include #include -#include "configure.h" #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" +#include "Snapshot.h" // upward dependency on lto::version() namespace lto { @@ -43,9 +49,19 @@ namespace lto { // magic to place command line in crash reports const int crashreporterBufferSize = 2000; -extern "C" char* __crashreporter_info__; static char crashreporterBuffer[crashreporterBufferSize]; -char* __crashreporter_info__ = crashreporterBuffer; +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + #include + // hack until ld does not need to build on 10.6 anymore + struct crashreporter_annotations_t gCRAnnotations + __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) + = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 }; +#else + extern "C" char* __crashreporter_info__; + __attribute__((used)) + char* __crashreporter_info__ = crashreporterBuffer; +#endif + static bool sEmitWarnings = true; static bool sFatalWarnings = false; @@ -88,6 +104,19 @@ void throwf(const char* format, ...) throw t; } +bool Options::FileInfo::checkFileExists(const char *p) +{ + struct stat statBuffer; + if (p == NULL) p = path; + if ( stat(p, &statBuffer) == 0 ) { + if (p != path) path = strdup(p); + fileLen = statBuffer.st_size; + modTime = statBuffer.st_mtime; + return true; + } + return false; +} + Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), @@ -102,8 +131,8 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), - fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), + fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), + fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), @@ -112,7 +141,7 @@ Options::Options(int argc, const char* argv[]) fSharedRegionEligible(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), - fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), + fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false), fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), @@ -131,10 +160,16 @@ Options::Options(int argc, const char* argv[]) fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false), fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false), + fDataInCodeInfoLoadCommand(false), fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false), + fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false), + fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false), + fSourceVersionLoadCommand(false), + fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), + fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), - fSaveTempFiles(false) + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -148,7 +183,6 @@ Options::~Options() { } - bool Options::errorBecauseOfWarnings() const { return (sFatalWarnings && (sWarningsCount > 0)); @@ -479,114 +513,77 @@ bool Options::keepLocalSymbol(const char* symbolName) const void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) { - fArchitecture = type; - fSubArchitecture = subtype; - switch ( type ) { - case CPU_TYPE_I386: - fArchitectureName = "i386"; - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); - #else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; - #endif - } - if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; - break; - case CPU_TYPE_X86_64: - fArchitectureName = "x86_64"; - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); - #else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; - #endif - } - if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; - break; - case CPU_TYPE_ARM: - fHasPreferredSubType = true; - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( t->subType == subtype ) { - fArchitectureName = t->subTypeName; - fArchSupportsThumb2 = t->supportsThumb2; - break; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) { + fArchitecture = type; + fSubArchitecture = subtype; + fArchitectureName = t->archName; + fHasPreferredSubType = t->isSubType; + fArchSupportsThumb2 = t->supportsThumb2; + switch ( type ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specified, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + case CPU_TYPE_ARM: + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); + #elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specified, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; } + fLinkSnapshot.recordArch(fArchitectureName); + return; } - assert(fArchitectureName != NULL); - if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { -#if defined(DEFAULT_IPHONEOS_MIN_VERSION) - warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION); - setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); -#elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); -#else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; -#endif - } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; - break; - default: - fArchitectureName = "unknown architecture"; - break; } + fArchitectureName = "unknown architecture"; } void Options::parseArch(const char* arch) { if ( arch == NULL ) throw "-arch must be followed by an architecture string"; - fArchitectureName = arch; - if ( strcmp(arch, "i386") == 0 ) { - fArchitecture = CPU_TYPE_I386; - fSubArchitecture = CPU_SUBTYPE_I386_ALL; - } - else if ( strcmp(arch, "x86_64") == 0 ) { - fArchitecture = CPU_TYPE_X86_64; - fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; - } - else if ( strcmp(arch, "arm") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_ALL; - } - else { - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( strcmp(t->subTypeName,arch) == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = t->subType; - fArchSupportsThumb2 = t->supportsThumb2; - fHasPreferredSubType = true; - return; - } + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( strcmp(t->archName,arch) == 0 ) { + fArchitectureName = arch; + fArchitecture = t->cpuType; + fSubArchitecture = t->cpuSubType; + fHasPreferredSubType = t->isSubType; + fArchSupportsThumb2 = t->supportsThumb2; + return; } - throwf("unknown/unsupported architecture name for: -arch %s", arch); } + throwf("unknown/unsupported architecture name for: -arch %s", arch); } bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const { - struct stat statBuffer; char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); - bool found = (stat(possiblePath, &statBuffer) == 0); + bool found = result.checkFileExists(possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); - if ( found ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return true; - } - return false; + return found; } @@ -674,7 +671,6 @@ Options::FileInfo Options::findFramework(const char* frameworkName) Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) { - struct stat statBuffer; for (std::vector::iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { @@ -695,15 +691,12 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi strcat(possiblePath, suffix); } } - bool found = (stat(possiblePath, &statBuffer) == 0); + FileInfo result; + bool found = result.checkFileExists(possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", (found ? " " : " not "), possiblePath); if ( found ) { - FileInfo result; - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; return result; } } @@ -717,7 +710,6 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi Options::FileInfo Options::findFile(const char* path) const { FileInfo result; - struct stat statBuffer; // if absolute path and not a .o file, the use SDK prefix if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { @@ -731,19 +723,13 @@ Options::FileInfo Options::findFile(const char* path) const if ( possiblePath[sdkPathDirLen-1] == '/' ) possiblePath[sdkPathDirLen-1] = '\0'; strcat(possiblePath, path); - if ( stat(possiblePath, &statBuffer) == 0 ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; + if ( result.checkFileExists(possiblePath) ) { return result; } } } // try raw path - if ( stat(path, &statBuffer) == 0 ) { - result.path = strdup(path); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; + if ( result.checkFileExists(path) ) { return result; } @@ -756,10 +742,7 @@ Options::FileInfo Options::findFile(const char* path) const strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); - if ( stat(newPath, &statBuffer) == 0 ) { - result.path = strdup(newPath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; + if ( result.checkFileExists(newPath) ) { return result; } } @@ -885,7 +868,7 @@ void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) fclose(file); } -void Options::loadFileList(const char* fileOfPaths) +void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal) { FILE* file; const char* comma = strrchr(fileOfPaths, ','); @@ -901,16 +884,17 @@ void Options::loadFileList(const char* fileOfPaths) realFileOfPaths[realFileOfPathsLen] = '\0'; file = fopen(realFileOfPaths, "r"); if ( file == NULL ) - throwf("-filelist file not found: %s\n", realFileOfPaths); + throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); } } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) - throwf("-filelist file not found: %s\n", fileOfPaths); + throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); } char path[PATH_MAX]; + ld::File::Ordinal previousOrdinal = baseOrdinal; while ( fgets(path, PATH_MAX, file) != NULL ) { path[PATH_MAX-1] = '\0'; char* eol = strchr(path, '\n'); @@ -921,10 +905,34 @@ void Options::loadFileList(const char* fileOfPaths) strcpy(builtPath, prefix); strcat(builtPath, "/"); strcat(builtPath, path); - fInputFiles.push_back(findFile(builtPath)); + if (fPipelineFifo != NULL) { + FileInfo info = FileInfo(builtPath); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } else { + FileInfo info = findFile(builtPath); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } } else { - fInputFiles.push_back(findFile(path)); + if (fPipelineFifo != NULL) { + FileInfo info = FileInfo(path); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } else { + FileInfo info = findFile(path); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } } } fclose(file); @@ -1719,6 +1727,9 @@ void Options::warnObsolete(const char* arg) // void Options::parse(int argc, const char* argv[]) { + // Store the original args in the link snapshot. + fLinkSnapshot.recordRawArgs(argc, argv); + // pass one builds search list from -L and -F options this->buildSearchPaths(argc, argv); @@ -1730,12 +1741,17 @@ void Options::parse(int argc, const char* argv[]) const char* arg = argv[i]; if ( arg[0] == '-' ) { + // by default, copy one arg to the snapshot link command, and do no file copying + int snapshotArgIndex = i; + int snapshotArgCount = -1; // -1 means compute count based on change in index + int snapshotFileArgIndex = -1; // -1 means no data file parameter to arg // Since we don't care about the files passed, just the option names, we do this here. if (fPrintOptions) fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + snapshotArgCount = 0; // stripped out of link snapshot if (arg[2] == '\0') ++i; // previously handled by buildSearchPaths() @@ -1782,22 +1798,38 @@ void Options::parse(int argc, const char* argv[]) fOutputKind = kKextBundle; } else if ( strcmp(arg, "-o") == 0 ) { + snapshotArgCount = 0; fOutputFile = argv[++i]; + fLinkSnapshot.setSnapshotName(fOutputFile); } else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[7], true); info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; } + else if ( strcmp(arg, "-lto_library") == 0 ) { + snapshotFileArgIndex = 1; + fOverridePathlibLTO = argv[++i]; + if ( fOverridePathlibLTO == NULL ) + throw "missing argument to -lto_library"; + } else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { - addLibrary(findLibrary(&arg[2])); + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[2]); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); } // This causes a dylib to be weakly bound at // link time. This corresponds to weak_import. else if ( strncmp(arg, "-weak-l", 7) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[7]); info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } // Avoid lazy binding. @@ -1831,6 +1863,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_load") == 0 ) { FileInfo info = findFile(argv[++i]); info.options.fForceLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } // Library versioning. @@ -1851,10 +1884,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-sectorder") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectorder missing

"; + snapshotFileArgIndex = 3; parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); i += 3; } else if ( strcmp(arg, "-order_file") == 0 ) { + snapshotFileArgIndex = 1; parseOrderFile(argv[++i], false); } else if ( strcmp(arg, "-order_file_statistics") == 0 ) { @@ -1865,6 +1900,7 @@ void Options::parse(int argc, const char* argv[]) else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectcreate missing
"; + snapshotFileArgIndex = 3; addSection(argv[i+1], argv[i+2], argv[i+3]); i += 3; } @@ -1873,7 +1909,7 @@ void Options::parse(int argc, const char* argv[]) || (strcmp(arg, "-dylinker_install_name") == 0) || (strcmp(arg, "-install_name") == 0)) { fDylibInstallName = argv[++i]; - if ( fDylibInstallName == NULL ) + if ( fDylibInstallName == NULL ) throw "-install_name missing "; } // Sets the base address of the output. @@ -1893,10 +1929,12 @@ void Options::parse(int argc, const char* argv[]) } // Same as -@ from the FSF linker. else if ( strcmp(arg, "-filelist") == 0 ) { + snapshotArgCount = 0; const char* path = argv[++i]; if ( (path == NULL) || (path[0] == '-') ) throw "-filelist missing "; - loadFileList(path); + ld::File::Ordinal baseOrdinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + loadFileList(path, baseOrdinal); } else if ( strcmp(arg, "-keep_private_externs") == 0 ) { fKeepPrivateExterns = true; @@ -1918,6 +1956,7 @@ void Options::parse(int argc, const char* argv[]) } } else if ( strcmp(arg, "-interposable_list") == 0 ) { + snapshotFileArgIndex = 1; fInterposeMode = kInterposeSome; loadExportFile(argv[++i], "-interposable_list", fInterposeList); } @@ -1926,12 +1965,14 @@ void Options::parse(int argc, const char* argv[]) fInterposeMode = kInterposeNone; } else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fExportMode == kDontExportSome ) throw "can't use -exported_symbols_list and -unexported_symbols_list"; fExportMode = kExportSome; loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); } else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fExportMode == kExportSome ) throw "can't use -unexported_symbols_list and -exported_symbols_list"; fExportMode = kDontExportSome; @@ -1950,12 +1991,14 @@ void Options::parse(int argc, const char* argv[]) fDontExportSymbols.insert(argv[++i]); } else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude ) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); } else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude ) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; @@ -1971,27 +2014,42 @@ void Options::parse(int argc, const char* argv[]) } // Similar to -weak-l but uses the absolute path name to the library. else if ( strcmp(arg, "-weak_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-lazy_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; } else if ( strcmp(arg, "-framework") == 0 ) { - addLibrary(findFramework(argv[++i])); + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); } else if ( strcmp(arg, "-weak_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-lazy_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; } @@ -2002,7 +2060,7 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-undefined") == 0 ) { - setUndefinedTreatment(argv[++i]); + setUndefinedTreatment(argv[++i]); } // Debugging output flag. else if ( strcmp(arg, "-arch_multiple") == 0 ) { @@ -2038,7 +2096,7 @@ void Options::parse(int argc, const char* argv[]) // Warn, error or make strong a mismatch between weak // and non-weak references. else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { - setWeakReferenceMismatchTreatment(argv[++i]); + setWeakReferenceMismatchTreatment(argv[++i]); } // For a deployment target of 10.3 and earlier ld64 will // prebind an executable with 0s in all addresses that @@ -2066,6 +2124,8 @@ void Options::parse(int argc, const char* argv[]) // This should probably be deprecated when we respect -L and -F // when searching for libraries. else if ( strcmp(arg, "-dylib_file") == 0 ) { + // ignore for snapshot because a stub dylib will be created in the snapshot + snapshotArgCount = 0; addDylibOverride(argv[++i]); } // What to expand @executable_path to if found in dependent dylibs @@ -2122,6 +2182,7 @@ void Options::parse(int argc, const char* argv[]) } // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { + snapshotFileArgIndex = 1; const char* name = argv[++i]; if ( name == NULL ) throw "-seg_addr_table missing argument"; @@ -2185,10 +2246,12 @@ void Options::parse(int argc, const char* argv[]) i += 2; } else if ( strcmp(arg, "-bundle_loader") == 0 ) { + snapshotFileArgIndex = 1; fBundleLoader = argv[++i]; if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) throw "-bundle_loader missing "; FileInfo info = findFile(fBundleLoader); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); info.options.fBundleLoader = true; fInputFiles.push_back(info); } @@ -2394,6 +2457,7 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-syslibroot") == 0 ) { + snapshotArgCount = 0; ++i; // previously handled by buildSearchPaths() } @@ -2404,6 +2468,7 @@ void Options::parse(int argc, const char* argv[]) fUUIDMode = kUUIDRandom; } else if ( strcmp(arg, "-dtrace") == 0 ) { + snapshotFileArgIndex = 1; const char* name = argv[++i]; if ( name == NULL ) throw "-dtrace missing argument"; @@ -2426,6 +2491,7 @@ void Options::parse(int argc, const char* argv[]) fAliases.push_back(pair); } else if ( strcmp(arg, "-alias_list") == 0 ) { + snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); } // put this last so that it does not interfer with other options starting with 'i' @@ -2468,33 +2534,51 @@ void Options::parse(int argc, const char* argv[]) fDisablePositionIndependentExecutable = true; } else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[11], true); info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-reexport_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-reexport_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strncmp(arg, "-upward-l", 9) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[9], true); info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-upward_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-upward_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { @@ -2539,6 +2623,7 @@ void Options::parse(int argc, const char* argv[]) fMarkDeadStrippableDylib = true; } else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { + snapshotFileArgIndex = 1; loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { @@ -2613,6 +2698,12 @@ void Options::parse(int argc, const char* argv[]) fFunctionStartsForcedOff = true; fFunctionStartsForcedOn = false; } + else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) { + fDataInCodeInfoLoadCommand = false; + } + else if ( strcmp(arg, "-data_in_code_info") == 0 ) { + fDataInCodeInfoLoadCommand = true; + } else if ( strcmp(arg, "-object_path_lto") == 0 ) { fTempLtoObjectPath = argv[++i]; if ( fTempLtoObjectPath == NULL ) @@ -2622,9 +2713,11 @@ void Options::parse(int argc, const char* argv[]) fObjcCategoryMerging = false; } else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { + snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); } else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { + snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); } else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { @@ -2640,6 +2733,7 @@ void Options::parse(int argc, const char* argv[]) fForceNotWeakSymbols.insert(symbol); } else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fExportMode == kExportSome ) throw "can't use -exported_symbols_list and -reexported_symbols_list"; loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols); @@ -2654,13 +2748,56 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) { fPageAlignDataAtoms = true; + } + else if (strcmp(arg, "-debug_snapshot") == 0) { + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fSnapshotRequested = true; + } + else if ( strcmp(arg, "-new_main") == 0 ) { + fEntryPointLoadCommandForceOn = true; + } + else if ( strcmp(arg, "-no_new_main") == 0 ) { + fEntryPointLoadCommandForceOff = true; + } + else if ( strcmp(arg, "-source_version") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-source_version missing "; + fSourceVersion = parseVersionNumber64(vers); + } + else if ( strcmp(arg, "-add_source_version") == 0 ) { + fSourceVersionLoadCommandForceOn = true; + } + else if ( strcmp(arg, "-no_source_version") == 0 ) { + fSourceVersionLoadCommandForceOff = true; + } + else if ( strcmp(arg, "-sdk_version") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-sdk_version missing "; + fSDKVersion = parseVersionNumber32(vers); + } + else if ( strcmp(arg, "-dependent_dr_info") == 0 ) { + fDependentDRInfoForcedOn = true; + } + else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) { + fDependentDRInfoForcedOff = true; + } + else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { + fKextsUseStubs = true; } else { throwf("unknown option: %s", arg); } + + if (snapshotArgCount == -1) + snapshotArgCount = i-snapshotArgIndex+1; + if (snapshotArgCount > 0) + fLinkSnapshot.addSnapshotLinkArg(snapshotArgIndex, snapshotArgCount, snapshotFileArgIndex); } else { FileInfo info = findFile(arg); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 ) addLibrary(info); else @@ -2670,8 +2807,13 @@ void Options::parse(int argc, const char* argv[]) // if a -lazy option was used, implicitly link in lazydylib1.o if ( fUsingLazyDylibLinking ) { - addLibrary(findLibrary("lazydylib1.o")); + FileInfo info = findLibrary("lazydylib1.o"); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)argc); + addLibrary(info); } + + if (fSnapshotRequested) + fLinkSnapshot.createSnapshot(); } @@ -2746,11 +2888,12 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fVerbose = true; extern const char ldVersionString[]; fprintf(stderr, "%s", ldVersionString); + fprintf(stderr, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS); // if only -v specified, exit cleanly if ( argc == 2 ) { const char* ltoVers = lto::version(); if ( ltoVers != NULL ) - fprintf(stderr, "%s\n", ltoVers); + fprintf(stderr, "LTO support using: %s\n", ltoVers); exit(0); } } @@ -2924,6 +3067,19 @@ void Options::parsePreCommandLineEnvironmentSettings() const char* customDyldPath = getenv("LD_DYLD_PATH"); if ( customDyldPath != NULL ) fDyldInstallPath = customDyldPath; + + const char* debugArchivePath = getenv("LD_DEBUG_SNAPSHOT"); + if (debugArchivePath != NULL) { + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + if (strlen(debugArchivePath) > 0) + fLinkSnapshot.setSnapshotPath(debugArchivePath); + fSnapshotRequested = true; + } + + const char* pipeFdString = getenv("LD_PIPELINE_FIFO"); + if (pipeFdString != NULL) { + fPipelineFifo = pipeFdString; + } } @@ -2966,6 +3122,13 @@ void Options::parsePostCommandLineEnvironmentSettings() // allow build system to force on -warn_commons if ( getenv("LD_WARN_COMMONS") != NULL ) fWarnCommons = true; + + // allow B&I to set default -source_version + if ( fSourceVersion == 0 ) { + const char* vers = getenv("RC_ProjectSourceVersion"); + if ( vers != NULL ) + fSourceVersion = parseVersionNumber64(vers); + } } @@ -3019,10 +3182,10 @@ void Options::reconfigureDefaults() case CPU_TYPE_X86_64: if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specificed, assuming 10.6"); + warning("-macosx_version_min not specified, assuming 10.6"); fMacVersionMin = ld::mac10_6; #endif } @@ -3030,13 +3193,13 @@ void Options::reconfigureDefaults() case CPU_TYPE_ARM: if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) - warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); #elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specificed, assuming 10.6"); + warning("-macosx_version_min not specified, assuming 10.6"); fMacVersionMin = ld::mac10_6; #endif } @@ -3058,7 +3221,7 @@ void Options::reconfigureDefaults() } break; case CPU_TYPE_X86_64: - if ( fMacVersionMin < ld::mac10_4 ) { + if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); fMacVersionMin = ld::mac10_4; } @@ -3080,7 +3243,8 @@ void Options::reconfigureDefaults() // iOS 5.0 and later use new MH_KEXT_BUNDLE type fMakeCompressedDyldInfo = false; fMakeCompressedDyldInfoForceOff = true; - fAllowTextRelocs = true; + fAllowTextRelocs = true; + fKextsUseStubs = !fAllowTextRelocs; fUndefinedTreatment = kUndefinedDynamicLookup; break; } @@ -3357,27 +3521,14 @@ void Options::reconfigureDefaults() // only use compressed LINKEDIT for: - // x86_64 and i386 on Mac OS X 10.6 or later - // arm on iPhoneOS 3.1 or later + // Mac OS X 10.6 or later + // iOS 3.1 or later if ( fMakeCompressedDyldInfo ) { - switch (fArchitecture) { - case CPU_TYPE_I386: - if ( fIOSVersionMin != ld::iOSVersionUnset ) // simulator always uses compressed LINKEDIT - break; - case CPU_TYPE_X86_64: - if ( fMacVersionMin < ld::mac10_6 ) - fMakeCompressedDyldInfo = false; - break; - case CPU_TYPE_ARM: - if ( !minOS(ld::mac10_6, ld::iOS_3_1) ) - fMakeCompressedDyldInfo = false; - break; - default: - fMakeCompressedDyldInfo = false; - } + if ( !minOS(ld::mac10_6, ld::iOS_3_1) ) + fMakeCompressedDyldInfo = false; } - + // only ARM enforces that cpu-sub-types must match if ( fArchitecture != CPU_TYPE_ARM ) fAllowCpuSubtypeMismatches = true; @@ -3437,9 +3588,9 @@ void Options::reconfigureDefaults() // set fOutputSlidable switch ( fOutputKind ) { case Options::kObjectFile: - case Options::kStaticExecutable: fOutputSlidable = false; break; + case Options::kStaticExecutable: case Options::kDynamicExecutable: fOutputSlidable = fPositionIndependentExecutable; break; @@ -3490,10 +3641,12 @@ void Options::reconfigureDefaults() switch ( fOutputKind ) { case Options::kObjectFile: fFunctionStartsLoadCommand = false; + fDataInCodeInfoLoadCommand = false; break; case Options::kPreload: case Options::kStaticExecutable: case Options::kKextBundle: + fDataInCodeInfoLoadCommand = false; if ( fFunctionStartsForcedOn ) fFunctionStartsLoadCommand = true; break; @@ -3530,6 +3683,130 @@ void Options::reconfigureDefaults() // on the command line if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && !fDisableNonExecutableHeap) fNonExecutableHeap = true; + + // Use LC_MAIN instead of LC_UNIXTHREAD for newer OSs + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + if ( fEntryPointLoadCommandForceOn ) { + fEntryPointLoadCommand = true; + fEntryName = "_main"; + } + else if ( fEntryPointLoadCommandForceOff ) { + fNeedsThreadLoadCommand = true; + } + else { + if ( minOS(ld::mac10_8, ld::iOS_Future) ) { + fEntryPointLoadCommand = true; + fEntryName = "_main"; + } + else + fNeedsThreadLoadCommand = true; + } + break; + case Options::kObjectFile: + case Options::kKextBundle: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kDyld: + fNeedsThreadLoadCommand = true; + break; + } + + // add LC_SOURCE_VERSION + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kKextBundle: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kStaticExecutable: + if ( fSourceVersionLoadCommandForceOn ) { + fSourceVersionLoadCommand = true; + } + else if ( fSourceVersionLoadCommandForceOff ) { + fSourceVersionLoadCommand = false; + } + else { + if ( minOS(ld::mac10_8, ld::iOS_Future) ) { + fSourceVersionLoadCommand = true; + } + else + fSourceVersionLoadCommand = false; + } + break; + case Options::kObjectFile: + case Options::kPreload: + fSourceVersionLoadCommand = false; + break; + } + + + // add LC_DYLIB_CODE_SIGN_DRS + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + if ( fDependentDRInfoForcedOn ) { + fDependentDRInfo = true; + } + else if ( fDependentDRInfoForcedOff ) { + fDependentDRInfo = false; + } + else { + if ( minOS(ld::mac10_8, ld::iOS_Future) ) + fDependentDRInfo = true; + else + fDependentDRInfo = false; + } + break; + case Options::kKextBundle: + case Options::kDyld: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kPreload: + fDependentDRInfo = false; + break; + } + + // if -sdk_version not on command line, infer from -syslibroot + if ( (fSDKVersion == 0) && (fSDKPaths.size() > 0) ) { + const char* sdkPath = fSDKPaths.front(); + const char* end = &sdkPath[strlen(sdkPath)-1]; + while ( !isdigit(*end) && (end > sdkPath) ) + --end; + const char* start = end-1; + while ( (isdigit(*start) || (*start == '.')) && (start > sdkPath)) + --start; + char sdkVersionStr[32]; + int len = end-start+1; + if ( len > 2 ) { + strlcpy(sdkVersionStr, start+1, len); + fSDKVersion = parseVersionNumber32(sdkVersionStr); + } + } + + // if -sdk_version and -syslibroot not used, but targeting MacOSX, use current OS version + if ( (fSDKVersion == 0) && (fMacVersionMin != ld::macVersionUnset) ) { + // special case if RC_ProjectName and MACOSX_DEPLOYMENT_TARGET are both set that sdkversion=minos + if ( getenv("RC_ProjectName") && getenv("MACOSX_DEPLOYMENT_TARGET") ) { + fSDKVersion = fMacVersionMin; + } + else { + int mib[2] = { CTL_KERN, KERN_OSRELEASE }; + char kernVersStr[100]; + size_t strlen = sizeof(kernVersStr); + if ( sysctl(mib, 2, kernVersStr, &strlen, NULL, 0) != -1 ) { + uint32_t kernVers = parseVersionNumber32(kernVersStr); + int minor = (kernVers >> 16) - 4; // kernel major version is 4 ahead of x in 10.x + fSDKVersion = 0x000A0000 + (minor << 8); + } + } + } + } void Options::checkIllegalOptionCombinations() @@ -3560,6 +3837,7 @@ void Options::checkIllegalOptionCombinations() if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { info.options.fReExport = true; found = true; + fLinkSnapshot.recordSubUmbrella(info.path); break; } } @@ -3582,6 +3860,7 @@ void Options::checkIllegalOptionCombinations() if ( strncmp(&lastSlash[1], subLibrary, dot-lastSlash-1) == 0 ) { info.options.fReExport = true; found = true; + fLinkSnapshot.recordSubLibrary(info.path); break; } } @@ -3891,10 +4170,10 @@ void Options::checkIllegalOptionCombinations() } } - // check -pie is only used when building a dynamic main executable for 10.5 if ( fPositionIndependentExecutable ) { switch ( fOutputKind ) { case Options::kDynamicExecutable: + // check -pie is only used when building a dynamic main executable for 10.5 if ( !minOS(ld::mac10_5, ld::iOS_4_2) ) { if ( fIOSVersionMin == ld::iOSVersionUnset ) throw "-pie can only be used when targeting Mac OS X 10.5 or later"; @@ -3902,14 +4181,15 @@ void Options::checkIllegalOptionCombinations() throw "-pie can only be used when targeting iOS 4.2 or later"; } break; + case Options::kStaticExecutable: case Options::kPreload: + // -pie is ok with -static or -preload break; case Options::kDynamicLibrary: case Options::kDynamicBundle: warning("-pie being ignored. It is only used when linking a main executable"); fPositionIndependentExecutable = false; break; - case Options::kStaticExecutable: case Options::kObjectFile: case Options::kDyld: case Options::kKextBundle: @@ -3966,6 +4246,9 @@ void Options::checkForClassic(int argc, const char* argv[]) bool newLinker = false; // build command line buffer in case ld crashes +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + CRSetCrashLogMessage(crashreporterBuffer); +#endif const char* srcRoot = getenv("SRCROOT"); if ( srcRoot != NULL ) { strlcpy(crashreporterBuffer, "SRCROOT=", crashreporterBufferSize); @@ -4018,31 +4301,6 @@ void Options::checkForClassic(int argc, const char* argv[]) } } } - - // -dtrace only supported by new linker - if( dtraceFound ) - return; - - if( archFound ) { - switch ( fArchitecture ) { - case CPU_TYPE_I386: - if ( (staticFound || kextFound) && !newLinker ) { - // this environment variable will disable use of ld_classic for -static links - if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { - this->gotoClassicLinker(argc, argv); - } - } - break; - } - } - else { - // work around for VSPTool - if ( staticFound ) { - warning("using ld_classic"); - this->gotoClassicLinker(argc, argv); - } - } - } void Options::gotoClassicLinker(int argc, const char* argv[]) @@ -4096,3 +4354,32 @@ void Options::gotoClassicLinker(int argc, const char* argv[]) fprintf(stderr, "can't exec ld_classic\n"); exit(1); } + + +// Note, returned string buffer is own by this function. +// It should not be freed +// It will be reused, so clients need to strdup() if they want +// to use it long term. +const char* Options::demangleSymbol(const char* sym) const +{ + // only try to demangle symbols if -demangle on command line + if ( !fDemangle ) + return sym; + + // only try to demangle symbols that look like C++ symbols + if ( strncmp(sym, "__Z", 3) != 0 ) + return sym; + + static size_t size = 1024; + static char* buff = (char*)malloc(size); + int status; + + char* result = abi::__cxa_demangle(&sym[1], buff, &size, &status); + if ( result != NULL ) { + // if demangling successful, keep buffer for next demangle + buff = result; + return buff; + } + return sym; +} + diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 5a8a7e0..be6dc56 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -34,10 +34,13 @@ #include #include "ld.hpp" +#include "Snapshot.h" extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); +class Snapshot; + class LibraryOptions { public: @@ -82,12 +85,42 @@ class Options enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; - struct FileInfo { + class FileInfo { + public: const char* path; uint64_t fileLen; time_t modTime; LibraryOptions options; - }; + ld::File::Ordinal ordinal; + bool fromFileList; + + // These are used by the threaded input file parsing engine. + mutable int inputFileSlot; // The input file "slot" assigned to this particular file + bool readyToParse; + + // The use pattern for FileInfo is to create one on the stack in a leaf function and return + // it to the calling frame by copy. Therefore the copy constructor steals the path string from + // the source, which dies with the stack frame. + FileInfo(FileInfo const &other) : path(other.path), fileLen(other.fileLen), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; }; + + // Create an empty FileInfo. The path can be set implicitly by checkFileExists(). + FileInfo() : path(NULL), fileLen(0), modTime(0), options(), fromFileList(false) {}; + + // Create a FileInfo for a specific path, but does not stat the file. + FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(0), options(), fromFileList(false) {}; + + ~FileInfo() { if (path) ::free((void*)path); } + + // Stat the file and update fileLen and modTime. + // If the object already has a path the p must be NULL. + // If the object does not have a path then p can be any candidate path, and if the file exists the object permanently remembers the path. + // Returns true if the file exists, false if not. + bool checkFileExists(const char *p=NULL); + + // Returns true if a previous call to checkFileExists() succeeded. + // Returns false if the file does not exist of checkFileExists() has never been called. + bool missing() const { return modTime==0; } +}; struct ExtraSection { const char* segmentName; @@ -244,6 +277,7 @@ class Options bool keepLocalSymbol(const char* symbolName) const; bool allowTextRelocs() const { return fAllowTextRelocs; } bool warnAboutTextRelocs() const { return fWarnTextRelocs; } + bool kextsUseStubs() const { return fKextsUseStubs; } bool usingLazyDylibLinking() const { return fUsingLazyDylibLinking; } bool verbose() const { return fVerbose; } bool makeEncryptable() const { return fEncryptable; } @@ -282,11 +316,12 @@ class Options bool objcGc() const { return fObjCGc; } bool objcGcOnly() const { return fObjCGcOnly; } bool canUseThreadLocalVariables() const { return fTLVSupport; } - bool demangleSymbols() const { return fDemangle; } bool addVersionLoadCommand() const { return fVersionLoadCommand; } bool addFunctionStarts() const { return fFunctionStartsLoadCommand; } + bool addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; } bool canReExportSymbols() const { return fCanReExportSymbols; } const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } + const char* overridePathlibLTO() const { return fOverridePathlibLTO; } bool objcCategoryMerging() const { return fObjcCategoryMerging; } bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; } bool hasWeakBitTweaks() const; @@ -294,8 +329,18 @@ class Options bool forceNotWeak(const char* symbolName) const; bool forceWeakNonWildCard(const char* symbolName) const; bool forceNotWeakNonWildcard(const char* symbolName) const; + Snapshot& snapshot() const { return fLinkSnapshot; } bool errorBecauseOfWarnings() const; - + bool needsThreadLoadCommand() const { return fNeedsThreadLoadCommand; } + bool needsEntryPointLoadCommand() const { return fEntryPointLoadCommand; } + bool needsSourceVersionLoadCommand() const { return fSourceVersionLoadCommand; } + bool needsDependentDRInfo() const { return fDependentDRInfo; } + uint64_t sourceVersion() const { return fSourceVersion; } + uint32_t sdkVersion() const { return fSDKVersion; } + const char* demangleSymbol(const char* sym) const; + bool pipelineEnabled() const { return fPipelineFifo != NULL; } + const char* pipelineFifo() const { return fPipelineFifo; } + private: class CStringEquals { @@ -343,7 +388,7 @@ class Options void parseOrderFile(const char* path, bool cstring); void addSection(const char* segment, const char* section, const char* path); void addSubLibrary(const char* name); - void loadFileList(const char* fileOfPaths); + void loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal); uint64_t parseAddress(const char* addr); void loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set); void parseAliasFile(const char* fileOfAliases); @@ -423,9 +468,12 @@ class Options const char* fMapPath; const char* fDyldInstallPath; const char* fTempLtoObjectPath; + const char* fOverridePathlibLTO; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; + uint64_t fSourceVersion; + uint32_t fSDKVersion; bool fExecutableStack; bool fNonExecutableHeap; bool fDisableNonExecutableHeap; @@ -454,6 +502,7 @@ class Options bool fDeadStripDylibs; bool fAllowTextRelocs; bool fWarnTextRelocs; + bool fKextsUseStubs; bool fUsingLazyDylibLinking; bool fEncryptable; bool fOrderData; @@ -502,9 +551,20 @@ class Options bool fFunctionStartsLoadCommand; bool fFunctionStartsForcedOn; bool fFunctionStartsForcedOff; + bool fDataInCodeInfoLoadCommand; bool fCanReExportSymbols; bool fObjcCategoryMerging; bool fPageAlignDataAtoms; + bool fNeedsThreadLoadCommand; + bool fEntryPointLoadCommand; + bool fEntryPointLoadCommandForceOn; + bool fEntryPointLoadCommandForceOff; + bool fSourceVersionLoadCommand; + bool fSourceVersionLoadCommandForceOn; + bool fSourceVersionLoadCommandForceOff; + bool fDependentDRInfo; + bool fDependentDRInfoForcedOn; + bool fDependentDRInfoForcedOff; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; @@ -526,6 +586,9 @@ class Options std::vector fSDKPaths; std::vector fDyldEnvironExtras; bool fSaveTempFiles; + mutable Snapshot fLinkSnapshot; + bool fSnapshotRequested; + const char * fPipelineFifo; }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index f217eb0..6fc91e8 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -72,11 +72,12 @@ namespace tool { OutputFile::OutputFile(const Options& opts) : hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), - _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), + _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), hasDataInCode(false), headerAndLoadCommandsSection(NULL), rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), lazyBindingSection(NULL), exportSection(NULL), splitSegInfoSection(NULL), functionStartsSection(NULL), + dataInCodeSection(NULL), dependentDRsSection(NULL), symbolTableSection(NULL), stringPoolSection(NULL), localRelocationsSection(NULL), externalRelocationsSection(NULL), sectionRelocationsSection(NULL), @@ -87,6 +88,8 @@ OutputFile::OutputFile(const Options& opts) _hasSectionRelocations(opts.outputKind() == Options::kObjectFile), _hasSplitSegInfo(opts.sharedRegionEligible()), _hasFunctionStartsInfo(opts.addFunctionStarts()), + _hasDataInCodeInfo(opts.addDataInCodeInfo()), + _hasDependentDRInfo(opts.needsDependentDRInfo()), _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), @@ -109,7 +112,9 @@ OutputFile::OutputFile(const Options& opts) _weakBindingInfoAtom(NULL), _exportInfoAtom(NULL), _splitSegInfoAtom(NULL), - _functionStartsAtom(NULL) + _functionStartsAtom(NULL), + _dataInCodeAtom(NULL), + _dependentDRInfoAtom(NULL) { } @@ -179,10 +184,14 @@ bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start void OutputFile::assignAtomAddresses(ld::Internal& state) { + const bool log = false; + if ( log ) fprintf(stderr, "assignAtomAddresses()\n"); for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName()); for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; + if ( log ) fprintf(stderr, " atom=%p, name=%s\n", atom, atom->name()); switch ( sect-> type() ) { case ld::Section::typeImportProxies: // want finalAddress() of all proxy atoms to be zero @@ -239,6 +248,18 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _functionStartsAtom->encode(); } + if ( _options.addDataInCodeInfo() ) { + // build data-in-code info + assert(_dataInCodeAtom != NULL); + _dataInCodeAtom->encode(); + } + + if ( _options.needsDependentDRInfo() ) { + // build dependent dylib DR info + assert(_dependentDRInfoAtom != NULL); + _dependentDRInfoAtom->encode(); + } + // build classic symbol table assert(_symbolTableAtom != NULL); _symbolTableAtom->encode(); @@ -572,8 +593,8 @@ void OutputFile::assignFileOffsets(ld::Internal& state) } } - if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", - sect->address, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, sect->segmentName(), sect->sectionName()); // update running totals if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) @@ -605,6 +626,9 @@ void OutputFile::assignFileOffsets(ld::Internal& state) if ( hasZeroForFileOffset(sect) ) { // fileoff of zerofill sections is moot, but historically it is set to zero sect->fileOffset = 0; + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; } else { // page align file offset at start of each segment @@ -713,6 +737,11 @@ uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup return (*target)->finalAddress(); case ld::Fixup::bindingsIndirectlyBound: *target = state.indirectBindingTable[fixup->u.bindingIndex]; + #ifndef NDEBUG + if ( ! (*target)->finalAddressMode() ) { + throwf("reference to symbol (which has not been assigned an address) %s", (*target)->name()); + } + #endif return (*target)->finalAddress(); } throw "unexpected binding"; @@ -1205,6 +1234,13 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: assert(fit->binding == ld::Fixup::bindingDirectlyBound); accumulator = this->lazyBindingInfoOffsetForLazyPointerAddress(fit->u.target->finalAddress()); break; + case ld::Fixup::kindDataInCodeStartData: + case ld::Fixup::kindDataInCodeStartJT8: + case ld::Fixup::kindDataInCodeStartJT16: + case ld::Fixup::kindDataInCodeStartJT32: + case ld::Fixup::kindDataInCodeStartJTA32: + case ld::Fixup::kindDataInCodeEnd: + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); @@ -1529,35 +1565,8 @@ bool OutputFile::hasZeroForFileOffset(const ld::Section* sect) return false; } - -void OutputFile::writeOutputFile(ld::Internal& state) +void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) { - // for UNIX conformance, error if file exists and is not writable - if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) ) - throwf("can't write output file: %s", _options.outputFilePath()); - - int permissions = 0777; - if ( _options.outputKind() == Options::kObjectFile ) - permissions = 0666; - // Calling unlink first assures the file is gone so that open creates it with correct permissions - // It also handles the case where __options.outputFilePath() file is not writable but its directory is - // And it means we don't have to truncate the file when done writing (in case new is smaller than old) - // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). - struct stat stat_buf; - if ( (stat(_options.outputFilePath(), &stat_buf) != -1) && (stat_buf.st_mode & S_IFREG) ) - (void)unlink(_options.outputFilePath()); - - // try to allocate buffer for entire output file content - uint8_t* wholeBuffer = (uint8_t*)calloc(_fileSize, 1); - if ( wholeBuffer == NULL ) - throwf("can't create buffer of %llu bytes for output", _fileSize); - - if ( _options.UUIDMode() == Options::kUUIDRandom ) { - uint8_t bits[16]; - ::uuid_generate_random(bits); - _headersAndLoadCommandAtom->setUUID(bits); - } - // have each atom write itself uint64_t fileOffsetOfEndOfLastAtom = 0; uint64_t mhAddress = 0; @@ -1598,74 +1607,153 @@ void OutputFile::writeOutputFile(ld::Internal& state) } } } +} + + +void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) +{ + const bool log = false; + if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + uint32_t stabsStringsOffsetStart; + uint32_t tabsStringsOffsetEnd; + uint32_t stabsOffsetStart; + uint32_t stabsOffsetEnd; + if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) { + // find two areas of file that are stabs info and should not contribute to checksum + uint64_t stringPoolFileOffset = 0; + uint64_t symbolTableFileOffset = 0; + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeLinkEdit ) { + if ( strcmp(sect->sectionName(), "__string_pool") == 0 ) + stringPoolFileOffset = sect->fileOffset; + else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 ) + symbolTableFileOffset = sect->fileOffset; + } + } + uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart; + uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; + uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; + uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; + if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); + if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); + if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); + if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); + assert(firstStabNlistFileOffset <= firstStabStringFileOffset); + + CC_MD5_CTX md5state; + CC_MD5_Init(&md5state); + // checksum everything up to first stabs nlist + if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); + // checkusm everything after last stabs nlist and up to first stabs string + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); + // checksum everything after last stabs string to end of file + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); + CC_MD5_Final(digest, &md5state); + if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], + digest[3], digest[4], digest[5], digest[6], digest[7]); + } + else { + CC_MD5(wholeBuffer, _fileSize, digest); + } + // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); + digest[8] = ( digest[8] & 0x3F ) | 0x80; + // update buffer with new UUID + _headersAndLoadCommandAtom->setUUID(digest); + _headersAndLoadCommandAtom->recopyUUIDCommand(); + } +} - // compute UUID - if ( _options.UUIDMode() == Options::kUUIDContent ) { - const bool log = false; - if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { - uint8_t digest[CC_MD5_DIGEST_LENGTH]; - uint32_t stabsStringsOffsetStart; - uint32_t tabsStringsOffsetEnd; - uint32_t stabsOffsetStart; - uint32_t stabsOffsetEnd; - if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) { - // find two areas of file that are stabs info and should not contribute to checksum - uint64_t stringPoolFileOffset = 0; - uint64_t symbolTableFileOffset = 0; - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeLinkEdit ) { - if ( strcmp(sect->sectionName(), "__string_pool") == 0 ) - stringPoolFileOffset = sect->fileOffset; - else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 ) - symbolTableFileOffset = sect->fileOffset; - } - } - uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart; - uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; - uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; - uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; - if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); - if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); - if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); - if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); - assert(firstStabNlistFileOffset <= firstStabStringFileOffset); - - CC_MD5_CTX md5state; - CC_MD5_Init(&md5state); - // checksum everything up to first stabs nlist - if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); - // checkusm everything after last stabs nlist and up to first stabs string - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); - // checksum everything after last stabs string to end of file - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); - CC_MD5_Final(digest, &md5state); - if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], - digest[3], digest[4], digest[5], digest[6], digest[7]); - } - else { - CC_MD5(wholeBuffer, _fileSize, digest); - } - // LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats - digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); - digest[8] = ( digest[8] & 0x3F ) | 0x80; - // update buffer with new UUID - _headersAndLoadCommandAtom->setUUID(digest); - _headersAndLoadCommandAtom->recopyUUIDCommand(); + +void OutputFile::writeOutputFile(ld::Internal& state) +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) ) + throwf("can't write output file: %s", _options.outputFilePath()); + + mode_t permissions = 0777; + if ( _options.outputKind() == Options::kObjectFile ) + permissions = 0666; + mode_t umask = ::umask(0); + ::umask(umask); // put back the original umask + permissions &= ~umask; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where __options.outputFilePath() file is not writable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). + struct stat stat_buf; + bool outputIsRegularFile = true; + if ( stat(_options.outputFilePath(), &stat_buf) != -1 ) { + if (stat_buf.st_mode & S_IFREG) { + (void)unlink(_options.outputFilePath()); + } else { + outputIsRegularFile = false; + } + } + + int fd; + // Construct a temporary path of the form {outputFilePath}.ld_XXXXXX + const char filenameTemplate[] = ".ld_XXXXXX"; + char tmpOutput[PATH_MAX]; + uint8_t *wholeBuffer; + if (outputIsRegularFile) { + strcpy(tmpOutput, _options.outputFilePath()); + // If the path is too long to add a suffix for a temporary name then + // just fall back to using the output path. + if (strlen(tmpOutput)+strlen(filenameTemplate) < PATH_MAX) { + strcat(tmpOutput, filenameTemplate); + fd = mkstemp(tmpOutput); + } else { + fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); } + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", tmpOutput, errno); + ftruncate(fd, _fileSize); + + wholeBuffer = (uint8_t *)mmap(NULL, _fileSize, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + if ( wholeBuffer == MAP_FAILED ) + throwf("can't create buffer of %llu bytes for output", _fileSize); + } else { + fd = open(_options.outputFilePath(), O_WRONLY); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); + // try to allocate buffer for entire output file content + wholeBuffer = (uint8_t*)calloc(_fileSize, 1); + if ( wholeBuffer == NULL ) + throwf("can't create buffer of %llu bytes for output", _fileSize); + } + + if ( _options.UUIDMode() == Options::kUUIDRandom ) { + uint8_t bits[16]; + ::uuid_generate_random(bits); + _headersAndLoadCommandAtom->setUUID(bits); } - // write whole output file in one chunk - int fd = open(_options.outputFilePath(), O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); - if ( ::pwrite(fd, wholeBuffer, _fileSize, 0) == -1 ) - throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); - close(fd); - free(wholeBuffer); + writeAtoms(state, wholeBuffer); + + // compute UUID + if ( _options.UUIDMode() == Options::kUUIDContent ) + computeContentUUID(state, wholeBuffer); + + if (outputIsRegularFile) { + if ( ::chmod(tmpOutput, permissions) == -1 ) { + unlink(tmpOutput); + throwf("can't set permissions on output file: %s, errno=%d", tmpOutput, errno); + } + if ( ::rename(tmpOutput, _options.outputFilePath()) == -1 && strcmp(tmpOutput, _options.outputFilePath()) != 0) { + unlink(tmpOutput); + throwf("can't move output file in place, errno=%d", errno); + } + } else { + if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { + throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); + } + } } struct AtomByNameSorter @@ -1778,27 +1866,6 @@ void OutputFile::buildSymbolTable(ld::Internal& state) continue; } - // Add command line options to control symbol weak-def bit on exported symbols - if ( _options.hasWeakBitTweaks() && (atom->definition() == ld::Atom::definitionRegular) ) { - const char* name = atom->name(); - if ( atom->scope() == ld::Atom::scopeGlobal ) { - if ( atom->combine() == ld::Atom::combineNever ) { - if ( _options.forceWeak(name) ) - (const_cast(atom))->setCombine(ld::Atom::combineByName); - } - else if ( atom->combine() == ld::Atom::combineByName ) { - if ( _options.forceNotWeak(name) ) - (const_cast(atom))->setCombine(ld::Atom::combineNever); - } - } - else { - if ( _options.forceWeakNonWildCard(name) ) - warning("cannot force to be weak, non-external symbol %s", name); - else if ( _options.forceNotWeakNonWildcard(name) ) - warning("cannot force to be not-weak, non-external symbol %s", name); - } - } - switch ( atom->scope() ) { case ld::Atom::scopeTranslationUnit: if ( _options.keepLocalSymbol(atom->name()) ) { @@ -1879,6 +1946,7 @@ void OutputFile::buildSymbolTable(ld::Internal& state) void OutputFile::addPreloadLinkEdit(ld::Internal& state) { switch ( _options.architecture() ) { +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1897,6 +1965,8 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1915,6 +1985,8 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( _hasLocalRelocations ) { _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); @@ -1933,6 +2005,7 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif default: throw "architecture not supported for -preload"; } @@ -1947,6 +2020,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) return addPreloadLinkEdit(state); switch ( _options.architecture() ) { +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -1980,6 +2054,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -1995,6 +2077,8 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2028,6 +2112,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -2043,6 +2135,8 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( _hasSectionRelocations ) { _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); @@ -2076,6 +2170,14 @@ void OutputFile::addLinkEdit(ld::Internal& state) _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); functionStartsSection = state.addAtom(*_functionStartsAtom); } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -2091,6 +2193,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif default: throw "unknown architecture"; } @@ -2099,18 +2202,24 @@ void OutputFile::addLinkEdit(ld::Internal& state) void OutputFile::addLoadCommands(ld::Internal& state) { switch ( _options.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif default: throw "unknown architecture"; } @@ -2485,7 +2594,15 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) } assert(minusTarget != NULL); break; - default: + case ld::Fixup::kindDataInCodeStartData: + case ld::Fixup::kindDataInCodeStartJT8: + case ld::Fixup::kindDataInCodeStartJT16: + case ld::Fixup::kindDataInCodeStartJT32: + case ld::Fixup::kindDataInCodeStartJTA32: + case ld::Fixup::kindDataInCodeEnd: + hasDataInCode = true; + break; + default: break; } if ( this->isStore(fit->kind) ) { @@ -2530,7 +2647,8 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) if ( _options.warnAboutTextRelocs() ) warning("text reloc in %s to %s", atom->name(), target->name()); } - else if ( _options.positionIndependentExecutable() && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { + else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable) + && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { if ( ! this->pieDisabled ) { warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " "but used in %s from %s. " @@ -2568,9 +2686,10 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s return; } // Have direct reference to weak-global. This should be an indrect reference + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - atom->name(), target->name()); + demangledName, _options.demangleSymbol(target->name())); } return; } @@ -2596,9 +2715,10 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s return; } // Have direct reference to weak-global. This should be an indrect reference + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - atom->name(), target->name()); + demangledName, _options.demangleSymbol(target->name())); } return; } @@ -2620,7 +2740,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s uint8_t rebaseType = REBASE_TYPE_POINTER; uint8_t type = BIND_TYPE_POINTER; const ld::dylib::File* dylib = dynamic_cast(target->file()); - bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->forcedWeakLinked())); + bool weak_import = (fixupWithTarget->weakImport || ((dylib != NULL) && dylib->forcedWeakLinked())); uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom; uint64_t addend = targetAddend - minusTargetAddend; @@ -2716,6 +2836,22 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32; } + if ( (addend != 0) && _options.sharedRegionEligible() ) { + // make sure the addend does not cause the pointer to point outside the target's segment + // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache + uint64_t targetAddress = target->finalAddress(); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sct = *sit; + uint64_t sctEnd = (sct->address+sct->size); + if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) { + if ( (targetAddress+addend) > sctEnd ) { + warning("data symbol %s from %s has pointer to %s + 0x%08llX. " + "That large of an addend may disable %s from being put in the dyld shared cache.", + atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + } + } + } + } _rebaseInfo.push_back(RebaseInfo(rebaseType, address)); } if ( needsBinding ) { @@ -2745,10 +2881,20 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio return; // non-lazy-pointer section is encoded in indirect symbol table - not using relocations - if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) { - assert(target != NULL); - assert(fixupWithTarget != NULL); - return; + if ( sect->type() == ld::Section::typeNonLazyPointer ) { + // except kexts and static pie which *do* use relocations + switch (_options.outputKind()) { + case Options::kKextBundle: + break; + case Options::kStaticExecutable: + if ( _options.positionIndependentExecutable() ) + break; + // else fall into default case + default: + assert(target != NULL); + assert(fixupWithTarget != NULL); + return; + } } // no need to rebase or bind PCRel stores @@ -3091,8 +3237,8 @@ void OutputFile::writeMapFile(ld::Internal& state) // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); //} // write table of object files - std::map readerToOrdinal; - std::map ordinalToReader; + std::map readerToOrdinal; + std::map ordinalToReader; std::map readerToFileOrdinal; for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; @@ -3103,8 +3249,8 @@ void OutputFile::writeMapFile(ld::Internal& state) const ld::File* reader = atom->file(); if ( reader == NULL ) continue; - uint32_t readerOrdinal = reader->ordinal(); - std::map::iterator pos = readerToOrdinal.find(reader); + ld::File::Ordinal readerOrdinal = reader->ordinal(); + std::map::iterator pos = readerToOrdinal.find(reader); if ( pos == readerToOrdinal.end() ) { readerToOrdinal[reader] = readerOrdinal; ordinalToReader[readerOrdinal] = reader; @@ -3113,13 +3259,10 @@ void OutputFile::writeMapFile(ld::Internal& state) } fprintf(mapFile, "# Object files:\n"); fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); - uint32_t fileIndex = 0; - readerToFileOrdinal[NULL] = fileIndex++; - for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first != 0 ) { - fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path()); - readerToFileOrdinal[it->second] = fileIndex++; - } + uint32_t fileIndex = 1; + for(std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path()); + readerToFileOrdinal[it->second] = fileIndex++; } // write table of sections fprintf(mapFile, "# Sections:\n"); @@ -3196,8 +3339,8 @@ class DebugNoteSorter bool operator()(const ld::Atom* left, const ld::Atom* right) const { // first sort by reader - uint32_t leftFileOrdinal = left->file()->ordinal(); - uint32_t rightFileOrdinal = right->file()->ordinal(); + ld::File::Ordinal leftFileOrdinal = left->file()->ordinal(); + ld::File::Ordinal rightFileOrdinal = right->file()->ordinal(); if ( leftFileOrdinal!= rightFileOrdinal) return (leftFileOrdinal < rightFileOrdinal); @@ -3300,11 +3443,13 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) const char* newDirPath; const char* newFilename; //fprintf(stderr, "debug note for %s\n", atom->name()); - if ( atom->translationUnitSource(&newDirPath, &newFilename) ) { + // guard against dwarf info that has no directory + if ( atom->translationUnitSource(&newDirPath, &newFilename) && (newDirPath != NULL)) { // need SO's whenever the translation unit source file changes if ( newFilename != filename ) { // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + size_t len = strlen(newDirPath); + if ( (newDirPath != NULL) && (len > 1 ) && (newDirPath[len-1] != '/') ) asprintf((char**)&newDirPath, "%s/", newDirPath); if ( filename != NULL ) { // translation unit change, emit ending SO diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index 8401716..d3567ae 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -74,6 +74,7 @@ class OutputFile bool _noReExportedDylibs; bool hasThreadLocalVariableDefinitions; bool pieDisabled; + bool hasDataInCode; ld::Internal::FinalSection* headerAndLoadCommandsSection; ld::Internal::FinalSection* rebaseSection; ld::Internal::FinalSection* bindingSection; @@ -82,6 +83,8 @@ class OutputFile ld::Internal::FinalSection* exportSection; ld::Internal::FinalSection* splitSegInfoSection; ld::Internal::FinalSection* functionStartsSection; + ld::Internal::FinalSection* dataInCodeSection; + ld::Internal::FinalSection* dependentDRsSection; ld::Internal::FinalSection* symbolTableSection; ld::Internal::FinalSection* stringPoolSection; ld::Internal::FinalSection* localRelocationsSection; @@ -137,6 +140,8 @@ class OutputFile }; private: + void writeAtoms(ld::Internal& state, uint8_t* wholeBuffer); + void computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer); void buildDylibOrdinalMapping(ld::Internal&); bool hasOrdinalForInstallPath(const char* path, int* ordinal); void addLoadCommands(ld::Internal& state); @@ -243,6 +248,8 @@ class OutputFile const bool _hasSectionRelocations; const bool _hasSplitSegInfo; const bool _hasFunctionStartsInfo; + const bool _hasDataInCodeInfo; + const bool _hasDependentDRInfo; bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; @@ -280,6 +287,8 @@ class OutputFile class LinkEditAtom* _exportInfoAtom; class LinkEditAtom* _splitSegInfoAtom; class LinkEditAtom* _functionStartsAtom; + class LinkEditAtom* _dataInCodeAtom; + class LinkEditAtom* _dependentDRInfoAtom; }; } // namespace tool diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 2502820..3a28788 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -292,6 +292,7 @@ void Resolver::buildAtomList() // each input files contributes initial atoms _atoms.reserve(1024); _inputFiles.forEachInitialAtom(*this); + _completedInitialObjectFiles = true; //_symbolTable.printStatistics(); @@ -332,10 +333,6 @@ void Resolver::doFile(const ld::File& file) if ( objFile->debugInfo() == ld::relocatable::File::kDebugInfoDwarf ) _internal.someObjectFileHasDwarf = true; - // remember if any objc classes built for fix-and-continue - if ( objFile->objcReplacementClasses() ) - _internal.hasObjcReplacementClasses = true; - // remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set if ( ! objFile->canScatterAtoms() ) _internal.allObjectFilesScatterable = false; @@ -427,16 +424,16 @@ void Resolver::doAtom(const ld::Atom& atom) } else if ( _options.outputKind() == Options::kDynamicLibrary ) { if ( atom.file() != NULL ) - warning("target OS does not support re-exporting symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path()); + warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.file()->path()); else - warning("target OS does not support re-exporting symbol %s\n", SymbolTable::demangle(name)); + warning("target OS does not support re-exporting symbol %s\n", _options.demangleSymbol(name)); } } else { if ( atom.file() != NULL ) - warning("cannot export hidden symbol %s from %s", SymbolTable::demangle(name), atom.file()->path()); + warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.file()->path()); else - warning("cannot export hidden symbol %s", SymbolTable::demangle(name)); + warning("cannot export hidden symbol %s", _options.demangleSymbol(name)); } } } @@ -446,7 +443,7 @@ void Resolver::doAtom(const ld::Atom& atom) (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); } else { - throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); } } break; @@ -457,7 +454,7 @@ void Resolver::doAtom(const ld::Atom& atom) //fprintf(stderr, "demote %s to hidden\n", name); } if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) { - throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); } break; } @@ -491,11 +488,7 @@ void Resolver::doAtom(const ld::Atom& atom) // remember if any atoms are proxies that require LTO if ( atom.contentType() == ld::Atom::typeLTOtemporary ) _haveLLVMObjs = true; - - // if we've already partitioned into final sections, and lto needs a symbol very late, add it - if ( _addToFinalSection ) - _internal.addAtom(atom); - + if ( _options.deadCodeStrip() ) { // add to set of dead-strip-roots, all symbols that the compiler marks as don't strip if ( atom.dontDeadStrip() ) @@ -856,9 +849,10 @@ void Resolver::deadStripOptimize() // now remove all non-live atoms from _atoms const bool log = false; if ( log ) { - fprintf(stderr, "deadStripOptimize() all atoms with liveness:\n"); + fprintf(stderr, "deadStripOptimize() all %ld atoms with liveness:\n", _atoms.size()); for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { - fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name()); + const ld::File* file = (*it)->file(); + fprintf(stderr, " live=%d atom=%p name=%s from=%s\n", (*it)->live(), *it, (*it)->name(), (file ? file->path() : "")); } } @@ -869,9 +863,56 @@ void Resolver::deadStripOptimize() else { _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); } + + if ( log ) { + fprintf(stderr, "deadStripOptimize() %ld remaining atoms\n", _atoms.size()); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + fprintf(stderr, " live=%d atom=%p name=%s\n", (*it)->live(), *it, (*it)->name()); + } + } } +// This is called when LTO is used but -dead_strip is not used. +// Some undefines were eliminated by LTO, but others were not. +void Resolver::remainingUndefines(std::vector& undefs) +{ + StringSet undefSet; + // search all atoms for references that are unbound + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( (ld::Fixup::TargetBinding)fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + assert(0 && "should not be by-name this late"); + undefSet.insert(fit->u.name); + break; + case ld::Fixup::bindingsIndirectlyBound: + if ( _internal.indirectBindingTable[fit->u.bindingIndex] == NULL ) { + undefSet.insert(_symbolTable.indirectName(fit->u.bindingIndex)); + } + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingNone: + case ld::Fixup::bindingDirectlyBound: + break; + } + } + } + // look for any initial undefines that are still undefined + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + if ( ! _symbolTable.hasName(*uit) ) { + undefSet.insert(*uit); + } + } + + // copy set to vector + for (StringSet::const_iterator it=undefSet.begin(); it != undefSet.end(); ++it) { + fprintf(stderr, "undef: %s\n", *it); + undefs.push_back(*it); + } +} + void Resolver::liveUndefines(std::vector& undefs) { StringSet undefSet; @@ -990,7 +1031,7 @@ bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingS ++foundReferenceCount; } else { - fprintf(stderr, " %s in %s\n", SymbolTable::demangle(atom->name()), pathLeafName(atom->file()->path())); + fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->file()->path())); ++foundReferenceCount; break; // if undefined used twice in a function, only show first } @@ -1029,9 +1070,10 @@ void Resolver::checkUndefines(bool force) break; } std::vector unresolvableUndefines; - // LTO many have eliminated need for some undefines - if ( _options.deadCodeStrip() || _haveLLVMObjs ) + if ( _options.deadCodeStrip() ) this->liveUndefines(unresolvableUndefines); + else if( _haveLLVMObjs ) + this->remainingUndefines(unresolvableUndefines); // LTO may have eliminated need for some undefines else _symbolTable.undefines(unresolvableUndefines); @@ -1056,7 +1098,7 @@ void Resolver::checkUndefines(bool force) for (int i=0; i < unresolvableCount; ++i) { const char* name = unresolvableUndefines[i]; unsigned int slot = _symbolTable.findSlotForName(name); - fprintf(stderr, " \"%s\", referenced from:\n", SymbolTable::demangle(name)); + fprintf(stderr, " \"%s\", referenced from:\n", _options.demangleSymbol(name)); // scan all atoms for references bool foundAtomReference = printReferencedBy(name, slot); // scan command line options @@ -1174,8 +1216,6 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) else if ( _internal.indirectBindingTable[slot]->definition() == ld::Atom::definitionProxy ) { if ( makingDylib ) throwf("-init function (%s) found in linked dylib, must be in dylib being linked", symbolName); - else - throwf("entry point (%s) found in linked dylib, must be in executable being linked", symbolName); } return _internal.indirectBindingTable[slot]; } @@ -1262,7 +1302,17 @@ void Resolver::fillInInternalState() void Resolver::removeCoalescedAwayAtoms() { + const bool log = false; + if ( log ) { + fprintf(stderr, "removeCoalescedAwayAtoms() starts with %lu atoms\n", _atoms.size()); + } _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), AtomCoalescedAway()), _atoms.end()); + if ( log ) { + fprintf(stderr, "removeCoalescedAwayAtoms() after removing coalesced atoms, %lu remain\n", _atoms.size()); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + fprintf(stderr, " atom=%p %s\n", *it, (*it)->name()); + } + } } void Resolver::linkTimeOptimize() @@ -1275,7 +1325,7 @@ void Resolver::linkTimeOptimize() lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); optOpt.tmpObjectFilePath = _options.tempLtoObjectPath(); - optOpt.allGlobalsAReDeadStripRoots = _options.allGlobalsAreDeadStripRoots(); + optOpt.preserveAllGlobals = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList(); optOpt.verbose = _options.verbose(); optOpt.saveTemps = _options.saveTempFiles(); optOpt.pie = _options.positionIndependentExecutable(); @@ -1289,7 +1339,7 @@ void Resolver::linkTimeOptimize() std::vector newAtoms; std::vector additionalUndefines; - if ( ! lto::optimize(_atoms, _internal, _inputFiles.nextInputOrdinal(), optOpt, *this, newAtoms, additionalUndefines) ) + if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) ) return; // if nothing done @@ -1299,36 +1349,12 @@ void Resolver::linkTimeOptimize() // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); - - // add new atoms into their final section - for (std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) { - _internal.addAtom(**it); - } - // remove temp lto section and move all of its atoms to their final section - ld::Internal::FinalSection* tempLTOsection = NULL; - for (std::vector::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeTempLTO ) { - tempLTOsection = sect; - // remove temp lto section from final image - _internal.sections.erase(sit); - break; - } - } - // lto atoms now have proper section info, so add to final section - if ( tempLTOsection != NULL ) { - for (std::vector::iterator ait=tempLTOsection->atoms.begin(); ait != tempLTOsection->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - if ( ! atom->coalescedAway() ) { - this->convertReferencesToIndirect(*atom); - _internal.addAtom(*atom); - } - } - } - + // run through all atoms again and make sure newly codegened atoms have refernces bound + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) + this->convertReferencesToIndirect(**it); + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) - _addToFinalSection = true; for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; // these symbols may or may not already be in linker's symbol table @@ -1336,7 +1362,6 @@ void Resolver::linkTimeOptimize() _inputFiles.searchLibraries(targetName, true, true, false, *this); } } - _addToFinalSection = false; // if -dead_strip on command line if ( _options.deadCodeStrip() ) { @@ -1346,19 +1371,11 @@ void Resolver::linkTimeOptimize() } // and re-compute dead code this->deadStripOptimize(); - - // remove newly dead atoms from each section - for (std::vector::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NotLive()), sect->atoms.end()); - } } if ( _options.outputKind() == Options::kObjectFile ) { // if -r mode, add proxies for new undefines (e.g. ___stack_chk_fail) - _addToFinalSection = true; this->resolveUndefines(); - _addToFinalSection = false; } else { // last chance to check for undefines @@ -1370,6 +1387,39 @@ void Resolver::linkTimeOptimize() } +void Resolver::tweakWeakness() +{ + // Add command line options to control symbol weak-def bit on exported symbols + if ( _options.hasWeakBitTweaks() ) { + for (std::vector::iterator sit = _internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->definition() != ld::Atom::definitionRegular ) + continue; + const char* name = atom->name(); + if ( atom->scope() == ld::Atom::scopeGlobal ) { + if ( atom->combine() == ld::Atom::combineNever ) { + if ( _options.forceWeak(name) ) + (const_cast(atom))->setCombine(ld::Atom::combineByName); + } + else if ( atom->combine() == ld::Atom::combineByName ) { + if ( _options.forceNotWeak(name) ) + (const_cast(atom))->setCombine(ld::Atom::combineNever); + } + } + else { + if ( _options.forceWeakNonWildCard(name) ) + warning("cannot force to be weak, non-external symbol %s", name); + else if ( _options.forceNotWeakNonWildcard(name) ) + warning("cannot force to be not-weak, non-external symbol %s", name); + } + } + } + } +} + + void Resolver::resolve() { this->initializeState(); @@ -1381,8 +1431,10 @@ void Resolver::resolve() this->checkUndefines(); this->checkDylibSymbolCollisions(); this->removeCoalescedAwayAtoms(); - this->fillInInternalState(); this->linkTimeOptimize(); + this->fillInInternalState(); + this->tweakWeakness(); + _symbolTable.checkDuplicateSymbols(); } diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 65e2006..c761990 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -57,10 +57,10 @@ namespace tool { class Resolver : public ld::File::AtomHandler { public: - Resolver(const Options& opts, const InputFiles& inputs, ld::Internal& state) + Resolver(const Options& opts, InputFiles& inputs, ld::Internal& state) : _options(opts), _inputFiles(inputs), _internal(state), _symbolTable(opts, state.indirectBindingTable), - _haveLLVMObjs(false), _addToFinalSection(false), + _haveLLVMObjs(false), _completedInitialObjectFiles(false) {} @@ -94,8 +94,9 @@ class Resolver : public ld::File::AtomHandler void markLive(const ld::Atom& atom, WhyLiveBackChain* previous); bool isDtraceProbe(ld::Fixup::Kind kind); void liveUndefines(std::vector&); + void remainingUndefines(std::vector&); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); - + void tweakWeakness(); class CStringEquals { public: @@ -118,14 +119,13 @@ class Resolver : public ld::File::AtomHandler }; const Options& _options; - const InputFiles& _inputFiles; + InputFiles& _inputFiles; ld::Internal& _internal; std::vector _atoms; std::set _deadStripRoots; std::vector _atomsWithUnresolvedReferences; SymbolTable _symbolTable; bool _haveLLVMObjs; - bool _addToFinalSection; bool _completedInitialObjectFiles; }; diff --git a/ld64/src/ld/Snapshot.cpp b/ld64/src/ld/Snapshot.cpp new file mode 100644 index 0000000..27ce370 --- /dev/null +++ b/ld64/src/ld/Snapshot.cpp @@ -0,0 +1,538 @@ +// +// Snapshot.cpp +// ld64 +// +// Created by Josh Behnke on 8/25/11. +// Copyright (c) 2011 Apple Inc. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Snapshot.h" +#include "Options.h" + +#include "compile_stubs.h" + +//#define STORE_PID_IN_SNAPSHOT 1 + +// Well known snapshot file/directory names. These appear in the root of the snapshot. +// They are collected together here to make managing the namespace easier. +static const char *frameworksString = "frameworks"; // directory containing framework stubs (mach-o files) +static const char *dylibsString = "dylibs"; // directory containing dylib stubs (mach-o files) +static const char *archiveFilesString = "archive_files"; // directory containing .a files +static const char *origCommandLineString = "orig_command_line"; // text file containing the original command line +static const char *linkCommandString = "link_command"; // text file containing the snapshot equivalent command line +static const char *dataFilesString = "data_files"; // arbitrary data files referenced on the command line +static const char *objectsString = "objects"; // directory containing object files +static const char *frameworkStubsString = "framework_stubs"; // directory containing framework stub info (text files) +static const char *dylibStubsString = "dylib_stubs"; // directory containing dylib stub info (text files) +static const char *assertFileString = "assert_info"; // text file containing assertion failure logs +static const char *compileFileString = "compile_stubs"; // text file containing compile_stubs script + +Snapshot *Snapshot::globalSnapshot = NULL; + +Snapshot::Snapshot() : fRecordArgs(false), fRecordObjects(false), fRecordDylibSymbols(false), fRecordArchiveFiles(false), fRecordUmbrellaFiles(false), fRecordDataFiles(false), fFrameworkArgAdded(false), fSnapshotLocation(NULL), fSnapshotName(NULL), fRootDir(NULL), fFilelistFile(-1), fCopiedArchives(NULL) +{ + if (globalSnapshot != NULL) + throw "only one snapshot supported"; + globalSnapshot = this; +} + + +Snapshot::~Snapshot() +{ + // Lots of things leak under the assumption the linker is about to exit. +} + + +void Snapshot::setSnapshotPath(const char *path) +{ + if (fRootDir == NULL) { + fSnapshotLocation = strdup(path); + } +} + + +void Snapshot::setSnapshotMode(SnapshotMode mode) +{ + if (fRootDir == NULL) { + fRecordArgs = false; + fRecordObjects = false; + fRecordDylibSymbols = false; + fRecordArchiveFiles = false; + fRecordUmbrellaFiles = false; + fRecordDataFiles = false; + + switch (mode) { + case SNAPSHOT_DISABLED: + break; + case SNAPSHOT_DEBUG: + fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true; + break; + default: + break; + } + } +} + +void Snapshot::setSnapshotName(const char *path) +{ + if (fRootDir == NULL) { + const char *base = basename((char *)path); + time_t now = time(NULL); + struct tm t; + localtime_r(&now, &t); + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf)-1, "%s-%4.4d-%2.2d-%2.2d-%2.2d%2.2d%2.2d.ld-snapshot", base, t.tm_year+1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + fSnapshotName = strdup(buf); + } +} + + +// Construct a path string in the snapshot. +// subdir - an optional subdirectory name +// file - the file name +void Snapshot::buildPath(char *buf, const char *subdir, const char *file) +{ + if (fRootDir == NULL) + throw "snapshot not created"; + + strcpy(buf, fRootDir); + strcat(buf, "/"); + if (subdir) { + strcat(buf, subdir); + // implicitly create the subdirectory + mkdir(buf, S_IRUSR|S_IWUSR|S_IXUSR); + strcat(buf, "/"); + } + if (file != NULL) + strcat(buf, basename((char *)file)); +} + + +// Construct a unique path string in the snapshot. If a path collision is detected then uniquing +// is accomplished by appending a counter to the path until there is no preexisting file. +// subdir - an optional subdirectory name +// file - the file name +void Snapshot::buildUniquePath(char *buf, const char *subdir, const char *file) +{ + buildPath(buf, subdir, file); + struct stat st; + if (stat(buf, &st)==0) { + // make it unique + int counter=1; + char *number = strrchr(buf, 0); + number[0]='-'; + number++; + do { + sprintf(number, "%d", counter++); + } while (stat(buf, &st) == 0); + } +} + + +// Copy a file to the snapshot. +// sourcePath is the original file +// subdir is an optional subdirectory in the snapshot +// path is an optional out parameter containing the final uniqued path in the snapshot +// where the file was copied +void Snapshot::copyFileToSnapshot(const char *sourcePath, const char *subdir, char *path) +{ + const int copyBufSize=(1<<14); // 16kb buffer + static void *copyBuf = NULL; + if (copyBuf == NULL) + copyBuf = malloc(copyBufSize); + + char *file=basename((char *)sourcePath); + char buf[PATH_MAX]; + if (path == NULL) path = buf; + buildUniquePath(path, subdir, file); + int out_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + int in_fd = open(sourcePath, O_RDONLY); + int len; + if (out_fd != -1 && in_fd != -1) { + do { + len = read(in_fd, copyBuf, copyBufSize); + if (len > 0) write(out_fd, copyBuf, len); + } while (len == copyBufSize); + } + close(in_fd); + close(out_fd); +} + + +// Create the snapshot root directory. +void Snapshot::createSnapshot() +{ + if (fRootDir == NULL) { + // provide default name and location + if (fSnapshotLocation == NULL) + fSnapshotLocation = "/tmp"; + if (fSnapshotName == NULL) { + setSnapshotName("ld_snapshot"); + } + + char buf[PATH_MAX]; + fRootDir = (char *)fSnapshotLocation; + buildUniquePath(buf, NULL, fSnapshotName); + fRootDir = strdup(buf); + if (mkdir(fRootDir, S_IRUSR|S_IWUSR|S_IXUSR)!=0) { + warning("unable to create link snapshot directory: %s", fRootDir); + fRootDir = NULL; + setSnapshotMode(SNAPSHOT_DISABLED); // don't try to write anything if we can't create snapshot dir + } + + buildPath(buf, NULL, compileFileString); + int compileScript = open(buf, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR); + write(compileScript, compile_stubs, strlen(compile_stubs)); + close(compileScript); + + SnapshotLog::iterator it; + for (it = fLog.begin(); it != fLog.end(); it++) { + void (^logItem)(void) = *it; + logItem(); + Block_release(logItem); + } + fLog.erase(fLog.begin(), fLog.end()); + + if (fRecordArgs) { + writeCommandLine(fRawArgs, origCommandLineString, true); + writeCommandLine(fArgs); + } + +#if STORE_PID_IN_SNAPSHOT + char path[PATH_MAX]; + buildUniquePath(path, NULL, pidString); + int pidfile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + char pid_buf[32]; + sprintf(pid_buf, "%lu\n", (long unsigned)getpid()); + write(pidfile, pid_buf, strlen(pid_buf)); + write(pidfile, "\n", 1); + close(pidfile); +#endif + + } +} + + +// Write the current command line vector to filename. +void Snapshot::writeCommandLine(StringVector &args, const char *filename, bool includeCWD) +{ + if (!isLazy() && fRecordArgs) { + // Figure out the file name and open it. + if (filename == NULL) + filename = linkCommandString; + char path[PATH_MAX]; + buildPath(path, NULL, filename); + int argsFile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR); + FILE *argsStream = fdopen(argsFile, "w"); + + if (includeCWD) + fprintf(argsStream, "cd %s\n", getcwd(path, sizeof(path))); + + // iterate to write args, quoting as needed + StringVector::iterator it; + for (it = args.begin(); it != args.end(); it++) { + const char *arg = *it; + bool needQuotes = false; + for (const char *c = arg; *c != 0 && !needQuotes; c++) { + if (isspace(*c)) + needQuotes = true; + } + if (it != args.begin()) fprintf(argsStream, " "); + if (needQuotes) fprintf(argsStream, "\""); + fprintf(argsStream, "%s", arg); + if (needQuotes) fprintf(argsStream, "\""); + } + fprintf(argsStream, "\n"); + fclose(argsStream); + } +} + + +// Store the command line args in the snapshot. +void Snapshot::recordRawArgs(int argc, const char *argv[]) +{ + // first store the original command line as-is + for (int i=0; iaddSnapshotLinkArg(argIndex, argCount, fileArg); })); + } else { + char buf[PATH_MAX]; + const char *subdir = dataFilesString; + for (int i=0, arg=argIndex; irecordArch(arch); })); + } else { + char path_buf[PATH_MAX]; + buildUniquePath(path_buf, NULL, "arch"); + int fd=open(path_buf, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + write(fd, arch, strlen(arch)); + close(fd); + } + } +} + +// Record an object file in the snapshot. +// path - the object file's path +// fileContent - a pointer to the object file content +// fileLength - the buffer size of fileContent +void Snapshot::recordObjectFile(const char *path) +{ + if (fRootDir == NULL) { + fLog.push_back(Block_copy(^{ this->recordObjectFile(path); })); + } else { + if (fRecordObjects) { + char path_buf[PATH_MAX]; + copyFileToSnapshot(path, objectsString, path_buf); + + // lazily open the filelist file + if (fFilelistFile == -1) { + char filelist_path[PATH_MAX]; + buildUniquePath(filelist_path, objectsString, "filelist"); + fFilelistFile = open(filelist_path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + fArgs.push_back("-filelist"); + fArgs.push_back(strdup(snapshotRelativePath(filelist_path))); + writeCommandLine(fArgs); + } + + // record the snapshot path in the filelist + const char *relative_path = snapshotRelativePath(path_buf); + write(fFilelistFile, relative_path, strlen(relative_path)); + write(fFilelistFile, "\n", 1); + } + } +} + +void Snapshot::addFrameworkArg(const char *framework) +{ + bool found=false; + for (unsigned i=0; irecordDylibSymbol(dylibFile, name); })); + } else { + if (fRecordDylibSymbols) { + // find the dylib in the table + DylibMap::iterator it; + const char *dylibPath = dylibFile->path(); + it = fDylibSymbols.find(dylibPath); + bool isFramework = (strstr(dylibPath, "framework") != NULL); + int dylibFd; + if (it == fDylibSymbols.end()) { + // Didn't find a file descriptor for this dylib. Create one and add it to the dylib map. + char path_buf[PATH_MAX]; + buildUniquePath(path_buf, isFramework ? frameworkStubsString : dylibStubsString, dylibPath); + dylibFd = open(path_buf, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR); + fDylibSymbols.insert(std::pair(dylibPath, dylibFd)); + char *base_name = strdup(basename(path_buf)); + if (isFramework) { + addFrameworkArg(base_name); + } else { + addDylibArg(base_name); + } + writeCommandLine(fArgs); + } else { + dylibFd = it->second; + } + // Record the symbol. + + bool isIdentifier = (name[0] == '_'); + for (const char *c = name; *c != 0 && isIdentifier; c++) + if (!isalnum(*c) && *c!='_') + isIdentifier = false; + const char *prefix = "void "; + const char *weakAttr = "__attribute__ ((weak)) "; + const char *suffix = "(void){}\n"; + if (isIdentifier) { + write(dylibFd, prefix, strlen(prefix)); + if (dylibFile->hasWeakExternals() && dylibFile->hasWeakDefinition(name)) + write(dylibFd, weakAttr, strlen(weakAttr)); + if (*name == '_') name++; + write(dylibFd, name, strlen(name)); + write(dylibFd, suffix, strlen(suffix)); + } else { + static int symbolCounter = 0; + char buf[64+strlen(name)]; + sprintf(buf, "void s_%5.5d(void) __asm(\"%s\");\nvoid s_%5.5d(){}\n", symbolCounter, name, symbolCounter); + write(dylibFd, buf, strlen(buf)); + symbolCounter++; + } + } + } +} + + +// Record a .a archive in the snapshot. +void Snapshot::recordArchive(const char *archiveFile) +{ + if (fRootDir == NULL) { + const char *copy = strdup(archiveFile); + fLog.push_back(Block_copy(^{ this->recordArchive(archiveFile); ::free((void *)copy); })); + } else { + if (fRecordArchiveFiles) { + // lazily create a vector of .a files that have been added + if (fCopiedArchives == NULL) { + fCopiedArchives = new StringVector; + } + + // See if we have already added this .a + StringVector::iterator it; + bool found = false; + for (it = fCopiedArchives->begin(); it != fCopiedArchives->end() && !found; it++) { + if (strcmp(archiveFile, *it) == 0) + found = true; + } + + // If this is a new .a then copy it to the snapshot and add it to the snapshot link command. + if (!found) { + char path[PATH_MAX]; + fCopiedArchives->push_back(archiveFile); + copyFileToSnapshot(archiveFile, archiveFilesString, path); + fArgs.push_back(strdup(snapshotRelativePath(path))); + writeCommandLine(fArgs); + } + } + } +} + +void Snapshot::recordSubUmbrella(const char *frameworkPath) +{ + if (fRootDir == NULL) { + const char *copy = strdup(frameworkPath); + fLog.push_back(Block_copy(^{ this->recordSubUmbrella(copy); ::free((void *)copy); })); + } else { + if (fRecordUmbrellaFiles) { + const char *framework = basename((char *)frameworkPath); + char buf[PATH_MAX], wrapper[PATH_MAX]; + strcpy(wrapper, frameworksString); + buildPath(buf, wrapper, NULL); // ensure the frameworks directory exists + strcat(wrapper, "/"); + strcat(wrapper, framework); + strcat(wrapper, ".framework"); + copyFileToSnapshot(frameworkPath, wrapper); + addFrameworkArg(framework); + } + } +} + +void Snapshot::recordSubLibrary(const char *dylibPath) +{ + if (fRootDir == NULL) { + const char *copy = strdup(dylibPath); + fLog.push_back(Block_copy(^{ this->recordSubLibrary(copy); ::free((void *)copy); })); + } else { + if (fRecordUmbrellaFiles) { + copyFileToSnapshot(dylibPath, dylibsString); + addDylibArg(basename((char *)dylibPath)); + } + } +} + +void Snapshot::recordAssertionMessage(const char *fmt, ...) +{ + char *msg; + va_list args; + va_start(args, fmt); + vasprintf(&msg, fmt, args); + va_end(args); + if (msg != NULL) { + if (fRootDir == NULL) { + fLog.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg); free(msg); })); + } else { + char path[PATH_MAX]; + buildPath(path, NULL, assertFileString); + int log = open(path, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR); + write(log, msg, strlen(msg)); + close(log); + free(msg); + } + } +} diff --git a/ld64/src/ld/Snapshot.h b/ld64/src/ld/Snapshot.h new file mode 100644 index 0000000..8cfd17e --- /dev/null +++ b/ld64/src/ld/Snapshot.h @@ -0,0 +1,153 @@ +// +// Snapshot.h +// ld64 +// +// Created by Josh Behnke on 8/25/11. +// Copyright (c) 2011 Apple Inc. All rights reserved. +// + +#ifndef ld64_Snapshot_h +#define ld64_Snapshot_h +#include +#include +#include +#include + +#include "ld.hpp" + +class Options; +class SnapshotLogItem; + +class Snapshot { + +public: + static Snapshot *globalSnapshot; + + typedef enum { + SNAPSHOT_DISABLED, // nothing is recorded + SNAPSHOT_DEBUG, // records: .o, .dylib, .framework, .a, and other data files + } SnapshotMode; + + Snapshot(); + ~Snapshot(); + + // Control the data captured in the snapshot + void setSnapshotMode(SnapshotMode mode); + + // Use the basename of path to construct the snapshot name. + // Must be called prior to createSnapshot(). + void setSnapshotName(const char *path); + + // Set the directory in which the snapshot will be created. + // Must be called prior to createSnapshot(). + void setSnapshotPath(const char *path); + + // Stores the linker command line in the snapshot + void recordRawArgs(int argc, const char *argv[]); + + // Adds one or more args to the snapshot link command. + // argIndex is the index in the original raw args vector to start adding args + // argCount is the count of args to copy from the raw args vector + // fileArg is the index relative to argIndex of a file arg. The file is copied into the + // snapshot and the path is fixed up in the snapshot link command. (skipped if fileArg==-1) + // recordRawArgs() must be called prior to the first call to addSnapshotLinkArg() + void addSnapshotLinkArg(int argIndex, int argCount=1, int fileArg=-1); + + // record the -arch string + void recordArch(const char *arch); + + // Stores an object file in the snapshot, using a unique name in an "objects" subdir. + void recordObjectFile(const char *path); + + // Records symbol names used in dylibs. Does not store anything in the snapshot. + void recordDylibSymbol(ld::dylib::File* dylibFile, const char *name); + + // Stores an archive (.a) file in the snapshot. + void recordArchive(const char *archiveFile); + + // Copies the framework binary into the snapshot frameworks directory. + void recordSubUmbrella(const char *frameworkPath); + + // Copies the library binary into the snapshot dylibs directory. + void recordSubLibrary(const char *dylibPath); + + // Records arbitrary text messages into a log file in the snapshot. + // Used by the assertion failure machienery. + void recordAssertionMessage(const char *fmt, ...); + + // Create the snapshot. + // Until this is called the snapshot operates lazily, storing minimal data in memory. + // When this is called the snapshot is created and any previously recorded data is + // immediately copied. Any subsequent additions to the snapshot are copied immediately. + void createSnapshot(); + + // Returns the snapshot root directory. + const char *rootDir() { return fRootDir; } + +private: + + friend class SnapshotArchiveFileLog; + + typedef std::vector SnapshotLog; + + struct strcompclass { + bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } + }; + typedef std::vector StringVector; + typedef std::map DylibMap; + typedef std::map PathMap; + + + // Write the current contents of the args vector to a file in the snapshot. + // If filename is NULL then "link_command" is used. + // This is used to write both the original and the "cooked" versions of the link command + void writeCommandLine(StringVector &args, const char *filename=NULL, bool includeCWD=false); + + // Construct a path in the snapshot. + // buf is a sring buffer in which the path is constructed + // subdir is an optional subdirectory, and file is a file name + // Constructs the path // in buf + void buildPath(char *buf, const char *subdir, const char *file); + + // Similar to buildPath(), except this ensures the returned path + // does not reference an existing file in the snapshot. + // Performs uniquing by appending a count suffex to the path (ie .../file-XX) + void buildUniquePath(char *buf, const char *subdir, const char *file); + + // Copies an arbitrary file to the snapshot. Subdir specifies an optional subdirectory name. + // Uses buildUniquePath to construct a unique path. If the result path is needed by the caller + // then a path buffer can be supplied in buf. Otherwise an internal buffer is used. + void copyFileToSnapshot(const char *sourcePath, const char *subdir, char *buf=NULL); + + // Convert a full path to snapshot relative by constructing an interior pointer at the right offset. + const char *snapshotRelativePath(const char *path) { return path+strlen(fRootDir)+1; } + + // returns true if the snapshot has not been created (by createSnapshot()) yet + bool isLazy() { return fRootDir == NULL; } + + void addFrameworkArg(const char *framework); + void addDylibArg(const char *dylib); + + SnapshotLog fLog; // log of events that recorded data in a snapshot prior to createSnapshot() + bool fRecordArgs; // record command line + bool fRecordObjects; // record .o files + bool fRecordDylibSymbols; // record referenced dylib/framework symbols + bool fRecordArchiveFiles; // record .a files + bool fRecordUmbrellaFiles; // record re-exported sub frameworks/dylibs + bool fRecordDataFiles; // record other data files + bool fFrameworkArgAdded; + + const char *fSnapshotLocation; // parent directory of frootDir + const char *fSnapshotName; // a string to use in constructing the snapshot name + char *fRootDir; // root directory of the snapshot + int fFilelistFile; // file descriptor to the open text file used for the -filelist + + StringVector fRawArgs; // stores the raw command line args + StringVector fArgs; // stores the "cooked" command line args + PathMap fPathMap; // mapping of original paths->snapshot paths for copied files + + DylibMap fDylibSymbols; // map of dylib names to string vector containing referenced symbol names + StringVector *fCopiedArchives; // vector of .a files that have been copied to the snapshot +}; + +#endif diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index 66ff358..406556a 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -57,14 +59,12 @@ namespace tool { // HACK, I can't find a way to pass values in the compare classes (e.g. ContentFuncs) // so use global variable to pass info. static ld::IndirectBindingTable* _s_indirectBindingTable = NULL; -bool SymbolTable::_s_doDemangle = false; SymbolTable::SymbolTable(const Options& opts, std::vector& ibt) - : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) + : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) { _s_indirectBindingTable = this; - _s_doDemangle = _options.demangleSymbols(); } @@ -119,251 +119,295 @@ bool SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* left, const ld } +void SymbolTable::addDuplicateSymbol(const char *name, const ld::Atom *atom) +{ + // Look up or create the file list for name. + DuplicateSymbols::iterator symbolsIterator = _duplicateSymbols.find(name); + DuplicatedSymbolAtomList *atoms = NULL; + if (symbolsIterator != _duplicateSymbols.end()) { + atoms = symbolsIterator->second; + } else { + atoms = new std::vector; + _duplicateSymbols.insert(std::pair(name, atoms)); + } + + // check if file is already in the list, add it if not + bool found = false; + for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !found && it != atoms->end(); it++) + if (strcmp((*it)->file()->path(), atom->file()->path()) == 0) + found = true; + if (!found) + atoms->push_back(atom); +} -bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) +void SymbolTable::checkDuplicateSymbols() const { - bool useNew = true; - bool checkVisibilityMismatch = false; - assert(newAtom.name() != NULL); - const char* name = newAtom.name(); - IndirectBindingSlot slot = this->findSlotForName(name); - const ld::Atom* existingAtom = _indirectBindingTable[slot]; - //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); - if ( existingAtom != NULL ) { - assert(&newAtom != existingAtom); - switch ( existingAtom->definition() ) { - case ld::Atom::definitionRegular: - switch ( newAtom.definition() ) { - case ld::Atom::definitionRegular: - if ( existingAtom->combine() == ld::Atom::combineByName ) { - if ( newAtom.combine() == ld::Atom::combineByName ) { - // always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom - const bool existingIsLTO = (existingAtom->contentType() == ld::Atom::typeLTOtemporary); - const bool newIsLTO = (newAtom.contentType() == ld::Atom::typeLTOtemporary); - if ( existingIsLTO != newIsLTO ) { - useNew = existingIsLTO; - } - else { - // both weak, prefer non-auto-hide one - if ( newAtom.autoHide() != existingAtom->autoHide() ) { - // support auto hidden weak symbols: .weak_def_can_be_hidden - useNew = existingAtom->autoHide(); - // don't check for visibility mismatch - } - else if ( newAtom.autoHide() && existingAtom->autoHide() ) { - // both have auto-hide, so use one with greater alignment - useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); - } - else { - // neither auto-hide, check visibility - if ( newAtom.scope() != existingAtom->scope() ) { - // use more visible weak def symbol - useNew = (newAtom.scope() == ld::Atom::scopeGlobal); - } - else { - // both have same visibility, use one with greater alignment - useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); - } - } - } - } - else { - // existing weak, new is not-weak - useNew = true; - } + bool foundDuplicate = false; + for (DuplicateSymbols::const_iterator symbolIt = _duplicateSymbols.begin(); symbolIt != _duplicateSymbols.end(); symbolIt++) { + DuplicatedSymbolAtomList *atoms = symbolIt->second; + bool reportDuplicate; + if (_options.deadCodeStrip()) { + // search for a live atom + reportDuplicate = false; + for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !reportDuplicate && it != atoms->end(); it++) { + if ((*it)->live()) + reportDuplicate = true; + } + } else { + reportDuplicate = true; + } + if (reportDuplicate) { + foundDuplicate = true; + fprintf(stderr, "duplicate symbol %s in:\n", symbolIt->first); + for (DuplicatedSymbolAtomList::iterator atomIt = atoms->begin(); atomIt != atoms->end(); atomIt++) { + fprintf(stderr, " %s\n", (*atomIt)->file()->path()); + } + } + } + if (foundDuplicate) + throwf("%d duplicate symbol%s", (int)_duplicateSymbols.size(), _duplicateSymbols.size()==1?"":"s"); +} + +// AtomPicker encapsulates the logic for picking which atom to use when adding an atom by name results in a collision +class NameCollisionResolution { +public: + NameCollisionResolution(const ld::Atom& a, const ld::Atom& b, bool ignoreDuplicates, const Options& options) : _atomA(a), _atomB(b), _options(options), _reportDuplicate(false), _ignoreDuplicates(ignoreDuplicates) { + pickAtom(); + } + + // Returns which atom to use + const ld::Atom& chosen() { return *_chosen; } + bool choseAtom(const ld::Atom& atom) { return _chosen == &atom; } + + // Returns true if the two atoms should be reported as a duplicate symbol + bool reportDuplicate() { return _reportDuplicate; } + +private: + const ld::Atom& _atomA; + const ld::Atom& _atomB; + const Options& _options; + const ld::Atom* _chosen; + bool _reportDuplicate; + bool _ignoreDuplicates; + + void pickAtom(const ld::Atom& atom) { _chosen = &atom; } // primitive to set which atom is picked + void pickAtomA() { pickAtom(_atomA); } // primitive to pick atom A + void pickAtomB() { pickAtom(_atomB); } // primitive to pick atom B + + // use atom A if pickA, otherwise use atom B + void pickAOrB(bool pickA) { if (pickA) pickAtomA(); else pickAtomB(); } + + void pickHigherOrdinal() { + pickAOrB(_atomA.file()->ordinal() < _atomB.file()->ordinal()); + } + + void pickLowerOrdinal() { + pickAOrB(_atomA.file()->ordinal() > _atomB.file()->ordinal()); + } + + void pickLargerSize() { + if (_atomA.size() == _atomB.size()) + pickLowerOrdinal(); + else + pickAOrB(_atomA.size() > _atomB.size()); + } + + void pickGreaterAlignment() { + pickAOrB(_atomA.alignment().trailingZeros() > _atomB.alignment().trailingZeros()); + } + + void pickBetweenRegularAtoms() { + if ( _atomA.combine() == ld::Atom::combineByName ) { + if ( _atomB.combine() == ld::Atom::combineByName ) { + // always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom + const bool aIsLTO = (_atomA.contentType() == ld::Atom::typeLTOtemporary); + const bool bIsLTO = (_atomB.contentType() == ld::Atom::typeLTOtemporary); + // always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom + if ( aIsLTO != bIsLTO ) { + pickAOrB(!aIsLTO); + } + else { + // both weak, prefer non-auto-hide one + if ( _atomA.autoHide() != _atomB.autoHide() ) { + // support auto hidden weak symbols: .weak_def_can_be_hidden + pickAOrB(!_atomA.autoHide()); + } + else if ( _atomA.autoHide() && _atomB.autoHide() ) { + // both have auto-hide, so use one with greater alignment + pickGreaterAlignment(); + } + else { + // neither auto-hide, check visibility + if ( _atomA.scope() != _atomB.scope() ) { + // use more visible weak def symbol + pickAOrB(_atomA.scope() == ld::Atom::scopeGlobal); } else { - if ( newAtom.combine() == ld::Atom::combineByName ) { - // existing not-weak, new is weak - useNew = false; - } - else { - // existing not-weak, new is not-weak - if ( newAtom.section().type() == ld::Section::typeMachHeader ) { - warning("ignoring override of built-in symbol %s from %s", newAtom.name(), existingAtom->file()->path()); - useNew = true; - } - else if ( existingAtom->section().type() == ld::Section::typeMachHeader ) { - warning("ignoring override of built-in symbol %s from %s", newAtom.name(), newAtom.file()->path()); - useNew = false; - } - else { - if ( ignoreDuplicates ) { - useNew = false; - static bool fullWarning = false; - if ( ! fullWarning ) { - warning("-dead_strip with lazy loaded static (library) archives " - "has resulted in a duplicate symbol. You can change your " - "source code to rename symbols to avoid the collision. " - "This will be an error in a future linker."); - fullWarning = true; - } - warning("duplicate symbol %s originally in %s now lazily loaded from %s", - SymbolTable::demangle(name), existingAtom->file()->path(), newAtom.file()->path()); - } - else { - throwf("duplicate symbol %s in %s and %s", - SymbolTable::demangle(name), newAtom.file()->path(), existingAtom->file()->path()); - } - } - } + // both have same visibility, use one with greater alignment + pickGreaterAlignment(); } + } + } + } + else { + pickAtomB(); // pick not-weak + + } + } + else { + if ( _atomB.combine() == ld::Atom::combineByName ) { + pickAtomA(); // pick not-weak + + } + else { + // both are not-weak + if ( _atomA.section().type() == ld::Section::typeMachHeader ) { + pickAtomA(); + } + else if ( _atomB.section().type() == ld::Section::typeMachHeader ) { + pickAtomB(); + } + else { + if ( _ignoreDuplicates ) { + pickLowerOrdinal(); + } + else { + _reportDuplicate = true; + } + } + } + } + } + + void pickCommonsMode(const ld::Atom& dylib, const ld::Atom& proxy) { + assert(dylib.definition() == ld::Atom::definitionTentative); + assert(proxy.definition() == ld::Atom::definitionProxy); + switch ( _options.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( _options.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + proxy.name(), proxy.file()->path(), dylib.file()->path()); + pickAtom(dylib); + break; + case Options::kCommonsOverriddenByDylibs: + if ( _options.warnCommons() ) + warning("replacing common symbol %s from %s with true definition from dylib %s", + proxy.name(), proxy.file()->path(), dylib.file()->path()); + pickAtom(proxy); + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + proxy.name(), proxy.file()->path(), dylib.file()->path()); + } + } + + void pickProxyAtom() { + // both atoms are definitionProxy + // ld should keep looking when it finds a weak definition in a dylib + if ( _atomA.combine() == ld::Atom::combineByName ) { + pickAtomB(); + } else if ( _atomB.combine() == ld::Atom::combineByName ) { + pickAtomA(); + } else { + throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.file()->path(), _atomB.file()->path()); + } + } + + void pickAtom() { + // First, discriminate by definition + switch (_atomA.definition()) { + case ld::Atom::definitionRegular: + switch (_atomB.definition()) { + case ld::Atom::definitionRegular: + pickBetweenRegularAtoms(); break; case ld::Atom::definitionTentative: - // ignore new tentative atom, because we already have a regular one - useNew = false; - checkVisibilityMismatch = true; - if ( newAtom.size() > existingAtom->size() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "is smaller than the real definition of size %llu from %s", - newAtom.name(), newAtom.size(), newAtom.file()->path(), - existingAtom->size(), existingAtom->file()->path()); - } + pickAtomA(); break; case ld::Atom::definitionAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + _reportDuplicate = true; + pickHigherOrdinal(); + break; case ld::Atom::definitionProxy: - // ignore external atom, because we already have a one - useNew = false; + pickAtomA(); break; } break; case ld::Atom::definitionTentative: - switch ( newAtom.definition() ) { + switch (_atomB.definition()) { case ld::Atom::definitionRegular: - // replace existing tentative atom with regular one - if ( newAtom.section().type() == ld::Section::typeMachHeader ) { - // silently replace tentative __dso_handle with real linker created symbol - useNew = true; - } - else if ( existingAtom->section().type() == ld::Section::typeMachHeader ) { - // silently replace tentative __dso_handle with real linker created symbol - useNew = false; - } - else { - checkVisibilityMismatch = true; - if ( newAtom.size() < existingAtom->size() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "being replaced by a real definition of size %llu from %s", - newAtom.name(), existingAtom->size(), existingAtom->file()->path(), - newAtom.size(), newAtom.file()->path()); - } - if ( newAtom.section().type() == ld::Section::typeCode ) { - warning("for symbol %s tentative (data) defintion from %s is " - "being replaced by code from %s", newAtom.name(), existingAtom->file()->path(), - newAtom.file()->path()); - } - } + pickAtomB(); break; case ld::Atom::definitionTentative: - // new and existing are both tentative definitions, use largest - checkVisibilityMismatch = true; - if ( newAtom.size() < existingAtom->size() ) { - useNew = false; - } - else { - if ( newAtom.alignment().trailingZeros() < existingAtom->alignment().trailingZeros() ) - warning("alignment lost in merging tentative definition %s", newAtom.name()); - } + pickLargerSize(); break; case ld::Atom::definitionAbsolute: - // replace tentative with absolute - useNew = true; + pickHigherOrdinal(); break; case ld::Atom::definitionProxy: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( _options.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( _options.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); - useNew = false; - break; - case Options::kCommonsOverriddenByDylibs: - if ( _options.warnCommons() ) - warning("replacing common symbol %s from %s with true definition from dylib %s", - existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); - } + pickCommonsMode(_atomA, _atomB); break; } break; case ld::Atom::definitionAbsolute: - switch ( newAtom.definition() ) { + switch (_atomB.definition()) { case ld::Atom::definitionRegular: - throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + _reportDuplicate = true; + pickHigherOrdinal(); + break; case ld::Atom::definitionTentative: - // ignore new tentative atom, because we already have a regular one - useNew = false; + pickAtomA(); break; case ld::Atom::definitionAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + _reportDuplicate = true; + pickHigherOrdinal(); + break; case ld::Atom::definitionProxy: - // ignore external atom, because we already have a one - useNew = false; + pickAtomA(); break; } break; case ld::Atom::definitionProxy: - switch ( newAtom.definition() ) { + switch (_atomB.definition()) { case ld::Atom::definitionRegular: - // replace external atom with regular one - useNew = true; + pickAtomB(); break; case ld::Atom::definitionTentative: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( _options.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( _options.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.name(), newAtom.file()->path(), existingAtom->file()->path()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( _options.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.name(), existingAtom->file()->path(), newAtom.file()->path()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.name(), newAtom.file()->path(), existingAtom->file()->path()); - } + pickCommonsMode(_atomB, _atomA); break; case ld::Atom::definitionAbsolute: - // replace external atom with absolute one - useNew = true; + pickAtomB(); break; case ld::Atom::definitionProxy: - // ld should keep looking when it finds a weak definition in a dylib - if ( newAtom.combine() == ld::Atom::combineByName ) { - useNew = false; - } - else { - if ( existingAtom->combine() == ld::Atom::combineByName ) - useNew = true; - else - throwf("symbol %s exported from both %s and %s\n", name, newAtom.file()->path(), existingAtom->file()->path()); - } + pickProxyAtom(); break; } break; - } + } } - if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.scope() != existingAtom->scope()) ) { - warning("%s has different visibility (%s) in %s and (%s) in %s", - SymbolTable::demangle(newAtom.name()), (newAtom.scope() == 1 ? "hidden" : "default"), newAtom.file()->path(), (existingAtom->scope() == 1 ? "hidden" : "default"), existingAtom->file()->path()); +}; + +bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) +{ + bool useNew = true; + assert(newAtom.name() != NULL); + const char* name = newAtom.name(); + IndirectBindingSlot slot = this->findSlotForName(name); + const ld::Atom* existingAtom = _indirectBindingTable[slot]; + //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + assert(&newAtom != existingAtom); + NameCollisionResolution picker(newAtom, *existingAtom, ignoreDuplicates, _options); + if (picker.reportDuplicate()) { + addDuplicateSymbol(name, existingAtom); + addDuplicateSymbol(name, &newAtom); + } + useNew = picker.choseAtom(newAtom); } if ( useNew ) { _indirectBindingTable[slot] = &newAtom; if ( existingAtom != NULL ) { markCoalescedAway(existingAtom); -// if ( fOwner.fInitialLoadsDone ) { -// //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->name(), &newAtom); -// fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom); -// } } if ( newAtom.scope() == ld::Atom::scopeGlobal ) { if ( newAtom.definition() == ld::Atom::definitionTentative ) { @@ -474,6 +518,16 @@ void SymbolTable::markCoalescedAway(const ld::Atom* atom) } + +struct StrcmpSorter { + bool operator() (const char* i,const char* j) { + if (i==NULL) + return true; + if (j==NULL) + return false; + return strcmp(i, j)<0;} +}; + void SymbolTable::undefines(std::vector& undefs) { // return all names in _byNameTable that have no associated atom @@ -483,7 +537,8 @@ void SymbolTable::undefines(std::vector& undefs) undefs.push_back(it->first); } // sort so that undefines are in a stable order (not dependent on hashing functions) - std::sort(undefs.begin(), undefs.end()); + struct StrcmpSorter strcmpSorter; + std::sort(undefs.begin(), undefs.end(), strcmpSorter); } @@ -684,35 +739,6 @@ const ld::Atom* SymbolTable::indirectAtom(IndirectBindingSlot slot) const return _indirectBindingTable[slot]; } -extern "C" char* __cxa_demangle (const char* mangled_name, - char* buf, - size_t* n, - int* status); - -const char* SymbolTable::demangle(const char* sym) -{ - // only try to demangle symbols if -demangle on command line - if ( !_s_doDemangle ) - return sym; - - // only try to demangle symbols that look like C++ symbols - if ( strncmp(sym, "__Z", 3) != 0 ) - return sym; - - static size_t size = 1024; - static char* buff = (char*)malloc(size); - int status; - - char* result = __cxa_demangle(&sym[1], buff, &size, &status); - if ( result != NULL ) { - // if demangling succesful, keep buffer for next demangle - buff = result; - return buff; - } - return sym; -} - - void SymbolTable::printStatistics() { // fprintf(stderr, "cstring table size: %lu, bucket count: %lu, hash func called %u times\n", diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h index f352a51..451d064 100644 --- a/ld64/src/ld/SymbolTable.h +++ b/ld64/src/ld/SymbolTable.h @@ -93,6 +93,9 @@ class SymbolTable : public ld::IndirectBindingTable typedef std::map SlotToName; typedef __gnu_cxx::hash_map, CStringEquals> NameToMap; + + typedef std::vector DuplicatedSymbolAtomList; + typedef std::map DuplicateSymbols; public: @@ -126,17 +129,24 @@ class SymbolTable : public ld::IndirectBindingTable byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } byNameIterator end() { return byNameIterator(_byNameTable.end(),_indirectBindingTable); } void printStatistics(); - static const char* demangle(const char* sym); // from ld::IndirectBindingTable virtual const char* indirectName(IndirectBindingSlot slot) const; virtual const ld::Atom* indirectAtom(IndirectBindingSlot slot) const; + + // Prints the duplicated symbols to stderr and throws. Only valid to call if hasDuplicateSymbols() returns true. + void checkDuplicateSymbols() const; + private: bool addByName(const ld::Atom& atom, bool ignoreDuplicates); bool addByContent(const ld::Atom& atom); bool addByReferences(const ld::Atom& atom); void markCoalescedAway(const ld::Atom* atom); + + // Tracks duplicated symbols. Each call adds file to the list of files defining symbol. + // The file list is uniqued per symbol, so calling multiple times for the same symbol/file pair is permitted. + void addDuplicateSymbol(const char *symbol, const ld::Atom* atom); const Options& _options; NameToSlot _byNameTable; @@ -154,7 +164,7 @@ class SymbolTable : public ld::IndirectBindingTable std::vector& _indirectBindingTable; bool _hasExternalTentativeDefinitions; - static bool _s_doDemangle; + DuplicateSymbols _duplicateSymbols; }; diff --git a/ld64/src/ld/code-sign-blobs/blob.cpp b/ld64/src/ld/code-sign-blobs/blob.cpp new file mode 100644 index 0000000..5b02f32 --- /dev/null +++ b/ld64/src/ld/code-sign-blobs/blob.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// blob - generic extensible binary blob frame +// +#include "blob.h" + +namespace Security { + + +// +// Content access and validation calls +// +char *BlobCore::stringAt(Offset offset) +{ + char *s = at(offset); + if (offset < this->length() && memchr(s, 0, this->length() - offset)) + return s; + else + return NULL; +} + +const char *BlobCore::stringAt(Offset offset) const +{ + const char *s = at(offset); + if (offset < this->length() && memchr(s, 0, this->length() - offset)) + return s; + else + return NULL; +} + + +// +// Read a blob from a standard file stream. +// Reads in one pass, so it's suitable for transmission over pipes and networks. +// The blob is allocated with malloc(3). +// On error, sets errno and returns NULL; in which case the input stream may +// be partially consumed. +// +BlobCore *BlobCore::readBlob(int fd, size_t offset, uint32_t magic, size_t minSize, size_t maxSize) +{ + BlobCore header; + if (::pread(fd, &header, sizeof(header), offset) == sizeof(header)) + if (header.validateBlob(magic, minSize, maxSize)) + if (BlobCore *blob = (BlobCore *)malloc(header.length())) { + memcpy(blob, &header, sizeof(header)); + size_t remainder = header.length() - sizeof(header); + if (::pread(fd, blob+1, remainder, offset + sizeof(header)) == ssize_t(remainder)) + return blob; + free(blob); + errno = EINVAL; + } + return NULL; +} + +BlobCore *BlobCore::readBlob(int fd, uint32_t magic, size_t minSize, size_t maxSize) +{ + BlobCore header; + if (::read(fd, &header, sizeof(header)) == sizeof(header)) + if (header.validateBlob(magic, minSize, maxSize)) + if (BlobCore *blob = (BlobCore *)malloc(header.length())) { + memcpy(blob, &header, sizeof(header)); + size_t remainder = header.length() - sizeof(header); + if (::read(fd, blob+1, remainder) == ssize_t(remainder)) + return blob; + free(blob); + errno = EINVAL; + } + return NULL; +} + +BlobCore *BlobCore::readBlob(std::FILE *file, uint32_t magic, size_t minSize, size_t maxSize) +{ + BlobCore header; + if (::fread(&header, sizeof(header), 1, file) == 1) + if (header.validateBlob(magic, minSize, maxSize)) + if (BlobCore *blob = (BlobCore *)malloc(header.length())) { + memcpy(blob, &header, sizeof(header)); + if (::fread(blob+1, header.length() - sizeof(header), 1, file) == 1) + return blob; + free(blob); + errno = EINVAL; + } + return NULL; +} + + +// +// BlobWrappers +// +BlobWrapper *BlobWrapper::alloc(size_t length, Magic magic /* = _magic */) +{ + size_t wrapLength = length + sizeof(BlobCore); + BlobWrapper *w = (BlobWrapper *)malloc(wrapLength); + w->BlobCore::initialize(magic, wrapLength); + return w; +} + +BlobWrapper *BlobWrapper::alloc(const void *data, size_t length, Magic magic /* = _magic */) +{ + BlobWrapper *w = alloc(length, magic); + memcpy(w->data(), data, w->length()); + return w; +} + + +} // Security diff --git a/ld64/src/ld/code-sign-blobs/blob.h b/ld64/src/ld/code-sign-blobs/blob.h new file mode 100644 index 0000000..db18746 --- /dev/null +++ b/ld64/src/ld/code-sign-blobs/blob.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// blob - generic extensible binary blob frame +// +// To define a new type of binary blob: +// class MyBlob : public Blob { ... } +// Pick a unique magic number (32-bit). Blobs are understood to be a MyBlob +// header possibly followed by more data as a contiguous memory area. Length +// is overall (including the header), so a fixed-size blob would have length +// sizeof(MyBlob). Both length and magic are stored in NBO. +// +// You are highly encouraged to follow these rules: +// Store all integers in NBO, including offsets. +// Use internal offsets to "point to" dynamically-sized elements past the +// header (using the at(offset) method for access). +// Don't use pointers in your blob. +// If you follow those rules, your blobs will be fully relocatable, byte-order +// independent, and generally spreading happiness around your code. +// +#ifndef _H_BLOB +#define _H_BLOB + +#include "endian.h" +#include "memutils.h" +#include + +namespace Security { + +enum { + // CodeDirectory slot numbers, used to index the EmbeddedSignatureBlob (from codedirectory.h, internal) + cdRequirementsSlot = 2 // embedded signature: internal requirements +}; + +enum { + // Code Signing magic blob types (from ) + kSecCodeMagicRequirement = 0xfade0c00, /* single requirement */ + kSecCodeMagicRequirementSet = 0xfade0c01, /* requirement set */ + kSecCodeMagicEmbeddedSignature = 0xfade0cc0, /* single-architecture embedded signature */ + + kSecCodeMagicDRList = 0xfade0c05 +}; + +enum { + // from CSCommon.h + kSecDesignatedRequirementType = 3 /* designated requirement */ +}; + +// +// All blobs in memory have this form. +// You can have polymorphic memory blobs (C style) using different magics numbers. +// +class BlobCore { +public: + typedef uint32_t Offset; + typedef uint32_t Magic; + + Magic magic() const { return mMagic; } + size_t length() const { return mLength; } + + void initialize(Magic mag, size_t len = 0) + { mMagic = mag; mLength = len; } + + bool validateBlob(Magic magic, size_t minSize = 0, size_t maxSize = 0) const; + + template + T *at(Offset offset) + { return LowLevelMemoryUtilities::increment(this, offset); } + + template + const T *at(Offset offset) const + { return LowLevelMemoryUtilities::increment(this, offset); } + + template + bool contains(Offset1 offset, Offset2 size) const + { return offset >= 0 && size_t(offset) >= sizeof(BlobCore) && (size_t(offset) + size) <= this->length(); } + + template + bool contains(Base *ptr, Offset size) const + { return contains(LowLevelMemoryUtilities::difference(ptr, this), size); } + + char *stringAt(Offset offset); + const char *stringAt(Offset offset) const; + + void *data() { return this; } + const void *data() const { return this; } + void length(size_t size) { mLength = size; } + + template + bool is() const { return magic() == BlobType::typeMagic; } + + static BlobCore *readBlob(std::FILE *file) { return readBlob(file, 0, 0, 0); } + static BlobCore *readBlob(int fd) { return readBlob(fd, 0, 0, 0); } + +protected: + static BlobCore *readBlob(std::FILE *file, uint32_t magic, size_t minSize, size_t maxSize); // streaming + static BlobCore *readBlob(int fd, uint32_t magic, size_t minSize, size_t maxSize); // streaming + static BlobCore *readBlob(int fd, size_t offset, uint32_t magic, size_t minSize, size_t maxSize); // pread(2)@offset + +protected: + Endian mMagic; + Endian mLength; +}; + + +// basic validation helper +inline bool BlobCore::validateBlob(Magic mag, size_t minSize /* = 0 */, size_t maxSize /* = 0 */) const +{ + uint32_t len = this->mLength; + if (mag && (mag != this->mMagic)) { + errno = EINVAL; + return false; + } + if (minSize ? (len < minSize) : (len < sizeof(BlobCore))) { + errno = EINVAL; + return false; + } + if (maxSize && len > maxSize) { + errno = ENOMEM; + return false; + } + return true; +} + + +// +// Typed Blobs (BlobCores that know their real type and magic) +// +template +class Blob: public BlobCore { +public: + void initialize(size_t size = 0) { BlobCore::initialize(_magic, size); } + + static const Magic typeMagic = _magic; + + bool validateBlob() const + { return BlobCore::validateBlob(_magic, sizeof(BlobType)); } + + bool validateBlob(size_t extLength) const + { return validateBlob() && mLength == extLength; } + + static BlobType *specific(BlobCore *blob, bool unalloc = false) + { + if (BlobType *p = static_cast(blob)) { + if (p->validateBlob()) + return p; + if (unalloc) + ::free(p); + } + return NULL; + } + + static const BlobType *specific(const BlobCore *blob) + { + const BlobType *p = static_cast(blob); + if (p && p->validateBlob()) + return p; + return NULL; + } + + BlobType *clone() const + { assert(validateBlob()); return specific(this->BlobCore::clone()); } + + static BlobType *readBlob(int fd) + { return specific(BlobCore::readBlob(fd, _magic, sizeof(BlobType), 0), true); } + + static BlobType *readBlob(int fd, size_t offset, size_t maxSize = 0) + { return specific(BlobCore::readBlob(fd, offset, _magic, sizeof(BlobType), maxSize), true); } + + static BlobType *readBlob(std::FILE *file) + { return specific(BlobCore::readBlob(file, _magic, sizeof(BlobType), 0), true); } +}; + + +// +// A generic blob wrapped around arbitrary (flat) binary data. +// This can be used to "regularize" plain binary data, so it can be handled +// as a genuine Blob (e.g. for insertion into a SuperBlob). +// +class BlobWrapper : public Blob { +public: + static BlobWrapper *alloc(size_t length, Magic magic = BlobWrapper::typeMagic); + static BlobWrapper *alloc(const void *data, size_t length, Magic magic = BlobWrapper::typeMagic); + + unsigned char dataArea[0]; + + // override data/length to point to the payload (only) + void *data() { return dataArea; } + const void *data() const { return dataArea; } + size_t length() const { return BlobCore::length() - sizeof(BlobCore); } +}; + + +} // Security + +#endif //_H_BLOB diff --git a/ld64/src/ld/code-sign-blobs/endian.h b/ld64/src/ld/code-sign-blobs/endian.h new file mode 100644 index 0000000..f312e43 --- /dev/null +++ b/ld64/src/ld/code-sign-blobs/endian.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2002-2004 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +/* + * cssm utilities + */ +#ifndef _H_ENDIAN +#define _H_ENDIAN + +#include +#include +//#include +#include "memutils.h" + +namespace Security { + + +// +// Encode/decode operations by type, overloaded. +// You can use these functions directly, but consider using +// the higher-level constructs below instead. +// +#ifdef __LP64__ +static inline unsigned long h2n(unsigned long v) { return OSSwapHostToBigInt64(v); } +static inline unsigned long n2h(unsigned long v) { return OSSwapBigToHostInt64(v); } +static inline unsigned long flip(unsigned long v) { return OSSwapInt64(v); } +static inline signed long h2n(signed long v) { return OSSwapHostToBigInt64(v); } +static inline signed long n2h(signed long v) { return OSSwapBigToHostInt64(v); } +static inline signed long flip(signed long v) { return OSSwapInt64(v); } +#else +static inline unsigned long h2n(unsigned long v) { return htonl(v); } +static inline unsigned long n2h(unsigned long v) { return ntohl(v); } +static inline unsigned long flip(unsigned long v) { return OSSwapInt32(v); } +static inline signed long h2n(signed long v) { return htonl(v); } +static inline signed long n2h(signed long v) { return ntohl(v); } +static inline signed long flip(signed long v) { return OSSwapInt32(v); } +#endif + +static inline unsigned long long h2n(unsigned long long v) { return OSSwapHostToBigInt64(v); } +static inline unsigned long long n2h(unsigned long long v) { return OSSwapBigToHostInt64(v); } +static inline unsigned long long flip(unsigned long long v) { return OSSwapInt64(v); } +static inline long long h2n(long long v) { return OSSwapHostToBigInt64(v); } +static inline long long n2h(long long v) { return OSSwapBigToHostInt64(v); } +static inline long long flip(long long v) { return OSSwapInt64(v); } + +static inline unsigned int h2n(unsigned int v) { return htonl(v); } +static inline unsigned int n2h(unsigned int v) { return ntohl(v); } +static inline unsigned int flip(unsigned int v) { return OSSwapInt32(v); } +static inline signed int h2n(int v) { return htonl(v); } +static inline signed int n2h(int v) { return ntohl(v); } +static inline signed int flip(int v) { return OSSwapInt32(v); } + +static inline unsigned short h2n(unsigned short v) { return htons(v); } +static inline unsigned short n2h(unsigned short v) { return ntohs(v); } +static inline unsigned short flip(unsigned short v) { return OSSwapInt16(v); } +static inline signed short h2n(signed short v) { return htons(v); } +static inline signed short n2h(signed short v) { return ntohs(v); } +static inline signed short flip(signed short v) { return OSSwapInt16(v); } + +static inline unsigned char h2n(unsigned char v) { return v; } +static inline unsigned char n2h(unsigned char v) { return v; } +static inline unsigned char flip(unsigned char v) { return v; } +static inline signed char h2n(signed char v) { return v; } +static inline signed char n2h(signed char v) { return v; } +static inline signed char flip(signed char v) { return v; } + + +// +// Flip pointers +// +template +static inline Base *h2n(Base *p) { return (Base *)h2n(uintptr_t(p)); } + +template +static inline Base *n2h(Base *p) { return (Base *)n2h(uintptr_t(p)); } + + +// +// In-place fix operations +// +template +static inline void h2ni(Type &v) { v = h2n(v); } + +template +static inline void n2hi(Type &v) { v = n2h(v); } + +// +// Endian keeps NBO values in memory and converts +// during loads and stores. This presumes that you are using +// memory blocks thare are read/written/mapped as amorphous byte +// streams, but want to be byte-order clean using them. +// +// The generic definition uses h2n/n2h to flip bytes. Feel free +// to declare specializations of Endian as appropriate. +// +// Note well that the address of an Endian is not an address-of-T, +// and there is no conversion available. +// +template +class Endian { +public: + typedef Type Value; + Endian() : mValue(Type(0)) { } + Endian(Value v) : mValue(h2n(v)) { } + + operator Value () const { return n2h(mValue); } + Endian &operator = (Value v) { mValue = h2n(v); return *this; } + +private: + Value mValue; +}; + + +} // end namespace Security + + +#endif //_H_ENDIAN diff --git a/ld64/src/ld/code-sign-blobs/memutils.h b/ld64/src/ld/code-sign-blobs/memutils.h new file mode 100644 index 0000000..391ddc1 --- /dev/null +++ b/ld64/src/ld/code-sign-blobs/memutils.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// memutils - memory-related low-level utilities for easier living +// +#ifndef _H_MEMUTILS +#define _H_MEMUTILS + +//#include +#include +#include +#include + + +// +// Encapsulate these very sharp tools in a separate (ugly-named) namespace +// +namespace Security { +namespace LowLevelMemoryUtilities { + + +// +// The default system alignment. +// +static const size_t systemAlignment = 4; + + +// +// Get the local alignment for a type, as used by the acting compiler. +// +template +inline size_t alignof() { struct { char c; T t; } s; return sizeof(s) - sizeof(T); } + + +// +// Get the local offset of a field in a (struct or class) type, as layed out +// by the acting compiler. +// NB: "offsetof" is a standard-defined macro. Don't use that. +// +template +inline size_t fieldOffsetOf(Field (Type::*field)) +{ + Type *object = 0; // we don't REALLY need this, but it's easier to read + return uintptr_t(&(object->*field)) - uintptr_t(object); +} + + +// +// Round up a size or pointer to an alignment boundary. +// Alignment must be a power of two; default is default alignment. +// +inline size_t alignUp(size_t size, size_t alignment = systemAlignment) +{ + return ((size - 1) & ~(alignment - 1)) + alignment; +} + +inline void *alignUp(void *p, size_t alignment = systemAlignment) +{ + return reinterpret_cast(alignUp(uintptr_t(p), alignment)); +} + +inline const void *alignUp(const void *p, size_t alignment = systemAlignment) +{ + return reinterpret_cast(alignUp(uintptr_t(p), alignment)); +} + +template +inline const T *increment(const void *p, ptrdiff_t offset) +{ return reinterpret_cast(uintptr_t(p) + offset); } + +template +inline T *increment(void *p, ptrdiff_t offset) +{ return reinterpret_cast(uintptr_t(p) + offset); } + +inline const void *increment(const void *p, ptrdiff_t offset) +{ return increment(p, offset); } + +inline void *increment(void *p, ptrdiff_t offset) +{ return increment(p, offset); } + +template +inline const T *increment(const void *p, ptrdiff_t offset, size_t alignment) +{ return increment(alignUp(p, alignment), offset); } + +template +inline T *increment(void *p, ptrdiff_t offset, size_t alignment) +{ return increment(alignUp(p, alignment), offset); } + +inline const void *increment(const void *p, ptrdiff_t offset, size_t alignment) +{ return increment(p, offset, alignment); } + +inline void *increment(void *p, ptrdiff_t offset, size_t alignment) +{ return increment(p, offset, alignment); } + +inline ptrdiff_t difference(const void *p1, const void *p2) +{ return uintptr_t(p1) - uintptr_t(p2); } + + +} // end namespace LowLevelMemoryUtilities +} // end namespace Security + +#endif //_H_MEMUTILS diff --git a/ld64/src/ld/code-sign-blobs/superblob.h b/ld64/src/ld/code-sign-blobs/superblob.h new file mode 100644 index 0000000..7fea85e --- /dev/null +++ b/ld64/src/ld/code-sign-blobs/superblob.h @@ -0,0 +1,249 @@ +// +// SuperBlob - a typed bag of Blobs +// +#ifndef _H_SUPERBLOB +#define _H_SUPERBLOB + +#include "blob.h" +#include +#include +#include + +using namespace std; + +namespace Security { + + +// +// A SuperBlob is a Blob that contains multiple sub-Blobs of varying type. +// The SuperBlob is contiguous and contains a directory of its sub-blobs. +// A Maker is included. +// +// SuperBlobCore lets you define your own SuperBlob type. To just use a generic +// SuperBlob, use SuperBlob<> below. +// +template +class SuperBlobCore: public Blob<_BlobType, _magic> { +public: + class Maker; friend class Maker; + + typedef _Type Type; + + // echoes from parent BlobCore (the C++ type system is too restrictive here) + typedef BlobCore::Offset Offset; + template BlobType *at(Offset offset) { return BlobCore::at(offset); } + template const BlobType *at(Offset offset) const { return BlobCore::at(offset); } + + void setup(size_t size, unsigned cnt) + { this->initialize(size); this->mCount = cnt; } + + struct Index { + Endian type; // type of sub-Blob + Endian offset; // starting offset + }; + + bool validateBlob(size_t maxSize = 0) const; + + unsigned count() const { return mCount; } + + // access by index number + Type type(unsigned n) const { assert(n < mCount); return mIndex[n].type; } + const BlobCore *blob(unsigned n) const { assert(n < mCount); Offset off=mIndex[n].offset; return off ? at(off) : NULL; } + + template + const BlobType *blob(unsigned n) const { return BlobType::specific(blob(n)); } + + // access by index type (assumes unique types) + const BlobCore *find(Type type) const; + template + const BlobType *find(Type t) const { return BlobType::specific(find(t)); } + +private: + Endian mCount; // number of sub-Blobs following + Index mIndex[0]; // IndexSlot structures + // followed by sub-Blobs, packed and ordered in an undefined way +}; + + +template +inline bool SuperBlobCore<_BlobType, _magic, _Type>::validateBlob(size_t maxSize /* = 0 */) const +{ + unsigned cnt = mCount; + size_t ixLimit = sizeof(SuperBlobCore) + cnt * sizeof(Index); // end of index vector + if (!BlobCore::validateBlob(_magic, ixLimit, maxSize)) + return false; + + for (const Index *ix = mIndex + cnt - 1; ix >= mIndex; ix--) { + Offset offset = ix->offset; + if ( offset == 0 ) + continue; // offset==0 means unused entry + if (offset < ixLimit // offset not too small + || offset + sizeof(BlobCore) > this->length() // fits Blob header (including length field) + || offset + at(offset)->length() > this->length()) // fits entire blob + return false; + } + return true; +} + + +// +// A generic SuperBlob ready for use. You still need to specify a magic number. +// +template +class SuperBlob : public SuperBlobCore, _magic, _Type> { +}; + + +template +const BlobCore *SuperBlobCore<_BlobType, _magic, _Type>::find(Type t) const +{ + for (unsigned slot = 0; slot < mCount; slot++) { + if (mIndex[slot].type == t) { + uint32_t off = mIndex[slot].offset; + if ( off == 0 ) + return NULL; + else + return at(off); + } + } + return NULL; // not found +} + + +// +// A SuperBlob::Maker simply assembles multiple Blobs into a single, indexed +// super-blob. Just add() sub-Blobs by type and call build() to get +// the result, malloc'ed. A Maker is not reusable. +// Maker can repeatedly make SuperBlobs from the same (cached) inputs. +// It can also tell you how big its output will be, given established contents +// plus (optional) additional sizes of blobs yet to come. +// +template +class SuperBlobCore<_BlobType, _magic, _Type>::Maker { +public: + Maker() { } + + Maker(const Maker &src) + { + for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it) + mPieces.insert(make_pair(it->first, it->second->clone())); + } + + ~Maker() + { + for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it) + ::free(it->second); + } + + void add(Type type, BlobCore *blob); // takes ownership of blob + void add(const _BlobType *blobs); // copies all blobs + void add(const Maker &maker); // ditto + + size_t size(size_t size1 = 0, ...) const; // size with optional additional blob sizes + _BlobType *make() const; // create (malloc) and return SuperBlob + _BlobType *operator () () const { return make(); } + +private: + typedef std::map BlobMap; + BlobMap mPieces; +}; + + +// +// Add a Blob to a SuperBlob::Maker. +// This takes ownership of the blob, which must have been malloc'ed. +// Any previous value set for this Type will be freed immediately. +// +template +void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(Type type, BlobCore *blob) +{ + pair r = mPieces.insert(make_pair(type, blob)); + if (!r.second) { // already there + //secdebug("superblob", "Maker %p replaces type=%d", this, type); + ::free(r.first->second); + r.first->second = blob; + } +} + +template +void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const _BlobType *blobs) +{ + for (uint32_t ix = 0; ix < blobs->mCount; ix++) + this->add(blobs->mIndex[ix].type, blobs->blob(ix)->clone()); +} + +template +void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const Maker &maker) +{ + for (typename BlobMap::const_iterator it = maker.mPieces.begin(); it != maker.mPieces.end(); ++it) + this->add(it->first, it->second->clone()); +} + + +// +// Calculate the size the new SuperBlob would have, given the contents of the Maker +// so far, plus additional blobs with the sizes given. +// +template +size_t SuperBlobCore<_BlobType, _magic, _Type>::Maker::size(size_t size1, ...) const +{ + // count established blobs + unsigned count = mPieces.size(); + size_t total = 0; + for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) { + if ( it->second != NULL ) + total += (it->second->length() + 3) & (-4); // 4-byte align each element + } + + // add preview blob sizes to calculation (if any) + if (size1) { + va_list args; + va_start(args, size1); + do { + count++; + total += size1; + size1 = va_arg(args, size_t); + } while (size1); + va_end(args); + } + + return sizeof(SuperBlobCore) + count * sizeof(Index) + total; +} + + +// +// Finish SuperBlob construction and return the new, malloc'ed, SuperBlob. +// This can be done repeatedly. +// +template +_BlobType *SuperBlobCore<_BlobType, _magic, _Type>::Maker::make() const +{ + Offset pc = sizeof(SuperBlobCore) + mPieces.size() * sizeof(Index); + Offset total = size(); + _BlobType *result = (_BlobType *)calloc(1, total); + if (!result) + throw ENOMEM; + result->setup(total, mPieces.size()); + unsigned n = 0; + for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) { + result->mIndex[n].type = it->first; + BlobCore* b = it->second; + if ( b != NULL ) { + result->mIndex[n].offset = pc; + memcpy(result->template at(pc), b, b->length()); + pc += ((b->length() + 3) & (-4)); // 4-byte align each element + } + else { + result->mIndex[n].offset = 0; + } + n++; + } + //secdebug("superblob", "Maker %p assembles %ld blob(s) into %p (size=%d)", + // this, mPieces.size(), result, total); + return result; +} + + +} // Security + +#endif //_H_SUPERBLOB diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index ce49e73..24e9c48 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -67,6 +67,7 @@ extern "C" double log2 ( double ); #include "InputFiles.h" #include "Resolver.h" #include "OutputFile.h" +#include "Snapshot.h" #include "passes/stubs/make_stubs.h" #include "passes/dtrace_dof.h" @@ -619,6 +620,25 @@ static void getVMInfo(vm_statistics_data_t& info) } } + + +static const char* sOverridePathlibLTO = NULL; + +// +// This is magic glue that overrides the default behaviour +// of lazydylib1.o which is used to lazily load libLTO.dylib. +// +extern "C" const char* dyld_lazy_dylib_path_fix(const char* path); +const char* dyld_lazy_dylib_path_fix(const char* path) +{ + if ( sOverridePathlibLTO != NULL ) + return sOverridePathlibLTO; + else + return path; +} + + + int main(int argc, const char* argv[]) { const char* archName = NULL; @@ -632,6 +652,9 @@ int main(int argc, const char* argv[]) Options options(argc, argv); InternalState state(options); + // allow libLTO to be overridden by command line -lto_library + sOverridePathlibLTO = options.overridePathlibLTO(); + // gather vm stats if ( options.printStatistics() ) getVMInfo(statistics.vmStart); @@ -649,7 +672,7 @@ int main(int argc, const char* argv[]) statistics.startResolver = mach_absolute_time(); ld::tool::Resolver resolver(options, inputFiles, state); resolver.resolve(); - + // add dylibs used statistics.startDylibs = mach_absolute_time(); inputFiles.dylibs(state); @@ -727,7 +750,11 @@ int main(int argc, const char* argv[]) // implement assert() function to print out a backtrace before aborting void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) { - fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + Snapshot *snapshot = Snapshot::globalSnapshot; + + snapshot->setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + snapshot->createSnapshot(); + snapshot->recordAssertionMessage("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); void* callStack[128]; int depth = ::backtrace(callStack, 128); @@ -745,7 +772,10 @@ void __assert_rtn(const char* func, const char* file, int line, const char* fail } long offset = (uintptr_t)callStack[i] - (uintptr_t)info.dli_saddr; fprintf(stderr, "%d %p %s + %ld\n", i, callStack[i], symboName, offset); + snapshot->recordAssertionMessage("%d %p %s + %ld\n", i, callStack[i], symboName, offset); } + fprintf(stderr, "A linker snapshot was created at:\n\t%s\n", snapshot->rootDir()); + fprintf(stderr, "ld: Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); exit(1); } #endif diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index b615046..c33bff6 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -60,21 +60,93 @@ class File virtual void doFile(const class File&) = 0; }; - File(const char* pth, time_t modTime, uint32_t ord) - : _path(pth), _modTime(modTime), _ordinal(ord) { } + // + // ld::File::Ordinal + // + // Codifies the rules of ordering input files for symbol precedence. These are: + // - Input files listed on the command line are ordered according to their index in the argument list. + // - Input files listed in a file list are ordered first at the index of the file list argument, then + // by index in the file list + // - Input files extracted from archives are ordered using the ordinal of the archive itself plus the + // index of the object file within the archive + // - Indirect dylibs are ordered after all input files derived from the command line, in the order that + // they are discovered. + // - The LTO object file is last. + // + class Ordinal + { + private: + // The actual numeric ordinal. Lower values have higher precedence and a zero value is invalid. + // The 64 bit ordinal is broken into 4 16 bit chunks. The high 16 bits are a "partition" that + // is used to distinguish major ordinal groups: command line, indirect dylib, LTO. + // The remaining chunks are used according to the partition (see below). + uint64_t _ordinal; + + Ordinal (uint64_t ordinal) : _ordinal(ordinal) {} + + enum { ArgListPartition=0, IndirectDylibPartition=1, LTOPartition = 2, InvalidParition=0xffff }; + Ordinal(uint16_t partition, uint16_t majorIndex, uint16_t minorIndex, uint16_t counter) { + _ordinal = ((uint64_t)partition<<48) | ((uint64_t)majorIndex<<32) | ((uint64_t)minorIndex<<16) | ((uint64_t)counter<<0); + } + + const uint16_t partition() const { return (_ordinal>>48)&0xffff; } + const uint16_t majorIndex() const { return (_ordinal>>32)&0xffff; } + const uint16_t minorIndex() const { return (_ordinal>>16)&0xffff; } + const uint16_t counter() const { return (_ordinal>>00)&0xffff; } + + const Ordinal nextMajorIndex() const { assert(majorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<32)); } + const Ordinal nextMinorIndex() const { assert(minorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<16)); } + const Ordinal nextCounter() const { assert(counter() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<0)); } + + public: + Ordinal() : _ordinal(0) {}; + + static const Ordinal NullOrdinal() { return Ordinal((uint64_t)0); } + + const bool validOrdinal() const { return _ordinal != 0; } + + bool operator ==(const Ordinal& rhs) const { return _ordinal == rhs._ordinal; } + bool operator !=(const Ordinal& rhs) const { return _ordinal != rhs._ordinal; } + bool operator < (const Ordinal& rhs) const { return _ordinal < rhs._ordinal; } + bool operator > (const Ordinal& rhs) const { return _ordinal > rhs._ordinal; } + + // For ordinals derived from the command line args the partition is ArgListPartition + // The majorIndex is the arg index that pulls in the file, file list, or archive. + // The minorIndex is used for files pulled in by a file list and the value is the index of the file in the file list. + // The counter is used for .a files and the value is the index of the object in the archive. + // Thus, an object pulled in from a .a that was listed in a file list could use all three fields. + static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(ArgListPartition, argIndex, 0, 0); }; + const Ordinal nextFileListOrdinal() const { return nextMinorIndex(); } + const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(ArgListPartition, majorIndex(), minorIndex(), index); } + + // For indirect libraries the partition is IndirectDylibPartition and the counter is used or order the libraries. + static const ld::File::Ordinal indirectDylibBase() { return Ordinal(IndirectDylibPartition, 0, 0, 0); } + const Ordinal nextIndirectDylibOrdinal() const { return nextCounter(); } + + // For the LTO mach-o the partition is LTOPartition. As there is only one LTO file no other fields are needed. + static const ld::File::Ordinal LTOOrdinal() { return Ordinal(LTOPartition, 0, 0, 0); } + }; + + typedef enum { Reloc, Dylib, Archive, Other } Type; + + File(const char* pth, time_t modTime, Ordinal ord, Type type) + : _path(pth), _modTime(modTime), _ordinal(ord), _type(type) { } virtual ~File() {} const char* path() const { return _path; } time_t modificationTime() const{ return _modTime; } - uint32_t ordinal() const { return _ordinal; } + Ordinal ordinal() const { return _ordinal; } virtual bool forEachAtom(AtomHandler&) const = 0; virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0; virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; } virtual uint32_t cpuSubType() const { return 0; } virtual uint32_t subFileCount() const { return 1; } + bool fileExists() const { return _modTime != 0; } + Type type() const { return _type; } private: const char* _path; time_t _modTime; - uint32_t _ordinal; + const Ordinal _ordinal; + const Type _type; }; @@ -82,9 +154,11 @@ class File // minumum OS versions // enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, - mac10_6=0x000A0600, mac10_7=0x000A0700 }; + mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800, + mac10_Future=0x10000000 }; enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, - iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000 }; + iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, + iOS_Future=0x10000000}; namespace relocatable { // @@ -92,8 +166,6 @@ namespace relocatable { // // Abstract base class for object files the linker processes. // - // objcReplacementClasses() is reflects if the file was compiled for fix-and-continue - // // debugInfo() returns if the object file contains debugger information (stabs or dwarf). // // stabs() lazily creates a vector of Stab objects for each atom @@ -118,10 +190,9 @@ namespace relocatable { const char* string; }; - File(const char* pth, time_t modTime, uint32_t ord) - : ld::File(pth, modTime, ord) { } + File(const char* pth, time_t modTime, Ordinal ord) + : ld::File(pth, modTime, ord, Reloc) { } virtual ~File() {} - virtual bool objcReplacementClasses() const = 0; virtual DebugInfoKind debugInfo() const = 0; virtual const char* debugInfoPath() const { return path(); } virtual time_t debugInfoModificationTime() const { return modificationTime(); } @@ -149,8 +220,8 @@ namespace dylib { virtual File* findDylib(const char* installPath, const char* fromPath) = 0; }; - File(const char* pth, time_t modTime, uint32_t ord) - : ld::File(pth, modTime, ord), _dylibInstallPath(NULL), + File(const char* pth, time_t modTime, Ordinal ord) + : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL), _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0), _explicitlyLinked(false), _implicitlyLinked(false), _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false), @@ -163,6 +234,7 @@ namespace dylib { bool explicitlyLinked() const { return _explicitlyLinked; } void setImplicitlyLinked() { _implicitlyLinked = true; } bool implicitlyLinked() const { return _implicitlyLinked; } + // attributes of how dylib will be used when linked void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; } bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; } @@ -185,6 +257,7 @@ namespace dylib { virtual bool hasWeakDefinition(const char* name) const = 0; virtual bool hasPublicInstallName() const = 0; virtual bool allSymbolsAreWeakImported() const = 0; + virtual const void* codeSignatureDR() const = 0; protected: const char* _dylibInstallPath; uint32_t _dylibTimeStamp; @@ -210,8 +283,8 @@ namespace archive { class File : public ld::File { public: - File(const char* pth, time_t modTime, uint32_t ord) - : ld::File(pth, modTime, ord) { } + File(const char* pth, time_t modTime, Ordinal ord) + : ld::File(pth, modTime, ord, Archive) { } virtual ~File() {} virtual bool justInTimeDataOnlyforEachAtom(const char* name, AtomHandler&) const = 0; }; @@ -325,6 +398,9 @@ struct Fixup kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, // lazy binding kindLazyTarget, kindSetLazyOffset, + // data-in-code markers + kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16, + kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd, // pointer store combinations kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32 kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64 @@ -579,7 +655,9 @@ class Atom void setSectionStartAddress(uint64_t a) { assert(_mode == modeSectionOffset); _address += a; _mode = modeFinalAddress; } uint64_t sectionOffset() const { assert(_mode == modeSectionOffset); return _address; } uint64_t finalAddress() const { assert(_mode == modeFinalAddress); return _address; } - +#ifndef NDEBUG + bool finalAddressMode() const { return (_mode == modeFinalAddress); } +#endif virtual const File* file() const = 0; virtual bool translationUnitSource(const char** dir, const char** name) const = 0; virtual const char* name() const = 0; @@ -591,6 +669,13 @@ class Atom virtual bool canCoalesceWith(const Atom& rhs, const class IndirectBindingTable&) const { return false; } virtual Fixup::iterator fixupsBegin() const { return NULL; } virtual Fixup::iterator fixupsEnd() const { return NULL; } + bool hasFixupsOfKind(Fixup::Kind kind) const { + for (ld::Fixup::iterator fit = fixupsBegin(), end=fixupsEnd(); fit != end; ++fit) { + if ( fit->kind == kind ) return true; + } + return false; + } + virtual UnwindInfo::iterator beginUnwind() const { return NULL; } virtual UnwindInfo::iterator endUnwind() const { return NULL; } virtual LineInfo::iterator beginLineInfo() const { return NULL; } @@ -681,7 +766,7 @@ class Internal objcObjectConstraint(ld::File::objcConstraintNone), objcDylibConstraint(ld::File::objcConstraintNone), cpuSubType(0), - allObjectFilesScatterable(true), hasObjcReplacementClasses(false), + allObjectFilesScatterable(true), someObjectFileHasDwarf(false), usingHugeSections(false) { } std::vector sections; @@ -697,7 +782,6 @@ class Internal ld::File::ObjcConstraint objcDylibConstraint; uint32_t cpuSubType; bool allObjectFilesScatterable; - bool hasObjcReplacementClasses; bool someObjectFileHasDwarf; bool usingHugeSections; }; diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index cf0a058..78d8912 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -62,7 +62,7 @@ class Parser return File::validFile(fileContent, fileLength, opts); } static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, - uint32_t ordinal, const ParserOptions& opts) { + ld::File::Ordinal ordinal, const ParserOptions& opts) { return new File(fileContent, fileLength, path, mTime, ordinal, opts); } @@ -77,7 +77,7 @@ class File : public ld::archive::File const mach_o::relocatable::ParserOptions& opts); File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t modTime, - uint32_t ord, const ParserOptions& opts); + ld::File::Ordinal ord, const ParserOptions& opts); virtual ~File() {} // overrides of ld::File @@ -98,7 +98,7 @@ class File : public ld::archive::File class Entry : ar_hdr { public: - const char* name() const; + void getName(char *, int) const; time_t modificationTime() const; const uint8_t* content() const; uint32_t contentSize() const; @@ -109,6 +109,9 @@ class File : public ld::archive::File }; + struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint16_t index;}; + bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const; + class CStringEquals { public: @@ -119,8 +122,6 @@ class File : public ld::archive::File typedef typename A::P P; typedef typename A::P::E E; - struct MemberState { ld::relocatable::File* file; bool logged; bool loaded; }; - typedef std::map MemberToStateMap; const struct ranlib* ranlibHashSearch(const char* name) const; @@ -161,23 +162,21 @@ unsigned int File::Entry::getLongNameSpace() const } template -const char* File::Entry::name() const +void File::Entry::getName(char *buf, int bufsz) const { if ( this->hasLongName() ) { int len = this->getLongNameSpace(); - static char longName[256]; - strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); - longName[len] = '\0'; - return longName; + assert(bufsz >= len+1); + strncpy(buf, ((char*)this)+sizeof(ar_hdr), len); + buf[len] = '\0'; } else { - static char shortName[20]; - strncpy(shortName, this->ar_name, 16); - shortName[16] = '\0'; - char* space = strchr(shortName, ' '); + assert(bufsz >= 16+1); + strncpy(buf, this->ar_name, 16); + buf[16] = '\0'; + char* space = strchr(buf, ' '); if ( space != NULL ) *space = '\0'; - return shortName; } } @@ -256,7 +255,8 @@ bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const m const Entry* const start = (Entry*)&fileContent[8]; const Entry* const end = (Entry*)&fileContent[fileLength]; for (const Entry* p=start; p < end; p = p->next()) { - const char* memberName = p->name(); + char memberName[256]; + p->getName(memberName, sizeof(memberName)); // skip option table-of-content member if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) continue; @@ -270,7 +270,7 @@ bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const m template File::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, time_t modTime, - uint32_t ord, const ParserOptions& opts) + ld::File::Ordinal ord, const ParserOptions& opts) : ld::archive::File(strdup(pth), modTime, ord), _archiveFileContent(fileContent), _archiveFilelength(fileLength), _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL), @@ -283,7 +283,9 @@ File::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, if ( !_forceLoadAll ) { const Entry* const firstMember = (Entry*)&_archiveFileContent[8]; - if ( (strcmp(firstMember->name(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->name(), SYMDEF) == 0) ) { + char memberName[256]; + firstMember->getName(memberName, sizeof(memberName)); + if ( (strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0) ) { const uint8_t* contents = firstMember->content(); uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); _tableOfContents = (const struct ranlib*)&contents[4]; @@ -308,7 +310,8 @@ bool File::memberHasObjCCategories(const Entry* member) const } else { // i386 uses ObjC1 ABI which has .objc_category* global symbols - return false; + // strip -S on i386 pulls out .objc_category_name symbols from static frameworks + return mach_o::relocatable::hasObjC1Categories(member->content()); } } @@ -325,12 +328,37 @@ bool File::memberHasObjCCategories(const Entry* member) const template typename File::MemberState& File::makeObjectFileForMember(const Entry* member) const { + uint16_t memberIndex = 0; // in case member was instantiated earlier but not needed yet typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member); - if ( pos != _instantiatedEntries.end() ) - return pos->second; - - const char* memberName = member->name(); + if ( pos == _instantiatedEntries.end() ) { + // Have to find the index of this member + const Entry* start; + uint16_t index; + if (_instantiatedEntries.size() == 0) { + start = (Entry*)&_archiveFileContent[8]; + index = 1; + } else { + MemberState &lastKnown = _instantiatedEntries.rbegin()->second; + start = lastKnown.entry->next(); + index = lastKnown.index+1; + } + for (const Entry* p=start; p <= member; p = p->next(), index++) { + MemberState state = {NULL, p, false, false, index}; + _instantiatedEntries[p] = state; + if (member == p) { + memberIndex = index; + } + } + } else { + MemberState& state = pos->second; + if (state.file) + return state; + memberIndex = state.index; + } + assert(memberIndex != 0); + char memberName[256]; + member->getName(memberName, sizeof(memberName)); char memberPath[strlen(this->path()) + strlen(memberName)+4]; strcpy(memberPath, this->path()); strcat(memberPath, "("); @@ -344,23 +372,22 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem if ( (member->content() + member->contentSize()) > (_archiveFileContent+_archiveFilelength) ) throwf("corrupt archive, member contents extends past end of file"); const char* mPath = strdup(memberPath); - // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive - uint32_t memberIndex = ((uint8_t*)member - _archiveFileContent)/sizeof(ar_hdr); // see if member is mach-o file + ld::File::Ordinal ordinal = this->ordinal().archiveOrdinalWithMemberIndex(memberIndex); ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(), mPath, member->modificationTime(), - this->ordinal() + memberIndex, _objOpts); + ordinal, _objOpts); if ( result != NULL ) { - MemberState state = {result, false, false}; + MemberState state = {result, member, false, false, memberIndex}; _instantiatedEntries[member] = state; return _instantiatedEntries[member]; } // see if member is llvm bitcode file result = lto::parse(member->content(), member->contentSize(), - mPath, member->modificationTime(), this->ordinal() + memberIndex, + mPath, member->modificationTime(), _objOpts.architecture, _objOpts.subType, _logAllFiles); if ( result != NULL ) { - MemberState state = {result, false, false}; + MemberState state = {result, member, false, false, memberIndex}; _instantiatedEntries[member] = state; return _instantiatedEntries[member]; } @@ -373,6 +400,25 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem } +template +bool File::loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const +{ + bool didSomething = false; + if (!state.loaded) { + if ( _verboseLoad && !state.logged ) { + va_list list; + va_start(list, format); + vprintf(format, list); + va_end(list); + state.logged = true; + } + state.loaded = true; + didSomething = state.file->forEachAtom(handler); + } + return didSomething; +} + + template bool File::forEachAtom(ld::File::AtomHandler& handler) const { @@ -382,19 +428,12 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const const Entry* const start = (Entry*)&_archiveFileContent[8]; const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; for (const Entry* p=start; p < end; p = p->next()) { - const char* memberName = p->name(); + char memberName[256]; + p->getName(memberName, sizeof(memberName)); if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) continue; MemberState& state = this->makeObjectFileForMember(p); - if ( _verboseLoad ) { - if ( _forceLoadThis ) - printf("-force_load forced load of %s(%s)\n", this->path(), memberName); - else - printf("-all_load forced load of %s(%s)\n", this->path(), memberName); - state.logged = true; - } - didSome |= state.file->forEachAtom(handler); - state.loaded = true; + didSome |= loadMember(state, handler, "%s forced load of %s(%s)\n", _forceLoadThis ? "-force_load" : "-all_load", this->path(), memberName); } } else if ( _forceLoadObjC ) { @@ -403,33 +442,28 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { const Entry* member = (Entry*)&_archiveFileContent[E::get32(it->second->ran_off)]; MemberState& state = this->makeObjectFileForMember(member); - if ( _verboseLoad && !state.logged ) { - printf("-ObjC forced load of %s(%s)\n", this->path(), member->name()); - state.logged = true; - } - if ( ! state.loaded ) { - didSome |= state.file->forEachAtom(handler); - state.loaded = true; - } + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); } } // ObjC2 has no symbols in .o files with categories but not classes, look deeper for those const Entry* const start = (Entry*)&_archiveFileContent[8]; const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; for (const Entry* member=start; member < end; member = member->next()) { - // only look at files not already instantiated - if ( _instantiatedEntries.count(member) == 0 ) { - //fprintf(stderr, "checking member %s\n", member->name()); + char mname[256]; + member->getName(mname, sizeof(mname)); + // skip table-of-content member + if ( (member==start) && ((strcmp(mname, SYMDEF_SORTED) == 0) || (strcmp(mname, SYMDEF) == 0)) ) + continue; + MemberState& state = this->makeObjectFileForMember(member); + // only look at files not already loaded + if ( ! state.loaded ) { if ( this->memberHasObjCCategories(member) ) { MemberState& state = this->makeObjectFileForMember(member); - if ( _verboseLoad && !state.logged ) { - printf("-ObjC forced load of %s(%s)\n", this->path(), member->name()); - state.logged = true; - } - if ( ! state.loaded ) { - didSome |= state.file->forEachAtom(handler); - state.loaded = true; - } + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); } } } @@ -449,15 +483,9 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han if ( result != NULL ) { const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)]; MemberState& state = this->makeObjectFileForMember(member); - // only call handler for each member once - if ( ! state.loaded && !state.logged ) { - if ( _verboseLoad ) { - printf("%s forced load of %s(%s)\n", name, this->path(), member->name()); - state.logged = true; - } - state.loaded = true; - return state.file->forEachAtom(handler); - } + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); } //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); return false; @@ -499,12 +527,9 @@ bool File::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHand CheckIsDataSymbolHandler checker(name); state.file->forEachAtom(checker); if ( checker.symbolIsDataDefinition() ) { - if ( _verboseLoad && !state.logged ) { - printf("%s forced load of %s(%s)\n", name, this->path(), member->name()); - state.logged = true; - } - state.loaded = true; - return state.file->forEachAtom(handler); + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); } } } @@ -558,21 +583,27 @@ void File::dumpTableOfContents() // main function used by linker to instantiate archive files // ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts) + const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { switch ( opts.objOpts.architecture ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif } return NULL; } diff --git a/ld64/src/ld/parsers/archive_file.h b/ld64/src/ld/parsers/archive_file.h index 31bab29..1ed411a 100644 --- a/ld64/src/ld/parsers/archive_file.h +++ b/ld64/src/ld/parsers/archive_file.h @@ -41,7 +41,7 @@ struct ParserOptions { }; extern ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts); + const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts); } // namespace archive diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 3a11c18..0bc1831 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -91,7 +91,7 @@ class File : public ld::relocatable::File { public: File(const char* path, time_t mTime, const uint8_t* content, - uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); + uint32_t contentLength, cpu_type_t arch); virtual ~File(); // overrides of ld::File @@ -101,7 +101,6 @@ class File : public ld::relocatable::File virtual uint32_t cpuSubType() const { return _cpuSubType; } // overrides of ld::relocatable::File - virtual bool objcReplacementClasses() const { return false; } virtual DebugInfoKind debugInfo() const { return _debugInfo; } virtual const char* debugInfoPath() const { return _debugInfoPath; } virtual time_t debugInfoModificationTime() const @@ -199,11 +198,10 @@ class Parser static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + time_t modTime, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } static bool optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, @@ -213,7 +211,7 @@ class Parser private: static const char* tripletPrefixForArch(cpu_type_t arch); - static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options); + static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options); class CStringEquals { @@ -246,17 +244,17 @@ std::vector Parser::_s_files; bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) { - switch (architecture) { - case CPU_TYPE_I386: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "i386-"); - case CPU_TYPE_X86_64: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-"); - case CPU_TYPE_ARM: - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( subarch == t->subType ) - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix); + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (architecture == t->cpuType) && (!(t->isSubType) || (subarch == t->cpuSubType)) ) { + bool result = ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix); + if ( !result ) { + // LTO only supports thumbv7 not armv7 + if ( t->llvmTriplePrefixAlt[0] != '\0' ) { + result = ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefixAlt); + } } - break; + return result; + } } return false; } @@ -264,18 +262,17 @@ bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) { if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { - uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); - switch (arch) { - case CPU_TYPE_I386: - return "i386"; - case CPU_TYPE_X86_64: - return "x86_64"; - case CPU_TYPE_ARM: - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { + cpu_type_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( arch == t->cpuType ) { + if ( t->isSubType ) { if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, t->llvmTriplePrefix) ) - return t->subTypeName; + return t->archName; + } + else { + return t->archName; } - return "arm"; + } } return "unknown bitcode architecture"; } @@ -283,9 +280,9 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) } File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) { - File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); + File* f = new File(path, modTime, fileContent, fileLength, architecture); _s_files.push_back(f); if ( logAllFiles ) printf("%s\n", path); @@ -293,7 +290,7 @@ File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* } -ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options) +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options) { mach_o::relocatable::ParserOptions objOpts; objOpts.architecture = options.arch; @@ -312,7 +309,7 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint modTime = statBuffer.st_mtime; } - ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, nextInputOrdinal, objOpts); + ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, ld::File::Ordinal::LTOOrdinal(), objOpts); if ( result != NULL ) return result; throw "LLVM LTO, file is not of required architecture"; @@ -320,8 +317,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint -File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ord, cpu_type_t arch) - : ld::relocatable::File(pth,mTime,ord), _architecture(arch), _internalAtom(*this), +File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) + : ld::relocatable::File(pth,mTime,ld::File::Ordinal::LTOOrdinal()), _architecture(arch), _internalAtom(*this), _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), @@ -399,7 +396,7 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); // make Atom using placement new operator new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment, autohide); - if ( scope == ld::Atom::scopeLinkageUnit ) + if ( scope != ld::Atom::scopeTranslationUnit ) _internalAtom.addReference(name); if ( log ) fprintf(stderr, "\t0x%08X %s\n", attr, name); } @@ -458,7 +455,6 @@ void Atom::setCompiledAtom(const ld::Atom& atom) bool Parser::optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, @@ -577,7 +573,7 @@ bool Parser::optimize( const std::vector& allAtoms, // 1 - atom scope is global (and not linkage unit). // 2 - included in nonLLVMRefs set. // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( (atom->scope() == ld::Atom::scopeGlobal) ) { + if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) { if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name); ::lto_codegen_add_must_preserve_symbol(generator, name); } @@ -680,7 +676,7 @@ bool Parser::optimize( const std::vector& allAtoms, } // parse generated mach-o file into a MachOReader - ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, options); + ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, options); // sync generated mach-o atoms with existing atoms ld knows about if ( logAtomsBeforeSync ) { @@ -806,12 +802,20 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) } +class Mutex { + static pthread_mutex_t lto_lock; +public: + Mutex() { pthread_mutex_lock(<o_lock); } + ~Mutex() { pthread_mutex_unlock(<o_lock); } +}; +pthread_mutex_t Mutex::lto_lock = PTHREAD_MUTEX_INITIALIZER; // // Used by archive reader to see if member is an llvm bitcode file // bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) { + Mutex lock; return Parser::validFile(fileContent, fileLength, architecture, subarch); } @@ -820,11 +824,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // main function used by linker to instantiate ld::Files // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, + const char* path, time_t modTime, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) { + Mutex lock; if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); + return Parser::parse(fileContent, fileLength, path, modTime, architecture, subarch, logAllFiles); else return NULL; } @@ -834,6 +839,7 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, // const char* version() { + Mutex lock; return ::lto_get_version(); } @@ -843,6 +849,7 @@ const char* version() // bool libLTOisLoaded() { + Mutex lock; return (::lto_get_version() != NULL); } @@ -851,6 +858,7 @@ bool libLTOisLoaded() // const char* archName(const uint8_t* fileContent, uint64_t fileLength) { + Mutex lock; return Parser::fileKind(fileContent, fileLength); } @@ -859,13 +867,13 @@ const char* archName(const uint8_t* fileContent, uint64_t fileLength) // bool optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, std::vector& additionalUndefines) { - return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines); + Mutex lock; + return Parser::optimize(allAtoms, state, options, handler, newAtoms, additionalUndefines); } diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index dbb82a5..492d32c 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -38,13 +38,13 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, + const char* path, time_t modTime, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); struct OptimizeOptions { const char* outputFilePath; const char* tmpObjectFilePath; - bool allGlobalsAReDeadStripRoots; + bool preserveAllGlobals; bool verbose; bool saveTemps; bool pie; @@ -59,7 +59,6 @@ struct OptimizeOptions { extern bool optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 39cebfb..11c03ac 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -40,7 +40,7 @@ #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" #include "macho_dylib_file.h" - +#include "../code-sign-blobs/superblob.h" namespace mach_o { namespace dylib { @@ -144,7 +144,7 @@ class File : public ld::dylib::File public: static bool validFile(const uint8_t* fileContent, bool executableOrDylib); File(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t mTime, uint32_t ordinal, bool linkingFlatNamespace, + time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool addVers, bool logAllFiles, const char* installPath, bool indirectDylib); @@ -165,6 +165,7 @@ class File : public ld::dylib::File virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } virtual bool hasWeakDefinition(const char* name) const; virtual bool allSymbolsAreWeakImported() const; + virtual const void* codeSignatureDR() const { return _codeSignatureDR; } protected: @@ -218,6 +219,7 @@ class File : public ld::dylib::File NameSet _ignoreExports; const char* _parentUmbrella; ImportAtom* _importAtom; + const void* _codeSignatureDR; bool _noRexports; bool _hasWeakExports; bool _deadStrippable; @@ -240,7 +242,7 @@ template <> const char* File::objCInfoSectionName() { return "__objc_imagei template const char* File::objCInfoSectionName() { return "__image_info"; } template -File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord, +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool addVers, bool logAllFiles, const char* targetInstallPath, bool indirectDylib) @@ -250,7 +252,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _objcContraint(ld::File::objcConstraintNone), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), - _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false), + _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), + _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), _providedAtom(false), _explictReExportFound(false) { @@ -281,6 +284,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format const macho_dysymtab_command

* dynamicInfo = NULL; const macho_dyld_info_command

* dyldInfo = NULL; + const macho_linkedit_data_command

* codeSignature = NULL; const macho_nlist

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

*)cmd; symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); strings = (char*)header + symtab->stroff(); + if ( (symtab->stroff() + symtab->strsize()) > fileLength ) + throwf("mach-o string pool extends beyond end of file in %s", pth); break; case LC_DYSYMTAB: dynamicInfo = (macho_dysymtab_command

*)cmd; @@ -333,6 +339,9 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( _addVersionLoadCommand && !indirectDylib && (_macVersionMin != ld::macVersionUnset) ) warning("building for MacOSX, but linking against dylib built for iOS: %s", pth); break; + case LC_CODE_SIGNATURE: + codeSignature = (macho_linkedit_data_command

* )cmd; + break; case macho_segment_command

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

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { @@ -460,6 +469,25 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _importAtom = new ImportAtom(*this, importNames); } + // if the dylib is code signed, look for its Designated Requirement + if ( codeSignature != NULL ) { + const Security::BlobCore* overallSignature = (Security::BlobCore*)((char*)header + codeSignature->dataoff()); + typedef Security::SuperBlob EmbeddedSignatureBlob; + typedef Security::SuperBlob InternalRequirementsBlob; + const EmbeddedSignatureBlob* signature = EmbeddedSignatureBlob::specific(overallSignature); + if ( signature->validateBlob(codeSignature->datasize()) ) { + const InternalRequirementsBlob* ireq = signature->find(Security::cdRequirementsSlot); + if ( (ireq != NULL) && ireq->validateBlob() ) { + const Security::BlobCore* dr = ireq->find(Security::kSecDesignatedRequirementType); + if ( (dr != NULL) && dr->validateBlob(Security::kSecCodeMagicRequirement) ) { + // make copy because mapped file is about to be unmapped + _codeSignatureDR = ::malloc(dr->length()); + ::memcpy((void*)_codeSignatureDR, dr, dr->length()); + } + } + } + } + // build hash table if ( dyldInfo != NULL ) buildExportHashTableFromExportInfo(dyldInfo, fileContent); @@ -577,6 +605,10 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address this->addSymbol(symName, weakDef, false, 0); return; } + else if ( strncmp(symAction, "install_name$", 13) == 0 ) { + _dylibInstallPath = symName; + return; + } else { warning("bad symbol action: %s in dylib %s", name, this->path()); } @@ -838,8 +870,8 @@ class Parser static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, - uint32_t ordinal, const Options& opts, bool indirectDylib) { - return new File(fileContent, fileLength, path, mTime, + ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { + return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), @@ -942,22 +974,28 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor // main function used by linker to instantiate ld::Files // ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, const Options& opts, uint32_t ordinal, + const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib) { switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif } return NULL; } diff --git a/ld64/src/ld/parsers/macho_dylib_file.h b/ld64/src/ld/parsers/macho_dylib_file.h index d26f50e..ad03af1 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.h +++ b/ld64/src/ld/parsers/macho_dylib_file.h @@ -32,7 +32,7 @@ namespace mach_o { namespace dylib { extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, const Options& opts, uint32_t ordinal, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib); } // namespace dylib diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index e79d261..50f5327 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -70,7 +70,7 @@ template class File : public ld::relocatable::File { public: - File(const char* p, time_t mTime, const uint8_t* content, uint32_t ord) : + File(const char* p, time_t mTime, const uint8_t* content, ld::File::Ordinal ord) : ld::relocatable::File(p,mTime,ord), _fileContent(content), _sectionsArray(NULL), _atomsArray(NULL), _sectionsArrayCount(0), _atomsArrayCount(0), @@ -80,7 +80,7 @@ class File : public ld::relocatable::File _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), _objConstraint(ld::File::objcConstraintNone), _cpuSubType(0), - _ojcReplacmentClass(false), _canScatterAtoms(false) {} + _canScatterAtoms(false) {} virtual ~File(); // overrides of ld::File @@ -89,7 +89,6 @@ class File : public ld::relocatable::File { return false; } // overrides of ld::relocatable::File - virtual bool objcReplacementClasses() const { return _ojcReplacmentClass; } virtual ObjcConstraint objCConstraint() const { return _objConstraint; } virtual uint32_t cpuSubType() const { return _cpuSubType; } virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } @@ -124,7 +123,6 @@ class File : public ld::relocatable::File const macho_section

* _dwarfDebugStringSect; ld::File::ObjcConstraint _objConstraint; uint32_t _cpuSubType; - bool _ojcReplacmentClass; bool _canScatterAtoms; }; @@ -861,8 +859,9 @@ class Parser cpu_subtype_t subtype=0); static const char* fileKind(const uint8_t* fileContent); static bool hasObjC2Categories(const uint8_t* fileContent); + static bool hasObjC1Categories(const uint8_t* fileContent); static ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, + const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { Parser p(fileContent, fileLength, path, modTime, ordinal, opts.convertUnwindInfo); @@ -972,6 +971,7 @@ class Parser void addDtraceExtraInfos(const SourceLocation& src, const char* provider); const char* scanSymbolTableForAddress(uint64_t addr); bool convertUnwindInfo() { return _convertUnwindInfo; } + bool hasDataInCodeLabels() { return _hasDataInCodeLabels; } void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target); @@ -1048,7 +1048,7 @@ class Parser Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - uint32_t ordinal, bool convertUnwindInfo); + ld::File::Ordinal ordinal, bool convertUnwindInfo); ld::relocatable::File* parse(const ParserOptions& opts); uint8_t loadCommandSizeMask(); bool parseLoadCommands(); @@ -1075,7 +1075,7 @@ class Parser uint32_t _fileLength; const char* _path; time_t _modTime; - uint32_t _ordinal; + ld::File::Ordinal _ordinal; // filled in by parseLoadCommands() File* _file; @@ -1102,6 +1102,7 @@ class Parser bool _AppleObjc; // FSF has objc that uses different data layout bool _overlappingSymbols; bool _convertUnwindInfo; + bool _hasDataInCodeLabels; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1112,7 +1113,7 @@ class Parser template Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - uint32_t ordinal, bool convertDUI) + ld::File::Ordinal ordinal, bool convertDUI) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), @@ -1122,7 +1123,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL), _tentativeDefinitionCount(0), _absoluteSymbolCount(0), _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), - _overlappingSymbols(false), _convertUnwindInfo(convertDUI), + _overlappingSymbols(false), _convertUnwindInfo(convertDUI), _hasDataInCodeLabels(false), _stubsSectionNum(0), _stubsMachOSection(NULL) { } @@ -1207,9 +1208,9 @@ const char* Parser::fileKind(const uint8_t* fileContent) return NULL; if ( header->cputype() != CPU_TYPE_ARM ) return NULL; - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( t->subType == (cpu_subtype_t)header->cpusubtype() ) { - return t->subTypeName; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) { + return t->archName; } } return "arm???"; @@ -1244,6 +1245,35 @@ bool Parser::hasObjC2Categories(const uint8_t* fileContent) return false; } + +template +bool Parser::hasObjC1Categories(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* segment = (macho_segment_command

*)cmd; + const macho_section

* sectionsStart = (macho_section

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

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

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

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return false; +} + template int Parser::pointerSorter(const void* l, const void* r) { @@ -1724,6 +1754,7 @@ void Parser::prescanSymbolTable() _tentativeDefinitionCount = 0; _absoluteSymbolCount = 0; _symbolsInSections = 0; + _hasDataInCodeLabels = false; for (uint32_t i=0; i < this->_symbolCount; ++i) { const macho_nlist

& sym = symbolFromIndex(i); // ignore stabs @@ -1769,9 +1800,12 @@ void Parser::prescanSymbolTable() continue; // 'L' labels do not denote atom breaks - if ( symbolName[0] == 'L' ) + if ( symbolName[0] == 'L' ) { + // Formalize data in code with L$start$ labels + if ( strncmp(symbolName, "L$start$", 8) == 0 ) + _hasDataInCodeLabels = true; continue; - + } // how many def syms in each section if ( sym.n_sect() > _machOSectionsCount ) throw "bad n_sect in symbol table"; @@ -1986,8 +2020,6 @@ void Parser::makeSections() _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC; else _file->_objConstraint = ld::File::objcConstraintRetainRelease; - if ( (flags & 1) == 1 ) - _file->_ojcReplacmentClass = true; if ( sect->size() > 8 ) { warning("section %s/%s has unexpectedly large size %llu in %s", sect->segname(), Section::makeSectionName(sect), sect->size(), _file->path()); @@ -5532,6 +5564,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati +#if SUPPORT_ARCH_arm_any template <> bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

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

& sym = parser.symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + // ignore non-definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // 'L' labels do not denote atom breaks + const char* symbolName = parser.nameFromSymbol(sym); + if ( symbolName[0] == 'L' ) { + if ( strncmp(symbolName, "L$start$", 8) == 0 ) { + ld::Fixup::Kind kind = ld::Fixup::kindNone; + if ( strncmp(&symbolName[8], "data$", 5) == 0 ) + kind = ld::Fixup::kindDataInCodeStartData; + else if ( strncmp(&symbolName[8], "code$", 5) == 0 ) + kind = ld::Fixup::kindDataInCodeEnd; + else if ( strncmp(&symbolName[8], "jt8$", 4) == 0 ) + kind = ld::Fixup::kindDataInCodeStartJT8; + else if ( strncmp(&symbolName[8], "jt16$", 4) == 0 ) + kind = ld::Fixup::kindDataInCodeStartJT16; + else if ( strncmp(&symbolName[8], "jt32$", 4) == 0 ) + kind = ld::Fixup::kindDataInCodeStartJT32; + else if ( strncmp(&symbolName[8], "jta32$", 4) == 0 ) + kind = ld::Fixup::kindDataInCodeStartJTA32; + else + warning("unknown L$start$ label %s in file %s", symbolName, this->file().path()); + if ( kind != ld::Fixup::kindNone ) { + Atom* inAtom = parser.findAtomByAddress(sym.n_value()); + typename Parser::SourceLocation src(inAtom, sym.n_value() - inAtom->objectAddress()); + parser.addFixup(src, ld::Fixup::k1of1, kind); + } + } + } + } + } + // add follow-on fixups for aliases if ( _hasAliases ) { for(Atom* p = _beginAtoms; p < _endAtoms; ++p) { @@ -6136,21 +6210,27 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI // main function used by linker to instantiate ld::Files // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts) + const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { switch ( opts.architecture ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: if ( mach_o::relocatable::Parser::validFile(fileContent) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( mach_o::relocatable::Parser::validFile(fileContent) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif } return NULL; } @@ -6229,6 +6309,17 @@ bool hasObjC2Categories(const uint8_t* fileContent) return false; } +// +// Used by archive reader when -ObjC option is specified +// +bool hasObjC1Categories(const uint8_t* fileContent) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::hasObjC1Categories(fileContent); + } + return false; +} + } // namespace relocatable diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index 6d0e25e..021c3f2 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -40,7 +40,7 @@ struct ParserOptions { }; extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, + const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts); extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts); @@ -49,6 +49,8 @@ extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_sub extern bool hasObjC2Categories(const uint8_t* fileContent); +extern bool hasObjC1Categories(const uint8_t* fileContent); + extern const char* archName(const uint8_t* fileContent); } // namespace relocatable diff --git a/ld64/src/ld/parsers/opaque_section_file.cpp b/ld64/src/ld/parsers/opaque_section_file.cpp index 660f66d..4098958 100644 --- a/ld64/src/ld/parsers/opaque_section_file.cpp +++ b/ld64/src/ld/parsers/opaque_section_file.cpp @@ -61,9 +61,9 @@ class File : public ld::File { public: File(const char* segmentName, const char* sectionName, const char* pth, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ord, + const uint8_t fileContent[], uint64_t fileLength, const char* symbolName="sect_create") - : ld::File(pth, 0, ord), + : ld::File(pth, 0, ld::File::Ordinal::NullOrdinal(), Other), _atom(*this, symbolName, fileContent, fileLength), _section(segmentName, sectionName, ld::Section::typeUnclassified) { } virtual ~File() { } @@ -90,10 +90,10 @@ Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) // main function used by linker for -sectcreate // ld::File* parse(const char* segmentName, const char* sectionName, const char* path, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, + const uint8_t fileContent[], uint64_t fileLength, const char* symbolName) { - return new File(segmentName, sectionName, path, fileContent, fileLength, ordinal, symbolName); + return new File(segmentName, sectionName, path, fileContent, fileLength, symbolName); } diff --git a/ld64/src/ld/parsers/opaque_section_file.h b/ld64/src/ld/parsers/opaque_section_file.h index 04db805..e105a07 100644 --- a/ld64/src/ld/parsers/opaque_section_file.h +++ b/ld64/src/ld/parsers/opaque_section_file.h @@ -32,7 +32,7 @@ namespace opaque_section { extern ld::File* parse(const char* segmentName, const char* sectionName, const char* path, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, + const uint8_t fileContent[], uint64_t fileLength, const char* symbolName="opaque_section"); diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 773031e..06953a7 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -412,9 +412,45 @@ void doPass(const Options& opts, ld::Internal& state) return; if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize); - // figure out how many regions of branch islands will be needed - const uint32_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section - const int kIslandRegionsCount = totalTextSize / kBetweenRegions; + // Figure out how many regions of branch islands will be needed, and their locations. + // Construct a vector containing the atoms after which branch islands will be inserted, + // taking into account follow on fixups. No atom run without an island can exceed kBetweenRegions. + const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section + std::vector branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted + uint64_t previousIslandEndAddr = 0; + const ld::Atom *insertionPoint; + branchIslandInsertionPoints.reserve(totalTextSize/kBetweenRegions*2); + for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { + const ld::Atom* atom = *it; + // if we move past the next atom, will the run length exceed kBetweenRegions? + if ( atom->sectionOffset() + atom->size() - previousIslandEndAddr > kBetweenRegions ) { + // yes. Add the last known good location (atom) for inserting a branch island. + if ( insertionPoint == NULL ) + throwf("Unable to insert branch island. No insertion point available."); + branchIslandInsertionPoints.push_back(insertionPoint); + previousIslandEndAddr = insertionPoint->sectionOffset()+insertionPoint->size(); + insertionPoint = NULL; + } + // Can we insert an island after this atom? If so then keep track of it. + if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) ) + insertionPoint = atom; + } + // add one more island after the last atom + if (insertionPoint != NULL) + branchIslandInsertionPoints.push_back(insertionPoint); + const int kIslandRegionsCount = branchIslandInsertionPoints.size(); + if (_s_log) { + fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + for (std::vector::iterator it = branchIslandInsertionPoints.begin(); it != branchIslandInsertionPoints.end(); ++it) { + const ld::Atom* atom = *it; + const ld::File *file = atom->file(); + fprintf(stderr, "ld: branch island will be inserted at 0x%llx after %s", atom->sectionOffset()+atom->size(), atom->name()); + if (file) fprintf(stderr, " (%s)", atom->file()->path()); + fprintf(stderr, "\n"); + } + } + + typedef std::map AtomToIsland; AtomToIsland* regionsMap[kIslandRegionsCount]; std::vector* regionsIslands[kIslandRegionsCount]; @@ -423,7 +459,6 @@ void doPass(const Options& opts, ld::Internal& state) regionsIslands[i] = new std::vector(); } unsigned int islandCount = 0; - if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); // create islands for branches in __text that are out of range for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { @@ -533,30 +568,25 @@ void doPass(const Options& opts, ld::Internal& state) if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); std::vector newAtomList; newAtomList.reserve(textSection->atoms.size()+islandCount); - uint64_t islandRegionAddr = kBetweenRegions;; - int regionIndex = 0; - for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { - const ld::Atom* atom = *it; - if ( (atom->sectionOffset()+atom->size()) > islandRegionAddr ) { - std::vector* regionIslands = regionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - const ld::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); - } - ++regionIndex; - islandRegionAddr += kBetweenRegions; + + uint64_t regionIndex = 0; + for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ait++) { + newAtomList.push_back(*ait); + // copy over atoms until we find an island insertion point + // Note that the last insertion point is the last atom, so this loop never moves the iterator to atoms.end(). + while (*ait != branchIslandInsertionPoints[regionIndex]) { + ait++; + newAtomList.push_back(*ait); } - newAtomList.push_back(atom); - } - // put any remaining islands at end of __text section - if ( regionIndex < kIslandRegionsCount ) { + + // insert the branch island atoms after the insertion point atom std::vector* regionIslands = regionsIslands[regionIndex]; for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { const ld::Atom* islandAtom = *rit; newAtomList.push_back(islandAtom); if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); } + regionIndex++; } // swap in new list of atoms for __text section textSection->atoms.clear(); diff --git a/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp index 7a2bd50..443f28e 100644 --- a/ld64/src/ld/passes/branch_shim.cpp +++ b/ld64/src/ld/passes/branch_shim.cpp @@ -328,7 +328,7 @@ void doPass(const Options& opts, ld::Internal& state) if ( pos == thumbToAtomMap.end() ) { if ( opts.archSupportsThumb2() ) { // make long-branch style shims for arm kexts - if ( makingKextBundle ) + if ( makingKextBundle && opts.allowTextRelocs() ) shim = new NoPICThumb2ToArmShimAtom(target, *sect); else shim = new Thumb2ToArmShimAtom(target, *sect); @@ -365,7 +365,7 @@ void doPass(const Options& opts, ld::Internal& state) std::map::iterator pos = atomToThumbMap.find(target); if ( pos == atomToThumbMap.end() ) { // make long-branch style shims for arm kexts - if ( makingKextBundle ) + if ( makingKextBundle && opts.allowTextRelocs() ) shim = new NoPICARMtoThumbShimAtom(target, *sect); else shim = new ARMtoThumbShimAtom(target, *sect); diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 86c8eec..858b40f 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -774,12 +774,16 @@ static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::In // create atom that contains the whole compact unwind table switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; +#endif default: assert(0 && "no compact unwind for arch"); } @@ -876,12 +880,16 @@ static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, cons uint32_t startOffset, uint32_t endOffset, uint32_t cui) { switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; +#endif } } diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index 665e3ae..02055f3 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -77,9 +77,9 @@ class File : public ld::File { public: File(const char* segmentName, const char* sectionName, const char* pth, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ord, + const uint8_t fileContent[], uint64_t fileLength, Ordinal ord, const char* symbolName="dof") - : ld::File(pth, 0, ord), + : ld::File(pth, 0, ord, Other), _atom(*this, symbolName, fileContent, fileLength), _section(segmentName, sectionName, ld::Section::typeDtraceDOF) { } virtual ~File() {} @@ -299,7 +299,7 @@ void doPass(const Options& opts, ld::Internal& internal) sectionNamesUsed.insert(sectionName); char symbolName[strlen(providerName)+64]; sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); - File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, 0, symbolName); + File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, ld::File::Ordinal::NullOrdinal(), symbolName); if ( log ) { fprintf(stderr, "libdtrace created DOF of size %ld\n", dofSectionSize); } diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index b209b1d..cf6f1d4 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -50,7 +50,6 @@ struct objc_image_info { uint32_t flags; }; -#define OBJC_IMAGE_IS_REPLACEMENT (1<<0) #define OBJC_IMAGE_SUPPORTS_GC (1<<1) #define OBJC_IMAGE_REQUIRES_GC (1<<2) #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) @@ -65,7 +64,7 @@ template class ObjCImageInfoAtom : public ld::Atom { public: ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, - bool compaction, bool objcReplacementClasses, bool abi2); + bool compaction, bool abi2); virtual const ld::File* file() const { return NULL; } virtual bool translationUnitSource(const char** dir, const char**) const @@ -91,15 +90,13 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, - bool objcReplacementClasses, bool abi2) + bool abi2) : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { uint32_t value = 0; - if ( objcReplacementClasses ) - value = OBJC_IMAGE_IS_REPLACEMENT; switch ( objcConstraint ) { case ld::File::objcConstraintNone: case ld::File::objcConstraintRetainRelease: @@ -143,7 +140,7 @@ class MethodListAtom : public ld::Atom { virtual void setScope(Scope) { } virtual void copyRawContent(uint8_t buffer[]) const { bzero(buffer, size()); - A::P::E::set32(*((uint32_t*)(&buffer[0])), 24); + A::P::E::set32(*((uint32_t*)(&buffer[0])), 3*sizeof(pint_t)); // entry size A::P::E::set32(*((uint32_t*)(&buffer[4])), _methodCount); } virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } @@ -775,6 +772,35 @@ class OptimizedAway { const std::set& _dead; }; + struct AtomSorter + { + bool operator()(const Atom* left, const Atom* right) + { + // sort by file ordinal, then object address, then zero size, then symbol name + // only file based atoms are supported (file() != NULL) + if (left==right) return false; + const File *leftf = left->file(); + const File *rightf = right->file(); + + if (leftf == rightf) { + if (left->objectAddress() != right->objectAddress()) { + return left->objectAddress() < right->objectAddress(); + } else { + // for atoms in the same file with the same address, zero sized + // atoms must sort before nonzero sized atoms + if ((left->size() == 0 && right->size() > 0) || (left->size() > 0 && right->size() == 0)) + return left->size() < right->size(); + return strcmp(left->name(), right->name()); + } + } + return (leftf->ordinal() < rightf->ordinal()); + } + }; + + static void sortAtomVector(std::vector &atoms) { + std::sort(atoms.begin(), atoms.end(), AtomSorter()); + } + template void OptimizeCategories::doit(const Options& opts, ld::Internal& state) { @@ -798,6 +824,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // build map of all classes in this image that have categories on them typedef std::map*> CatMap; CatMap classToCategories; + std::vector classOrder; std::set deadAtoms; ld::Internal::FinalSection* methodListSection = NULL; for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { @@ -813,7 +840,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) continue; } assert(categoryAtom != NULL); - assert(categoryAtom->size() == Category::size()); + assert(categoryAtom->size() >= Category::size()); // ignore categories also in __objc_nlcatlist if ( nlcatListAtoms.count(categoryAtom) != 0 ) continue; @@ -824,6 +851,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); if ( pos == classToCategories.end() ) { classToCategories[categoryOnClassAtom] = new std::vector(); + classOrder.push_back(categoryOnClassAtom); } classToCategories[categoryOnClassAtom]->push_back(categoryAtom); // mark category atom and catlist atom as dead @@ -840,10 +868,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // if found some categories if ( classToCategories.size() != 0 ) { assert(methodListSection != NULL); + sortAtomVector(classOrder); // alter each class definition to have new method list which includes all category methods - for (CatMap::iterator it=classToCategories.begin(); it != classToCategories.end(); ++it) { - const ld::Atom* classAtom = it->first; - const std::vector* categories = it->second; + for (std::vector::iterator it = classOrder.begin(); it != classOrder.end(); it++) { + const ld::Atom* classAtom = *it; + const std::vector* categories = classToCategories[classAtom]; assert(categories->size() != 0); // if any category adds instance methods, generate new merged method list, and replace if ( OptimizeCategories::hasInstanceMethods(state, categories) ) { @@ -1145,17 +1174,21 @@ void doPass(const Options& opts, ld::Internal& state) // add image info atom switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); + true)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false)); + opts.objCABIVersion2POverride() ? true : false)); break; +#endif case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); + true)); break; default: assert(0 && "unknown objc arch"); @@ -1165,18 +1198,24 @@ void doPass(const Options& opts, ld::Internal& state) if ( opts.objcCategoryMerging() ) { // optimize classes defined in this linkage unit by merging in categories also in this linkage unit switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: // disable optimization until fully tested - //if ( opts.objCABIVersion2POverride() ) - // OptimizeCategories::doit(opts, state); + if ( opts.objCABIVersion2POverride() ) + OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: // disable optimization until fully tested - //OptimizeCategories::doit(opts, state); + OptimizeCategories::doit(opts, state); break; +#endif default: assert(0 && "unknown objc arch"); } diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index ce17a35..6876d8f 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -193,8 +193,9 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) // sort by .o order const ld::File* leftFile = left->file(); const ld::File* rightFile = right->file(); - uint32_t leftFileOrdinal = (leftFile != NULL) ? leftFile->ordinal() : 0; - uint32_t rightFileOrdinal = (rightFile != NULL) ? rightFile->ordinal() : 0; + // properly sort if on file is NULL and the other is not + ld::File::Ordinal leftFileOrdinal = (leftFile != NULL) ? leftFile->ordinal() : ld::File::Ordinal::NullOrdinal(); + ld::File::Ordinal rightFileOrdinal = (rightFile != NULL) ? rightFile->ordinal() : ld::File::Ordinal::NullOrdinal(); if ( leftFileOrdinal != rightFileOrdinal ) return leftFileOrdinal< rightFileOrdinal; diff --git a/ld64/src/ld/passes/stubs/stub_arm.hpp b/ld64/src/ld/passes/stubs/stub_arm.hpp index e8dedda..4650ddb 100644 --- a/ld64/src/ld/passes/stubs/stub_arm.hpp +++ b/ld64/src/ld/passes/stubs/stub_arm.hpp @@ -287,6 +287,95 @@ ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section ld::Section LazyPointerAtom::_s_sectionClose("__DATA", "__lazy_symbol", ld::Section::typeLazyPointerClose); +class NonLazyPointerAtom : public ld::Atom { +public: + NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &stubTo) { + pass.addAtom(*this); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + + static ld::Section _s_section; + static ld::Section _s_sectionClose; +}; + +ld::Section NonLazyPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class StubPICKextAtom : public ld::Atom { +public: + StubPICKextAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableIn, false, true, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _nonLazyPointer(pass, stubTo), + _fixup1(0, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_nonLazyPointer), + _fixup2(0, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(0, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(0, ld::Fixup::k4of4, ld::Fixup::kindStoreThumbLow16), + _fixup5(4, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_nonLazyPointer), + _fixup6(4, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup7(4, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup8(4, ld::Fixup::k4of4, ld::Fixup::kindStoreThumbHigh16) { + pass.addAtom(*this); + asprintf((char**)&_name, "%s.stub", _stubTo.name()); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0x0c00f240); // movw ip, #lo(nlp - L1) + OSWriteLittleInt32(&buffer[ 4], 0, 0x0c00f2c0); // movt ip, #hi(nlp - L1) + OSWriteLittleInt16(&buffer[ 8], 0, 0x44fc); // add ip, pc + OSWriteLittleInt32(&buffer[10], 0, 0xc000f8dc); // ldr.w ip, [ip] + OSWriteLittleInt16(&buffer[14], 0, 0x4760); // bx ip + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } + +private: + const ld::Atom& _stubTo; + const char* _name; + NonLazyPointerAtom _nonLazyPointer; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + ld::Fixup _fixup6; + ld::Fixup _fixup7; + ld::Fixup _fixup8; + + static ld::Section _s_section; +}; + +ld::Section StubPICKextAtom::_s_section("__TEXT", "__stub", ld::Section::typeCode); + + class StubPICAtom : public ld::Atom { public: diff --git a/ld64/src/ld/passes/stubs/stub_x86_64.hpp b/ld64/src/ld/passes/stubs/stub_x86_64.hpp index e74b37d..e3cebca 100644 --- a/ld64/src/ld/passes/stubs/stub_x86_64.hpp +++ b/ld64/src/ld/passes/stubs/stub_x86_64.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -362,5 +362,78 @@ ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + +class NonLazyPointerAtom : public ld::Atom { +public: + NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) { + pass.addAtom(*this); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + + static ld::Section _s_section; +}; + +ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + + +class KextStubAtom : public ld::Atom { +public: + KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _nonLazyPointer(pass, stubTo), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_nonLazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$non_lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + NonLazyPointerAtom _nonLazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + } // namespace x86_64 diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index aa4aaf1..5ea1b05 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -36,6 +36,7 @@ #include "Options.h" #include "ld.hpp" +#include "MachOFileAbstraction.hpp" #include "make_stubs.h" @@ -118,6 +119,7 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: + assert(target != NULL); // create stub if target is in a dylib if ( target->definition() == ld::Atom::definitionProxy ) return target; @@ -175,20 +177,31 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) } switch ( _architecture ) { +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( usingCompressedLINKEDIT() && !forLazyDylib ) return new ld::passes::stubs::x86::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); else return new ld::passes::stubs::x86::classic::StubAtom(*this, target, forLazyDylib, weakImport); break; +#endif +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: - if ( usingCompressedLINKEDIT() && !forLazyDylib ) + if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) + return new ld::passes::stubs::x86_64::KextStubAtom(*this, target); + else if ( usingCompressedLINKEDIT() && !forLazyDylib ) return new ld::passes::stubs::x86_64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); else return new ld::passes::stubs::x86_64::classic::StubAtom(*this, target, forLazyDylib, weakImport); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - if ( usingCompressedLINKEDIT() && !forLazyDylib ) { + if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) { + // if text relocs are not allows in kext bundles, then linker must create a stub + return new ld::passes::stubs::arm::StubPICKextAtom(*this, target); + } + else if ( usingCompressedLINKEDIT() && !forLazyDylib ) { if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText ) return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); else if ( _pic ) @@ -203,6 +216,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport); } break; +#endif } throw "unsupported arch for stub"; } @@ -226,7 +240,6 @@ void Pass::process(ld::Internal& state) case Options::kObjectFile: // these kinds don't use stubs and can have resolver functions return; - case Options::kKextBundle: case Options::kStaticExecutable: case Options::kPreload: case Options::kDyld: @@ -236,6 +249,12 @@ void Pass::process(ld::Internal& state) case Options::kDynamicLibrary: // uses stubs and can have resolver functions break; + case Options::kKextBundle: + verifyNoResolverFunctions(state); + // if kext don't use stubs, don't do this pass + if ( !_options.kextsUseStubs() ) + return; + break; case Options::kDynamicExecutable: case Options::kDynamicBundle: // these kinds do use stubs and cannot have resolver functions @@ -302,6 +321,15 @@ void Pass::process(ld::Internal& state) } } } + + const bool needStubForMain = _options.needsEntryPointLoadCommand() + && (state.entryPoint != NULL) + && (state.entryPoint->definition() == ld::Atom::definitionProxy); + if ( needStubForMain ) { + // _main not found in any .o files. Currently have proxy to dylib + // Add to map, so that a stub will be made + stubFor[state.entryPoint] = NULL; + } // short circuit if no stubs needed _internal = &state; @@ -310,7 +338,7 @@ void Pass::process(ld::Internal& state) return; // lazily check for helper - if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) ) + if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) && (_options.outputKind() != Options::kKextBundle) ) throw "symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o"; // disable arm close stubs in some cases @@ -357,6 +385,13 @@ void Pass::process(ld::Internal& state) } } + // switch entry point from proxy to stub + if ( needStubForMain ) { + const ld::Atom* mainStub = stubFor[state.entryPoint]; + assert(mainStub != NULL); + state.entryPoint = mainStub; + } + // sort new atoms so links are consistent for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 44d8f53..55e1068 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -830,7 +830,25 @@ void dumper::dumpFixup(const ld::Fixup* ref) break; case ld::Fixup::kindSetLazyOffset: printf("offset of lazy binding info for %s", referenceTargetAtomName(ref)); - break; + break; + case ld::Fixup::kindDataInCodeStartData: + printf("start of data in code"); + break; + case ld::Fixup::kindDataInCodeStartJT8: + printf("start of jump table 8 data in code"); + break; + case ld::Fixup::kindDataInCodeStartJT16: + printf("start of jump table 16 data in code"); + break; + case ld::Fixup::kindDataInCodeStartJT32: + printf("start of jump table 32 data in code"); + break; + case ld::Fixup::kindDataInCodeStartJTA32: + printf("start of jump table absolute 32 data in code"); + break; + case ld::Fixup::kindDataInCodeEnd: + printf("end of data in code"); + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref)); break; @@ -1110,12 +1128,12 @@ static ld::relocatable::File* createReader(const char* path) } } - ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts); + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), objOpts); if ( objResult != NULL ) return objResult; // see if it is an llvm object file - objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, 0, sPreferredArch, sPreferredSubArch, false); + objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, sPreferredArch, sPreferredSubArch, false); if ( objResult != NULL ) return objResult; @@ -1183,24 +1201,20 @@ int main(int argc, const char* argv[]) sShowLineInfo = false; } else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = ++isubTypeName != NULL; ++t) { - if ( strcmp(t->subTypeName,arch) == 0 ) { - sPreferredArch = CPU_TYPE_ARM; - sPreferredSubArch = t->subType; - found = true; - break; - } + const char* archName = argv[++i]; + if ( archName == NULL ) + throw "-arch missing architecture name"; + bool found = false; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( strcmp(t->archName,archName) == 0 ) { + sPreferredArch = t->cpuType; + if ( t->isSubType ) + sPreferredSubArch = t->cpuSubType; + found = true; } - if ( !found ) - throwf("unknown architecture %s", arch); } + if ( !found ) + throwf("unknown architecture %s", archName); } else if ( strcmp(arg, "-only") == 0 ) { sMatchName = ++i* fInfo; const macho_linkedit_data_command

* fSharedRegionInfo; const macho_linkedit_data_command

* fFunctionStartsInfo; + const macho_linkedit_data_command

* fDataInCode; + const macho_linkedit_data_command

* fDRInfo; uint64_t fBaseAddress; const macho_dysymtab_command

* fDynamicSymbolTable; const macho_segment_command

* fFirstSegment; @@ -166,6 +173,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) switch (header->filetype()) { case MH_EXECUTE: case MH_DYLIB: + case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: return true; @@ -184,6 +192,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) switch (header->filetype()) { case MH_EXECUTE: case MH_DYLIB: + case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: return true; @@ -202,6 +211,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) switch (header->filetype()) { case MH_EXECUTE: case MH_DYLIB: + case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: return true; @@ -220,6 +230,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) switch (header->filetype()) { case MH_EXECUTE: case MH_DYLIB: + case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: return true; @@ -227,6 +238,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) return false; } +#if SUPPORT_ARCH_arm_any template <> bool DyldInfoPrinter::validFile(const uint8_t* fileContent) { @@ -238,18 +250,20 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) switch (header->filetype()) { case MH_EXECUTE: case MH_DYLIB: + case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: return true; } return false; } +#endif template DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch) : fHeader(NULL), fLength(fileLength), fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), - fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL), + fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL), fDataInCode(NULL), fDRInfo(NULL), fBaseAddress(0), fDynamicSymbolTable(NULL), fFirstSegment(NULL), fFirstWritableSegment(NULL), fWriteableSegmentWithAddrOver4G(false) { @@ -332,32 +346,26 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen case LC_FUNCTION_STARTS: fFunctionStartsInfo = (macho_linkedit_data_command

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

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

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

*)endOfCmd; } if ( printArch ) { - switch ( fHeader->cputype() ) { - case CPU_TYPE_I386: - printf("for arch i386:\n"); - break; - case CPU_TYPE_X86_64: - printf("for arch x86_64:\n"); - break; - case CPU_TYPE_POWERPC: - printf("for arch ppc:\n"); - break; - case CPU_TYPE_ARM: - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( (cpu_subtype_t)fHeader->cpusubtype() == t->subType) { - printf("for arch %s:\n", t->subTypeName); - break; - } - } + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (cpu_type_t)fHeader->cputype() == t->cpuType ) { + if ( t->isSubType && ((cpu_subtype_t)fHeader->cpusubtype() != t->cpuSubType) ) + continue; + printf("for arch %s:\n", t->archName); + } } } - if ( printRebase ) { if ( fInfo != NULL ) printRebaseInfo(); @@ -400,6 +408,10 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen printFunctionStartsInfo(); if ( printDylibs ) printDylibsInfo(); + if ( printDRs ) + printDRInfo(); + if ( printDataCode ) + printDataInCode(); } static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) @@ -432,7 +444,7 @@ static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) if (p == end) throwf("malformed sleb128"); byte = *p++; - result |= ((byte & 0x7f) << bit); + result |= (((int64_t)(byte & 0x7f)) << bit); bit += 7; } while (byte & 0x80); // sign extend negative numbers @@ -1545,6 +1557,7 @@ void DyldInfoPrinter::printSharedRegionInfo() } } +#if SUPPORT_ARCH_arm_any template <> void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) { @@ -1553,6 +1566,7 @@ void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) else printf("0x%0llX %s\n", addr, symbolNameForAddress(addr)); } +#endif template void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) @@ -1617,6 +1631,85 @@ void DyldInfoPrinter::printDylibsInfo() } } +template +void DyldInfoPrinter::printDRInfo() +{ + if ( fDRInfo == NULL ) { + printf("no Designated Requirements info\n"); + } + else { + printf("dylibs DRs\n"); + const uint8_t* start = ((uint8_t*)fHeader + fDRInfo->dataoff()); + //const uint8_t* end = ((uint8_t*)fHeader + fDRInfo->dataoff() + fDRInfo->datasize()); + typedef Security::SuperBlob DRListSuperBlob; + typedef Security::SuperBlob InternalRequirementsSetBlob; + const DRListSuperBlob* topBlob = (DRListSuperBlob*)start; + if ( topBlob->validateBlob(fDRInfo->datasize()) ) { + if ( topBlob->count() == fDylibLoadCommands.size() ) { + for(unsigned i=0; i < topBlob->count(); ++i) { + printf(" %-20s ", fDylibs[i]); + const Security::BlobCore* item = topBlob->find(i); + if ( item != NULL ) { + const uint8_t* itemStart = (uint8_t*)item; + const uint8_t* itemEnd = itemStart + item->length(); + for(const uint8_t* p=itemStart; p < itemEnd; ++p) + printf("%02X ", *p); + } + else { + printf("no DR info"); + } + printf("\n"); + } + } + else { + fprintf(stderr, "superblob of DRs has a different number of elements than dylib load commands\n"); + } + } + else { + fprintf(stderr, "superblob of DRs invalid\n"); + } + } +} + + + + + +template +void DyldInfoPrinter::printDataInCode() +{ + if ( fDataInCode == NULL ) { + printf("no data-in-code info\n"); + } + else { + printf("offset length data-kind\n"); + const macho_data_in_code_entry

* start = (macho_data_in_code_entry

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

* end = (macho_data_in_code_entry

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

* p=start; p < end; ++p) { + const char* kindStr = "???"; + switch ( p->kind() ) { + case 1: + kindStr = "data"; + break; + case 2: + kindStr = "jumptable8"; + break; + case 3: + kindStr = "jumptable16"; + break; + case 4: + kindStr = "jumptable32"; + break; + case 5: + kindStr = "jumptable32absolute"; + break; + } + printf("0x%08X 0x%04X %s\n", p->offset(), p->length(), kindStr); + } + } +} + + template <> ppc::P::uint_t DyldInfoPrinter::relocBase() @@ -1652,6 +1745,7 @@ x86_64::P::uint_t DyldInfoPrinter::relocBase() return fFirstWritableSegment->vmaddr(); } +#if SUPPORT_ARCH_arm_any template <> arm::P::uint_t DyldInfoPrinter::relocBase() { @@ -1660,6 +1754,7 @@ arm::P::uint_t DyldInfoPrinter::relocBase() else return fFirstSegment->vmaddr(); } +#endif template <> @@ -1700,6 +1795,7 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) return "??"; } +#if SUPPORT_ARCH_arm_any template <> const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) { @@ -1710,7 +1806,7 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) else return "??"; } - +#endif template void DyldInfoPrinter::printRelocRebaseInfo() @@ -2001,12 +2097,14 @@ static void dump(const char* path) else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( DyldInfoPrinter::validFile(p + offset) ) DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); else throw "in universal file, arm slice does not contain arm mach-o"; break; +#endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } @@ -2025,9 +2123,11 @@ static void dump(const char* path) else if ( DyldInfoPrinter::validFile(p) ) { DyldInfoPrinter::make(p, length, path, false); } +#if SUPPORT_ARCH_arm_any else if ( DyldInfoPrinter::validFile(p) ) { DyldInfoPrinter::make(p, length, path, false); } +#endif else { throw "not a known file type"; } @@ -2041,6 +2141,7 @@ static void usage() { fprintf(stderr, "Usage: dyldinfo [-arch ] \n" "\t-dylibs print dependent dylibs\n" + "\t-dr print dependent dylibs and show any recorded DR info\n" "\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n" "\t-bind print addresses dyld will set based on symbolic lookups\n" "\t-weak_bind print symbols which dyld must coalesce\n" @@ -2049,6 +2150,7 @@ static void usage() "\t-opcodes print opcodes used to generate the rebase and binding information\n" "\t-function_starts print table of function start addresses\n" "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n" + "\t-data_in_code print any data-in-code inforamtion\n" ); } @@ -2076,17 +2178,19 @@ int main(int argc, const char* argv[]) else if ( strcmp(arch, "x86_64") == 0 ) sPreferredArch = CPU_TYPE_X86_64; else { + const char* archName = argv[++i]; + if ( archName == NULL ) + throw "-arch missing architecture name"; bool found = false; - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( strcmp(t->subTypeName,arch) == 0 ) { - sPreferredArch = CPU_TYPE_ARM; - sPreferredSubArch = t->subType; - found = true; - break; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( strcmp(t->archName,archName) == 0 ) { + sPreferredArch = t->cpuType; + if ( t->isSubType ) + sPreferredSubArch = t->cpuSubType; } } if ( !found ) - throwf("unknown architecture %s", arch); + throwf("unknown architecture %s", archName); } } else if ( strcmp(arg, "-rebase") == 0 ) { @@ -2122,6 +2226,12 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-dylibs") == 0 ) { printDylibs = true; } + else if ( strcmp(arg, "-dr") == 0 ) { + printDRs = true; + } + else if ( strcmp(arg, "-data_in_code") == 0 ) { + printDataCode = true; + } else { throwf("unknown option: %s\n", arg); } diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 54dc560..36a71fd 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -388,6 +388,7 @@ void MachOChecker::checkLoadCommands() // check that all load commands fit within the load command space file const macho_encryption_info_command

* encryption_info = NULL; const macho_thread_command

* threadInfo = NULL; + const macho_entry_point_command

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

) + fHeader->sizeofcmds(); const uint32_t cmd_count = fHeader->ncmds(); @@ -424,8 +425,12 @@ void MachOChecker::checkLoadCommands() case LC_LOAD_UPWARD_DYLIB: case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: - case LC_FUNCTION_STARTS: case LC_RPATH: + case LC_FUNCTION_STARTS: + case LC_DYLD_ENVIRONMENT: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + case LC_SOURCE_VERSION: break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: @@ -439,6 +444,11 @@ void MachOChecker::checkLoadCommands() if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS ) throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; break; + case LC_MAIN: + if ( fHeader->filetype() != MH_EXECUTE ) + throw "LC_MAIN can only be used in MH_EXECUTE file types"; + entryPoint = (macho_entry_point_command

*)cmd; + break; case LC_UNIXTHREAD: if ( fHeader->filetype() != MH_EXECUTE ) throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types"; @@ -590,7 +600,11 @@ void MachOChecker::checkLoadCommands() if ( (initialPC < fTEXTSegment->vmaddr()) || (initialPC >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) ) throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC); } - + else if ( entryPoint != NULL ) { + pint_t initialOffset = entryPoint->entryoff(); + if ( (initialOffset < fTEXTSegment->fileoff()) || (initialOffset >= (fTEXTSegment->fileoff()+fTEXTSegment->filesize())) ) + throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset); + } // checks for executables bool isStaticExecutable = false; @@ -607,7 +621,7 @@ void MachOChecker::checkLoadCommands() cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } if ( isStaticExecutable ) { - if ( fHeader->flags() != MH_NOUNDEFS ) + if ( (fHeader->flags() != MH_NOUNDEFS) && (fHeader->flags() != (MH_NOUNDEFS|MH_PIE)) ) throw "invalid bits in mach_header flags for static executable"; } } @@ -661,7 +675,7 @@ void MachOChecker::checkLoadCommands() break; case LC_DYSYMTAB: { - if ( isStaticExecutable ) + if ( isStaticExecutable &&! fSlidableImage ) throw "LC_DYSYMTAB should not be used in static executable"; foundDynamicSymTab = true; fDynamicSymbolTable = (macho_dysymtab_command

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

* info = (macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "data-in-code data not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "data-in-code data not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "data-in-code data table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "data-in-code data size not a multiple of pointer size"; + } + break; + case LC_DYLIB_CODE_SIGN_DRS: + { + const macho_linkedit_data_command

* info = (macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "dependent dylib DR data not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "dependent dylib DR data not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "dependent dylib DR data table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "dependent dylib DR data size not a multiple of pointer size"; + } + break; } cmd = (const macho_load_command

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

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

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

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

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

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

* relo throw "local relocation address not in writable segment"; } } +#endif template void MachOChecker::checkRelocations() @@ -1273,6 +1317,7 @@ bool MachOChecker::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) } } } + return false; } template @@ -1518,12 +1563,14 @@ static void check(const char* path) else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( MachOChecker::validFile(p + offset) ) MachOChecker::make(p + offset, size, path); else throw "in universal file, arm slice does not contain arm mach-o"; break; +#endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } @@ -1541,9 +1588,11 @@ static void check(const char* path) else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#if SUPPORT_ARCH_arm_any else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#endif else { throw "not a known file type"; } diff --git a/ld64/src/other/rebase.cpp b/ld64/src/other/rebase.cpp index 2255789..d1e3194 100644 --- a/ld64/src/other/rebase.cpp +++ b/ld64/src/other/rebase.cpp @@ -674,6 +674,7 @@ void Rebaser::doLocalRelocation(const macho_relocation_info

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

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

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

* reloc) @@ -1008,27 +1010,18 @@ int main(int argc, const char* argv[]) highAddress = strtoull(argv[++i], &endptr, 16); } else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = argv[++i]; - if ( strcmp(arch, "ppc") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC); - else if ( strcmp(arch, "ppc64") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC64); - else if ( strcmp(arch, "i386") == 0 ) - onlyArchs.insert(CPU_TYPE_I386); - else if ( strcmp(arch, "x86_64") == 0 ) - onlyArchs.insert(CPU_TYPE_X86_64); - else { - bool found = false; - for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) { - if ( strcmp(t->subTypeName,arch) == 0 ) { - onlyArchs.insert(CPU_TYPE_ARM); - found = true; - break; - } + const char* archName = argv[++i]; + if ( archName == NULL ) + throw "-arch missing architecture name"; + bool found = false; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( strcmp(t->archName,archName) == 0 ) { + onlyArchs.insert(t->cpuType); + found = true; } - if ( !found ) - throwf("unknown architecture %s", arch); } + if ( !found ) + throwf("unknown architecture %s", archName); } else { usage(); diff --git a/ld64/unit-tests/bin/make-recursive.pl b/ld64/unit-tests/bin/make-recursive.pl index f860985..0d681ba 100755 --- a/ld64/unit-tests/bin/make-recursive.pl +++ b/ld64/unit-tests/bin/make-recursive.pl @@ -4,6 +4,7 @@ use Data::Dumper; use File::Find; use Cwd qw(realpath); +use English; my @args = @ARGV; @@ -31,6 +32,17 @@ 'stderr' => [], }; +# Determine how many tests to run at a time in parallel. Default to cpu count. +my $max_concurrent_tests = $ENV{'LD_UNIT_TEST_CONCURRENCY'}; +if (!defined $max_concurrent_tests) { + # shell command returns cpu count in exit status + system("/bin/csh", "-c", "set n=`sysctl hw.ncpu`; exit \$n[2]"); + if ($? == -1 || $? & 127) { + die("could not determine cpu count"); + } + $max_concurrent_tests = $? >> 8; +} + my $keyword; my $max_keyword_len = 0; foreach $keyword (keys %$keywords) @@ -42,82 +54,117 @@ sub print_line { - my ($keyword, $val) = @_; - + my ($file, $keyword, $val) = @_; + if(!exists($$keywords{$keyword})) { - print STDERR "error: keyword $keyword not in \$keywords set\n"; - exit(1); + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); } - + my $keyword_len = 0; - + if($keyword ne $last_keyword) { - print("$keyword"); print($delim); - $keyword_len = length($keyword) + length($delim); + print($file "$keyword"); print($file $delim); + $keyword_len = length($keyword) + length($delim); } if($max_keyword_len > $keyword_len) { - my $num_spaces = $max_keyword_len - $keyword_len; - print(' ' x $num_spaces); + my $num_spaces = $max_keyword_len - $keyword_len; + print($file ' ' x $num_spaces); } - print("$val"); + print($file "$val"); if(0) { - $last_keyword = $keyword; + $last_keyword = $keyword; } } my $root = '.'; $root = &realpath($root); -print_line("root", "$root\n"); - +print_line(*STDOUT, "root", "$root\n"); +my $running_test_count=0; find($find_opts, $root); +while ( $running_test_count > 0 ) { + &reaper; +} sub find_callback { if(exists($$makefiles{$_})) { - my $makefile = $_; - my $reldir = $File::Find::dir; - $reldir =~ s|^$root/||; - - &print_line("cwd", "\$root/$reldir\n"); - my $cmd = [ "make" ]; - - my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? - &print_line("cmd", "@$cmd\n"); - - open(SAVEOUT, ">&STDOUT") || die("$!"); - open(SAVEERR, ">&STDERR") || die("$!"); - open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); - open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); - - $ENV{UNIT_TEST_NAME} = $reldir; - my $exit = system(@$cmd); - - close(STDOUT) || die("$!"); - close(STDERR) || die("$!"); - open(STDOUT, ">&SAVEOUT") || die("$!"); - open(STDERR, ">&SAVEERR") || die("$!"); - - &print_line("exit", "$exit\n"); + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + my $cmd = [ "make" ]; + + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + + $ENV{UNIT_TEST_NAME} = $reldir; + my $pid = fork(); + if (not defined $pid) { + die "Couldn't fork" + } + elsif ($pid == 0) { + # Child. Redirect stdout/stderr to files and exec test. + open(STDOUT, ">/tmp/unit-tests-stdout.$PID") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr.$PID") || die("$!"); + exec 'make', @ARGV; + exit(-1); #just to be sure + } + + # Write the test cwd/cmd to a temporary file associated with the child's pid, to be retrieved later. + my $info; + open($info, ">/tmp/unit-tests-info.$pid") || die("$!"); + &print_line($info, "cwd", "\$root/$reldir\n"); # post filtering depends on this line being first + &print_line($info, "cmd", "@$cmd\n"); + close($info) || die("$!"); + + $running_test_count++; + # if we have reached max # of concurrent tests, wait for one to exit + if ( $running_test_count == $max_concurrent_tests ) { + &reaper; + } + } +} - open(OUT, ") - { - &print_line("stdout", "$_"); - } - close(OUT) || die("$!"); - unlink("/tmp/unit-tests-stdout"); - - open(ERR, ") - { - &print_line("stderr", "$_"); +sub reaper { + if ( $running_test_count > 0 ) { + my $pid = wait; + if ( $pid == -1 ) { + die("no child\n"); + } + my $exit = $?; + + $running_test_count--; + + open(INFO, ") + { + print $_; + } + close(INFO) || die("$!"); + unlink("/tmp/unit-tests-info.$pid"); + + &print_line(*STDOUT, "exit", "$exit\n"); + + open(OUT, ") + { + &print_line(*STDOUT, "stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout.$pid"); + + open(ERR, ") + { + &print_line(*STDOUT, "stderr", "$_"); + } + close(ERR) || die("$!"); + unlink("/tmp/unit-tests-stderr.$pid"); } - close(ERR) || die("$!"); - } - unlink("/tmp/unit-tests-stderr"); } + diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index adb5468..97864ca 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -8,7 +8,6 @@ ARCH ?= $(shell arch) # set default to be all VALID_ARCHS ?= "i386 x86_64 armv6" -IOS_SDK = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.Internal.sdk MYDIR=$(shell cd ../../bin;pwd) LD = ld @@ -56,21 +55,23 @@ ifeq ($(ARCH),ppc) SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk endif -CC = cc -arch ${ARCH} ${SDKExtra} +CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} CCFLAGS = -Wall ASMFLAGS = VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6 VERSION_OLD_LINKEDIT = -mmacosx-version-min=10.4 LD_NEW_LINKEDIT = -macosx_version_min 10.6 -CXX = c++ -arch ${ARCH} ${SDKExtra} +CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall +IOS_SDK = $(shell xcodebuild -sdk iphoneos6.0.internal -version Path) + ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) override FILEARCH = arm - CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) - CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) @@ -82,8 +83,8 @@ endif ifeq ($(ARCH),armv7) LDFLAGS := -syslibroot $(IOS_SDK) override FILEARCH = arm - CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) - CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) @@ -98,8 +99,8 @@ ifeq ($(ARCH),thumb) CXXFLAGS += -mthumb override ARCH = armv6 override FILEARCH = arm - CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) - CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) @@ -114,8 +115,8 @@ ifeq ($(ARCH),thumb2) CXXFLAGS += -mthumb override ARCH = armv7 override FILEARCH = arm - CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) - CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) diff --git a/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests index c07c947..9285128 100755 --- a/ld64/unit-tests/run-all-unit-tests +++ b/ld64/unit-tests/run-all-unit-tests @@ -15,7 +15,7 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` all_archs="x86_64 i386" valid_archs="x86_64 i386" # only test arm code if iOS platform tools installed -if [ -d /Developer/Platforms/iPhoneOS.platform/Developer/SDKs ] +if [ -d /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs ] then all_archs="${all_archs} armv7" valid_archs="${valid_archs} armv7" diff --git a/ld64/unit-tests/test-cases/16-byte-alignment/Makefile b/ld64/unit-tests/test-cases/16-byte-alignment/Makefile index e178ddb..533e222 100644 --- a/ld64/unit-tests/test-cases/16-byte-alignment/Makefile +++ b/ld64/unit-tests/test-cases/16-byte-alignment/Makefile @@ -33,11 +33,11 @@ all: ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o # verify that the alignment is correct in the .o - ${OBJECTDUMP} -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null + ${OBJECTDUMP} -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '0 mod 16' >/dev/null # now verify the executable ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH} - ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai\>' >/dev/null" + ${FAIL_IF_ERROR} sh -c "nm tl_test2-${ARCH}|grep '0 D _ai' >/dev/null" ${PASS_IFF_GOOD_MACHO} tl_test2-${ARCH} clean: diff --git a/ld64/unit-tests/test-cases/archive-r-ObjC/Makefile b/ld64/unit-tests/test-cases/archive-r-ObjC/Makefile new file mode 100644 index 0000000..939d8a9 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-r-ObjC/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is to check that -ObjC loads all (and only) +# .o files that contain Objective-C code that has gone through ld -r. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${LD} -r -arch ${ARCH} foo.o -o foo-r.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + ${LD} -r -arch ${ARCH} bar.o -o bar-r.o + ${CC} ${CCFLAGS} baz.m -c -o baz.o + ${LD} -r -arch ${ARCH} baz.o -o baz-r.o + ${CC} ${CCFLAGS} cat.m -c -o cat.o + ${LD} -r -arch ${ARCH} cat.o -o cat-r.o + libtool -static foo-r.o bar-r.o baz-r.o cat-r.o -o liball.a + ${CC} ${CCFLAGS} main.c liball.a -o main -ObjC -framework Foundation -framework CoreFoundation + ${FAIL_IF_BAD_MACHO} main + nm main | grep "_bar" | ${FAIL_IF_STDIN} + nm main | grep "_Foo" | ${FAIL_IF_EMPTY} + nm main | grep "_Baz" | ${FAIL_IF_EMPTY} + nm main | grep "_mycatfunc" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.o *.a diff --git a/ld64/unit-tests/test-cases/archive-r-ObjC/bar.c b/ld64/unit-tests/test-cases/archive-r-ObjC/bar.c new file mode 100644 index 0000000..d9b468b --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-r-ObjC/bar.c @@ -0,0 +1,2 @@ + +int bar() { return 0; } diff --git a/ld64/unit-tests/test-cases/archive-r-ObjC/baz.m b/ld64/unit-tests/test-cases/archive-r-ObjC/baz.m new file mode 100644 index 0000000..90ae0a1 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-r-ObjC/baz.m @@ -0,0 +1,8 @@ +#include + +@interface Baz : NSObject +@end + +@implementation Baz +@end + diff --git a/ld64/unit-tests/test-cases/archive-r-ObjC/cat.m b/ld64/unit-tests/test-cases/archive-r-ObjC/cat.m new file mode 100644 index 0000000..aaabfe3 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-r-ObjC/cat.m @@ -0,0 +1,16 @@ +#include + +@interface NSObject(Cat) +@end + +@implementation NSObject(Cat) +@end + +@interface NSObject(Dog) +@end + +@implementation NSObject(Dog) +@end + +void mycatfunc() {} + diff --git a/ld64/unit-tests/test-cases/archive-r-ObjC/foo.m b/ld64/unit-tests/test-cases/archive-r-ObjC/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-r-ObjC/foo.m @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/ld64/unit-tests/test-cases/archive-r-ObjC/main.c b/ld64/unit-tests/test-cases/archive-r-ObjC/main.c new file mode 100644 index 0000000..dc2fbef --- /dev/null +++ b/ld64/unit-tests/test-cases/archive-r-ObjC/main.c @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/branch-islands/Makefile index 471e446..8e1870b 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/branch-islands/Makefile @@ -32,8 +32,12 @@ run: all all: + # Verify that we fail if there is no valid place to insert branch islands. + ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} + ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} ${PASS_IFF_GOOD_MACHO} hello + clean: rm hello diff --git a/ld64/unit-tests/test-cases/branch-islands/atomic_space.s b/ld64/unit-tests/test-cases/branch-islands/atomic_space.s new file mode 100644 index 0000000..4402d66 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-islands/atomic_space.s @@ -0,0 +1,76 @@ + +#if __ppc__ + + .text + +_prejunk: + mr r3,r5 + mr r3,r4 + blr + + +_space1: + .space 15*1024*1024 + 2 + + .align 5 +_junk: + mr r3,r5 + mr r3,r4 + blr + + +_space2: + .space 2*1024*1024 + +#endif + + +#if __arm__ + + .text +_prejunk: + mov r0, #1 + nop + +#if __thumb2__ + // thumb2 branches are +/- 16MB +_space1: + .space 14*1024*1024 +_space2: + .space 14*1024*1024 +_space3: + .space 14*1024*1024 + + +#elif __thumb__ + // thumb1 branches are +/- 4MB +_space1: + .space 3*1024*1024 +_space2: + .space 3*1024*1024 +_space3: + .space 3*1024*1024 + +#else + + // ARM branches are +/- 32MB +_space1: + .space 14*1024*1024 +_space2: + .space 14*1024*1024 +_space3: + .space 14*1024*1024 + +#endif + + .align 5 +_junk: + mov r0, #1 + nop + + +_space4: + .space 2*1024*1024 +#endif + + //.subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/data-in-code/Makefile b/ld64/unit-tests/test-cases/data-in-code/Makefile new file mode 100644 index 0000000..0897726 --- /dev/null +++ b/ld64/unit-tests/test-cases/data-in-code/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2011 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that L$start$.. labels are tracked +# + +all: + ${CC} ${CCFLAGS} -c test.s -o test.o + ${LD} -r -arch ${ARCH} test.o -o test2.o + #nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} + #${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf test.o test2.o diff --git a/ld64/unit-tests/test-cases/data-in-code/main.c b/ld64/unit-tests/test-cases/data-in-code/main.c new file mode 100644 index 0000000..811449a --- /dev/null +++ b/ld64/unit-tests/test-cases/data-in-code/main.c @@ -0,0 +1,49 @@ + +#include + +#define DTRACE_STRINGIFY(s) #s +#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s) + +#define DTRACE_NOPS \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" + + +#define DTRACE_LAB(p, n) \ + "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n) + +#define DTRACE_LABEL(p, n) \ + ".section __DATA, __data\n\t" \ + ".globl " DTRACE_LAB(p, n) "\n\t" \ + DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \ + ".text" "\n\t" \ + "1:" + +#define DTRACE_CALL(p,n) \ + DTRACE_LABEL(p,n) \ + DTRACE_NOPS + +#define DTRACE_CALL0ARGS(provider, name) \ + __asm volatile ( \ + DTRACE_CALL(provider, name) \ + : \ + : \ + ); + +int deadwood() +{ + DTRACE_CALL0ARGS(__foo__, test2) + return 0; +} + + +int main() { + int a = 1; + + while(a) { + DTRACE_CALL0ARGS(__foo__, test1) + } + + return 0; +} diff --git a/ld64/unit-tests/test-cases/data-in-code/test.s b/ld64/unit-tests/test-cases/data-in-code/test.s new file mode 100644 index 0000000..8dc92b8 --- /dev/null +++ b/ld64/unit-tests/test-cases/data-in-code/test.s @@ -0,0 +1,25 @@ + + .text + +_foo: + nop + nop +l$start$data$1: + nop + nop + nop +l$start$jt8$2: + nop + nop +l$start$jt16$3: + nop + nop +l$start$code$n4: + nop + nop + + + + .subsections_via_symbols + + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/duplicate_symbols/Makefile b/ld64/unit-tests/test-cases/duplicate_symbols/Makefile new file mode 100644 index 0000000..a16e69b --- /dev/null +++ b/ld64/unit-tests/test-cases/duplicate_symbols/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that multiple duplicate symbols are reported. +# + +all: + ${CC} -arch ${ARCH} -c main_extern.c + ${CC} -arch ${ARCH} -c main_no_extern.c + ${CC} -arch ${ARCH} -c duplicates.c + ${CC} -arch ${ARCH} -dynamiclib -o duplicates.dylib duplicates.o + libtool -static -o duplicates.a duplicates.o + + # simple case of directly linking duplicates should fail + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main_extern.o duplicates.o 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_EMPTY} + + + # duplicates in a dylib succeed + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} main_extern.o duplicates.dylib 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_STDIN} + + # static lib - only fail if module containing duplicate symbols is actually referenced + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main_extern.o duplicates.a 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_EMPTY} + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} main_no_extern.o duplicates.a 2>&1 | grep "2 duplicate symbols" | ${FAIL_IF_STDIN} + + # simple case should succeed if we dead strip because none of the duplicates are referenced + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -dead_strip main_extern.o duplicates.o + + rm -f a.out *.o *.a *.dylib + ${PASS_IFF} true diff --git a/ld64/unit-tests/test-cases/duplicate_symbols/duplicates.c b/ld64/unit-tests/test-cases/duplicate_symbols/duplicates.c new file mode 100644 index 0000000..2a62979 --- /dev/null +++ b/ld64/unit-tests/test-cases/duplicate_symbols/duplicates.c @@ -0,0 +1,10 @@ +#include + +void a() { +} + +void b() { +} + +void c() { +} diff --git a/ld64/unit-tests/test-cases/duplicate_symbols/main_extern.c b/ld64/unit-tests/test-cases/duplicate_symbols/main_extern.c new file mode 100644 index 0000000..b78ea48 --- /dev/null +++ b/ld64/unit-tests/test-cases/duplicate_symbols/main_extern.c @@ -0,0 +1,16 @@ +/* +This file references an extern function c() that lives in +a separate compilation unit that also has a() and b(). +*/ + +extern void c(); + +void a() { +} + +void b() { +} + +int main() { +c(); +} diff --git a/ld64/unit-tests/test-cases/duplicate_symbols/main_no_extern.c b/ld64/unit-tests/test-cases/duplicate_symbols/main_no_extern.c new file mode 100644 index 0000000..cbdbeb6 --- /dev/null +++ b/ld64/unit-tests/test-cases/duplicate_symbols/main_no_extern.c @@ -0,0 +1,13 @@ +/* +This file contains symbols that are duplicated in another file, +but does not reference anything that would pull in the duplicates. +*/ + +void a() { +} + +void b() { +} + +int main() { +} diff --git a/ld64/unit-tests/test-cases/weak_import3/Makefile b/ld64/unit-tests/test-cases/dylib-main/Makefile similarity index 72% rename from ld64/unit-tests/test-cases/weak_import3/Makefile rename to ld64/unit-tests/test-cases/dylib-main/Makefile index 2c8806d..f01ef41 100644 --- a/ld64/unit-tests/test-cases/weak_import3/Makefile +++ b/ld64/unit-tests/test-cases/dylib-main/Makefile @@ -1,15 +1,15 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ -# +# # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. -# +# # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -17,30 +17,22 @@ # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. -# +# # @APPLE_LICENSE_HEADER_END@ ## TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Test the weak_import attribute works +# Test that a program can have is "main" in a dylib # - run: all -all: - ${CC} ${CCFLAGS} -c foo.c -o foo.o - ${FAIL_IF_BAD_OBJ} foo.o - - ${CC} ${CCFLAGS} -c foo1.c -o foo1.o - ${FAIL_IF_BAD_OBJ} foo1.o - - ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo.o foo1.o -o libfoo.dylib +all: + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib + ${CC} ${CCFLAGS} foo.c libmain.dylib -o main -Wl,-new_main + ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf foo.o foo1.o libfoo.dylib - - -#2>/dev/null \ No newline at end of file + rm -rf libmain.dylib main diff --git a/ld64/unit-tests/test-cases/dylib-main/foo.c b/ld64/unit-tests/test-cases/dylib-main/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-main/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/ld64/unit-tests/test-cases/dylib-main/main.c b/ld64/unit-tests/test-cases/dylib-main/main.c new file mode 100644 index 0000000..a444c65 --- /dev/null +++ b/ld64/unit-tests/test-cases/dylib-main/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + fprintf(stdout, "hello\n"); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/force-weak/Makefile b/ld64/unit-tests/test-cases/force-weak/Makefile new file mode 100644 index 0000000..4dbf4e9 --- /dev/null +++ b/ld64/unit-tests/test-cases/force-weak/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2011 Apple, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that forcing a symbol weak does not cause spurious warnings +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c test.c -dynamiclib -o libtest.dylib -Wl,-force_symbols_weak_list,weak.exp + otool -Iv libtest.dylib | grep _foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm libtest.dylib diff --git a/ld64/unit-tests/test-cases/force-weak/foo.c b/ld64/unit-tests/test-cases/force-weak/foo.c new file mode 100644 index 0000000..485aed6 --- /dev/null +++ b/ld64/unit-tests/test-cases/force-weak/foo.c @@ -0,0 +1,2 @@ + +int foo = 5; \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/force-weak/test.c b/ld64/unit-tests/test-cases/force-weak/test.c new file mode 100644 index 0000000..bd0da12 --- /dev/null +++ b/ld64/unit-tests/test-cases/force-weak/test.c @@ -0,0 +1,5 @@ + +extern int foo; + +int getfoo() { return foo; } + diff --git a/ld64/unit-tests/test-cases/force-weak/weak.exp b/ld64/unit-tests/test-cases/force-weak/weak.exp new file mode 100644 index 0000000..33c5f68 --- /dev/null +++ b/ld64/unit-tests/test-cases/force-weak/weak.exp @@ -0,0 +1,2 @@ +_foo + diff --git a/ld64/unit-tests/test-cases/install-name-override/Makefile b/ld64/unit-tests/test-cases/install-name-override/Makefile new file mode 100644 index 0000000..d4cfe56 --- /dev/null +++ b/ld64/unit-tests/test-cases/install-name-override/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2011 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify $ld$install_name$osXX$/new/path overides install_name +# + +FORCE_MIN_OS_VERSION = -mmacosx-version-min=10.5 + +ifeq ($(FILEARCH),arm) + FORCE_MIN_OS_VERSION = -miphoneos-version-min=4.0 +endif + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib + otool -L libfoo.dylib | grep /usr/local/lib/libfoo.dylib | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + otool -L main | grep /usr/local/lib/libfoo.dylib | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c libfoo.dylib -o mainAlt ${FORCE_MIN_OS_VERSION} + otool -L mainAlt | grep /usr/lib/libfoo.dylib | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -f libfoo.dylib main mainAlt diff --git a/ld64/unit-tests/test-cases/install-name-override/foo.c b/ld64/unit-tests/test-cases/install-name-override/foo.c new file mode 100644 index 0000000..1a68260 --- /dev/null +++ b/ld64/unit-tests/test-cases/install-name-override/foo.c @@ -0,0 +1,18 @@ + +int foo() +{ + return 0; +} + + +#if __arm__ + #define INSTALL_NAME_4_0(sym) \ + extern const char install_name_4_0 __asm("$ld$install_name$os4.0$" #sym ); const char install_name_4_0 = 0; + + INSTALL_NAME_4_0(/usr/lib/libfoo.dylib) +#else + #define INSTALL_NAME_10_5(sym) \ + extern const char install_name_10_5 __asm("$ld$install_name$os10.5$" #sym ); const char install_name_10_5 = 0; + + INSTALL_NAME_10_5(/usr/lib/libfoo.dylib) +#endif diff --git a/ld64/unit-tests/test-cases/install-name-override/main.c b/ld64/unit-tests/test-cases/install-name-override/main.c new file mode 100644 index 0000000..7f346c1 --- /dev/null +++ b/ld64/unit-tests/test-cases/install-name-override/main.c @@ -0,0 +1,9 @@ + +extern int foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/llvm-integration/Makefile b/ld64/unit-tests/test-cases/llvm-integration/Makefile index 8f33f36..bd2150c 100644 --- a/ld64/unit-tests/test-cases/llvm-integration/Makefile +++ b/ld64/unit-tests/test-cases/llvm-integration/Makefile @@ -32,12 +32,6 @@ LLVMAR = /usr/local/bin/llvm-ar # # Test the we set the stack execution bit properly. -run: - if [ -f /Developer/usr/bin/llvm-gcc-4.2 ] ; then \ - $(MAKE) all ; \ - else \ - ${PASS_IFF} /usr/bin/true ; \ - fi all: zero one two three four five six seven eight nine ten \ eleven twelve thirteen fourteen fifteen sixteen seventeen \ diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile new file mode 100644 index 0000000..35a0220 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# [lto] Linking libLTO.dylib causing an assertion in ld +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o + ${CC} ${CCFLAGS} bar.o -dynamiclib -o libbar.dylib -dead_strip -Wl,-exported_symbol,_bar + ${PASS_IFF_GOOD_MACHO} libbar.dylib + +clean: + rm -f bar.o libbar.dylib + diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c b/ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c new file mode 100644 index 0000000..2b49dc1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dead_strip-inline-asm/bar.c @@ -0,0 +1,14 @@ + + +void qux() {} + +asm("\t.text\n" + "\t.globl _foo\n" + "_foo:\n" + "\tnop\n"); + +extern void foo(); + +void (*bar())() { + return foo; +} diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile index 82192f8..79a193f 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile +++ b/ld64/unit-tests/test-cases/lto-dead_strip-unused/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +SHELL = bash # use bash shell so we can redirect just stderr # # LTO with 'dead code strip' can't ignore unused functions with undefined references @@ -36,6 +37,8 @@ all: ${CC} ${CCFLAGS} -flto main.c -c -o main.o ${CC} ${CCFLAGS} main.o bar.o -o main ${CC} ${CCFLAGS} main.o bar.o -o main -dead_strip + ${CC} ${CCFLAGS} main.o bar.o -dynamiclib -o libmain.dylib -Wl,-exported_symbol,_main + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.o bar.o -dynamiclib -o libmain.dylib -dead_strip 2>/dev/null ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/lto-preload-pie/Makefile b/ld64/unit-tests/test-cases/lto-preload-pie/Makefile index 063bc33..6f9cc2c 100644 --- a/ld64/unit-tests/test-cases/lto-preload-pie/Makefile +++ b/ld64/unit-tests/test-cases/lto-preload-pie/Makefile @@ -27,16 +27,13 @@ include ${TESTROOT}/include/common.makefile # verify -preload -pie produces relocations # -LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} -LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} - run: all all: - ${LLVMGCC} ${CCFLAGS} --emit-llvm a.c -c -o a.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b.o - ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o - ${LLVMGCC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \ + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \ -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 otool -rv main.preload | grep "Local relocation information" | ${PASS_IFF_STDIN} diff --git a/ld64/unit-tests/test-cases/no-uuid/Makefile b/ld64/unit-tests/test-cases/no-uuid/Makefile index 7c5304b..dcae189 100644 --- a/ld64/unit-tests/test-cases/no-uuid/Makefile +++ b/ld64/unit-tests/test-cases/no-uuid/Makefile @@ -35,29 +35,7 @@ all: ${CC} ${CCFLAGS} foo.c -o foo -gdwarf-2 ${FAIL_IF_BAD_MACHO} foo ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} - rm -f foo - -# Test main executable built with stabs has uuid - ${CC} ${CCFLAGS} foo.c -o foo -gfull -gstabs+ - ${FAIL_IF_BAD_MACHO} foo - ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_EMPTY} - -# Test main executable built with dwarf and -no_uuid does not have uuid - ${CC} ${CCFLAGS} foo.c -o foo -Wl,-no_uuid -gdwarf-2 - ${FAIL_IF_BAD_MACHO} foo - ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN} - -# Test ld -r of stabs file has no uuid (llvm does not support stabs, so use gcc) - gcc-4.2 -arch ${ARCH} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ - ${LD} -arch ${ARCH} foo.o -r -o foo2.o - ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN} - -# Test ld -r of two files one with uuid produces a uuid - ${CC} ${CCFLAGS} foo.c -c -o foo.o -gdwarf-2 - ${LD} -arch ${ARCH} foo.o -r -o foo2.o - ${CC} ${CCFLAGS} bar.c -c -gstabs+ -o bar.o - ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o - ${OTOOL} -hlv foobar.o | grep LC_UUID | ${PASS_IFF_STDIN} + ${PASS_IFF_GOOD_MACHO} foo clean: - rm -rf foo foo.o foo2.o bar.o foobar.o foo.dSYM + rm -rf foo diff --git a/ld64/unit-tests/test-cases/objc-category-archive/Makefile b/ld64/unit-tests/test-cases/objc-category-archive/Makefile index b56bacf..7bcd9a4 100644 --- a/ld64/unit-tests/test-cases/objc-category-archive/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-archive/Makefile @@ -12,16 +12,19 @@ run: all all: ${CC} ${CCFLAGS} -g test.m -c -o test.o - libtool -static test.o -o libtest.a + ${CC} ${CCFLAGS} -g test2.m -c -o test2.o + libtool -static test.o test2.o -o libtest.a ${CC} ${CCFLAGS} main.m libtest.a -ObjC -o main -framework Foundation nm main | grep mycatmethod1 | ${FAIL_IF_EMPTY} nm main | grep mycatmethod2 | ${FAIL_IF_EMPTY} - ${LD} -arch ${ARCH} -r -S -keep_private_externs test.o -o test-stripped.o + nm main | grep mymethod2 | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r -S -keep_private_externs test.o test2.o -o test-stripped.o libtool -static test-stripped.o -o libtest-stripped.a ${CC} ${CCFLAGS} main.m libtest-stripped.a -all_load -dead_strip -o main2 -framework Foundation nm main2 | grep mycatmethod1 | ${FAIL_IF_EMPTY} nm main2 | grep mycatmethod2 | ${FAIL_IF_EMPTY} + nm main2 | grep mymethod2 | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main2 clean: - rm -rf test.o test-stripped.o libtest.a libtest-stripped.a main main2 + rm -rf test.o test2.o test-stripped.o libtest.a libtest-stripped.a main main2 diff --git a/ld64/unit-tests/test-cases/objc-category-archive/test2.m b/ld64/unit-tests/test-cases/objc-category-archive/test2.m new file mode 100644 index 0000000..e1e5653 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-archive/test2.m @@ -0,0 +1,11 @@ + +#include + +@interface MyClass : NSObject +- (void) mymethod2; +@end + +@implementation MyClass +- (void) mymethod2 { } +@end + diff --git a/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile b/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile index 44023e6..9d719ee 100644 --- a/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-debug-notes/Makefile @@ -32,7 +32,7 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation + ${CC} ${CCFLAGS} test.m -g -o test -framework Foundation -Wno-objc-protocol-method-implementation nm -ap test | grep GSYM | grep category | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} test diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile index 5652312..2bbc5dd 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch endif all: all-${ARCH} @@ -40,6 +40,7 @@ all-ppc: all-i386: all-rest all-x86_64: all-rest all-armv6: all-rest +all-armv7: all-rest all-rest: # check optimzation of category methods diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile index c452bec..c395988 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile @@ -29,7 +29,11 @@ include ${TESTROOT}/include/common.makefile OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch +endif + +ifeq ($(ARCH),arm) + OPTIONS = -isysroot=${IOS_SDK} endif all: all-${ARCH} @@ -40,6 +44,7 @@ all-ppc: all-i386: all-rest all-x86_64: all-rest all-armv6: all-rest +all-armv7: all-rest all-rest: # check optimization can be turned off diff --git a/ld64/unit-tests/test-cases/objc-category-warning/Makefile b/ld64/unit-tests/test-cases/objc-category-warning/Makefile index 95cc91d..5481ba0 100644 --- a/ld64/unit-tests/test-cases/objc-category-warning/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-warning/Makefile @@ -30,7 +30,7 @@ SHELL = bash # use bash shell so we can redirect just stderr OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch endif all: all-${FILEARCH} @@ -52,4 +52,4 @@ all-rest: ${PASS_IFF_GOOD_MACHO} libfoo2.dylib clean: - rm -rf lib*.dylib warnings*.txt \ No newline at end of file + rm -rf lib*.dylib warnings*.txt diff --git a/ld64/unit-tests/test-cases/order_file/Makefile b/ld64/unit-tests/test-cases/order_file/Makefile index 1aec6f1..3e88763 100644 --- a/ld64/unit-tests/test-cases/order_file/Makefile +++ b/ld64/unit-tests/test-cases/order_file/Makefile @@ -51,7 +51,7 @@ all: ${CC} ${CCFLAGS} main.c extra.s -DSUBSECTIONS=1 -o main4 -Wl,-order_file -Wl,main4.order ${FAIL_IF_BAD_MACHO} main4 - nm -n -g -j main4 | nm -njg main4 | egrep '*[1-9]' > main4.nm + nm -n -g -j main4 | egrep '_[a-zA-Z]+[1-9]' > main4.nm ${PASS_IFF} diff main4.nm main4.expected diff --git a/ld64/unit-tests/test-cases/pipelined-linking/Makefile b/ld64/unit-tests/test-cases/pipelined-linking/Makefile new file mode 100644 index 0000000..fe9197f --- /dev/null +++ b/ld64/unit-tests/test-cases/pipelined-linking/Makefile @@ -0,0 +1,74 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Validate these pipelined linking cases: +# - missing pipe +# - bad file passed via pipe +# - eof on pipe +# - success case +# - differing file orders produce same binary +# +# When testing the success case take care to verify that pipelined linking is actually +# enabled (environment variable is set). Because there is no difference in the command +# line the test will still succeed with pipelined linking turned off, without testing +# pipelined linking. +# + +all: + # Make some object files + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + ${CC} ${CCFLAGS} cat.c -c -o cat.o + ls *.o > filelist + + # missing fifo + (LD_PIPELINE_FIFO=bad_fifo ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o partial ; exit 0) 2>&1 | ${PASS_IFF_STDIN} + + # partial file list passed via pipe + head -1 filelist > pipelineFile + (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o partial ; exit 0) 2>&1 | ${PASS_IFF_STDIN} + ${FAIL_IFF} ${MACHOCHECK} partial 2>&1 > /dev/null + + # bogus file passed via pipe + echo "bogus_file.o" > pipelineFile + (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -filelist filelist -o bogus ; exit 0) 2>&1 | ${PASS_IFF_STDIN} + ${FAIL_IFF} ${MACHOCHECK} bogus 2>&1 > /dev/null + + # success cases - check different orderings of files + sort < filelist > pipelineFile + (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o normal) + ${FAIL_IF_BAD_MACHO} normal + sort -r < filelist > pipelineFile + (LD_PIPELINE_FIFO=pipelineFile ; export LD_PIPELINE_FIFO ; ${CC} ${CCFLAGS} -filelist filelist -o reverse) + ${FAIL_IF_BAD_MACHO} reverse + + # verify that the same binary was generated using both forward and reverse ordering + ${PASS_IFF} cmp reverse normal + + +clean: + rm -f *.o filelist partial bogus reverse normal pipelineFile + diff --git a/ld64/unit-tests/test-cases/pipelined-linking/bar.c b/ld64/unit-tests/test-cases/pipelined-linking/bar.c new file mode 100644 index 0000000..320c246 --- /dev/null +++ b/ld64/unit-tests/test-cases/pipelined-linking/bar.c @@ -0,0 +1,4 @@ +void bar() +{ +} + diff --git a/ld64/unit-tests/test-cases/pipelined-linking/cat.c b/ld64/unit-tests/test-cases/pipelined-linking/cat.c new file mode 100644 index 0000000..0e32166 --- /dev/null +++ b/ld64/unit-tests/test-cases/pipelined-linking/cat.c @@ -0,0 +1,4 @@ +void cat() +{ +} + diff --git a/ld64/unit-tests/test-cases/pipelined-linking/foo.c b/ld64/unit-tests/test-cases/pipelined-linking/foo.c new file mode 100644 index 0000000..6e0d320 --- /dev/null +++ b/ld64/unit-tests/test-cases/pipelined-linking/foo.c @@ -0,0 +1,10 @@ + +void foo() +{ +} + +int main() +{ +return 0; +} + diff --git a/ld64/unit-tests/test-cases/static-executable-pie/Makefile b/ld64/unit-tests/test-cases/static-executable-pie/Makefile new file mode 100644 index 0000000..5854b0f --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable-pie/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2011 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +RELOC_COUNT = 3 +ifeq (${ARCH},x86_64) + RELOC_COUNT = 2 +endif + + +# +# Test that ld can link a pie static executable +# + +all: + ${CC} ${CCFLAGS} test.c -c -o test.o + ${CC} ${CCFLAGS} test.o -static -Wl,-pie -o test -e _entry -nostdlib -Wl,-new_linker + otool -rv test | grep __DATA | wc -l | grep ${RELOC_COUNT} | ${FAIL_IF_EMPTY} + # verify trying to use absolute addressing fails + ${CC} ${CCFLAGS} -static bad.c -c -o bad.o + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} test.o bad.o -static -Wl,-pie -o test.bad -e _entry -nostdlib -Wl,-new_linker 2>/dev/null + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test test.o bad.o test.bad + + diff --git a/ld64/unit-tests/test-cases/static-executable-pie/bad.c b/ld64/unit-tests/test-cases/static-executable-pie/bad.c new file mode 100644 index 0000000..40f0614 --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable-pie/bad.c @@ -0,0 +1,9 @@ +static int my; + +int getmy() +{ +#if __x86_64__ + __asm(" .quad _my"); +#endif + return my; +} diff --git a/ld64/unit-tests/test-cases/static-executable-pie/test.c b/ld64/unit-tests/test-cases/static-executable-pie/test.c new file mode 100644 index 0000000..4d09a3a --- /dev/null +++ b/ld64/unit-tests/test-cases/static-executable-pie/test.c @@ -0,0 +1,19 @@ + +int a; +int b = 5; +int* pa = &a; +int* pb = &b; + +int foo() +{ + *pa = 4; + return a+b; +} + + +int entry() +{ + return foo(); +} + + diff --git a/ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile b/ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile index 7c2c28f..ba343cc 100644 --- a/ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile +++ b/ld64/unit-tests/test-cases/tentative-and-archive-code/Makefile @@ -55,8 +55,8 @@ all: ${CC} ${CCFLAGS} main.c libfoo_code.a -o main_code nm -m main_code | grep _foo | grep __TEXT | ${FAIL_IF_STDIN} # verify warning when code force to override tent - ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo_code.a -o main_code_force 2>main_code_force_warnings.txt - grep warning main_code_force_warnings.txt | ${FAIL_IF_EMPTY} + #${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo_code.a -o main_code_force 2>main_code_force_warnings.txt + #grep warning main_code_force_warnings.txt | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main_code clean: diff --git a/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile b/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile index 568ebc6..2793f3e 100644 --- a/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile +++ b/ld64/unit-tests/test-cases/tlv-dead_strip/Makefile @@ -30,7 +30,7 @@ include ${TESTROOT}/include/common.makefile run: all all: - clang ${CCFLAGS} main.c -o main -dead_strip -mmacosx-version-min=10.7 + ${CC} ${CCFLAGS} main.c -o main -dead_strip -mmacosx-version-min=10.7 otool -lv main | grep S_THREAD_LOCAL_VARIABLES | ${FAIL_IF_EMPTY} otool -lv main | egrep 'S_THREAD_LOCAL_REGULAR|S_THREAD_LOCAL_ZEROFILL' | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main diff --git a/ld64/unit-tests/test-cases/weak_import-undefined/Makefile b/ld64/unit-tests/test-cases/weak_import-undefined/Makefile new file mode 100644 index 0000000..56e888f --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-undefined/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that setting weak_import on symbols in a linkage unit works +# + + +run: all + +all: + ${CC} ${CCFLAGS} -o weak weak.c -undefined dynamic_lookup + ${DYLDINFO} -bind weak | grep _myweakfunc | grep "weak import" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind weak | grep _myweakfunc | grep "weak import" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} weak + +clean: + rm -rf main diff --git a/ld64/unit-tests/test-cases/weak_import-undefined/weak.c b/ld64/unit-tests/test-cases/weak_import-undefined/weak.c new file mode 100644 index 0000000..f8e103b --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-undefined/weak.c @@ -0,0 +1,13 @@ +#include + +char *myweakfunc(void) __attribute__((weak)) ; + +int main(int argc, char **argv) +{ + if (myweakfunc) + printf ("found myweakfunc %s\n", myweakfunc()); + else + printf("Weak func not found\n"); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak_import3/comment.txt b/ld64/unit-tests/test-cases/weak_import3/comment.txt deleted file mode 100644 index 5be42d8..0000000 --- a/ld64/unit-tests/test-cases/weak_import3/comment.txt +++ /dev/null @@ -1 +0,0 @@ -Test the weak_import attribute works diff --git a/ld64/unit-tests/test-cases/weak_import3/foo.c b/ld64/unit-tests/test-cases/weak_import3/foo.c deleted file mode 100644 index 900b052..0000000 --- a/ld64/unit-tests/test-cases/weak_import3/foo.c +++ /dev/null @@ -1,17 +0,0 @@ - - -#include "foo.h" - -void func1() {} -void func2() {} -void func3() {} -void func4() {} - - -int data1 = 0; -int data2 = 0; // weak_import initialized -int data3; -int data4; // weak_import uninitialized -int data5 = 0; -int data6 = 0; // weak_import - diff --git a/ld64/unit-tests/test-cases/weak_import3/foo.h b/ld64/unit-tests/test-cases/weak_import3/foo.h deleted file mode 100644 index f455515..0000000 --- a/ld64/unit-tests/test-cases/weak_import3/foo.h +++ /dev/null @@ -1,16 +0,0 @@ - - -extern void func1(); -extern void func2() __attribute__((weak_import)); -extern void func3(); -extern void func4() __attribute__((weak_import)); - -extern int data1; -extern int data2 __attribute__((weak_import)); -extern int data3; -extern int data4 __attribute__((weak_import)); -extern int data5; -extern int data6 __attribute__((weak_import)); - - - diff --git a/ld64/unit-tests/test-cases/weak_import3/foo1.c b/ld64/unit-tests/test-cases/weak_import3/foo1.c deleted file mode 100644 index 392a5b7..0000000 --- a/ld64/unit-tests/test-cases/weak_import3/foo1.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "foo.h" - -int data4; // foo.c also has weak_import uninitialized - diff --git a/ld64/unit-tests/test-cases/weak_import3/main.c b/ld64/unit-tests/test-cases/weak_import3/main.c deleted file mode 100644 index 3266aed..0000000 --- a/ld64/unit-tests/test-cases/weak_import3/main.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include "foo.h" - - -int* pdata5 = &data5; -int* pdata6 = &data6; - - -int main (void) -{ - // make non-lazy reference to func3 and func4 - if ( &func3 == &func4 ) { - // make lazy reference to func3 and func4 - func1(); - func2(); - } - - return data1 + data2 + data3 + data4; -} - From d3bb66a6461497173634cf4b4fa0bdcd39a4977b Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:20:41 +0100 Subject: [PATCH 13/48] 134-9 --- ld64/ld64.xcodeproj/project.pbxproj | 156 +++++++++--------- ld64/src/abstraction/MachOFileAbstraction.hpp | 15 ++ ld64/src/abstraction/MachOTrie.hpp | 4 +- ld64/src/create_configure | 2 +- ld64/src/ld/HeaderAndLoadCommands.hpp | 4 +- ld64/src/ld/InputFiles.cpp | 27 +-- ld64/src/ld/LinkEdit.hpp | 16 +- ld64/src/ld/LinkEditClassic.hpp | 80 +++------ ld64/src/ld/Options.cpp | 35 ++-- ld64/src/ld/Options.h | 2 + ld64/src/ld/OutputFile.cpp | 41 +++-- ld64/src/ld/Resolver.cpp | 23 ++- ld64/src/ld/Resolver.h | 3 +- ld64/src/ld/ld.cpp | 2 +- ld64/src/ld/ld.hpp | 4 +- ld64/src/ld/parsers/archive_file.cpp | 2 +- ld64/src/ld/parsers/lto_file.cpp | 24 ++- ld64/src/ld/parsers/lto_file.h | 2 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 8 +- .../src/ld/parsers/macho_relocatable_file.cpp | 93 +++++++++-- ld64/src/ld/parsers/opaque_section_file.cpp | 2 - ld64/src/ld/passes/branch_island.cpp | 8 - ld64/src/ld/passes/branch_shim.cpp | 10 -- ld64/src/ld/passes/compact_unwind.cpp | 4 - ld64/src/ld/passes/dtrace_dof.cpp | 2 - ld64/src/ld/passes/dylibs.cpp | 4 - ld64/src/ld/passes/got.cpp | 2 - ld64/src/ld/passes/objc.cpp | 10 -- ld64/src/ld/passes/stubs/stub_arm.hpp | 22 --- ld64/src/ld/passes/stubs/stub_arm_classic.hpp | 6 - ld64/src/ld/passes/stubs/stub_x86.hpp | 14 -- ld64/src/ld/passes/stubs/stub_x86_64.hpp | 18 -- .../ld/passes/stubs/stub_x86_64_classic.hpp | 6 - ld64/src/ld/passes/stubs/stub_x86_classic.hpp | 6 - ld64/src/ld/passes/tlvp.cpp | 2 - ld64/src/other/ObjectDump.cpp | 2 +- ld64/unit-tests/include/common.makefile | 6 +- .../dwarf-debug-notes/expected-stabs | 2 +- .../test-cases/lto-dead_strip-coalesce/main.c | 5 + ld64/unit-tests/test-cases/no-uuid/Makefile | 2 +- .../test-cases/operator-new/main.cxx | 14 ++ .../test-cases/stabs-directory-slash/Makefile | 2 +- ld64/unit-tests/test-cases/tlv-r/Makefile | 41 +++++ ld64/unit-tests/test-cases/tlv-r/foo.c | 32 ++++ .../test-cases/weak_import-r/Makefile | 58 +++++++ .../unit-tests/test-cases/weak_import-r/foo.h | 16 ++ .../test-cases/weak_import-r/main.c | 23 +++ 47 files changed, 500 insertions(+), 362 deletions(-) create mode 100644 ld64/unit-tests/test-cases/tlv-r/Makefile create mode 100644 ld64/unit-tests/test-cases/tlv-r/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import-r/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import-r/foo.h create mode 100644 ld64/unit-tests/test-cases/weak_import-r/main.c diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 11e716e..08145e5 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -241,92 +241,92 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; }; - B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; }; - B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; }; + B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; }; - F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; }; - F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; }; - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; }; - F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; }; - F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = ""; }; - F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = ""; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; - F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = ""; }; - F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; }; - F9849E3410B38EF5009E9878 /* order.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = order.cpp; sourceTree = ""; }; - F9849E3510B38EF5009E9878 /* order.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = order.h; sourceTree = ""; }; - F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm_classic.hpp; sourceTree = ""; }; - F984A38010BB4B0D009E9878 /* branch_island.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_island.cpp; sourceTree = ""; }; - F984A38110BB4B0D009E9878 /* branch_island.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_island.h; sourceTree = ""; }; - F989D0391062E6350014B60C /* stub_x86_64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64.hpp; sourceTree = ""; }; - F989D30B106826020014B60C /* OutputFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OutputFile.cpp; path = src/ld/OutputFile.cpp; sourceTree = ""; }; - F989D30C106826020014B60C /* OutputFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OutputFile.h; path = src/ld/OutputFile.h; sourceTree = ""; }; - F989D3AA10684F5B0014B60C /* LinkEdit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEdit.hpp; path = src/ld/LinkEdit.hpp; sourceTree = ""; }; - F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEditClassic.hpp; path = src/ld/LinkEditClassic.hpp; sourceTree = ""; }; - F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = HeaderAndLoadCommands.hpp; path = src/ld/HeaderAndLoadCommands.hpp; sourceTree = ""; }; + F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9849E3410B38EF5009E9878 /* order.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = order.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9849E3510B38EF5009E9878 /* order.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = order.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F984A38010BB4B0D009E9878 /* branch_island.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_island.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F984A38110BB4B0D009E9878 /* branch_island.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = branch_island.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F989D0391062E6350014B60C /* stub_x86_64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F989D30B106826020014B60C /* OutputFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OutputFile.cpp; path = src/ld/OutputFile.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F989D30C106826020014B60C /* OutputFile.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = OutputFile.h; path = src/ld/OutputFile.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F989D3AA10684F5B0014B60C /* LinkEdit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEdit.hpp; path = src/ld/LinkEdit.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEditClassic.hpp; path = src/ld/LinkEditClassic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = HeaderAndLoadCommands.hpp; path = src/ld/HeaderAndLoadCommands.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprunetrie.a; sourceTree = BUILT_PRODUCTS_DIR; }; - F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PruneTrie.cpp; path = src/other/PruneTrie.cpp; sourceTree = ""; }; - F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prune_trie.h; path = src/other/prune_trie.h; sourceTree = ""; }; - F9A4DB8F10F816FF00BD8423 /* objc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = objc.cpp; sourceTree = ""; }; - F9A4DB9010F816FF00BD8423 /* objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = objc.h; sourceTree = ""; }; - F9AA44DA1294885F00CB8390 /* branch_shim.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_shim.cpp; sourceTree = ""; }; - F9AA44DB1294885F00CB8390 /* branch_shim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_shim.h; sourceTree = ""; }; - F9AA5FCC103F5CD1003E3539 /* ld.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ld.hpp; path = src/ld/ld.hpp; sourceTree = ""; }; - F9AA650D1051BD2B003E3539 /* make_stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = make_stubs.h; sourceTree = ""; }; - F9AA650F1051BD2B003E3539 /* stub_arm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm.hpp; sourceTree = ""; }; - F9AA65101051BD2B003E3539 /* stubs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stubs.cpp; sourceTree = ""; }; - F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_relocatable_file.cpp; sourceTree = ""; }; - F9AA65881051E750003E3539 /* macho_relocatable_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_relocatable_file.h; sourceTree = ""; }; - F9AA65D71051EC4A003E3539 /* archive_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = archive_file.cpp; sourceTree = ""; }; - F9AA65D81051EC4A003E3539 /* archive_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_file.h; sourceTree = ""; }; - F9AA65D91051EC4A003E3539 /* lto_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lto_file.cpp; sourceTree = ""; }; - F9AA65DA1051EC4A003E3539 /* lto_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lto_file.h; sourceTree = ""; }; - F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_dylib_file.cpp; sourceTree = ""; }; - F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_dylib_file.h; sourceTree = ""; }; - F9AA6784105700C2003E3539 /* opaque_section_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_section_file.cpp; sourceTree = ""; }; - F9AA6785105700C2003E3539 /* opaque_section_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opaque_section_file.h; sourceTree = ""; }; - F9AA67B410570C41003E3539 /* dtrace_dof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dtrace_dof.h; sourceTree = ""; }; - F9AA67B510570C41003E3539 /* dtrace_dof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtrace_dof.cpp; sourceTree = ""; }; - F9AA687A10572E27003E3539 /* InputFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InputFiles.cpp; path = src/ld/InputFiles.cpp; sourceTree = ""; }; - F9AA687B10572E27003E3539 /* InputFiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InputFiles.h; path = src/ld/InputFiles.h; sourceTree = ""; }; - F9AA69B410583C0C003E3539 /* SymbolTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolTable.cpp; path = src/ld/SymbolTable.cpp; sourceTree = ""; }; - F9AA69B510583C0C003E3539 /* SymbolTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolTable.h; path = src/ld/SymbolTable.h; sourceTree = ""; }; - F9AA69BF10583E19003E3539 /* Resolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resolver.cpp; path = src/ld/Resolver.cpp; sourceTree = ""; }; - F9AA69C010583E19003E3539 /* Resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Resolver.h; path = src/ld/Resolver.h; sourceTree = ""; }; - F9AB1063107D380700E54C9E /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = got.cpp; sourceTree = ""; }; - F9AB1064107D380700E54C9E /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = got.h; sourceTree = ""; }; - F9AE20FD1107D1440007ED5D /* dylibs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dylibs.cpp; sourceTree = ""; }; - F9AE20FE1107D1440007ED5D /* dylibs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dylibs.h; sourceTree = ""; }; - F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; }; + F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PruneTrie.cpp; path = src/other/PruneTrie.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = prune_trie.h; path = src/other/prune_trie.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9A4DB8F10F816FF00BD8423 /* objc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = objc.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9A4DB9010F816FF00BD8423 /* objc.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = objc.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA44DA1294885F00CB8390 /* branch_shim.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_shim.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA44DB1294885F00CB8390 /* branch_shim.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = branch_shim.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA5FCC103F5CD1003E3539 /* ld.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = ld.hpp; path = src/ld/ld.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA650D1051BD2B003E3539 /* make_stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = make_stubs.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA650F1051BD2B003E3539 /* stub_arm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65101051BD2B003E3539 /* stubs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stubs.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_relocatable_file.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65881051E750003E3539 /* macho_relocatable_file.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = macho_relocatable_file.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65D71051EC4A003E3539 /* archive_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = archive_file.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65D81051EC4A003E3539 /* archive_file.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = archive_file.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65D91051EC4A003E3539 /* lto_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lto_file.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65DA1051EC4A003E3539 /* lto_file.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = lto_file.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_dylib_file.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = macho_dylib_file.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA6784105700C2003E3539 /* opaque_section_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_section_file.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA6785105700C2003E3539 /* opaque_section_file.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = opaque_section_file.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA67B410570C41003E3539 /* dtrace_dof.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = dtrace_dof.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA67B510570C41003E3539 /* dtrace_dof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtrace_dof.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA687A10572E27003E3539 /* InputFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InputFiles.cpp; path = src/ld/InputFiles.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA687B10572E27003E3539 /* InputFiles.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = InputFiles.h; path = src/ld/InputFiles.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA69B410583C0C003E3539 /* SymbolTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolTable.cpp; path = src/ld/SymbolTable.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA69B510583C0C003E3539 /* SymbolTable.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = SymbolTable.h; path = src/ld/SymbolTable.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA69BF10583E19003E3539 /* Resolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resolver.cpp; path = src/ld/Resolver.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AA69C010583E19003E3539 /* Resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Resolver.h; path = src/ld/Resolver.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AB1063107D380700E54C9E /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = got.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AB1064107D380700E54C9E /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = got.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AE20FD1107D1440007ED5D /* dylibs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dylibs.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9AE20FE1107D1440007ED5D /* dylibs.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = dylibs.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9B670080DDA176100E6D0DA /* unwinddump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unwinddump; sourceTree = BUILT_PRODUCTS_DIR; }; - F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindDump.cpp; path = src/other/unwinddump.cpp; sourceTree = ""; }; - F9B813810EC2653000F94C13 /* unwinddump.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = unwinddump.1; path = doc/man/man1/unwinddump.1; sourceTree = ""; }; - F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOTrie.hpp; path = src/abstraction/MachOTrie.hpp; sourceTree = ""; }; - F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = src/other/dyldinfo.cpp; sourceTree = ""; }; + F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindDump.cpp; path = src/other/unwinddump.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9B813810EC2653000F94C13 /* unwinddump.1 */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.man; name = unwinddump.1; path = doc/man/man1/unwinddump.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOTrie.hpp; path = src/abstraction/MachOTrie.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = src/other/dyldinfo.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA51610ECE58BE00D1D62E /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; }; - F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_classic.hpp; sourceTree = ""; }; - F9BA8A7F1096150F0097A440 /* stub_x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86.hpp; sourceTree = ""; }; - F9BA955C10A233000097A440 /* huge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = huge.cpp; sourceTree = ""; }; - F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = ""; }; - F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = ""; }; - F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = ""; }; - F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; }; - F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; }; - F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; }; - F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; }; - F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; }; - F9CC24161461FB4300A92174 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = ""; }; - F9CC24171461FB4300A92174 /* memutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memutils.h; sourceTree = ""; }; - F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = ""; }; - F9CCF761144CE1AD007CB524 /* create_configure */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = create_configure; path = src/create_configure; sourceTree = ""; }; + F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9BA8A7F1096150F0097A440 /* stub_x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9BA955C10A233000097A440 /* huge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = huge.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9CC24161461FB4300A92174 /* endian.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = endian.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9CC24171461FB4300A92174 /* memutils.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = memutils.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9CC24181461FB4300A92174 /* superblob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = superblob.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9CCF761144CE1AD007CB524 /* create_configure */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.script.sh; name = create_configure; path = src/create_configure; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EA72CB097454A6008B4F1D /* machocheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = machocheck; sourceTree = BUILT_PRODUCTS_DIR; }; - F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = ""; }; + F9EA72D4097454FF008B4F1D /* machochecker.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = machochecker.cpp; path = src/other/machochecker.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EA7582097882F3008B4F1D /* debugline.c */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.c; name = debugline.c; path = src/ld/debugline.c; sourceTree = ""; tabWidth = 8; usesTabs = 1; }; - F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; }; + F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; - F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = ""; }; + F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 80a4e81..ca76609 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -222,6 +222,9 @@ #ifndef CPU_SUBTYPE_ARM_V7K #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) #endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif #ifndef LC_SOURCE_VERSION @@ -280,6 +283,18 @@ static const ArchInfo archInfoArray[] = { #if SUPPORT_ARCH_armv7 { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, "thumbv7-", "armv7-", true, true }, #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7f + { "armv7f", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F, "thumbv7f-", "", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7k + { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K, "thumbv7k-", "", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7s + { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S, "thumbv7s-", "armv7s", true, true }, + #define SUPPORT_ARCH_arm_any 1 #endif { NULL, 0, 0, NULL, NULL, false, false } }; diff --git a/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp index 76f575a..1885e48 100644 --- a/ld64/src/abstraction/MachOTrie.hpp +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -373,7 +373,8 @@ inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector entries; processExportNode(start, start, end, cummulativeString, 0, entries); // to preserve tie layout order, sort by node offset @@ -382,6 +383,7 @@ inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector::iterator it=entries.begin(); it != entries.end(); ++it) output.push_back(it->entry); + delete cummulativeString; } diff --git a/ld64/src/create_configure b/ld64/src/create_configure index 5375911..1a85b46 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -16,7 +16,7 @@ fi for ANARCH in ${RC_SUPPORTED_ARCHS} do - KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,i386,x86_64," + KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,i386,x86_64," FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` if [ $FOUND ]; then echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index e425fd4..0885715 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -60,8 +60,6 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract // overrides of ld::Atom virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "mach-o header and load commands"; } virtual uint64_t size() const; virtual uint64_t objectAddress() const { return _address; } @@ -667,6 +665,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* 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 if ( (strncmp(sect->sectionName(), "__objc_nlcatlist", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; else return S_REGULAR; case ld::Section::typeCode: diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 766b17c..c0b5cdb 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -80,16 +80,15 @@ class IgnoredFile : public ld::File { class DSOHandleAtom : public ld::Atom { public: DSOHandleAtom(const char* nm, ld::Atom::Scope sc, - ld::Atom::SymbolTableInclusion inc, bool preload=false) - : ld::Atom(preload ? _s_section_preload : _s_section, - ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::SymbolTableInclusion inc, ld::Section& sect=_s_section) + : ld::Atom(sect, ld::Atom::definitionRegular, + (sect == _s_section_text) ? ld::Atom::combineByName : ld::Atom::combineNever, + // make "weak def" so that link succeeds even if app defines __dso_handle sc, ld::Atom::typeUnclassified, inc, true, false, false, ld::Atom::Alignment(1)), _name(nm) {} virtual 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 const char* name() const { return _name; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const @@ -100,6 +99,7 @@ class DSOHandleAtom : public ld::Atom { static ld::Section _s_section; static ld::Section _s_section_preload; + static ld::Section _s_section_text; static DSOHandleAtom _s_atomAll; static DSOHandleAtom _s_atomExecutable; static DSOHandleAtom _s_atomDylib; @@ -107,18 +107,21 @@ class DSOHandleAtom : public ld::Atom { static DSOHandleAtom _s_atomDyld; static DSOHandleAtom _s_atomObjectFile; static DSOHandleAtom _s_atomPreload; + static DSOHandleAtom _s_atomPreloadDSO; private: const char* _name; }; ld::Section DSOHandleAtom::_s_section("__TEXT", "__mach_header", ld::Section::typeMachHeader, true); ld::Section DSOHandleAtom::_s_section_preload("__HEADER", "__mach_header", ld::Section::typeMachHeader, true); +ld::Section DSOHandleAtom::_s_section_text("__TEXT", "__text", ld::Section::typeCode, false); DSOHandleAtom DSOHandleAtom::_s_atomAll("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); DSOHandleAtom DSOHandleAtom::_s_atomExecutable("__mh_execute_header", ld::Atom::scopeGlobal, ld::Atom::symbolTableInAndNeverStrip); DSOHandleAtom DSOHandleAtom::_s_atomDylib("__mh_dylib_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); DSOHandleAtom DSOHandleAtom::_s_atomBundle("__mh_bundle_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); DSOHandleAtom DSOHandleAtom::_s_atomDyld("__mh_dylinker_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); DSOHandleAtom DSOHandleAtom::_s_atomObjectFile("__mh_object_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); -DSOHandleAtom DSOHandleAtom::_s_atomPreload("__mh_preload_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, true); +DSOHandleAtom DSOHandleAtom::_s_atomPreload("__mh_preload_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, _s_section_preload); +DSOHandleAtom DSOHandleAtom::_s_atomPreloadDSO("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, _s_section_text); @@ -131,8 +134,6 @@ class PageZeroAtom : public ld::Atom { _size(sz) {} virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return "page zero"; } virtual uint64_t size() const { return _size; } virtual uint64_t objectAddress() const { return 0; } @@ -159,8 +160,6 @@ class CustomStackAtom : public ld::Atom { _size(sz) {} virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return "custom stack"; } virtual uint64_t size() const { return _size; } virtual uint64_t objectAddress() const { return 0; } @@ -286,7 +285,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } // see if it is an llvm object file - objResult = lto::parse(p, len, info.path, info.modTime, _options.architecture(), _options.subArchitecture(), _options.logAllFiles()); + objResult = lto::parse(p, len, info.path, info.modTime, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles()); if ( objResult != NULL ) { OSAtomicAdd64(len, &_totalObjectSize); OSAtomicIncrement32(&_totalObjectLoaded); @@ -1053,7 +1052,9 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) case Options::kPreload: // add implicit __mh_preload_header label handler.doAtom(DSOHandleAtom::_s_atomPreload); - handler.doAtom(DSOHandleAtom::_s_atomAll); + // add implicit __dso_handle label, but put it in __text section because + // with -preload the mach_header is no in the address space. + handler.doAtom(DSOHandleAtom::_s_atomPreloadDSO); break; case Options::kObjectFile: handler.doAtom(DSOHandleAtom::_s_atomObjectFile); diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index e436082..c45e34f 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -113,8 +113,6 @@ class LinkEditAtom : public ld::Atom // overrides of ld::Atom virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual uint64_t objectAddress() const { return 0; } virtual uint64_t size() const; virtual void copyRawContent(uint8_t buffer[]) const; @@ -1278,7 +1276,7 @@ template void FunctionStartsAtom::encode() const { this->_encodedData.reserve(8192); - const uint64_t badAddress = 1; + const uint64_t badAddress = 0xFFFFFFFFFFFFFFFF; uint64_t addr = badAddress; // delta compress all function addresses for (std::vector::iterator it = this->_state.sections.begin(); it != this->_state.sections.end(); ++it) { @@ -1350,19 +1348,19 @@ class DataInCodeAtom : public LinkEditAtom entry.set_length(len); switch ( kind ) { case ld::Fixup::kindDataInCodeStartData: - entry.set_kind(1); + entry.set_kind(DICE_KIND_DATA); break; case ld::Fixup::kindDataInCodeStartJT8: - entry.set_kind(2); + entry.set_kind(DICE_KIND_JUMP_TABLE8); break; case ld::Fixup::kindDataInCodeStartJT16: - entry.set_kind(3); + entry.set_kind(DICE_KIND_JUMP_TABLE16); break; case ld::Fixup::kindDataInCodeStartJT32: - entry.set_kind(4); + entry.set_kind(DICE_KIND_JUMP_TABLE32); break; case ld::Fixup::kindDataInCodeStartJTA32: - entry.set_kind(5); + entry.set_kind(DICE_KIND_ABS_JUMP_TABLE32); break; default: assert(0 && "bad L$start$ label to encode"); @@ -1500,6 +1498,8 @@ void DependentDRAtom::encode() const for(size_t i=0; i < topBlob->length(); ++i) _encodedData.append_byte(data[i]); + this->_encodedData.pad_to_size(sizeof(pint_t)); + this->_encoded = true; } diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 172bb4c..4df2d5f 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -49,8 +49,6 @@ class ClassicLinkEditAtom : public ld::Atom // overrides of ld::Atom virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual uint64_t objectAddress() const { return 0; } virtual void encode() = 0; @@ -230,7 +228,6 @@ class SymbolTableAtom : public ClassicLinkEditAtom uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool); uint64_t valueForStab(const ld::relocatable::File::Stab& stab); uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab); - void addDataInCodeLabels(const ld::Atom* atom, uint32_t& symbolIndex); mutable std::vector > _globals; @@ -624,48 +621,6 @@ bool SymbolTableAtom::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, } -template -void SymbolTableAtom::addDataInCodeLabels(const ld::Atom* atom, uint32_t& symbolIndex) -{ - char label[64]; - for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { - label[0] = '\0'; - switch ( fit->kind ) { - case ld::Fixup::kindDataInCodeStartData: - sprintf(label, "L$start$data$%03u", symbolIndex); - break; - case ld::Fixup::kindDataInCodeStartJT8: - sprintf(label, "L$start$jt8$%03u", symbolIndex); - break; - case ld::Fixup::kindDataInCodeStartJT16: - sprintf(label, "L$start$jt16$%03u", symbolIndex); - break; - case ld::Fixup::kindDataInCodeStartJT32: - sprintf(label, "L$start$jt32$%03u", symbolIndex); - break; - case ld::Fixup::kindDataInCodeStartJTA32: - sprintf(label, "L$start$jta32$%03u", symbolIndex); - break; - case ld::Fixup::kindDataInCodeEnd: - sprintf(label, "L$start$code$%03u", symbolIndex); - break; - default: - break; - } - if ( label[0] != '\0' ) { - macho_nlist

entry; - entry.set_n_type(N_SECT); - entry.set_n_sect(atom->machoSection()); - entry.set_n_desc(0); - entry.set_n_value(atom->finalAddress() + fit->offsetInAtom); - entry.set_n_strx(this->_writer._stringPoolAtom->add(label)); - _locals.push_back(entry); - ++symbolIndex; - } - } -} - - template void SymbolTableAtom::encode() { @@ -696,15 +651,6 @@ void SymbolTableAtom::encode() if ( this->addLocal(atom, this->_writer._stringPoolAtom) ) this->_writer._atomToSymbolIndex[atom] = symbolIndex++; } - // recreate L$start$ labels in -r mode - if ( (_options.outputKind() == Options::kObjectFile) && this->_writer.hasDataInCode ) { - for (std::vector::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) { - this->addDataInCodeLabels(*it, symbolIndex); - } - for (std::vector::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) { - this->addDataInCodeLabels(*it, symbolIndex); - } - } this->_writer._localSymbolsCount = symbolIndex; @@ -1286,6 +1232,15 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSecti relocs.push_back(reloc1); } break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_TLV); + relocs.push_back(reloc1); + break; default: assert(0 && "need to handle -r reloc"); @@ -1307,7 +1262,7 @@ uint32_t SectionRelocationsAtom::sectSymNum(bool external, const ld::Atom* ta } template <> -void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, const Entry& entry, std::vector >& relocs) { macho_relocation_info

reloc1; @@ -1323,8 +1278,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* fromExternal = entry.fromTargetUsesExternalReloc; fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); } - - + switch ( entry.kind ) { case ld::Fixup::kindStoreX86PCRel32: case ld::Fixup::kindStoreX86BranchPCRel32: @@ -1451,6 +1405,17 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* relocs.push_back(reloc1); } break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86Abs32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(entry.kind == ld::Fixup::kindStoreX86PCRel32TLVLoad); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RLEOC_TLV); + relocs.push_back(reloc1); + break; default: assert(0 && "need to handle -r reloc"); @@ -1458,6 +1423,7 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } + #if SUPPORT_ARCH_arm_any template <> void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 58d49a7..543a348 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -160,7 +160,7 @@ Options::Options(int argc, const char* argv[]) fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false), fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false), - fDataInCodeInfoLoadCommand(false), + fDataInCodeInfoLoadCommand(false), fDataInCodeInfoLoadCommandForcedOn(false), fDataInCodeInfoLoadCommandForcedOff(false), fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false), fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false), fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false), @@ -301,7 +301,8 @@ 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 ( fIOSVersionMin != ld::iOSVersionUnset ) + // simulator apps need to use MacOSX max-prot + if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fArchitecture != CPU_TYPE_I386) ) return initialSegProtection(segName); for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -2699,10 +2700,12 @@ void Options::parse(int argc, const char* argv[]) fFunctionStartsForcedOn = false; } else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) { - fDataInCodeInfoLoadCommand = false; + fDataInCodeInfoLoadCommandForcedOff = true; + fDataInCodeInfoLoadCommandForcedOn = false; } else if ( strcmp(arg, "-data_in_code_info") == 0 ) { - fDataInCodeInfoLoadCommand = true; + fDataInCodeInfoLoadCommandForcedOn = true; + fDataInCodeInfoLoadCommandForcedOff = false; } else if ( strcmp(arg, "-object_path_lto") == 0 ) { fTempLtoObjectPath = argv[++i]; @@ -3243,7 +3246,8 @@ void Options::reconfigureDefaults() // iOS 5.0 and later use new MH_KEXT_BUNDLE type fMakeCompressedDyldInfo = false; fMakeCompressedDyldInfoForceOff = true; - fAllowTextRelocs = true; + // kexts are PIC in iOS 6.0 and later + fAllowTextRelocs = (fIOSVersionMin < ld::iOS_6_0); fKextsUseStubs = !fAllowTextRelocs; fUndefinedTreatment = kUndefinedDynamicLookup; break; @@ -3639,21 +3643,21 @@ void Options::reconfigureDefaults() // default to adding functions start for dynamic code, static code must opt-in switch ( fOutputKind ) { - case Options::kObjectFile: - fFunctionStartsLoadCommand = false; - fDataInCodeInfoLoadCommand = false; - break; case Options::kPreload: case Options::kStaticExecutable: case Options::kKextBundle: - fDataInCodeInfoLoadCommand = false; + if ( fDataInCodeInfoLoadCommandForcedOn ) + fDataInCodeInfoLoadCommand = true; if ( fFunctionStartsForcedOn ) fFunctionStartsLoadCommand = true; break; + case Options::kObjectFile: case Options::kDynamicExecutable: case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: + if ( !fDataInCodeInfoLoadCommandForcedOff ) + fDataInCodeInfoLoadCommand = true; if ( !fFunctionStartsForcedOff ) fFunctionStartsLoadCommand = true; break; @@ -3695,7 +3699,12 @@ void Options::reconfigureDefaults() fNeedsThreadLoadCommand = true; } else { - if ( minOS(ld::mac10_8, ld::iOS_Future) ) { + if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fArchitecture == CPU_TYPE_I386) ) { + // don't use LC_MAIN for simulator until min host OS is 10.8 for simulator + fNeedsThreadLoadCommand = true; + fEntryPointLoadCommand = false; + } + else if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { fEntryPointLoadCommand = true; fEntryName = "_main"; } @@ -3731,7 +3740,7 @@ void Options::reconfigureDefaults() fSourceVersionLoadCommand = false; } else { - if ( minOS(ld::mac10_8, ld::iOS_Future) ) { + if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { fSourceVersionLoadCommand = true; } else @@ -3757,7 +3766,7 @@ void Options::reconfigureDefaults() fDependentDRInfo = false; } else { - if ( minOS(ld::mac10_8, ld::iOS_Future) ) + if ( minOS(ld::mac10_8, ld::iOS_6_0) ) fDependentDRInfo = true; else fDependentDRInfo = false; diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index be6dc56..4289e68 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -552,6 +552,8 @@ class Options bool fFunctionStartsForcedOn; bool fFunctionStartsForcedOff; bool fDataInCodeInfoLoadCommand; + bool fDataInCodeInfoLoadCommandForcedOn; + bool fDataInCodeInfoLoadCommandForcedOff; bool fCanReExportSymbols; bool fObjcCategoryMerging; bool fPageAlignDataAtoms; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 6fc91e8..0f11536 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -856,7 +856,7 @@ void OutputFile::rangeCheckBranch32(int64_t displacement, ld::Internal& state, c printSectionLayout(state); const ld::Atom* target; - throwf("32-bit branch out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + throwf("32-bit branch out of range (%lld max is +/-2GB): from %s (0x%08llX) to %s (0x%08llX)", displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), addressOf(state, fixup, &target)); } @@ -2426,6 +2426,8 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: @@ -3055,6 +3057,11 @@ bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* t } } } + + if ( (_options.architecture() == CPU_TYPE_I386) && (_options.outputKind() == Options::kObjectFile) ) { + if ( target->contentType() == ld::Atom::typeTLV ) + return true; + } // most architectures use external relocations only for references // to a symbol in another translation unit or for references to "weak symbols" or tentative definitions @@ -3112,7 +3119,15 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio // pc-rel instructions are funny. If the target is _foo+8 and _foo is // external, then the pc-rel instruction *evalutates* to the address 8. if ( targetUsesExternalReloc ) { - if ( isPcRelStore(fixupWithStore->kind) ) { + // TLV support for i386 acts like RIP relative addressing + // The addend is the offset from the PICBase to the end of the instruction + if ( (_options.architecture() == CPU_TYPE_I386) + && (_options.outputKind() == Options::kObjectFile) + && (fixupWithStore->kind == ld::Fixup::kindStoreX86PCRel32TLVLoad) ) { + fixupWithTarget->contentAddendOnly = true; + fixupWithStore->contentAddendOnly = true; + } + else if ( isPcRelStore(fixupWithStore->kind) ) { fixupWithTarget->contentDetlaToAddendOnly = true; fixupWithStore->contentDetlaToAddendOnly = true; } @@ -3440,17 +3455,21 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) const ld::Atom* atom = *it; const ld::File* atomFile = atom->file(); const ld::relocatable::File* atomObjFile = dynamic_cast(atomFile); - const char* newDirPath; - const char* newFilename; //fprintf(stderr, "debug note for %s\n", atom->name()); - // guard against dwarf info that has no directory - if ( atom->translationUnitSource(&newDirPath, &newFilename) && (newDirPath != NULL)) { + const char* newPath = atom->translationUnitSource(); + if ( newPath != NULL ) { + const char* newDirPath; + const char* newFilename; + const char* lastSlash = strrchr(newPath, '/'); + if ( lastSlash == NULL ) + continue; + newFilename = lastSlash+1; + char* temp = strdup(newPath); + newDirPath = temp; + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + temp[lastSlash-newPath+1] = '\0'; // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - size_t len = strlen(newDirPath); - if ( (newDirPath != NULL) && (len > 1 ) && (newDirPath[len-1] != '/') ) - asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( (filename == NULL) || (strcmp(newFilename,filename) != 0) ) { if ( filename != NULL ) { // translation unit change, emit ending SO ld::relocatable::File::Stab endFileStab; diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 3a28788..588def7 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -80,8 +80,6 @@ class UndefinedProxyAtom : public ld::Atom _name(nm) {} // overrides of ld::Atom virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return 0; } @@ -116,8 +114,8 @@ class AliasAtom : public ld::Atom // overrides of ld::Atom virtual const ld::File* file() const { return _aliasOf.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return _aliasOf.translationUnitSource(dir, nm); } + virtual const char* translationUnitSource() const + { return _aliasOf.translationUnitSource(); } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return _aliasOf.objectAddress(); } @@ -149,8 +147,6 @@ class SectionBoundaryAtom : public ld::Atom static SectionBoundaryAtom* makeOldSectionBoundaryAtom(const char* name, bool start); // overrides of ld::Atom - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } @@ -215,8 +211,6 @@ class SegmentBoundaryAtom : public ld::Atom static SegmentBoundaryAtom* makeOldSegmentBoundaryAtom(const char* name, bool start); // overrides of ld::Atom - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } @@ -794,7 +788,7 @@ class NotLiveLTO { } }; -void Resolver::deadStripOptimize() +void Resolver::deadStripOptimize(bool force) { // only do this optimization with -dead_strip if ( ! _options.deadCodeStrip() ) @@ -856,7 +850,7 @@ void Resolver::deadStripOptimize() } } - if ( _haveLLVMObjs ) { + if ( _haveLLVMObjs && !force ) { // don't remove combinable atoms, they may come back in lto output _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end()); } @@ -1293,8 +1287,10 @@ void Resolver::fillInInternalState() // make sure there is a __text section so that codesigning works if ( (_options.outputKind() == Options::kDynamicLibrary) || (_options.outputKind() == Options::kDynamicBundle) ) _internal.getFinalSection(ld::Section("__TEXT", "__text", ld::Section::typeCode)); +} - // add entry point +void Resolver::fillInEntryPoint() +{ _internal.entryPoint = this->entryPoint(true); } @@ -1320,7 +1316,7 @@ void Resolver::linkTimeOptimize() // only do work here if some llvm obj files where loaded if ( ! _haveLLVMObjs ) return; - + // run LLVM lto code-gen lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); @@ -1370,7 +1366,7 @@ void Resolver::linkTimeOptimize() (const_cast(*it))->setLive((*it)->dontDeadStrip()); } // and re-compute dead code - this->deadStripOptimize(); + this->deadStripOptimize(true); } if ( _options.outputKind() == Options::kObjectFile ) { @@ -1431,6 +1427,7 @@ void Resolver::resolve() this->checkUndefines(); this->checkDylibSymbolCollisions(); this->removeCoalescedAwayAtoms(); + this->fillInEntryPoint(); this->linkTimeOptimize(); this->fillInInternalState(); this->tweakWeakness(); diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index c761990..a267489 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -80,7 +80,7 @@ class Resolver : public ld::File::AtomHandler void initializeState(); void buildAtomList(); void addInitialUndefines(); - void deadStripOptimize(); + void deadStripOptimize(bool force=false); void resolveUndefines(); void checkUndefines(bool force=false); void checkDylibSymbolCollisions(); @@ -88,6 +88,7 @@ class Resolver : public ld::File::AtomHandler void fillInInternalState(); void fillInHelpersInInternalState(); void removeCoalescedAwayAtoms(); + void fillInEntryPoint(); void linkTimeOptimize(); void convertReferencesToIndirect(const ld::Atom& atom); const ld::Atom* entryPoint(bool searchArchives); diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 24e9c48..1a234db 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -378,7 +378,7 @@ static void validateFixups(const ld::Atom& atom) uint32_t curClusterOffsetInAtom = 0; for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize); - assert((fit->offsetInAtom < atom.size()) || (fit->offsetInAtom == 0)); + assert((fit->offsetInAtom <= atom.size()) || (fit->offsetInAtom == 0)); if ( fit->firstInCluster() ) { assert(lastWasClusterEnd); curClusterOffsetInAtom = fit->offsetInAtom; diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index c33bff6..ed021ff 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -158,7 +158,7 @@ enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, mac10_Future=0x10000000 }; enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, - iOS_Future=0x10000000}; + iOS_6_0=0x00060000, iOS_Future=0x10000000}; namespace relocatable { // @@ -659,7 +659,7 @@ class Atom bool finalAddressMode() const { return (_mode == modeFinalAddress); } #endif virtual const File* file() const = 0; - virtual bool translationUnitSource(const char** dir, const char** name) const = 0; + virtual const char* translationUnitSource() const { return NULL; } virtual const char* name() const = 0; virtual uint64_t objectAddress() const = 0; virtual uint64_t size() const = 0; diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 78d8912..29ff25f 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -384,7 +384,7 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem } // see if member is llvm bitcode file result = lto::parse(member->content(), member->contentSize(), - mPath, member->modificationTime(), + mPath, member->modificationTime(), ordinal, _objOpts.architecture, _objOpts.subType, _logAllFiles); if ( result != NULL ) { MemberState state = {result, member, false, false, memberIndex}; diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 0bc1831..c9c4ed0 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -63,8 +63,6 @@ class InternalAtom : public ld::Atom InternalAtom(class File& f); // overrides of ld::Atom virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "import-atom"; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return 0; } @@ -90,8 +88,8 @@ class InternalAtom : public ld::Atom class File : public ld::relocatable::File { public: - File(const char* path, time_t mTime, const uint8_t* content, - uint32_t contentLength, cpu_type_t arch); + File(const char* path, time_t mTime, ld::File::Ordinal ordinal, + const uint8_t* content, uint32_t contentLength, cpu_type_t arch); virtual ~File(); // overrides of ld::File @@ -150,8 +148,8 @@ class Atom : public ld::Atom // overrides of ld::Atom virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } + virtual const char* translationUnitSource() const + { return (_compiledAtom ? _compiledAtom->translationUnitSource() : NULL); } virtual const char* name() const { return _name; } virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } @@ -198,7 +196,7 @@ class Parser static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } static bool optimize( const std::vector& allAtoms, ld::Internal& state, @@ -279,10 +277,10 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) return NULL; } -File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, +File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) { - File* f = new File(path, modTime, fileContent, fileLength, architecture); + File* f = new File(path, modTime, ordinal, fileContent, fileLength, architecture); _s_files.push_back(f); if ( logAllFiles ) printf("%s\n", path); @@ -317,8 +315,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons -File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) - : ld::relocatable::File(pth,mTime,ld::File::Ordinal::LTOOrdinal()), _architecture(arch), _internalAtom(*this), +File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) + : ld::relocatable::File(pth,mTime,ordinal), _architecture(arch), _internalAtom(*this), _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), @@ -824,12 +822,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // main function used by linker to instantiate ld::Files // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, + const char* path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) { Mutex lock; if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, architecture, subarch, logAllFiles); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); else return NULL; } diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index 492d32c..3503fa9 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -38,7 +38,7 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, + const char* path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); struct OptimizeOptions { diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 11c03ac..20b062b 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -68,8 +68,6 @@ class ExportAtom : public ld::Atom _file(f), _name(nm), _address(address) {} // overrides of ld::Atom virtual const ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return _address; } @@ -101,8 +99,6 @@ class ImportAtom : public ld::Atom // overrides of ld::Atom virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "import-atom"; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return 0; } @@ -389,6 +385,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // pass 2 builds list of all dependent libraries _dependentDylibs.reserve(dependentLibCount); cmd = cmds; + unsigned int reExportDylibCount = 0; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { case LC_LOAD_DYLIB: @@ -397,6 +394,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( compressedLinkEdit && !linkingFlatNamespace ) break; case LC_REEXPORT_DYLIB: + ++reExportDylibCount; Dependent entry; entry.path = strdup(((macho_dylib_command

*)cmd)->name()); entry.dylib = NULL; @@ -409,7 +407,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } // verify MH_NO_REEXPORTED_DYLIBS bit was correct if ( compressedLinkEdit && !linkingFlatNamespace ) { - assert(_dependentDylibs.size() != 0); + assert(reExportDylibCount != 0); } // pass 3 add re-export info cmd = cmds; diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 50f5327..e6c3efe 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -75,7 +75,7 @@ class File : public ld::relocatable::File _sectionsArray(NULL), _atomsArray(NULL), _sectionsArrayCount(0), _atomsArrayCount(0), _debugInfoKind(ld::relocatable::File::kDebugInfoNone), - _dwarfTranslationUnitDir(NULL), _dwarfTranslationUnitFile(NULL), + _dwarfTranslationUnitPath(NULL), _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), _objConstraint(ld::File::objcConstraintNone), @@ -94,7 +94,7 @@ class File : public ld::relocatable::File virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } virtual const std::vector* stabs() const { return &_stabs; } virtual bool canScatterAtoms() const { return _canScatterAtoms; } - bool translationUnitSource(const char** dir, const char** name) const; + virtual const char* translationUnitSource() const; const uint8_t* fileContent() { return _fileContent; } private: @@ -115,8 +115,7 @@ class File : public ld::relocatable::File std::vector _lineInfos; std::vector_stabs; ld::relocatable::File::DebugInfoKind _debugInfoKind; - const char* _dwarfTranslationUnitDir; - const char* _dwarfTranslationUnitFile; + const char* _dwarfTranslationUnitPath; const macho_section

* _dwarfDebugInfoSect; const macho_section

* _dwarfDebugAbbrevSect; const macho_section

* _dwarfDebugLineSect; @@ -673,8 +672,8 @@ class Atom : public ld::Atom public: // overrides of ld::Atom virtual ld::File* file() const { return §().file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return sect().file().translationUnitSource(dir, nm); } + virtual const char* translationUnitSource() const + { return sect().file().translationUnitSource(); } virtual const char* name() const { return _name; } virtual uint64_t size() const { return _size; } virtual uint64_t objectAddress() const { return _objAddress; } @@ -973,6 +972,8 @@ class Parser bool convertUnwindInfo() { return _convertUnwindInfo; } bool hasDataInCodeLabels() { return _hasDataInCodeLabels; } + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } + macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target); void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase); @@ -1090,7 +1091,9 @@ class Parser const macho_section

* _sectionsStart; uint32_t _machOSectionsCount; bool _hasUUID; - + macho_data_in_code_entry

* _dataInCodeStart; + macho_data_in_code_entry

* _dataInCodeEnd; + // filled in by parse() CFISection* _EHFrameSection; CUSection* _compactUnwindSection; @@ -1120,6 +1123,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), + _dataInCodeStart(NULL), _dataInCodeEnd(NULL), _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL), _tentativeDefinitionCount(0), _absoluteSymbolCount(0), _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), @@ -1724,7 +1728,14 @@ bool Parser::parseLoadCommands() case LC_UUID: _hasUUID = true; break; - + case LC_DATA_IN_CODE: + { + const macho_linkedit_data_command

* dc = (macho_linkedit_data_command

*)cmd; + _dataInCodeStart = (macho_data_in_code_entry

*)(_fileContent + dc->dataoff()); + _dataInCodeEnd = (macho_data_in_code_entry

*)(_fileContent + dc->dataoff() + dc->datasize()); + if ( _dataInCodeEnd > (macho_data_in_code_entry

*)endOfFile ) + throw "LC_DATA_IN_CODE table extends beyond end of file"; + } default: if ( cmd->cmd() == macho_segment_command

::CMD ) { if ( segment != NULL ) @@ -2965,14 +2976,27 @@ void Parser::parseDebugInfo() return; uint64_t stmtList; - if ( !read_comp_unit(&_file->_dwarfTranslationUnitFile, &_file->_dwarfTranslationUnitDir, &stmtList) ) { + const char* tuDir; + const char* tuName; + if ( !read_comp_unit(&tuName, &tuDir, &stmtList) ) { // if can't parse dwarf, warn and give up - _file->_dwarfTranslationUnitFile = NULL; - _file->_dwarfTranslationUnitDir = NULL; + _file->_dwarfTranslationUnitPath = NULL; warning("can't parse dwarf compilation unit info in %s", _path); _file->_debugInfoKind = ld::relocatable::File::kDebugInfoNone; return; } + if ( (tuName != NULL) && (tuName[1] == '/') ) { + _file->_dwarfTranslationUnitPath = tuName; + } + else if ( (tuDir != NULL) && (tuName != NULL) ) { + asprintf((char**)&(_file->_dwarfTranslationUnitPath), "%s/%s", tuDir, tuName); + } + else if ( tuDir == NULL ) { + _file->_dwarfTranslationUnitPath = tuName; + } + else { + _file->_dwarfTranslationUnitPath = NULL; + } // add line number info to atoms from dwarf std::vector > entries; @@ -3455,14 +3479,9 @@ File::~File() } template -bool File::translationUnitSource(const char** dir, const char** name) const +const char* File::translationUnitSource() const { - if ( _debugInfoKind == ld::relocatable::File::kDebugInfoDwarf ) { - *dir = _dwarfTranslationUnitDir; - *name = _dwarfTranslationUnitFile; - return (_dwarfTranslationUnitFile != NULL); - } - return false; + return _dwarfTranslationUnitPath; } @@ -6190,6 +6209,44 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } + // Handle LC_DATA_IN_CODE in object files + if ( this->type() == ld::Section::typeCode ) { + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + for ( const macho_data_in_code_entry

* p = parser.dataInCodeStart(); p != parser.dataInCodeEnd(); ++p ) { + if ( (p->offset() >= startAddr) && (p->offset() < endAddr) ) { + ld::Fixup::Kind kind = ld::Fixup::kindNone; + switch ( p->kind() ) { + case DICE_KIND_DATA: + kind = ld::Fixup::kindDataInCodeStartData; + break; + case DICE_KIND_JUMP_TABLE8: + kind = ld::Fixup::kindDataInCodeStartJT8; + break; + case DICE_KIND_JUMP_TABLE16: + kind = ld::Fixup::kindDataInCodeStartJT16; + break; + case DICE_KIND_JUMP_TABLE32: + kind = ld::Fixup::kindDataInCodeStartJT32; + break; + case DICE_KIND_ABS_JUMP_TABLE32: + kind = ld::Fixup::kindDataInCodeStartJTA32; + break; + default: + kind = ld::Fixup::kindDataInCodeStartData; + warning("uknown LC_DATA_IN_CODE kind (%d) at offset 0x%08X", p->kind(), p->offset()); + break; + } + Atom* inAtom = parser.findAtomByAddress(p->offset()); + typename Parser::SourceLocation srcStart(inAtom, p->offset() - inAtom->objectAddress()); + parser.addFixup(srcStart, ld::Fixup::k1of1, kind); + typename Parser::SourceLocation srcEnd(inAtom, p->offset() + p->length() - inAtom->objectAddress()); + parser.addFixup(srcEnd, ld::Fixup::k1of1, ld::Fixup::kindDataInCodeEnd); + } + } + } + + // add follow-on fixups for aliases if ( _hasAliases ) { for(Atom* p = _beginAtoms; p < _endAtoms; ++p) { diff --git a/ld64/src/ld/parsers/opaque_section_file.cpp b/ld64/src/ld/parsers/opaque_section_file.cpp index 4098958..e60332b 100644 --- a/ld64/src/ld/parsers/opaque_section_file.cpp +++ b/ld64/src/ld/parsers/opaque_section_file.cpp @@ -36,8 +36,6 @@ namespace opaque_section { class Atom : public ld::Atom { public: virtual ld::File* file() const { return (ld::File*)&_file; } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return _size; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 06953a7..96b6d35 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -72,8 +72,6 @@ class ARMtoARMBranchIslandAtom : public ld::Atom { _finalTarget(finalTarget) { } 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 4; } virtual uint64_t objectAddress() const { return 0; } @@ -122,8 +120,6 @@ class ARMtoThumb1BranchIslandAtom : public ld::Atom { _finalTarget(finalTarget) { } 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 16; } virtual uint64_t objectAddress() const { return 0; } @@ -162,8 +158,6 @@ class Thumb2toThumbBranchIslandAtom : public ld::Atom { _finalTarget(finalTarget) { } 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 4; } virtual uint64_t objectAddress() const { return 0; } @@ -227,8 +221,6 @@ class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { _finalTarget(finalTarget) { } 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 8; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp index 443f28e..840a391 100644 --- a/ld64/src/ld/passes/branch_shim.cpp +++ b/ld64/src/ld/passes/branch_shim.cpp @@ -59,8 +59,6 @@ class Thumb2ToArmShimAtom : public ld::Atom { { 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; } @@ -100,8 +98,6 @@ class NoPICThumb2ToArmShimAtom : public ld::Atom { { 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; } @@ -141,8 +137,6 @@ class Thumb1ToArmShimAtom : public ld::Atom { { 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 16; } virtual uint64_t objectAddress() const { return 0; } @@ -189,8 +183,6 @@ class ARMtoThumbShimAtom : public ld::Atom { { 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 16; } virtual uint64_t objectAddress() const { return 0; } @@ -229,8 +221,6 @@ class NoPICARMtoThumbShimAtom : public ld::Atom { { 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; } diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 858b40f..f86aee0 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -71,8 +71,6 @@ class UnwindInfoAtom : public ld::Atom { ~UnwindInfoAtom(); 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 _headerSize+_pagesSize; } virtual uint64_t objectAddress() const { return 0; } @@ -799,8 +797,6 @@ class CompactUnwindAtom : public ld::Atom { ~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; } diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index 02055f3..175629d 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -50,8 +50,6 @@ class Atom : public ld::Atom { Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz); virtual ld::File* file() const { return (ld::File*)&_file; } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return _size; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/dylibs.cpp b/ld64/src/ld/passes/dylibs.cpp index 47b1647..af1ecf6 100644 --- a/ld64/src/ld/passes/dylibs.cpp +++ b/ld64/src/ld/passes/dylibs.cpp @@ -52,10 +52,6 @@ void doPass(const Options& opts, ld::Internal& state) { // const bool log = false; - // only optimize dylibs in final linked images - if ( opts.outputKind() == Options::kObjectFile ) - return; - // clear "willRemoved" bit on all dylibs for (std::vector::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { ld::dylib::File* aDylib = *it; diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 08eeb73..4631d5b 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -52,8 +52,6 @@ class GOTEntryAtom : public ld::Atom { { _fixup.weakImport = weakImport; internal.addAtom(*this); } 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 _target->name(); } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index cf6f1d4..d471d98 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -67,8 +67,6 @@ class ObjCImageInfoAtom : public ld::Atom { bool compaction, bool abi2); 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 "objc image info"; } virtual uint64_t size() const { return sizeof(objc_image_info); } virtual uint64_t objectAddress() const { return 0; } @@ -132,8 +130,6 @@ class MethodListAtom : public ld::Atom { std::set& deadAtoms); virtual const ld::File* file() const { return _file; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc merged method list"; } virtual uint64_t size() const { return _methodCount*3*sizeof(pint_t) + 8; } virtual uint64_t objectAddress() const { return 0; } @@ -171,8 +167,6 @@ class ProtocolListAtom : public ld::Atom { std::set& deadAtoms); virtual const ld::File* file() const { return _file; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc merged protocol list"; } virtual uint64_t size() const { return (_protocolCount+1)*sizeof(pint_t); } virtual uint64_t objectAddress() const { return 0; } @@ -210,8 +204,6 @@ class PropertyListAtom : public ld::Atom { std::set& deadAtoms); virtual const ld::File* file() const { return _file; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc merged property list"; } virtual uint64_t size() const { return _propertyCount*2*sizeof(pint_t) + 8; } virtual uint64_t objectAddress() const { return 0; } @@ -252,8 +244,6 @@ class ClassROOverlayAtom : public ld::Atom { // overrides of ld::Atom virtual const ld::File* file() const { return _atom->file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return _atom->translationUnitSource(dir, nm); } virtual const char* name() const { return _atom->name(); } virtual uint64_t size() const { return _atom->size(); } virtual uint64_t objectAddress() const { return _atom->objectAddress(); } diff --git a/ld64/src/ld/passes/stubs/stub_arm.hpp b/ld64/src/ld/passes/stubs/stub_arm.hpp index 4650ddb..5c8fc42 100644 --- a/ld64/src/ld/passes/stubs/stub_arm.hpp +++ b/ld64/src/ld/passes/stubs/stub_arm.hpp @@ -36,8 +36,6 @@ class FastBindingPointerAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "fast binder pointer"; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -63,8 +61,6 @@ class ImageCachePointerAtom : public ld::Atom { symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { pass.addAtom(*this); } virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "non-lazy pointer"; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -97,8 +93,6 @@ class StubHelperHelperAtom : public ld::Atom { { pass.addAtom(*this); } virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return " stub helpers"; } virtual uint64_t size() const { return 36; } virtual uint64_t objectAddress() const { return 0; } @@ -160,8 +154,6 @@ class StubHelperAtom : public ld::Atom { _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 12; } virtual uint64_t objectAddress() const { return 0; } @@ -206,8 +198,6 @@ class ResolverHelperAtom : public ld::Atom { _fixup5(32, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 36; } virtual uint64_t objectAddress() const { return 0; } @@ -262,8 +252,6 @@ class LazyPointerAtom : public ld::Atom { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -299,8 +287,6 @@ class NonLazyPointerAtom : public ld::Atom { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -341,8 +327,6 @@ class StubPICKextAtom : public ld::Atom { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 16; } virtual uint64_t objectAddress() const { return 0; } @@ -393,8 +377,6 @@ class StubPICAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 16; } virtual uint64_t objectAddress() const { return 0; } @@ -436,8 +418,6 @@ class StubNoPICAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 12; } virtual uint64_t objectAddress() const { return 0; } @@ -476,8 +456,6 @@ class StubCloseAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/stubs/stub_arm_classic.hpp b/ld64/src/ld/passes/stubs/stub_arm_classic.hpp index 50870f4..c7967d7 100644 --- a/ld64/src/ld/passes/stubs/stub_arm_classic.hpp +++ b/ld64/src/ld/passes/stubs/stub_arm_classic.hpp @@ -41,8 +41,6 @@ class LazyPointerAtom : public ld::Atom { { _fixup2.weakImport = weakImport; pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -80,8 +78,6 @@ class StubPICAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 16; } virtual uint64_t objectAddress() const { return 0; } @@ -123,8 +119,6 @@ class StubNoPICAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 12; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/stubs/stub_x86.hpp b/ld64/src/ld/passes/stubs/stub_x86.hpp index 0eda4ad..08879c1 100644 --- a/ld64/src/ld/passes/stubs/stub_x86.hpp +++ b/ld64/src/ld/passes/stubs/stub_x86.hpp @@ -39,8 +39,6 @@ class FastBindingPointerAtom : public ld::Atom { { pass.addAtom(*this); } 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 "fast binder pointer"; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -66,8 +64,6 @@ class ImageCachePointerAtom : public ld::Atom { symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { pass.addAtom(*this); } 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 "image cache pointer"; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -101,8 +97,6 @@ class StubHelperHelperAtom : public ld::Atom { { pass.addAtom(*this); } virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return "helper helper"; } virtual uint64_t size() const { return 12; } virtual uint64_t objectAddress() const { return 0; } @@ -159,8 +153,6 @@ class StubHelperAtom : public ld::Atom { _fixup3(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, helperHelper(pass)) { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 10; } virtual uint64_t objectAddress() const { return 0; } @@ -211,8 +203,6 @@ class ResolverHelperAtom : public ld::Atom { _fixup3(18, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, lazyPointer) { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 22; } virtual uint64_t objectAddress() const { return 0; } @@ -279,8 +269,6 @@ class LazyPointerAtom : public ld::Atom { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -314,8 +302,6 @@ class StubAtom : public ld::Atom { _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 6; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/stubs/stub_x86_64.hpp b/ld64/src/ld/passes/stubs/stub_x86_64.hpp index e3cebca..5031dbc 100644 --- a/ld64/src/ld/passes/stubs/stub_x86_64.hpp +++ b/ld64/src/ld/passes/stubs/stub_x86_64.hpp @@ -39,8 +39,6 @@ class FastBindingPointerAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "fast binder pointer"; } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } @@ -66,8 +64,6 @@ class ImageCachePointerAtom : public ld::Atom { symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); } virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "image cache pointer"; } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } @@ -101,8 +97,6 @@ class StubHelperHelperAtom : public ld::Atom { { pass.addAtom(*this); } virtual ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "helper helper"; } virtual uint64_t size() const { return 16; } virtual uint64_t objectAddress() const { return 0; } @@ -163,8 +157,6 @@ class StubHelperAtom : public ld::Atom { _fixup3(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, helperHelper(pass)) { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 10; } virtual uint64_t objectAddress() const { return 0; } @@ -215,8 +207,6 @@ class ResolverHelperAtom : public ld::Atom { _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, lazyPointer) { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 36; } virtual uint64_t objectAddress() const { return 0; } @@ -298,8 +288,6 @@ class LazyPointerAtom : public ld::Atom { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } @@ -333,8 +321,6 @@ class StubAtom : public ld::Atom { _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_lazyPointer) { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 6; } virtual uint64_t objectAddress() const { return 0; } @@ -375,8 +361,6 @@ class NonLazyPointerAtom : public ld::Atom { } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } @@ -407,8 +391,6 @@ class KextStubAtom : public ld::Atom { _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_nonLazyPointer) { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 6; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp b/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp index 14507a2..057ce61 100644 --- a/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp +++ b/ld64/src/ld/passes/stubs/stub_x86_64_classic.hpp @@ -44,8 +44,6 @@ class StubHelperAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 12; } virtual uint64_t objectAddress() const { return 0; } @@ -95,8 +93,6 @@ class LazyPointerAtom : public ld::Atom { { _fixup2.weakImport = weakImport; pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } @@ -132,8 +128,6 @@ class StubAtom : public ld::Atom { _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_lazyPointer) { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 6; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/stubs/stub_x86_classic.hpp b/ld64/src/ld/passes/stubs/stub_x86_classic.hpp index 0562218..481055d 100644 --- a/ld64/src/ld/passes/stubs/stub_x86_classic.hpp +++ b/ld64/src/ld/passes/stubs/stub_x86_classic.hpp @@ -44,8 +44,6 @@ class StubHelperAtom : public ld::Atom { { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 10; } virtual uint64_t objectAddress() const { return 0; } @@ -93,8 +91,6 @@ class LazyPointerAtom : public ld::Atom { { _fixup2.weakImport = weakImport; pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } @@ -130,8 +126,6 @@ class StubAtom : public ld::Atom { _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } virtual const ld::File* file() const { return _stubTo.file(); } - virtual bool translationUnitSource(const char** dir, const char** ) const - { return false; } virtual const char* name() const { return _stubTo.name(); } virtual uint64_t size() const { return 6; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index a102e97..eed532e 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/src/ld/passes/tlvp.cpp @@ -52,8 +52,6 @@ class TLVEntryAtom : public ld::Atom { { _fixup.weakImport = weakImport; internal.addAtom(*this); } 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 _target->name(); } virtual uint64_t size() const { return 8; } virtual uint64_t objectAddress() const { return 0; } diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 55e1068..cc673a3 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -1133,7 +1133,7 @@ static ld::relocatable::File* createReader(const char* path) return objResult; // see if it is an llvm object file - objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, sPreferredArch, sPreferredSubArch, false); + objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false); if ( objResult != NULL ) return objResult; diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 97864ca..99ddfa6 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -63,15 +63,15 @@ VERSION_OLD_LINKEDIT = -mmacosx-version-min=10.4 LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} -CXXFLAGS = -Wall +CXXFLAGS = -Wall -stdlib=libc++ IOS_SDK = $(shell xcodebuild -sdk iphoneos6.0.internal -version Path) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) override FILEARCH = arm - CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) - CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index f7eb45a..78c62e0 100644 --- a/ld64/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/ld64/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 ./header.h 0000 FUN 0000 ENSYM 0000 SO diff --git a/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c b/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c index ecbd4f2..08e2cd2 100644 --- a/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c +++ b/ld64/unit-tests/test-cases/lto-dead_strip-coalesce/main.c @@ -4,6 +4,7 @@ extern void foo1(); +extern void foo2(); void t1() @@ -24,6 +25,10 @@ void t3() strlen("str3"); } +// error with LTO and dead strip of non-lazy-pointer +void* foo2p() { + return &foo2; +} int main() { diff --git a/ld64/unit-tests/test-cases/no-uuid/Makefile b/ld64/unit-tests/test-cases/no-uuid/Makefile index dcae189..227a9c7 100644 --- a/ld64/unit-tests/test-cases/no-uuid/Makefile +++ b/ld64/unit-tests/test-cases/no-uuid/Makefile @@ -38,4 +38,4 @@ all: ${PASS_IFF_GOOD_MACHO} foo clean: - rm -rf foo + rm -rf foo foo.dSYM diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx index b5d3272..7d1ef81 100644 --- a/ld64/unit-tests/test-cases/operator-new/main.cxx +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -40,8 +40,22 @@ void* operator new(size_t s) throw (std::bad_alloc) } #endif +class Foo { + int a; + int b; +public: + void print(); +}; + +void Foo::print() { + printf("%d\n", a); +} + + int main() { + Foo* f = new Foo(); + f->print(); return 0; } diff --git a/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile b/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile index d6c5b82..cd857b9 100644 --- a/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile +++ b/ld64/unit-tests/test-cases/stabs-directory-slash/Makefile @@ -36,4 +36,4 @@ all: nm -ap outfile | grep '.*\.*test-cases.*/$$' | ${PASS_IFF_STDIN} clean: - rm outfile* + rm -rf outfile* diff --git a/ld64/unit-tests/test-cases/tlv-r/Makefile b/ld64/unit-tests/test-cases/tlv-r/Makefile new file mode 100644 index 0000000..76afb76 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-r/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that main executable can use TLVs with -dead_strip +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o #-mdynamic-no-pic + ${LD} -arch ${ARCH} -r foo.o -o foo-r.o + ${OBJECTDUMP} foo.o > foo.o.dump + ${OBJECTDUMP} foo-r.o > foo-r.o.dump + ${PASS_IFF} diff foo.o.dump foo-r.o.dump + +clean: + rm -f foo.o foo-r.o foo.o.dump foo-r.o.dump + diff --git a/ld64/unit-tests/test-cases/tlv-r/foo.c b/ld64/unit-tests/test-cases/tlv-r/foo.c new file mode 100644 index 0000000..6f6c6c6 --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-r/foo.c @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +__thread int a = 0; +__thread static int b = 0; +extern __thread int c; + +int foo() { + return a+b+c; +} + diff --git a/ld64/unit-tests/test-cases/weak_import-r/Makefile b/ld64/unit-tests/test-cases/weak_import-r/Makefile new file mode 100644 index 0000000..a29248a --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-r/Makefile @@ -0,0 +1,58 @@ +## +# Copyright (c) 2012 Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute is preserved in -r mode. +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + nm -m main.o | grep _func1 | grep -v weak >/dev/null + nm -m main.o | grep _func2 | grep weak >/dev/null + nm -m main.o | grep _func3 | grep -v weak >/dev/null + nm -m main.o | grep _func4 | grep weak >/dev/null + nm -m main.o | grep _data1 | grep -v weak >/dev/null + nm -m main.o | grep _data2 | grep weak >/dev/null + nm -m main.o | grep _data3 | grep -v weak >/dev/null + nm -m main.o | grep _data4 | grep weak >/dev/null + nm -m main.o | grep _data5 | grep -v weak >/dev/null + nm -m main.o | grep _data6 | grep weak >/dev/null + + ${LD} -r -arch ${ARCH} main.o -o main-r.o + nm -m main-r.o | grep _func1 | grep -v weak >/dev/null + nm -m main-r.o | grep _func2 | grep weak >/dev/null + nm -m main-r.o | grep _func3 | grep -v weak >/dev/null + nm -m main-r.o | grep _func4 | grep weak >/dev/null + nm -m main-r.o | grep _data1 | grep -v weak >/dev/null + nm -m main-r.o | grep _data2 | grep weak >/dev/null + nm -m main-r.o | grep _data3 | grep -v weak >/dev/null + nm -m main-r.o | grep _data4 | grep weak >/dev/null + ${PASS_IFF} true + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/weak_import-r/foo.h b/ld64/unit-tests/test-cases/weak_import-r/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-r/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/ld64/unit-tests/test-cases/weak_import-r/main.c b/ld64/unit-tests/test-cases/weak_import-r/main.c new file mode 100644 index 0000000..a08ed86 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-r/main.c @@ -0,0 +1,23 @@ +#include + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + +void* pf3; + +int main (void) +{ + // make non-lazy reference to func3 and func4 + pf3 = &func3; + if ( &func4 == NULL ) { + // make lazy reference to func1 and func2 + func1(); + func2(); + } + + return data1 + data2 + data3 + data4; +} + From 2e04bfdac4cae63df7e49cb20c5221c9de129fc2 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:21:38 +0100 Subject: [PATCH 14/48] 136 --- ld64/doc/man/man1/ld.1 | 2 +- ld64/ld64.xcodeproj/project.pbxproj | 117 ++++++++++++++++-- ld64/src/abstraction/MachOFileAbstraction.hpp | 1 + ld64/src/create_configure | 2 +- ld64/src/ld/InputFiles.cpp | 18 ++- ld64/src/ld/InputFiles.h | 7 +- ld64/src/ld/LinkEdit.hpp | 6 +- ld64/src/ld/LinkEditClassic.hpp | 8 +- ld64/src/ld/Options.cpp | 48 +++---- ld64/src/ld/Options.h | 13 +- ld64/src/ld/OutputFile.cpp | 115 ++++++++++++----- ld64/src/ld/Resolver.cpp | 39 +++++- ld64/src/ld/Resolver.h | 8 +- ld64/src/ld/SymbolTable.cpp | 4 +- ld64/src/ld/SymbolTable.h | 18 ++- ld64/src/ld/code-sign-blobs/blob.cpp | 1 + ld64/src/ld/code-sign-blobs/blob.h | 1 + ld64/src/ld/ld.cpp | 7 +- ld64/src/ld/ld.hpp | 14 +++ ld64/src/ld/parsers/archive_file.cpp | 9 +- ld64/src/ld/parsers/lto_file.cpp | 26 ++-- ld64/src/ld/parsers/macho_dylib_file.cpp | 28 ++--- .../src/ld/parsers/macho_relocatable_file.cpp | 23 +++- ld64/src/ld/passes/dtrace_dof.cpp | 13 +- ld64/src/ld/passes/got.cpp | 10 +- ld64/src/ld/passes/huge.cpp | 2 +- ld64/src/ld/passes/order.cpp | 7 +- ld64/src/ld/passes/tlvp.cpp | 1 - ld64/src/other/dyldinfo.cpp | 71 ++++++----- ld64/src/other/machochecker.cpp | 18 ++- ld64/src/other/rebase.cpp | 3 +- ld64/src/other/unwinddump.cpp | 10 +- ld64/unit-tests/include/common.makefile | 2 +- .../test-cases/lto-dylib-aliases/Makefile | 40 ++++++ .../test-cases/lto-dylib-aliases/foo.c | 7 ++ .../test-cases/lto-dylib-export_list/Makefile | 43 +++++++ .../test-cases/lto-dylib-export_list/bar.cxx | 11 ++ .../test-cases/lto-dylib-export_list/foo.cxx | 11 ++ .../test-cases/lto-dylib-export_list/foo.exp | 3 + .../test-cases/lto-dylib-export_list/foo.h | 6 + .../test-cases/lto-static-pie/Makefile | 43 +++++++ ld64/unit-tests/test-cases/lto-static-pie/a.c | 10 ++ ld64/unit-tests/test-cases/lto-static-pie/b.c | 2 + .../test-cases/lto-static-pie/main.c | 12 ++ .../test-cases/no-object-symbols/Makefile | 2 +- .../test-cases/re-export-symbol/Makefile | 4 +- .../test-cases/re-export-symbol/bar.c | 5 + .../test-cases/re-export-symbol/foo.exp | 1 + .../test-cases/re-export-symbol/main1.c | 2 + ld64/unit-tests/test-cases/tlv-dylib/Makefile | 10 +- ld64/unit-tests/test-cases/tlv-dylib/foo.c | 7 ++ ld64/unit-tests/test-cases/tlv-dylib/foo.s | 33 ----- ld64/unit-tests/test-cases/tlv-dylib/getbar.s | 29 ----- ld64/unit-tests/test-cases/tlv-dylib/getfoo.s | 29 ----- ld64/unit-tests/test-cases/tlv-dylib/main.c | 13 +- 55 files changed, 643 insertions(+), 332 deletions(-) create mode 100644 ld64/unit-tests/test-cases/lto-dylib-aliases/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dylib-aliases/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-dylib-export_list/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dylib-export_list/bar.cxx create mode 100644 ld64/unit-tests/test-cases/lto-dylib-export_list/foo.cxx create mode 100644 ld64/unit-tests/test-cases/lto-dylib-export_list/foo.exp create mode 100644 ld64/unit-tests/test-cases/lto-dylib-export_list/foo.h create mode 100644 ld64/unit-tests/test-cases/lto-static-pie/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-static-pie/a.c create mode 100644 ld64/unit-tests/test-cases/lto-static-pie/b.c create mode 100644 ld64/unit-tests/test-cases/lto-static-pie/main.c create mode 100644 ld64/unit-tests/test-cases/tlv-dylib/foo.c delete mode 100644 ld64/unit-tests/test-cases/tlv-dylib/foo.s delete mode 100644 ld64/unit-tests/test-cases/tlv-dylib/getbar.s delete mode 100644 ld64/unit-tests/test-cases/tlv-dylib/getfoo.s diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index a0850f6..c2eb01d 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -72,7 +72,7 @@ library search path is /usr/lib then /usr/local/lib. The -L option will add a n path. The default framework search path is /Library/Frameworks then /System/Library/Frameworks. (Note: previously, /Network/Library/Frameworks was at the end of the default path. If you need that functionality, you need to explicitly add -F/Network/Library/Frameworks). -The -F option will a new framework search path. The -Z option will remove +The -F option will add a new framework search path. The -Z option will remove the standard search paths. The -syslibroot option will prepend a prefix to all search paths. .Ss Two-level namespace diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 08145e5..dd74668 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -1016,6 +1016,7 @@ F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; @@ -1057,8 +1058,12 @@ INSTALL_PATH = /usr/bin; LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; - OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CPLUSPLUSFLAGS)", + ); OTHER_LDFLAGS = ( + "-stdlib=libc++", "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", "-Wl,-exported_symbol,__mh_execute_header", ); @@ -1073,13 +1078,14 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 3; + GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = ( "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", NDEBUG, @@ -1116,8 +1122,12 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; - OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CPLUSPLUSFLAGS)", + ); OTHER_LDFLAGS = ( + "-stdlib=libc++", "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", "-Wl,-exported_symbol,__mh_execute_header", ); @@ -1135,6 +1145,7 @@ F933D92009291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1154,7 +1165,14 @@ "$(DEVELOPER_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + ); OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; @@ -1170,6 +1188,7 @@ F933D92109291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1181,7 +1200,14 @@ "$(DEVELOPER_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + ); OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; @@ -1255,13 +1281,14 @@ F9849FFA10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_OPTIMIZATION_LEVEL = 3; + GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = ( "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", @@ -1297,8 +1324,12 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; - OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CPLUSPLUSFLAGS)", + ); OTHER_LDFLAGS = ( + "-stdlib=libc++", "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", "-Wl,-exported_symbol,__mh_execute_header", ); @@ -1316,6 +1347,7 @@ F9849FFB10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1335,6 +1367,7 @@ F9849FFC10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1343,7 +1376,14 @@ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/bin; - OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,__mh_execute_header", + ); PREBINDING = NO; PRODUCT_NAME = unwinddump; STRIP_INSTALLED_PRODUCT = YES; @@ -1354,6 +1394,7 @@ F9849FFD10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1366,7 +1407,14 @@ "$(DEVELOPER_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; - OTHER_LDFLAGS = "@$(DERIVED_SOURCES_DIR)/LTO_option.txt"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + ); OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; @@ -1381,12 +1429,18 @@ F9849FFE10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = "$(HOME)/bin"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = machocheck; }; @@ -1396,12 +1450,17 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; INSTALL_PATH = /usr/bin; - OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = dyldinfo; STRIP_INSTALLED_PRODUCT = YES; @@ -1490,6 +1549,7 @@ F9B670060DDA176100E6D0DA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1498,6 +1558,11 @@ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = "$(HOME)/bin"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = unwinddump; }; @@ -1506,6 +1571,7 @@ F9B670070DDA176100E6D0DA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1514,6 +1580,10 @@ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; PREBINDING = NO; PRODUCT_NAME = unwinddump; @@ -1526,6 +1596,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = NO; @@ -1542,6 +1613,11 @@ GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = /usr/local/bin; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = dyldinfo; WARNING_CFLAGS = "-Wall"; @@ -1552,12 +1628,17 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; INSTALL_PATH = /usr/bin; - OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = dyldinfo; STRIP_INSTALLED_PRODUCT = YES; @@ -1569,12 +1650,18 @@ F9EA72D0097454D5008B4F1D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; INSTALL_PATH = "$(HOME)/bin"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = machocheck; }; @@ -1583,12 +1670,18 @@ F9EA72D1097454D5008B4F1D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = "$(HOME)/bin"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = machocheck; }; @@ -1597,6 +1690,7 @@ F9EC77F10A2F8616002A3E39 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_DYNAMIC_NO_PIC = NO; @@ -1612,6 +1706,7 @@ F9EC77F20A2F8616002A3E39 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index ca76609..4fd6123 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -1467,6 +1467,7 @@ class macho_data_in_code_entry { }; + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/ld64/src/create_configure b/ld64/src/create_configure index 1a85b46..447ec7a 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -11,7 +11,7 @@ else fi if [ -z "${RC_SUPPORTED_ARCHS}" ]; then - RC_SUPPORTED_ARCHS="i386 x86_64" + RC_SUPPORTED_ARCHS="i386 x86_64 armv7 armv7s" fi for ANARCH in ${RC_SUPPORTED_ARCHS} diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index c0b5cdb..7bf136b 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -49,8 +49,6 @@ #include #include #include -#include -#include #include #include @@ -483,7 +481,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from throwf("indirect dylib at %s is not a dylib", info.path); } catch (const char* msg) { - throwf("in %s, %s", info.path, msg); + throwf("in '%s', %s", info.path, msg); } } } @@ -674,7 +672,7 @@ InputFiles::InputFiles(Options& opts, const char** archName) : _totalObjectSize(0), _totalArchiveSize(0), _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), _options(opts), _bundleLoader(NULL), - _allDirectDylibsLoaded(false), _inferredArch(false), _fileMonitor(-1), + _allDirectDylibsLoaded(false), _inferredArch(false), _exception(NULL) { // fStartCreateReadersTime = mach_absolute_time(); @@ -882,7 +880,9 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& // update stats _totalDylibsLoaded++; - _searchLibraries.push_back(LibraryInfo(reader)); + // just add direct libraries to search-first list + if ( !_allDirectDylibsLoaded ) + _searchLibraries.push_back(LibraryInfo(reader)); return reader; } @@ -1070,11 +1070,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const { // Check each input library. - std::vector::const_iterator libIterator = _searchLibraries.begin(); - - - while (libIterator != _searchLibraries.end()) { - LibraryInfo lib = *libIterator; + for (std::vector::const_iterator it=_searchLibraries.begin(); it != _searchLibraries.end(); ++it) { + LibraryInfo lib = *it; if (lib.isDylib()) { if (searchDylibs) { ld::dylib::File *dylibFile = lib.dylib(); @@ -1112,7 +1109,6 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc } } } - libIterator++; } // search indirect dylibs diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index afc2077..9b969ee 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -103,11 +103,7 @@ class InputFiles : public ld::dylib::File::DylibHandler static void parseWorkerThread(InputFiles *inputFiles); void startThread(void (*threadFunc)(InputFiles *)) const; - class CStringEquals { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> InstallNameToDylib; + typedef std::unordered_map InstallNameToDylib; const Options& _options; std::vector _inputFiles; @@ -117,7 +113,6 @@ class InputFiles : public ld::dylib::File::DylibHandler ld::dylib::File* _bundleLoader; bool _allDirectDylibsLoaded; bool _inferredArch; - int _fileMonitor; struct strcompclass { bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } }; diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index c45e34f..1149f0d 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -976,11 +976,11 @@ void ExportInfoAtom::encode() const uint64_t flags = (atom->contentType() == ld::Atom::typeTLV) ? EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL : EXPORT_SYMBOL_FLAGS_KIND_REGULAR; uint64_t other = 0; uint64_t address = atom->finalAddress() - imageBaseAddress; - if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) - flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; if ( atom->definition() == ld::Atom::definitionProxy ) { entry.name = atom->name(); entry.flags = flags | EXPORT_SYMBOL_FLAGS_REEXPORT; + if ( atom->combine() == ld::Atom::combineByName ) + entry.flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; entry.other = this->_writer.compressedOrdinalForAtom(atom); if ( entry.other == BIND_SPECIAL_DYLIB_SELF ) { warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->file()->path()); @@ -1006,6 +1006,8 @@ void ExportInfoAtom::encode() const //fprintf(stderr, "re-export %s from lib %llu as %s\n", entry.importName, entry.other, entry.name); } else { + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) + flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; if ( atom->isThumb() ) address |= 1; if ( atom->contentType() == ld::Atom::typeResolver ) { diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 4df2d5f..60a4fa9 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -32,6 +32,7 @@ #include #include +#include #include "Options.h" #include "ld.hpp" @@ -90,13 +91,8 @@ class StringPoolAtom : public ClassicLinkEditAtom uint32_t currentOffset(); private: - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; enum { kBufferSize = 0x01000000 }; - typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; + typedef std::unordered_map StringToOffset; const uint32_t _pointerSize; std::vector _fullBuffers; diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 543a348..0131911 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -404,7 +404,7 @@ void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderM ::close(fd); - // parse into symbols and add to hash_set + // parse into symbols and add to unordered_set unsigned int count = 0; char * const end = &p[stat_buf.st_size]; enum { lineStart, inSymbol, inComment } state = lineStart; @@ -1066,7 +1066,7 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, SetW ::close(fd); - // parse into symbols and add to hash_set + // parse into symbols and add to unordered_set char * const end = &p[stat_buf.st_size]; enum { lineStart, inSymbol, inComment } state = lineStart; char* symbolStart = NULL; @@ -3231,6 +3231,28 @@ void Options::reconfigureDefaults() break; } + // default to adding functions start for dynamic code, static code must opt-in + switch ( fOutputKind ) { + case Options::kPreload: + case Options::kStaticExecutable: + case Options::kKextBundle: + if ( fDataInCodeInfoLoadCommandForcedOn ) + fDataInCodeInfoLoadCommand = true; + if ( fFunctionStartsForcedOn ) + fFunctionStartsLoadCommand = true; + break; + case Options::kObjectFile: + case Options::kDynamicExecutable: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + if ( !fDataInCodeInfoLoadCommandForcedOff ) + fDataInCodeInfoLoadCommand = true; + if ( !fFunctionStartsForcedOff ) + fFunctionStartsLoadCommand = true; + break; + } + // adjust kext type based on architecture if ( fOutputKind == kKextBundle ) { switch ( fArchitecture ) { @@ -3641,28 +3663,6 @@ void Options::reconfigureDefaults() break; } - // default to adding functions start for dynamic code, static code must opt-in - switch ( fOutputKind ) { - case Options::kPreload: - case Options::kStaticExecutable: - case Options::kKextBundle: - if ( fDataInCodeInfoLoadCommandForcedOn ) - fDataInCodeInfoLoadCommand = true; - if ( fFunctionStartsForcedOn ) - fFunctionStartsLoadCommand = true; - break; - case Options::kObjectFile: - case Options::kDynamicExecutable: - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - if ( !fDataInCodeInfoLoadCommandForcedOff ) - fDataInCodeInfoLoadCommand = true; - 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::iOS_4_2) ) fCanReExportSymbols = true; diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 4289e68..41e5653 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -30,8 +30,8 @@ #include #include -#include -#include +#include +#include #include "ld.hpp" #include "Snapshot.h" @@ -342,13 +342,8 @@ class Options const char* pipelineFifo() const { return fPipelineFifo; } private: - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToOrder; - typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + typedef std::unordered_map NameToOrder; + typedef std::unordered_set NameSet; enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 0f11536..16fdd66 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -48,8 +50,7 @@ #include #include #include -#include -#include +#include #include #include @@ -873,8 +874,8 @@ void OutputFile::rangeCheckAbsolute32(int64_t displacement, ld::Internal& state, // .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 ) { + // Unlikely userland code does funky stuff like this, so warn for them, but not warn for -preload or -static + if ( (_options.outputKind() != Options::kPreload) && (_options.outputKind() != Options::kStaticExecutable) ) { 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); } @@ -1431,8 +1432,8 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; uint32_t firstDisp = (s << 10) | imm10; newInstruction = instruction | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, delta, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, instruction=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, instruction, firstDisp, nextDisp, newInstruction, delta, atom->name(), toTarget->name()); set32LE(fixUpLocation, newInstruction); } else { @@ -1601,9 +1602,9 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } catch (const char* msg) { if ( atom->file() != NULL ) - throwf("%s in %s from %s", msg, atom->name(), atom->file()->path()); + throwf("%s in '%s' from %s", msg, atom->name(), atom->file()->path()); else - throwf("%s in %s", msg, atom->name()); + throwf("%s in '%s'", msg, atom->name()); } } } @@ -1687,28 +1688,61 @@ void OutputFile::writeOutputFile(ld::Internal& state) // And it means we don't have to truncate the file when done writing (in case new is smaller than old) // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). struct stat stat_buf; - bool outputIsRegularFile = true; + bool outputIsRegularFile = false; + bool outputIsMappableFile = false; if ( stat(_options.outputFilePath(), &stat_buf) != -1 ) { if (stat_buf.st_mode & S_IFREG) { - (void)unlink(_options.outputFilePath()); - } else { + outputIsRegularFile = true; + // Don't use mmap on non-hfs volumes + struct statfs fsInfo; + if ( statfs(_options.outputFilePath(), &fsInfo) != -1 ) { + if ( strcmp(fsInfo.f_fstypename, "hfs") == 0) { + (void)unlink(_options.outputFilePath()); + outputIsMappableFile = true; + } + } + else { + outputIsMappableFile = false; + } + } + else { outputIsRegularFile = false; } } - + else { + // special files (pipes, devices, etc) must already exist + outputIsRegularFile = true; + // output file does not exist yet + char dirPath[PATH_MAX]; + strcpy(dirPath, _options.outputFilePath()); + char* end = strrchr(dirPath, '/'); + if ( end != NULL ) { + end[1] = '\0'; + struct statfs fsInfo; + if ( statfs(dirPath, &fsInfo) != -1 ) { + if ( strcmp(fsInfo.f_fstypename, "hfs") == 0) { + outputIsMappableFile = true; + } + } + } + } + + //fprintf(stderr, "outputIsMappableFile=%d, outputIsRegularFile=%d, path=%s\n", outputIsMappableFile, outputIsRegularFile, _options.outputFilePath()); + int fd; // Construct a temporary path of the form {outputFilePath}.ld_XXXXXX const char filenameTemplate[] = ".ld_XXXXXX"; char tmpOutput[PATH_MAX]; uint8_t *wholeBuffer; - if (outputIsRegularFile) { + if ( outputIsRegularFile && outputIsMappableFile ) { strcpy(tmpOutput, _options.outputFilePath()); // If the path is too long to add a suffix for a temporary name then // just fall back to using the output path. if (strlen(tmpOutput)+strlen(filenameTemplate) < PATH_MAX) { strcat(tmpOutput, filenameTemplate); fd = mkstemp(tmpOutput); - } else { + } + else { fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); } if ( fd == -1 ) @@ -1718,8 +1752,12 @@ void OutputFile::writeOutputFile(ld::Internal& state) wholeBuffer = (uint8_t *)mmap(NULL, _fileSize, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); if ( wholeBuffer == MAP_FAILED ) throwf("can't create buffer of %llu bytes for output", _fileSize); - } else { - fd = open(_options.outputFilePath(), O_WRONLY); + } + else { + if ( outputIsRegularFile ) + fd = open(_options.outputFilePath(), O_RDWR|O_CREAT, permissions); + else + fd = open(_options.outputFilePath(), O_WRONLY); if ( fd == -1 ) throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); // try to allocate buffer for entire output file content @@ -1740,7 +1778,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) if ( _options.UUIDMode() == Options::kUUIDContent ) computeContentUUID(state, wholeBuffer); - if (outputIsRegularFile) { + if ( outputIsRegularFile && outputIsMappableFile ) { if ( ::chmod(tmpOutput, permissions) == -1 ) { unlink(tmpOutput); throwf("can't set permissions on output file: %s, errno=%d", tmpOutput, errno); @@ -1749,7 +1787,8 @@ void OutputFile::writeOutputFile(ld::Internal& state) unlink(tmpOutput); throwf("can't move output file in place, errno=%d", errno); } - } else { + } + else { if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); } @@ -1853,6 +1892,10 @@ void OutputFile::buildSymbolTable(ld::Internal& state) if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotIn ) { continue; // don't add to symbol table } + if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel) + && (_options.outputKind() != Options::kObjectFile) ) { + continue; // don't add to symbol table + } if ( (atom->definition() == ld::Atom::definitionTentative) && (_options.outputKind() == Options::kObjectFile) ) { if ( _options.makeTentativeDefinitionsReal() ) { @@ -1865,7 +1908,7 @@ void OutputFile::buildSymbolTable(ld::Internal& state) } continue; } - + switch ( atom->scope() ) { case ld::Atom::scopeTranslationUnit: if ( _options.keepLocalSymbol(atom->name()) ) { @@ -2341,9 +2384,13 @@ int OutputFile::compressedOrdinalForAtom(const ld::Atom* target) // regular ordinal const ld::dylib::File* dylib = dynamic_cast(target->file()); - if ( dylib != NULL ) - return _dylibToOrdinal[dylib]; - + if ( dylib != NULL ) { + std::map::iterator pos = _dylibToOrdinal.find(dylib); + if ( pos != _dylibToOrdinal.end() ) + return pos->second; + assert(0 && "dylib not assigned ordinal"); + } + // handle undefined dynamic_lookup if ( _options.undefinedTreatment() == Options::kUndefinedDynamicLookup ) return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; @@ -2378,6 +2425,8 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: @@ -2963,7 +3012,8 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio // reference to global weak def needs weak binding in dynamic images if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) - && (_options.outputKind() != Options::kStaticExecutable) ) { + && (_options.outputKind() != Options::kStaticExecutable) + && (_options.outputKind() != Options::kPreload) ) { needsExternReloc = true; } else if ( _options.outputKind() == Options::kDynamicExecutable ) { @@ -3301,6 +3351,9 @@ void OutputFile::writeMapFile(ld::Internal& state) char buffer[4096]; const ld::Atom* atom = *ait; const char* name = atom->name(); + // don't add auto-stripped aliases to .map file + if ( (atom->size() == 0) && (atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) ) + continue; if ( atom->contentType() == ld::Atom::typeCString ) { strcpy(buffer, "literal string: "); strlcat(buffer, (char*)atom->rawContentPointer(), 4096); @@ -3367,12 +3420,6 @@ class DebugNoteSorter }; -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - const char* OutputFile::assureFullPath(const char* path) { if ( path[0] == '/' ) @@ -3408,12 +3455,20 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) continue; if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) continue; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) + continue; // no stabs for absolute symbols if ( atom->definition() == ld::Atom::definitionAbsolute ) continue; // no stabs for .eh atoms if ( atom->contentType() == ld::Atom::typeCFI ) continue; + // no stabs for string literal atoms + if ( atom->contentType() == ld::Atom::typeCString ) + continue; + // no stabs for kernel dtrace probes + if ( (_options.outputKind() == Options::kStaticExecutable) && (strncmp(atom->name(), "__dtrace_probe$", 15) == 0) ) + continue; const ld::File* file = atom->file(); if ( file != NULL ) { if ( file != objFile ) { @@ -3450,7 +3505,7 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) const char* filename = NULL; bool wroteStartSO = false; state.stabs.reserve(atomsNeedingDebugNotes.size()*4); - __gnu_cxx::hash_set, CStringEquals> seenFiles; + std::unordered_set seenFiles; for (std::vector::iterator it=atomsNeedingDebugNotes.begin(); it != atomsNeedingDebugNotes.end(); it++) { const ld::Atom* atom = *it; const ld::File* atomFile = atom->file(); diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 588def7..ad4b22d 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -47,8 +47,6 @@ #include #include #include -#include -#include #include #include @@ -131,6 +129,10 @@ class AliasAtom : public ld::Atom virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return NULL; } virtual ld::Atom::LineInfo::iterator beginLineInfo() const { return NULL; } virtual ld::Atom::LineInfo::iterator endLineInfo() const { return NULL; } + + void setFinalAliasOf() const { + (const_cast(this))->setAttributesFromAtom(_aliasOf); + } private: const char* _name; @@ -456,7 +458,8 @@ void Resolver::doAtom(const ld::Atom& atom) // work around for kernel that uses 'l' labels in assembly code if ( (atom.symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) - && (atom.name()[0] == 'l') && (_options.outputKind() == Options::kStaticExecutable) ) + && (atom.name()[0] == 'l') && (_options.outputKind() == Options::kStaticExecutable) + && (strncmp(atom.name(), "ltmp", 4) != 0) ) (const_cast(&atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); @@ -469,7 +472,8 @@ void Resolver::doAtom(const ld::Atom& atom) const std::vector& aliases = _options.cmdLineAliases(); for (std::vector::const_iterator it=aliases.begin(); it != aliases.end(); ++it) { if ( strcmp(it->realName, atom.name()) == 0 ) { - const ld::Atom* alias = new AliasAtom(atom, it->alias); + const AliasAtom* alias = new AliasAtom(atom, it->alias); + _aliasesFromCmdLine.push_back(alias); this->doAtom(*alias); } } @@ -1109,6 +1113,10 @@ void Resolver::checkUndefines(bool force) else if ( _options.hasReExportList() && _options.shouldReExport(name) ) { fprintf(stderr, " -reexported_symbols_list command line option\n"); } + else if ( (_options.outputKind() == Options::kDynamicExecutable) + && (_options.entryName() != NULL) && (strcmp(name, _options.entryName()) == 0) ) { + fprintf(stderr, " implicit entry/start for main executable\n"); + } else { bool isInitialUndefine = false; for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { @@ -1286,7 +1294,7 @@ void Resolver::fillInInternalState() // make sure there is a __text section so that codesigning works if ( (_options.outputKind() == Options::kDynamicLibrary) || (_options.outputKind() == Options::kDynamicBundle) ) - _internal.getFinalSection(ld::Section("__TEXT", "__text", ld::Section::typeCode)); + _internal.getFinalSection(*new ld::Section("__TEXT", "__text", ld::Section::typeCode)); } void Resolver::fillInEntryPoint() @@ -1346,10 +1354,17 @@ void Resolver::linkTimeOptimize() // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); - // run through all atoms again and make sure newly codegened atoms have refernces bound + // run through all atoms again and make sure newly codegened atoms have references bound for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) this->convertReferencesToIndirect(**it); + // adjust section of any new + for (std::vector::const_iterator it=_aliasesFromCmdLine.begin(); it != _aliasesFromCmdLine.end(); ++it) { + const AliasAtom* aliasAtom = *it; + // update fields in AliasAtom to match newly constructed mach-o atom + aliasAtom->setFinalAliasOf(); + } + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; @@ -1369,6 +1384,18 @@ void Resolver::linkTimeOptimize() this->deadStripOptimize(true); } + // if -exported_symbols_list on command line, re-force scope + if ( _options.hasExportMaskList() ) { + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->scope() == ld::Atom::scopeGlobal ) { + if ( !_options.shouldExport(atom->name()) ) { + (const_cast(atom))->setScope(ld::Atom::scopeLinkageUnit); + } + } + } + } + if ( _options.outputKind() == Options::kObjectFile ) { // if -r mode, add proxies for new undefines (e.g. ___stack_chk_fail) this->resolveUndefines(); diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index a267489..32d1d50 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -42,6 +42,7 @@ #include #include +#include #include "Options.h" #include "ld.hpp" @@ -99,11 +100,7 @@ class Resolver : public ld::File::AtomHandler bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); void tweakWeakness(); - class CStringEquals { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + typedef std::unordered_set StringSet; class NotLive { public: @@ -125,6 +122,7 @@ class Resolver : public ld::File::AtomHandler std::vector _atoms; std::set _deadStripRoots; std::vector _atomsWithUnresolvedReferences; + std::vector _aliasesFromCmdLine; SymbolTable _symbolTable; bool _haveLLVMObjs; bool _completedInitialObjectFiles; diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index 406556a..2ea690f 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -41,8 +41,6 @@ #include #include #include -#include -#include #include "Options.h" @@ -748,7 +746,7 @@ void SymbolTable::printStatistics() count[b] = 0; } for(unsigned int i=0; i < _cstringTable.bucket_count(); ++i) { - unsigned int n = _cstringTable.elems_in_bucket(i); + unsigned int n = _cstringTable.bucket_size(i); if ( n < 10 ) count[n] += 1; else diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h index 451d064..5575f31 100644 --- a/ld64/src/ld/SymbolTable.h +++ b/ld64/src/ld/SymbolTable.h @@ -42,7 +42,7 @@ #include #include -#include +#include #include "Options.h" #include "ld.hpp" @@ -57,42 +57,38 @@ class SymbolTable : public ld::IndirectBindingTable typedef uint32_t IndirectBindingSlot; private: - class CStringEquals { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToSlot; + typedef std::unordered_map NameToSlot; class ContentFuncs { public: size_t operator()(const ld::Atom*) const; bool operator()(const ld::Atom* left, const ld::Atom* right) const; }; - typedef __gnu_cxx::hash_map ContentToSlot; + typedef std::unordered_map ContentToSlot; class ReferencesHashFuncs { public: size_t operator()(const ld::Atom*) const; bool operator()(const ld::Atom* left, const ld::Atom* right) const; }; - typedef __gnu_cxx::hash_map ReferencesToSlot; + typedef std::unordered_map ReferencesToSlot; class CStringHashFuncs { public: size_t operator()(const ld::Atom*) const; bool operator()(const ld::Atom* left, const ld::Atom* right) const; }; - typedef __gnu_cxx::hash_map CStringToSlot; + typedef std::unordered_map CStringToSlot; class UTF16StringHashFuncs { public: size_t operator()(const ld::Atom*) const; bool operator()(const ld::Atom* left, const ld::Atom* right) const; }; - typedef __gnu_cxx::hash_map UTF16StringToSlot; + typedef std::unordered_map UTF16StringToSlot; typedef std::map SlotToName; - typedef __gnu_cxx::hash_map, CStringEquals> NameToMap; + typedef std::unordered_map NameToMap; typedef std::vector DuplicatedSymbolAtomList; typedef std::map DuplicateSymbols; diff --git a/ld64/src/ld/code-sign-blobs/blob.cpp b/ld64/src/ld/code-sign-blobs/blob.cpp index 5b02f32..0f0567b 100644 --- a/ld64/src/ld/code-sign-blobs/blob.cpp +++ b/ld64/src/ld/code-sign-blobs/blob.cpp @@ -21,6 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include // // blob - generic extensible binary blob frame diff --git a/ld64/src/ld/code-sign-blobs/blob.h b/ld64/src/ld/code-sign-blobs/blob.h index db18746..19c63a9 100644 --- a/ld64/src/ld/code-sign-blobs/blob.h +++ b/ld64/src/ld/code-sign-blobs/blob.h @@ -45,6 +45,7 @@ #include "endian.h" #include "memutils.h" #include +#include namespace Security { diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 1a234db..a5db06e 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -54,8 +54,7 @@ extern "C" double log2 ( double ); #include #include #include -#include -#include +#include #include #include "Options.h" @@ -147,7 +146,7 @@ class InternalState : public ld::Internal struct SectionEquals { bool operator()(const ld::Section* left, const ld::Section* right) const; }; - typedef __gnu_cxx::hash_map SectionInToOut; + typedef std::unordered_map SectionInToOut; SectionInToOut _sectionInToFinalMap; @@ -168,7 +167,7 @@ std::vector InternalState::FinalSection::_s_segmentsSeen; size_t InternalState::SectionHash::operator()(const ld::Section* sect) const { size_t hash = 0; - __gnu_cxx::hash temp; + ld::CStringHash temp; hash += temp.operator()(sect->segmentName()); hash += temp.operator()(sect->sectionName()); return hash; diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index ed021ff..7be00a3 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -788,6 +788,20 @@ class Internal + +// utility classes for using std::unordered_map with c-strings +struct CStringHash { + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +struct CStringEquals +{ + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 29ff25f..708f1fb 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -112,12 +112,7 @@ class File : public ld::archive::File struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint16_t index;}; bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const; - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToEntryMap; + typedef std::unordered_map NameToEntryMap; typedef typename A::P P; typedef typename A::P::E E; diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index c9c4ed0..023f6e3 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -30,10 +30,11 @@ #include #include #include +#include #include #include -#include -#include +#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -211,13 +212,8 @@ class Parser static const char* tripletPrefixForArch(cpu_type_t arch); static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options); - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; + typedef std::unordered_set CStringSet; + typedef std::unordered_map CStringToAtom; class AtomSyncer : public ld::File::AtomHandler { public: @@ -327,7 +323,7 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 // create llvm module _module = ::lto_module_create_from_memory(content, contentLength); if ( _module == NULL ) - throwf("could not parse object file %s: %s", pth, lto_get_error_message()); + throwf("could not parse object file %s: '%s', using libLTO version '%s'", pth, ::lto_get_error_message(), ::lto_get_version()); if ( log ) fprintf(stderr, "bitcode file: %s\n", pth); @@ -469,14 +465,14 @@ bool Parser::optimize( const std::vector& allAtoms, // print out LTO version string if -v was used if ( options.verbose ) - fprintf(stderr, "%s\n", lto_get_version()); + fprintf(stderr, "%s\n", ::lto_get_version()); // create optimizer and add each Reader lto_code_gen_t generator = ::lto_codegen_create(); for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); + throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", (*it)->path(), ::lto_get_error_message(), ::lto_get_version()); } // add any -mllvm command line options @@ -594,8 +590,8 @@ bool Parser::optimize( const std::vector& allAtoms, lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; if ( options.mainExecutable ) { if ( options.staticExecutable ) { - // darwin x86_64 "static" code model is really dynamic code model - if ( options.arch == CPU_TYPE_X86_64 ) + // x86_64 "static" or any "-static -pie" is really dynamic code model + if ( (options.arch == CPU_TYPE_X86_64) || options.pie ) model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; else model = LTO_CODEGEN_PIC_MODEL_STATIC; @@ -642,7 +638,7 @@ bool Parser::optimize( const std::vector& allAtoms, size_t machOFileLen; const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); // if requested, save off temp mach-o file if ( options.saveTemps ) { diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 20b062b..aad1a8b 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -33,8 +33,8 @@ #include #include #include -#include -#include +#include +#include #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" @@ -178,14 +178,17 @@ class File : public ld::dylib::File friend class ExportAtom; friend class ImportAtom; - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + struct CStringHash { + std::size_t operator()(const char* __s) const { + unsigned long __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return size_t(__h); + }; }; 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; + typedef std::unordered_map NameToAtomMap; + typedef std::unordered_set NameSet; struct Dependent { const char* path; File* dylib; bool reExport; }; @@ -506,14 +509,14 @@ void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

nextdefsym(), this->path()); const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; - _atoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + _atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count for (const macho_nlist

* sym=start; sym < end; ++sym) { this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); } } else { int32_t count = dynamicInfo->ntoc(); - _atoms.resize(count); // set initial bucket count + _atoms.reserve(count); // set initial bucket count if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); for (int32_t i = 0; i < count; ++i) { @@ -569,9 +572,6 @@ void File::addDyldFastStub() template void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) { - if ( weakDef ) { - assert(_hasWeakExports); - } //fprintf(stderr, "addSymbol() %s\n", name); // symbols that start with $ld$ are meta-data to the static linker // need way for ld and dyld to see different exported symbols in a dylib @@ -700,7 +700,7 @@ bool File::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pi if ( _ignoreExports.count(name) != 0 ) return false; -// check myself + // check myself typename NameToAtomMap::iterator pos = _atoms.find(name); if ( pos != _atoms.end() ) { *weakDef = pos->second.weakDef; diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index e6c3efe..b49d63f 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -818,6 +818,8 @@ template const uint8_t* Atom::contentPointer() const { const macho_section

* sct = this->sect().machoSection(); + if ( this->_objAddress > sct->addr() + sct->size() ) + throwf("malformed .o file, symbol has address 0x%0llX which is outside range of its section", (uint64_t)this->_objAddress); uint32_t fileOffset = sct->offset() - sct->addr() + this->_objAddress; return this->sect().file().fileContent()+fileOffset; } @@ -1904,6 +1906,15 @@ int Parser::symbolIndexSorter(void* extra, const void* l, const void* r) } } // two symbols in same section, means one is an alias + // if one is ltmp*, make it an alias (sort first) + const char* leftName = parser->nameFromSymbol(leftSym); + const char* rightName = parser->nameFromSymbol(rightSym); + bool leftIsTmp = strncmp(leftName, "ltmp", 4); + bool rightIsTmp = strncmp(rightName, "ltmp", 4); + if ( leftIsTmp != rightIsTmp ) { + return (rightIsTmp ? -1 : 1); + } + // 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 ) @@ -1911,8 +1922,8 @@ int Parser::symbolIndexSorter(void* extra, const void* l, const void* r) else return 1; } - // if both are global, make alphabetically last one be the alias - return ( strcmp(parser->nameFromSymbol(rightSym), parser->nameFromSymbol(leftSym)) ); + // if both are global, sort alphabetically. earlier one will be the alias + return ( strcmp(rightName, leftName) ); } else if ( result < 0 ) return -1; @@ -1954,6 +1965,7 @@ void Parser::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionA // sort by symbol table address ParserAndSectionsArray extra = { this, sectionArray }; ::qsort_r(array, _symbolsInSections, sizeof(uint32_t), &extra, &symbolIndexSorter); + // look for two symbols at same address _overlappingSymbols = false; @@ -1961,6 +1973,7 @@ void Parser::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionA if ( symbolFromIndex(array[i-1]).n_value() == symbolFromIndex(array[i]).n_value() ) { //fprintf(stderr, "overlapping symbols at 0x%08llX\n", symbolFromIndex(array[i-1]).n_value()); _overlappingSymbols = true; + break; } } @@ -2985,7 +2998,7 @@ void Parser::parseDebugInfo() _file->_debugInfoKind = ld::relocatable::File::kDebugInfoNone; return; } - if ( (tuName != NULL) && (tuName[1] == '/') ) { + if ( (tuName != NULL) && (tuName[0] == '/') ) { _file->_dwarfTranslationUnitPath = tuName; } else if ( (tuDir != NULL) && (tuName != NULL) ) { @@ -5676,7 +5689,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati displacement += 4; // If the instruction was blx, force the low 2 bits to be clear dstAddr = srcAddr + displacement; - if ((instruction & 0xF8000000) == 0xE8000000) + if ((instruction & 0xD0000000) == 0xC0000000) dstAddr &= 0xFFFFFFFC; if ( reloc->r_extern() ) { @@ -5824,6 +5837,8 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati if ( sreloc->r_length() != 2 ) throw "bad length for ARM_RELOC_VANILLA"; target.atom = parser.findAtomByAddress(sreloc->r_value()); + if ( target.atom == NULL ) + throwf("bad r_value (0x%08X) for ARM_RELOC_VANILLA\n", sreloc->r_value()); contentValue = LittleEndian::get32(*fixUpPtr); target.addend = contentValue - target.atom->_objAddress; if ( target.atom->isThumb() ) diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index 175629d..74328ff 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -30,7 +30,8 @@ #include #include -#include +#include +#include #include "ld.hpp" #include "dtrace_dof.h" @@ -103,20 +104,14 @@ Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - struct DTraceProbeInfo { DTraceProbeInfo(const ld::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} const ld::Atom* atom; uint32_t offset; const char* probeName; }; -typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> ProviderToProbes; -typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; +typedef std::unordered_map, CStringHash, CStringEquals> ProviderToProbes; +typedef std::unordered_set CStringSet; diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 4631d5b..3e6824f 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -30,7 +30,6 @@ #include #include -#include #include "ld.hpp" #include "got.h" @@ -112,6 +111,15 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom if ( opts.nameSpace() != Options::kTwoLevelNameSpace ) *optimizable = false; } + else if ( targetOfGOT->scope() == ld::Atom::scopeLinkageUnit) { + // don't do optimization if target is in custom segment + if ( opts.sharedRegionEligible() ) { + const char* segName = targetOfGOT->section().segmentName(); + if ( (strcmp(segName, "__TEXT") != 0) && (strcmp(segName, "__DATA") != 0) ) { + *optimizable = false; + } + } + } return true; case ld::Fixup::kindStoreX86PCRel32GOT: *optimizable = false; diff --git a/ld64/src/ld/passes/huge.cpp b/ld64/src/ld/passes/huge.cpp index 932122a..51dda80 100644 --- a/ld64/src/ld/passes/huge.cpp +++ b/ld64/src/ld/passes/huge.cpp @@ -83,7 +83,7 @@ void doPass(const Options& opts, ld::Internal& state) return; // move all zero fill atoms that >1MB in size to a new __huge section - ld::Internal::FinalSection* hugeSection = state.getFinalSection(ld::Section("__DATA", "__huge", ld::Section::typeZeroFill)); + ld::Internal::FinalSection* hugeSection = state.getFinalSection(*new ld::Section("__DATA", "__huge", ld::Section::typeZeroFill)); for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; if ( sect == hugeSection ) diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index 6876d8f..b4be79f 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "ld.hpp" #include "order.h" @@ -82,11 +83,7 @@ class Layout const Layout& _layout; }; - class CStringEquals { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToAtom; + typedef std::unordered_map NameToAtom; typedef std::map AtomToAtom; diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index eed532e..6a58fdf 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/src/ld/passes/tlvp.cpp @@ -30,7 +30,6 @@ #include #include -#include #include "ld.hpp" #include "tlvp.h" diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 4ad006a..71f13b8 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -33,8 +33,9 @@ #include #include -#include +#include +#include "configure.h" #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" #include "MachOTrie.hpp" @@ -86,14 +87,6 @@ class DyldInfoPrinter typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - - typedef __gnu_cxx::hash_set, CStringEquals> StringSet; - DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch); void printRebaseInfo(); void printRebaseInfoOpcodes(); @@ -1271,26 +1264,43 @@ void DyldInfoPrinter::printExportInfo() parseTrie(start, end, list); //std::sort(list.begin(), list.end(), SortExportsByAddress()); for (std::vector::iterator it=list.begin(); it != list.end(); ++it) { - if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { - if ( it->importName[0] == '\0' ) - fprintf(stdout, "[re-export] %s from dylib=%llu\n", it->name, it->other); - else - fprintf(stdout, "[re-export] %s from dylib=%llu named=%s\n", it->name, it->other, it->importName); - } - else { - const char* flags = ""; - if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) - flags = "[weak_def] "; - else if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL ) - flags = "[per-thread] "; - if ( it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { - flags = "[resolver] "; - fprintf(stdout, "0x%08llX %s%s (resolver=0x%08llX)\n", fBaseAddress+it->address, flags, it->name, it->other); + const bool reExport = (it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT); + const bool weakDef = (it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); + const bool threadLocal = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL); + const bool resolver = (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); + if ( reExport ) + printf("[re-export] "); + else + printf("0x%08llX ", fBaseAddress+it->address); + printf("%s", it->name); + if ( weakDef || threadLocal || resolver ) { + bool needComma = false; + printf(" ["); + if ( weakDef ) { + printf("weak_def"); + needComma = true; } - else { - fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name); + if ( threadLocal ) { + if ( needComma ) + printf(", "); + printf("per-thread"); + needComma = true; + } + if ( resolver ) { + if ( needComma ) + printf(", "); + printf("resolver=0x%08llX", it->other); + needComma = true; } + printf("]"); + } + if ( reExport ) { + if ( it->importName[0] == '\0' ) + printf(" (from %s)", fDylibs[it->other - 1]); + else + printf(" (%s from %s)", it->importName, fDylibs[it->other - 1]); } + printf("\n"); } } } @@ -2178,19 +2188,20 @@ int main(int argc, const char* argv[]) else if ( strcmp(arch, "x86_64") == 0 ) sPreferredArch = CPU_TYPE_X86_64; else { - const char* archName = argv[++i]; - if ( archName == NULL ) + if ( arch == NULL ) throw "-arch missing architecture name"; bool found = false; for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { - if ( strcmp(t->archName,archName) == 0 ) { + if ( strcmp(t->archName,arch) == 0 ) { sPreferredArch = t->cpuType; if ( t->isSubType ) sPreferredSubArch = t->cpuSubType; + found = true; + break; } } if ( !found ) - throwf("unknown architecture %s", archName); + throwf("unknown architecture %s", arch); } } else if ( strcmp(arg, "-rebase") == 0 ) { diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 36a71fd..bd585d1 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -33,7 +33,9 @@ #include #include -#include +#include + +#include "configure.h" #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -108,13 +110,21 @@ class MachOChecker typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; - class CStringEquals + // utility classes for using std::unordered_map with c-strings + struct CStringHash { + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; + struct CStringEquals { - public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + typedef std::unordered_set StringSet; MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); void checkMachHeader(); diff --git a/ld64/src/other/rebase.cpp b/ld64/src/other/rebase.cpp index d1e3194..e2776cf 100644 --- a/ld64/src/other/rebase.cpp +++ b/ld64/src/other/rebase.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * Copyright (c) 2006-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 9060557..731f2a3 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -33,7 +33,7 @@ #include #include -#include +#include #include "MachOFileAbstraction.hpp" @@ -71,14 +71,6 @@ class UnwindPrinter typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - - typedef __gnu_cxx::hash_set, CStringEquals> StringSet; - UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames); bool findUnwindSection(); diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 99ddfa6..41cf6cf 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos6.0.internal -version Path) +IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) diff --git a/ld64/unit-tests/test-cases/lto-dylib-aliases/Makefile b/ld64/unit-tests/test-cases/lto-dylib-aliases/Makefile new file mode 100644 index 0000000..c8b4ab7 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-aliases/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Assertion failure when linking CoreFoundation with LTO +# +# Command line aliases need to have section type reset after lto pass. +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o + ${CC} ${CCFLAGS} foo.o -dynamiclib -o libfoo.dylib -Wl,-alias,_foo,_bar + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf foo.o libfoo.dylib diff --git a/ld64/unit-tests/test-cases/lto-dylib-aliases/foo.c b/ld64/unit-tests/test-cases/lto-dylib-aliases/foo.c new file mode 100644 index 0000000..2114ee8 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-aliases/foo.c @@ -0,0 +1,7 @@ +#include + + +void foo() { + printf("hello\n"); +} + diff --git a/ld64/unit-tests/test-cases/lto-dylib-export_list/Makefile b/ld64/unit-tests/test-cases/lto-dylib-export_list/Makefile new file mode 100644 index 0000000..0fa97ce --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-export_list/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# clang built with LTO has verification errors +# +# LTO on coalesced away symbols can cause weak symbols to be exported +# in spite of -exported_symbols_list. +# + +run: all + +all: + ${CXX} ${CXXFLAGS} -flto foo.cxx -c -o foo.o + ${CXX} ${CXXFLAGS} -flto bar.cxx -c -o bar.o + ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoo.dylib -exported_symbols_list foo.exp + nm -m libfoo.dylib | grep "weak external" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf foo.o bar.o libfoo.dylib diff --git a/ld64/unit-tests/test-cases/lto-dylib-export_list/bar.cxx b/ld64/unit-tests/test-cases/lto-dylib-export_list/bar.cxx new file mode 100644 index 0000000..c4e5485 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-export_list/bar.cxx @@ -0,0 +1,11 @@ +#include +#include "foo.h" + +static void bar1() {} + +void bar_test() { + printf("%p\n", &bar1); + printf("%p\n", &Foo::doit); + printf("%p\n", &Foo::doit2); +} + diff --git a/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.cxx b/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.cxx new file mode 100644 index 0000000..e4d62c9 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.cxx @@ -0,0 +1,11 @@ +#include +#include "foo.h" + +static void foo1() {} + +void foo_test() { + printf("%p\n", &foo1); + printf("%p\n", &Foo::doit); + printf("%p\n", &Foo::doit2); +} + diff --git a/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.exp b/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.exp new file mode 100644 index 0000000..7f4b861 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.exp @@ -0,0 +1,3 @@ +__Z8bar_testv +__Z8foo_testv + diff --git a/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.h b/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.h new file mode 100644 index 0000000..d9b8af4 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dylib-export_list/foo.h @@ -0,0 +1,6 @@ + +class Foo { +public: + static void doit() { } + static void doit2() { } +}; diff --git a/ld64/unit-tests/test-cases/lto-static-pie/Makefile b/ld64/unit-tests/test-cases/lto-static-pie/Makefile new file mode 100644 index 0000000..4b801a2 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-static-pie/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -preload -pie produces relocations +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o a.o b.o -static -Wl,-pie -o main-static-pie \ + -e _entry -nostdlib + ${PASS_IFF_GOOD_MACHO} main-static-pie + + + +clean: + rm a.o b.o main.o main-static-pie diff --git a/ld64/unit-tests/test-cases/lto-static-pie/a.c b/ld64/unit-tests/test-cases/lto-static-pie/a.c new file mode 100644 index 0000000..bbf5a3f --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-static-pie/a.c @@ -0,0 +1,10 @@ +#include + +int a_value = 10; + +int a(const char* l, const char* r) +{ + a_value = *l; + return (*l == *r); +} + diff --git a/ld64/unit-tests/test-cases/lto-static-pie/b.c b/ld64/unit-tests/test-cases/lto-static-pie/b.c new file mode 100644 index 0000000..5a12dc8 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-static-pie/b.c @@ -0,0 +1,2 @@ + const char* b = "hello"; + diff --git a/ld64/unit-tests/test-cases/lto-static-pie/main.c b/ld64/unit-tests/test-cases/lto-static-pie/main.c new file mode 100644 index 0000000..e67730c --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-static-pie/main.c @@ -0,0 +1,12 @@ + +extern int a(const char*, const char*); +extern char* b; +extern int a_value; + +int entry(const char* param) { + if ( a(param, b) ) { + return a_value + 10; + } + return a_value; +} + diff --git a/ld64/unit-tests/test-cases/no-object-symbols/Makefile b/ld64/unit-tests/test-cases/no-object-symbols/Makefile index b213511..933533f 100644 --- a/ld64/unit-tests/test-cases/no-object-symbols/Makefile +++ b/ld64/unit-tests/test-cases/no-object-symbols/Makefile @@ -34,7 +34,7 @@ include ${TESTROOT}/include/common.makefile run: all all: - as -arch ${ARCH} -n empty.s -o empty.o + ${CC} -arch ${ARCH} -c -Wa,-n empty.s -o empty.o ${LD} -r empty.o -x -o empty2.o otool -lv empty2.o | egrep 'vmsize 0x0[0]+$$' | ${FAIL_IF_EMPTY} otool -lv empty2.o | grep 'filesize 0' | ${FAIL_IF_EMPTY} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/Makefile b/ld64/unit-tests/test-cases/re-export-symbol/Makefile index 6de347c..4da677c 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/Makefile +++ b/ld64/unit-tests/test-cases/re-export-symbol/Makefile @@ -34,13 +34,15 @@ all: ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib ${FAIL_IF_BAD_MACHO} libbar.dylib - # build library the re-exports _bar from base library + # build library that re-exports _bar from base library ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -exported_symbols_list foo.exp ${FAIL_IF_BAD_MACHO} libfoo.dylib ${DYLDINFO} -export libfoo.dylib | grep _bar | grep 're-export' | ${FAIL_IF_EMPTY} + ${DYLDINFO} -export libfoo.dylib | grep _bar_weak | grep 're-export' | grep 'weak_def' |${FAIL_IF_EMPTY} # link against dylib and verify _bar is marked as coming from libfoo ${CC} ${CCFLAGS} main1.c libfoo.dylib -o main1 ${DYLDINFO} -bind -lazy_bind main1 | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind -lazy_bind main1 | grep _bar_weak | grep libfoo | ${FAIL_IF_EMPTY} # build library the re-exports _bar from base library as _mybar ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp diff --git a/ld64/unit-tests/test-cases/re-export-symbol/bar.c b/ld64/unit-tests/test-cases/re-export-symbol/bar.c index 34e5666..c593e8e 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/bar.c +++ b/ld64/unit-tests/test-cases/re-export-symbol/bar.c @@ -3,3 +3,8 @@ int bar(void) { return 1; } + +__attribute__((weak)) +void bar_weak() +{ +} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/foo.exp b/ld64/unit-tests/test-cases/re-export-symbol/foo.exp index b9e50b8..4207d93 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/foo.exp +++ b/ld64/unit-tests/test-cases/re-export-symbol/foo.exp @@ -1,2 +1,3 @@ _foo _bar +_bar_weak diff --git a/ld64/unit-tests/test-cases/re-export-symbol/main1.c b/ld64/unit-tests/test-cases/re-export-symbol/main1.c index 4c0b25e..7bb5c6b 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/main1.c +++ b/ld64/unit-tests/test-cases/re-export-symbol/main1.c @@ -1,9 +1,11 @@ extern int foo(); extern int bar(); +extern int bar_weak(); int main() { foo(); bar(); + bar_weak(); return 0; } diff --git a/ld64/unit-tests/test-cases/tlv-dylib/Makefile b/ld64/unit-tests/test-cases/tlv-dylib/Makefile index 5017692..5a87a9e 100644 --- a/ld64/unit-tests/test-cases/tlv-dylib/Makefile +++ b/ld64/unit-tests/test-cases/tlv-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2010 Apple Inc. All rights reserved. +# Copyright (c) 2010-2012 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -33,13 +33,13 @@ SHELL = bash # use bash shell so we can redirect just stderr run: all all: - ${CC} ${CCFLAGS} foo.s -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib # check trying to access TLV _foo as regular variable is an error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DUSE_FOO libfoo.dylib -o main 2>/dev/null + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DUSE_FOO_WRONG libfoo.dylib -o main 2>/dev/null # check trying to access regular variable _bar as TLV is an error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c getbar.s libfoo.dylib -o main 2>/dev/null + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DUSE_BAR_WRONG libfoo.dylib -o main 2>/dev/null # check can link with TLV _foo in dylib - ${CC} ${CCFLAGS} main.c getfoo.s libfoo.dylib -o main + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/tlv-dylib/foo.c b/ld64/unit-tests/test-cases/tlv-dylib/foo.c new file mode 100644 index 0000000..a82eafb --- /dev/null +++ b/ld64/unit-tests/test-cases/tlv-dylib/foo.c @@ -0,0 +1,7 @@ + +// foo is an exported thread local variable +__thread int foo = 6; + +// _bar is an exported regular variable +int bar = 5; + diff --git a/ld64/unit-tests/test-cases/tlv-dylib/foo.s b/ld64/unit-tests/test-cases/tlv-dylib/foo.s deleted file mode 100644 index fd2f23d..0000000 --- a/ld64/unit-tests/test-cases/tlv-dylib/foo.s +++ /dev/null @@ -1,33 +0,0 @@ - -// _foo is an exported thread local variable -// _bar is an exported regular variable - - # _a is zerofill global TLV - .tbss _a$tlv$init,4,2 - -#if __x86_64__ - .tlv - .globl _foo -_foo: .quad __tlv_bootstrap - .quad 0 - .quad _a$tlv$init - - -#endif - -#if __i386__ - .tlv - .globl _foo -_foo: .long __tlv_bootstrap - .long 0 - .long _a$tlv$init -#endif - - - .data - .globl _bar -_bar: .long 0 - - - - .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-dylib/getbar.s b/ld64/unit-tests/test-cases/tlv-dylib/getbar.s deleted file mode 100644 index 33b0418..0000000 --- a/ld64/unit-tests/test-cases/tlv-dylib/getbar.s +++ /dev/null @@ -1,29 +0,0 @@ - -#if __x86_64__ - .text - .globl _get_bar -_get_bar: - pushq %rbp - movq %rsp, %rbp - movq _bar@TLVP(%rip), %rdi - call *(%rdi) - popq %rbp - ret -#endif - - -#if __i386__ - .text - .globl _get_bar -_get_bar: - pushl %ebp - movl %esp, %ebp - subl $8, %esp - movl _bar@TLVP, %eax - call *(%eax) - movl %ebp, %esp - popl %ebp - ret -#endif - -.subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-dylib/getfoo.s b/ld64/unit-tests/test-cases/tlv-dylib/getfoo.s deleted file mode 100644 index 21db32c..0000000 --- a/ld64/unit-tests/test-cases/tlv-dylib/getfoo.s +++ /dev/null @@ -1,29 +0,0 @@ - -#if __x86_64__ - .text - .globl _get_foo -_get_foo: - pushq %rbp - movq %rsp, %rbp - movq _foo@TLVP(%rip), %rdi - call *(%rdi) - popq %rbp - ret -#endif - - -#if __i386__ - .text - .globl _get_foo -_get_foo: - pushl %ebp - movl %esp, %ebp - subl $8, %esp - movl _foo@TLVP, %eax - call *(%eax) - movl %ebp, %esp - popl %ebp - ret -#endif - -.subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-dylib/main.c b/ld64/unit-tests/test-cases/tlv-dylib/main.c index d5ec3b8..5d0eb88 100644 --- a/ld64/unit-tests/test-cases/tlv-dylib/main.c +++ b/ld64/unit-tests/test-cases/tlv-dylib/main.c @@ -22,13 +22,22 @@ * @APPLE_LICENSE_HEADER_END@ */ -#if USE_FOO +#if USE_FOO_WRONG extern int foo; +#elif USE_BAR_WRONG + extern __thread int bar; +#else + extern __thread int foo; #endif + int main() { -#if USE_FOO +#if USE_FOO_WRONG + foo = 1; +#elif USE_BAR_WRONG + bar = 1; +#else foo = 1; #endif return 0; From 4096c83d62922a335203224cd21fb4402153bdf3 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:23:48 +0100 Subject: [PATCH 15/48] 236-3 --- ld64/doc/man/man1/ld.1 | 14 +- ld64/ld64.xcodeproj/project.pbxproj | 37 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 273 ++- ld64/src/abstraction/MachOTrie.hpp | 2 +- ld64/src/create_configure | 19 +- ld64/src/ld/Architectures.hpp | 5 + ld64/src/ld/HeaderAndLoadCommands.hpp | 136 +- ld64/src/ld/InputFiles.cpp | 175 +- ld64/src/ld/InputFiles.h | 12 +- ld64/src/ld/LinkEdit.hpp | 112 +- ld64/src/ld/LinkEditClassic.hpp | 206 ++ ld64/src/ld/Options.cpp | 481 ++++- ld64/src/ld/Options.h | 70 +- ld64/src/ld/OutputFile.cpp | 1858 ++++++++++++++--- ld64/src/ld/OutputFile.h | 34 +- ld64/src/ld/Resolver.cpp | 154 +- ld64/src/ld/Resolver.h | 8 +- ld64/src/ld/SymbolTable.cpp | 26 + ld64/src/ld/SymbolTable.h | 1 + ld64/src/ld/dwarf2.h | 8 +- ld64/src/ld/ld.cpp | 339 ++- ld64/src/ld/ld.hpp | 155 +- ld64/src/ld/parsers/archive_file.cpp | 15 +- .../parsers/libunwind/DwarfInstructions.hpp | 308 ++- ld64/src/ld/parsers/libunwind/DwarfParser.hpp | 2 +- ld64/src/ld/parsers/libunwind/Registers.hpp | 281 ++- ld64/src/ld/parsers/lto_file.cpp | 83 +- ld64/src/ld/parsers/lto_file.h | 7 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 220 +- ld64/src/ld/parsers/macho_dylib_file.h | 6 + .../src/ld/parsers/macho_relocatable_file.cpp | 1112 ++++++++-- ld64/src/ld/parsers/macho_relocatable_file.h | 6 +- ld64/src/ld/passes/branch_island.cpp | 382 ++-- ld64/src/ld/passes/compact_unwind.cpp | 76 +- ld64/src/ld/passes/dtrace_dof.cpp | 8 + ld64/src/ld/passes/got.cpp | 64 +- ld64/src/ld/passes/objc.cpp | 19 +- ld64/src/ld/passes/order.cpp | 68 +- ld64/src/ld/passes/stubs/stub_arm64.hpp | 396 ++++ ld64/src/ld/passes/stubs/stubs.cpp | 18 +- ld64/src/ld/passes/tlvp.cpp | 12 + ld64/src/other/ObjectDump.cpp | 117 +- ld64/src/other/dyldinfo.cpp | 77 +- ld64/src/other/machochecker.cpp | 72 +- ld64/src/other/unwinddump.cpp | 142 +- ld64/unit-tests/include/common.makefile | 19 +- .../test-cases/branch-islands/Makefile | 4 +- .../test-cases/branch-islands/extra.c | 2 + .../test-cases/branch-islands/hello.c | 5 +- .../test-cases/branch-islands/space.s | 6 +- .../test-cases/branch-long/Makefile | 59 + ld64/unit-tests/test-cases/branch-long/bar.s | 24 + ld64/unit-tests/test-cases/branch-long/foo.c | 25 + .../test-cases/coalesce-force/Makefile | 45 + .../test-cases/coalesce-force/foo.c | 21 + .../test-cases/coalesce-force/foo.exp | 2 + .../test-cases/cpu-sub-types/Makefile | 34 +- .../test-cases/data-in-code/Makefile | 9 +- .../unit-tests/test-cases/data-in-code/main.c | 49 - .../unit-tests/test-cases/data-in-code/test.s | 14 +- .../unit-tests/test-cases/dso_handle/Makefile | 2 +- .../test-cases/eh-coalescing-r/Makefile | 3 +- .../linker-optimization-hints/AdrpAdd.s | 27 + .../linker-optimization-hints/AdrpAddLdr.s | 55 + .../linker-optimization-hints/AdrpAddStr.s | 44 + .../linker-optimization-hints/AdrpLdr.s | 64 + .../linker-optimization-hints/AdrpLdrGot.s | 30 + .../linker-optimization-hints/AdrpLdrGotLdr.s | 56 + .../linker-optimization-hints/AdrpLdrGotStr.s | 49 + .../linker-optimization-hints/Makefile | 1418 +++++++++++++ .../linker-optimization-hints/main.s | 6 + .../linker_options-framework/Makefile | 43 + .../test-cases/linker_options-framework/foo.c | 3 + .../linker_options-framework/main.c | 8 + .../linker_options-library/Makefile | 44 + .../test-cases/linker_options-library/bar.c | 1 + .../test-cases/linker_options-library/foo.c | 3 + .../test-cases/linker_options-library/main.c | 10 + .../test-cases/lto-dynamic_export/Makefile | 42 + .../test-cases/lto-dynamic_export/main.c | 14 + ld64/unit-tests/test-cases/objc-abi/Makefile | 6 +- ld64/unit-tests/test-cases/objc-abi/test.m | 2 +- .../objc-category-optimize-load/Makefile | 2 +- .../objc-category-optimize/Makefile | 2 +- .../test-cases/objc-category-warning/Makefile | 2 +- .../test-cases/objc-gc-checks/Makefile | 40 +- .../test-cases/objc-gc-checks/bar.m | 3 +- .../test-cases/objc-gc-checks/foo.m | 3 +- .../objc-literal-pointers-strip/test.m | 4 +- .../test-cases/objc-properties/test.m | 2 +- .../test-cases/operator-new/main.cxx | 2 +- .../test-cases/order_file-archive/Makefile | 43 + .../test-cases/order_file-archive/foo.c | 4 + .../test-cases/order_file-archive/main.c | 33 + .../order_file-archive/main.expected | 4 + .../test-cases/order_file-archive/main.order | 5 + .../test-cases/re-export-symbol/Makefile | 6 +- .../test-cases/re-export-symbol/foo.c | 19 +- .../test-cases/relocs-literals/Makefile | 2 +- .../test-cases/relocs-literals/test.c | 4 +- .../test-cases/relocs-literals2/Makefile | 2 +- .../test-cases/relocs-literals2/test.c | 4 +- ld64/unit-tests/test-cases/relocs-objc/test.m | 2 +- 103 files changed, 8966 insertions(+), 1086 deletions(-) create mode 100644 ld64/src/ld/passes/stubs/stub_arm64.hpp create mode 100644 ld64/unit-tests/test-cases/branch-long/Makefile create mode 100644 ld64/unit-tests/test-cases/branch-long/bar.s create mode 100644 ld64/unit-tests/test-cases/branch-long/foo.c create mode 100644 ld64/unit-tests/test-cases/coalesce-force/Makefile create mode 100644 ld64/unit-tests/test-cases/coalesce-force/foo.c create mode 100644 ld64/unit-tests/test-cases/coalesce-force/foo.exp delete mode 100644 ld64/unit-tests/test-cases/data-in-code/main.c create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/Makefile create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/main.s create mode 100644 ld64/unit-tests/test-cases/linker_options-framework/Makefile create mode 100644 ld64/unit-tests/test-cases/linker_options-framework/foo.c create mode 100644 ld64/unit-tests/test-cases/linker_options-framework/main.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library/Makefile create mode 100644 ld64/unit-tests/test-cases/linker_options-library/bar.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library/foo.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library/main.c create mode 100644 ld64/unit-tests/test-cases/lto-dynamic_export/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-dynamic_export/main.c create mode 100644 ld64/unit-tests/test-cases/order_file-archive/Makefile create mode 100644 ld64/unit-tests/test-cases/order_file-archive/foo.c create mode 100644 ld64/unit-tests/test-cases/order_file-archive/main.c create mode 100644 ld64/unit-tests/test-cases/order_file-archive/main.expected create mode 100644 ld64/unit-tests/test-cases/order_file-archive/main.order diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index c2eb01d..964d099 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -297,6 +297,10 @@ them to use no space on disk. This option suppresses that optimization, so zero 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. +.It Fl no_branch_islands +Disables linker creation of branch islands which allows images to be created that are larger than the +maximum branch distance. Useful with -preload when code is in multiple sections but all are within +the branch range. .El .Ss Options when creating a dynamic library (dylib) .Bl -tag @@ -357,6 +361,10 @@ is a hexadecimal number with an optional leading 0x. The should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. .It Fl allow_stack_execute Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. +.It Fl export_dynamic +Preserves all global symbols in main executables during LTO. Without this option, Link Time Optimization +is allowed to inline and remove global functions. This option is used when a main executable may load +a plug-in which requires certain symbols from the main executable. .El .Ss Options when creating a bundle .Bl -tag @@ -376,6 +384,8 @@ Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d Force definition of common symbols. That is, transform tentative definitions into real definitions. +.It Fl rename_section Ar fromSegment fromSection toSegment toSection +Renames section fromSegment/fromSection to toSegment/toSection. .El .Ss Options that control symbol resolution .Bl -tag @@ -695,7 +705,7 @@ order file to cluster commonly used/dirty globals onto the same page(s). .Ss Obsolete Options .Bl -tag .It Fl segalign Ar value -All segments must be page aligned. This option is obsolete. +All segments must be page aligned. .It Fl seglinkedit Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. .It Fl noseglinkedit @@ -703,7 +713,7 @@ This is the default. This option is obsolete. .It Fl fvmlib Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. .It Fl preload -Preload executables (MH_PRELOAD) are no longer supported. This option is obsolete. +Preload executables (MH_PRELOAD) are no longer supported. .It Fl sectobjectsymbols Ar segname Ar sectname Adding a local label at a section start is no longer supported. This option is obsolete. .It Fl nofixprebinding diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index dd74668..250fa5e 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -87,7 +87,7 @@ /* Begin PBXBuildRule section */ F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.llvm.clang.1_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -248,8 +248,9 @@ F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F93A9BEC12C2E51900BAA11D /* stub_arm64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm64.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -312,8 +313,8 @@ F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -436,6 +437,7 @@ F9AA65101051BD2B003E3539 /* stubs.cpp */, F9AA650F1051BD2B003E3539 /* stub_arm.hpp */, F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */, + F93A9BEC12C2E51900BAA11D /* stub_arm64.hpp */, F9BA8A7F1096150F0097A440 /* stub_x86.hpp */, F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */, F989D0391062E6350014B60C /* stub_x86_64.hpp */, @@ -1056,6 +1058,7 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; OTHER_CPLUSPLUSFLAGS = ( @@ -1064,7 +1067,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1122,13 +1125,14 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CPLUSPLUSFLAGS)", ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1171,7 +1175,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1206,7 +1210,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1324,13 +1328,14 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CPLUSPLUSFLAGS)", ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1413,7 +1418,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1479,6 +1484,10 @@ GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; @@ -1504,6 +1513,10 @@ GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; @@ -1519,6 +1532,10 @@ GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 4fd6123..9279734 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "FileAbstraction.hpp" @@ -205,7 +206,7 @@ #ifndef LC_DATA_IN_CODE #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */ - struct data_in_code_entry { + struct data_in_code_entry { uint32_t offset; uint16_t length; uint16_t kind; @@ -216,6 +217,20 @@ #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef LC_ENCRYPTION_INFO_64 + #define LC_ENCRYPTION_INFO_64 0x2C + struct encryption_info_command_64 { + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; + uint32_t cryptsize; + uint32_t cryptid; + uint32_t pad; + }; +#endif + + + #ifndef CPU_SUBTYPE_ARM_V7F #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) #endif @@ -227,6 +242,124 @@ #endif + +// hack until arm64 headers are worked out +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#define CPU_SUBTYPE_ARM64_ALL 0 +#define CPU_SUBTYPE_ARM64_V8 1 + +#define ARM64_RELOC_UNSIGNED 0 // for pointers +#define ARM64_RELOC_SUBTRACTOR 1 // must be followed by a ARM64_RELOC_UNSIGNED +#define ARM64_RELOC_BRANCH26 2 // a B/BL instruction with 26-bit displacement +#define ARM64_RELOC_PAGE21 3 // pc-rel distance to page of target +#define ARM64_RELOC_PAGEOFF12 4 // offset within page, scaled by r_length +#define ARM64_RELOC_GOT_LOAD_PAGE21 5 // pc-rel distance to page of GOT slot +#define ARM64_RELOC_GOT_LOAD_PAGEOFF12 6 // offset within page of GOT slot, scaled by r_length +#define ARM64_RELOC_POINTER_TO_GOT 7 // for pointers to GOT slots +#define ARM64_RELOC_TLVP_LOAD_PAGE21 8 // pc-rel distance to page of TLVP slot +#define ARM64_RELOC_TLVP_LOAD_PAGEOFF12 9 // offset within page of TLVP slot, scaled by r_length +#define ARM64_RELOC_ADDEND 10 // r_symbolnum is addend for next reloc + + + +#define UNW_ARM64_X0 0 +#define UNW_ARM64_X1 1 +#define UNW_ARM64_X2 2 +#define UNW_ARM64_X3 3 +#define UNW_ARM64_X4 4 +#define UNW_ARM64_X5 5 +#define UNW_ARM64_X6 6 +#define UNW_ARM64_X7 7 +#define UNW_ARM64_X8 8 +#define UNW_ARM64_X9 9 +#define UNW_ARM64_X10 10 +#define UNW_ARM64_X11 11 +#define UNW_ARM64_X12 12 +#define UNW_ARM64_X13 13 +#define UNW_ARM64_X14 14 +#define UNW_ARM64_X15 15 +#define UNW_ARM64_X16 16 +#define UNW_ARM64_X17 17 +#define UNW_ARM64_X18 18 +#define UNW_ARM64_X19 19 +#define UNW_ARM64_X20 20 +#define UNW_ARM64_X21 21 +#define UNW_ARM64_X22 22 +#define UNW_ARM64_X23 23 +#define UNW_ARM64_X24 24 +#define UNW_ARM64_X25 25 +#define UNW_ARM64_X26 26 +#define UNW_ARM64_X27 27 +#define UNW_ARM64_X28 28 +#define UNW_ARM64_X29 29 +#define UNW_ARM64_FP 29 +#define UNW_ARM64_X30 30 +#define UNW_ARM64_LR 30 +#define UNW_ARM64_X31 31 +#define UNW_ARM64_SP 31 +#define UNW_ARM64_D0 64 +#define UNW_ARM64_D1 65 +#define UNW_ARM64_D2 66 +#define UNW_ARM64_D3 67 +#define UNW_ARM64_D4 68 +#define UNW_ARM64_D5 69 +#define UNW_ARM64_D6 70 +#define UNW_ARM64_D7 71 +#define UNW_ARM64_D8 72 +#define UNW_ARM64_D9 73 +#define UNW_ARM64_D10 74 +#define UNW_ARM64_D11 75 +#define UNW_ARM64_D12 76 +#define UNW_ARM64_D13 77 +#define UNW_ARM64_D14 78 +#define UNW_ARM64_D15 79 +#define UNW_ARM64_D16 80 +#define UNW_ARM64_D17 81 +#define UNW_ARM64_D18 82 +#define UNW_ARM64_D19 83 +#define UNW_ARM64_D20 84 +#define UNW_ARM64_D21 85 +#define UNW_ARM64_D22 86 +#define UNW_ARM64_D23 87 +#define UNW_ARM64_D24 88 +#define UNW_ARM64_D25 89 +#define UNW_ARM64_D26 90 +#define UNW_ARM64_D27 91 +#define UNW_ARM64_D28 92 +#define UNW_ARM64_D29 93 +#define UNW_ARM64_D30 94 +#define UNW_ARM64_D31 95 + +#define UNWIND_ARM64_MODE_MASK 0x0F000000 +#define UNWIND_ARM64_MODE_FRAME_OLD 0x01000000 +#define UNWIND_ARM64_MODE_FRAMELESS 0x02000000 +#define UNWIND_ARM64_MODE_DWARF 0x03000000 +#define UNWIND_ARM64_MODE_FRAME 0x04000000 + +#define UNWIND_ARM64_FRAME_X19_X20_PAIR 0x00000001 +#define UNWIND_ARM64_FRAME_X21_X22_PAIR 0x00000002 +#define UNWIND_ARM64_FRAME_X23_X24_PAIR 0x00000004 +#define UNWIND_ARM64_FRAME_X25_X26_PAIR 0x00000008 +#define UNWIND_ARM64_FRAME_X27_X28_PAIR 0x00000010 +#define UNWIND_ARM64_FRAME_D8_D9_PAIR 0x00000100 +#define UNWIND_ARM64_FRAME_D10_D11_PAIR 0x00000200 +#define UNWIND_ARM64_FRAME_D12_D13_PAIR 0x00000400 +#define UNWIND_ARM64_FRAME_D14_D15_PAIR 0x00000800 + +#define UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK 0x00FFF000 + +#define UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD 0x00000001 +#define UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD 0x00000002 +#define UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD 0x00000004 +#define UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD 0x00000008 +#define UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD 0x00000010 +#define UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD 0x00000020 +#define UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD 0x00000040 +#define UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD 0x00000080 + + +#define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF + #ifndef LC_SOURCE_VERSION #define LC_SOURCE_VERSION 0x2A struct source_version_command { @@ -250,6 +383,53 @@ #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef LC_LINKER_OPTION + #define LC_LINKER_OPTION 0x2D + + struct linker_option_command { + uint32_t cmd; /*LC_LINKER_OPTION only used in MH_OBJECT filetypes */ + uint32_t cmdsize; + uint32_t count; /* number of strings */ + /* concatenation of zero terminated UTF8 strings. Zero filled at end to align */ + }; +#endif + +#ifndef LC_LINKER_OPTIMIZATION_HINTS + #define LC_LINKER_OPTIMIZATION_HINTS 0x2E + #define LOH_ARM64_ADRP_ADRP 1 + #define LOH_ARM64_ADRP_LDR 2 + #define LOH_ARM64_ADRP_ADD_LDR 3 + #define LOH_ARM64_ADRP_LDR_GOT_LDR 4 + #define LOH_ARM64_ADRP_ADD_STR 5 + #define LOH_ARM64_ADRP_LDR_GOT_STR 6 + #define LOH_ARM64_ADRP_ADD 7 + #define LOH_ARM64_ADRP_LDR_GOT 8 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + + +#ifndef CPU_SUBTYPE_ARM_V8 + #define CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) +#endif + +#ifndef CPU_SUBTYPE_ARM_V6M + #define CPU_SUBTYPE_ARM_V6M ((cpu_subtype_t) 14) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7M + #define CPU_SUBTYPE_ARM_V7M ((cpu_subtype_t) 15) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7EM + #define CPU_SUBTYPE_ARM_V7EM ((cpu_subtype_t) 16) +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif struct ArchInfo { const char* archName; @@ -263,7 +443,10 @@ struct ArchInfo { static const ArchInfo archInfoArray[] = { #if SUPPORT_ARCH_x86_64 - { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", false, false }, + { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", true, false }, +#endif +#if SUPPORT_ARCH_x86_64h + { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, "x86_64h-", "", true, false }, #endif #if SUPPORT_ARCH_i386 { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, "i386-", "", false, false }, @@ -295,11 +478,33 @@ static const ArchInfo archInfoArray[] = { #if SUPPORT_ARCH_armv7s { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S, "thumbv7s-", "armv7s", true, true }, #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv6m + { "armv6m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M, "thumbv6m-", "", true, false }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7m + { "armv7m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M, "thumbv7m-", "armv7m", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7em + { "armv7em", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM, "thumbv7em-", "armv7em", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv8 + { "armv8", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V8, "thumbv8-", "armv8", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_arm64 + { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, "arm64-", "", false, false }, +#endif +#if SUPPORT_ARCH_arm64v8 + { "arm64v8", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8, "arm64v8-", "", true, false }, #endif { NULL, 0, 0, NULL, NULL, false, false } }; - + // weird, but this include must wait until after SUPPORT_ARCH_arm_any is set up #if SUPPORT_ARCH_arm_any #include @@ -1125,27 +1330,37 @@ class macho_scattered_relocation_info { // // mach-o encyrption info load command // +template struct macho_encryption_info_content {}; +template <> struct macho_encryption_info_content > { struct encryption_info_command fields; }; +template <> struct macho_encryption_info_content > { struct encryption_info_command_64 fields; }; +template <> struct macho_encryption_info_content > { struct encryption_info_command fields; }; +template <> struct macho_encryption_info_content > { struct encryption_info_command_64 fields; }; + + template class macho_encryption_info_command { public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + uint32_t cmd() const INLINE { return E::get32(entry.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(entry.fields.cmd, value); } - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + uint32_t cmdsize() const INLINE { return E::get32(entry.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(entry.fields.cmdsize, value); } - uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); } - void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); } + uint32_t cryptoff() const INLINE { return E::get32(entry.fields.cryptoff); } + void set_cryptoff(uint32_t value) INLINE { E::set32(entry.fields.cryptoff, value); } - uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); } - void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); } + uint32_t cryptsize() const INLINE { return E::get32(entry.fields.cryptsize); } + void set_cryptsize(uint32_t value) INLINE { E::set32(entry.fields.cryptsize, value); } - uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); } - void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); } + uint32_t cryptid() const INLINE { return E::get32(entry.fields.cryptid); } + void set_cryptid(uint32_t value) INLINE { E::set32(entry.fields.cryptid, value); } + uint32_t pad() const INLINE { return E::get32(entry.fields.pad); } + void set_pad(uint32_t value) INLINE { E::set32(entry.fields.pad, value); } + typedef typename P::E E; private: - encryption_info_command fields; + macho_encryption_info_content

entry; }; @@ -1466,6 +1681,36 @@ class macho_data_in_code_entry { data_in_code_entry fields; }; +#ifndef DICE_KIND_DATA + #define DICE_KIND_DATA 0x0001 + #define DICE_KIND_JUMP_TABLE8 0x0002 + #define DICE_KIND_JUMP_TABLE16 0x0003 + #define DICE_KIND_JUMP_TABLE32 0x0004 + #define DICE_KIND_ABS_JUMP_TABLE32 0x0005 +#endif + +template +class macho_linker_option_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint64_t count() const INLINE { return fields.count; } + void set_count(uint32_t value) INLINE { E::set32(fields.count, value); } + + const char* buffer() const INLINE { return ((char*)&fields) + sizeof(linker_option_command); } + char* buffer() INLINE { return ((char*)&fields) + sizeof(linker_option_command); } + + typedef typename P::E E; +private: + linker_option_command fields; +}; + + + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp index 1885e48..b7c55c1 100644 --- a/ld64/src/abstraction/MachOTrie.hpp +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -329,7 +329,7 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* { if ( p >= end ) throw "malformed trie, node past end"; - const uint8_t terminalSize = read_uleb128(p, end); + const uint64_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { EntryWithOffset e; diff --git a/ld64/src/create_configure b/ld64/src/create_configure index 447ec7a..8ca92be 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -11,35 +11,22 @@ else fi if [ -z "${RC_SUPPORTED_ARCHS}" ]; then - RC_SUPPORTED_ARCHS="i386 x86_64 armv7 armv7s" + RC_SUPPORTED_ARCHS="i386 x86_64 x86_64h armv6 armv7 armv7s armv7m arm64" fi for ANARCH in ${RC_SUPPORTED_ARCHS} do - KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,i386,x86_64," + KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h," FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` if [ $FOUND ]; then echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h else - echo "#error uknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h + echo "#error unknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h fi done echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h -# ld64 hardcodes a reference to /Developer/usr/lib/libLTO.dylib -if [ -n "${DT_TOOLCHAIN_DIR}" ] -then - echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt -else - if [ -e "/Developer/usr/lib/libLTO.dylib" ] - then - echo "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt - else - echo "-Wl,-lazy_library,${BUILT_PRODUCTS_DIR}/../lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt - fi -fi - diff --git a/ld64/src/ld/Architectures.hpp b/ld64/src/ld/Architectures.hpp index 1145550..fdf795b 100644 --- a/ld64/src/ld/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -56,6 +56,11 @@ struct arm typedef Pointer32 P; }; +struct arm64 +{ + typedef Pointer64 P; +}; + #endif // __ARCHITECTURES__ diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 0885715..acbdf4f 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -109,7 +109,9 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract uint8_t* copyDataInCodeLoadCommand(uint8_t* p) const; uint8_t* copyDependentDRLoadCommand(uint8_t* p) const; uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; - + uint8_t* copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector&) const; + uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const; + uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const; @@ -136,6 +138,7 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract bool _hasDataInCodeLoadCommand; bool _hasSourceVersionLoadCommand; bool _hasDependentDRInfo; + bool _hasOptimizationHints; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; uint32_t _dyldEnvironExrasCount; @@ -143,6 +146,7 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract std::vector _subUmbrellaNames; uint8_t _uuid[16]; mutable macho_uuid_command

* _uuidCmdInOutputBuffer; + std::vector< std::vector > _linkerOptions; static ld::Section _s_section; static ld::Section _s_preload_section; @@ -174,6 +178,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _hasRoutinesLoadCommand = (opts.initFunctionName() != NULL); _hasSymbolTableLoadCommand = true; _hasUUIDLoadCommand = (opts.UUIDMode() != Options::kUUIDNone); + _hasOptimizationHints = (_state.someObjectHasOptimizationHints && (opts.outputKind() == Options::kObjectFile)); switch ( opts.outputKind() ) { case Options::kDynamicExecutable: case Options::kDynamicLibrary: @@ -192,6 +197,22 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: break; } } + for (CStringSet::const_iterator it = _state.linkerOptionFrameworks.begin(); it != _state.linkerOptionFrameworks.end(); ++it) { + const char* frameWorkName = *it; + std::vector* lo = new std::vector(); + lo->push_back("-framework"); + lo->push_back(frameWorkName); + _linkerOptions.push_back(*lo); + }; + for (CStringSet::const_iterator it = _state.linkerOptionLibraries.begin(); it != _state.linkerOptionLibraries.end(); ++it) { + const char* libName = *it; + std::vector* lo = new std::vector(); + char * s = new char[strlen(libName)+3]; + strcpy(s, "-l"); + strcat(s, libName); + lo->push_back(s); + _linkerOptions.push_back(*lo); + }; break; case Options::kStaticExecutable: _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); @@ -210,6 +231,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _dylibLoadCommmandsCount = _writer.dylibCount(); _allowableClientLoadCommmandsCount = _options.allowableClients().size(); _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); + if ( ! _options.useSimplifiedDylibReExports() ) { // target OS does not support LC_REEXPORT_DYLIB, so use old complicated load commands for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { @@ -394,8 +416,22 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasDataInCodeLoadCommand ) sz += sizeof(macho_linkedit_data_command

); + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + uint32_t s = sizeof(macho_linker_option_command

); + const std::vector& options = *it; + for (std::vector::const_iterator t=options.begin(); t != options.end(); ++t) { + s += (strlen(*t) + 1); + } + sz += alignedSize(s); + } + } + if ( _hasDependentDRInfo ) sz += sizeof(macho_linkedit_data_command

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

); return sz; } @@ -465,8 +501,17 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasDataInCodeLoadCommand ) ++count; + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + ++count; + } + } + if ( _hasDependentDRInfo ) ++count; + + if ( _hasOptimizationHints ) + ++count; return count; } @@ -525,9 +570,9 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const bits |= MH_FORCE_FLAT; break; } - if ( _writer.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols ) + if ( _state.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols ) bits |= MH_WEAK_DEFINES; - if ( _writer.usesWeakExternalSymbols || _writer.hasWeakExternalSymbols ) + if ( _writer.usesWeakExternalSymbols || _state.hasWeakExternalSymbols ) bits |= MH_BINDS_TO_WEAK; if ( _options.prebind() ) bits |= MH_PREBOUND; @@ -542,7 +587,7 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const bits |= MH_PIE; if ( _options.markAutoDeadStripDylib() ) bits |= MH_DEAD_STRIPPABLE_DYLIB; - if ( _writer.hasThreadLocalVariableDefinitions ) + if ( _state.hasThreadLocalVariableDefinitions ) bits |= MH_HAS_TLV_DESCRIPTORS; if ( _options.hasNonExecutableHeap() ) bits |= MH_NO_HEAP_EXECUTION; @@ -556,10 +601,12 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_I386; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_X86_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM64; } @@ -572,10 +619,10 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const template <> uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const { - if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) - return (CPU_SUBTYPE_X86_64_ALL | 0x80000000); + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_state.cpuSubType == CPU_SUBTYPE_X86_64_ALL) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (_state.cpuSubType | 0x80000000); else - return CPU_SUBTYPE_X86_64_ALL; + return _state.cpuSubType; } template <> @@ -584,6 +631,12 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const return _state.cpuSubType; } +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return CPU_SUBTYPE_ARM64_ALL; +} + template @@ -1144,6 +1197,28 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) cons } +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command

* cmd = (macho_thread_command

*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(6); // ARM_THREAD_STATE64 + cmd->set_count(68); // ARM_EXCEPTION_STATE64_COUNT + cmd->set_thread_register(32, start); // pc + if ( _options.hasCustomStack() ) + cmd->set_thread_register(31, _options.customStackAddr()); // sp + return p + threadLoadCommandSize(); +} + template uint8_t* HeaderAndLoadCommandsAtom::copyEntryPointLoadCommand(uint8_t* p) const @@ -1165,7 +1240,7 @@ template uint8_t* HeaderAndLoadCommandsAtom::copyEncryptionLoadCommand(uint8_t* p) const { macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)p; - cmd->set_cmd(LC_ENCRYPTION_INFO); + cmd->set_cmd(sizeof(typename A::P::uint_t) == 4 ? LC_ENCRYPTION_INFO : LC_ENCRYPTION_INFO_64); cmd->set_cmdsize(sizeof(macho_encryption_info_command

)); assert(_writer.encryptedTextStartOffset() != 0); assert(_writer.encryptedTextEndOffset() != 0); @@ -1310,6 +1385,27 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDataInCodeLoadCommand(uint8_t* p) con } +template +uint8_t* HeaderAndLoadCommandsAtom::copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector& options) const +{ + macho_linker_option_command

* cmd = (macho_linker_option_command

*)p; + cmd->set_cmd(LC_LINKER_OPTION); + cmd->set_count(options.size()); + char* buffer = cmd->buffer(); + uint32_t sz = sizeof(macho_linker_option_command

); + for (std::vector::const_iterator it=options.begin(); it != options.end(); ++it) { + const char* opt = *it; + uint32_t len = strlen(opt); + strcpy(buffer, opt); + sz += (len + 1); + buffer += (len + 1); + } + sz = alignedSize(sz); + cmd->set_cmdsize(sz); + return p + sz; +} + + template uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) const { @@ -1322,6 +1418,19 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) co } + +template +uint8_t* HeaderAndLoadCommandsAtom::copyOptimizationHintsLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

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

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

); +} + + template void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { @@ -1384,7 +1493,7 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasSplitSegInfoLoadCommand ) p = this->copySplitSegInfoLoadCommand(p); - for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + for (uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { p = this->copyDylibLoadCommand(p, _writer.dylibByOrdinal(ord)); } @@ -1425,10 +1534,19 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasDataInCodeLoadCommand ) p = this->copyDataInCodeLoadCommand(p); + + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + p = this->copyLinkerOptionsLoadCommand(p, *it); + } + } if ( _hasDependentDRInfo ) p = this->copyDependentDRLoadCommand(p); + if ( _hasOptimizationHints ) + p = this->copyOptimizationHintsLoadCommand(p); + } diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 7bf136b..f49f2e3 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -1,3 +1,4 @@ + /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * * Copyright (c) 2009-2011 Apple Inc. All rights reserved. @@ -60,6 +61,7 @@ #include "archive_file.h" #include "lto_file.h" #include "opaque_section_file.h" +#include "MachOFileAbstraction.hpp" #include "Snapshot.h" const bool _s_logPThreads = false; @@ -180,7 +182,11 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) const char* result = mach_o::relocatable::archName(p); if ( result != NULL ) return result; - + + result = mach_o::dylib::archName(p); + if ( result != NULL ) + return result; + result = lto::archName(p, len); if ( result != NULL ) return result; @@ -192,7 +198,7 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) strcpy(unsupported, "unsupported file format ("); for (unsigned i=0; i(file); + if ( file == _bundleLoader ) { + _options.dumpDependency(Options::depBundleLoader, file->path()); + } + else if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) { + if ( indirect ) + _options.dumpDependency(Options::depUpwardIndirectDylib, file->path()); + else + _options.dumpDependency(Options::depUpwardDirectDylib, file->path()); + } + else { + if ( indirect ) + _options.dumpDependency(Options::depIndirectDylib, file->path()); + else + _options.dumpDependency(Options::depDirectDylib, file->path()); + } + } } void InputFiles::logArchive(ld::File* file) const @@ -396,7 +425,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const if ( trace_file_path != NULL ) { trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); if ( trace_file == -1 ) - throwf("Could not open or create trace file: %s", trace_file_path); + throwf("Could not open or create trace file (errno=%d): %s", errno, trace_file_path); } else { trace_file = fileno(stderr); @@ -420,6 +449,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const } } + ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath) { //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); @@ -435,6 +465,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from Options::FileInfo info = _options.findFile(dit->useInstead); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; + info.options.fIndirectDylib = true; ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { @@ -467,6 +498,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from Options::FileInfo info = _options.findFileUsingPaths(installPath); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; + info.options.fIndirectDylib = true; try { ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); @@ -487,18 +519,86 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from } - -void InputFiles::createIndirectDylibs() -{ - _allDirectDylibsLoaded = true; - _indirectDylibOrdinal = ld::File::Ordinal::indirectDylibBase(); - - // mark all dylibs initially specified as required and check if they can be used +// mark all dylibs initially specified as required, and check if they can be used +void InputFiles::markExplicitlyLinkedDylibs() +{ for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) { it->second->setExplicitlyLinked(); this->checkDylibClientRestrictions(it->second); } - +} + +bool InputFiles::libraryAlreadyLoaded(const char* path) +{ + for (std::vector::const_iterator it = _inputFiles.begin(); it != _inputFiles.end(); ++it) { + if ( strcmp(path, (*it)->path()) == 0 ) + return true; + } + return false; +} + + +void InputFiles::addLinkerOptionLibraries(ld::Internal& state) +{ + if ( _options.outputKind() == Options::kObjectFile ) + return; + + // process frameworks specified in .o linker options + for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) { + const char* frameworkName = *it; + Options::FileInfo info = _options.findFramework(frameworkName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->setImplicitlyLinked(); + this->addDylib(dylibReader, info); + } + } + else { + throwf("framework linker option at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + warning("Auto-Linking supplied '%s', %s", info.path, msg); + } + } + } + // process libraries specified in .o linker options + for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) { + const char* libName = *it; + Options::FileInfo info = _options.findLibrary(libName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + ld::archive::File* archiveReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + dylibReader->setImplicitlyLinked(); + this->addDylib(dylibReader, info); + } + else if ( archiveReader != NULL ) { + _searchLibraries.push_back(LibraryInfo(archiveReader)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archiveReader->path()); + } + else { + throwf("linker option dylib at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + warning("Auto-Linking supplied '%s', %s", info.path, msg); + } + } + } +} + +void InputFiles::createIndirectDylibs() +{ // keep processing dylibs until no more dylibs are added unsigned long lastMapSize = 0; std::set dylibsProcessed; @@ -539,9 +639,11 @@ void InputFiles::createIndirectDylibs() void InputFiles::createOpaqueFileSections() { - // extra command line section always at end + // extra command line sections always at end for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depSection, it->path); } } @@ -672,8 +774,10 @@ InputFiles::InputFiles(Options& opts, const char** archName) : _totalObjectSize(0), _totalArchiveSize(0), _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), _options(opts), _bundleLoader(NULL), - _allDirectDylibsLoaded(false), _inferredArch(false), - _exception(NULL) + _inferredArch(false), + _exception(NULL), + _indirectDylibOrdinal(ld::File::Ordinal::indirectDylibBase()), + _linkerOptionOrdinal(ld::File::Ordinal::linkeOptionBase()) { // fStartCreateReadersTime = mach_absolute_time(); if ( opts.architecture() == 0 ) { @@ -781,7 +885,8 @@ void InputFiles::parseWorkerThread() { if (_s_logPThreads) printf("parsing index %u\n", slot); try { file = makeFile(entry, false); - } catch (const char *msg) { + } + catch (const char *msg) { if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { if ( _options.ignoreOtherArchInputFiles() ) { // ignore, because this is about an architecture not in use @@ -789,8 +894,9 @@ void InputFiles::parseWorkerThread() { else { warning("ignoring file %s, %s", entry.path, msg); } - } else { - exception = msg; + } + else { + asprintf((char**)&exception, "%s file '%s'", msg, entry.path); } file = new IgnoredFile(entry.path, entry.modTime, entry.ordinal, ld::File::Other); } @@ -802,7 +908,8 @@ void InputFiles::parseWorkerThread() { // We are about to die, so set to zero to stop other threads from doing unneeded work. _remainingInputFiles = 0; _exception = exception; - } else { + } + else { _inputFiles[slot] = file; if (_neededFileSlot == slot) pthread_cond_signal(&_newFileAvailable); @@ -867,22 +974,23 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& } // remove warning for Same install name for CoreServices and CFNetwork? //if ( !dylibOnCommandLineTwice && !isSymlink ) - // warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + // warning("dylibs with same install name: %p %s and %p %s", pos->second, pos->second->path(), reader, reader->path()); } } else if ( info.options.fBundleLoader ) _bundleLoader = reader; // log direct readers - if ( !_allDirectDylibsLoaded ) + if ( ! info.options.fIndirectDylib ) this->logDylib(reader, false); // update stats _totalDylibsLoaded++; // just add direct libraries to search-first list - if ( !_allDirectDylibsLoaded ) + if ( ! info.options.fIndirectDylib ) _searchLibraries.push_back(LibraryInfo(reader)); + return reader; } @@ -918,7 +1026,7 @@ void InputFiles::waitForInputFiles() if (it == fileMap.end()) throwf("pipelined linking error - not in file list: %s\n", path_buf); Options::FileInfo* inputInfo = (Options::FileInfo*)it->second; - if (!inputInfo->checkFileExists()) + if (!inputInfo->checkFileExists(_options)) throwf("pipelined linking error - file does not exist: %s\n", inputInfo->path); pthread_mutex_lock(&_parseLock); if (_idleWorkers) @@ -946,7 +1054,7 @@ void InputFiles::waitForInputFiles(InputFiles *inputFiles) { #endif -void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) +void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal& state) { // add all direct object, archives, and dylibs const std::vector& files = _options.getInputFiles(); @@ -986,6 +1094,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) { ld::relocatable::File* reloc = (ld::relocatable::File*)file; _options.snapshot().recordObjectFile(reloc->path()); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depObjectFile, reloc->path()); } break; case ld::File::Dylib: @@ -1001,6 +1111,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) logArchive(archive); _searchLibraries.push_back(LibraryInfo(archive)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archive->path()); } break; case ld::File::Other: @@ -1014,6 +1126,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) file->forEachAtom(handler); } + markExplicitlyLinkedDylibs(); + addLinkerOptionLibraries(state); createIndirectDylibs(); createOpaqueFileSections(); @@ -1095,7 +1209,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc logArchive(archiveFile); _options.snapshot().recordArchive(archiveFile->path()); // found data definition in static library, done - return true; + return true; } } else { @@ -1145,7 +1259,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc bool InputFiles::searchWeakDefInDylib(const char* name) const { - // search all relevant dylibs to see if any of a weak-def with this name + // search all relevant dylibs to see if any have a weak-def with this name for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { ld::dylib::File* dylibFile = it->second; if ( dylibFile->implicitlyLinked() || dylibFile->explicitlyLinked() ) { @@ -1224,4 +1338,3 @@ void InputFiles::dylibs(ld::Internal& state) } // namespace tool } // namespace ld - diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index 9b969ee..b1e85cb 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -63,7 +63,7 @@ class InputFiles : public ld::dylib::File::DylibHandler virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath); // iterates all atoms in initial files - void forEachInitialAtom(ld::File::AtomHandler&); + void forEachInitialAtom(ld::File::AtomHandler&, ld::Internal& state); // searches libraries for name bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler&) const; @@ -74,6 +74,9 @@ class InputFiles : public ld::dylib::File::DylibHandler bool inferredArch() const { return _inferredArch; } + void addLinkerOptionLibraries(ld::Internal& state); + void createIndirectDylibs(); + // for -print_statistics volatile int64_t _totalObjectSize; volatile int64_t _totalArchiveSize; @@ -90,12 +93,13 @@ class InputFiles : public ld::dylib::File::DylibHandler void logTraceInfo (const char* format, ...) const; void logDylib(ld::File*, bool indirect); void logArchive(ld::File*) const; - void createIndirectDylibs(); + void markExplicitlyLinkedDylibs(); void checkDylibClientRestrictions(ld::dylib::File*); void createOpaqueFileSections(); + bool libraryAlreadyLoaded(const char* path); // for pipelined linking - void waitForInputFiles(); + void waitForInputFiles(); static void waitForInputFiles(InputFiles *inputFiles); // for threaded input file processing @@ -111,7 +115,6 @@ class InputFiles : public ld::dylib::File::DylibHandler InstallNameToDylib _installPathToDylibs; std::set _allDylibs; ld::dylib::File* _bundleLoader; - bool _allDirectDylibsLoaded; bool _inferredArch; struct strcompclass { bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } @@ -132,6 +135,7 @@ class InputFiles : public ld::dylib::File::DylibHandler int _remainingInputFiles; // number of input files still to parse ld::File::Ordinal _indirectDylibOrdinal; + ld::File::Ordinal _linkerOptionOrdinal; class LibraryInfo { ld::File* _lib; diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index 1149f0d..3420626 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -1005,6 +1005,14 @@ void ExportInfoAtom::encode() const entries.push_back(entry); //fprintf(stderr, "re-export %s from lib %llu as %s\n", entry.importName, entry.other, entry.name); } + else if ( atom->definition() == ld::Atom::definitionAbsolute ) { + entry.name = atom->name(); + entry.flags = _options.canUseAbsoluteSymbols() ? EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE : EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + entry.address = address; + entry.other = other; + entry.importName = NULL; + entries.push_back(entry); + } else { if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; @@ -1068,6 +1076,7 @@ class SplitSegInfoAtom : public LinkEditAtom mutable std::vector _thumbHi16Locations[16]; mutable std::vector _armLo16Locations; mutable std::vector _armHi16Locations[16]; + mutable std::vector _adrpLocations; static ld::Section _s_section; @@ -1092,6 +1101,8 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: _32bitPointerLocations.push_back(address); break; case ld::Fixup::kindStoreLittleEndian64: @@ -1110,6 +1121,8 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki switch (kind) { case ld::Fixup::kindStoreLittleEndian32: case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: _32bitPointerLocations.push_back(address); break; default: @@ -1145,7 +1158,34 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki } } - +#if SUPPORT_ARCH_arm64 +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +{ + switch (kind) { + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + _adrpLocations.push_back(address); + break; + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreARM64PCRelToGOT: + _32bitPointerLocations.push_back(address); + break; + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + _64bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} +#endif template void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& locations) const @@ -1201,6 +1241,14 @@ void SplitSegInfoAtom::encode() const this->_encodedData.append_byte(0); // terminator } + if ( _adrpLocations.size() != 0 ) { + this->_encodedData.append_byte(3); + //fprintf(stderr, "type 3:\n"); + std::sort(_adrpLocations.begin(), _adrpLocations.end()); + this->uleb128EncodeAddresses(_adrpLocations); + this->_encodedData.append_byte(0); // terminator + } + if ( _thumbLo16Locations.size() != 0 ) { this->_encodedData.append_byte(5); //fprintf(stderr, "type 5:\n"); @@ -1507,6 +1555,68 @@ void DependentDRAtom::encode() const +template +class OptimizationHintsAtom : public LinkEditAtom +{ +public: + OptimizationHintsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { + assert(opts.outputKind() == Options::kObjectFile); + } + + // overrides of ld::Atom + virtual const char* name() const { return "linker optimization hints"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; + +}; + +template +ld::Section OptimizationHintsAtom::_s_section("__LINKEDIT", "__opt_hints", ld::Section::typeLinkEdit, true); + +template +void OptimizationHintsAtom::encode() const +{ + if ( _state.someObjectHasOptimizationHints ) { + for (std::vector::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeCode ) + continue; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + uint64_t address = atom->finalAddress(); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint) + continue; + ld::Fixup::LOH_arm64 extra; + extra.addend = fit->u.addend; + _encodedData.append_uleb128(extra.info.kind); + _encodedData.append_uleb128(extra.info.count+1); + _encodedData.append_uleb128((extra.info.delta1 << 2) + fit->offsetInAtom + address); + if ( extra.info.count > 0 ) + _encodedData.append_uleb128((extra.info.delta2 << 2) + fit->offsetInAtom + address); + if ( extra.info.count > 1 ) + _encodedData.append_uleb128((extra.info.delta3 << 2) + fit->offsetInAtom + address); + if ( extra.info.count > 2 ) + _encodedData.append_uleb128((extra.info.delta4 << 2) + fit->offsetInAtom + address); + } + } + } + + this->_encodedData.pad_to_size(sizeof(pint_t)); + } + + this->_encoded = true; +} + + } // namespace tool } // namespace ld diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 60a4fa9..b9b1215 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -927,6 +927,9 @@ uint64_t ExternalRelocationsAtom::size() const return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info

); } +#if SUPPORT_ARCH_arm64 +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM64_RELOC_UNSIGNED; } +#endif #if SUPPORT_ARCH_arm_any template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } #endif @@ -936,6 +939,10 @@ template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X8 template <> uint32_t ExternalRelocationsAtom::callReloc() { return X86_64_RELOC_BRANCH; } template <> uint32_t ExternalRelocationsAtom::callReloc() { return GENERIC_RELOC_VANILLA; } +#if SUPPORT_ARCH_arm64 +template <> uint32_t ExternalRelocationsAtom::callReloc() { return ARM64_RELOC_BRANCH26; } +#endif + template uint32_t ExternalRelocationsAtom::callReloc() { @@ -1647,6 +1654,205 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } #endif +#if SUPPORT_ARCH_arm64 +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreARM64Branch26: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_BRANCH26); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64Page21: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64Page21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_PAGE21); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64PageOff12: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_PAGEOFF12); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGE21); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGEOFF12); + relocs.push_back(reloc1); + break; + + + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreARM64PointerToGOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64PCRelToGOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT); + relocs.push_back(reloc1); + break; + + default: + assert(0 && "need to handle arm64 -r reloc"); + + } + +} +#endif // SUPPORT_ARCH_arm64 template diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 0131911..057fad9 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -104,19 +104,24 @@ void throwf(const char* format, ...) throw t; } -bool Options::FileInfo::checkFileExists(const char *p) + +bool Options::FileInfo::checkFileExists(const Options& options, const char *p) { struct stat statBuffer; - if (p == NULL) p = path; + if (p == NULL) + p = path; if ( stat(p, &statBuffer) == 0 ) { if (p != path) path = strdup(p); fileLen = statBuffer.st_size; modTime = statBuffer.st_mtime; return true; } + if ( options.dumpDependencyInfo() ) + options.dumpDependency(Options::depNotFound, p); return false; } + Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), @@ -131,7 +136,7 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), + fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), @@ -167,9 +172,15 @@ Options::Options(int argc, const char* argv[]) fSourceVersionLoadCommand(false), fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false), + fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), + fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), + fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), + fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), + fGenerateDtraceDOF(true), fAllowBranchIslands(true), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), - fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL) + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -177,10 +188,18 @@ Options::Options(int argc, const char* argv[]) this->parsePostCommandLineEnvironmentSettings(); this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); + + if ( this->dumpDependencyInfo() ) { + this->dumpDependency(depOutputFile, fOutputFile); + if ( fMapPath != NULL ) + this->dumpDependency(depOutputFile, fMapPath); + } } Options::~Options() { + if ( fDependencyFileDescriptor != -1 ) + ::close(fDependencyFileDescriptor); } bool Options::errorBecauseOfWarnings() const @@ -246,7 +265,11 @@ bool Options::allGlobalsAreDeadStripRoots() const // switch ( fOutputKind ) { case Options::kDynamicExecutable: + // Add the -export_dynamic flag + return fExportDynamic; case Options::kStaticExecutable: + // Support the -export_dynamic flag for xnu + return fExportDynamic; case Options::kPreload: // by default unused globals in a main executable are stripped return false; @@ -276,7 +299,6 @@ const char* Options::executablePath() return fExecutablePath; } - uint32_t Options::initialSegProtection(const char* segName) const { for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -478,6 +500,11 @@ bool Options::forceNotWeakNonWildcard(const char* symbolName) const return fForceNotWeakSymbols.containsNonWildcard(symbolName); } +bool Options::forceCoalesce(const char* symbolName) const +{ + return fForceCoalesceSymbols.contains(symbolName); +} + bool Options::shouldExport(const char* symbolName) const { @@ -533,27 +560,29 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fMacVersionMin = ld::mac10_6; #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; break; case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); - #elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; break; } fLinkSnapshot.recordArch(fArchitectureName); + // only use compressed LINKEDIT for: + // Mac OS X 10.6 or later + // iOS 3.1 or later + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB + if ( minOS(ld::mac10_5, ld::iOS_2_0) ) + fUseSimplifiedDylibReExports = true; return; } } @@ -581,20 +610,20 @@ bool Options::checkForFile(const char* format, const char* dir, const char* root { char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); - bool found = result.checkFileExists(possiblePath); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); return found; } -Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const { FileInfo result; const int rootNameLen = strlen(rootName); // if rootName ends in .o there is no .a vs .dylib choice if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -603,19 +632,33 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } } else { - bool lookForDylibs = ( fOutputKind != Options::kDyld); + bool lookForDylibs = false; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // + lookForDylibs = true; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + lookForDylibs = false; + break; + } switch ( fLibrarySearchMode ) { case kSearchAllDirsForDylibsThenAllDirsForArchives: // first look in all directories for just for dylibs if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; } - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -625,7 +668,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } // next look in all directories for just for archives if ( !dylibsOnly ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -637,7 +680,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) case kSearchDylibAndArchiveInEachDir: // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -654,7 +697,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) throwf("library not found for -l%s", rootName); } -Options::FileInfo Options::findFramework(const char* frameworkName) +Options::FileInfo Options::findFramework(const char* frameworkName) const { if ( frameworkName == NULL ) throw "-framework missing next argument"; @@ -670,9 +713,9 @@ Options::FileInfo Options::findFramework(const char* frameworkName) return findFramework(name, suffix); } -Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const { - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { // ??? Shouldn't we be using String here and just initializing it? @@ -693,7 +736,7 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi } } FileInfo result; - bool found = result.checkFileExists(possiblePath); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", (found ? " " : " not "), possiblePath); @@ -724,13 +767,13 @@ Options::FileInfo Options::findFile(const char* path) const if ( possiblePath[sdkPathDirLen-1] == '/' ) possiblePath[sdkPathDirLen-1] = '\0'; strcat(possiblePath, path); - if ( result.checkFileExists(possiblePath) ) { + if ( result.checkFileExists(*this, possiblePath) ) { return result; } } } // try raw path - if ( result.checkFileExists(path) ) { + if ( result.checkFileExists(*this, path) ) { return result; } @@ -743,7 +786,7 @@ Options::FileInfo Options::findFile(const char* path) const strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); - if ( result.checkFileExists(newPath) ) { + if ( result.checkFileExists(*this, newPath) ) { return result; } } @@ -815,7 +858,8 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) const // If we didn't find it fall back to findFile. return findFile(path); } - + + void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) { @@ -886,12 +930,16 @@ void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdina file = fopen(realFileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, realFileOfPaths); } } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, fileOfPaths); } char path[PATH_MAX]; @@ -1064,6 +1112,9 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, SetW if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfExports); + ::close(fd); // parse into symbols and add to unordered_set @@ -1135,6 +1186,8 @@ void Options::parseAliasFile(const char* fileOfAliases) throwf("can't read alias file: %s", fileOfAliases); p[stat_buf.st_size] = '\n'; ::close(fd); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfAliases); // parse into symbols and add to fAliases AliasPair pair; @@ -1248,7 +1301,14 @@ void Options::setMacOSXVersionMin(const char* version) throw "-macosx_version_min argument missing"; if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - unsigned int minorVersion = version[3] - '0'; + unsigned int minorVersion = 0; + for (int i=3; isdigit(version[i]); ++i) { + minorVersion = minorVersion*10 + (version[i] - '0'); + } + if ( minorVersion > 255 ) { + warning("Mac OS X minor version > 255 in '%s'", version); + minorVersion = 255; + } fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); } else { @@ -1533,6 +1593,8 @@ void Options::parseOrderFile(const char* path, bool cstring) throwf("can't read order file: %s", path); ::close(fd); p[stat_buf.st_size] = '\n'; + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, path); // parse into vector of pairs char * const end = &p[stat_buf.st_size+1]; @@ -1592,6 +1654,14 @@ void Options::parseOrderFile(const char* path, bool cstring) objFileName = symbolStart; symbolStart = &colon[3]; } + else { + colon = strstr(symbolStart, ".o):"); + if ( colon != NULL ) { + colon[3] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[4]; + } + } // trim leading spaces while ( isspace(*symbolStart) ) ++symbolStart; @@ -1662,6 +1732,27 @@ void Options::addSection(const char* segment, const char* section, const char* p fExtraSections.push_back(info); } +void Options::addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(srcSection) > 16 ) + throw "-rename_section section name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(dstSection) > 16 ) + throw "-rename_section section name max 16 chars"; + + SectionRename info; + info.fromSegment = srcSegment; + info.fromSection = srcSection; + info.toSegment = dstSegment; + info.toSection = dstSection; + + fSectionRenames.push_back(info); +} + + void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) { if ( strlen(segment) > 16 ) @@ -2264,13 +2355,34 @@ void Options::parse(int argc, const char* argv[]) } // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - setMacOSXVersionMin(argv[++i]); + const char* macVers = argv[++i]; + const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { + // when conflicting deployments set, break tie by looking at syslibroot + warning("both MACOSX_DEPLOYMENT_TARGET and IPHONEOS_DEPLOYMENT_TARGET are set"); + if ( !fSDKPaths.empty() ) { + const char* sysrootPath = fSDKPaths.back(); + const char* lastSlash = strrchr(sysrootPath, '/'); + if ( strstr(lastSlash, "Simulator") != NULL ) + setIOSVersionMin(enviPhoneVers); + else + setMacOSXVersionMin(macVers); + } + else { + setMacOSXVersionMin(macVers); + } + } + else { + setMacOSXVersionMin(macVers); + } } 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]); + fTargetIOSSimulator = true; } else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); @@ -2495,18 +2607,6 @@ void Options::parse(int argc, const char* argv[]) snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); } - // put this last so that it does not interfer with other options starting with 'i' - else if ( strncmp(arg, "-i", 2) == 0 ) { - const char* colon = strchr(arg, ':'); - if ( colon == NULL ) - throwf("unknown option: %s", arg); - Options::AliasPair pair; - char* temp = new char[colon-arg]; - strlcpy(temp, &arg[2], colon-arg-1); - pair.realName = &colon[1]; - pair.alias = temp; - fAliases.push_back(pair); - } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; } @@ -2603,6 +2703,12 @@ void Options::parse(int argc, const char* argv[]) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); } + else if ( strcmp(arg, "-mcpu") == 0 ) { + const char* cpu = argv[++i]; + if ( cpu == NULL ) + throw "missing argument to -mcpu"; + fLtoCpu = cpu; + } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; } @@ -2755,6 +2861,14 @@ void Options::parse(int argc, const char* argv[]) else if (strcmp(arg, "-debug_snapshot") == 0) { fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fSnapshotRequested = true; + } + else if (strcmp(arg, "-snapshot_dir") == 0) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-snapshot_dir missing path"; + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fLinkSnapshot.setSnapshotPath(path); + fSnapshotRequested = true; } else if ( strcmp(arg, "-new_main") == 0 ) { fEntryPointLoadCommandForceOn = true; @@ -2789,6 +2903,80 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { fKextsUseStubs = true; } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-export_dynamic") == 0 ) { + fExportDynamic = true; + } + else if ( strcmp(arg, "-force_symbols_coalesce_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_coalesce_list", fForceCoalesceSymbols); + } + else if ( strcmp(arg, "-add_linker_option") == 0 ) { + // ex: -add_linker_option '-framework Foundation' + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-add_linker_option missing

"; + addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]); + i += 4; + } + else if ( strcmp(arg, "-no_branch_islands") == 0 ) { + fAllowBranchIslands = false; + } + // put this last so that it does not interfer with other options starting with 'i' + else if ( strncmp(arg, "-i", 2) == 0 ) { + const char* colon = strchr(arg, ':'); + if ( colon == NULL ) + throwf("unknown option: %s", arg); + Options::AliasPair pair; + char* temp = new char[colon-arg]; + strlcpy(temp, &arg[2], colon-arg-1); + pair.realName = &colon[1]; + pair.alias = temp; + fAliases.push_back(pair); + } else { throwf("unknown option: %s", arg); } @@ -2918,6 +3106,12 @@ void Options::buildSearchPaths(int argc, const char* argv[]) else if ( strcmp(argv[i], "-fatal_warnings") == 0 ) { sFatalWarnings = true; } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-dependency_info missing "; + fDependencyInfoPath = path; + } } int standardLibraryPathsStartIndex = libraryPaths.size(); int standardFrameworkPathsStartIndex = frameworkPaths.size(); @@ -3198,17 +3392,14 @@ void Options::reconfigureDefaults() #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); - #elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); #endif } break; default: - // architecture will be infered ;ater by examining .o files + // architecture will be infered later by examining .o files break; } } @@ -3229,6 +3420,12 @@ void Options::reconfigureDefaults() fMacVersionMin = ld::mac10_4; } break; + case CPU_TYPE_ARM64: + if ( fIOSVersionMin < ld::iOS_7_0 ) { + //warning("-mios_version_min should be 7.0 or later for arm64"); + fIOSVersionMin = ld::iOS_7_0; + } + break; } // default to adding functions start for dynamic code, static code must opt-in @@ -3242,6 +3439,11 @@ void Options::reconfigureDefaults() fFunctionStartsLoadCommand = true; break; case Options::kObjectFile: + if ( !fDataInCodeInfoLoadCommandForcedOff ) + fDataInCodeInfoLoadCommand = true; + if ( fFunctionStartsForcedOn ) + fFunctionStartsLoadCommand = true; + break; case Options::kDynamicExecutable: case Options::kDyld: case Options::kDynamicLibrary: @@ -3263,6 +3465,14 @@ void Options::reconfigureDefaults() fAllowTextRelocs = true; fUndefinedTreatment = kUndefinedDynamicLookup; break; + case CPU_TYPE_ARM64: + // arm64 uses new MH_KEXT_BUNDLE type + fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; + fAllowTextRelocs = false; + fKextsUseStubs = 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 @@ -3470,11 +3680,16 @@ void Options::reconfigureDefaults() // -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) fDebugInfoStripping = Options::kDebugInfoNone; - + + // -r implies -no_uuid + if ( fOutputKind == Options::kObjectFile ) + fUUIDMode = kUUIDNone; + // choose how to process unwind info switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: switch ( fOutputKind ) { case Options::kObjectFile: case Options::kStaticExecutable: @@ -3501,10 +3716,10 @@ void Options::reconfigureDefaults() break; } - // only ARM main executables can be encrypted + // only iOS main executables should be encrypted if ( fOutputKind != Options::kDynamicExecutable ) fEncryptable = false; - if ( fArchitecture != CPU_TYPE_ARM ) + if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) ) fEncryptable = false; // don't move inits in dyld because dyld wants certain @@ -3554,10 +3769,17 @@ void Options::reconfigureDefaults() fMakeCompressedDyldInfo = false; } - - // only ARM enforces that cpu-sub-types must match - if ( fArchitecture != CPU_TYPE_ARM ) - fAllowCpuSubtypeMismatches = true; + // only ARM and x86_64 enforces that cpu-sub-types must match + switch ( fArchitecture ) { + case CPU_TYPE_ARM: + case CPU_TYPE_X86_64: + break; + case CPU_TYPE_I386: + case CPU_TYPE_ARM64: + fAllowCpuSubtypeMismatches = true; + break; + } + // only final linked images can not optimize zero fill sections if ( fOutputKind == Options::kObjectFile ) @@ -3611,6 +3833,11 @@ void Options::reconfigureDefaults() if ( fDisablePositionIndependentExecutable ) fPositionIndependentExecutable = false; + // arm64 is always PIE + if ( (fArchitecture == CPU_TYPE_ARM64) && (fOutputKind == kDynamicExecutable) ) { + fPositionIndependentExecutable = true; + } + // set fOutputSlidable switch ( fOutputKind ) { case Options::kObjectFile: @@ -3635,7 +3862,10 @@ void Options::reconfigureDefaults() if ( fMacVersionMin >= ld::mac10_7 ) { fTLVSupport = true; } - + else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= 0x00080000) ) { + fTLVSupport = true; + } + // default to adding version load command for dynamic code, static code must opt-in switch ( fOutputKind ) { case Options::kObjectFile: @@ -3653,13 +3883,6 @@ void Options::reconfigureDefaults() 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; } @@ -3699,12 +3922,7 @@ void Options::reconfigureDefaults() fNeedsThreadLoadCommand = true; } else { - if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fArchitecture == CPU_TYPE_I386) ) { - // don't use LC_MAIN for simulator until min host OS is 10.8 for simulator - fNeedsThreadLoadCommand = true; - fEntryPointLoadCommand = false; - } - else if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { + if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { fEntryPointLoadCommand = true; fEntryName = "_main"; } @@ -3816,6 +4034,70 @@ void Options::reconfigureDefaults() } } + // allow trie based absolute symbols if targeting new enough OS + if ( fMakeCompressedDyldInfo ) { + if ( minOS(ld::mac10_9, ld::iOS_7_0) ) { + // Allow absolute symbols in export trie for device but not simulator + if ( !fTargetIOSSimulator ) + fAbsoluteSymbols = true; + } + } + + // iOS main executables now default to 16KB page size + if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fOutputKind == Options::kDynamicExecutable) ) { + // Only third party apps should have 16KB page segments by default + if ( fEncryptable ) { + if ( fSegmentAlignment == 4096 ) + fSegmentAlignment = 4096*4; + } + } + + // ARM64 needs 16KB page size for user land code + if ( fArchitecture == CPU_TYPE_ARM64 ) { + if ( fSegmentAlignment == 4096 ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + fSegmentAlignment = 4096*4; + break; + case Options::kStaticExecutable: + case Options::kKextBundle: + case Options::kObjectFile: + case Options::kPreload: + break; + } + } + } + + // linker should not convert dwarf unwind if .o file has compact unwind section + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + if ( fKeepDwarfUnwindForcedOn ) { + fKeepDwarfUnwind = true; + } + else if ( fKeepDwarfUnwindForcedOff ) { + fKeepDwarfUnwind = false; + } + else { + if ( minOS(ld::mac10_9, ld::iOS_7_0) ) + fKeepDwarfUnwind = false; + else + fKeepDwarfUnwind = true; + } + break; + case Options::kKextBundle: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kPreload: + fKeepDwarfUnwind = true; + break; + } + } void Options::checkIllegalOptionCombinations() @@ -3890,6 +4172,7 @@ void Options::checkIllegalOptionCombinations() throw "-stack_addr must be < 4G for 32-bit processes"; break; case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: break; } if ( (fStackAddr & -4096) != fStackAddr ) @@ -3917,11 +4200,19 @@ void Options::checkIllegalOptionCombinations() fStackAddr = 0x2F000000; if ( fStackAddr > 0x30000000) throw "-stack_addr must be < 0x30000000 for arm"; + break; case CPU_TYPE_X86_64: if ( fStackAddr == 0 ) { fStackAddr = 0x00007FFF5C000000LL; } break; + case CPU_TYPE_ARM64: + if ( fStackSize > 0x20000000 ) + throw "-stack_size must be < 512MB"; + if ( fStackAddr == 0 ) { + fStackAddr = 0x120000000; + } + break; } if ( (fStackSize & -4096) != fStackSize ) throw "-stack_size must be multiples of 4K"; @@ -4018,6 +4309,10 @@ void Options::checkIllegalOptionCombinations() if ( fSetuidSafe && (fOutputKind == Options::kObjectFile) ) throw "-setuid_safe cannot be used with -r"; + // compiler driver no longer uses -objc_abi_version, it uses -ios_simulator_version_min instead + if ( !fObjCABIVersion1Override && !fObjCABIVersion2Override && fTargetIOSSimulator ) + fObjCABIVersion2Override = true; + // rdar://problem/4718189 map ObjC class names to new runtime names bool alterObjC1ClassNamesToObjC2 = false; switch (fArchitecture) { @@ -4028,6 +4323,7 @@ void Options::checkIllegalOptionCombinations() break; case CPU_TYPE_X86_64: case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: alterObjC1ClassNamesToObjC2 = true; break; } @@ -4123,6 +4419,7 @@ void Options::checkIllegalOptionCombinations() // first 4KB for 32-bit architectures fZeroPageSize = 0x1000; break; + case CPU_TYPE_ARM64: case CPU_TYPE_X86_64: // first 4GB for x86_64 on all OS's fZeroPageSize = 0x100000000ULL; @@ -4240,6 +4537,10 @@ void Options::checkIllegalOptionCombinations() // -dyld_env can only be used with main executables if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) ) throw "-dyld_env can only used used when created main executables"; + + // -rename_sections can only be used in -r mode + if ( (fSectionRenames.size() != 0) && (fOutputKind != Options::kObjectFile) ) + throw "-rename_sections can only used used in -r mode"; } @@ -4392,3 +4693,41 @@ const char* Options::demangleSymbol(const char* sym) const return sym; } + +void Options::dumpDependency(uint8_t opcode, const char* path) const +{ + if ( !this->dumpDependencyInfo() ) + return; + + // one time open() of -dependency_info file + if ( fDependencyFileDescriptor == -1 ) { + fDependencyFileDescriptor = open(this->dependencyInfoPath(), O_WRONLY | O_TRUNC | O_CREAT, 0666); + if ( fDependencyFileDescriptor == -1 ) + throwf("Could not open or create -dependency_info file: %s", this->dependencyInfoPath()); + + // write header + uint8_t version = depLinkerVersion; + if ( write(fDependencyFileDescriptor, &version, 1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + extern const char ldVersionString[]; + if ( write(fDependencyFileDescriptor, ldVersionString, strlen(ldVersionString)+1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + } + + char realPath[PATH_MAX]; + if ( path[0] != '/' ) { + if ( realpath(path, realPath) != NULL ) { + path = realPath; + } + } + + if ( write(fDependencyFileDescriptor, &opcode, 1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + if ( write(fDependencyFileDescriptor, path, strlen(path)+1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + + //fprintf(stderr, "0x%02X %s\n", opcode, path); +} + + + diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 41e5653..0195815 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -45,13 +45,15 @@ class LibraryOptions { public: LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), - fLazyLoad(false), fUpward(false), fForceLoad(false) {} + fLazyLoad(false), fUpward(false), fIndirectDylib(false), + fForceLoad(false) {} // for dynamic libraries bool fWeakImport; bool fReExport; bool fBundleLoader; bool fLazyLoad; bool fUpward; + bool fIndirectDylib; // for static libraries bool fForceLoad; }; @@ -115,12 +117,12 @@ class Options // If the object already has a path the p must be NULL. // If the object does not have a path then p can be any candidate path, and if the file exists the object permanently remembers the path. // Returns true if the file exists, false if not. - bool checkFileExists(const char *p=NULL); + bool checkFileExists(const Options& options, const char *p=NULL); // Returns true if a previous call to checkFileExists() succeeded. // Returns false if the file does not exist of checkFileExists() has never been called. bool missing() const { return modTime==0; } -}; + }; struct ExtraSection { const char* segmentName; @@ -170,6 +172,21 @@ class Options const char* alias; }; + struct SectionRename { + const char* fromSegment; + const char* fromSection; + const char* toSegment; + const char* toSection; + }; + + + enum { depLinkerVersion=0x00, depObjectFile=0x10, depDirectDylib=0x10, depIndirectDylib=0x10, + depUpwardDirectDylib=0x10, depUpwardIndirectDylib=0x10, depArchive=0x10, + depFileList=0x10, depSection=0x10, depBundleLoader=0x10, depMisc=0x10, depNotFound=0x11, + depOutputFile = 0x40 }; + + void dumpDependency(uint8_t, const char* path) const; + typedef const char* const* UndefinesIterator; // const ObjectFile::ReaderOptions& readerOptions(); @@ -322,25 +339,42 @@ class Options bool canReExportSymbols() const { return fCanReExportSymbols; } const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } const char* overridePathlibLTO() const { return fOverridePathlibLTO; } + const char* mcpuLTO() const { return fLtoCpu; } bool objcCategoryMerging() const { return fObjcCategoryMerging; } bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; } + bool keepDwarfUnwind() const { return fKeepDwarfUnwind; } + bool verboseOptimizationHints() const { return fVerboseOptimizationHints; } + bool ignoreOptimizationHints() const { return fIgnoreOptimizationHints; } + bool generateDtraceDOF() const { return fGenerateDtraceDOF; } + bool allowBranchIslands() const { return fAllowBranchIslands; } bool hasWeakBitTweaks() const; bool forceWeak(const char* symbolName) const; bool forceNotWeak(const char* symbolName) const; bool forceWeakNonWildCard(const char* symbolName) const; bool forceNotWeakNonWildcard(const char* symbolName) const; + bool forceCoalesce(const char* symbolName) const; Snapshot& snapshot() const { return fLinkSnapshot; } bool errorBecauseOfWarnings() const; bool needsThreadLoadCommand() const { return fNeedsThreadLoadCommand; } bool needsEntryPointLoadCommand() const { return fEntryPointLoadCommand; } bool needsSourceVersionLoadCommand() const { return fSourceVersionLoadCommand; } bool needsDependentDRInfo() const { return fDependentDRInfo; } + bool canUseAbsoluteSymbols() const { return fAbsoluteSymbols; } + bool allowSimulatorToLinkWithMacOSX() const { return fAllowSimulatorToLinkWithMacOSX; } uint64_t sourceVersion() const { return fSourceVersion; } uint32_t sdkVersion() const { return fSDKVersion; } const char* demangleSymbol(const char* sym) const; bool pipelineEnabled() const { return fPipelineFifo != NULL; } const char* pipelineFifo() const { return fPipelineFifo; } - + bool dumpDependencyInfo() const { return (fDependencyInfoPath != NULL); } + const char* dependencyInfoPath() const { return fDependencyInfoPath; } + bool targetIOSSimulator() const { return fTargetIOSSimulator; } + ld::relocatable::File::LinkerOptionsList& + linkerOptions() const { return fLinkerOptions; } + FileInfo findFramework(const char* frameworkName) const; + FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const; + const std::vector& sectionRenames() const { return fSectionRenames; } + private: typedef std::unordered_map NameToOrder; typedef std::unordered_set NameSet; @@ -372,9 +406,7 @@ class Options void checkIllegalOptionCombinations(); void buildSearchPaths(int argc, const char* argv[]); void parseArch(const char* architecture); - FileInfo findLibrary(const char* rootName, bool dylibsOnly=false); - FileInfo findFramework(const char* frameworkName); - FileInfo findFramework(const char* rootName, const char* suffix); + FileInfo findFramework(const char* rootName, const char* suffix) const; bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const; uint64_t parseVersionNumber64(const char*); @@ -404,6 +436,7 @@ class Options void warnObsolete(const char* arg); uint32_t parseProtection(const char* prot); void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping); + void addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection); @@ -441,6 +474,7 @@ class Options SetWithWildcards fForceWeakSymbols; SetWithWildcards fForceNotWeakSymbols; SetWithWildcards fReExportSymbols; + SetWithWildcards fForceCoalesceSymbols; NameSet fRemovedExports; NameToOrder fExportSymbolsOrder; ExportMode fExportMode; @@ -464,6 +498,7 @@ class Options const char* fDyldInstallPath; const char* fTempLtoObjectPath; const char* fOverridePathlibLTO; + const char* fLtoCpu; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; @@ -562,6 +597,17 @@ class Options bool fDependentDRInfo; bool fDependentDRInfoForcedOn; bool fDependentDRInfoForcedOff; + bool fTargetIOSSimulator; + bool fExportDynamic; + bool fAbsoluteSymbols; + bool fAllowSimulatorToLinkWithMacOSX; + bool fKeepDwarfUnwind; + bool fKeepDwarfUnwindForcedOn; + bool fKeepDwarfUnwindForcedOff; + bool fVerboseOptimizationHints; + bool fIgnoreOptimizationHints; + bool fGenerateDtraceDOF; + bool fAllowBranchIslands; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; @@ -582,10 +628,14 @@ class Options std::vector fFrameworkSearchPaths; std::vector fSDKPaths; std::vector fDyldEnvironExtras; + std::vector< std::vector > fLinkerOptions; + std::vector fSectionRenames; bool fSaveTempFiles; - mutable Snapshot fLinkSnapshot; - bool fSnapshotRequested; - const char * fPipelineFifo; + mutable Snapshot fLinkSnapshot; + bool fSnapshotRequested; + const char* fPipelineFifo; + const char* fDependencyInfoPath; + mutable int fDependencyFileDescriptor; }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 16fdd66..c7a0918 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -69,16 +69,20 @@ namespace ld { namespace tool { +uint32_t sAdrpNA = 0; +uint32_t sAdrpNoped = 0; +uint32_t sAdrpNotNoped = 0; + OutputFile::OutputFile(const Options& opts) : - hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), - _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), hasDataInCode(false), + usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), + _noReExportedDylibs(false), pieDisabled(false), hasDataInCode(false), headerAndLoadCommandsSection(NULL), rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), lazyBindingSection(NULL), exportSection(NULL), splitSegInfoSection(NULL), functionStartsSection(NULL), - dataInCodeSection(NULL), dependentDRsSection(NULL), + dataInCodeSection(NULL), optimizationHintsSection(NULL), dependentDRsSection(NULL), symbolTableSection(NULL), stringPoolSection(NULL), localRelocationsSection(NULL), externalRelocationsSection(NULL), sectionRelocationsSection(NULL), @@ -94,6 +98,7 @@ OutputFile::OutputFile(const Options& opts) _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), + _hasOptimizationHints(opts.outputKind() == Options::kObjectFile), _encryptedTEXTstartOffset(0), _encryptedTEXTendOffset(0), _localSymbolsStartIndex(0), @@ -115,7 +120,8 @@ OutputFile::OutputFile(const Options& opts) _splitSegInfoAtom(NULL), _functionStartsAtom(NULL), _dataInCodeAtom(NULL), - _dependentDRInfoAtom(NULL) + _dependentDRInfoAtom(NULL), + _optimizationHintsAtom(NULL) { } @@ -143,9 +149,9 @@ void OutputFile::write(ld::Internal& state) this->buildDylibOrdinalMapping(state); this->addLoadCommands(state); this->addLinkEdit(state); - this->setSectionSizesAndAlignments(state); + state.setSectionSizesAndAlignments(); this->setLoadCommandsPadding(state); - this->assignFileOffsets(state); + _fileSize = state.assignFileOffsets(); this->assignAtomAddresses(state); this->synthesizeDebugNotes(state); this->buildSymbolTable(state); @@ -192,7 +198,6 @@ void OutputFile::assignAtomAddresses(ld::Internal& state) if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName()); for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - if ( log ) fprintf(stderr, " atom=%p, name=%s\n", atom, atom->name()); switch ( sect-> type() ) { case ld::Section::typeImportProxies: // want finalAddress() of all proxy atoms to be zero @@ -207,6 +212,7 @@ void OutputFile::assignAtomAddresses(ld::Internal& state) break; default: (const_cast(atom))->setSectionStartAddress(sect->address); + if ( log ) fprintf(stderr, " atom=%p, addr=0x%08llX, name=%s\n", atom, atom->finalAddress(), atom->name()); break; } } @@ -255,6 +261,12 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _dataInCodeAtom->encode(); } + if ( _hasOptimizationHints ) { + // build linker-optimization-hint info + assert(_optimizationHintsAtom != NULL); + _optimizationHintsAtom->encode(); + } + if ( _options.needsDependentDRInfo() ) { // build dependent dylib DR info assert(_dependentDRInfoAtom != NULL); @@ -326,83 +338,6 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _fileSize = state.sections.back()->fileOffset + state.sections.back()->size; } -void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) -{ - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { - // absolute symbols need their finalAddress() to their value - for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - (const_cast(atom))->setSectionOffset(atom->objectAddress()); - } - } - else { - uint16_t maxAlignment = 0; - uint64_t offset = 0; - for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - 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 << atomAlignmentPowerOf2; - uint64_t currentModulus = (offset % alignment); - uint64_t requiredModulus = atom->alignment().modulus; - if ( currentModulus != requiredModulus ) { - if ( requiredModulus > currentModulus ) - offset += requiredModulus-currentModulus; - else - offset += requiredModulus+alignment-currentModulus; - } - // LINKEDIT atoms are laid out later - 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) - && (atom->combine() == ld::Atom::combineByName) - && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) - || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { - this->hasWeakExternalSymbols = true; - if ( _options.warnWeakExports() ) - warning("weak external symbol: %s", atom->name()); - } - } - sect->size = offset; - // section alignment is that of a contained atom with the greatest alignment - sect->alignment = maxAlignment; - // unless -sectalign command line option overrides - if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) - sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); - // each atom in __eh_frame has zero alignment to assure they pack together, - // but compilers usually make the CFIs pointer sized, so we want whole section - // to start on pointer sized boundary. - if ( sect->type() == ld::Section::typeCFI ) - sect->alignment = 3; - if ( sect->type() == ld::Section::typeTLVDefs ) - this->hasThreadLocalVariableDefinitions = true; - } - } -} void OutputFile::setLoadCommandsPadding(ld::Internal& state) { @@ -427,13 +362,14 @@ void OutputFile::setLoadCommandsPadding(ld::Internal& state) default: // work backwards from end of segment and lay out sections so that extra room goes to padding atom uint64_t addr = 0; + uint64_t textSegPageSize = _options.segPageSize("__TEXT"); for (std::vector::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) { ld::Internal::FinalSection* sect = *it; if ( strcmp(sect->segmentName(), "__TEXT") != 0 ) continue; if ( sect == headerAndLoadCommandsSection ) { addr -= headerAndLoadCommandsSection->size; - paddingSize = addr % _options.segmentAlignment(); + paddingSize = addr % textSegPageSize; break; } addr -= sect->size; @@ -484,192 +420,6 @@ uint64_t OutputFile::pageAlign(uint64_t addr, uint64_t pageSize) return ((addr+pageSize-1) & (-pageSize)); } - -void OutputFile::assignFileOffsets(ld::Internal& state) -{ - const bool log = false; - const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) - && (_options.outputKind() != Options::kPreload)); - const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); - - uint64_t address = 0; - const char* lastSegName = ""; - uint64_t floatingAddressStart = _options.baseAddress(); - - // first pass, assign addresses to sections in segments with fixed start addresses - if ( log ) fprintf(stderr, "Fixed address segments:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) - continue; - if ( segmentsArePageAligned ) { - if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { - address = _options.customSegmentAddress(sect->segmentName()); - lastSegName = sect->segmentName(); - } - } - // adjust section address based on alignment - uint64_t unalignedAddress = address; - uint64_t alignment = (1 << sect->alignment); - address = ( (unalignedAddress+alignment-1) & (-alignment) ); - - // update section info - sect->address = address; - sect->alignmentPaddingBytes = (address - unalignedAddress); - - // sanity check size - if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) - && (_options.outputKind() != Options::kStaticExecutable) ) - throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", - sect->sectionName(), address, sect->size); - - if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", - sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); - // update running totals - if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) - address += sect->size; - - // if TEXT segment address is fixed, then flow other segments after it - if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { - floatingAddressStart = address; - } - } - - // second pass, assign section address to sections in segments that are contiguous with previous segment - address = floatingAddressStart; - lastSegName = ""; - ld::Internal::FinalSection* overlappingFixedSection = NULL; - ld::Internal::FinalSection* overlappingFlowSection = NULL; - if ( log ) fprintf(stderr, "Regular layout segments:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) - continue; - if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { - sect->alignmentPaddingBytes = 0; - continue; - } - if ( segmentsArePageAligned ) { - if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { - // round up size of last segment if needed - if ( *lastSegName != '\0' ) { - address = pageAlign(address, _options.segPageSize(lastSegName)); - } - // set segment address based on end of last segment - address = pageAlign(address); - lastSegName = sect->segmentName(); - } - } - // adjust section address based on alignment - uint64_t unalignedAddress = address; - uint64_t alignment = (1 << sect->alignment); - address = ( (unalignedAddress+alignment-1) & (-alignment) ); - - // update section info - sect->address = address; - sect->alignmentPaddingBytes = (address - unalignedAddress); - - // sanity check size - if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) - && (_options.outputKind() != Options::kStaticExecutable) ) - throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", - sect->sectionName(), address, sect->size); - - // sanity check it does not overlap a fixed address segment - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* otherSect = *sit; - if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) - continue; - if ( sect->address > otherSect->address ) { - if ( (otherSect->address+otherSect->size) > sect->address ) { - overlappingFixedSection = otherSect; - overlappingFlowSection = sect; - } - } - else { - if ( (sect->address+sect->size) > otherSect->address ) { - overlappingFixedSection = otherSect; - overlappingFlowSection = sect; - } - } - } - - if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", - sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, - sect->segmentName(), sect->sectionName()); - // update running totals - if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) - address += sect->size; - } - if ( overlappingFixedSection != NULL ) { - fprintf(stderr, "Section layout:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( sect->isSectionHidden() ) - continue; - fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", - sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, - sect->segmentName(), sect->sectionName()); - - } - throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", - overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), - overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); - } - - - // third pass, assign section file offsets - uint64_t fileOffset = 0; - lastSegName = ""; - if ( log ) fprintf(stderr, "All segments with file offsets:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( hasZeroForFileOffset(sect) ) { - // fileoff of zerofill sections is moot, but historically it is set to zero - sect->fileOffset = 0; - - // align file offset with address layout - fileOffset += sect->alignmentPaddingBytes; - } - else { - // page align file offset at start of each segment - if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { - fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); - } - lastSegName = sect->segmentName(); - - // align file offset with address layout - fileOffset += sect->alignmentPaddingBytes; - - // update section info - sect->fileOffset = fileOffset; - - // update running total - fileOffset += sect->size; - } - - if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", - sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, - sect->segmentName(), sect->sectionName()); - } - - - // for encrypted iPhoneOS apps - if ( _options.makeEncryptable() ) { - // remember end of __TEXT for later use by load command - 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 + sect->size); - } - } - } - - // remember total file size - _fileSize = fileOffset; -} - - static const char* makeName(const ld::Atom& atom) { static char buffer[4096]; @@ -923,49 +673,91 @@ void OutputFile::rangeCheckARM12(int64_t displacement, ld::Internal& state, cons } } +bool OutputFile::checkArmBranch24Displacement(int64_t displacement) +{ + return ( (displacement < 33554428LL) && (displacement > (-33554432LL)) ); +} void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - // show layout of final image - printSectionLayout(state); + if ( checkArmBranch24Displacement(displacement) ) + return; - const ld::Atom* target; - throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); - } + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); } -void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +bool OutputFile::checkThumbBranch22Displacement(int64_t displacement) { - // thumb2 supports a larger displacement + // thumb2 supports +/- 16MB displacement if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); + return false; } } else { + // thumb1 supports +/- 4MB displacement if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); + return false; } } + return true; +} + +void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( checkThumbBranch22Displacement(displacement) ) + return; + + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { + throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } + else { + throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } } +void OutputFile::rangeCheckARM64Branch26(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t bl_128MegLimit = 0x07FFFFFF; + if ( (displacement > bl_128MegLimit) || (displacement < (-bl_128MegLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b(l) ARM64 branch out of range (%lld max is +/-128MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} +void OutputFile::rangeCheckARM64Page21(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t adrp_4GigLimit = 0x100000000ULL; + if ( (displacement > adrp_4GigLimit) || (displacement < (-adrp_4GigLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("ARM64 ADRP out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} uint16_t OutputFile::get16LE(uint8_t* loc) { return LittleEndian::get16(*(uint16_t*)loc); } @@ -986,6 +778,517 @@ void OutputFile::set32BE(uint8_t* loc, uint32_t value) { BigEndian::set32(*( uint64_t OutputFile::get64BE(uint8_t* loc) { return BigEndian::get64(*(uint64_t*)loc); } void OutputFile::set64BE(uint8_t* loc, uint64_t value) { BigEndian::set64(*(uint64_t*)loc, value); } +static uint32_t makeNOP() { + return 0xD503201F; +} + +enum SignExtension { signedNot, signed32, signed64 }; +struct LoadStoreInfo { + uint32_t reg; + uint32_t baseReg; + uint32_t offset; // after scaling + uint32_t size; // 1,2,4,8, or 16 + bool isStore; + bool isFloat; // if destReg is FP/SIMD + SignExtension signEx; // if load is sign extended +}; + +static uint32_t makeLDR_literal(const LoadStoreInfo& info, uint64_t targetAddress, uint64_t instructionAddress) +{ + int64_t delta = targetAddress - instructionAddress; + assert(delta < 1024*1024); + assert(delta > -1024*1024); + assert((info.reg & 0xFFFFFFE0) == 0); + assert((targetAddress & 0x3) == 0); + assert((instructionAddress & 0x3) == 0); + assert(!info.isStore); + uint32_t imm19 = (delta << 3) & 0x00FFFFE0; + uint32_t instruction = 0; + switch ( info.size ) { + case 4: + if ( info.isFloat ) { + assert(info.signEx == signedNot); + instruction = 0x1C000000; + } + else { + if ( info.signEx == signed64 ) + instruction = 0x98000000; + else + instruction = 0x18000000; + } + break; + case 8: + assert(info.signEx == signedNot); + instruction = info.isFloat ? 0x5C000000 : 0x58000000; + break; + case 16: + assert(info.signEx == signedNot); + instruction = 0x9C000000; + break; + default: + assert(0 && "invalid load size for literal"); + } + return (instruction | imm19 | info.reg); +} + +static uint32_t makeADR(uint32_t destReg, uint64_t targetAddress, uint64_t instructionAddress) +{ + assert((destReg & 0xFFFFFFE0) == 0); + assert((instructionAddress & 0x3) == 0); + uint32_t instruction = 0x10000000; + int64_t delta = targetAddress - instructionAddress; + assert(delta < 1024*1024); + assert(delta > -1024*1024); + uint32_t immhi = (delta & 0x001FFFFC) << 3; + uint32_t immlo = (delta & 0x00000003) << 29; + return (instruction | immhi | immlo | destReg); +} + +static uint32_t makeLoadOrStore(const LoadStoreInfo& info) +{ + uint32_t instruction = 0x39000000; + if ( info.isFloat ) + instruction |= 0x04000000; + instruction |= info.reg; + instruction |= (info.baseReg << 5); + uint32_t sizeBits = 0; + uint32_t opcBits = 0; + uint32_t imm12Bits = 0; + switch ( info.size ) { + case 1: + sizeBits = 0; + imm12Bits = info.offset; + if ( info.isStore ) { + opcBits = 0; + } + else { + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + opcBits = 3; + break; + case signed64: + opcBits = 2; + break; + } + } + break; + case 2: + sizeBits = 1; + assert((info.offset % 2) == 0); + imm12Bits = info.offset/2; + if ( info.isStore ) { + opcBits = 0; + } + else { + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + opcBits = 3; + break; + case signed64: + opcBits = 2; + break; + } + } + break; + case 4: + sizeBits = 2; + assert((info.offset % 4) == 0); + imm12Bits = info.offset/4; + if ( info.isStore ) { + opcBits = 0; + } + else { + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + assert(0 && "cannot use signed32 with 32-bit load/store"); + break; + case signed64: + opcBits = 2; + break; + } + } + break; + case 8: + sizeBits = 3; + assert((info.offset % 8) == 0); + imm12Bits = info.offset/8; + if ( info.isStore ) { + opcBits = 0; + } + else { + opcBits = 1; + assert(info.signEx == signedNot); + } + break; + case 16: + sizeBits = 0; + assert((info.offset % 16) == 0); + imm12Bits = info.offset/16; + assert(info.isFloat); + if ( info.isStore ) { + opcBits = 2; + } + else { + opcBits = 3; + } + break; + default: + assert(0 && "bad load/store size"); + break; + } + assert(imm12Bits < 4096); + return (instruction | (sizeBits << 30) | (opcBits << 22) | (imm12Bits << 10)); +} + +static bool parseLoadOrStore(uint32_t instruction, LoadStoreInfo& info) +{ + if ( (instruction & 0x3B000000) != 0x39000000 ) + return false; + info.isFloat = ( (instruction & 0x04000000) != 0 ); + info.reg = (instruction & 0x1F); + info.baseReg = ((instruction>>5) & 0x1F); + switch (instruction & 0xC0C00000) { + case 0x00000000: + info.size = 1; + info.isStore = true; + info.signEx = signedNot; + break; + case 0x00400000: + info.size = 1; + info.isStore = false; + info.signEx = signedNot; + break; + case 0x00800000: + if ( info.isFloat ) { + info.size = 16; + info.isStore = true; + info.signEx = signedNot; + } + else { + info.size = 1; + info.isStore = false; + info.signEx = signed64; + } + break; + case 0x00C00000: + if ( info.isFloat ) { + info.size = 16; + info.isStore = false; + info.signEx = signedNot; + } + else { + info.size = 1; + info.isStore = false; + info.signEx = signed32; + } + break; + case 0x40000000: + info.size = 2; + info.isStore = true; + info.signEx = signedNot; + break; + case 0x40400000: + info.size = 2; + info.isStore = false; + info.signEx = signedNot; + break; + case 0x40800000: + info.size = 2; + info.isStore = false; + info.signEx = signed64; + break; + case 0x40C00000: + info.size = 2; + info.isStore = false; + info.signEx = signed32; + break; + case 0x80000000: + info.size = 4; + info.isStore = true; + info.signEx = signedNot; + break; + case 0x80400000: + info.size = 4; + info.isStore = false; + info.signEx = signedNot; + break; + case 0x80800000: + info.size = 4; + info.isStore = false; + info.signEx = signed64; + break; + case 0xC0000000: + info.size = 8; + info.isStore = true; + info.signEx = signedNot; + break; + case 0xC0400000: + info.size = 8; + info.isStore = false; + info.signEx = signedNot; + break; + default: + return false; + } + info.offset = ((instruction >> 10) & 0x0FFF) * info.size; + return true; +} + +struct AdrpInfo { + uint32_t destReg; +}; + +static bool parseADRP(uint32_t instruction, AdrpInfo& info) +{ + if ( (instruction & 0x9F000000) != 0x90000000 ) + return false; + info.destReg = (instruction & 0x1F); + return true; +} + +struct AddInfo { + uint32_t destReg; + uint32_t srcReg; + uint32_t addend; +}; + +static bool parseADD(uint32_t instruction, AddInfo& info) +{ + if ( (instruction & 0xFFC00000) != 0x91000000 ) + return false; + info.destReg = (instruction & 0x1F); + info.srcReg = ((instruction>>5) & 0x1F); + info.addend = ((instruction>>10) & 0xFFF); + return true; +} + + + +#if 0 +static uint32_t makeLDR_scaledOffset(const LoadStoreInfo& info) +{ + assert((info.reg & 0xFFFFFFE0) == 0); + assert((info.baseReg & 0xFFFFFFE0) == 0); + assert(!info.isFloat || (info.signEx != signedNot)); + uint32_t sizeBits = 0; + uint32_t opcBits = 1; + uint32_t vBit = info.isFloat; + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + opcBits = 3; + break; + case signed64: + opcBits = 2; + break; + default: + assert(0 && "bad SignExtension runtime value"); + } + switch ( info.size ) { + case 1: + sizeBits = 0; + break; + case 2: + sizeBits = 1; + break; + case 4: + sizeBits = 2; + break; + case 8: + sizeBits = 3; + break; + case 16: + sizeBits = 0; + vBit = 1; + opcBits = 3; + break; + default: + assert(0 && "invalid load size for literal"); + } + assert((info.offset % info.size) == 0); + uint32_t scaledOffset = info.offset/info.size; + assert(scaledOffset < 4096); + return (0x39000000 | (sizeBits<<30) | (vBit<<26) | (opcBits<<22) | (scaledOffset<<10) | (info.baseReg<<5) | info.reg); +} + +static uint32_t makeLDR_literal(uint32_t destReg, uint32_t loadSize, bool isFloat, uint64_t targetAddress, uint64_t instructionAddress) +{ + int64_t delta = targetAddress - instructionAddress; + assert(delta < 1024*1024); + assert(delta > -1024*1024); + assert((destReg & 0xFFFFFFE0) == 0); + assert((targetAddress & 0x3) == 0); + assert((instructionAddress & 0x3) == 0); + uint32_t imm19 = (delta << 3) & 0x00FFFFE0; + uint32_t instruction = 0; + switch ( loadSize ) { + case 4: + instruction = isFloat ? 0x1C000000 : 0x18000000; + break; + case 8: + instruction = isFloat ? 0x5C000000 : 0x58000000; + break; + case 16: + instruction = 0x9C000000; + break; + default: + assert(0 && "invalid load size for literal"); + } + return (instruction | imm19 | destReg); +} + + +static bool ldrInfo(uint32_t instruction, uint8_t* size, uint8_t* destReg, bool* v, uint32_t* scaledOffset) +{ + *v = ( (instruction & 0x04000000) != 0 ); + *destReg = (instruction & 0x1F); + uint32_t imm12 = ((instruction >> 10) & 0x00000FFF); + switch ( (instruction & 0xC0000000) >> 30 ) { + case 0: + // vector and byte LDR have same "size" bits, need to check other bits to differenciate + if ( (instruction & 0x00800000) == 0 ) { + *size = 1; + *scaledOffset = imm12; + } + else { + *size = 16; + *scaledOffset = imm12 * 16; + } + break; + case 1: + *size = 2; + *scaledOffset = imm12 * 2; + break; + case 2: + *size = 4; + *scaledOffset = imm12 * 4; + break; + case 3: + *size = 8; + *scaledOffset = imm12 * 8; + break; + } + return ((instruction & 0x3B400000) == 0x39400000); +} +#endif + +static bool withinOneMeg(uint64_t addr1, uint64_t addr2) { + int64_t delta = (addr2 - addr1); + return ( (delta < 1024*1024) && (delta > -1024*1024) ); +} + +void OutputFile::setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map& usedByHints, + uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info) +{ + info->offsetInAtom = offsetInAtom + delta; + std::map::const_iterator pos = usedByHints.find(info->offsetInAtom); + if ( (pos != usedByHints.end()) && (pos->second != NULL) ) { + info->fixup = pos->second; + info->targetAddress = addressOf(state, info->fixup, &info->target); + if ( info->fixup->clusterSize != ld::Fixup::k1of1 ) { + assert(info->fixup->firstInCluster()); + const ld::Fixup* nextFixup = info->fixup + 1; + if ( nextFixup->kind == ld::Fixup::kindAddAddend ) { + info->targetAddress += nextFixup->u.addend; + } + else { + assert(0 && "expected addend"); + } + } + } + else { + info->fixup = NULL; + info->targetAddress = 0; + info->target = NULL; + } + info->instructionContent = &buffer[info->offsetInAtom]; + info->instructionAddress = atom->finalAddress() + info->offsetInAtom; + info->instruction = get32LE(info->instructionContent); +} + +static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false) +{ + if ( fixup == NULL ) + return false; + const ld::Fixup* f; + switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressARM64Page21: + return !mustBeGOT; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + return true; + case ld::Fixup::kindSetTargetAddress: + f = fixup; + do { + ++f; + } while ( ! f->lastInCluster() ); + switch (f->kind ) { + case ld::Fixup::kindStoreARM64Page21: + return !mustBeGOT; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + return true; + default: + break; + } + break; + default: + break; + } + return false; +} + +static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) +{ + if ( fixup == NULL ) + return false; + const ld::Fixup* f; + switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + return !mustBeGOT; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + return true; + case ld::Fixup::kindSetTargetAddress: + f = fixup; + do { + ++f; + } while ( ! f->lastInCluster() ); + switch (f->kind ) { + case ld::Fixup::kindStoreARM64PageOff12: + return !mustBeGOT; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + return true; + default: + break; + } + break; + default: + break; + } + return false; +} + + +#define LOH_ASSERT(cond) \ + if ( !(cond) ) { \ + warning("ignoring linker optimzation hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \ + break; \ + } + + void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer) { //fprintf(stderr, "applyFixUps() on %s\n", atom->name()); @@ -999,8 +1302,10 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: bool is_blx; bool is_b; bool thumbTarget = false; + std::map usedByHints; for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { uint8_t* fixUpLocation = &buffer[fit->offsetInAtom]; + ld::Fixup::LOH_arm64 lohExtra; switch ( (ld::Fixup::Kind)(fit->kind) ) { case ld::Fixup::kindNone: case ld::Fixup::kindNoneFollowOn: @@ -1023,16 +1328,18 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: accumulator -= delta; break; case ld::Fixup::kindAddAddend: - // ARM main executables main contain .long constants pointing - // into themselves such as jump tables. These .long should not have thumb bit set - // even though the target is a thumb instruction. We can tell it is an interior pointer - // because we are processing an addend. - if ( thumbTarget && (toTarget == atom) && ((int32_t)fit->u.addend > 0) ) { - accumulator &= (-2); - //warning("removing thumb bit from intra-atom pointer in %s %s+0x%0X", - // atom->section().sectionName(), atom->name(), fit->offsetInAtom); + if ( ! fit->contentIgnoresAddend ) { + // ARM main executables main contain .long constants pointing + // into themselves such as jump tables. These .long should not have thumb bit set + // even though the target is a thumb instruction. We can tell it is an interior pointer + // because we are processing an addend. + if ( thumbTarget && (toTarget == atom) && ((int32_t)fit->u.addend > 0) ) { + accumulator &= (-2); + //warning("removing thumb bit from intra-atom pointer in %s %s+0x%0X", + // atom->section().sectionName(), atom->name(), fit->offsetInAtom); + } + accumulator += fit->u.addend; } - accumulator += fit->u.addend; break; case ld::Fixup::kindSubtractAddend: accumulator -= fit->u.addend; @@ -1229,7 +1536,20 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, 0x46C04040); } break; + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32LE(fixUpLocation, 0xD503201F); + } + break; + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to 'MOVZ X0,0' + set32LE(fixUpLocation, 0xD2800000); + } + break; case ld::Fixup::kindLazyTarget: + case ld::Fixup::kindIslandTarget: break; case ld::Fixup::kindSetLazyOffset: assert(fit->binding == ld::Fixup::bindingDirectlyBound); @@ -1242,6 +1562,17 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindDataInCodeStartJTA32: case ld::Fixup::kindDataInCodeEnd: break; + case ld::Fixup::kindLinkerOptimizationHint: + // expand table of address/offsets used by hints + lohExtra.addend = fit->u.addend; + usedByHints[fit->offsetInAtom + (lohExtra.info.delta1 << 2)] = NULL; + if ( lohExtra.info.count > 0 ) + usedByHints[fit->offsetInAtom + (lohExtra.info.delta2 << 2)] = NULL; + if ( lohExtra.info.count > 1 ) + usedByHints[fit->offsetInAtom + (lohExtra.info.delta3 << 2)] = NULL; + if ( lohExtra.info.count > 2 ) + usedByHints[fit->offsetInAtom + (lohExtra.info.delta4 << 2)] = NULL; + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); @@ -1326,6 +1657,22 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindStoreTargetAddressARMBranch24: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); + if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) { + // Branching to island. If ultimate target is in range, branch there directly. + for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); islandfit != end; ++islandfit) { + if ( islandfit->kind == ld::Fixup::kindIslandTarget ) { + const ld::Atom* islandTarget = NULL; + uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget); + delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4); + if ( checkArmBranch24Displacement(delta) ) { + toTarget = islandTarget; + accumulator = islandTargetAddress; + thumbTarget = targetIsThumb(state, islandfit); + } + break; + } + } + } if ( thumbTarget ) accumulator |= 1; if ( fit->contentDetlaToAddendOnly ) @@ -1336,18 +1683,18 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); rangeCheckARMBranch24(delta, state, atom, fit); instruction = get32LE(fixUpLocation); - // Make sure we are calling arm with bl, thumb with blx + // 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; + if ( (is_bl | is_blx) && thumbTarget ) { + uint32_t opcode = 0xFA000000; // force to be blx uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; uint32_t h_bit = (uint32_t)(delta << 23) & 0x01000000; newInstruction = opcode | h_bit | disp; } - else if ( is_blx && !thumbTarget ) { - uint32_t opcode = 0xEB000000; + else if ( (is_bl | is_blx) && !thumbTarget ) { + uint32_t opcode = 0xEB000000; // force to be bl uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; newInstruction = opcode | disp; } @@ -1370,6 +1717,23 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindStoreTargetAddressThumbBranch22: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); + if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) { + // branching to island, so see if ultimate target is in range + // and if so branch to ultimate target instead. + for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); islandfit != end; ++islandfit) { + if ( islandfit->kind == ld::Fixup::kindIslandTarget ) { + const ld::Atom* islandTarget = NULL; + uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget); + delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4); + if ( checkThumbBranch22Displacement(delta) ) { + toTarget = islandTarget; + accumulator = islandTargetAddress; + thumbTarget = targetIsThumb(state, islandfit); + } + break; + } + } + } if ( thumbTarget ) accumulator |= 1; if ( fit->contentDetlaToAddendOnly ) @@ -1504,8 +1868,591 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, newInstruction); } break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64Branch26 case + case ld::Fixup::kindStoreARM64Branch26: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckARM64Branch26(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0xFC000000) | ((uint32_t)(delta >> 2) & 0x03FFFFFF); + set32LE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64Branch26 case + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: + case ld::Fixup::kindStoreARM64Page21: + { + // the ADRP instruction adds the imm << 12 to the page that the pc is on + if ( fit->contentAddendOnly ) + delta = 0; + else + delta = (accumulator & (-4096)) - ((atom->finalAddress() + fit->offsetInAtom) & (-4096)); + rangeCheckARM64Page21(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + uint32_t immhi = (delta >> 9) & (0x00FFFFE0); + uint32_t immlo = (delta << 17) & (0x60000000); + newInstruction = (instruction & 0x9F00001F) | immlo | immhi; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindAddressARM64PageOff12 case + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64PageOff12: + { + uint32_t offset = accumulator & 0x00000FFF; + instruction = get32LE(fixUpLocation); + // LDR/STR instruction have implicit scale factor, need to compensate for that + if ( instruction & 0x08000000 ) { + uint32_t implictShift = ((instruction >> 30) & 0x3); + switch ( implictShift ) { + case 0: + if ( (instruction & 0x04800000) == 0x04800000 ) { + // vector and byte LDR/STR have same "size" bits, need to check other bits to differenciate + implictShift = 4; + if ( (offset & 0xF) != 0 ) { + throwf("128-bit LDR/STR not 16-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + } + break; + case 1: + if ( (offset & 0x1) != 0 ) { + throwf("16-bit LDR/STR not 2-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + break; + case 2: + if ( (offset & 0x3) != 0 ) { + throwf("32-bit LDR/STR not 4-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + break; + case 3: + if ( (offset & 0x7) != 0 ) { + throwf("64-bit LDR/STR not 8-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + break; + } + // compensate for implicit scale + offset >>= implictShift; + } + if ( fit->contentAddendOnly ) + offset = 0; + uint32_t imm12 = offset << 10; + newInstruction = (instruction & 0xFFC003FF) | imm12; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64GOTLoadPage21 case + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + { + // GOT entry was optimized away, change LDR instruction to a ADD + instruction = get32LE(fixUpLocation); + if ( (instruction & 0xFFC00000) != 0xF9400000 ) + throwf("GOT load reloc does not point to a LDR instruction in %s", atom->name()); + uint32_t offset = accumulator & 0x00000FFF; + uint32_t imm12 = offset << 10; + newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64TLVPLeaPageOff12 case + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: + { + // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD + instruction = get32LE(fixUpLocation); + if ( (instruction & 0xFFC00000) != 0xF9400000 ) + throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name()); + uint32_t offset = accumulator & 0x00000FFF; + uint32_t imm12 = offset << 10; + newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreARM64PointerToGOT: + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreARM64PCRelToGOT: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + set32LE(fixUpLocation, delta); + break; +#endif + } + } + + // after all fixups are done on atom, if there are potential optimizations, do those + if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) { + // fill in second part of usedByHints map, so we can see the target of fixups that might be optimized + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindLinkerOptimizationHint: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: + break; + default: + if ( fit->firstInCluster() ) { + std::map::iterator pos = usedByHints.find(fit->offsetInAtom); + if ( pos != usedByHints.end() ) { + assert(pos->second == NULL && "two fixups in same hint location"); + pos->second = fit; + //fprintf(stderr, "setting %s usedByHints[0x%04X], kind = %d\n", atom->name(), fit->offsetInAtom, fit->kind); + } + } + } + } + + // apply hints pass 1 + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint ) + continue; + InstructionInfo infoA; + InstructionInfo infoB; + InstructionInfo infoC; + InstructionInfo infoD; + LoadStoreInfo ldrInfoB, ldrInfoC; + AddInfo addInfoB; + AdrpInfo adrpInfoA; + bool usableSegment; + bool targetFourByteAligned; + bool literalableSize, isADRP, isADD, isLDR, isSTR; + //uint8_t loadSize, destReg; + //uint32_t scaledOffset; + //uint32_t imm12; + ld::Fixup::LOH_arm64 alt; + alt.addend = fit->u.addend; + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA); + if ( alt.info.count > 0 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB); + if ( alt.info.count > 1 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC); + if ( alt.info.count > 2 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD); + + switch ( alt.info.kind ) { + case LOH_ARM64_ADRP_ADRP: + // processed in pass 2 beacuse some ADRP may have been removed + break; + case LOH_ARM64_ADRP_LDR: + LOH_ASSERT(alt.info.count == 1); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + LOH_ASSERT(isLDR); + LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg); + LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF)); + literalableSize = ( (ldrInfoB.size != 1) && (ldrInfoB.size != 2) ); + targetFourByteAligned = ( (infoA.targetAddress & 0x3) == 0 ); + if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr at 0x%08llX not transformed, isLDR=%d, literalableSize=%d, inRange=%d, usableSegment=%d, scaledOffset=%d\n", + infoB.instructionAddress, isLDR, literalableSize, withinOneMeg(infoB.instructionAddress, infoA.targetAddress), usableSegment, ldrInfoB.offset); + } + break; + case LOH_ARM64_ADRP_ADD_LDR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isADD = parseADD(infoB.instruction, addInfoB); + LOH_ASSERT(isADD); + LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); + isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC); + LOH_ASSERT(isLDR); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + targetFourByteAligned = ( ((infoB.targetAddress+ldrInfoC.offset) & 0x3) == 0 ); + literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); + if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) { + // can do T1 transformation to LDR literal + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress+ldrInfoC.offset, infoC.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-add-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress); + } + } + else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) { + // can to T4 transformation and turn ADRP/ADD into ADR + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoB.instructionAddress); + } + else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) { + // can do T2 transformation by merging ADD into LD + // Leave ADRP as-is + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset += addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-ldr at 0x%08llX could not be transformed, loadSize=%d, literalableSize=%d, inRange=%d, usableSegment=%d, targetFourByteAligned=%d, imm12=%d\n", + infoC.instructionAddress, ldrInfoC.size, literalableSize, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, targetFourByteAligned, ldrInfoC.offset); + } + break; + case LOH_ARM64_ADRP_ADD: + LOH_ASSERT(alt.info.count == 1); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isADD = parseADD(infoB.instruction, addInfoB); + LOH_ASSERT(isADD); + LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) { + // can do T4 transformation and use ADR + set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add at 0x%08llX transformed to ADR\n", infoB.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add at 0x%08llX not transformed, isAdd=%d, inRange=%d, usableSegment=%d\n", + infoB.instructionAddress, isADD, withinOneMeg(infoA.targetAddress, infoA.instructionAddress), usableSegment); + } + break; + case LOH_ARM64_ADRP_LDR_GOT_LDR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup, true)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup, true)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC); + LOH_ASSERT(isLDR); + LOH_ASSERT(ldrInfoC.offset == 0); + isADD = parseADD(infoB.instruction, addInfoB); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + if ( isLDR ) { + // target of GOT is external + LOH_ASSERT(ldrInfoB.size == 8); + LOH_ASSERT(!ldrInfoB.isFloat); + LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); + //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + // can do T5 transform + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T5 transformed to LDR literal of GOT plus LDR\n", infoC.instructionAddress); + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX no optimization done\n", infoC.instructionAddress); + } + } + else if ( isADD ) { + // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target + LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); + if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) { + // can do T1 transform + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress); + } + else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { + // can do T4 transform + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress); + } + } + else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) { + // can do T2 transform + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.baseReg = adrpInfoA.destReg; + ldrInfoC.offset = addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress); + } + } + else { + // T3 transform already done by ld::passes:got:doPass() + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T3 transformed to ADRP/ADD/LDR\n", infoC.instructionAddress); + } + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX not ADD or LDR\n", infoC.instructionAddress); + } + break; + case LOH_ARM64_ADRP_ADD_STR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isADD = parseADD(infoB.instruction, addInfoB); + LOH_ASSERT(isADD); + LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); + isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore); + LOH_ASSERT(isSTR); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) { + // can to T4 transformation and turn ADRP/ADD into ADR + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-str at 0x%08llX T4 transformed to ADR/STR\n", infoB.instructionAddress); + } + else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) { + // can do T2 transformation by merging ADD into STR + // Leave ADRP as-is + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset += addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-str at 0x%08llX T2 transformed to ADRP/STR \n", infoC.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-str at 0x%08llX could not be transformed, loadSize=%d, inRange=%d, usableSegment=%d, imm12=%d\n", + infoC.instructionAddress, ldrInfoC.size, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, ldrInfoC.offset); + } + break; + case LOH_ARM64_ADRP_LDR_GOT_STR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup, true)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup, true)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore); + LOH_ASSERT(isSTR); + LOH_ASSERT(ldrInfoC.offset == 0); + isADD = parseADD(infoB.instruction, addInfoB); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + if ( isLDR ) { + // target of GOT is external + LOH_ASSERT(ldrInfoB.size == 8); + LOH_ASSERT(!ldrInfoB.isFloat); + LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + // can do T5 transform + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T5 transformed to LDR literal of GOT plus STR\n", infoC.instructionAddress); + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX no optimization done\n", infoC.instructionAddress); + } + } + else if ( isADD ) { + // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target + LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); + if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { + // can do T4 transform + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress); + } + } + else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) { + // can do T2 transform + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.baseReg = adrpInfoA.destReg; + ldrInfoC.offset = addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress); + } + } + else { + // T3 transform already done by ld::passes:got:doPass() + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T3 transformed to ADRP/ADD/STR\n", infoC.instructionAddress); + } + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX not ADD or LDR\n", infoC.instructionAddress); + } + break; + case LOH_ARM64_ADRP_LDR_GOT: + LOH_ASSERT(alt.info.count == 1); + LOH_ASSERT(isPageKind(infoA.fixup, true)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup, true)); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + isADD = parseADD(infoB.instruction, addInfoB); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + if ( isADRP ) { + if ( isLDR ) { + if ( usableSegment && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + // can do T5 transform (LDR literal load of GOT) + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got at 0x%08llX T5 transformed to NOP/LDR\n", infoC.instructionAddress); + } + } + } + else if ( isADD ) { + if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { + // can do T4 transform (ADR to compute local address) + set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress); + } + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got at 0x%08llX not LDR or ADD\n", infoB.instructionAddress); + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got at 0x%08llX not ADRP\n", infoA.instructionAddress); + } + break; + default: + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "unknown hint kind %d alt.info.kind at 0x%08llX\n", alt.info.kind, infoA.instructionAddress); + break; + } + } + // apply hints pass 2 + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint ) + continue; + InstructionInfo infoA; + InstructionInfo infoB; + ld::Fixup::LOH_arm64 alt; + alt.addend = fit->u.addend; + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA); + if ( alt.info.count > 0 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB); + + switch ( alt.info.kind ) { + case LOH_ARM64_ADRP_ADRP: + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageKind(infoB.fixup)); + if ( (infoA.instruction & 0x9F000000) != 0x90000000 ) { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoA.instructionAddress, infoA.instruction); + sAdrpNA++; + break; + } + if ( (infoB.instruction & 0x9F000000) != 0x90000000 ) { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoB.instructionAddress, infoA.instruction); + sAdrpNA++; + break; + } + if ( (infoA.targetAddress & (-4096)) == (infoB.targetAddress & (-4096)) ) { + set32LE(infoB.instructionContent, 0xD503201F); + sAdrpNoped++; + } + else { + sAdrpNotNoped++; + } + break; + } } } + + + + } void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb) @@ -1608,6 +2555,12 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } } } + + if ( _options.verboseOptimizationHints() ) { + //fprintf(stderr, "ADRP optimized away: %d\n", sAdrpNA); + //fprintf(stderr, "ADRPs changed to NOPs: %d\n", sAdrpNoped); + //fprintf(stderr, "ADRPs unchanged: %d\n", sAdrpNotNoped); + } } @@ -1746,8 +2699,15 @@ void OutputFile::writeOutputFile(ld::Internal& state) fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); } if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", tmpOutput, errno); - ftruncate(fd, _fileSize); + throwf("can't open output file for writing '%s', errno=%d", tmpOutput, errno); + if ( ftruncate(fd, _fileSize) == -1 ) { + int err = errno; + unlink(tmpOutput); + if ( err == ENOSPC ) + throwf("not enough disk space for writing '%s'", _options.outputFilePath()); + else + throwf("can't grow file for writing '%s', errno=%d", _options.outputFilePath(), err); + } wholeBuffer = (uint8_t *)mmap(NULL, _fileSize, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); if ( wholeBuffer == MAP_FAILED ) @@ -1792,6 +2752,10 @@ void OutputFile::writeOutputFile(ld::Internal& state) if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); } + ::close(fd); + // NFS: iOS incremental builds in Xcode 4.6 fail with codesign error + // NFS seems to pad the end of the file sometimes. Calling trunc seems to correct it... + ::truncate(_options.outputFilePath(), _fileSize); } } @@ -1837,7 +2801,7 @@ void OutputFile::buildSymbolTable(ld::Internal& state) // in -r mode, clarify symbolTableNotInFinalLinkedImages if ( _options.outputKind() == Options::kObjectFile ) { - if ( _options.architecture() == CPU_TYPE_X86_64 ) { + if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) { // x86_64 .o files need labels on anonymous literal strings if ( (sect->type() == ld::Section::typeCString) && (atom->combine() == ld::Atom::combineByNameAndContent) ) { (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); @@ -1929,7 +2893,6 @@ void OutputFile::buildSymbolTable(ld::Internal& state) case ld::Atom::scopeLinkageUnit: if ( _options.outputKind() == Options::kObjectFile ) { if ( _options.keepPrivateExterns() ) { - assert( (atom->combine() == ld::Atom::combineNever) || (atom->combine() == ld::Atom::combineByName) ); _exportedAtoms.push_back(atom); } else if ( _options.keepLocalSymbol(atom->name()) ) { @@ -2048,9 +3011,29 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; #endif default: - throw "architecture not supported for -preload"; + throw "-preload not supported"; } } @@ -2101,6 +3084,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2159,6 +3146,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2217,6 +3208,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2236,6 +3231,68 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; #endif default: throw "unknown architecture"; @@ -2257,6 +3314,12 @@ void OutputFile::addLoadCommands(ld::Internal& state) headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; +#endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); @@ -2430,8 +3493,26 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64PageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64PCRelToGOT: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: +#endif return true; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif return (_options.outputKind() != Options::kKextBundle); default: break; @@ -2476,15 +3557,27 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: +#endif return true; case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: return (_options.outputKind() == Options::kObjectFile); @@ -2562,13 +3655,14 @@ uint64_t OutputFile::lookBackAddend(ld::Fixup::iterator fit) } - - - void OutputFile::generateLinkEditInfo(ld::Internal& state) { for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + // record end of last __TEXT section encrypted iPhoneOS apps. + if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) ) { + _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); + } bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer) && (strcmp(sect->sectionName(), "__cls_refs") == 0) && (strcmp(sect->segmentName(), "__OBJC") == 0) ); @@ -2590,6 +3684,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) ld::Fixup* fixupWithTarget = NULL; ld::Fixup* fixupWithMinusTarget = NULL; ld::Fixup* fixupWithStore = NULL; + ld::Fixup* fixupWithAddend = NULL; const ld::Atom* target = NULL; const ld::Atom* minusTarget = NULL; uint64_t targetAddend = 0; @@ -2624,9 +3719,11 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) switch ( fit->kind ) { case ld::Fixup::kindAddAddend: targetAddend = fit->u.addend; + fixupWithAddend = fit; break; case ld::Fixup::kindSubtractAddend: minusTargetAddend = fit->u.addend; + fixupWithAddend = fit; break; case ld::Fixup::kindSubtractTargetAddress: switch ( fit->binding ) { @@ -2662,7 +3759,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) if ( fit->lastInCluster() ) { if ( (fixupWithStore != NULL) && (target != NULL) ) { if ( _options.outputKind() == Options::kObjectFile ) { - this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithAddend, fixupWithStore, target, minusTarget, targetAddend, minusTargetAddend); } else { @@ -2701,10 +3798,19 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable) && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { if ( ! this->pieDisabled ) { - warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " +#if SUPPORT_ARCH_arm64 + if ( _options.architecture() == CPU_TYPE_ARM64 ) { + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); + throwf("Absolute addressing not allowed in arm64 code but used in '%s' referencing '%s'", demangledName, _options.demangleSymbol(target->name())); + } + else +#endif + { + warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " "but used in %s from %s. " "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", atom->name(), atom->file()->path()); + } } this->pieDisabled = true; } @@ -2712,7 +3818,10 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) 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-relocation to %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + if ( (target->file() != NULL) && (atom->file() != NULL) ) + throwf("illegal text-relocation to '%s' in %s from '%s' in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + else + throwf("illegal text reloc in '%s' to '%s'", atom->name(), target->name()); } } @@ -2736,6 +3845,11 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols return; } + // spurious warning when weak function has reference to itself + if ( fixupWithTarget->binding == ld::Fixup::bindingDirectlyBound ) { + // ok to ignore pc-rel references within a weak function to itself + return; + } // Have direct reference to weak-global. This should be an indrect reference const char* demangledName = strdup(_options.demangleSymbol(atom->name())); warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " @@ -2782,7 +3896,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s if ( target == NULL ) return; - bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 ); + bool inReadOnlySeg = ((_options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE) == 0); bool needsRebase = false; bool needsBinding = false; bool needsLazyBinding = false; @@ -2817,8 +3931,11 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // this will be done by other cluster on lazy pointer atom } } - else if ( (target->contentType() == ld::Atom::typeResolver) && (target->scope() != ld::Atom::scopeGlobal) ) { + else if ( target->contentType() == ld::Atom::typeResolver ) { // Hidden resolver functions should not have lazy binding info + // Resolver function run before initializers when overriding the dyld shared cache + // The lazy pointers used by stubs used when non-lazy binding to a resolver are not normal lazy pointers + // and should not be in lazy binding info. needsLazyBinding = false; } else { @@ -2873,6 +3990,9 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s needsRebase = false; needsBinding = true; } + else if ( _options.forceCoalesce(target->name()) ) { + needsWeakBinding = true; + } } break; case ld::Atom::definitionAbsolute: @@ -2880,6 +4000,20 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } } + // if target is an import alias, use base of alias + if ( target->isAlias() && (target->definition() == ld::Atom::definitionProxy) ) { + for (ld::Fixup::iterator fit = target->fixupsBegin(), end=target->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + //fprintf(stderr, "switching import of %s to import of %s\n", target->name(), fit->u.target->name()); + target = fit->u.target; + } + } + } + } + } + // record dyld info for this cluster if ( needsRebase ) { if ( inReadOnlySeg ) { @@ -2887,18 +4021,24 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32; } - if ( (addend != 0) && _options.sharedRegionEligible() ) { - // make sure the addend does not cause the pointer to point outside the target's segment - // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache - uint64_t targetAddress = target->finalAddress(); - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sct = *sit; - uint64_t sctEnd = (sct->address+sct->size); - if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) { - if ( (targetAddress+addend) > sctEnd ) { - warning("data symbol %s from %s has pointer to %s + 0x%08llX. " - "That large of an addend may disable %s from being put in the dyld shared cache.", - atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + if ( _options.sharedRegionEligible() ) { + // when range checking, ignore high byte of arm64 addends + uint64_t checkAddend = addend; + if ( _options.architecture() == CPU_TYPE_ARM64 ) + checkAddend &= 0x0FFFFFFFFFFFFFFFULL; + if ( checkAddend != 0 ) { + // make sure the addend does not cause the pointer to point outside the target's segment + // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache + uint64_t targetAddress = target->finalAddress(); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sct = *sit; + uint64_t sctEnd = (sct->address+sct->size); + if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) { + if ( (targetAddress+checkAddend) > sctEnd ) { + warning("data symbol %s from %s has pointer to %s + 0x%08llX. " + "That large of an addend may disable %s from being put in the dyld shared cache.", + atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + } } } } @@ -3013,7 +4153,8 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) && (_options.outputKind() != Options::kStaticExecutable) - && (_options.outputKind() != Options::kPreload) ) { + && (_options.outputKind() != Options::kPreload) + && (atom != target) ) { needsExternReloc = true; } else if ( _options.outputKind() == Options::kDynamicExecutable ) { @@ -3057,6 +4198,9 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio } break; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif if ( _options.outputKind() == Options::kKextBundle ) { assert(target != NULL); if ( target->definition() == ld::Atom::definitionProxy ) { @@ -3088,8 +4232,8 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget) { - if ( _options.architecture() == CPU_TYPE_X86_64 ) { - // x86_64 uses external relocations for everthing that has a symbol + if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) { + // x86_64 and ARM64 use external relocations for everthing that has a symbol return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); } @@ -3127,11 +4271,29 @@ bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* t return false; } +bool OutputFile::useSectionRelocAddend(ld::Fixup* fixupWithTarget) +{ +#if SUPPORT_ARCH_arm64 + if ( _options.architecture() == CPU_TYPE_ARM64 ) { + switch ( fixupWithTarget->kind ) { + case ld::Fixup::kindStoreARM64Branch26: + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64PageOff12: + return true; + default: + return false; + } + } +#endif + return false; +} + void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, - ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, + ld::Fixup* fixupWithAddend, ld::Fixup* fixupWithStore, const ld::Atom* target, const ld::Atom* minusTarget, uint64_t targetAddend, uint64_t minusTargetAddend) { @@ -3155,11 +4317,13 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio bool targetUsesExternalReloc = this->useExternalSectionReloc(atom, target, fixupWithTarget); bool minusTargetUsesExternalReloc = (minusTarget != NULL) && this->useExternalSectionReloc(atom, minusTarget, fixupWithMinusTarget); - // in x86_64 .o files an external reloc means the content contains just the addend - if ( _options.architecture() == CPU_TYPE_X86_64 ) { + // in x86_64 and arm64 .o files an external reloc means the content contains just the addend + if ( (_options.architecture() == CPU_TYPE_X86_64) ||(_options.architecture() == CPU_TYPE_ARM64) ) { if ( targetUsesExternalReloc ) { fixupWithTarget->contentAddendOnly = true; fixupWithStore->contentAddendOnly = true; + if ( this->useSectionRelocAddend(fixupWithStore) && (fixupWithAddend != NULL) ) + fixupWithAddend->contentIgnoresAddend = true; } if ( minusTargetUsesExternalReloc ) fixupWithMinusTarget->contentAddendOnly = true; @@ -3252,11 +4416,25 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreX86PCRel32GOTLoad: case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreARMLow16: case ld::Fixup::kindStoreThumbLow16: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64PCRelToGOT: +#endif assert(target != NULL); if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind)); diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index d3567ae..6bb793b 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -68,11 +68,9 @@ class OutputFile uint64_t fileSize() const { return _fileSize; } - bool hasWeakExternalSymbols; bool usesWeakExternalSymbols; bool overridesWeakExternalSymbols; bool _noReExportedDylibs; - bool hasThreadLocalVariableDefinitions; bool pieDisabled; bool hasDataInCode; ld::Internal::FinalSection* headerAndLoadCommandsSection; @@ -84,6 +82,7 @@ class OutputFile ld::Internal::FinalSection* splitSegInfoSection; ld::Internal::FinalSection* functionStartsSection; ld::Internal::FinalSection* dataInCodeSection; + ld::Internal::FinalSection* optimizationHintsSection; ld::Internal::FinalSection* dependentDRsSection; ld::Internal::FinalSection* symbolTableSection; ld::Internal::FinalSection* stringPoolSection; @@ -150,11 +149,10 @@ class OutputFile void generateLinkEditInfo(ld::Internal& state); void buildSymbolTable(ld::Internal& state); void writeOutputFile(ld::Internal& state); - void assignFileOffsets(ld::Internal& state); - void setSectionSizesAndAlignments(ld::Internal& state); void addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, ld::Fixup* fixupWithTarget, - ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithAddend, + ld::Fixup* fixupWithStore, const ld::Atom* target, const ld::Atom* minusTarget, uint64_t targetAddend, uint64_t minusTargetAddend); void addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect, @@ -169,6 +167,7 @@ class OutputFile uint64_t targetAddend, uint64_t minusTargetAddend); bool useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget); + bool useSectionRelocAddend(ld::Fixup* fixupWithTarget); uint64_t pageAlign(uint64_t addr); uint64_t pageAlign(uint64_t addr, uint64_t pageSize); void setLoadCommandsPadding(ld::Internal& state); @@ -199,6 +198,10 @@ class OutputFile bool hasZeroForFileOffset(const ld::Section* sect); void printSectionLayout(ld::Internal& state); + + bool checkThumbBranch22Displacement(int64_t displacement); + bool checkArmBranch24Displacement(int64_t displacement); + void rangeCheck8(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); void rangeCheck16(int64_t delta, ld::Internal& state, const ld::Atom* atom, @@ -215,6 +218,12 @@ class OutputFile const ld::Fixup* fixup); void rangeCheckThumbBranch22(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); + void rangeCheckARM64Branch26(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckARM64Page21(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + + uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); void dumpAtomsBySection(ld::Internal& state, bool); @@ -223,6 +232,19 @@ class OutputFile void noteTextReloc(const ld::Atom* atom, const ld::Atom* target); + struct InstructionInfo { + uint32_t offsetInAtom; + const ld::Fixup* fixup; + const ld::Atom* target; + uint64_t targetAddress; + uint8_t* instructionContent; + uint64_t instructionAddress; + uint32_t instruction; + }; + + void setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map& usedHints, + uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info); + static uint16_t get16LE(uint8_t* loc); static void set16LE(uint8_t* loc, uint16_t value); static uint32_t get32LE(uint8_t* loc); @@ -253,6 +275,7 @@ class OutputFile bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; + bool _hasOptimizationHints; uint64_t _fileSize; std::map _lazyPointerAddressToInfoOffset; uint32_t _encryptedTEXTstartOffset; @@ -289,6 +312,7 @@ class OutputFile class LinkEditAtom* _functionStartsAtom; class LinkEditAtom* _dataInCodeAtom; class LinkEditAtom* _dependentDRInfoAtom; + class LinkEditAtom* _optimizationHintsAtom; }; } // namespace tool diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index ad4b22d..573cad8 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -132,6 +132,7 @@ class AliasAtom : public ld::Atom void setFinalAliasOf() const { (const_cast(this))->setAttributesFromAtom(_aliasOf); + (const_cast(this))->setScope(ld::Atom::scopeGlobal); } private: @@ -281,13 +282,21 @@ void Resolver::initializeState() _internal.objcObjectConstraint = ld::File::objcConstraintGC; _internal.cpuSubType = _options.subArchitecture(); + + // In -r mode, look for -linker_option additions + if ( _options.outputKind() == Options::kObjectFile ) { + ld::relocatable::File::LinkerOptionsList lo = _options.linkerOptions(); + for (relocatable::File::LinkerOptionsList::const_iterator it=lo.begin(); it != lo.end(); ++it) { + doLinkerOption(*it, "command line"); + } + } } void Resolver::buildAtomList() { // each input files contributes initial atoms _atoms.reserve(1024); - _inputFiles.forEachInitialAtom(*this); + _inputFiles.forEachInitialAtom(*this, _internal); _completedInitialObjectFiles = true; @@ -295,12 +304,46 @@ void Resolver::buildAtomList() } +void Resolver::doLinkerOption(const std::vector& linkerOption, const char* fileName) +{ + if ( linkerOption.size() == 1 ) { + const char* lo1 = linkerOption.front(); + if ( strncmp(lo1, "-l", 2) == 0 ) { + _internal.linkerOptionLibraries.insert(&lo1[2]); + } + else { + warning("unknown linker option from object file ignored: '%s' in %s", lo1, fileName); + } + } + else if ( linkerOption.size() == 2 ) { + const char* lo2a = linkerOption[0]; + const char* lo2b = linkerOption[1]; + if ( strcmp(lo2a, "-framework") == 0 ) { + _internal.linkerOptionFrameworks.insert(lo2b); + } + else { + warning("unknown linker option from object file ignored: '%s' '%s' from %s", lo2a, lo2b, fileName); + } + } + else { + warning("unknown linker option from object file ignored, starting with: '%s' from %s", linkerOption.front(), fileName); + } +} + void Resolver::doFile(const ld::File& file) { const ld::relocatable::File* objFile = dynamic_cast(&file); const ld::dylib::File* dylibFile = dynamic_cast(&file); if ( objFile != NULL ) { + // if file has linker options, process them + ld::relocatable::File::LinkerOptionsList* lo = objFile->linkerOptions(); + if ( lo != NULL ) { + for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { + this->doLinkerOption(*it, file.path()); + } + } + // update which form of ObjC is being used switch ( file.objCConstraint() ) { case ld::File::objcConstraintNone: @@ -312,16 +355,31 @@ void Resolver::doFile(const ld::File& file) throwf("command line specified -objc_gc_only, but file is retain/release based: %s", file.path()); if ( _options.objcGc() ) throwf("command line specified -objc_gc, but file is retain/release based: %s", file.path()); - _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; + if ( !_options.targetIOSSimulator() && (_internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator) ) + _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; break; case ld::File::objcConstraintRetainReleaseOrGC: if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path()); break; case ld::File::objcConstraintGC: if ( _internal.objcObjectConstraint == ld::File::objcConstraintRetainRelease ) throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); _internal.objcObjectConstraint = ld::File::objcConstraintGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path()); + break; + case ld::File::objcConstraintRetainReleaseForSimulator: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) { + if ( !_options.targetIOSSimulator() && (_options.outputKind() != Options::kObjectFile) ) + warning("ObjC object file (%s) was compiled for iOS Simulator, but linking for MacOSX", file.path()); + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + } + else if ( _internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) { + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + } break; } @@ -360,7 +418,16 @@ void Resolver::doFile(const ld::File& file) break; case CPU_TYPE_X86_64: - _internal.cpuSubType = CPU_SUBTYPE_X86_64_ALL; + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( _options.allowSubArchitectureMismatches() ) { + warning("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + else { + throwf("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + } break; } } @@ -376,16 +443,30 @@ void Resolver::doFile(const ld::File& file) throwf("command line specified -objc_gc_only, but dylib is retain/release based: %s", file.path()); if ( _options.objcGc() ) throwf("command line specified -objc_gc, but dylib is retain/release based: %s", file.path()); + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); _internal.objcDylibConstraint = ld::File::objcConstraintRetainRelease; break; case ld::File::objcConstraintRetainReleaseOrGC: if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseOrGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); break; case ld::File::objcConstraintGC: if ( _internal.objcDylibConstraint == ld::File::objcConstraintRetainRelease ) throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); - _internal.objcDylibConstraint = ld::File::objcConstraintGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintGC; + break; + case ld::File::objcConstraintRetainReleaseForSimulator: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) + _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + else if ( _internal.objcDylibConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) { + warning("ObjC dylib (%s) was compiled for iOS Simulator, but dylibs others were compiled for MacOSX", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + } break; } } @@ -394,7 +475,9 @@ void Resolver::doFile(const ld::File& file) void Resolver::doAtom(const ld::Atom& atom) { - //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s\n", &atom, atom.name(), atom.section().sectionName()); + //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s, scope=%d\n", &atom, atom.name(), atom.section().sectionName(), atom.scope()); + if ( _ltoCodeGenFinished && (atom.contentType() == ld::Atom::typeLTOtemporary) && (atom.scope() != ld::Atom::scopeTranslationUnit) ) + warning("'%s' is implemented in bitcode, but it was loaded too late", atom.name()); // add to list of known atoms _atoms.push_back(&atom); @@ -510,6 +593,8 @@ bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: case ld::Fixup::kindDtraceExtra: @@ -527,6 +612,8 @@ void Resolver::convertReferencesToIndirect(const ld::Atom& atom) const ld::Atom* dummy; ld::Fixup::iterator end = atom.fixupsEnd(); for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != end; ++fit) { + if ( fit->kind == ld::Fixup::kindLinkerOptimizationHint ) + _internal.someObjectHasOptimizationHints = true; switch ( fit->binding ) { case ld::Fixup::bindingByNameUnbound: if ( isDtraceProbe(fit->kind) && (_options.outputKind() != Options::kObjectFile ) ) { @@ -638,14 +725,34 @@ void Resolver::resolveUndefines() } } } - + + // Use linker options to resolve an remaining undefined symbols + if ( !_internal.linkerOptionLibraries.empty() || !_internal.linkerOptionFrameworks.empty() ) { + std::vector undefineNames; + _symbolTable.undefines(undefineNames); + if ( undefineNames.size() != 0 ) { + for (std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* undef = *it; + if ( ! _symbolTable.hasName(undef) ) { + _inputFiles.searchLibraries(undef, true, true, false, *this); + } + } + } + } + // create proxies as needed for undefined symbols if ( (_options.undefinedTreatment() != Options::kUndefinedError) || (_options.outputKind() == Options::kObjectFile) ) { std::vector undefineNames; _symbolTable.undefines(undefineNames); for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - // make proxy - this->doAtom(*new UndefinedProxyAtom(*it)); + const char* undefName = *it; + // "ld -r -exported_symbol _foo" has wrong error message if _foo is undefined + bool makeProxy = true; + if ( (_options.outputKind() == Options::kObjectFile) && _options.hasExportMaskList() && _options.shouldExport(undefName) ) + makeProxy = false; + + if ( makeProxy ) + this->doAtom(*new UndefinedProxyAtom(undefName)); } } @@ -697,6 +804,7 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindNoneGroupSubordinate: case ld::Fixup::kindNoneGroupSubordinateFDE: case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: case ld::Fixup::kindSetTargetAddress: case ld::Fixup::kindSubtractTargetAddress: case ld::Fixup::kindStoreTargetAddressLittleEndian32: @@ -713,6 +821,12 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: +#endif if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() // but a archive loaded .o file may have a forward reference @@ -857,6 +971,7 @@ void Resolver::deadStripOptimize(bool force) if ( _haveLLVMObjs && !force ) { // don't remove combinable atoms, they may come back in lto output _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end()); + _symbolTable.removeDeadAtoms(); } else { _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); @@ -1325,6 +1440,9 @@ void Resolver::linkTimeOptimize() if ( ! _haveLLVMObjs ) return; + // LTO: Symbol multiply defined error should specify exactly where the symbol is found + _symbolTable.checkDuplicateSymbols(); + // run LLVM lto code-gen lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); @@ -1338,19 +1456,22 @@ void Resolver::linkTimeOptimize() optOpt.relocatable = (_options.outputKind() == Options::kObjectFile); optOpt.allowTextRelocs = _options.allowTextRelocs(); optOpt.linkerDeadStripping = _options.deadCodeStrip(); + optOpt.needsUnwindInfoSection = _options.needsUnwindInfoSection(); + optOpt.keepDwarfUnwind = _options.keepDwarfUnwind(); optOpt.arch = _options.architecture(); + optOpt.mcpu = _options.mcpuLTO(); optOpt.llvmOptions = &_options.llvmOptions(); std::vector newAtoms; std::vector additionalUndefines; if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) ) return; // if nothing done - + _ltoCodeGenFinished = true; // add all newly created atoms to _atoms and update symbol table for(std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) this->doAtom(**it); - + // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); @@ -1365,6 +1486,10 @@ void Resolver::linkTimeOptimize() aliasAtom->setFinalAliasOf(); } + // add any auto-link libraries requested by LTO output to dylibs to search + _inputFiles.addLinkerOptionLibraries(_internal); + _inputFiles.createIndirectDylibs(); + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; @@ -1402,6 +1527,7 @@ void Resolver::linkTimeOptimize() } else { // last chance to check for undefines + this->resolveUndefines(); this->checkUndefines(true); // check new code does not override some dylib @@ -1442,6 +1568,14 @@ void Resolver::tweakWeakness() } } +void Resolver::dumpAtoms() +{ + fprintf(stderr, "Resolver all atoms:\n"); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + fprintf(stderr, " %p name=%s, def=%d\n", atom, atom->name(), atom->definition()); + } +} void Resolver::resolve() { diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 32d1d50..4a3cd73 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -62,7 +62,8 @@ class Resolver : public ld::File::AtomHandler : _options(opts), _inputFiles(inputs), _internal(state), _symbolTable(opts, state.indirectBindingTable), _haveLLVMObjs(false), - _completedInitialObjectFiles(false) {} + _completedInitialObjectFiles(false), + _ltoCodeGenFinished(false) {} virtual void doAtom(const ld::Atom&); @@ -89,7 +90,7 @@ class Resolver : public ld::File::AtomHandler void fillInInternalState(); void fillInHelpersInInternalState(); void removeCoalescedAwayAtoms(); - void fillInEntryPoint(); + void fillInEntryPoint(); void linkTimeOptimize(); void convertReferencesToIndirect(const ld::Atom& atom); const ld::Atom* entryPoint(bool searchArchives); @@ -99,6 +100,8 @@ class Resolver : public ld::File::AtomHandler void remainingUndefines(std::vector&); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); void tweakWeakness(); + void doLinkerOption(const std::vector& linkerOption, const char* fileName); + void dumpAtoms(); typedef std::unordered_set StringSet; @@ -126,6 +129,7 @@ class Resolver : public ld::File::AtomHandler SymbolTable _symbolTable; bool _haveLLVMObjs; bool _completedInitialObjectFiles; + bool _ltoCodeGenFinished; }; diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index 2ea690f..b271259 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -312,6 +312,7 @@ class NameCollisionResolution { } void pickAtom() { + //fprintf(stderr, "pickAtom(), a=%p, def=%d, b=%p, def=%d\n", &_atomA, _atomA.definition(), &_atomB, _atomB.definition()); // First, discriminate by definition switch (_atomA.definition()) { case ld::Atom::definitionRegular: @@ -320,6 +321,12 @@ class NameCollisionResolution { pickBetweenRegularAtoms(); break; case ld::Atom::definitionTentative: + if ( _atomB.size() > _atomA.size() ) { + const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : ""; + const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : ""; + warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", + _atomA.name(), _atomB.size(), atomBpath, _atomA.size(), atomApath); + } pickAtomA(); break; case ld::Atom::definitionAbsolute: @@ -334,6 +341,12 @@ class NameCollisionResolution { case ld::Atom::definitionTentative: switch (_atomB.definition()) { case ld::Atom::definitionRegular: + if ( _atomA.size() > _atomB.size() ) { + const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : ""; + const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : ""; + warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", + _atomA.name(), _atomA.size(),atomApath, _atomB.size(), atomBpath); + } pickAtomB(); break; case ld::Atom::definitionTentative: @@ -575,6 +588,19 @@ SymbolTable::IndirectBindingSlot SymbolTable::findSlotForName(const char* name) return slot; } +void SymbolTable::removeDeadAtoms() +{ + for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { + IndirectBindingSlot slot = it->second; + const ld::Atom* atom = _indirectBindingTable[slot]; + if ( atom != NULL ) { + if ( !atom->live() && !atom->dontDeadStrip() ) { + //fprintf(stderr, "removing from symbolTable[%u] %s\n", slot, atom->name()); + _indirectBindingTable[slot] = NULL; + } + } + } +} // find existing or create new slot SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom) diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h index 5575f31..14c7a9e 100644 --- a/ld64/src/ld/SymbolTable.h +++ b/ld64/src/ld/SymbolTable.h @@ -120,6 +120,7 @@ class SymbolTable : public ld::IndirectBindingTable unsigned int updateCount() { return _indirectBindingTable.size(); } void undefines(std::vector& undefines); void tentativeDefs(std::vector& undefines); + void removeDeadAtoms(); bool hasName(const char* name); bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; } byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } diff --git a/ld64/src/ld/dwarf2.h b/ld64/src/ld/dwarf2.h index 411dbbc..ecdf898 100644 --- a/ld64/src/ld/dwarf2.h +++ b/ld64/src/ld/dwarf2.h @@ -62,9 +62,15 @@ enum { DW_FORM_ref4, DW_FORM_ref8, DW_FORM_ref_udata, - DW_FORM_indirect /* 22 */ + DW_FORM_indirect, + /* new in Dwarf 4 */ + DW_FORM_sec_offset, + DW_FORM_exprloc, + DW_FORM_flag_present, + DW_FORM_ref_sig8 }; + enum { DW_LNS_extended_op = 0, DW_LNS_copy, diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index a5db06e..844f614 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -100,9 +100,6 @@ struct PerformanceStatistics { }; - - - class InternalState : public ld::Internal { public: @@ -110,6 +107,8 @@ class InternalState : public ld::Internal virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); + uint64_t assignFileOffsets(); + void setSectionSizesAndAlignments(); void sortSections(); void markAtomsOrdered() { _atomsOrderedInSections = true; } virtual ~InternalState() {} @@ -121,7 +120,7 @@ class InternalState : public ld::Internal 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, bool mergeZeroFill); - static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal); + static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&); private: friend class InternalState; static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen); @@ -139,6 +138,9 @@ class InternalState : public ld::Internal static ld::Section _s_DATA_zerofill; }; + bool hasZeroForFileOffset(const ld::Section* sect); + uint64_t pageAlign(uint64_t addr); + uint64_t pageAlign(uint64_t addr, uint64_t pageSize); struct SectionHash { size_t operator()(const ld::Section*) const; @@ -243,10 +245,19 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& return sect; } -const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal) +const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, const Options& options) { + const std::vector& renames = options.sectionRenames(); + for ( std::vector::const_iterator it=renames.begin(); it != renames.end(); ++it) { + if ( (strcmp(sect.sectionName(), it->fromSection) == 0) && (strcmp(sect.segmentName(), it->fromSegment) == 0) ) { + ld::Section* s = new ld::Section(it->toSegment, it->toSection, sect.type()); + return *s; + } + } + + // in -r mode the only section that ever changes is __tenative -> __common with -d option - if ( (sect.type() == ld::Section::typeTentativeDefs) && makeTentativeDefsReal) + if ( (sect.type() == ld::Section::typeTentativeDefs) && options.makeTentativeDefinitionsReal()) return _s_DATA_common; return sect; } @@ -333,8 +344,11 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint else return INT_MAX-2; default: + // __DATA,__const section should be near __mod_init_func not __data + if ( strcmp(sect.sectionName(), "__const") == 0 ) + return 14; // Reorder sections to reduce page faults in object files - if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) return 20; else if ( strcmp(sect.sectionName(), "__objc_nlclslist") == 0 ) return 21; @@ -519,7 +533,7 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in } break; case Options::kObjectFile: - baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options.makeTentativeDefinitionsReal()); + baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options); pos = _sectionInToFinalMap.find(baseForFinalSection); if ( pos != _sectionInToFinalMap.end() ) { _sectionInToFinalMap[&inputSection] = pos->second; @@ -566,6 +580,308 @@ void InternalState::sortSections() } + +bool InternalState::hasZeroForFileOffset(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + +uint64_t InternalState::pageAlign(uint64_t addr) +{ + const uint64_t alignment = _options.segmentAlignment(); + return ((addr+alignment-1) & (-alignment)); +} + +uint64_t InternalState::pageAlign(uint64_t addr, uint64_t pageSize) +{ + return ((addr+pageSize-1) & (-pageSize)); +} + +void InternalState::setSectionSizesAndAlignments() +{ + for (std::vector::iterator sit = sections.begin(); sit != sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { + // absolute symbols need their finalAddress() to their value + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + (const_cast(atom))->setSectionOffset(atom->objectAddress()); + } + } + else { + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + bool pagePerAtom = false; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + uint32_t atomModulus = atom->alignment().modulus; + if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { + // most objc sections cannot be padded + bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 ); + if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 ) + contiguousObjCSection = false; + if ( strcmp(atom->section().sectionName(), "__objc_data") == 0 ) + contiguousObjCSection = false; + switch ( atom->section().type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeTentativeDefs: + case ld::Section::typeZeroFill: + if ( contiguousObjCSection ) + break; + pagePerAtom = true; + if ( atomAlignmentPowerOf2 < 12 ) { + atomAlignmentPowerOf2 = 12; + atomModulus = 0; + } + break; + default: + break; + } + } + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atomAlignmentPowerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atomModulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + // LINKEDIT atoms are laid out later + 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) + && (atom->combine() == ld::Atom::combineByName) + && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { + this->hasWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("weak external symbol: %s", atom->name()); + } + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + // unless -sectalign command line option overrides + if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) + sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); + // each atom in __eh_frame has zero alignment to assure they pack together, + // but compilers usually make the CFIs pointer sized, so we want whole section + // to start on pointer sized boundary. + if ( sect->type() == ld::Section::typeCFI ) + sect->alignment = 3; + if ( sect->type() == ld::Section::typeTLVDefs ) + this->hasThreadLocalVariableDefinitions = true; + } + } +} + +uint64_t InternalState::assignFileOffsets() +{ + const bool log = false; + const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kPreload)); + const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); + + uint64_t address = 0; + const char* lastSegName = ""; + uint64_t floatingAddressStart = _options.baseAddress(); + + // first pass, assign addresses to sections in segments with fixed start addresses + if ( log ) fprintf(stderr, "Fixed address segments:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + address = _options.customSegmentAddress(sect->segmentName()); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + + // if TEXT segment address is fixed, then flow other segments after it + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + floatingAddressStart = address; + } + } + + // second pass, assign section address to sections in segments that are contiguous with previous segment + address = floatingAddressStart; + lastSegName = ""; + ld::Internal::FinalSection* overlappingFixedSection = NULL; + ld::Internal::FinalSection* overlappingFlowSection = NULL; + if ( log ) fprintf(stderr, "Regular layout segments:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { + sect->alignmentPaddingBytes = 0; + continue; + } + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + // round up size of last segment if needed + if ( *lastSegName != '\0' ) { + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // set segment address based on end of last segment + address = pageAlign(address); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + // sanity check it does not overlap a fixed address segment + for (std::vector::iterator sit = sections.begin(); sit != sections.end(); ++sit) { + ld::Internal::FinalSection* otherSect = *sit; + if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) + continue; + if ( sect->address > otherSect->address ) { + if ( (otherSect->address+otherSect->size) > sect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + else { + if ( (sect->address+sect->size) > otherSect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + } + + if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + if ( overlappingFixedSection != NULL ) { + fprintf(stderr, "Section layout:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->isSectionHidden() ) + continue; + fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", + sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + + } + throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", + overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), + overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); + } + + + // third pass, assign section file offsets + uint64_t fileOffset = 0; + lastSegName = ""; + if ( log ) fprintf(stderr, "All segments with file offsets:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( hasZeroForFileOffset(sect) ) { + // fileoff of zerofill sections is moot, but historically it is set to zero + sect->fileOffset = 0; + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + } + else { + // page align file offset at start of each segment + if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { + fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); + } + lastSegName = sect->segmentName(); + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + + // update section info + sect->fileOffset = fileOffset; + + // update running total + fileOffset += sect->size; + } + + if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", + sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, + sect->segmentName(), sect->sectionName()); + } + +#if 0 + // for encrypted iPhoneOS apps + if ( _options.makeEncryptable() ) { + // remember end of __TEXT for later use by load command + 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 + sect->size); + } + } + } +#endif + + // return total file size + return fileOffset; +} + static char* commatize(uint64_t in, char* out) { char* result = out; @@ -587,10 +903,9 @@ static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) static uint64_t sUnitsPerSecond = 0; if ( sUnitsPerSecond == 0 ) { struct mach_timebase_info timeBaseInfo; - if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; - //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); - } + if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS ) + return; + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; } if ( partTime < sUnitsPerSecond ) { uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 7be00a3..95973aa 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -32,8 +32,9 @@ #include #include -#include +#include +#include "configure.h" namespace ld { @@ -51,7 +52,9 @@ namespace ld { class File { public: - enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, objcConstraintRetainReleaseOrGC, objcConstraintGC }; + enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, + objcConstraintRetainReleaseOrGC, objcConstraintGC, + objcConstraintRetainReleaseForSimulator }; class AtomHandler { public: @@ -84,7 +87,7 @@ class File Ordinal (uint64_t ordinal) : _ordinal(ordinal) {} - enum { ArgListPartition=0, IndirectDylibPartition=1, LTOPartition = 2, InvalidParition=0xffff }; + enum { kArgListPartition=0, kIndirectDylibPartition=1, kLTOPartition = 2, kLinkerOptionPartition = 2, InvalidParition=0xffff }; Ordinal(uint16_t partition, uint16_t majorIndex, uint16_t minorIndex, uint16_t counter) { _ordinal = ((uint64_t)partition<<48) | ((uint64_t)majorIndex<<32) | ((uint64_t)minorIndex<<16) | ((uint64_t)counter<<0); } @@ -115,16 +118,21 @@ class File // The minorIndex is used for files pulled in by a file list and the value is the index of the file in the file list. // The counter is used for .a files and the value is the index of the object in the archive. // Thus, an object pulled in from a .a that was listed in a file list could use all three fields. - static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(ArgListPartition, argIndex, 0, 0); }; + static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(kArgListPartition, argIndex, 0, 0); }; const Ordinal nextFileListOrdinal() const { return nextMinorIndex(); } - const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(ArgListPartition, majorIndex(), minorIndex(), index); } + const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(kArgListPartition, majorIndex(), minorIndex(), index); } // For indirect libraries the partition is IndirectDylibPartition and the counter is used or order the libraries. - static const ld::File::Ordinal indirectDylibBase() { return Ordinal(IndirectDylibPartition, 0, 0, 0); } + static const ld::File::Ordinal indirectDylibBase() { return Ordinal(kIndirectDylibPartition, 0, 0, 0); } const Ordinal nextIndirectDylibOrdinal() const { return nextCounter(); } // For the LTO mach-o the partition is LTOPartition. As there is only one LTO file no other fields are needed. - static const ld::File::Ordinal LTOOrdinal() { return Ordinal(LTOPartition, 0, 0, 0); } + static const ld::File::Ordinal LTOOrdinal() { return Ordinal(kLTOPartition, 0, 0, 0); } + + // For linker options embedded in object files + static const ld::File::Ordinal linkeOptionBase() { return Ordinal(kIndirectDylibPartition, 1, 0, 0); } + const Ordinal nextLinkerOptionOrdinal() { nextCounter(); return *this; }; + }; typedef enum { Reloc, Dylib, Archive, Other } Type; @@ -155,10 +163,11 @@ class File // enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800, - mac10_Future=0x10000000 }; + mac10_9=0x000A0900, mac10_Future=0x10000000 }; enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, - iOS_6_0=0x00060000, iOS_Future=0x10000000}; + iOS_6_0=0x00060000, iOS_7_0=0x00070000, + iOS_Future=0x10000000}; namespace relocatable { // @@ -189,6 +198,7 @@ namespace relocatable { uint32_t value; const char* string; }; + typedef const std::vector< std::vector > LinkerOptionsList; File(const char* pth, time_t modTime, Ordinal ord) : ld::File(pth, modTime, ord, Reloc) { } @@ -199,6 +209,7 @@ namespace relocatable { virtual const std::vector* stabs() const = 0; virtual bool canScatterAtoms() const = 0; virtual bool hasLongBranchStubs() { return false; } + virtual LinkerOptionsList* linkerOptions() const = 0; }; } // namespace relocatable @@ -258,6 +269,7 @@ namespace dylib { virtual bool hasPublicInstallName() const = 0; virtual bool allSymbolsAreWeakImported() const = 0; virtual const void* codeSignatureDR() const = 0; + virtual bool installPathVersionSpecific() const { return false; } protected: const char* _dylibInstallPath; uint32_t _dylibTimeStamp; @@ -391,16 +403,31 @@ struct Fixup kindStoreARMLoad12, kindStoreARMLow16, kindStoreARMHigh16, kindStoreThumbLow16, kindStoreThumbHigh16, +#if SUPPORT_ARCH_arm64 + // ARM64 specific store kinds + kindStoreARM64Branch26, + kindStoreARM64Page21, kindStoreARM64PageOff12, + kindStoreARM64GOTLoadPage21, kindStoreARM64GOTLoadPageOff12, + kindStoreARM64GOTLeaPage21, kindStoreARM64GOTLeaPageOff12, + kindStoreARM64TLVPLoadPage21, kindStoreARM64TLVPLoadPageOff12, + kindStoreARM64TLVPLoadNowLeaPage21, kindStoreARM64TLVPLoadNowLeaPageOff12, + kindStoreARM64PointerToGOT, kindStoreARM64PCRelToGOT, +#endif // dtrace probes kindDtraceExtra, kindStoreX86DtraceCallSiteNop, kindStoreX86DtraceIsEnableSiteClear, kindStoreARMDtraceCallSiteNop, kindStoreARMDtraceIsEnableSiteClear, + kindStoreARM64DtraceCallSiteNop, kindStoreARM64DtraceIsEnableSiteClear, kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, // lazy binding kindLazyTarget, kindSetLazyOffset, + // islands + kindIslandTarget, // data-in-code markers kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16, kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd, + // linker optimzation hints + kindLinkerOptimizationHint, // pointer store combinations kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32 kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64 @@ -421,6 +448,20 @@ struct Fixup kindStoreTargetAddressARMBranch24, // kindSetTargetAddress + kindStoreARMBranch24 kindStoreTargetAddressThumbBranch22, // kindSetTargetAddress + kindStoreThumbBranch22 kindStoreTargetAddressARMLoad12, // kindSetTargetAddress + kindStoreARMLoad12 +#if SUPPORT_ARCH_arm64 + // ARM64 value calculation and store combinations + kindStoreTargetAddressARM64Branch26, // kindSetTargetAddress + kindStoreARM64Branch26 + kindStoreTargetAddressARM64Page21, // kindSetTargetAddress + kindStoreARM64Page21 + kindStoreTargetAddressARM64PageOff12, // kindSetTargetAddress + kindStoreARM64PageOff12 + kindStoreTargetAddressARM64GOTLoadPage21, // kindSetTargetAddress + kindStoreARM64GOTLoadPage21 + kindStoreTargetAddressARM64GOTLoadPageOff12,// kindSetTargetAddress + kindStoreARM64GOTLoadPageOff12 + kindStoreTargetAddressARM64GOTLeaPage21, // kindSetTargetAddress + kindStoreARM64GOTLeaPage21 + kindStoreTargetAddressARM64GOTLeaPageOff12, // kindSetTargetAddress + kindStoreARM64GOTLeaPageOff12 + kindStoreTargetAddressARM64TLVPLoadPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadPage21 + kindStoreTargetAddressARM64TLVPLoadPageOff12,// kindSetTargetAddress + kindStoreARM64TLVPLoadPageOff12 + kindStoreTargetAddressARM64TLVPLoadNowLeaPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPage21 + kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPageOff12 +#endif }; union { @@ -436,54 +477,72 @@ struct Fixup TargetBinding binding : 3; bool contentAddendOnly : 1; bool contentDetlaToAddendOnly : 1; + bool contentIgnoresAddend : 1; typedef Fixup* iterator; Fixup() : offsetInAtom(0), kind(kindNone), clusterSize(k1of1), weakImport(false), binding(bindingNone), - contentAddendOnly(false), contentDetlaToAddendOnly(false) { u.target = NULL; } + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { u.target = NULL; } Fixup(Kind k, Atom* targetAtom) : offsetInAtom(0), kind(k), clusterSize(k1of1), weakImport(false), binding(Fixup::bindingDirectlyBound), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(targetAtom != NULL); u.target = targetAtom; } Fixup(uint32_t off, Cluster c, Kind k) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(Fixup::bindingNone), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { u.addend = 0; } Fixup(uint32_t off, Cluster c, Kind k, bool weakIm, const char* name) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(weakIm), binding(Fixup::bindingByNameUnbound), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(name != NULL); u.name = name; } Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const char* name) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(name != NULL); u.name = name; } Fixup(uint32_t off, Cluster c, Kind k, const Atom* targetAtom) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(Fixup::bindingDirectlyBound), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(targetAtom != NULL); u.target = targetAtom; } Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const Atom* targetAtom) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(targetAtom != NULL); u.target = targetAtom; } Fixup(uint32_t off, Cluster c, Kind k, uint64_t addend) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(Fixup::bindingNone), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { u.addend = addend; } +#if SUPPORT_ARCH_arm64 + Fixup(Kind k, uint32_t lohKind, uint32_t off1, uint32_t off2) : + offsetInAtom(off1), kind(k), clusterSize(k1of1), + weakImport(false), binding(Fixup::bindingNone), contentAddendOnly(false), + contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { + assert(k == kindLinkerOptimizationHint); + LOH_arm64 extra; + extra.addend = 0; + extra.info.kind = lohKind; + extra.info.count = 1; + extra.info.delta1 = 0; + extra.info.delta2 = (off2 - off1) >> 2; + u.addend = extra.addend; + } +#endif + + bool firstInCluster() const { switch (clusterSize) { case k1of1: @@ -512,6 +571,20 @@ struct Fixup return false; } +#if SUPPORT_ARCH_arm64 + union LOH_arm64 { + uint64_t addend; + struct { + unsigned kind : 6, + count : 2, // 00 => 1 addr, 11 => 4 addrs + delta1 : 14, // 16-bit delta, low 2 bits assumed zero + delta2 : 14, + delta3 : 14, + delta4 : 14; + } info; + }; +#endif + }; // @@ -611,7 +684,7 @@ class Atom switch ( _combine ) { case combineByNameAndContent: case combineByNameAndReferences: - assert(_symbolTableInclusion == symbolTableNotIn); + assert(_symbolTableInclusion != symbolTableIn); assert(_scope != scopeGlobal); break; case combineByName: @@ -675,6 +748,7 @@ class Atom } return false; } + virtual void setFile(const File* f) { } virtual UnwindInfo::iterator beginUnwind() const { return NULL; } virtual UnwindInfo::iterator endUnwind() const { return NULL; } @@ -692,7 +766,6 @@ class Atom _combine = a._combine; _dontDeadStrip = a._dontDeadStrip; _thumb = a._thumb; - _alias = a._alias; _autoHide = a._autoHide; _contentType = a._contentType; _symbolTableInclusion = a._symbolTableInclusion; @@ -733,6 +806,23 @@ class IndirectBindingTable }; + +// utility classes for using std::unordered_map with c-strings +struct CStringHash { + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +struct CStringEquals +{ + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +typedef std::unordered_set CStringSet; + class Internal { public: @@ -757,6 +847,8 @@ class Internal bool hasExternalRelocs; }; + virtual uint64_t assignFileOffsets() = 0; + virtual void setSectionSizesAndAlignments() = 0; virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0; virtual ld::Internal::FinalSection* getFinalSection(const ld::Section& inputSection) = 0; virtual ~Internal() {} @@ -767,11 +859,16 @@ class Internal objcDylibConstraint(ld::File::objcConstraintNone), cpuSubType(0), allObjectFilesScatterable(true), - someObjectFileHasDwarf(false), usingHugeSections(false) { } + someObjectFileHasDwarf(false), usingHugeSections(false), + hasThreadLocalVariableDefinitions(false), + hasWeakExternalSymbols(false), + someObjectHasOptimizationHints(false) { } std::vector sections; std::vector dylibs; std::vector stabs; + CStringSet linkerOptionLibraries; + CStringSet linkerOptionFrameworks; std::vector indirectBindingTable; const ld::dylib::File* bundleLoader; const Atom* entryPoint; @@ -784,25 +881,13 @@ class Internal bool allObjectFilesScatterable; bool someObjectFileHasDwarf; bool usingHugeSections; + bool hasThreadLocalVariableDefinitions; + bool hasWeakExternalSymbols; + bool someObjectHasOptimizationHints; }; - -// utility classes for using std::unordered_map with c-strings -struct CStringHash { - size_t operator()(const char* __s) const { - size_t __h = 0; - for ( ; *__s; ++__s) - __h = 5 * __h + *__s; - return __h; - }; -}; -struct CStringEquals -{ - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 708f1fb..9004530 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -109,7 +109,7 @@ class File : public ld::archive::File }; - struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint16_t index;}; + struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint32_t index;}; bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const; typedef std::unordered_map NameToEntryMap; @@ -223,6 +223,7 @@ const class File::Entry* File::Entry::next() const template <> cpu_type_t File::architecture() { return CPU_TYPE_I386; } template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM64; } template @@ -323,13 +324,13 @@ bool File::memberHasObjCCategories(const Entry* member) const template typename File::MemberState& File::makeObjectFileForMember(const Entry* member) const { - uint16_t memberIndex = 0; + uint32_t memberIndex = 0; // in case member was instantiated earlier but not needed yet typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member); if ( pos == _instantiatedEntries.end() ) { // Have to find the index of this member const Entry* start; - uint16_t index; + uint32_t index; if (_instantiatedEntries.size() == 0) { start = (Entry*)&_archiveFileContent[8]; index = 1; @@ -380,7 +381,7 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem // see if member is llvm bitcode file result = lto::parse(member->content(), member->contentSize(), mPath, member->modificationTime(), ordinal, - _objOpts.architecture, _objOpts.subType, _logAllFiles); + _objOpts.architecture, _objOpts.subType, _logAllFiles, _objOpts.verboseOptimizationHints); if ( result != NULL ) { MemberState state = {result, member, false, false, memberIndex}; _instantiatedEntries[member] = state; @@ -598,6 +599,12 @@ ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; #endif } return NULL; diff --git a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp index b04582d..a37c8a0 100644 --- a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -95,7 +95,9 @@ class DwarfInstructions 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); + const pint_t cuStarts[], uint32_t cuCount, + bool keepDwarfWhichHasCU, bool forceDwarfConversion, bool neverConvertToCU, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn); static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, @@ -152,6 +154,18 @@ class DwarfInstructions static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, const Registers_ppc&, const typename CFI_Parser::PrologInfo& prolog, char warningBuffer[1024]); + + // arm64 specific variants + static bool isReturnAddressRegister(int regNum, const Registers_arm64&); + static int lastRestoreReg(const Registers_arm64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_arm64&); + static bool checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_arm64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + }; @@ -159,7 +173,9 @@ class DwarfInstructions template const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, - CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn) + const pint_t cuStarts[], uint32_t cuCount, + bool keepDwarfWhichHasCU, bool forceDwarfConversion, bool neverConvertToCU, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn) { typename CFI_Parser::CIE_Info cieInfo; CFI_Atom_Info* entry = infos; @@ -221,7 +237,6 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS 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; @@ -243,34 +258,60 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS } 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); + // See if already is a compact unwind for this address. + bool alreadyHaveCU = false; + for (uint32_t i=0; i < cuCount; ++i) { + if (cuStarts[i] == entry->u.fdeInfo.function.targetAddress) { + alreadyHaveCU = true; + break; + } + } + //fprintf(stderr, "FDE for func at 0x%08X, alreadyHaveCU=%d\n", (uint32_t)entry->u.fdeInfo.function.targetAddress, alreadyHaveCU); + if ( alreadyHaveCU && !forceDwarfConversion ) { + if ( keepDwarfWhichHasCU ) + ++entry; } else { - warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); - entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + if ( neverConvertToCU || ((cuCount != 0) && !forceDwarfConversion) ) { + // Have some compact unwind, so this is a new .o file, therefore anything without + // compact unwind must be something not expressable in compact unwind. + R dummy; + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + else { + // compute compact unwind encoding by parsing dwarf + 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; } - ++entry; } p = nextCFI; } - if ( entry != end ) - return "wrong entry count for parseCFIs"; + if ( entry != end ) { + //fprintf(stderr, "DwarfInstructions::parseCFIs() infosCount was %d on input, now %ld\n", infosCount, entry - infos); + infosCount = (entry - infos); + } + return NULL; // success } @@ -1037,7 +1078,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo 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); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_64_MODE_DWARF; } switch (i) { @@ -1184,15 +1225,19 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo 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; + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + #if __EXCEPTIONS try { + #endif uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + #if __EXCEPTIONS } catch (...) { strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { @@ -1410,7 +1455,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo 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); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_MODE_DWARF; } switch (i) { @@ -1554,12 +1599,22 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo 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; + #if __EXCEPTIONS + try { + #endif + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + } + catch (...) { + strcpy(warningBuffer, "stack size is large but stack subl instruction not found"); + return UNWIND_X86_MODE_DWARF; + } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { - strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + strcpy(warningBuffer, "stack subl instruction is too different from dwarf stack size"); return UNWIND_X86_MODE_DWARF; } encoding = UNWIND_X86_MODE_STACK_IND; @@ -1715,6 +1770,195 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo +// +// arm64 specific functions +// + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_arm64&) +{ + return UNWIND_ARM64_MODE_DWARF; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_arm64&) +{ + return (regNum == UNW_ARM64_LR); +} + +template +int DwarfInstructions::lastRestoreReg(const Registers_arm64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_ARM64_D31 ); + return UNW_ARM64_D31; +} + + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_arm64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else + ABORT("getCFA(): unsupported location for arm64 cfa"); +} + +template +bool DwarfInstructions::checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]) +{ + if ( (prolog.savedRegisters[reg].location != CFI_Parser::kRegisterUnused) + || (prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterUnused) ) { + if ( prolog.savedRegisters[reg].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg); + return false; + } + if ( prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != prolog.savedRegisters[reg+1].value + 8 ) { + sprintf(warningBuffer, "registers %d and %d not saved contiguously in frame", reg, reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != offset ) { + sprintf(warningBuffer, "registers %d not saved contiguously in frame", reg); + return false; + } + offset -= 16; + return true; + } + return false; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == UNW_ARM64_LR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_ARM64_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_ARM64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_ARM64_MODE_DWARF; + } + + compact_unwind_encoding_t encoding = 0; + int offset = 0; + + // figure out which kind of frame this function uses + bool standardFPframe = ( + (prolog.cfaRegister == UNW_ARM64_FP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_ARM64_FP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_FP].value == -16) + && (prolog.savedRegisters[UNW_ARM64_LR].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_LR].value == -8) ); + + bool standardFrameless = ( prolog.cfaRegister == UNW_ARM64_SP ); + + if ( standardFrameless ) { + // verify enough space for registers saved + int count = 0; + for (int i=0; i < 96; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) + ++count; + } + if ( count * 8 > prolog.cfaRegisterOffset ) { + strcpy(warningBuffer, "saved registers do not fit in stack size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( (prolog.cfaRegisterOffset % 16) != 0 ) { + strcpy(warningBuffer, "stack size is not 16-byte multiple"); + return UNWIND_ARM64_MODE_DWARF; + } + const int32_t maxStack = (UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK >> __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + if ( (prolog.cfaRegisterOffset / 16) > maxStack ) { + strcpy(warningBuffer, "stack size is too large for frameless function"); + return UNWIND_ARM64_MODE_DWARF; + } + encoding = UNWIND_ARM64_MODE_FRAMELESS | ((prolog.cfaRegisterOffset/16) << __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + offset = -16; + } + else if ( standardFPframe ) { + encoding = UNWIND_ARM64_MODE_FRAME; + offset = -24; + } + else { + // no compact encoding for this + strcpy(warningBuffer, "does not use standard frame"); + return UNWIND_ARM64_MODE_DWARF; + } + + // make sure no volatile registers are saved + for (int i=UNW_ARM64_X0; i < UNW_ARM64_X19; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_SP+1; i < UNW_ARM64_D8; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_D16; i < UNW_ARM64_D31+1; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + + // compute encoding + bool X19_X20_saved = checkRegisterPair(UNW_ARM64_X19, prolog, offset, warningBuffer); + bool X21_X22_saved = checkRegisterPair(UNW_ARM64_X21, prolog, offset, warningBuffer); + bool X23_X24_saved = checkRegisterPair(UNW_ARM64_X23, prolog, offset, warningBuffer); + bool X25_X26_saved = checkRegisterPair(UNW_ARM64_X25, prolog, offset, warningBuffer); + bool X27_X28_saved = checkRegisterPair(UNW_ARM64_X27, prolog, offset, warningBuffer); + bool D8_D9_saved = checkRegisterPair(UNW_ARM64_D8, prolog, offset, warningBuffer); + bool D10_D11_saved = checkRegisterPair(UNW_ARM64_D10, prolog, offset, warningBuffer); + bool D12_D13_saved = checkRegisterPair(UNW_ARM64_D12, prolog, offset, warningBuffer); + bool D14_D15_saved = checkRegisterPair(UNW_ARM64_D14, prolog, offset, warningBuffer); + if ( warningBuffer[0] != '\0' ) + return UNWIND_ARM64_MODE_DWARF; + + if ( X19_X20_saved ) + encoding |= UNWIND_ARM64_FRAME_X19_X20_PAIR; + if ( X21_X22_saved ) + encoding |= UNWIND_ARM64_FRAME_X21_X22_PAIR; + if ( X23_X24_saved ) + encoding |= UNWIND_ARM64_FRAME_X23_X24_PAIR; + if ( X25_X26_saved ) + encoding |= UNWIND_ARM64_FRAME_X25_X26_PAIR; + if ( X27_X28_saved ) + encoding |= UNWIND_ARM64_FRAME_X27_X28_PAIR; + if ( D8_D9_saved ) + encoding |= UNWIND_ARM64_FRAME_D8_D9_PAIR; + if ( D10_D11_saved ) + encoding |= UNWIND_ARM64_FRAME_D10_D11_PAIR; + if ( D12_D13_saved ) + encoding |= UNWIND_ARM64_FRAME_D12_D13_PAIR; + if ( D14_D15_saved ) + encoding |= UNWIND_ARM64_FRAME_D14_D15_PAIR; + + return encoding; +} } // namespace libunwind diff --git a/ld64/src/ld/parsers/libunwind/DwarfParser.hpp b/ld64/src/ld/parsers/libunwind/DwarfParser.hpp index 3824d2e..2e4a3bb 100644 --- a/ld64/src/ld/parsers/libunwind/DwarfParser.hpp +++ b/ld64/src/ld/parsers/libunwind/DwarfParser.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * diff --git a/ld64/src/ld/parsers/libunwind/Registers.hpp b/ld64/src/ld/parsers/libunwind/Registers.hpp index 7d39fd7..0247066 100644 --- a/ld64/src/ld/parsers/libunwind/Registers.hpp +++ b/ld64/src/ld/parsers/libunwind/Registers.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2007-2009 Apple Inc. All rights reserved. + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -1035,9 +1035,288 @@ inline const char* Registers_ppc::getRegisterName(int regNum) return "unknown register"; } +} + + + + + +struct arm_thread_state64_t +{ + __uint64_t __x[29]; /* General purpose registers x0-x28 */ + __uint64_t __fp; /* Frame pointer x29 */ + __uint64_t __lr; /* Link register x30 */ + __uint64_t __sp; /* Stack pointer x31 */ + __uint64_t __pc; /* Program counter */ + __uint32_t __cpsr; /* Current program status register */ + __uint32_t padding[3]; /* round up struct size to be 0x110 */ +}; + + +/// +/// Registers_arm64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_arm64 +{ +public: + Registers_arm64(); + Registers_arm64(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; + 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); + const char* getRegisterName(int num); + void jumpto(); + uint64_t getSP() const { return fRegisters.__sp; } + void setSP(uint64_t value) { fRegisters.__sp = value; } + uint64_t getIP() const { return fRegisters.__pc; } + void setIP(uint64_t value) { fRegisters.__pc = value; } + uint64_t getFP() const { return fRegisters.__fp; } + void setFP(uint64_t value) { fRegisters.__fp = value; } +private: + arm_thread_state64_t fRegisters; + double fHalfVectorRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_arm64) < sizeof(unw_context_t) ); + fRegisters = *((arm_thread_state64_t*)registers); +} + +inline Registers_arm64::Registers_arm64() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fRegisters, sizeof(fHalfVectorRegisters)); +} + + +inline bool Registers_arm64::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 > 95 ) + return false; + if ( (regNum > 31) && (regNum < 64) ) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return fRegisters.__pc; + if ( regNum == UNW_REG_SP ) + return fRegisters.__sp; + if ( (regNum >= 0) && (regNum < 32) ) + return fRegisters.__x[regNum]; + ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value) +{ + if ( regNum == UNW_REG_IP ) + fRegisters.__pc = value; + else if ( regNum == UNW_REG_SP ) + fRegisters.__sp = value; + else if ( (regNum >= 0) && (regNum < 32) ) + fRegisters.__x[regNum] = value; + else + ABORT("unsupported arm64 register"); +} + +inline const char* Registers_arm64::getRegisterName(int regNum) +{ + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_ARM64_X0: + return "x0"; + case UNW_ARM64_X1: + return "x1"; + case UNW_ARM64_X2: + return "x2"; + case UNW_ARM64_X3: + return "x3"; + case UNW_ARM64_X4: + return "x4"; + case UNW_ARM64_X5: + return "x5"; + case UNW_ARM64_X6: + return "x6"; + case UNW_ARM64_X7: + return "x7"; + case UNW_ARM64_X8: + return "x8"; + case UNW_ARM64_X9: + return "x9"; + case UNW_ARM64_X10: + return "x10"; + case UNW_ARM64_X11: + return "x11"; + case UNW_ARM64_X12: + return "x12"; + case UNW_ARM64_X13: + return "x13"; + case UNW_ARM64_X14: + return "x14"; + case UNW_ARM64_X15: + return "x15"; + case UNW_ARM64_X16: + return "x16"; + case UNW_ARM64_X17: + return "x17"; + case UNW_ARM64_X18: + return "x18"; + case UNW_ARM64_X19: + return "x19"; + case UNW_ARM64_X20: + return "x20"; + case UNW_ARM64_X21: + return "x21"; + case UNW_ARM64_X22: + return "x22"; + case UNW_ARM64_X23: + return "x23"; + case UNW_ARM64_X24: + return "x24"; + case UNW_ARM64_X25: + return "x25"; + case UNW_ARM64_X26: + return "x26"; + case UNW_ARM64_X27: + return "x27"; + case UNW_ARM64_X28: + return "x28"; + case UNW_ARM64_X29: + return "fp"; + case UNW_ARM64_X30: + return "lr"; + case UNW_ARM64_X31: + return "sp"; + case UNW_ARM64_D0: + return "d0"; + case UNW_ARM64_D1: + return "d1"; + case UNW_ARM64_D2: + return "d2"; + case UNW_ARM64_D3: + return "d3"; + case UNW_ARM64_D4: + return "d4"; + case UNW_ARM64_D5: + return "d5"; + case UNW_ARM64_D6: + return "d6"; + case UNW_ARM64_D7: + return "d7"; + case UNW_ARM64_D8: + return "d8"; + case UNW_ARM64_D9: + return "d9"; + case UNW_ARM64_D10: + return "d10"; + case UNW_ARM64_D11: + return "d11"; + case UNW_ARM64_D12: + return "d12"; + case UNW_ARM64_D13: + return "d13"; + case UNW_ARM64_D14: + return "d14"; + case UNW_ARM64_D15: + return "d15"; + case UNW_ARM64_D16: + return "d16"; + case UNW_ARM64_D17: + return "d17"; + case UNW_ARM64_D18: + return "d18"; + case UNW_ARM64_D19: + return "d19"; + case UNW_ARM64_D20: + return "d20"; + case UNW_ARM64_D21: + return "d21"; + case UNW_ARM64_D22: + return "d22"; + case UNW_ARM64_D23: + return "d23"; + case UNW_ARM64_D24: + return "d24"; + case UNW_ARM64_D25: + return "d25"; + case UNW_ARM64_D26: + return "d26"; + case UNW_ARM64_D27: + return "d27"; + case UNW_ARM64_D28: + return "d28"; + case UNW_ARM64_D29: + return "d29"; + case UNW_ARM64_D30: + return "d30"; + case UNW_ARM64_D31: + return "d31"; + default: + return "unknown register"; + } +} + +bool Registers_arm64::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_ARM64_D0 ) + return false; + if ( regNum > UNW_ARM64_D31 ) + return false; + return true; +} + +double Registers_arm64::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fHalfVectorRegisters[regNum-UNW_ARM64_D0]; +} + +void Registers_arm64::setFloatRegister(int regNum, double value) +{ + assert(validFloatRegister(regNum)); + fHalfVectorRegisters[regNum-UNW_ARM64_D0] = value; } +inline bool Registers_arm64::validVectorRegister(int regNum) const +{ + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int regNum) const +{ + ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int regNum, v128 value) +{ + ABORT("no arm64 vector register support yet"); +} + + } // namespace libunwind diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 023f6e3..3fe21f6 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -47,7 +47,6 @@ #define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" - namespace lto { @@ -106,6 +105,8 @@ class File : public ld::relocatable::File { return _debugInfoModTime; } virtual const std::vector* stabs() const { return NULL; } virtual bool canScatterAtoms() const { return true; } + virtual LinkerOptionsList* linkerOptions() const { return NULL; } + lto_module_t module() { return _module; } class InternalAtom& internalAtom() { return _internalAtom; } @@ -197,7 +198,8 @@ class Parser static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, + bool logAllFiles, bool verboseOptimizationHints); static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } static bool optimize( const std::vector& allAtoms, ld::Internal& state, @@ -211,6 +213,9 @@ class Parser private: static const char* tripletPrefixForArch(cpu_type_t arch); static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options); +#if LTO_API_VERSION >= 7 + static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t, const char*, void*); +#endif typedef std::unordered_set CStringSet; typedef std::unordered_map CStringToAtom; @@ -274,7 +279,7 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) } File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints) { File* f = new File(path, modTime, ordinal, fileContent, fileLength, architecture); _s_files.push_back(f); @@ -290,7 +295,11 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.architecture = options.arch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = options.needsUnwindInfoSection; + objOpts.keepDwarfUnwind = options.keepDwarfUnwind; + objOpts.forceDwarfConversion = false; + objOpts.neverConvertDwarf = false; + objOpts.verboseOptimizationHints = options.verboseOptimizationHints; objOpts.subType = 0; // mach-o parsing is done in-memory, but need path for debug notes @@ -447,6 +456,30 @@ void Atom::setCompiledAtom(const ld::Atom& atom) +// The order that files are merged must match command line order +struct CommandLineOrderFileSorter +{ + bool operator()(File* left, File* right) + { + return ( left->ordinal() < right->ordinal() ); + } +}; + + +#if LTO_API_VERSION >= 7 +void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*) +{ + switch ( severity ) { + case LTO_DS_NOTE: + case LTO_DS_WARNING: + warning("%s", message); + break; + case LTO_DS_ERROR: + throwf("%s", message); + } +} +#endif + bool Parser::optimize( const std::vector& allAtoms, ld::Internal& state, const OptimizeOptions& options, @@ -469,10 +502,20 @@ bool Parser::optimize( const std::vector& allAtoms, // create optimizer and add each Reader lto_code_gen_t generator = ::lto_codegen_create(); +#if LTO_API_VERSION >= 7 + lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); +#endif + + // The order that files are merged must match command line order + std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); + ld::File::Ordinal lastOrdinal; for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); - if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", (*it)->path(), ::lto_get_error_message(), ::lto_get_version()); + File* f = *it; + assert(f->ordinal() > lastOrdinal); + if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( ::lto_codegen_add_module(generator, f->module()) ) + throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); + lastOrdinal = f->ordinal(); } // add any -mllvm command line options @@ -481,6 +524,10 @@ bool Parser::optimize( const std::vector& allAtoms, ::lto_codegen_debug_options(generator, *it); } + // Need a way for LTO to get cpu variants (until that info is in bitcode) + if ( options.mcpu != NULL ) + ::lto_codegen_set_cpu(generator, options.mcpu); + // The atom graph uses directed edges (references). Collect all references where // originating atom is not part of any LTO Reader. This allows optimizer to optimize an // external (i.e. not originated from same .o file) reference if all originating atoms are also @@ -505,10 +552,7 @@ bool Parser::optimize( const std::vector& allAtoms, break; case ld::Fixup::bindingsIndirectlyBound: target = state.indirectBindingTable[fit->u.bindingIndex]; - if ( target == NULL ) - throwf("'%s' in %s contains undefined reference", atom->name(), atom->file()->path()); - assert(target != NULL); - if ( target->contentType() == ld::Atom::typeLTOtemporary ) + if ( (target != NULL) && (target->contentType() == ld::Atom::typeLTOtemporary) ) nonLLVMRefs.insert(target->name()); default: break; @@ -718,6 +762,8 @@ bool Parser::optimize( const std::vector& allAtoms, void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) { + static const ld::Atom* lastProxiedAtom = NULL; + static const ld::File* lastProxiedFile = NULL; // update proxy atoms to point to real atoms and find new atoms const char* name = machoAtom.name(); if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { @@ -725,6 +771,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) if ( pos != _llvmAtoms.end() ) { // turn Atom into a proxy for this mach-o atom pos->second->setCompiledAtom(machoAtom); + lastProxiedAtom = &machoAtom; + lastProxiedFile = pos->second->file(); } else { // an atom of this name was not in the allAtoms list the linker gave us @@ -750,6 +798,11 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) else { // ld only knew about non-static atoms, so this one must be new _newAtoms.push_back(&machoAtom); + // if new static atom in same section as previous non-static atom, assign to same file as previous + if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) { + ld::Atom* ma = const_cast(&machoAtom); + ma->setFile(lastProxiedFile); + } } // adjust fixups to go through proxy atoms @@ -775,7 +828,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) fit->u.target = pos->second; } else { - if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) { + // Don't unbind follow-on reference into by-name reference + if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { // target was coalesed away and replace by mach-o atom from a non llvm .o file fit->binding = ld::Fixup::bindingByNameUnbound; fit->u.name = targetName; @@ -819,11 +873,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints) { Mutex lock; if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); else return NULL; } diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index 3503fa9..d75aab3 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -39,7 +39,8 @@ extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_ty extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints); struct OptimizeOptions { const char* outputFilePath; @@ -53,7 +54,11 @@ struct OptimizeOptions { bool relocatable; bool allowTextRelocs; bool linkerDeadStripping; + bool needsUnwindInfoSection; + bool keepDwarfUnwind; + bool verboseOptimizationHints; cpu_type_t arch; + const char* mcpu; const std::vector* llvmOptions; }; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index aad1a8b..d37098b 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -1,3 +1,4 @@ + /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2005-2011 Apple Inc. All rights reserved. @@ -142,7 +143,7 @@ class File : public ld::dylib::File File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool addVers, + ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool allowSimToMacOSX, bool addVers, bool logAllFiles, const char* installPath, bool indirectDylib); virtual ~File() {} @@ -162,6 +163,7 @@ class File : public ld::dylib::File virtual bool hasWeakDefinition(const char* name) const; virtual bool allSymbolsAreWeakImported() const; virtual const void* codeSignatureDR() const { return _codeSignatureDR; } + virtual bool installPathVersionSpecific() const { return _installPathOverride; } protected: @@ -194,6 +196,7 @@ class File : public ld::dylib::File bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; bool isPublicLocation(const char* pth); + bool wrongOS() { return _wrongOS; } void addSymbol(const char* name, bool weak, bool tlv, pint_t address); void addDyldFastStub(); void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, @@ -201,11 +204,13 @@ class File : public ld::dylib::File void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, const macho_nlist

* symbolTable, const char* strings, const uint8_t* fileContent); + static uint32_t parseVersionNumber32(const char* versionString); static const char* objCInfoSegmentName(); static const char* objCInfoSectionName(); const ld::MacVersionMin _macVersionMin; const ld::IOSVersionMin _iOSVersionMin; + const bool _allowSimToMacOSXLinking; const bool _addVersionLoadCommand; bool _linkingFlat; bool _implicitlyLinkPublicDylibs; @@ -225,7 +230,10 @@ class File : public ld::dylib::File bool _hasPublicInstallName; mutable bool _providedAtom; bool _explictReExportFound; - + bool _wrongOS; + bool _installPathOverride; + bool _indirectDylibsProcessed; + static bool _s_logHashtable; }; @@ -243,10 +251,10 @@ template const char* File::objCInfoSectionName() { return "__ima template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool addVers, + ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool allowSimToMacOSX, bool addVers, bool logAllFiles, const char* targetInstallPath, bool indirectDylib) : ld::dylib::File(strdup(pth), mTime, ord), - _macVersionMin(macMin), _iOSVersionMin(iOSMin), _addVersionLoadCommand(addVers), + _macVersionMin(macMin), _iOSVersionMin(iOSMin), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), _objcContraint(ld::File::objcConstraintNone), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), @@ -254,7 +262,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), - _providedAtom(false), _explictReExportFound(false) + _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), _indirectDylibsProcessed(false) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -331,12 +339,18 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _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); + if ( (_iOSVersionMin != ld::iOSVersionUnset) && !_allowSimToMacOSXLinking ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib ) + throw "building for iOS Simulator, but linking against dylib built for MacOSX"; + } 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); + if ( _macVersionMin != ld::macVersionUnset ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib ) + throw "building for MacOSX, but linking against dylib built for iOS Simulator"; + } break; case LC_CODE_SIGNATURE: codeSignature = (macho_linkedit_data_command

* )cmd; @@ -355,6 +369,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // }; // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 + // #define OBJC_IMAGE_IS_SIMULATED 32 // const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -363,6 +378,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _objcContraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( (flags & 32) == 32 ) + _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; else _objcContraint = ld::File::objcConstraintRetainRelease; } @@ -499,6 +516,28 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +template +uint32_t File::parseVersionNumber32(const char* versionString) +{ + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + char* end; + x = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed 32-bit x.y.z version number: %s", versionString); + + return (x << 16) | ( y << 8 ) | z; +} template void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, @@ -605,6 +644,14 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address } else if ( strncmp(symAction, "install_name$", 13) == 0 ) { _dylibInstallPath = symName; + _installPathOverride = true; + // CoreGraphics redirects to ApplicationServices, but with wrong compat version + if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) + _dylibCompatibilityVersion = parseVersionNumber32("1.0"); + return; + } + else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { + _dylibCompatibilityVersion = parseVersionNumber32(symName); return; } else { @@ -777,6 +824,9 @@ bool File::isPublicLocation(const char* pth) template void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) { + // only do this once + if ( _indirectDylibsProcessed ) + return; const static bool log = false; if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); if ( _linkingFlat ) { @@ -794,7 +844,7 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child it->dylib = (File*)handler->findDylib(it->path, this->path()); - if ( it->dylib->hasPublicInstallName() ) { + if ( it->dylib->hasPublicInstallName() && !it->dylib->wrongOS() ) { // promote this child to be automatically added as a direct dependent if this already is if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); @@ -834,6 +884,8 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b chain.prev = NULL; chain.file = this; this->assertNoReExportCycles(&chain); + + _indirectDylibsProcessed = true; } template @@ -866,6 +918,7 @@ class Parser typedef typename A::P P; static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); + static const char* fileKind(const uint8_t* fileContent); static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { @@ -875,6 +928,7 @@ class Parser opts.implicitlyLinkIndirectPublicDylibs(), opts.macosxVersionMin(), opts.iOSVersionMin(), + opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), opts.logAllFiles(), opts.installPath(), @@ -968,6 +1022,146 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + + +bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult) +{ + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_X86_64; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_I386; + *subResult = CPU_SUBTYPE_X86_ALL; + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_ARM; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_ARM64; + *subResult = CPU_SUBTYPE_ARM64_ALL; + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_POWERPC; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_POWERPC64; + *subResult = CPU_SUBTYPE_POWERPC_ALL; + return true; + } + return false; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_I386 ) + return NULL; + return "i386"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return NULL; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return NULL; + return "x86_64"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM ) + return NULL; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) { + return t->archName; + } + } + return "arm???"; +} + +#if SUPPORT_ARCH_arm64 +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return NULL; + return "arm64"; +} +#endif + +// +// used by linker is error messages to describe mismatched files +// +const char* archName(const uint8_t* fileContent) +{ + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } +#if SUPPORT_ARCH_arm64 + if ( Parser::validFile(fileContent, false) ) { + return Parser::fileKind(fileContent); + } +#endif + return NULL; +} + + // // main function used by linker to instantiate ld::Files // @@ -993,6 +1187,12 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; #endif } return NULL; diff --git a/ld64/src/ld/parsers/macho_dylib_file.h b/ld64/src/ld/parsers/macho_dylib_file.h index ad03af1..4d093f1 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.h +++ b/ld64/src/ld/parsers/macho_dylib_file.h @@ -31,6 +31,12 @@ namespace mach_o { namespace dylib { + +extern bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); + +extern const char* archName(const uint8_t* fileContent); + + extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib); diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index b49d63f..ad5720e 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -92,9 +92,10 @@ class File : public ld::relocatable::File virtual ObjcConstraint objCConstraint() const { return _objConstraint; } virtual uint32_t cpuSubType() const { return _cpuSubType; } virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } - virtual const std::vector* stabs() const { return &_stabs; } + virtual const std::vector* stabs() const { return &_stabs; } virtual bool canScatterAtoms() const { return _canScatterAtoms; } virtual const char* translationUnitSource() const; + virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } const uint8_t* fileContent() { return _fileContent; } private: @@ -123,6 +124,7 @@ class File : public ld::relocatable::File ld::File::ObjcConstraint _objConstraint; uint32_t _cpuSubType; bool _canScatterAtoms; + std::vector > _linkerOptions; }; @@ -154,6 +156,7 @@ class Section : public ld::Section 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; } + virtual bool ignoreLabel(const char* label) const { return false; } static const char* makeSectionName(const macho_section* s); protected: @@ -167,6 +170,7 @@ class Section : public ld::Section Atom* findContentAtomByAddress(pint_t addr, class Atom* start, class Atom* end); uint32_t x86_64PcRelOffset(uint8_t r_type); + void addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]); static const char* makeSegmentName(const macho_section* s); static bool readable(const macho_section* s); static bool writable(const macho_section* s); @@ -235,7 +239,7 @@ class CFISection : public Section typedef typename A::P::uint_t pint_t; typedef libunwind::CFI_Atom_Info CFI_Atom_Info; - void cfiParse(class Parser& parser, uint8_t* buffer, CFI_Atom_Info cfiArray[], uint32_t cfiCount); + void cfiParse(class Parser& parser, uint8_t* buffer, CFI_Atom_Info cfiArray[], uint32_t& cfiCount, const pint_t cuStarts[], uint32_t cuCount); bool needsRelocating(); static bool bigEndian(); @@ -274,6 +278,7 @@ class CUSection : public Section uint32_t count(); void parse(class Parser& parser, uint32_t cnt, Info array[]); + static bool encodingMeansUseDwarf(compact_unwind_encoding_t enc); private: @@ -379,16 +384,17 @@ class ImplicitSizeSection : public Section virtual bool addFollowOnFixups() const { return false; } virtual const char* unlabeledAtomName(Parser& parser, pint_t addr) = 0; - virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableNotIn; } + virtual ld::Atom::SymbolTableInclusion symbolTableInclusion(); virtual pint_t elementSizeAtAddress(pint_t addr) = 0; virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr) { return ld::Atom::scopeLinkageUnit; } virtual bool useElementAt(Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, pint_t addr) = 0; virtual ld::Atom::Definition definition() { return ld::Atom::definitionRegular; } virtual ld::Atom::Combine combine(Parser& parser, pint_t addr) = 0; - virtual bool ignoreLabel(const char* label) { return (label[0] == 'L'); } + virtual bool ignoreLabel(const char* label) const { return (label[0] == 'L'); } }; + template class FixedSizeSection : public ImplicitSizeSection { @@ -481,7 +487,7 @@ class NonLazyPointerSection : public FixedSizeSection virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t); - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -505,7 +511,7 @@ class CFStringSection : public FixedSizeSection virtual const char* unlabeledAtomName(Parser&, pint_t) { return "CFString"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return 4*sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -533,7 +539,7 @@ class ObjC1ClassSection : public FixedSizeSection virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableIn; } virtual pint_t elementSizeAtAddress(pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineNever; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } 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, @@ -555,7 +561,7 @@ class ObjC2ClassRefsSection : public FixedSizeSection virtual const char* unlabeledAtomName(Parser&, pint_t) { return "objc-class-ref"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -578,7 +584,7 @@ class ObjC2CategoryListSection : public FixedSizeSection virtual const char* unlabeledAtomName(Parser&, pint_t) { return "objc-cat-list"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineNever; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } private: const char* targetClassName(const class Atom* atom, const ld::IndirectBindingTable& ind) const; }; @@ -597,7 +603,7 @@ class PointerToCStringSection : public FixedSizeSection virtual const char* unlabeledAtomName(Parser&, pint_t) { return "pointer-to-literal-cstring"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -635,7 +641,7 @@ class CStringSection : public ImplicitSizeSection 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 ignoreLabel(const char* label) const; virtual bool useElementAt(Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } @@ -671,7 +677,7 @@ class Atom : public ld::Atom { public: // overrides of ld::Atom - virtual ld::File* file() const { return §().file(); } + virtual const ld::File* file() const; virtual const char* translationUnitSource() const { return sect().file().translationUnitSource(); } virtual const char* name() const { return _name; } @@ -689,6 +695,7 @@ class Atom : public ld::Atom virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex+_unwindInfoCount]; } virtual ld::Atom::LineInfo::iterator beginLineInfo() const{ return &machofile()._lineInfos[_lineInfoStartIndex]; } virtual ld::Atom::LineInfo::iterator endLineInfo() const { return &machofile()._lineInfos[_lineInfoStartIndex+_lineInfoCount]; } + virtual void setFile(const ld::File* f); private: @@ -714,7 +721,7 @@ class Atom : public ld::Atom throwf("too may fixups in %s", name()); ++_fixupsCount; } const uint8_t* contentPointer() const; uint32_t fixupCount() const { return _fixupsCount; } - void verifyAlignment() const; + void verifyAlignment(const macho_section&) const; typedef typename A::P P; typedef typename A::P::E E; @@ -748,7 +755,7 @@ class Atom : public ld::Atom if ( _scope == ld::Atom::scopeGlobal && (sym.n_desc() & (N_WEAK_DEF|N_WEAK_REF)) == (N_WEAK_DEF|N_WEAK_REF) ) this->setAutoHide(); - this->verifyAlignment(); + this->verifyAlignment(*sct.machoSection()); } private: @@ -768,10 +775,27 @@ class Atom : public ld::Atom _fixupsCount : kFixupCountBits, _lineInfoCount : kLineInfoCountBits, _unwindInfoCount : kUnwindInfoCountBits; - + + static std::map _s_fileOverride; }; +template +std::map Atom::_s_fileOverride; +template +void Atom::setFile(const ld::File* f) { + _s_fileOverride[this] = f; +} + +template +const ld::File* Atom::file() const +{ + std::map::iterator pos = _s_fileOverride.find(this); + if ( pos != _s_fileOverride.end() ) + return pos->second; + + return §().file(); +} template void Atom::setFixupsRange(uint32_t startIndex, uint32_t count) @@ -838,7 +862,7 @@ void Atom::copyRawContent(uint8_t buffer[]) const } template <> -void Atom::verifyAlignment() const +void Atom::verifyAlignment(const macho_section

&) const { if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) { if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) ) @@ -846,8 +870,19 @@ void Atom::verifyAlignment() const } } +#if SUPPORT_ARCH_arm64 +template <> +void Atom::verifyAlignment(const macho_section

& sect) const +{ + if ( (this->section().type() == ld::Section::typeCode) && (sect.size() != 0) ) { + if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) ) + warning("arm64 function not 4-byte aligned: %s from %s", this->name(), this->file()->path()); + } +} +#endif + template -void Atom::verifyAlignment() const +void Atom::verifyAlignment(const macho_section

&) const { } @@ -865,7 +900,9 @@ class Parser const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { Parser p(fileContent, fileLength, path, modTime, - ordinal, opts.convertUnwindInfo); + ordinal, opts.warnUnwindConversionProblems, + opts.keepDwarfUnwind, opts.forceDwarfConversion, + opts.neverConvertDwarf, opts.verboseOptimizationHints); return p.parse(opts); } @@ -934,7 +971,7 @@ class Parser _allFixups.push_back(FixupInAtom(src, c, k)); } - + const char* path() { return _path; } uint32_t symbolCount() { return _symbolCount; } uint32_t indirectSymbol(uint32_t indirectIndex); const macho_nlist

& symbolFromIndex(uint32_t index); @@ -971,11 +1008,19 @@ class Parser unsigned int stubsSectionNum() { return _stubsSectionNum; } void addDtraceExtraInfos(const SourceLocation& src, const char* provider); const char* scanSymbolTableForAddress(uint64_t addr); - bool convertUnwindInfo() { return _convertUnwindInfo; } + bool warnUnwindConversionProblems() { return _warnUnwindConversionProblems; } bool hasDataInCodeLabels() { return _hasDataInCodeLabels; } - + bool keepDwarfUnwind() { return _keepDwarfUnwind; } + bool forceDwarfConversion() { return _forceDwarfConversion; } + bool verboseOptimizationHints() { return _verboseOptimizationHints; } + bool neverConvertDwarf() { return _neverConvertDwarf; } + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } + const uint8_t* optimizationHintsStart() { return _lohStart; } + const uint8_t* optimizationHintsEnd() { return _lohEnd; } + bool hasOptimizationHints() { return _lohStart != _lohEnd; } + void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target); void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase); @@ -989,7 +1034,7 @@ class Parser : sortedSymbolIndexes(ssa), sortedSymbolCount(ssc), cfiStartsArray(cfisa), cfiStartsCount(cfisc), fileHasOverlappingSymbols(ols), newSection(false), cfiIndex(0), symIndex(0) {} - bool next(Parser& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, + bool next(Parser& parser, const Section& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr, pint_t* addr, pint_t* size, const macho_nlist

** sym); pint_t peek(Parser& parser, pint_t startAddr, pint_t endAddr); void beginSection() { newSection = true; symIndex = 0; } @@ -1050,8 +1095,9 @@ class Parser Parser(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, - ld::File::Ordinal ordinal, bool convertUnwindInfo); + const char* path, time_t modTime, ld::File::Ordinal ordinal, + bool warnUnwindConversionProblems, bool keepDwarfUnwind, + bool forceDwarfConversion, bool neverConvertDwarf, bool verboseOptimizationHints); ld::relocatable::File* parse(const ParserOptions& opts); uint8_t loadCommandSizeMask(); bool parseLoadCommands(); @@ -1095,6 +1141,8 @@ class Parser bool _hasUUID; macho_data_in_code_entry

* _dataInCodeStart; macho_data_in_code_entry

* _dataInCodeEnd; + const uint8_t* _lohStart; + const uint8_t* _lohEnd; // filled in by parse() CFISection* _EHFrameSection; @@ -1106,8 +1154,12 @@ class Parser bool _hasLongBranchStubs; bool _AppleObjc; // FSF has objc that uses different data layout bool _overlappingSymbols; - bool _convertUnwindInfo; + bool _warnUnwindConversionProblems; bool _hasDataInCodeLabels; + bool _keepDwarfUnwind; + bool _forceDwarfConversion; + bool _neverConvertDwarf; + bool _verboseOptimizationHints; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1118,7 +1170,8 @@ class Parser template Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - ld::File::Ordinal ordinal, bool convertDUI) + ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion, + bool neverConvertDwarf, bool verboseOptimizationHints) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), @@ -1126,10 +1179,14 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _undefinedStartIndex(0), _undefinedEndIndex(0), _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), _dataInCodeStart(NULL), _dataInCodeEnd(NULL), + _lohStart(NULL), _lohEnd(NULL), _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL), _tentativeDefinitionCount(0), _absoluteSymbolCount(0), _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), - _overlappingSymbols(false), _convertUnwindInfo(convertDUI), _hasDataInCodeLabels(false), + _overlappingSymbols(false), _warnUnwindConversionProblems(convertDUI), _hasDataInCodeLabels(false), + _keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion), + _neverConvertDwarf(neverConvertDwarf), + _verboseOptimizationHints(verboseOptimizationHints), _stubsSectionNum(0), _stubsMachOSection(NULL) { } @@ -1183,6 +1240,19 @@ bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, c } +template <> +bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + template <> const char* Parser::fileKind(const uint8_t* fileContent) @@ -1222,6 +1292,18 @@ const char* Parser::fileKind(const uint8_t* fileContent) return "arm???"; } +#if SUPPORT_ARCH_arm64 +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return NULL; + return "arm64"; +} +#endif template bool Parser::hasObjC2Categories(const uint8_t* fileContent) @@ -1322,7 +1404,7 @@ typename A::P::uint_t Parser::LabelAndCFIBreakIterator::peek(Parser& parse // was becuase of a label, the symbol). Returns false when no more chunks. // template -bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, +bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr, pint_t* addr, pint_t* size, const macho_nlist

** symbol) { // may not be a label on start of section, but need atom demarcation there @@ -1331,10 +1413,12 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN // advance symIndex until we get to the first label at or past the start of this section while ( symIndex < sortedSymbolCount ) { const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); - pint_t nextSymbolAddr = sym.n_value(); - //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; + if ( ! sect.ignoreLabel(parser.nameFromSymbol(sym)) ) { + pint_t nextSymbolAddr = sym.n_value(); + //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; } if ( symIndex < sortedSymbolCount ) { @@ -1381,7 +1465,19 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN *symbol = NULL; return true; } - // no symbols left in whole file, so entire section is one chunk + // no symbols in section, check CFI + if ( cfiIndex < cfiStartsCount ) { + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextCfiAddr < endAddr ) { + // use cfi + ++cfiIndex; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + } + // no cfi, so whole section is one chunk *addr = startAddr; *size = endAddr - startAddr; *symbol = NULL; @@ -1459,6 +1555,16 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN return false; } +#define STACK_ALLOC_IF_SMALL(_type, _name, _actual_count, _maxCount) \ + _type* _name = NULL; \ + uint32_t _name##_count = 1; \ + if ( _actual_count > _maxCount ) \ + _name = (_type*)malloc(sizeof(_type) * _actual_count); \ + else \ + _name##_count = _actual_count; \ + _type _name##_buffer[_name##_count]; \ + if ( _name == NULL ) \ + _name = _name##_buffer; template @@ -1475,7 +1581,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) if ( ! parseLoadCommands() ) return _file; - // make array of + // make array of uint32_t sortedSectionIndexes[_machOSectionsCount]; this->makeSortedSectionsArray(sortedSectionIndexes); @@ -1491,40 +1597,46 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) 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; + // stack allocate (if not too large) cuInfoBuffer + STACK_ALLOC_IF_SMALL(typename CUSection::Info, cuInfoArray, countOfCUs, 1024); if ( countOfCUs != 0 ) _compactUnwindSection->parse(*this, countOfCUs, cuInfoArray); + + // create lists of address that already have compact unwind and thus don't need the dwarf parsed + unsigned cuLsdaCount = 0; + pint_t cuStarts[countOfCUs]; + for (uint32_t i=0; i < countOfCUs; ++i) { + if ( CUSection::encodingMeansUseDwarf(cuInfoArray[i].compactUnwindInfo) ) + cuStarts[i] = -1; + else + cuStarts[i] = cuInfoArray[i].functionStartAddress; + if ( cuInfoArray[i].lsdaAddress != 0 ) + ++cuLsdaCount; + } + // if it exists, do special early parsing of __eh_frame section - // stack allocate array of CFI_Atom_Info + // stack allocate (if not too large) array of CFI_Atom_Info uint32_t countOfCFIs = 0; if ( _EHFrameSection != NULL ) countOfCFIs = _EHFrameSection->cfiCount(); - typename CFISection::CFI_Atom_Info cfiArray[countOfCFIs]; + STACK_ALLOC_IF_SMALL(typename CFISection::CFI_Atom_Info, cfiArray, countOfCFIs, 1024); + // stack allocate (if not too large) a copy of __eh_frame to apply relocations to - uint8_t* ehBuffer = NULL; - uint32_t stackAllocSize = 0; - if ( (countOfCFIs != 0) && _EHFrameSection->needsRelocating() ) { - uint32_t sectSize = _EHFrameSection->machoSection()->size(); - if ( sectSize > 50*1024 ) - ehBuffer = (uint8_t*)malloc(sectSize); - else - stackAllocSize = sectSize; - } - uint32_t ehStackBuffer[1+stackAllocSize/4]; // make 4-byte aligned stack bufffer - if ( ehBuffer == NULL ) - ehBuffer = (uint8_t*)&ehStackBuffer; + uint32_t sectSize = 4; + if ( (countOfCFIs != 0) && _EHFrameSection->needsRelocating() ) + sectSize = _EHFrameSection->machoSection()->size()+4; + STACK_ALLOC_IF_SMALL(uint8_t, ehBuffer, sectSize, 50*1024); uint32_t cfiStartsCount = 0; if ( countOfCFIs != 0 ) { - _EHFrameSection->cfiParse(*this, ehBuffer, cfiArray, countOfCFIs); + _EHFrameSection->cfiParse(*this, ehBuffer, cfiArray, countOfCFIs, cuStarts, countOfCUs); // count functions and lsdas for(uint32_t i=0; i < countOfCFIs; ++i) { if ( cfiArray[i].isCIE ) continue; - //fprintf(stderr, "cfiArray[i].func = 0x%08llX, cfiArray[i].lsda = 0x%08llX, encoding=0x%08X\n", - // (uint64_t)cfiArray[i].u.fdeInfo.function.targetAddress, - // (uint64_t)cfiArray[i].u.fdeInfo.lsda.targetAddress, + //fprintf(stderr, "cfiArray[i].func = 0x%08llX, cfiArray[i].lsda = 0x%08llX, encoding=0x%08X\n", + // (uint64_t)cfiArray[i].u.fdeInfo.function.targetAddress, + // (uint64_t)cfiArray[i].u.fdeInfo.lsda.targetAddress, // cfiArray[i].u.fdeInfo.compactUnwindInfo); if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) ++cfiStartsCount; @@ -1535,23 +1647,41 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) CFI_CU_InfoArrays cfis(cfiArray, countOfCFIs, cuInfoArray, countOfCUs); // create sorted array of function starts and lsda starts - pint_t cfiStartsArray[cfiStartsCount]; + pint_t cfiStartsArray[cfiStartsCount+cuLsdaCount]; uint32_t countOfFDEs = 0; + uint32_t cfiStartsArrayCount = 0; if ( countOfCFIs != 0 ) { - int index = 0; for(uint32_t i=0; i < countOfCFIs; ++i) { if ( cfiArray[i].isCIE ) continue; if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) - cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.function.targetAddress; + cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.function.targetAddress; if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) - cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; + cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; ++countOfFDEs; } - ::qsort(cfiStartsArray, cfiStartsCount, sizeof(pint_t), pointerSorter); + } + if ( cuLsdaCount != 0 ) { + // merge in an lsda info from compact unwind + for (uint32_t i=0; i < countOfCUs; ++i) { + if ( cuInfoArray[i].lsdaAddress == 0 ) + continue; + // append to cfiStartsArray if not already in that list + bool found = false; + for(uint32_t j=0; j < cfiStartsArrayCount; ++j) { + if ( cfiStartsArray[j] == cuInfoArray[i].lsdaAddress ) + found = true; + } + if ( ! found ) { + cfiStartsArray[cfiStartsArrayCount++] = cuInfoArray[i].lsdaAddress; + } + } + } + if ( cfiStartsArrayCount != 0 ) { + ::qsort(cfiStartsArray, cfiStartsArrayCount, sizeof(pint_t), pointerSorter); #ifndef NDEBUG // scan for FDEs claming the same function - for(int i=1; i < index; ++i) { + for(uint32_t i=1; i < cfiStartsArrayCount; ++i) { assert( cfiStartsArray[i] != cfiStartsArray[i-1] ); } #endif @@ -1562,7 +1692,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // figure out how many atoms will be allocated and allocate LabelAndCFIBreakIterator breakIterator(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, - cfiStartsCount, _overlappingSymbols); + cfiStartsArrayCount, _overlappingSymbols); uint32_t computedAtomCount = 0; for (uint32_t i=0; i < sectionsCount; ++i ) { breakIterator.beginSection(); @@ -1577,7 +1707,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // have each section append atoms to _atomsArray LabelAndCFIBreakIterator breakIterator2(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, - cfiStartsCount, _overlappingSymbols); + cfiStartsArrayCount, _overlappingSymbols); for (uint32_t i=0; i < sectionsCount; ++i ) { uint8_t* atoms = _file->_atomsArray + _file->_atomsArrayCount*sizeof(Atom); breakIterator2.beginSection(); @@ -1628,6 +1758,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _file->_unwindInfos.push_back(info); Atom* func = findAtomByAddress(cfiArray[i].u.fdeInfo.function.targetAddress); func->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + //fprintf(stderr, "cu from dwarf =0x%08X, atom=%s\n", info.unwindInfo, func->name()); } } // apply compact infos in __LD,__compact_unwind section to each function @@ -1639,22 +1770,26 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) assert(info->function != NULL); ld::Atom::UnwindInfo ui; ui.startOffset = info->functionStartAddress - info->function->objectAddress(); - ui.unwindInfo = info->compactUnwindInfo; + 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); + // don't override with converted cu with "use dwarf" cu, if forcing dwarf conversion + if ( !_forceDwarfConversion || !CUSection::encodingMeansUseDwarf(info->compactUnwindInfo) ) { + //fprintf(stderr, "cu=0x%08X, atom=%s\n", ui.unwindInfo, info->function->name()); + // 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(); } - lastFunc->extendUnwindInfoRange(); + else + info->function->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + lastFunc = info->function; + lastEnd = ui.startOffset + info->rangeLength; } - 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 @@ -1668,6 +1803,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template bool Parser::parseLoadCommands() @@ -1738,6 +1874,30 @@ bool Parser::parseLoadCommands() if ( _dataInCodeEnd > (macho_data_in_code_entry

*)endOfFile ) throw "LC_DATA_IN_CODE table extends beyond end of file"; } + break; + case LC_LINKER_OPTION: + { + const macho_linker_option_command

* loc = (macho_linker_option_command

*)cmd; + const char* buffer = loc->buffer(); + _file->_linkerOptions.resize(_file->_linkerOptions.size() + 1); + std::vector& vec = _file->_linkerOptions.back(); + for (uint32_t j=0; j < loc->count(); ++j) { + vec.push_back(buffer); + buffer += strlen(buffer) + 1; + } + if ( buffer > ((char*)cmd + loc->cmdsize()) ) + throw "malformed LC_LINKER_OPTION"; + } + break; + case LC_LINKER_OPTIMIZATION_HINTS: + { + const macho_linkedit_data_command

* loh = (macho_linkedit_data_command

*)cmd; + _lohStart = _fileContent + loh->dataoff(); + _lohEnd = _fileContent + loh->dataoff() + loh->datasize(); + if ( _lohEnd > endOfFile ) + throw "LC_LINKER_OPTIMIZATION_HINTS table extends beyond end of file"; + } + break; default: if ( cmd->cmd() == macho_segment_command

::CMD ) { if ( segment != NULL ) @@ -1984,7 +2144,6 @@ void Parser::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionA } } - template void Parser::makeSections() { @@ -2034,6 +2193,7 @@ void Parser::makeSections() // }; // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 + // #define OBJC_IMAGE_IS_SIMULATED 32 // const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset()); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -2042,6 +2202,8 @@ void Parser::makeSections() _file->_objConstraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( (flags & 32) == 32 ) + _file->_objConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else _file->_objConstraint = ld::File::objcConstraintRetainRelease; if ( sect->size() > 8 ) { @@ -2463,8 +2625,14 @@ const char* Parser::scanSymbolTableForAddress(uint64_t addr) continue; // return with exact match - if ( sym.n_value() == addr ) - return nameFromSymbol(sym); + if ( sym.n_value() == addr ) { + const char* name = nameFromSymbol(sym); + if ( strncmp(name, "ltmp", 4) != 0 ) + return name; + // treat 'ltmp*' labels as close match + closestSymAddr = sym.n_value(); + closestSymName = name; + } // record closest seen so far if ( (sym.n_value() < addr) && ((sym.n_value() > closestSymAddr) || (closestSymName == NULL)) ) @@ -2519,6 +2687,29 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co case ld::Fixup::kindStoreThumbBranch22: firstKind = ld::Fixup::kindStoreTargetAddressThumbBranch22; break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Branch26: + firstKind = ld::Fixup::kindStoreTargetAddressARM64Branch26; + break; + case ld::Fixup::kindStoreARM64Page21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64Page21; + break; + case ld::Fixup::kindStoreARM64PageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64PageOff12; + break; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21; + break; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12; + break; + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21; + break; + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12; + break; +#endif default: combined = false; cl = ld::Fixup::k1of2; @@ -2537,6 +2728,10 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co // backing string in CFStrings should always be direct addFixup(src, cl, firstKind, target.atom); } + else if ( (src.atom == target.atom) && (target.atom->combine() == ld::Atom::combineByName) ) { + // reference to self should always be direct + addFixup(src, cl, firstKind, target.atom); + } else { // change direct fixup to by-name fixup addFixup(src, cl, firstKind, false, target.atom->name()); @@ -2922,6 +3117,22 @@ bool Parser::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t sz = 4; break; + case DW_FORM_sec_offset: + sz = sizeof(typename A::P::uint_t); + break; + + case DW_FORM_exprloc: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_flag_present: + sz = 0; + break; + + case DW_FORM_ref_sig8: + sz = 8; + break; + default: return false; } @@ -3402,7 +3613,7 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, return false; vers = A::P::E::get16(*(uint16_t*)di); - if (vers < 2 || vers > 3) + if (vers < 2 || vers > 4) /* DWARF version wrong for this code. Chances are we could continue anyway, but we don't know for sure. */ return false; @@ -3695,7 +3906,10 @@ template ld::Atom::Alignment Section::alignmentForAddress(pint_t addr) { const uint32_t sectionAlignment = this->_machOSection->align(); - return ld::Atom::Alignment(sectionAlignment, (addr % (1 << sectionAlignment))); + uint32_t modulus = (addr % (1 << sectionAlignment)); + if ( modulus > 0xFFFF ) + warning("alignment for symbol at address 0x%08llX in %s exceeds 2^16", (uint64_t)addr, this->file().path()); + return ld::Atom::Alignment(sectionAlignment, modulus); } template @@ -3723,7 +3937,7 @@ template void CFISection::warnFunc(void* ref, uint64_t funcAddr, const char* msg) { Parser* parser = (Parser*)ref; - if ( ! parser->convertUnwindInfo() ) + if ( ! parser->warnUnwindConversionProblems() ) return; if ( funcAddr != CFI_INVALID_ADDRESS ) { // atoms are not constructed yet, so scan symbol table for labels @@ -3741,6 +3955,12 @@ bool CFISection::needsRelocating() return true; } +template <> +bool CFISection::needsRelocating() +{ + return true; +} + template bool CFISection::needsRelocating() { @@ -3748,9 +3968,9 @@ bool CFISection::needsRelocating() } template <> -void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // copy __eh_frame data to buffer memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); @@ -3796,7 +4016,6 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, } } - // create ObjectAddressSpace object for use by libunwind OAS oas(*this, buffer); @@ -3804,6 +4023,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::parseCFIs( oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), cfiArray, count, (void*)&parser, warnFunc); if ( msg != NULL ) throwf("malformed __eh_frame section: %s", msg); @@ -3812,7 +4032,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // create ObjectAddressSpace object for use by libunwind OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); @@ -3821,6 +4041,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::parseCFIs( oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), cfiArray, count, (void*)&parser, warnFunc); if ( msg != NULL ) throwf("malformed __eh_frame section: %s", msg); @@ -3832,12 +4053,76 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // arm does not use zero cost exceptions assert(count == 0); } +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) +{ + // copy __eh_frame data to buffer + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + + // and apply relocations + const macho_relocation_info

* relocs = (macho_relocation_info

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

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + uint64_t* p64 = (uint64_t*)&buffer[reloc->r_address()]; + uint32_t* p32 = (uint32_t*)&buffer[reloc->r_address()]; + uint32_t addend32 = E::get32(*p32); + uint64_t addend64 = E::get64(*p64); + uint64_t value = 0; + switch ( reloc->r_type() ) { + case ARM64_RELOC_SUBTRACTOR: + value = 0 - parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + ++reloc; + if ( reloc->r_extern() ) + value += parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case ARM64_RELOC_UNSIGNED: + value = parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case ARM64_RELOC_POINTER_TO_GOT: + // this is used for the reference to the personality function in CIEs + // store the symbol number of the personality function for later use as a Fixup + value = reloc->r_symbolnum(); + addend32 = 0; + addend64 = 0; + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address()); + break; + } + switch ( reloc->r_length() ) { + case 3: + E::set64(*p64, value + addend64); + break; + case 2: + E::set32(*p32, value + addend32); + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation size at r_address=0x%08X\n", reloc->r_address()); + break; + } + } + + + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, buffer); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} template @@ -3875,6 +4160,7 @@ uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } +template <> bool CFISection::bigEndian() { return false; } template <> @@ -3924,11 +4210,36 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const C } + +#if SUPPORT_ARCH_arm64 +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( personalityEncoding == 0x9B ) { + // compiler always produces ARM64_RELOC_GOT r_pcrel=1 to personality function + // CFISection::cfiParse() set targetAddress to be symbolIndex + addressInCIE + uint32_t symbolIndex = cieInfo->u.cieInfo.personality.targetAddress + - cieInfo->address - cieInfo->u.cieInfo.personality.offsetInCFI; + const macho_nlist

& sym = parser.symbolFromIndex(symbolIndex); + const char* personalityName = parser.nameFromSymbol(sym); + + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + parser.addFixup(src, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, false, personalityName); + parser.addFixup(src, ld::Fixup::k2of2, ld::Fixup::kindStoreARM64PCRelToGOT); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} +#endif + template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { - // FIX ME - assert(0); + assert(0 && "addCiePersonalityFixups() not implemented for arch"); } template @@ -4162,27 +4473,103 @@ typename A::P::uint_t CFISection::OAS::getEncodedP(pint_t& addr, pint_t end, 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); + if ( reloc->r_extern() ) { + 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); + } + else { + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t personalityAddr = *content; + Section* personalitySection = parser.sectionForAddress(personalityAddr); + assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(personalityAddr); + return name; + } } 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); + if ( reloc->r_extern() ) { + 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); + } + else { + // support __LD, __compact_unwind personality entries which are pointer to personality non-lazy pointer + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t nlPointerAddr = *content; + Section* nlSection = parser.sectionForAddress(nlPointerAddr); + if ( nlSection->type() == ld::Section::typeCode ) { + // personality function is defined in this .o file, so this is a direct reference to it + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(nlPointerAddr); + return name; + } + else { + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(nlPointerAddr, nlSection->machoSection()); + const macho_nlist

& nlSymbol = parser.symbolFromIndex(symIndex); + return parser.nameFromSymbol(nlSymbol); + } + } } +#if SUPPORT_ARCH_arm64 +template <> +const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) +{ + if ( reloc->r_extern() ) { + assert((reloc->r_type() == ARM64_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); + } + else { + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t personalityAddr = *content; + Section* personalitySection = parser.sectionForAddress(personalityAddr); + assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(personalityAddr); + return name; + } +} +#endif + template const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info

* reloc) { return NULL; } +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); +} + +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); +} + +#if SUPPORT_ARCH_arm_any +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return false; +} +#endif + +#if SUPPORT_ARCH_arm64 +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); +} +#endif template int CUSection::infoSorter(const void* l, const void* r) @@ -4219,8 +4606,7 @@ void CUSection::parse(class Parser& parser, uint32_t cnt, Info array[]) } } - // scan relocs, local relocs are useless - ignore them - // extern relocs are needed for personality references (possibly for function/lsda refs??) + // scan relocs, 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) { @@ -4241,11 +4627,18 @@ void CUSection::parse(class Parser& parser, uint32_t cnt, Info array[]) 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(); + array[entryIndex].functionStartAddress += parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); } else { warning("unexpected extern relocation in __compact_unwind section"); } } + else { + 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); + } + } } // sort array by function start address so unwind infos will be contiguous for a given function @@ -4268,9 +4661,6 @@ void CUSection::makeFixups(class Parser& parser, const struct Parser::C 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 @@ -4356,7 +4746,7 @@ uint32_t SymboledSection::computeAtomCount(class Parser& parser, pint_t addr; pint_t size; const macho_nlist

* sym; - while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &sym) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &addr, &size, &sym) ) { ++count; } //fprintf(stderr, "computeAtomCount(%s,%s) => %d\n", this->segmentName(), this->sectionName(), count); @@ -4365,7 +4755,7 @@ uint32_t SymboledSection::computeAtomCount(class Parser& parser, template uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, - struct Parser::LabelAndCFIBreakIterator& it, + struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&) { this->_beginAtoms = (Atom*)p; @@ -4379,7 +4769,7 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, pint_t addr; pint_t size; const macho_nlist

* label; - while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &label) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &addr, &size, &label) ) { Atom* allocatedSpace = (Atom*)p; // is break because of label or CFI? if ( label != NULL ) { @@ -4396,7 +4786,7 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, 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, + new (allocatedSpace) Atom(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ctype, inclusion, this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); } @@ -4409,6 +4799,19 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, } +template <> +ld::Atom::SymbolTableInclusion ImplicitSizeSection::symbolTableInclusion() +{ + return ld::Atom::symbolTableInWithRandomAutoStripLabel; +} + +template +ld::Atom::SymbolTableInclusion ImplicitSizeSection::symbolTableInclusion() +{ + return ld::Atom::symbolTableNotIn; +} + + template uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, @@ -4426,15 +4829,18 @@ uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, // if there are multiple labels in this section for the same address, then clone them into multi atoms pint_t prevSymbolAddr = (pint_t)(-1); uint8_t prevSymbolSectNum = 0; + bool prevIgnore = false; for(uint32_t i=0; i < it.sortedSymbolCount; ++i) { const macho_nlist

& sym = parser.symbolFromIndex(it.sortedSymbolIndexes[i]); const pint_t symbolAddr = sym.n_value(); - const pint_t symbolSectNum = sym.n_sect(); - if ( (symbolAddr == prevSymbolAddr) && (prevSymbolSectNum == symbolSectNum) && (symbolSectNum == this->sectionNum(parser)) ) { + const uint8_t symbolSectNum = sym.n_sect(); + const bool ignore = this->ignoreLabel(parser.nameFromSymbol(sym)); + if ( !ignore && !prevIgnore && (symbolAddr == prevSymbolAddr) && (prevSymbolSectNum == symbolSectNum) && (symbolSectNum == this->sectionNum(parser)) ) { ++count; } prevSymbolAddr = symbolAddr; prevSymbolSectNum = symbolSectNum; + prevIgnore = ignore; } } return count; @@ -4457,33 +4863,44 @@ uint32_t ImplicitSizeSection::appendAtoms(class Parser& parser, uint8_t* p pint_t size; const macho_nlist

* foundLabel; Atom* allocatedSpace; - while ( it.next(parser, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { if ( foundLabel != NULL ) { + bool skip = false; pint_t labeledAtomSize = this->elementSizeAtAddress(foundAddr); allocatedSpace = (Atom*)p; if ( this->ignoreLabel(parser.nameFromSymbol(*foundLabel)) ) { - //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)foundAddr); - new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, + if ( size == 0 ) { + // + // a size of zero means there is another label at same location + // and we are supposed to ignore this label + skip = true; + } + else { + //fprintf(stderr, " 0x%08llX make annon, size=%lld\n", (uint64_t)foundAddr, (uint64_t)size); + new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, this->elementSizeAtAddress(foundAddr), this->definition(), this->combine(parser, foundAddr), this->scopeAtAddress(parser, foundAddr), this->contentType(), this->symbolTableInclusion(), this->dontDeadStrip(), false, false, this->alignmentForAddress(foundAddr)); + } } else { // make named atom for label //fprintf(stderr, " 0x%08llX make labeled\n", (uint64_t)foundAddr); new (allocatedSpace) Atom(*this, parser, *foundLabel, labeledAtomSize); } - ++count; - p += sizeof(Atom); - foundAddr += labeledAtomSize; - size -= labeledAtomSize; + if ( !skip ) { + ++count; + p += sizeof(Atom); + foundAddr += labeledAtomSize; + size -= labeledAtomSize; + } } // some number of anonymous atoms for (pint_t addr = foundAddr; addr < (foundAddr+size); addr += elementSizeAtAddress(addr) ) { // make anon atoms for area before label if ( this->useElementAt(parser, it, addr) ) { - //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)addr); + //fprintf(stderr, " 0x%08llX make annon, size=%lld\n", (uint64_t)addr, (uint64_t)elementSizeAtAddress(addr)); allocatedSpace = (Atom*)p; new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, addr), addr, this->elementSizeAtAddress(addr), this->definition(), this->combine(parser, addr), this->scopeAtAddress(parser, addr), @@ -4606,11 +5023,12 @@ bool CStringSection::useElementAt(Parser& parser, struct Parser::LabelA } template -bool CStringSection::ignoreLabel(const char* label) +bool CStringSection::ignoreLabel(const char* label) const { return (label[0] == 'L') || (label[0] == 'l'); } + template Atom* CStringSection::findAtomByAddress(pint_t addr) { @@ -4665,6 +5083,12 @@ ld::Fixup::Kind NonLazyPointerSection::fixupKind() return ld::Fixup::kindStoreLittleEndian32; } +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian64; +} + template <> void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -4832,7 +5256,11 @@ const uint8_t* CFStringSection::targetContent(const class Atom* atom, cons *ct = contentUTF16; *count = (targetAtom->size()+1)/2; // round up incase of buggy compiler that has only one trailing zero byte } - assert(target != NULL); + else { + *ct = contentUnknown; + *count = 0; + return NULL; + } return target->contentPointer(); } assert(0); @@ -4862,7 +5290,8 @@ unsigned long CFStringSection::contentHash(const class Atom* atom, const l } return hash; case contentUnknown: - return 0; + // For malformed CFStrings, hash to address of atom so they have unique hashes + return ULONG_MAX - (unsigned long)(atom); } return 0; } @@ -4891,6 +5320,12 @@ bool CFStringSection::canCoalesceWith(const class Atom* atom, const ld::At if ( thisType != rhsType ) return false; + if ( thisType == contentUnknown ) + return false; + + if ( rhsType == contentUnknown ) + return false; + // no need to compare content of pointers are already the same if ( cstringContent == rhsStringContent ) return true; @@ -5056,12 +5491,16 @@ const char* PointerToCStringSection::targetCString(const class Atom* atom, case ld::Fixup::bindingsIndirectlyBound: targetAtom = ind.indirectAtom(fit->u.bindingIndex); break; + case ld::Fixup::bindingDirectlyBound: + targetAtom = fit->u.target; + break; default: - assert(0); + assert(0 && "unsupported reference to selector"); } assert(targetAtom != NULL); const Atom* target = dynamic_cast*>(targetAtom); - assert(target != NULL); + assert(target != NULL); + assert(target->contentType() == ld::Atom::typeCString); return (char*)target->contentPointer(); } @@ -6054,8 +6493,314 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati #endif - - +#if SUPPORT_ARCH_arm64 +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + bool result = false; + Parser::SourceLocation src; + Parser::TargetDesc target = { NULL, NULL, false, 0 }; + Parser::TargetDesc toTarget; + int32_t prefixRelocAddend = 0; + if ( reloc->r_type() == ARM64_RELOC_ADDEND ) { + uint32_t rawAddend = reloc->r_symbolnum(); + prefixRelocAddend = rawAddend; + if ( rawAddend & 0x00800000 ) + prefixRelocAddend |= 0xFF000000; // sign extend 24-bit signed int to 32-bits + uint32_t addendAddress = reloc->r_address(); + ++reloc; //advance to next reloc record + result = true; + if ( reloc->r_address() != addendAddress ) + throw "ARM64_RELOC_ADDEND r_address does not match next reloc's r_address"; + } + const macho_section

* sect = this->machoSection(); + uint64_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint64_t contentValue = 0; + const macho_relocation_info* nextReloc = &reloc[1]; + bool useDirectBinding; + uint32_t instruction; + uint32_t encodedAddend; + switch ( reloc->r_length() ) { + case 0: + contentValue = *fixUpPtr; + break; + case 1: + contentValue = (int64_t)(int16_t)E::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + contentValue = (int64_t)(int32_t)E::get32(*((uint32_t*)fixUpPtr)); + break; + case 3: + contentValue = E::get64(*((uint64_t*)fixUpPtr)); + break; + } + if ( reloc->r_extern() ) { + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + const char* symbolName = parser.nameFromSymbol(sym); + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (symbolName[0] == 'L') || (symbolName[0] == 'l')) ) { + // use direct reference for local symbols + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + //target.addend += contentValue; + } + else if ( ((sym.n_type() & N_TYPE) == N_SECT) && (src.atom->_objAddress <= sym.n_value()) && (sym.n_value() < (src.atom->_objAddress+src.atom->size())) ) { + // spurious warning when weak function has reference to itself + // use direct reference when atom targets itself + target.atom = src.atom; + target.name = NULL; + } + else { + target.name = symbolName; + target.weakImport = parser.weakImportFromSymbol(sym); + //target.addend = contentValue; + } + // cfstrings should always use direct reference to backing store + if ( (this->type() == ld::Section::typeCFString) && (src.offsetInAtom != 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + //target.addend = contentValue; + } + } + else { + if ( reloc->r_pcrel() ) + contentValue += srcAddr; + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_type() ) { + case ARM64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_UNSIGNED not supported"; + target.addend = contentValue; + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "length < 2 and ARM64_RELOC_UNSIGNED not supported"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian64, target); + break; + } + break; + case ARM64_RELOC_BRANCH26: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_BRANCH26 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_BRANCH26 not supported"; + if ( reloc->r_length() != 2 ) + throw "r_length != 2 and ARM64_RELOC_BRANCH26 not supported"; + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreARM64DtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + target.addend = prefixRelocAddend; + instruction = contentValue; + encodedAddend = (instruction & 0x03FFFFFF) << 2; + if ( encodedAddend != 0 ) { + if ( prefixRelocAddend == 0 ) { + warning("branch26 instruction at 0x%08X has embedded addend. ARM64_RELOC_ADDEND should be used instead", reloc->r_address()); + target.addend = encodedAddend; + } + else { + throwf("branch26 instruction at 0x%08X has embedded addend and ARM64_RELOC_ADDEND also used", reloc->r_address()); + } + } + parser.addFixups(src, ld::Fixup::kindStoreARM64Branch26, target); + } + break; + case ARM64_RELOC_PAGE21: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_PAGE21 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_PAGE21 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_PAGE21 not supported"; + target.addend = prefixRelocAddend; + instruction = contentValue; + encodedAddend = ((instruction & 0x60000000) >> 29) | ((instruction & 0x01FFFFE0) >> 3); + encodedAddend *= 4096; // internally addend is in bytes, so scale + if ( encodedAddend != 0 ) { + if ( prefixRelocAddend == 0 ) { + warning("adrp instruction at 0x%08X has embedded addend. ARM64_RELOC_ADDEND should be used instead", reloc->r_address()); + target.addend = encodedAddend; + } + else { + throwf("adrp instruction at 0x%08X has embedded addend and ARM64_RELOC_ADDEND also used", reloc->r_address()); + } + } + parser.addFixups(src, ld::Fixup::kindStoreARM64Page21, target); + break; + case ARM64_RELOC_PAGEOFF12: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_PAGEOFF12 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_PAGEOFF12 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_PAGEOFF12 not supported"; + target.addend = prefixRelocAddend; + instruction = contentValue; + encodedAddend = ((instruction & 0x003FFC00) >> 10); + // internally addend is in bytes. Some instructions have an implicit scale factor + if ( (instruction & 0x3B000000) == 0x39000000 ) { + switch ( instruction & 0xC0000000 ) { + case 0x00000000: + break; + case 0x40000000: + encodedAddend *= 2; + break; + case 0x80000000: + encodedAddend *= 4; + break; + case 0xC0000000: + encodedAddend *= 8; + break; + } + } + if ( encodedAddend != 0 ) { + if ( prefixRelocAddend == 0 ) { + warning("pageoff12 instruction at 0x%08X has embedded addend. ARM64_RELOC_ADDEND should be used instead", reloc->r_address()); + target.addend = encodedAddend; + } + else { + throwf("pageoff12 instruction at 0x%08X has embedded addend and ARM64_RELOC_ADDEND also used", reloc->r_address()); + } + } + parser.addFixups(src, ld::Fixup::kindStoreARM64PageOff12, target); + break; + case ARM64_RELOC_GOT_LOAD_PAGE21: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x60000000) >> 29) | ((instruction & 0x01FFFFE0) >> 3); + if ( target.addend != 0 ) + throw "non-zero addend with ARM64_RELOC_GOT_LOAD_PAGE21 is not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64GOTLoadPage21, target); + break; + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x003FFC00) >> 10); + parser.addFixups(src, ld::Fixup::kindStoreARM64GOTLoadPageOff12, target); + break; + case ARM64_RELOC_TLVP_LOAD_PAGE21: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x60000000) >> 29) | ((instruction & 0x01FFFFE0) >> 3); + if ( target.addend != 0 ) + throw "non-zero addend with ARM64_RELOC_GOT_LOAD_PAGE21 is not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64TLVPLoadPage21, target); + break; + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x003FFC00) >> 10); + parser.addFixups(src, ld::Fixup::kindStoreARM64TLVPLoadPageOff12, target); + break; + case ARM64_RELOC_SUBTRACTOR: + if ( reloc->r_pcrel() ) + throw "ARM64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "ARM64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "ARM64_RELOC_SUBTRACTOR must have r_extern=1"; + if ( nextReloc->r_type() != ARM64_RELOC_UNSIGNED ) + throw "ARM64_RELOC_SUBTRACTOR must be followed by ARM64_RELOC_UNSIGNED"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_SUBTRACTOR not supported"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "ARM64_RELOC_UNSIGNED following a ARM64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "ARM64_RELOC_UNSIGNED following a ARM64_RELOC_SUBTRACTOR must have same r_length"; + if ( nextReloc->r_extern() ) { + const macho_nlist

& sym = parser.symbolFromIndex(nextReloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(sym)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), toTarget); + toTarget.addend = contentValue; + useDirectBinding = true; + } + else { + toTarget.name = parser.nameFromSymbol(sym); + toTarget.weakImport = parser.weakImportFromSymbol(sym); + toTarget.addend = contentValue; + useDirectBinding = false; + } + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget); + useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit); + } + if ( useDirectBinding ) + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); + else + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend); + if ( target.atom == NULL ) + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, false, target.name); + else + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, target.atom); + if ( reloc->r_length() == 2 ) + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + else + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian64); + break; + case ARM64_RELOC_POINTER_TO_GOT: + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_POINTER_TO_GOT not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_POINTER_TO_GOT not supported"; + if ( reloc->r_pcrel() ) { + if ( reloc->r_length() != 2 ) + throw "r_length != 2 and r_extern = 1 and ARM64_RELOC_POINTER_TO_GOT not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64PCRelToGOT, target); + } + else { + if ( reloc->r_length() != 3 ) + throw "r_length != 3 and r_extern = 0 and ARM64_RELOC_POINTER_TO_GOT not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64PointerToGOT, target); + } + break; + default: + throwf("unknown relocation type %d", reloc->r_type()); + } + return result; +} +#endif template bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) @@ -6146,6 +6891,72 @@ bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const m return PointerToCStringSection::addRelocFixup(parser, reloc); } +#if SUPPORT_ARCH_arm64 +template <> +void Section::addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]) { + switch (kind) { + case LOH_ARM64_ADRP_ADRP: + case LOH_ARM64_ADRP_LDR: + case LOH_ARM64_ADRP_ADD: + case LOH_ARM64_ADRP_LDR_GOT: + if ( count != 2 ) + warning("arm64 Linker Optimiztion Hint %d has wrong number of arguments", kind); + break; + case LOH_ARM64_ADRP_ADD_LDR: + case LOH_ARM64_ADRP_LDR_GOT_LDR: + case LOH_ARM64_ADRP_ADD_STR: + case LOH_ARM64_ADRP_LDR_GOT_STR: + if ( count != 3 ) + warning("arm64 Linker Optimiztion Hint %d has wrong number of arguments", kind); + } + + // pick lowest address in tuple for use as offsetInAtom + uint64_t lowestAddress = addrs[0]; + for(int i=1; i < count; ++i) { + if ( addrs[i] < lowestAddress ) + lowestAddress = addrs[i]; + } + // verify all other address are in same atom + Atom* inAtom = parser.findAtomByAddress(lowestAddress); + const uint64_t atomStartAddr = inAtom->objectAddress(); + const uint64_t atomEndAddr = atomStartAddr + inAtom->size(); + for(int i=0; i < count; ++i) { + if ( (addrs[i] < atomStartAddr) || (addrs[i] >= atomEndAddr) ) { + warning("arm64 Linker Optimiztion Hint addresses are not in same atom: 0x%08llX and 0x%08llX", + lowestAddress, addrs[i]); + return; // skip this LOH + } + if ( (addrs[i] & 0x3) != 0 ) { + warning("arm64 Linker Optimiztion Hint address is not 4-byte aligned: 0x%08llX", addrs[i]); + return; // skip this LOH + } + if ( (addrs[i] - lowestAddress) > 0xFFFF ) { + if ( parser.verboseOptimizationHints() ) { + warning("arm64 Linker Optimiztion Hint addresses are too far apart: 0x%08llX and 0x%08llX", + lowestAddress, addrs[i]); + } + return; // skip this LOH + } + } + + // encoded kind, count, and address deltas in 64-bit addend + ld::Fixup::LOH_arm64 extra; + extra.addend = 0; + extra.info.kind = kind; + extra.info.count = count-1; + extra.info.delta1 = (addrs[0] - lowestAddress) >> 2; + extra.info.delta2 = (count > 1) ? ((addrs[1] - lowestAddress) >> 2) : 0; + extra.info.delta3 = (count > 2) ? ((addrs[2] - lowestAddress) >> 2) : 0; + extra.info.delta4 = (count > 3) ? ((addrs[3] - lowestAddress) >> 2) : 0; + typename Parser::SourceLocation src(inAtom, lowestAddress- inAtom->objectAddress()); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindLinkerOptimizationHint, extra.addend); +} +#endif + +template +void Section::addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]) { + +} template void Section::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -6261,6 +7072,42 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } + // convert linker optimization hints into internal format + if ( this->type() == ld::Section::typeCode && parser.hasOptimizationHints() ) { + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + for (const uint8_t* p = parser.optimizationHintsStart(); p < parser.optimizationHintsEnd(); ) { + uint64_t addrs[4]; + int32_t kind = read_uleb128(&p, parser.optimizationHintsEnd()); + if ( kind == 0 ) // padding at end of loh buffer + break; + if ( kind == -1 ) { + warning("malformed uleb128 kind in LC_LINKER_OPTIMIZATION_HINTS"); + break; + } + int32_t count = read_uleb128(&p, parser.optimizationHintsEnd()); + if ( count == -1 ) { + warning("malformed uleb128 count in LC_LINKER_OPTIMIZATION_HINTS"); + break; + } + if ( count > 3 ) { + warning("address count > 3 in LC_LINKER_OPTIMIZATION_HINTS"); + break; + } + for (int32_t i=0; i < count; ++i) { + addrs[i] = read_uleb128(&p, parser.optimizationHintsEnd()); + } + if ( (startAddr <= addrs[0]) && (addrs[0] < endAddr) ) { + this->addLOH(parser, kind, count, addrs); + //fprintf(stderr, "kind=%d", kind); + //for (int32_t i=0; i < count; ++i) { + // fprintf(stderr, ", addr=0x%08llX", addrs[i]); + //} + //fprintf(stderr, "\n"); + } + } + } + // add follow-on fixups for aliases if ( _hasAliases ) { @@ -6302,6 +7149,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; #endif } return NULL; @@ -6319,6 +7172,8 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO return ( mach_o::relocatable::Parser::validFile(fileContent) ); case CPU_TYPE_ARM: return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); + case CPU_TYPE_ARM64: + return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); } return false; } @@ -6330,7 +7185,8 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* { if ( mach_o::relocatable::Parser::validFile(fileContent) ) { *result = CPU_TYPE_X86_64; - *subResult = CPU_SUBTYPE_X86_64_ALL; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent) ) { @@ -6344,6 +7200,11 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = header->cpusubtype(); return true; } + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + *result = CPU_TYPE_ARM64; + *subResult = CPU_SUBTYPE_ARM64_ALL; + return true; + } return false; } @@ -6378,6 +7239,11 @@ bool hasObjC2Categories(const uint8_t* fileContent) else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); } +#if SUPPORT_ARCH_arm64 + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); + } +#endif return false; } diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index 021c3f2..6d20847 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -35,7 +35,11 @@ struct ParserOptions { uint32_t architecture; bool objSubtypeMustMatch; bool logAllFiles; - bool convertUnwindInfo; + bool warnUnwindConversionProblems; + bool keepDwarfUnwind; + bool forceDwarfConversion; + bool neverConvertDwarf; + bool verboseOptimizationHints; uint32_t subType; }; diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 96b6d35..5f5612c 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -41,6 +41,7 @@ namespace passes { namespace branch_island { +static std::map sAtomToAddress; struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; }; @@ -68,43 +69,27 @@ class ARMtoARMBranchIslandAtom : public ld::Atom { ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, target), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%s: ARM jump instruction branch island to final target %s\n", + target->name(), finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { - int64_t displacement = _target->finalAddress() - this->finalAddress() - 8; - if ( _target->contentType() == ld::Atom::typeBranchIsland ) { - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 8; - if ( (skipToFinalDisplacement < 33554428LL) && (skipToFinalDisplacement > (-33554432LL)) ) { - // can skip branch island and jump straight to target - if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); - displacement = skipToFinalDisplacement; - } - else { - // ultimate target is too far, jump to island - if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); - } - } - uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; - int32_t branchInstruction = 0xEA000000 | imm24; - OSWriteLittleInt32(buffer, 0, branchInstruction); + OSWriteLittleInt32(buffer, 0, 0xEA000000); } virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: const char* _name; - const ld::Atom* _target; - TargetAndOffset _finalTarget; + ld::Fixup _fixup1; + ld::Fixup _fixup2; }; @@ -154,62 +139,68 @@ class Thumb2toThumbBranchIslandAtom : public ld::Atom { ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressThumbBranch22, target), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%s: Thumb jump instruction branch island to final target %s\n", + target->name(), finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { - int64_t displacement = _target->finalAddress() - this->finalAddress() - 4; - if ( _target->contentType() == ld::Atom::typeBranchIsland ) { - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 4; - if ( (skipToFinalDisplacement < 16777214) && (skipToFinalDisplacement > (-16777216LL)) ) { - // can skip branch island and jump straight to target - if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); - displacement = skipToFinalDisplacement; - } - else { - // ultimate target is too far for thumb2 branch, jump to island - if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); - } - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - uint32_t opcode = 0x9000F000; - uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; - uint32_t firstDisp = (s << 10) | imm10; - uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - OSWriteLittleInt32(buffer, 0, newInstruction); + OSWriteLittleInt32(buffer, 0, 0xf0008000); } virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: const char* _name; - const ld::Atom* _target; - TargetAndOffset _finalTarget; + ld::Fixup _fixup1; + ld::Fixup _fixup2; +}; + + + +class Thumb2toThumbBranchAbsoluteIslandAtom : public ld::Atom { +public: + Thumb2toThumbBranchAbsoluteIslandAtom(const char* nm, const ld::Section& inSect, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(nm), + _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom), + _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbLow16), + _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom), + _fixup4(4, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbHigh16), + _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x0c00f240); // movw r12, #0x5678 + OSWriteLittleInt32(&buffer[4], 0, 0x0c00f2c0); // movt r12, #0x1234 + OSWriteLittleInt16(&buffer[8], 0, 0x4760); // bx r12 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup5)[1]; } + +private: + const char* _name; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; }; + class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { public: NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) @@ -245,7 +236,8 @@ class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { }; -static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget) +static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, + TargetAndOffset finalTarget, const ld::Section& inSect, bool crossSectionBranch) { char* name; if ( finalTarget.offset == 0 ) { @@ -263,7 +255,10 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: - if ( finalTarget.atom->isThumb() ) { + if ( crossSectionBranch && opts.preferSubArchitecture() && opts.archSupportsThumb2() ) { + return new Thumb2toThumbBranchAbsoluteIslandAtom(name, inSect, finalTarget); + } + else if ( finalTarget.atom->isThumb() ) { if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) { return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget); } @@ -343,47 +338,51 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra // before any branches could be pushed out of range. // -void doPass(const Options& opts, ld::Internal& state) -{ - // only make branch islands in final linked images - if ( opts.outputKind() == Options::kObjectFile ) - return; - // only ARM needs branch islands - switch ( opts.architecture() ) { - case CPU_TYPE_ARM: - break; - default: - return; - } - - // scan to find __text section - ld::Internal::FinalSection* textSection = NULL; - 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; - +static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection) +{ // assign section offsets to each atom in __text section, watch for thumb branches, and find total size - const bool isARM = (opts.architecture() == CPU_TYPE_ARM); bool hasThumbBranches = false; + bool haveCrossSectionBranches = false; + const bool preload = (opts.outputKind() == Options::kPreload); uint64_t offset = 0; for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - // check for thumb branches - if ( isARM && ~hasThumbBranches ) { - for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { - switch ( fit->kind ) { - case ld::Fixup::kindStoreThumbBranch22: - case ld::Fixup::kindStoreTargetAddressThumbBranch22: - hasThumbBranches = true; - break; - default: - break; - } + // check for thumb branches and cross section branches + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + bool haveBranch = false; + switch (fit->kind) { + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + hasThumbBranches = true; + // fall into arm branch case + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + haveBranch = true; + break; + default: + break; + } + if ( haveBranch && (target->contentType() != ld::Atom::typeStub) ) { + // haveCrossSectionBranches only applies to -preload builds + if ( preload && (atom->section() != target->section()) ) + haveCrossSectionBranches = true; } } // align atom @@ -400,9 +399,9 @@ void doPass(const Options& opts, ld::Internal& state) offset += atom->size(); } uint64_t totalTextSize = offset; - if ( totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches) ) + if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches ) return; - if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize); + if (_s_log) fprintf(stderr, "ld: section %s size=%llu, might need branch islands\n", textSection->sectionName(), totalTextSize); // Figure out how many regions of branch islands will be needed, and their locations. // Construct a vector containing the atoms after which branch islands will be inserted, @@ -410,12 +409,12 @@ void doPass(const Options& opts, ld::Internal& state) const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section std::vector branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted uint64_t previousIslandEndAddr = 0; - const ld::Atom *insertionPoint; + const ld::Atom *insertionPoint = NULL; branchIslandInsertionPoints.reserve(totalTextSize/kBetweenRegions*2); for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { const ld::Atom* atom = *it; // if we move past the next atom, will the run length exceed kBetweenRegions? - if ( atom->sectionOffset() + atom->size() - previousIslandEndAddr > kBetweenRegions ) { + if ( atom->sectionOffset() + atom->size() > previousIslandEndAddr + kBetweenRegions ) { // yes. Add the last known good location (atom) for inserting a branch island. if ( insertionPoint == NULL ) throwf("Unable to insert branch island. No insertion point available."); @@ -427,28 +426,24 @@ void doPass(const Options& opts, ld::Internal& state) if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) ) insertionPoint = atom; } - // add one more island after the last atom - if (insertionPoint != NULL) + // add one more island after the last atom if close to limit + if ( (insertionPoint != NULL) && (insertionPoint->sectionOffset() + insertionPoint->size() > previousIslandEndAddr + (kBetweenRegions-0x100000)) ) branchIslandInsertionPoints.push_back(insertionPoint); - const int kIslandRegionsCount = branchIslandInsertionPoints.size(); - if (_s_log) { - fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); - for (std::vector::iterator it = branchIslandInsertionPoints.begin(); it != branchIslandInsertionPoints.end(); ++it) { - const ld::Atom* atom = *it; - const ld::File *file = atom->file(); - fprintf(stderr, "ld: branch island will be inserted at 0x%llx after %s", atom->sectionOffset()+atom->size(), atom->name()); - if (file) fprintf(stderr, " (%s)", atom->file()->path()); - fprintf(stderr, "\n"); - } + if ( haveCrossSectionBranches && branchIslandInsertionPoints.empty() ) { + branchIslandInsertionPoints.push_back(textSection->atoms.back()); } + const int kIslandRegionsCount = branchIslandInsertionPoints.size(); - + if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); typedef std::map AtomToIsland; AtomToIsland* regionsMap[kIslandRegionsCount]; + uint64_t regionAddresses[kIslandRegionsCount]; std::vector* regionsIslands[kIslandRegionsCount]; for(int i=0; i < kIslandRegionsCount; ++i) { regionsMap[i] = new AtomToIsland(); regionsIslands[i] = new std::vector(); + regionAddresses[i] = branchIslandInsertionPoints[i]->sectionOffset() + branchIslandInsertionPoints[i]->size(); + if (_s_log) fprintf(stderr, "ld: branch islands will be inserted at 0x%08llX after %s\n", regionAddresses[i], branchIslandInsertionPoints[i]->name()); } unsigned int islandCount = 0; @@ -493,25 +488,51 @@ void doPass(const Options& opts, ld::Internal& state) break; } if ( haveBranch ) { + bool crossSectionBranch = ( preload && (atom->section() != target->section()) ); int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom; int64_t dstAddr = target->sectionOffset() + addend; + if ( preload ) { + srcAddr = sAtomToAddress[atom] + fit->offsetInAtom; + dstAddr = sAtomToAddress[target] + addend; + } if ( target->section().type() == ld::Section::typeStub ) dstAddr = totalTextSize; int64_t displacement = dstAddr - srcAddr; TargetAndOffset finalTargetAndOffset = { target, addend }; const int64_t kBranchLimit = kBetweenRegions; - if ( displacement > kBranchLimit ) { + if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { + const ld::Atom* island; + AtomToIsland* region = regionsMap[0]; + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + island = makeBranchIsland(opts, fit->kind, 0, target, finalTargetAndOffset, atom->section(), true); + (*region)[finalTargetAndOffset] = island; + if (_s_log) fprintf(stderr, "added absolute branching island %p %s, displacement=%lld\n", + island, island->name(), displacement); + ++islandCount; + regionsIslands[0]->push_back(island); + } + else { + island = pos->second; + } + if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", island, island->name(), target->name(), atom->name()); + fixupWithTarget->u.target = island; + fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + } + else if ( displacement > kBranchLimit ) { // create forward branch chain const ld::Atom* nextTarget = target; + if (_s_log) fprintf(stderr, "need forward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", + srcAddr, dstAddr, target->name()); for (int i=kIslandRegionsCount-1; i >=0 ; --i) { AtomToIsland* region = regionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + int64_t islandRegionAddr = regionAddresses[i]; + if ( (srcAddr < islandRegionAddr) && ((islandRegionAddr <= dstAddr)) ) { AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset); + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset, atom->section(), false); (*region)[finalTargetAndOffset] = island; - if (_s_log) fprintf(stderr, "added island %s to region %d for %s\n", island->name(), i, atom->name()); + if (_s_log) fprintf(stderr, "added forward branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); ++islandCount; nextTarget = island; @@ -521,7 +542,7 @@ void doPass(const Options& opts, ld::Internal& state) } } } - if (_s_log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->name(), target->name(), atom->name()); + if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", nextTarget, nextTarget->name(), target->name(), atom->name()); fixupWithTarget->u.target = nextTarget; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; } @@ -530,13 +551,14 @@ void doPass(const Options& opts, ld::Internal& state) const ld::Atom* prevTarget = target; for (int i=0; i < kIslandRegionsCount ; ++i) { AtomToIsland* region = regionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + int64_t islandRegionAddr = regionAddresses[i]; + if ( (dstAddr < islandRegionAddr) && (islandRegionAddr <= srcAddr) ) { + if (_s_log) fprintf(stderr, "need backward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", srcAddr, dstAddr, target->name()); AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset); + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset, atom->section(), false); (*region)[finalTargetAndOffset] = island; - if (_s_log) fprintf(stderr, "added back island %s to region %d for %s\n", island->name(), i, atom->name()); + if (_s_log) fprintf(stderr, "added back branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); ++islandCount; prevTarget = island; @@ -546,7 +568,7 @@ void doPass(const Options& opts, ld::Internal& state) } } } - if (_s_log) fprintf(stderr, "using back island %s for %s\n", prevTarget->name(), atom->name()); + if (_s_log) fprintf(stderr, "using back island %p %s for %s\n", prevTarget, prevTarget->name(), atom->name()); fixupWithTarget->u.target = prevTarget; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; } @@ -561,24 +583,15 @@ void doPass(const Options& opts, ld::Internal& state) std::vector newAtomList; newAtomList.reserve(textSection->atoms.size()+islandCount); - uint64_t regionIndex = 0; + int regionIndex = 0; for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ait++) { - newAtomList.push_back(*ait); - // copy over atoms until we find an island insertion point - // Note that the last insertion point is the last atom, so this loop never moves the iterator to atoms.end(). - while (*ait != branchIslandInsertionPoints[regionIndex]) { - ait++; - newAtomList.push_back(*ait); + const ld::Atom* atom = *ait; + newAtomList.push_back(atom); + if ( (regionIndex < kIslandRegionsCount) && (atom == branchIslandInsertionPoints[regionIndex]) ) { + std::vector* islands = regionsIslands[regionIndex]; + newAtomList.insert(newAtomList.end(), islands->begin(), islands->end()); + ++regionIndex; } - - // insert the branch island atoms after the insertion point atom - std::vector* regionIslands = regionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - const ld::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); - } - regionIndex++; } // swap in new list of atoms for __text section textSection->atoms.clear(); @@ -588,6 +601,77 @@ void doPass(const Options& opts, ld::Internal& state) } +static void buildAddressMap(const Options& opts, ld::Internal& state) { + // Assign addresses to sections + state.setSectionSizesAndAlignments(); + state.assignFileOffsets(); + + // Assign addresses to atoms in a side table + const bool log = false; + if ( log ) fprintf(stderr, "buildAddressMap()\n"); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + uint16_t maxAlignment = 0; + uint64_t offset = 0; + if ( log ) fprintf(stderr, " section=%s/%s, address=0x%08llX\n", sect->segmentName(), sect->sectionName(), sect->address); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + uint32_t atomModulus = atom->alignment().modulus; + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atomAlignmentPowerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atomModulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + + if ( log ) fprintf(stderr, " 0x%08llX atom=%p, name=%s\n", sect->address+offset, atom, atom->name()); + sAtomToAddress[atom] = sect->address + offset; + + offset += atom->size(); + } + } + + +} + +void doPass(const Options& opts, ld::Internal& state) +{ + // only make branch islands in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // Allow user to disable branch island generation + if ( !opts.allowBranchIslands() ) + return; + + // only ARM needs branch islands + switch ( opts.architecture() ) { + case CPU_TYPE_ARM: + break; + default: + return; + } + + if ( opts.outputKind() == Options::kPreload ) { + buildAddressMap(opts, state); + } + + // scan sections and add island to each code section + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeCode ) + makeIslandsForSection(opts, state, sect); + } +} + + } // namespace branch_island } // namespace passes } // namespace ld diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index f86aee0..ad8a504 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -156,11 +156,10 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 std::vector lsdaIndex; makeLsdaIndex(uniqueEntries, lsdaIndex, lsdaIndexOffsetMap); - // calculate worst case size for all unwind info pages when allocating buffer const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); assert(uniqueEntries.size() > 0); - const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 1; + const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 2; _pagesForDelete = (uint8_t*)calloc(pageCount,4096); if ( _pagesForDelete == NULL ) { warning("could not allocate space for compact unwind info"); @@ -187,7 +186,11 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 secondLevelPagesStarts[secondLevelPageCount] = pageEnd; secondLevelFirstFuncs[secondLevelPageCount] = uniqueEntries[endIndex].func; ++secondLevelPageCount; - pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + // if this requires more than one page, align so that next starts on page boundary + if ( (pageSize != 4096) && (endIndex > 0) ) { + pageEnd = (uint8_t*)((uintptr_t)(pageEnd) & -4096); + pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + } } _pages = pageEnd; _pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd; @@ -294,6 +297,12 @@ bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); } +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); +} + template void UnwindInfoAtom::compressDuplicates(const std::vector& entries, std::vector& uniqueEntries) { @@ -400,6 +409,14 @@ void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, co _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + template <> void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) { @@ -414,6 +431,12 @@ void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const l _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} template <> void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) @@ -429,6 +452,13 @@ void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::A _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + template <> void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) { @@ -443,6 +473,13 @@ void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld: _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + template <> void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) { @@ -457,6 +494,13 @@ void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + template <> void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) { @@ -473,6 +517,13 @@ void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, cons _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} @@ -552,6 +603,7 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); if ( encodingIndex <= 255 ) { pageSpecificEncodings[encoding] = encodingIndex; + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): pageSpecificEncodings[%d]=0x%08X\n", encodingIndex, encoding); } else { canDo = false; // case 3) @@ -685,7 +737,7 @@ static void getAllUnwindInfos(const ld::Internal& state, std::vectorbeginUnwind() == atom->endUnwind() ) { // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info - if ( atom->section().type() == ld::Section::typeCode ) { + if ( (atom->section().type() == ld::Section::typeCode) && (atom->size() !=0) ) { entries.push_back(UnwindEntry(atom, address, 0, NULL, NULL, NULL, 0)); } } @@ -781,6 +833,11 @@ static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::In case CPU_TYPE_I386: state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); + break; #endif default: assert(0 && "no compact unwind for arch"); @@ -830,13 +887,17 @@ template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup:: 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; +#if SUPPORT_ARCH_arm64 +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; +#endif 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)), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(log2(sizeof(pint_t)))), _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)); @@ -885,6 +946,11 @@ static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, cons case CPU_TYPE_I386: state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); + break; #endif } } diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index 74328ff..6f8a544 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -34,6 +34,7 @@ #include #include "ld.hpp" +#include "MachOFileAbstraction.hpp" #include "dtrace_dof.h" // prototype for entry point in libdtrace.dylib @@ -122,6 +123,10 @@ void doPass(const Options& opts, ld::Internal& internal) // only make __dof section in final linked images if ( opts.outputKind() == Options::kObjectFile ) return; + + // skip making __dof section if command line option said not to + if ( ! opts.generateDtraceDOF() ) + return; // scan all atoms looking for dtrace probes std::vector probeSites; @@ -138,11 +143,13 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindDtraceExtra: @@ -164,6 +171,7 @@ void doPass(const Options& opts, ld::Internal& internal) case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: storeKind = ld::Fixup::kindStoreLittleEndian32; break; default: diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 3e6824f..2b9da3a 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -31,8 +31,10 @@ #include #include +#include "MachOFileAbstraction.hpp" #include "ld.hpp" #include "got.h" +#include "configure.h" namespace ld { namespace passes { @@ -42,17 +44,18 @@ class File; // forward reference class GOTEntryAtom : public ld::Atom { public: - GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool is64) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), - _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), - _target(target) + symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))), + _fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target), + _target(target), + _is64(is64) { _fixup.weakImport = weakImport; internal.addAtom(*this); } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _target->name(); } - virtual uint64_t size() const { return 8; } + virtual uint64_t size() const { return (_is64 ? 8 : 4); } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { } virtual void setScope(Scope) { } @@ -62,6 +65,7 @@ class GOTEntryAtom : public ld::Atom { private: mutable ld::Fixup _fixup; const ld::Atom* _target; + bool _is64; static ld::Section _s_section; }; @@ -73,6 +77,10 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom { switch (fixup->kind) { case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: +#endif // start by assuming this can be optimized *optimizable = true; // cannot do LEA optimization if target is in another dylib @@ -122,6 +130,9 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom } return true; case ld::Fixup::kindStoreX86PCRel32GOT: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64PCRelToGOT: +#endif *optimizable = false; return true; case ld::Fixup::kindNoneGroupSubordinatePersonality: @@ -189,7 +200,22 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::bindingDirectlyBound: fit->binding = ld::Fixup::bindingDirectlyBound; fit->u.target = targetOfGOT; - fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; + switch ( fit->kind ) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; + break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21; + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12; + break; +#endif + default: + assert(0 && "unsupported GOT reference kind"); + break; + } break; default: assert(0 && "unsupported GOT reference"); @@ -232,9 +258,33 @@ void doPass(const Options& opts, ld::Internal& internal) } } + bool is64 = false; + switch ( opts.architecture() ) { +#if SUPPORT_ARCH_i386 + case CPU_TYPE_I386: + is64 = false; + break; +#endif +#if SUPPORT_ARCH_x86_64 + case CPU_TYPE_X86_64: + is64 = true; + break; +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + is64 = false; + break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + is64 = true; + break; +#endif + } + // make GOT entries for (std::map::iterator it = gotMap.begin(); it != gotMap.end(); ++it) { - it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first]); + it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first], is64); } // update atoms to use GOT entries diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index d471d98..d921a64 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -54,6 +54,7 @@ struct objc_image_info { #define OBJC_IMAGE_REQUIRES_GC (1<<2) #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) +#define OBJC_IMAGE_IS_SIMULATED (1<<5) @@ -111,6 +112,9 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, if ( compaction ) value |= OBJC_IMAGE_SUPPORTS_COMPACTION; break; + case ld::File::objcConstraintRetainReleaseForSimulator: + value |= OBJC_IMAGE_IS_SIMULATED; + break; } _content.version = 0; @@ -1176,10 +1180,18 @@ void doPass(const Options& opts, ld::Internal& state) opts.objCABIVersion2POverride() ? true : false)); break; #endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, true)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + true)); + break; +#endif default: assert(0 && "unknown objc arch"); } @@ -1195,16 +1207,19 @@ void doPass(const Options& opts, ld::Internal& state) #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - // disable optimization until fully tested if ( opts.objCABIVersion2POverride() ) OptimizeCategories::doit(opts, state); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - // disable optimization until fully tested OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + // disabled until tested + break; #endif default: assert(0 && "unknown objc arch"); diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index b4be79f..684cb79 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include "ld.hpp" @@ -122,7 +123,7 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) { if ( left == right ) return false; - + // magic section$start symbol always sorts to the start of its section if ( left->contentType() == ld::Atom::typeSectionStart ) return true; @@ -162,6 +163,32 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) if ( right->contentType() == ld::Atom::typeSectionEnd ) return true; + // aliases sort before their target + bool leftIsAlias = left->isAlias(); + if ( leftIsAlias ) { + for (ld::Fixup::iterator fit=left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target == right ) + return true; // left already before right + left = fit->u.target; // sort as if alias was its target + break; + } + } + } + bool rightIsAlias = right->isAlias(); + if ( rightIsAlias ) { + for (ld::Fixup::iterator fit=right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target == left ) + return false; // need to swap, alias is after target + right = fit->u.target; // continue with sort as if right was target + break; + } + } + } + // the __common section can have real or tentative definitions // we want the real ones to sort before tentative ones bool leftIsTent = (left->definition() == ld::Atom::definitionTentative); @@ -204,8 +231,6 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) int64_t addrDiff = left->objectAddress() - right->objectAddress(); if ( addrDiff == 0 ) { // have same address so one might be an alias, and aliases need to sort before target - bool leftIsAlias = left->isAlias(); - bool rightIsAlias = right->isAlias(); if ( leftIsAlias != rightIsAlias ) return leftIsAlias; @@ -506,22 +531,32 @@ void Layout::buildOrdinalOverrideMap() warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() ); } - // When order file used on data, turn ordered zero fill symbols into zeroed data if ( ! moveToData.empty() ) { + // only move zero fill symbols to __data if there is a __data section + ld::Internal::FinalSection* dataSect = NULL; for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - switch ( sect->type() ) { - case ld::Section::typeZeroFill: - case ld::Section::typeTentativeDefs: - sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end()); - break; - case ld::Section::typeUnclassified: - if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) - sect->atoms.insert(sect->atoms.end(), moveToData.begin(), moveToData.end()); - break; - default: - break; + if ( sect->type() == ld::Section::typeUnclassified ) { + if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + dataSect = sect; + } + } + + if ( dataSect != NULL ) { + // add atoms to __data + dataSect->atoms.insert(dataSect->atoms.end(), moveToData.begin(), moveToData.end()); + // remove atoms from original sections + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTentativeDefs: + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end()); + break; + default: + break; + } } } } @@ -539,6 +574,7 @@ void Layout::doPass() // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + //fprintf(stderr, "sorting section %s\n", sect->sectionName()); std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); } @@ -547,7 +583,7 @@ void Layout::doPass() // ld::Internal::FinalSection* sect = *sit; // for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { // const ld::Atom* atom = *ait; - // fprintf(stderr, "\t%s\t%s\n", sect->sectionName(), atom->name()); + // fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); // } //} diff --git a/ld64/src/ld/passes/stubs/stub_arm64.hpp b/ld64/src/ld/passes/stubs/stub_arm64.hpp new file mode 100644 index 0000000..63382e5 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_arm64.hpp @@ -0,0 +1,396 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010-2013 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@ + */ + + +// already in ld::passes::stubs namespace +namespace arm64 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "image cache pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedImageCache(pass)), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedImageCache(pass)), + _fixup3(12, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedFastBinder(pass)), + _fixup4(16, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedFastBinder(pass)), + _fixup5(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_ADD, 0, 4), + _fixup6(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 12, 16) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 24; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0x90000011); // ADRP X17, dyld_mageLoaderCache@page + OSWriteLittleInt32(&buffer[ 4], 0, 0x91000231); // ADD X17, X17, dyld_mageLoaderCache@pageoff + OSWriteLittleInt32(&buffer[ 8], 0, 0xA9BF47F0); // STP X16/X17, [SP, #-16]! + OSWriteLittleInt32(&buffer[12], 0, 0x90000010); // ADRP X16, _fast_lazy_bind@page + OSWriteLittleInt32(&buffer[16], 0, 0xF9400210); // LDR X16, [X16,_fast_lazy_bind@pageoff] + OSWriteLittleInt32(&buffer[20], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup6)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + ld::Fixup _fixup6; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, helperHelper(pass)), + _fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x18000050); // LDR W16, L0 + OSWriteLittleInt32(&buffer[4], 0, 0x14000000); // B helperhelper + OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // L0: .long 0 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(24, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, &stubTo), + _fixup2(28, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, lazyPointer), + _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 68; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xa9bf7bfd); // stp fp, lr, [sp, #-16]! + OSWriteLittleInt32(&buffer[ 4], 0, 0x910003fd); // mov fp, sp + OSWriteLittleInt32(&buffer[ 8], 0, 0xa9bf03e1); // stp x1, x0, [sp, #-16]! + OSWriteLittleInt32(&buffer[12], 0, 0xa9bf0be3); // stp x3, x2, [sp, #-16]! + OSWriteLittleInt32(&buffer[16], 0, 0xa9bf13e5); // stp x5, x4, [sp, #-16]! + OSWriteLittleInt32(&buffer[20], 0, 0xa9bf1be7); // stp x7, x6, [sp, #-16]! + OSWriteLittleInt32(&buffer[24], 0, 0x94000000); // bl _foo + OSWriteLittleInt32(&buffer[28], 0, 0x90000010); // adrp x16, lazy_pointer@PAGE + OSWriteLittleInt32(&buffer[32], 0, 0x91000210); // add x16, x16, lazy_pointer@PAGEOFF + OSWriteLittleInt32(&buffer[36], 0, 0xf9000200); // str x0, [x16] + OSWriteLittleInt32(&buffer[40], 0, 0xaa0003f0); // mov x16, x0 + OSWriteLittleInt32(&buffer[44], 0, 0xa8c11be7); // ldp x7, x6, [sp], #16 + OSWriteLittleInt32(&buffer[48], 0, 0xa8c113e5); // ldp x5, x4, [sp], #16 + OSWriteLittleInt32(&buffer[52], 0, 0xa8c10be3); // ldp x3, x2, [sp], #16 + OSWriteLittleInt32(&buffer[56], 0, 0xa8c103e1); // ldp x1, x0, [sp], #16 + OSWriteLittleInt32(&buffer[60], 0, 0xa8c17bfd); // ldp fp, lr, [sp], #16 + OSWriteLittleInt32(&buffer[64], 0, 0xd61f0200); // br x16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + stubToResolver ? &_resolverHelper : + (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer), + _fixup3(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 0, 4) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, lazy_pointer@page + OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, lazy_pointer@pageoff] + OSWriteLittleInt32(&buffer[8], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + + +class NonLazyPointerAtom : public ld::Atom { +public: + NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) { + pass.addAtom(*this); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + + static ld::Section _s_section; +}; + +ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +class KextStubAtom : public ld::Atom { +public: + KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _nonLazyPointer(pass, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_nonLazyPointer), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_nonLazyPointer) { + pass.addAtom(*this); + asprintf((char**)&_name, "%s.stub", _stubTo.name()); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + 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 { + OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, non_lazy_pointer@page + OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, non_lazy_pointer@pageoff] + OSWriteLittleInt32(&buffer[8], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + const char* _name; + NonLazyPointerAtom _nonLazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeCode); + + +} // namespace x86_64 + diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index 5ea1b05..c01f3f9 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -35,8 +35,8 @@ #include #include "Options.h" -#include "ld.hpp" #include "MachOFileAbstraction.hpp" +#include "ld.hpp" #include "make_stubs.h" @@ -90,8 +90,9 @@ class Pass { #include "stub_x86_classic.hpp" #include "stub_arm.hpp" #include "stub_arm_classic.hpp" - - +#if SUPPORT_ARCH_arm64 +#include "stub_arm64.hpp" +#endif Pass::Pass(const Options& opts) : compressedHelperHelper(NULL), @@ -119,6 +120,9 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif assert(target != NULL); // create stub if target is in a dylib if ( target->definition() == ld::Atom::definitionProxy ) @@ -216,6 +220,14 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport); } break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) + return new ld::passes::stubs::arm64::KextStubAtom(*this, target); + else + return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + break; #endif } throw "unsupported arch for stub"; diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index 6a58fdf..e84fc25 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/src/ld/passes/tlvp.cpp @@ -142,6 +142,10 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: case ld::Fixup::kindStoreX86PCRel32TLVLoad: case ld::Fixup::kindStoreX86Abs32TLVLoad: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: +#endif ref.fixupWithTLVStore = fit; break; default: @@ -230,6 +234,14 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreX86Abs32TLVLoad: it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA; break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21; + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12; + break; +#endif default: assert(0 && "bad store kind for TLV optimization"); } diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index cc673a3..c9eb46c 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -804,6 +804,39 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbHigh16: printf(", then store high-16 in Thumb movt"); break; + case ld::Fixup::kindStoreARM64Branch26: + printf(", then store as ARM64 26-bit pcrel branch"); + break; + case ld::Fixup::kindStoreARM64Page21: + printf(", then store as ARM64 21-bit pcrel ADRP"); + break; + case ld::Fixup::kindStoreARM64PageOff12: + printf(", then store as ARM64 12-bit offset"); + break; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of GOT"); + break; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + printf(", then store as ARM64 12-bit page offset of GOT"); + break; + case ld::Fixup::kindStoreARM64GOTLeaPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of GOT lea"); + break; + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + printf(", then store as ARM64 12-bit page offset of GOT lea"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of TLVP"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + printf(", then store as ARM64 12-bit page offset of TLVP"); + break; + case ld::Fixup::kindStoreARM64PointerToGOT: + printf(", then store as 64-bit pointer to GOT entry"); + break; + case ld::Fixup::kindStoreARM64PCRelToGOT: + printf(", then store as 32-bit delta to GOT entry"); + break; case ld::Fixup::kindDtraceExtra: printf("dtrace static probe extra info"); break; @@ -825,12 +858,21 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: printf("Thumb dtrace static is-enabled site"); break; + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + printf("ARM64 dtrace static probe site"); + break; + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + printf("ARM64 dtrace static is-enabled site"); + break; case ld::Fixup::kindLazyTarget: printf("lazy reference to external symbol %s", referenceTargetAtomName(ref)); break; case ld::Fixup::kindSetLazyOffset: printf("offset of lazy binding info for %s", referenceTargetAtomName(ref)); break; + case ld::Fixup::kindIslandTarget: + printf("ultimate target of island %s", referenceTargetAtomName(ref)); + break; case ld::Fixup::kindDataInCodeStartData: printf("start of data in code"); break; @@ -849,6 +891,46 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindDataInCodeEnd: printf("end of data in code"); break; + case ld::Fixup::kindLinkerOptimizationHint: +#if SUPPORT_ARCH_arm64 + ld::Fixup::LOH_arm64 extra; + extra.addend = ref->u.addend; + printf("ARM64 hint: "); + switch(extra.info.kind) { + case LOH_ARM64_ADRP_ADRP: + printf("ADRP-ADRP"); + break; + case LOH_ARM64_ADRP_LDR: + printf("ADRP-LDR"); + break; + case LOH_ARM64_ADRP_ADD_LDR: + printf("ADRP-ADD-LDR"); + break; + case LOH_ARM64_ADRP_LDR_GOT_LDR: + printf("ADRP-LDR-GOT-LDR"); + break; + case LOH_ARM64_ADRP_ADD_STR: + printf("ADRP-ADD-STR"); + break; + case LOH_ARM64_ADRP_LDR_GOT_STR: + printf("ADRP-LDR-GOT-STR"); + break; + case LOH_ARM64_ADRP_ADD: + printf("ADRP-ADD"); + break; + default: + printf("kind=%d", extra.info.kind); + break; + } + printf(", offset1=0x%X", (extra.info.delta1 << 2) + ref->offsetInAtom); + if ( extra.info.count > 0 ) + printf(", offset2=0x%X", (extra.info.delta2 << 2) + ref->offsetInAtom); + if ( extra.info.count > 1 ) + printf(", offset3=0x%X", (extra.info.delta3 << 2) + ref->offsetInAtom); + if ( extra.info.count > 2 ) + printf(", offset4=0x%X", (extra.info.delta4 << 2) + ref->offsetInAtom); +#endif + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref)); break; @@ -898,6 +980,34 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: printf("tlv template offset of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + printf("ARM64 store 26-bit pcrel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64Page21: + printf("ARM64 store 21-bit pcrel ADRP to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + printf("ARM64 store 12-bit page offset of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPage21: + printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPageOff12: + printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + printf("ARM64 store 21-bit pcrel ADRP to GOT for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + printf("ARM64 store 12-bit page offset of GOT of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + printf("ARM64 store 21-bit pcrel ADRP for lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + printf("ARM64 store 12-bit page offset of lea of %s", referenceTargetAtomName(ref)); + break; //default: // printf("unknown fixup"); // break; @@ -1116,7 +1226,10 @@ static ld::relocatable::File* createReader(const char* path) objOpts.architecture = sPreferredArch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = true; + objOpts.keepDwarfUnwind = false; + objOpts.forceDwarfConversion = false; + objOpts.verboseOptimizationHints = true; objOpts.subType = sPreferredSubArch; #if 1 if ( ! foundFatSlice ) { @@ -1133,7 +1246,7 @@ static ld::relocatable::File* createReader(const char* path) return objResult; // see if it is an llvm object file - objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false); + objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false, true); if ( objResult != NULL ) return objResult; diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 71f13b8..6ac3311 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -252,6 +252,26 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) } #endif +#if SUPPORT_ARCH_arm64 +template <> +bool DyldInfoPrinter::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_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} +#endif + template DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch) : fHeader(NULL), fLength(fileLength), @@ -612,7 +632,7 @@ void DyldInfoPrinter::printRebaseInfo() uint64_t segOffset = 0; uint32_t count; uint32_t skip; - int segIndex; + int segIndex = 0; pint_t segStartAddr = 0; const char* segName = "??"; const char* typeName = "??"; @@ -1267,13 +1287,14 @@ void DyldInfoPrinter::printExportInfo() const bool reExport = (it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT); const bool weakDef = (it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); const bool threadLocal = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL); + const bool abs = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE); const bool resolver = (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); if ( reExport ) printf("[re-export] "); else printf("0x%08llX ", fBaseAddress+it->address); printf("%s", it->name); - if ( weakDef || threadLocal || resolver ) { + if ( weakDef || threadLocal || resolver || abs ) { bool needComma = false; printf(" ["); if ( weakDef ) { @@ -1286,6 +1307,12 @@ void DyldInfoPrinter::printExportInfo() printf("per-thread"); needComma = true; } + if ( abs ) { + if ( needComma ) + printf(", "); + printf("absolute"); + needComma = true; + } if ( resolver ) { if ( needComma ) printf(", "); @@ -1312,7 +1339,7 @@ void DyldInfoPrinter::processExportGraphNode(const uint8_t* const start, cons char* cummulativeString, int curStrOffset) { const uint8_t* const me = p; - const uint8_t terminalSize = read_uleb128(p, end); + const uint64_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { uint32_t flags = read_uleb128(p, end); @@ -1432,6 +1459,8 @@ void DyldInfoPrinter::printExportInfoNodes() printf("[addr=0x%06llX] ", address); else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL) printf("[flags=THREAD_LOCAL addr=0x%06llX] ", address); + else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE) + printf("[flags=ABSOLUTE addr=0x%06llX] ", address); else printf("[flags=0x%llX addr=0x%06llX] ", flags, address); } @@ -1467,7 +1496,12 @@ const uint8_t* DyldInfoPrinter::printSharedRegionInfoForEachULEB128Address(co kindStr = "64-bit pointer"; break; case 3: - kindStr = "ppc hi16"; +#if SUPPORT_ARCH_arm64 + if ( fHeader->cputype() == CPU_TYPE_ARM64 ) + kindStr = "arm64 ADRP"; + else +#endif + kindStr = "ppc hi16"; break; case 4: kindStr = "32-bit offset to IMPORT"; @@ -1751,7 +1785,6 @@ x86::P::uint_t DyldInfoPrinter::relocBase() template <> x86_64::P::uint_t DyldInfoPrinter::relocBase() { - // check for split-seg return fFirstWritableSegment->vmaddr(); } @@ -1766,6 +1799,13 @@ arm::P::uint_t DyldInfoPrinter::relocBase() } #endif +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t DyldInfoPrinter::relocBase() +{ + return fFirstWritableSegment->vmaddr(); +} +#endif template <> const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) @@ -1818,6 +1858,16 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) } #endif +#if SUPPORT_ARCH_arm64 +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == ARM64_RELOC_UNSIGNED ) + return "pointer"; + return "??"; +} +#endif + template void DyldInfoPrinter::printRelocRebaseInfo() { @@ -2114,6 +2164,14 @@ static void dump(const char* path) else throw "in universal file, arm slice does not contain arm mach-o"; break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); + else + throw "in universal file, arm64 slice does not contain arm mach-o"; + break; #endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); @@ -2137,6 +2195,11 @@ static void dump(const char* path) else if ( DyldInfoPrinter::validFile(p) ) { DyldInfoPrinter::make(p, length, path, false); } +#endif +#if SUPPORT_ARCH_arm64 + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path, false); + } #endif else { throw "not a known file type"; @@ -2187,6 +2250,10 @@ int main(int argc, const char* argv[]) sPreferredArch = CPU_TYPE_I386; else if ( strcmp(arch, "x86_64") == 0 ) sPreferredArch = CPU_TYPE_X86_64; +#if SUPPORT_ARCH_arm64 + else if ( strcmp(arch, "arm64") == 0 ) + sPreferredArch = CPU_TYPE_ARM64; +#endif else { if ( arch == NULL ) throw "-arch missing architecture name"; diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index bd585d1..aec6ebe 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -264,11 +264,34 @@ bool MachOChecker::validFile(const uint8_t* fileContent) return false; } +#if SUPPORT_ARCH_arm64 +template <> +bool MachOChecker::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_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} +#endif + template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +#if SUPPORT_ARCH_arm64 +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +#endif template <> @@ -301,9 +324,13 @@ arm::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_comm return threadInfo->thread_register(13); } - - - +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + throw "LC_UNIXTHREAD not supported for arm64"; +} +#endif template <> ppc::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) @@ -335,6 +362,13 @@ arm::P::uint_t MachOChecker::getEntryPoint(const macho_thread_commandthread_register(15); } +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + throw "LC_UNIXTHREAD not supported for arm64"; +} +#endif template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) @@ -447,6 +481,7 @@ void MachOChecker::checkLoadCommands() fDyldInfo = (macho_dyld_info_command

*)cmd; break; case LC_ENCRYPTION_INFO: + case LC_ENCRYPTION_INFO_64: encryption_info = (macho_encryption_info_command

*)cmd; break; case LC_SUB_UMBRELLA: @@ -993,6 +1028,15 @@ arm::P::uint_t MachOChecker::relocBase() return fFirstSegment->vmaddr(); } +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::relocBase() +{ + return fFirstWritableSegment->vmaddr(); +} +#endif + + template bool MachOChecker::addressInWritableSegment(pint_t address) @@ -1107,6 +1151,14 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* r } #endif +#if SUPPORT_ARCH_arm64 +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + throw "external relocations not used for arm64"; +} +#endif + template <> void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) @@ -1184,6 +1236,15 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* relo } #endif +#if SUPPORT_ARCH_arm64 +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + throw "local relocations not used for arm64"; +} +#endif + + template void MachOChecker::checkRelocations() { @@ -1602,6 +1663,11 @@ static void check(const char* path) else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#endif +#if SUPPORT_ARCH_arm64 + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } #endif else { throw "not a known file type"; diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 731f2a3..3da01c5 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -35,7 +35,7 @@ #include #include - +#include "configure.h" #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -99,7 +99,9 @@ class UnwindPrinter template <> const char* UnwindPrinter::archName() { return "i386"; } template <> const char* UnwindPrinter::archName() { return "x86_64"; } template <> const char* UnwindPrinter::archName() { return "arm"; } - +#if SUPPORT_ARCH_arm64 +template <> const char* UnwindPrinter::archName() { return "arm64"; } +#endif template <> bool UnwindPrinter::validFile(const uint8_t* fileContent) @@ -140,6 +142,27 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) } +#if SUPPORT_ARCH_arm64 +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_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + case MH_OBJECT: + return true; + } + return false; +} +#endif + template UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames) : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL), @@ -628,7 +651,84 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, cha } - +#if SUPPORT_ARCH_arm64 +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + uint32_t stackSize; + switch ( encoding & UNWIND_ARM64_MODE_MASK ) { + case UNWIND_ARM64_MODE_FRAMELESS: + stackSize = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); + if ( stackSize == 0 ) + strcpy(str, "no frame, no saved registers "); + else + sprintf(str, "stack size=%d: ", 16 * stackSize); + if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR ) + strcat(str, "x19/20 "); + if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR ) + strcat(str, "x21/22 "); + if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR ) + strcat(str, "x23/24 "); + if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR ) + strcat(str, "x25/26 "); + if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR ) + strcat(str, "x27/28 "); + if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR ) + strcat(str, "d8/9 "); + if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR ) + strcat(str, "d10/11 "); + if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR ) + strcat(str, "d12/13 "); + if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR ) + strcat(str, "d14/15 "); + break; + break; + case UNWIND_ARM64_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET); + break; + case UNWIND_ARM64_MODE_FRAME: + strcpy(str, "std frame: "); + if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR ) + strcat(str, "x19/20 "); + if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR ) + strcat(str, "x21/22 "); + if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR ) + strcat(str, "x23/24 "); + if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR ) + strcat(str, "x25/26 "); + if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR ) + strcat(str, "x27/28 "); + if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR ) + strcat(str, "d8/9 "); + if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR ) + strcat(str, "d10/11 "); + if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR ) + strcat(str, "d12/13 "); + if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR ) + strcat(str, "d14/15 "); + break; + case UNWIND_ARM64_MODE_FRAME_OLD: + strcpy(str, "old frame: "); + if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD ) + strcat(str, "x21/22 "); + if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD ) + strcat(str, "x23/24 "); + if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD ) + strcat(str, "x25/26 "); + if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD ) + strcat(str, "x27/28 "); + if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD ) + strcat(str, "d8/9 "); + if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD ) + strcat(str, "d10/11 "); + if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD ) + strcat(str, "d12/13 "); + if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD ) + strcat(str, "d14/15 "); + break; + } +} +#endif template <> const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) @@ -648,6 +748,17 @@ const char* UnwindPrinter::personalityName(const macho_relocation_info +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() == ARM64_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()]; +} +#endif + template bool UnwindPrinter::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr) { @@ -715,7 +826,6 @@ void UnwindPrinter::printObjectUnwindSection(bool showFunctionNames) printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset); } } - } @@ -789,7 +899,7 @@ void UnwindPrinter::printUnwindSection(bool showFunctionNames) char encodingString[100]; decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : ""; - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n", j, funcOffset, entry[j].encoding(), encodingString, name); } } @@ -827,7 +937,7 @@ void UnwindPrinter::printUnwindSection(bool showFunctionNames) fprintf(stderr, "MISSING LSDA entry for %s\n", name); } } - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n", + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n", j, funcOff, encodingIndex, encoding, encodingString, name); } } @@ -875,6 +985,14 @@ static void dump(const char* path, const std::set& onlyArchs, bool s else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path, showFunctionNames); + else + throw "in universal file, arm64 slice does not contain arm mach-o"; + break; +#endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } @@ -887,6 +1005,11 @@ static void dump(const char* path, const std::set& onlyArchs, bool s else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) { UnwindPrinter::make(p, length, path, showFunctionNames); } +#if SUPPORT_ARCH_arm64 + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) { + UnwindPrinter::make(p, length, path, showFunctionNames); + } +#endif else { throw "not a known file type"; } @@ -913,6 +1036,10 @@ 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); +#if SUPPORT_ARCH_arm64 + else if ( strcmp(arch, "arm64") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM64); +#endif else throwf("unknown architecture %s", arch); } @@ -932,6 +1059,9 @@ int main(int argc, const char* argv[]) if ( onlyArchs.size() == 0 ) { onlyArchs.insert(CPU_TYPE_I386); onlyArchs.insert(CPU_TYPE_X86_64); +#if SUPPORT_ARCH_arm64 + onlyArchs.insert(CPU_TYPE_ARM64); +#endif } // process each file diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 41cf6cf..3f4647d 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -13,7 +13,7 @@ MYDIR=$(shell cd ../../bin;pwd) LD = ld OBJECTDUMP = ObjectDump MACHOCHECK = machocheck -OTOOL = otool +OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/otool REBASE = rebase DYLDINFO = dyldinfo @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path) +IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path 2>/dev/null) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) @@ -125,6 +125,21 @@ else FILEARCH = $(ARCH) endif +ifeq ($(ARCH),arm64) + LDFLAGS := -syslibroot $(IOS_SDK) + CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + #CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + #CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/clang-loh -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 7.0 + OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/otool + #OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool) +else + FILEARCH = $(ARCH) +endif RM = rm RMFLAGS = -rf diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/branch-islands/Makefile index 8e1870b..9d30491 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/branch-islands/Makefile @@ -33,9 +33,9 @@ run: all all: # Verify that we fail if there is no valid place to insert branch islands. - ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} + #${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} - ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} + ${CC} ${CCFLAGS} hello.c space.s extra.c -Os -o hello ${ARCH_FLAGS} ${PASS_IFF_GOOD_MACHO} hello diff --git a/ld64/unit-tests/test-cases/branch-islands/extra.c b/ld64/unit-tests/test-cases/branch-islands/extra.c index a1991fe..3f43392 100644 --- a/ld64/unit-tests/test-cases/branch-islands/extra.c +++ b/ld64/unit-tests/test-cases/branch-islands/extra.c @@ -1,8 +1,10 @@ #include +extern void back(); void foo() { fprintf(stdout, "foo\n"); + back(); } diff --git a/ld64/unit-tests/test-cases/branch-islands/hello.c b/ld64/unit-tests/test-cases/branch-islands/hello.c index 3037663..0738f0e 100644 --- a/ld64/unit-tests/test-cases/branch-islands/hello.c +++ b/ld64/unit-tests/test-cases/branch-islands/hello.c @@ -5,7 +5,10 @@ extern void foo(); int main() { fprintf(stdout, "hello\n"); - foo(); + foo(); return 0; } +void back() +{ +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/branch-islands/space.s b/ld64/unit-tests/test-cases/branch-islands/space.s index 0218bea..a15a2c2 100644 --- a/ld64/unit-tests/test-cases/branch-islands/space.s +++ b/ld64/unit-tests/test-cases/branch-islands/space.s @@ -35,11 +35,11 @@ _prejunk: #if __thumb2__ // thumb2 branches are +/- 16MB _space1: - .space 14*1024*1024 + .space 13*1024*1024 _space2: - .space 14*1024*1024 + .space 13*1024*1024 _space3: - .space 14*1024*1024 + .space 13*1024*1024 #elif __thumb__ diff --git a/ld64/unit-tests/test-cases/branch-long/Makefile b/ld64/unit-tests/test-cases/branch-long/Makefile new file mode 100644 index 0000000..9b41703 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-long/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +# +# test thumb2 branch ranges +# + +run: all + +all: all-${ARCH} + +all-i386: + ${PASS_IFF} true + +all-x86_64: + ${PASS_IFF} true + +all-armv7: + ${CC} ${CCFLAGS} -static foo.c -c + ${CC} ${CCFLAGS} bar.s -c + # verify islands are created and used + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar -nostdlib -Wl,-preload -segaddr __MY 0x3000000 + ${OTOOL} -s __MY __text -V foobar | grep '_myweak1.island:' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar | grep '_foo2.island:' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar | grep '_foo.island:' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar | grep "bl\t_foo.island" | ${FAIL_IF_EMPTY} + # verify that is close enough islands are bypassed and target called directly + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar2 -nostdlib -Wl,-preload -segaddr __MY 0x30000 + ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_foo2' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_myweak1' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm *.o foobar* diff --git a/ld64/unit-tests/test-cases/branch-long/bar.s b/ld64/unit-tests/test-cases/branch-long/bar.s new file mode 100644 index 0000000..528512a --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-long/bar.s @@ -0,0 +1,24 @@ + +#if __arm__ + + .section __MY,__text,regular,pure_instructions + .align 4 + +#if __thumb__ + .thumb_func _bar + .code 16 +#endif + .globl _bar +_bar: + nop + bl _foo + blx _foo2 + bl _myweak1 + + +#endif // __arm__ + + + + .subsections_via_symbols + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/branch-long/foo.c b/ld64/unit-tests/test-cases/branch-long/foo.c new file mode 100644 index 0000000..60e96d2 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-long/foo.c @@ -0,0 +1,25 @@ + +int x = 1; +int y = 2; + +__attribute__((weak)) +void myweak1() +{ +} + +int foo() +{ + myweak1(); + return 1; +} + +int foo1() +{ + return x; +} + +int foo2() +{ + return y; +} + diff --git a/ld64/unit-tests/test-cases/coalesce-force/Makefile b/ld64/unit-tests/test-cases/coalesce-force/Makefile new file mode 100644 index 0000000..92aadd7 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce-force/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2013 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 -force_symbols_coalesce_list +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${DYLDINFO} -weak_bind libfoo.dylib | grep _foo1 | ${FAIL_IF_STDIN} + ${DYLDINFO} -weak_bind libfoo.dylib | grep _wildcheck | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib -Wl,-force_symbols_coalesce_list,foo.exp + ${DYLDINFO} -weak_bind libfoo2.dylib | grep _foo1 | ${FAIL_IF_EMPTY} + ${DYLDINFO} -weak_bind libfoo2.dylib | grep _wildcheck | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + + + +clean: + rm -rf libfoo.dylib libfoo2.dylib diff --git a/ld64/unit-tests/test-cases/coalesce-force/foo.c b/ld64/unit-tests/test-cases/coalesce-force/foo.c new file mode 100644 index 0000000..411d4c0 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce-force/foo.c @@ -0,0 +1,21 @@ + + +void foo1() {} +void foo3() {} + + +__attribute__((weak)) void foo2() {} +__attribute__((weak)) void foo4() {} + + +void wildcheck() {} +void willnot() {} + + + +__attribute__((weak)) void patterncheck() {} +__attribute__((weak)) void patnot() {} + + +void* pointers[] = { &foo1, &foo2, &foo3, &foo4, &wildcheck, &willnot, &patterncheck, &patnot }; + diff --git a/ld64/unit-tests/test-cases/coalesce-force/foo.exp b/ld64/unit-tests/test-cases/coalesce-force/foo.exp new file mode 100644 index 0000000..7268896 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce-force/foo.exp @@ -0,0 +1,2 @@ +_foo1 +_wild* diff --git a/ld64/unit-tests/test-cases/cpu-sub-types/Makefile b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile index f773c11..78a2484 100644 --- a/ld64/unit-tests/test-cases/cpu-sub-types/Makefile +++ b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile @@ -27,6 +27,8 @@ include ${TESTROOT}/include/common.makefile # Validate cpu subtypes processing # +CC_ARM = $(shell xcrun -find clang) -miphoneos-version-min=5.0 -isysroot ${IOS_SDK} + test: test-${ARCH} test-ppc64: @@ -42,31 +44,31 @@ test-armv6: test-arm test-armv7: test-arm test-arm: - clang foo.c -arch armv4t -c -o foo-v4.o + ${CC_ARM} foo.c -arch armv4t -c -o foo-v4.o ${FAIL_IF_BAD_OBJ} foo-v4.o - clang foo.c -arch armv5 -c -o foo-v5.o + ${CC_ARM} foo.c -arch armv5 -c -o foo-v5.o ${FAIL_IF_BAD_OBJ} foo-v5.o - clang foo.c -arch armv6 -c -o foo-v6.o + ${CC_ARM} foo.c -arch armv6 -c -o foo-v6.o ${FAIL_IF_BAD_OBJ} foo-v6.o - clang foo.c -arch armv7 -c -o foo-v7.o + ${CC_ARM} 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 + ${CC_ARM} foo.c -arch xscale -c -o foo-xscale.o ${FAIL_IF_BAD_OBJ} foo-xscale.o - clang main.c -arch armv4t -c -o main-v4.o + ${CC_ARM} main.c -arch armv4t -c -o main-v4.o ${FAIL_IF_BAD_OBJ} main-v4.o - clang main.c -arch armv5 -c -o main-v5.o + ${CC_ARM} main.c -arch armv5 -c -o main-v5.o ${FAIL_IF_BAD_OBJ} main-v5.o - clang main.c -arch armv6 -c -o main-v6.o + ${CC_ARM} main.c -arch armv6 -c -o main-v6.o ${FAIL_IF_BAD_OBJ} main-v6.o - clang main.c -arch xscale -c -o main-xscale.o + ${CC_ARM} 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 + ${CC_ARM} 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 - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY} +# ${LD} -r main-v4.o foo-v4.o -o all.o +# ${FAIL_IF_BAD_OBJ} all.o +# otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY} # check V4+V5 -> V5 #${LD} -r main-v4.o foo-v5.o -o all.o @@ -84,9 +86,9 @@ test-arm: #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} # check V5+V5 -> V5 - ${LD} -r main-v5.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-v5.o foo-v5.o -o all.o +# ${FAIL_IF_BAD_OBJ} all.o +# otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY} # check V5+V6 -> V6 #${LD} -r main-v5.o foo-v6.o -o all.o diff --git a/ld64/unit-tests/test-cases/data-in-code/Makefile b/ld64/unit-tests/test-cases/data-in-code/Makefile index 0897726..b1e7fd5 100644 --- a/ld64/unit-tests/test-cases/data-in-code/Makefile +++ b/ld64/unit-tests/test-cases/data-in-code/Makefile @@ -29,9 +29,12 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} -c test.s -o test.o + ${CC} ${CCFLAGS} test.o -dynamiclib -o libtest.dylib + dyldinfo -arch ${ARCH} -data_in_code libtest.dylib| ${FAIL_IF_EMPTY} ${LD} -r -arch ${ARCH} test.o -o test2.o - #nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} - #${PASS_IFF_GOOD_MACHO} main + ${CC} ${CCFLAGS} test2.o -dynamiclib -o libtest2.dylib + dyldinfo -arch ${ARCH} -data_in_code libtest2.dylib| ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib clean: - rm -rf test.o test2.o + rm -rf test.o test2.o libtest.dylib libtest2.dylib diff --git a/ld64/unit-tests/test-cases/data-in-code/main.c b/ld64/unit-tests/test-cases/data-in-code/main.c deleted file mode 100644 index 811449a..0000000 --- a/ld64/unit-tests/test-cases/data-in-code/main.c +++ /dev/null @@ -1,49 +0,0 @@ - -#include - -#define DTRACE_STRINGIFY(s) #s -#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s) - -#define DTRACE_NOPS \ - "nop" "\n\t" \ - "nop" "\n\t" \ - "nop" "\n\t" - - -#define DTRACE_LAB(p, n) \ - "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n) - -#define DTRACE_LABEL(p, n) \ - ".section __DATA, __data\n\t" \ - ".globl " DTRACE_LAB(p, n) "\n\t" \ - DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \ - ".text" "\n\t" \ - "1:" - -#define DTRACE_CALL(p,n) \ - DTRACE_LABEL(p,n) \ - DTRACE_NOPS - -#define DTRACE_CALL0ARGS(provider, name) \ - __asm volatile ( \ - DTRACE_CALL(provider, name) \ - : \ - : \ - ); - -int deadwood() -{ - DTRACE_CALL0ARGS(__foo__, test2) - return 0; -} - - -int main() { - int a = 1; - - while(a) { - DTRACE_CALL0ARGS(__foo__, test1) - } - - return 0; -} diff --git a/ld64/unit-tests/test-cases/data-in-code/test.s b/ld64/unit-tests/test-cases/data-in-code/test.s index 8dc92b8..19c2448 100644 --- a/ld64/unit-tests/test-cases/data-in-code/test.s +++ b/ld64/unit-tests/test-cases/data-in-code/test.s @@ -4,21 +4,23 @@ _foo: nop nop -l$start$data$1: + .data_region nop nop nop -l$start$jt8$2: + .data_region jt8 nop nop -l$start$jt16$3: +.data_region jt16 nop nop -l$start$code$n4: +.data_region jt32 nop nop - - +.end_data_region + nop + nop + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/dso_handle/Makefile b/ld64/unit-tests/test-cases/dso_handle/Makefile index f6c40e4..d91d17f 100644 --- a/ld64/unit-tests/test-cases/dso_handle/Makefile +++ b/ld64/unit-tests/test-cases/dso_handle/Makefile @@ -15,7 +15,7 @@ all: ${FAIL_IF_BAD_MACHO} test ${CC} ${CCFLAGS} test.c -DDSO_DEF=1 -o test-def 2>warnings.txt ${FAIL_IF_BAD_MACHO} test-def - ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent + ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent -Wl,-w ${PASS_IFF_GOOD_MACHO} test-tent diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile b/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile index 3fb8a25..e40cc53 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile @@ -31,13 +31,12 @@ SHELL = bash # use bash shell so we can redirect just stderr # # comdat warnings in ld -r # -# also use -falign-functions to force an out of order coalesing # run: all all: ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o - ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32 + ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o ${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o ${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log grep warning warnings.log | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s new file mode 100644 index 0000000..b99221b --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s @@ -0,0 +1,27 @@ + + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo@PAGE +L2: add x0, x0, _foo@PAGEOFF + nop + + .loh AdrpAdd L1, L2 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONST + .const +#endif + +#if FOO_AS_DATA + .data +#endif + +_foo: .long 0 + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s new file mode 100644 index 0000000..cdcaba9 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s @@ -0,0 +1,55 @@ + +#ifndef ADDEND + #define ADDEND 0 +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo@PAGE +L2: add x0, x0, _foo@PAGEOFF +#if LOAD_GPR_8 +L3: ldr b1, [x0, #ADDEND] +#elif LOAD_GPR_16 +L3: ldr h1, [x0, #ADDEND] +#elif LOAD_GPR_32 +L3: ldr w1, [x0, #ADDEND] +#elif LOAD_GPR_64 +L3: ldr x1, [x0, #ADDEND] +#elif LOAD_FPR_32 +L3: ldr s1, [x0, #ADDEND] +#elif LOAD_FPR_64 +L3: ldr d1, [x0, #ADDEND] +#elif LOAD_VEC_128 +L3: ldr q1, [x0, #ADDEND] +#endif + nop + + .loh AdrpAddLdr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONSTANT + .literal4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s new file mode 100644 index 0000000..3e3252a --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s @@ -0,0 +1,44 @@ + +#ifndef ADDEND + #define ADDEND 0 +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo@PAGE +L2: add x0, x0, _foo@PAGEOFF +#if LOAD_GPR_8 +L3: str b1, [x0, #ADDEND] +#elif LOAD_GPR_16 +L3: str h1, [x0, #ADDEND] +#elif LOAD_GPR_32 +L3: str w1, [x0, #ADDEND] +#elif LOAD_GPR_64 +L3: str x1, [x0, #ADDEND] +#elif LOAD_FPR_32 +L3: str s1, [x0, #ADDEND] +#elif LOAD_FPR_64 +L3: str d1, [x0, #ADDEND] +#elif LOAD_VEC_128 +L3: str q1, [x0, #ADDEND] +#endif + nop + + .loh AdrpAddStr L1, L2, L3 + + .data +_makePageOffsetNonZero: .long 0,0,0,0 + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s new file mode 100644 index 0000000..c91fa06 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s @@ -0,0 +1,64 @@ + +#ifndef ADDEND + #define ADDEND +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo ADDEND@PAGE +#if LOAD_GPR_8 +L3: ldr b1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_16 +L3: ldr h1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_32 +L3: ldr w1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_32_S16 +L3: ldrsh w1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_32_S8 +L3: ldrsb w1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64 +L3: ldr x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64_S32 +L3: ldrsw x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64_S16 +L3: ldrsh x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64_S8 +L3: ldrsb x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_FPR_32 +L3: ldr s1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_FPR_64 +L3: ldr d1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_VEC_128 +L3: ldr q1, [x0, _foo ADDEND@PAGEOFF] +#endif + nop + + .loh AdrpLdr L1, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONSTANT + .literal4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s new file mode 100644 index 0000000..40bc180 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s @@ -0,0 +1,30 @@ + +#ifndef TARGET + #define TARGET _malloc +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] + nop + + .loh AdrpLdrGot L1, L2 + +#if PADDING +_pad: + .space 1100000 +#endif + +_fooCode: + nop + + + .data +_makePageOffsetNonZero: .long 0,0,0,0 + +_fooData: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s new file mode 100644 index 0000000..0f90ff8 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s @@ -0,0 +1,56 @@ + +#ifndef TARGET + #define TARGET _foo +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] +#if LOAD_GPR_8 +L3: ldr b2, [x1] +#elif LOAD_GPR_16 +L3: ldr h2, [x1] +#elif LOAD_GPR_32 +L3: ldr w2, [x1] +#elif LOAD_GPR_64 +L3: ldr x2, [x1] +#elif LOAD_FPR_32 +L3: ldr s2, [x1] +#elif LOAD_FPR_64 +L3: ldr d2, [x1] +#elif LOAD_VEC_128 +L3: ldr q2, [x1] +#endif + nop + + .loh AdrpLdrGotLdr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONST + .const + .align 4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s new file mode 100644 index 0000000..d3fed03 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s @@ -0,0 +1,49 @@ + +#ifndef TARGET + #define TARGET _foo +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] +#if LOAD_GPR_8 +L3: str b2, [x1] +#elif LOAD_GPR_16 +L3: str h2, [x1] +#elif LOAD_GPR_32 +L3: str w2, [x1] +#elif LOAD_GPR_64 +L3: str x2, [x1] +#elif LOAD_FPR_32 +L3: str s2, [x1] +#elif LOAD_FPR_64 +L3: str d2, [x1] +#elif LOAD_VEC_128 +L3: str q2, [x1] +#endif + nop + + .loh AdrpLdrGotStr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + + .data +_makePageOffsetNonZero: .long 0,0,0,0 + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile b/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile new file mode 100644 index 0000000..de5da0d --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile @@ -0,0 +1,1418 @@ +## +# Copyright (c) 2013 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 linker does/doesnot apply optimization hints +# + +all: all-${ARCH} + +all-i386: skip +all-x86_64: skip +all-armv6: skip +all-armv7: skip + +all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpAddStr AdrpLdrGotStr AdrpLdrGot + +main.o: + ${CC} ${CCFLAGS} main.s -c -o main.o + +AdrpAdd: main.o + # test ADRP/ADD -> ADR when target is in __TEXT + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd1.o -DFOO_AS_CONST=1 + ${CC} ${CCFLAGS} AdrpAdd1.o main.o -o AdrpAdd1.exe + ${OTOOL} -tV AdrpAdd1.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd1.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAdd1.exe | grep 'add x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD -> ADR when target is in __DATA and main executable + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd2.o -DFOO_AS_DATA=1 + ${CC} ${CCFLAGS} AdrpAdd2.o main.o -o AdrpAdd2.exe + ${OTOOL} -tV AdrpAdd2.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd2.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAdd2.exe | grep 'add x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD -> ADR when target is in __DATA and dylib + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd3.o -DFOO_AS_DATA=1 + ${CC} ${CCFLAGS} AdrpAdd3.o -dynamiclib -o AdrpAdd3.dylib + ${OTOOL} -tV AdrpAdd3.dylib | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd3.dylib | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAdd3.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD !-> when target is in __DATA and dylib in shared region + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd4.o -DFOO_AS_DATA=1 + ${CC} ${CCFLAGS} AdrpAdd4.o -dynamiclib -o AdrpAdd4.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAdd4.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd4.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd4.dylib | grep 'adr x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD !-> when target is in __TEXT but too far + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd5.o -DFOO_AS_CONST=1 -DPADDING + ${CC} ${CCFLAGS} AdrpAdd5.o main.o -o AdrpAdd5.exe + ${OTOOL} -tV AdrpAdd5.exe | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd5.exe | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd5.exe | grep 'adr x0' | ${FAIL_IF_STDIN} + + + +AdrpAddLdr: AdrpAddLdr-ldr AdrpAddLdr-far AdrpAddLdr-seg AdrpAddLdr-align AdrpAddLdr-addend + true + +AdrpAddLdr-ldr: main.o + # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g8.o main.o -o AdrpAddLdr-ldr-g8.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g16.o main.o -o AdrpAddLdr-ldr-g16.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g32.o main.o -o AdrpAddLdr-ldr-g32.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'ldr w1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g64.o main.o -o AdrpAddLdr-ldr-g64.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-f32.o main.o -o AdrpAddLdr-ldr-f32.exe + ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'ldr s1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-f64.o main.o -o AdrpAddLdr-ldr-f64.exe + ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'ldr d1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-v128.o main.o -o AdrpAddLdr-ldr-v128.exe + ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'ldr q1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddLdr-far: main.o + # test ADRP/ADD/LD -> ADRP/LD when target is far for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g8.o main.o -o AdrpAddLdr-far-g8.exe + ${OTOOL} -tV AdrpAddLdr-far-g8.exe | grep 'ldr b1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g16.o main.o -o AdrpAddLdr-far-g16.exe + ${OTOOL} -tV AdrpAddLdr-far-g16.exe | grep 'ldr h1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g32.o main.o -o AdrpAddLdr-far-g32.exe + ${OTOOL} -tV AdrpAddLdr-far-g32.exe | grep 'ldr w1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g64.o main.o -o AdrpAddLdr-far-g64.exe + ${OTOOL} -tV AdrpAddLdr-far-g64.exe | grep 'ldr x1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-f32.o main.o -o AdrpAddLdr-far-f32.exe + ${OTOOL} -tV AdrpAddLdr-far-f32.exe | grep 'ldr s1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-f64.o main.o -o AdrpAddLdr-far-f64.exe + ${OTOOL} -tV AdrpAddLdr-far-f64.exe | grep 'ldr d1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-v128.o main.o -o AdrpAddLdr-far-v128.exe + ${OTOOL} -tV AdrpAddLdr-far-v128.exe | grep 'ldr q1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + + +AdrpAddLdr-align: main.o + # test ADRP/ADD/LD -> ADR/LD when target is for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g8.o main.o -o AdrpAddLdr-align-g8.exe + ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g16.o main.o -o AdrpAddLdr-align-g16.exe + ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'ldr h1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g32.o main.o -o AdrpAddLdr-align-g32.exe + ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'ldr w1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g64.o main.o -o AdrpAddLdr-align-g64.exe + ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'ldr x1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-f32.o main.o -o AdrpAddLdr-align-f32.exe + ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'ldr s1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-f64.o main.o -o AdrpAddLdr-align-f64.exe + ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'ldr d1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-v128.o main.o -o AdrpAddLdr-align-v128.exe + ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'ldr q1, \[x0\]' | ${FAIL_IF_EMPTY} + + + +AdrpAddLdr-seg: + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g8.o -dynamiclib -o AdrpAddLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr b1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g16.o -dynamiclib -o AdrpAddLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr h1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g32.o -dynamiclib -o AdrpAddLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr w1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g64.o -dynamiclib -o AdrpAddLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr x1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-f32.o -dynamiclib -o AdrpAddLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr s1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-f64.o -dynamiclib -o AdrpAddLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr d1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segmentfor 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-v128.o -dynamiclib -o AdrpAddLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr q1, \[x0,' | ${FAIL_IF_EMPTY} + + + +AdrpAddLdr-addend: + # test ADRP/ADD/LD -> ADRP/LD when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g8.o main.o -o AdrpAddLdr-addend-g8.exe + ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g16.o main.o -o AdrpAddLdr-addend-g16.exe + ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g32.o main.o -o AdrpAddLdr-addend-g32.exe + ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'ldr w1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g64.o main.o -o AdrpAddLdr-addend-g64.exe + ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'ldr x1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-f32.o main.o -o AdrpAddLdr-addend-f32.exe + ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'ldr s1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-f64.o main.o -o AdrpAddLdr-addend-f64.exe + ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'ldr d1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DADDEND=16 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-v128.o main.o -o AdrpAddLdr-addend-v128.exe + ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'ldr q1, _16foo16' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + + + +AdrpLdr: AdrpLdr-ldr AdrpLdr-seg AdrpLdr-addend + true + +AdrpLdr-ldr: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g8.o main.o -o AdrpLdr-ldr-g8.exe + ${OTOOL} -tV AdrpLdr-ldr-g8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g16.o main.o -o AdrpLdr-ldr-g16.exe + ${OTOOL} -tV AdrpLdr-ldr-g16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g16.exe | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g32.o main.o -o AdrpLdr-ldr-g32.exe + ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'ldr w1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR left untouched when target is in __TEXT for 32-bit load from signed 8-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32s8.o -DFOO_AS_CONST -DLOAD_GPR_32_S8 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g32s8.o main.o -o AdrpLdr-ldr-g32s8.exe + ${OTOOL} -tV AdrpLdr-ldr-g32s8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g32s8.exe | grep 'ldrsb\tw1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load from signed 16-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32s16.o -DFOO_AS_CONST -DLOAD_GPR_32_S16 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g32s16.o main.o -o AdrpLdr-ldr-g32s16.exe + ${OTOOL} -tV AdrpLdr-ldr-g32s16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g32s16.exe | grep 'ldrsh\tw1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64.o main.o -o AdrpLdr-ldr-g64.exe + ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR left untouched when target is in __TEXT for 64-bit load from signed 8-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s8.o -DFOO_AS_CONST -DLOAD_GPR_64_S8 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s8.o main.o -o AdrpLdr-ldr-g64s8.exe + ${OTOOL} -tV AdrpLdr-ldr-g64s8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64s8.exe | grep 'ldrsb\tx1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 64-bit load from signed 16-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s16.o -DFOO_AS_CONST -DLOAD_GPR_64_S16 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s16.o main.o -o AdrpLdr-ldr-g64s16.exe + ${OTOOL} -tV AdrpLdr-ldr-g64s16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64s16.exe | grep 'ldrsh\tx1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load from signed 32-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s32.o -DFOO_AS_CONST -DLOAD_GPR_64_S32 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s32.o main.o -o AdrpLdr-ldr-g64s32.exe + ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'ldrsw x1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdr-ldr-f32.o main.o -o AdrpLdr-ldr-f32.exe + ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'ldr s1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdr-ldr-f64.o main.o -o AdrpLdr-ldr-f64.exe + ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'ldr d1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdr-ldr-v128.o main.o -o AdrpLdr-ldr-v128.exe + ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'ldr q1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + +AdrpLdr-seg: + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdr-seg-g8.o -dynamiclib -o AdrpLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g8.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g8.dylib | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdr-seg-g16.o -dynamiclib -o AdrpLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g16.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g16.dylib | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdr-seg-g32.o -dynamiclib -o AdrpLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g32.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g32.dylib | grep 'ldr\tw1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdr-seg-g64.o -dynamiclib -o AdrpLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g64.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g64.dylib | grep 'ldr\tx1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdr-seg-f32.o -dynamiclib -o AdrpLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-f32.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-f32.dylib | grep 'ldr\ts1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdr-seg-f64.o -dynamiclib -o AdrpLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-f64.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-f64.dylib | grep 'ldr\td1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdr-seg-v128.o -dynamiclib -o AdrpLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-v128.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-v128.dylib | grep 'ldr\tq1, \[x0,' | ${FAIL_IF_EMPTY} + +AdrpLdr-addend: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g8.o main.o -o AdrpLdr-addend-g8.exe + ${OTOOL} -tV AdrpLdr-addend-g8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g8.exe | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g16.o main.o -o AdrpLdr-addend-g16.exe + ${OTOOL} -tV AdrpLdr-addend-g16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g16.exe | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g32.o main.o -o AdrpLdr-addend-g32.exe + ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'ldr w1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g64.o main.o -o AdrpLdr-addend-g64.exe + ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'ldr x1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-f32.o main.o -o AdrpLdr-addend-f32.exe + ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'ldr s1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-f64.o main.o -o AdrpLdr-addend-f64.exe + ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'ldr d1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 -DADDEND=+16 + ${CC} ${CCFLAGS} AdrpLdr-addend-v128.o main.o -o AdrpLdr-addend-v128.exe + ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'ldr q1, _16foo16' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpLdrGotLdr: AdrpLdrGotLdr-extern AdrpLdrGotLdr-externfargot AdrpLdrGotLdr-near AdrpLdrGotLdr-far AdrpLdrGotLdr-nearunaligned AdrpLdrGotLdr-farunaligned + true + +AdrpLdrGotLdr-extern: main.o + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g8.o main.o -o AdrpLdrGotLdr-extern-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g16.o main.o -o AdrpLdrGotLdr-extern-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g32.o main.o -o AdrpLdrGotLdr-extern-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g64.o main.o -o AdrpLdrGotLdr-extern-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-f32.o main.o -o AdrpLdrGotLdr-extern-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-f64.o main.o -o AdrpLdrGotLdr-extern-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-v128.o main.o -o AdrpLdrGotLdr-extern-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-externfargot: main.o + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g8.o main.o -o AdrpLdrGotLdr-externfargot-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g16.o main.o -o AdrpLdrGotLdr-externfargot-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot farfor 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g32.o main.o -o AdrpLdrGotLdr-externfargot-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g64.o main.o -o AdrpLdrGotLdr-externfargot-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-f32.o main.o -o AdrpLdrGotLdr-externfargot-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-f64.o main.o -o AdrpLdrGotLdr-externfargot-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-v128.o main.o -o AdrpLdrGotLdr-externfargot-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-near: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g8.o main.o -o AdrpLdrGotLdr-near-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g16.o main.o -o AdrpLdrGotLdr-near-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g32.o main.o -o AdrpLdrGotLdr-near-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'ldr\tw2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g64.o main.o -o AdrpLdrGotLdr-near-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'ldr\tx2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-f32.o main.o -o AdrpLdrGotLdr-near-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'ldr\ts2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-f64.o main.o -o AdrpLdrGotLdr-near-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'ldr\td2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-v128.o main.o -o AdrpLdrGotLdr-near-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'ldr\tq2, _foo' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-nearunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g8.o main.o -o AdrpLdrGotLdr-nearunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g16.o main.o -o AdrpLdrGotLdr-nearunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g32.o main.o -o AdrpLdrGotLdr-nearunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g64.o main.o -o AdrpLdrGotLdr-nearunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-f32.o main.o -o AdrpLdrGotLdr-nearunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-f64.o main.o -o AdrpLdrGotLdr-nearunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-v128.o main.o -o AdrpLdrGotLdr-nearunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-far: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g8.o main.o -o AdrpLdrGotLdr-far-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'ldr\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g16.o main.o -o AdrpLdrGotLdr-far-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'ldr\th2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g32.o main.o -o AdrpLdrGotLdr-far-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'ldr\tw2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g64.o main.o -o AdrpLdrGotLdr-far-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'ldr\tx2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-f32.o main.o -o AdrpLdrGotLdr-far-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'ldr\ts2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-f64.o main.o -o AdrpLdrGotLdr-far-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'ldr\td2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-v128.o main.o -o AdrpLdrGotLdr-far-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'ldr\tq2, \[x0,' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-farunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g8.o main.o -o AdrpLdrGotLdr-farunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'ldr\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g16.o main.o -o AdrpLdrGotLdr-farunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g32.o main.o -o AdrpLdrGotLdr-farunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g64.o main.o -o AdrpLdrGotLdr-farunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-f32.o main.o -o AdrpLdrGotLdr-farunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-f64.o main.o -o AdrpLdrGotLdr-farunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-v128.o main.o -o AdrpLdrGotLdr-farunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpAddStr: AdrpAddStr-base AdrpAddStr-seg AdrpAddStr-align AdrpAddStr-addend AdrpAddStr-faraddend + true + +AdrpAddStr-base: main.o + # test ADRP/ADD/STR -> ADR/STR for 8-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g8.o -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddStr-base-g8.o main.o -o AdrpAddStr-base-g8.exe + ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 16-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g16.o -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddStr-base-g16.o main.o -o AdrpAddStr-base-g16.exe + ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'str\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 32-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g32.o -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-base-g32.o main.o -o AdrpAddStr-base-g32.exe + ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'str\tw1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 64-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g64.o -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-base-g64.o main.o -o AdrpAddStr-base-g64.exe + ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'str\tx1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 32-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-f32.o -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-base-f32.o main.o -o AdrpAddStr-base-f32.exe + ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'str\ts1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 64-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-f64.o -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-base-f64.o main.o -o AdrpAddStr-base-f64.exe + ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'str\td1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 128-bit vec store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-v128.o -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddStr-base-v128.o main.o -o AdrpAddStr-base-v128.exe + ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'str\tq1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddStr-seg: main.o + # test ADRP/ADD/STR -> ADRP/STR for 8-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g8.o -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g8.o -o AdrpAddStr-seg-g8.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'str\tb1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 16-bit v + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g16.o -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g16.o -o AdrpAddStr-seg-g16.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'str\th1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 32-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g32.o -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g32.o -o AdrpAddStr-seg-g32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'str\tw1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 64-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g64.o -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g64.o -o AdrpAddStr-seg-g64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'str\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 32-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-f32.o -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-seg-f32.o -o AdrpAddStr-seg-f32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'str\ts1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 64-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-f64.o -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-seg-f64.o -o AdrpAddStr-seg-f64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'str\td1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 128-bit vec store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-v128.o -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddStr-seg-v128.o -o AdrpAddStr-seg-v128.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'str\tq1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddStr-align: main.o + # test ADRP/ADD/STR -> ADR/STR when target is for 8-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g8.o main.o -o AdrpAddStr-align-g8.exe + ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 16-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g16.o main.o -o AdrpAddStr-align-g16.exe + ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'str h1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 32-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g32.o main.o -o AdrpAddStr-align-g32.exe + ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'str w1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 64-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g64.o main.o -o AdrpAddStr-align-g64.exe + ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'str x1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-f32.o main.o -o AdrpAddStr-align-f32.exe + ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'str s1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-f64.o main.o -o AdrpAddStr-align-f64.exe + ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'str d1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-v128.o main.o -o AdrpAddStr-align-v128.exe + ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'str q1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + + +AdrpAddStr-addend: + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g8.o -DLOAD_GPR_8 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g8.o main.o -o AdrpAddStr-addend-g8.exe + ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g16.o -DLOAD_GPR_16 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g16.o main.o -o AdrpAddStr-addend-g16.exe + ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'str\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g32.o -DLOAD_GPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g32.o main.o -o AdrpAddStr-addend-g32.exe + ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'str\tw1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g64.o -DLOAD_GPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g64.o main.o -o AdrpAddStr-addend-g64.exe + ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'str\tx1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-f32.o -DLOAD_FPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-f32.o main.o -o AdrpAddStr-addend-f32.exe + ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'str\ts1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-f64.o -DLOAD_FPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-f64.o main.o -o AdrpAddStr-addend-f64.exe + ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'str\td1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-v128.o -DLOAD_VEC_128 -DADDEND=16 + ${CC} ${CCFLAGS} AdrpAddStr-addend-v128.o main.o -o AdrpAddStr-addend-v128.exe + ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'str\tq1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddStr-faraddend: + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g8.o -DLOAD_GPR_8 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g8.o -o AdrpAddStr-faraddend-g8.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'str\tb1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g16.o -DLOAD_GPR_16 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g16.o -o AdrpAddStr-faraddend-g16.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'str\th1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g32.o -DLOAD_GPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g32.o -o AdrpAddStr-faraddend-g32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'str\tw1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g64.o -DLOAD_GPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g64.o -o AdrpAddStr-faraddend-g64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'str\tx1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-f32.o -DLOAD_FPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-f32.o -o AdrpAddStr-faraddend-f32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'str\ts1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-f64.o -DLOAD_FPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-f64.o -o AdrpAddStr-faraddend-f64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'str\td1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-v128.o -DLOAD_VEC_128 -DADDEND=16 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-v128.o -o AdrpAddStr-faraddend-v128.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'str\tq1, \[x0' | ${FAIL_IF_EMPTY} + + + +AdrpLdrGotStr: AdrpLdrGotStr-extern AdrpLdrGotStr-externfargot AdrpLdrGotStr-near AdrpLdrGotStr-far AdrpLdrGotStr-nearunaligned AdrpLdrGotStr-farunaligned + true + +AdrpLdrGotStr-extern: main.o + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g8.o main.o -o AdrpLdrGotStr-extern-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g16.o main.o -o AdrpLdrGotStr-extern-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g32.o main.o -o AdrpLdrGotStr-extern-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g64.o main.o -o AdrpLdrGotStr-extern-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-f32.o main.o -o AdrpLdrGotStr-extern-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-f64.o main.o -o AdrpLdrGotStr-extern-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-v128.o main.o -o AdrpLdrGotStr-extern-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-externfargot: main.o + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g8.o main.o -o AdrpLdrGotStr-externfargot-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g16.o main.o -o AdrpLdrGotStr-externfargot-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot farfor 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g32.o main.o -o AdrpLdrGotStr-externfargot-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g64.o main.o -o AdrpLdrGotStr-externfargot-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-f32.o main.o -o AdrpLdrGotStr-externfargot-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-f64.o main.o -o AdrpLdrGotStr-externfargot-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-v128.o main.o -o AdrpLdrGotStr-externfargot-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-near: main.o + # test ADRP/LDR/STR -> ADR/STR when target close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g8.o -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g8.o main.o -o AdrpLdrGotStr-near-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g16.o -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g16.o main.o -o AdrpLdrGotStr-near-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g32.o -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g32.o main.o -o AdrpLdrGotStr-near-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g64.o -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g64.o main.o -o AdrpLdrGotStr-near-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-f32.o -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-f32.o main.o -o AdrpLdrGotStr-near-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-f64.o -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-f64.o main.o -o AdrpLdrGotStr-near-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-v128.o -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-v128.o main.o -o AdrpLdrGotStr-near-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-nearunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g8.o main.o -o AdrpLdrGotStr-nearunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g16.o main.o -o AdrpLdrGotStr-nearunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g32.o main.o -o AdrpLdrGotStr-nearunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g64.o main.o -o AdrpLdrGotStr-nearunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-f32.o main.o -o AdrpLdrGotStr-nearunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-f64.o main.o -o AdrpLdrGotStr-nearunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-v128.o main.o -o AdrpLdrGotStr-nearunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-far: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g8.o main.o -o AdrpLdrGotStr-far-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'str\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g16.o main.o -o AdrpLdrGotStr-far-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'str\th2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g32.o main.o -o AdrpLdrGotStr-far-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'str\tw2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g64.o main.o -o AdrpLdrGotStr-far-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'str\tx2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-f32.o main.o -o AdrpLdrGotStr-far-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'str\ts2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-f64.o main.o -o AdrpLdrGotStr-far-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'str\td2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-v128.o main.o -o AdrpLdrGotStr-far-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'str\tq2, \[x0,' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-farunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g8.o main.o -o AdrpLdrGotStr-farunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'str\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g16.o main.o -o AdrpLdrGotStr-farunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g32.o main.o -o AdrpLdrGotStr-farunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g64.o main.o -o AdrpLdrGotStr-farunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-f32.o main.o -o AdrpLdrGotStr-farunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-f64.o main.o -o AdrpLdrGotStr-farunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-v128.o main.o -o AdrpLdrGotStr-farunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGot: main.o + # test ADRP/LDR left untouched when target is extern and GOT is far + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-externfar.o -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGot-externfar.o main.o -o AdrpLdrGot-externfar.exe + ${OTOOL} -tV AdrpLdrGot-externfar.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGot-externfar.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> NOP/LDR literal when target is exern and GOT is near + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-externnear.o + ${CC} ${CCFLAGS} AdrpLdrGot-externnear.o main.o -o AdrpLdrGot-externnear.exe + ${OTOOL} -tV AdrpLdrGot-externnear.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGot-externnear.exe | grep 'ldr\tx1' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> ADR when target is local, near code + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localnear.o -DTARGET=_fooCode + ${CC} ${CCFLAGS} AdrpLdrGot-localnear.o main.o -o AdrpLdrGot-localnear.exe + ${OTOOL} -tV AdrpLdrGot-localnear.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGot-localnear.exe | grep 'adr\tx1' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> ADR when target is local, near data + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localnear2.o -DTARGET=_fooData + ${CC} ${CCFLAGS} AdrpLdrGot-localnear2.o main.o -o AdrpLdrGot-localnear2.exe + ${OTOOL} -tV AdrpLdrGot-localnear2.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGot-localnear2.exe | grep 'adr\tx1' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> ADR when target is local, far code + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localfar.o -DTARGET=_fooCode -DPADDING=1 + ${CC} ${CCFLAGS} AdrpLdrGot-localfar.o main.o -o AdrpLdrGot-localfar.exe + ${OTOOL} -tV AdrpLdrGot-localfar.exe | grep 'adrp\tx1' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGot-localfar.exe | grep 'add\tx1' | ${FAIL_IF_EMPTY} + + + +clean: + rm -f *.o *.exe *.dylib diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/main.s b/ld64/unit-tests/test-cases/linker-optimization-hints/main.s new file mode 100644 index 0000000..762b4e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/main.s @@ -0,0 +1,6 @@ + + .text + .align 2 + .globl _main +_main: ret lr + diff --git a/ld64/unit-tests/test-cases/linker_options-framework/Makefile b/ld64/unit-tests/test-cases/linker_options-framework/Makefile new file mode 100644 index 0000000..3ac482e --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-framework/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 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 linker options work for -framework +# + +run: all + +all: + mkdir -p Foo.framework + ${CC} ${CCFLAGS} foo.c -dynamiclib -o Foo.framework/Foo + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option '-framework Foo' -o main2.o + ${CC} ${CCFLAGS} main2.o -o main -F. + ${DYLDINFO} -lazy_bind main | grep _foo | grep Foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main libfoo.dylib main.o main2.o Foo.framework + diff --git a/ld64/unit-tests/test-cases/linker_options-framework/foo.c b/ld64/unit-tests/test-cases/linker_options-framework/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-framework/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/ld64/unit-tests/test-cases/linker_options-framework/main.c b/ld64/unit-tests/test-cases/linker_options-framework/main.c new file mode 100644 index 0000000..4f56fe0 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-framework/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/linker_options-library/Makefile b/ld64/unit-tests/test-cases/linker_options-library/Makefile new file mode 100644 index 0000000..93697e5 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2013 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 linker options work for -l +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static bar.o -o libbar.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option -lfoo -add_linker_option -lbar -o main2.o + ${CC} ${CCFLAGS} main2.o -o main -L. + ${DYLDINFO} -lazy_bind main | grep _foo | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main libfoo.dylib main.o main2.o bar.o libbar.a + diff --git a/ld64/unit-tests/test-cases/linker_options-library/bar.c b/ld64/unit-tests/test-cases/linker_options-library/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/unit-tests/test-cases/linker_options-library/foo.c b/ld64/unit-tests/test-cases/linker_options-library/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/ld64/unit-tests/test-cases/linker_options-library/main.c b/ld64/unit-tests/test-cases/linker_options-library/main.c new file mode 100644 index 0000000..594cf17 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/main.c @@ -0,0 +1,10 @@ + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/lto-dynamic_export/Makefile b/ld64/unit-tests/test-cases/lto-dynamic_export/Makefile new file mode 100644 index 0000000..416c5a9 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dynamic_export/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -preload -pie produces relocations +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -o main + nm -g main | grep _bar | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} main.o -o main-de -Wl,-export_dynamic + nm -g main-de | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main-de + + +clean: + rm main.o main main-de diff --git a/ld64/unit-tests/test-cases/lto-dynamic_export/main.c b/ld64/unit-tests/test-cases/lto-dynamic_export/main.c new file mode 100644 index 0000000..bb3ced0 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dynamic_export/main.c @@ -0,0 +1,14 @@ + +__attribute__((visibility("hidden"))) +void foo() { } + +void bar() { } + + +int main() +{ + foo(); + bar(); + + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/objc-abi/Makefile b/ld64/unit-tests/test-cases/objc-abi/Makefile index d7eb660..d563cd9 100644 --- a/ld64/unit-tests/test-cases/objc-abi/Makefile +++ b/ld64/unit-tests/test-cases/objc-abi/Makefile @@ -35,12 +35,12 @@ endif run: ${ALL} all: - ${PASS_IFF_GOOD_MACHO} /usr/bin/true + ${PASS_IFF} /usr/bin/true all-i386: - ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 + ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 -Wno-objc-root-class size -l test1 | grep __image_info | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} test.m -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 + ${CC} ${CCFLAGS} test.m -Wno-objc-root-class -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} test2 diff --git a/ld64/unit-tests/test-cases/objc-abi/test.m b/ld64/unit-tests/test-cases/objc-abi/test.m index 8b4a295..292c456 100644 --- a/ld64/unit-tests/test-cases/objc-abi/test.m +++ b/ld64/unit-tests/test-cases/objc-abi/test.m @@ -1,5 +1,5 @@ -@interface Foo +@interface Foo @end @implementation Foo diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile index 2bbc5dd..7dcafac 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch + OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ endif all: all-${ARCH} diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile index c395988..de4ea0c 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch + OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ endif ifeq ($(ARCH),arm) diff --git a/ld64/unit-tests/test-cases/objc-category-warning/Makefile b/ld64/unit-tests/test-cases/objc-category-warning/Makefile index 5481ba0..31d841b 100644 --- a/ld64/unit-tests/test-cases/objc-category-warning/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-warning/Makefile @@ -30,7 +30,7 @@ SHELL = bash # use bash shell so we can redirect just stderr OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch + OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ endif all: all-${FILEARCH} diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile index 67e04a3..13d1743 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile @@ -63,70 +63,70 @@ test-macosx: ${FAIL_IF_BAD_OBJ} bar-gc-only.o # check RR + RR -> RR - ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib # check GC/RR + GC/RR -> GC/RR - ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY} # check GC + GC -> GC - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} # check RR + GC/RR -> RR - ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} # check GC/RR + RR -> RR - ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} # check GC + GC/RR -> GC - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} # check RR + GC -> error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log # check cmd line GC/RR, GC/RR + RR -> error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc 2> fail.log + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc -framework Foundation 2> fail.log # check GC/RR + compaction - ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12 | ${FAIL_IF_EMPTY} # check GC + compaction - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16 | ${FAIL_IF_EMPTY} # none + GC/RR-dylib -> none - ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # none + GC-dylib -> none - ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # none + RR-dylib -> none - ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # check RR + GC-dylib -> error - ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log # check GC + RR-dylib -> error - ${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 + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log ${PASS_IFF} true diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/bar.m b/ld64/unit-tests/test-cases/objc-gc-checks/bar.m index 5c98709..f66df50 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/bar.m +++ b/ld64/unit-tests/test-cases/objc-gc-checks/bar.m @@ -1,5 +1,6 @@ +#include -@interface Bar { +@interface Bar : NSObject { int f; } - (void) doit; diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/foo.m b/ld64/unit-tests/test-cases/objc-gc-checks/foo.m index e13367e..819049d 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/foo.m +++ b/ld64/unit-tests/test-cases/objc-gc-checks/foo.m @@ -1,5 +1,6 @@ +#include -@interface Foo { +@interface Foo : NSObject { int f; } - (void) doit; diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m index 4837911..3ead0e6 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m @@ -25,8 +25,10 @@ #include #include +#include -@interface Foo @end +@interface Foo : NSObject +@end @implementation Foo +(void)initialize { } +(void)foo { diff --git a/ld64/unit-tests/test-cases/objc-properties/test.m b/ld64/unit-tests/test-cases/objc-properties/test.m index 25ca8c8..4a568ae 100644 --- a/ld64/unit-tests/test-cases/objc-properties/test.m +++ b/ld64/unit-tests/test-cases/objc-properties/test.m @@ -24,7 +24,7 @@ #include -@interface Test +@interface Test : NSObject { BOOL one; NSString* two; diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx index 7d1ef81..862e46e 100644 --- a/ld64/unit-tests/test-cases/operator-new/main.cxx +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -48,7 +48,7 @@ class Foo { }; void Foo::print() { - printf("%d\n", a); + printf("%d %d\n", a, b); } diff --git a/ld64/unit-tests/test-cases/order_file-archive/Makefile b/ld64/unit-tests/test-cases/order_file-archive/Makefile new file mode 100644 index 0000000..eb77525 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify order files can pick file in archives +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o libfoo.a -Wl,-order_file,main.order -o main + ${FAIL_IF_BAD_MACHO} main + nm -n -j main | egrep "_main|_foo" > main.actual + ${PASS_IFF} diff main.actual main.expected + + +clean: + rm -rf *.o libfoo.a main main.actual diff --git a/ld64/unit-tests/test-cases/order_file-archive/foo.c b/ld64/unit-tests/test-cases/order_file-archive/foo.c new file mode 100644 index 0000000..61be976 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/foo.c @@ -0,0 +1,4 @@ +void foo1() { } +void foo2() {} +void foo3() {} + diff --git a/ld64/unit-tests/test-cases/order_file-archive/main.c b/ld64/unit-tests/test-cases/order_file-archive/main.c new file mode 100644 index 0000000..298aedc --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2013 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 + + +extern void foo1(); + +int main() +{ + foo1(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/order_file-archive/main.expected b/ld64/unit-tests/test-cases/order_file-archive/main.expected new file mode 100644 index 0000000..31a7e2f --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/main.expected @@ -0,0 +1,4 @@ +_foo2 +_main +_foo3 +_foo1 diff --git a/ld64/unit-tests/test-cases/order_file-archive/main.order b/ld64/unit-tests/test-cases/order_file-archive/main.order new file mode 100644 index 0000000..195b1d7 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/main.order @@ -0,0 +1,5 @@ +_foo2 +_main +libfoo.a(foo.o):_foo3 +_foo1 + diff --git a/ld64/unit-tests/test-cases/re-export-symbol/Makefile b/ld64/unit-tests/test-cases/re-export-symbol/Makefile index 4da677c..2ad9e0c 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/Makefile +++ b/ld64/unit-tests/test-cases/re-export-symbol/Makefile @@ -44,10 +44,12 @@ all: ${DYLDINFO} -bind -lazy_bind main1 | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} ${DYLDINFO} -bind -lazy_bind main1 | grep _bar_weak | grep libfoo | ${FAIL_IF_EMPTY} - # build library the re-exports _bar from base library as _mybar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp + # build library that re-exports _bar from base library as _mybar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp -DUSE_MY ${FAIL_IF_BAD_MACHO} libfoo2.dylib ${DYLDINFO} -export libfoo2.dylib | grep _mybar | grep 're-export' | grep _bar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _bar | grep libbar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind libfoo2.dylib | grep _bar | grep libbar | ${FAIL_IF_EMPTY} # link against dylib and verify _mybar is marked as coming from libfoo ${CC} ${CCFLAGS} main2.c libfoo2.dylib -o main2 ${DYLDINFO} -bind -lazy_bind main2 | grep _mybar | grep libfoo2 | ${FAIL_IF_EMPTY} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/foo.c b/ld64/unit-tests/test-cases/re-export-symbol/foo.c index 714540a..61981fe 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/foo.c +++ b/ld64/unit-tests/test-cases/re-export-symbol/foo.c @@ -1,4 +1,21 @@ + +#if USE_MY + extern int mybar(); +#else + extern int bar(); +#endif + int foo(void) { - return 1; +#if USE_MY + return mybar() + 1; +#else + return bar() + 1; +#endif } + +#if USE_MY + void* p = &mybar; +#else + void* p = &bar; +#endif diff --git a/ld64/unit-tests/test-cases/relocs-literals/Makefile b/ld64/unit-tests/test-cases/relocs-literals/Makefile index a9ca5ef..d62de34 100644 --- a/ld64/unit-tests/test-cases/relocs-literals/Makefile +++ b/ld64/unit-tests/test-cases/relocs-literals/Makefile @@ -35,7 +35,7 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o + ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o -Wno-array-bounds ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o diff --git a/ld64/unit-tests/test-cases/relocs-literals/test.c b/ld64/unit-tests/test-cases/relocs-literals/test.c index 2d199d0..5939ef4 100644 --- a/ld64/unit-tests/test-cases/relocs-literals/test.c +++ b/ld64/unit-tests/test-cases/relocs-literals/test.c @@ -48,7 +48,7 @@ long double getLongDouble() { return 3.0; } // rdar://problem/4732996 const char* stringFutz(int x) { - return "hello" + 0x1000 + x; + return &"hello"[0x1000 + x]; } -const char* usesAddend = "teststr" + 0x2000; +const char* usesAddend = &"teststr"[0x2000]; diff --git a/ld64/unit-tests/test-cases/relocs-literals2/Makefile b/ld64/unit-tests/test-cases/relocs-literals2/Makefile index 23e4a82..b1820f0 100644 --- a/ld64/unit-tests/test-cases/relocs-literals2/Makefile +++ b/ld64/unit-tests/test-cases/relocs-literals2/Makefile @@ -38,7 +38,7 @@ endif run: all all: - ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o + ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o -Wno-array-bounds ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o diff --git a/ld64/unit-tests/test-cases/relocs-literals2/test.c b/ld64/unit-tests/test-cases/relocs-literals2/test.c index 2d199d0..5939ef4 100644 --- a/ld64/unit-tests/test-cases/relocs-literals2/test.c +++ b/ld64/unit-tests/test-cases/relocs-literals2/test.c @@ -48,7 +48,7 @@ long double getLongDouble() { return 3.0; } // rdar://problem/4732996 const char* stringFutz(int x) { - return "hello" + 0x1000 + x; + return &"hello"[0x1000 + x]; } -const char* usesAddend = "teststr" + 0x2000; +const char* usesAddend = &"teststr"[0x2000]; diff --git a/ld64/unit-tests/test-cases/relocs-objc/test.m b/ld64/unit-tests/test-cases/relocs-objc/test.m index 1ca2157..2b2bf9c 100644 --- a/ld64/unit-tests/test-cases/relocs-objc/test.m +++ b/ld64/unit-tests/test-cases/relocs-objc/test.m @@ -48,7 +48,7 @@ - (void) foo -@interface Base +@interface Base : NSObject @end From 8274337c4aafb9f3a2afe1fbbf823c77e7adee05 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:25:56 +0100 Subject: [PATCH 16/48] 241-9 --- ld64/doc/man/man1/ld.1 | 41 ++- ld64/ld64.xcodeproj/project.pbxproj | 20 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 12 + ld64/src/ld/HeaderAndLoadCommands.hpp | 5 + ld64/src/ld/InputFiles.cpp | 71 ++++- ld64/src/ld/InputFiles.h | 2 +- ld64/src/ld/LinkEditClassic.hpp | 65 ++++- ld64/src/ld/Options.cpp | 264 ++++++++++++++++-- ld64/src/ld/Options.h | 41 ++- ld64/src/ld/OutputFile.cpp | 117 ++++++-- ld64/src/ld/Resolver.cpp | 102 ++++++- ld64/src/ld/Resolver.h | 5 +- ld64/src/ld/SymbolTable.cpp | 80 ++++++ ld64/src/ld/ld.cpp | 218 +++++++++++---- ld64/src/ld/ld.hpp | 22 +- .../parsers/libunwind/DwarfInstructions.hpp | 2 + ld64/src/ld/parsers/libunwind/DwarfParser.hpp | 2 +- ld64/src/ld/parsers/libunwind/Registers.hpp | 32 +++ ld64/src/ld/parsers/lto_file.cpp | 124 +++++--- ld64/src/ld/parsers/lto_file.h | 1 + ld64/src/ld/parsers/macho_dylib_file.cpp | 29 +- .../src/ld/parsers/macho_relocatable_file.cpp | 260 ++++++++++++++--- ld64/src/ld/passes/branch_island.cpp | 31 +- ld64/src/ld/passes/compact_unwind.cpp | 25 +- ld64/src/ld/passes/objc.cpp | 32 ++- ld64/src/ld/passes/order.cpp | 72 +++-- ld64/src/other/ObjectDump.cpp | 28 +- ld64/src/other/unwinddump.cpp | 16 +- ld64/unit-tests/include/common.makefile | 4 +- .../test-cases/alias-basic/Makefile | 47 ++++ .../test-cases/alias-basic/aliases.s | 26 ++ ld64/unit-tests/test-cases/alias-basic/main.c | 16 ++ .../test-cases/alias-objects/Makefile | 14 +- .../test-cases/alias-objects/aliases.s | 13 +- ld64/unit-tests/test-cases/alt-entry/Makefile | 40 +++ ld64/unit-tests/test-cases/alt-entry/foo.c | 7 + ld64/unit-tests/test-cases/alt-entry/main.c | 7 + ld64/unit-tests/test-cases/lto-r/Makefile | 53 ++++ ld64/unit-tests/test-cases/lto-r/bar.c | 1 + ld64/unit-tests/test-cases/lto-r/foo.c | 38 +++ ld64/unit-tests/test-cases/lto-r/main.c | 15 + .../test-cases/lto-rename_section/Makefile | 52 ++++ .../test-cases/lto-rename_section/a.c | 4 + .../test-cases/lto-rename_section/b.c | 1 + .../test-cases/lto-rename_section/main.c | 11 + .../test-cases/lto-rename_segment/Makefile | 52 ++++ .../test-cases/lto-rename_segment/a.c | 4 + .../test-cases/lto-rename_segment/b.c | 1 + .../test-cases/lto-rename_segment/main.c | 18 ++ .../lto-symbol-section-move/Makefile | 60 ++++ .../test-cases/lto-symbol-section-move/foo.c | 38 +++ .../test-cases/lto-symbol-section-move/main.c | 37 +++ .../lto-symbol-section-move/other.c | 9 + .../lto-symbol-section-move/ram1.symbols | 5 + .../lto-symbol-section-move/rom1.symbols | 6 + .../test-cases/preload-section_order/Makefile | 54 ++++ .../test-cases/preload-section_order/extra.s | 15 + .../test-cases/preload-section_order/main.c | 5 + .../preload-section_order/main1.expected | 7 + .../preload-section_order/main2.expected | 7 + .../test-cases/preload-section_order/more.s | 19 ++ .../test-cases/preload-segment_order/Makefile | 50 ++++ .../test-cases/preload-segment_order/a.c | 13 + .../test-cases/preload-segment_order/b.c | 9 + .../preload-segment_order/main-segs.expected | 4 + .../test-cases/preload-segment_order/main.c | 11 + .../test-cases/section-labels/main.c | 6 +- .../test-cases/symbol-section-move/Makefile | 60 ++++ .../test-cases/symbol-section-move/main.c | 54 ++++ .../test-cases/symbol-section-move/other.c | 9 + .../symbol-section-move/ram1.symbols | 3 + .../symbol-section-move/rom1.symbols | 6 + .../test-cases/weak_import-undefined/Makefile | 2 +- 73 files changed, 2312 insertions(+), 320 deletions(-) create mode 100644 ld64/unit-tests/test-cases/alias-basic/Makefile create mode 100644 ld64/unit-tests/test-cases/alias-basic/aliases.s create mode 100644 ld64/unit-tests/test-cases/alias-basic/main.c create mode 100644 ld64/unit-tests/test-cases/alt-entry/Makefile create mode 100644 ld64/unit-tests/test-cases/alt-entry/foo.c create mode 100644 ld64/unit-tests/test-cases/alt-entry/main.c create mode 100644 ld64/unit-tests/test-cases/lto-r/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-r/bar.c create mode 100644 ld64/unit-tests/test-cases/lto-r/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-r/main.c create mode 100644 ld64/unit-tests/test-cases/lto-rename_section/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-rename_section/a.c create mode 100644 ld64/unit-tests/test-cases/lto-rename_section/b.c create mode 100644 ld64/unit-tests/test-cases/lto-rename_section/main.c create mode 100644 ld64/unit-tests/test-cases/lto-rename_segment/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-rename_segment/a.c create mode 100644 ld64/unit-tests/test-cases/lto-rename_segment/b.c create mode 100644 ld64/unit-tests/test-cases/lto-rename_segment/main.c create mode 100644 ld64/unit-tests/test-cases/lto-symbol-section-move/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-symbol-section-move/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-symbol-section-move/main.c create mode 100644 ld64/unit-tests/test-cases/lto-symbol-section-move/other.c create mode 100644 ld64/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols create mode 100644 ld64/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols create mode 100644 ld64/unit-tests/test-cases/preload-section_order/Makefile create mode 100644 ld64/unit-tests/test-cases/preload-section_order/extra.s create mode 100644 ld64/unit-tests/test-cases/preload-section_order/main.c create mode 100644 ld64/unit-tests/test-cases/preload-section_order/main1.expected create mode 100644 ld64/unit-tests/test-cases/preload-section_order/main2.expected create mode 100644 ld64/unit-tests/test-cases/preload-section_order/more.s create mode 100644 ld64/unit-tests/test-cases/preload-segment_order/Makefile create mode 100644 ld64/unit-tests/test-cases/preload-segment_order/a.c create mode 100644 ld64/unit-tests/test-cases/preload-segment_order/b.c create mode 100644 ld64/unit-tests/test-cases/preload-segment_order/main-segs.expected create mode 100644 ld64/unit-tests/test-cases/preload-segment_order/main.c create mode 100644 ld64/unit-tests/test-cases/symbol-section-move/Makefile create mode 100644 ld64/unit-tests/test-cases/symbol-section-move/main.c create mode 100644 ld64/unit-tests/test-cases/symbol-section-move/other.c create mode 100644 ld64/unit-tests/test-cases/symbol-section-move/ram1.symbols create mode 100644 ld64/unit-tests/test-cases/symbol-section-move/rom1.symbols diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index 964d099..df4b5a4 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -384,8 +384,6 @@ Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d Force definition of common symbols. That is, transform tentative definitions into real definitions. -.It Fl rename_section Ar fromSegment fromSection toSegment toSection -Renames section fromSegment/fromSection to toSegment/toSection. .El .Ss Options that control symbol resolution .Bl -tag @@ -521,10 +519,49 @@ of wildcards. .Bl -tag .It Fl v Prints the version of the linker. +.It Fl move_to_rw_segment Ar segment_name Ar filename +Moves data symbols to another segment. The command line option specifies the +target segment name and a path to a file containing a list of symbols to move. +Comments can be added to the symbol file by starting a line with a #. +If there are multiple instances of a symbol name (for instance a "static int foo=5;" in multiple files) +the symbol name in the symbol list file can be prefixed with the object file name +(e.g. "init.o:_foo") to move a specific instance. +.It Fl move_to_ro_section Ar segment_name Ar section_name Ar filename +Moves code symbols to another segment. The command line option specifies the +target segment name and a path to a file containing a list of symbols to move. +Comments can be added to the symbol file by starting a line with a #. +If there are multiple instances of a symbol name (for instance a "static int foo() {}" in multiple files) +the symbol name in the symbol list file can be prefixed with the object file name +(e.g. "init.o:_foo") to move a specific instance. +.It Fl rename_section Ar orgSegment orgSection newSegment newSection +Renames section orgSegment/orgSection to newSegment/newSection. +.It Fl rename_segment Ar orgSegment newSegment +Renames all sections with orgSegment segment name to have newSegment segment name. +.It Fl trace_symbol_layout +For using in debugging -rename_section, -rename_segment, -move_to_ro_segment, and -move_to_rw_segment. +This option prints out a line show where and why each symbol was moved. +Note: These options do not chain. For each symbol, the linker first checks +-move_to_ro_segment and -move_to_rw_segment. If the symbol is not moved, +it checks for an applicable -rename_section. Only if the symbol still has +not been moved, does the linker look for an applicable -rename_segment option. +.It Fl section_order Ar segname Ar colon_separated_section_list +Only for use with -preload. Specifies the order that sections with the specified segment should be layout out. +For example: "-section_order __ROM __text:__const:__cstring". +.It Fl segment_order Ar colon_separated_segment_list +Only for use with -preload. Specifies the order segments should be layout out. +For example: "-segment_order __ROM:__ROM2:__RAM". .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 behavior and allows instructions on any page to be executed. +.It Fl application_extension +Specifies that the code is being linked for use in an application extension. The linker +will then validiate that any dynamic libraries linked against are safe for use in +application extensions. +.It Fl no_application_extension +Specifies that the code is being linked is not safe for use in an application extension. +For instance, can be used when creating a framework that should not be used in +an application extension. .It Fl fatal_warnings Causes the linker to exit with a non-zero value if any warnings were emitted. .It Fl no_eh_labels diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 250fa5e..854356e 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -245,7 +245,12 @@ B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F91B7B0318987D5F0099486F /* AddressSpace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AddressSpace.hpp; sourceTree = ""; }; + F91B7B0418987D5F0099486F /* DwarfInstructions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfInstructions.hpp; sourceTree = ""; }; + F91B7B0518987D5F0099486F /* DwarfParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DwarfParser.hpp; sourceTree = ""; }; + F91B7B0618987D5F0099486F /* InternalMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalMacros.h; sourceTree = ""; }; + F91B7B0718987D5F0099486F /* Registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Registers.hpp; sourceTree = ""; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -401,6 +406,18 @@ name = Products; sourceTree = ""; }; + F91B7B0218987D5F0099486F /* libunwind */ = { + isa = PBXGroup; + children = ( + F91B7B0318987D5F0099486F /* AddressSpace.hpp */, + F91B7B0418987D5F0099486F /* DwarfInstructions.hpp */, + F91B7B0518987D5F0099486F /* DwarfParser.hpp */, + F91B7B0618987D5F0099486F /* InternalMacros.h */, + F91B7B0718987D5F0099486F /* Registers.hpp */, + ); + path = libunwind; + sourceTree = ""; + }; F9AA650B1051BD2B003E3539 /* passes */ = { isa = PBXGroup; children = ( @@ -449,6 +466,7 @@ F9AA65861051E750003E3539 /* parsers */ = { isa = PBXGroup; children = ( + F91B7B0218987D5F0099486F /* libunwind */, F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, F9AA6785105700C2003E3539 /* opaque_section_file.h */, F9AA65D71051EC4A003E3539 /* archive_file.cpp */, diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 9279734..8b58efd 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -192,6 +192,10 @@ #define N_SYMBOL_RESOLVER 0x100 #endif +#ifndef N_AST + #define N_AST 0x32 +#endif + #ifndef LC_FUNCTION_STARTS #define LC_FUNCTION_STARTS 0x26 #endif @@ -229,7 +233,13 @@ }; #endif +#ifndef MH_APP_EXTENSION_SAFE + #define MH_APP_EXTENSION_SAFE 0x02000000 +#endif +#ifndef N_ALT_ENTRY + #define N_ALT_ENTRY 0x0200 +#endif #ifndef CPU_SUBTYPE_ARM_V7F #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) @@ -360,6 +370,7 @@ #define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF + #ifndef LC_SOURCE_VERSION #define LC_SOURCE_VERSION 0x2A struct source_version_command { @@ -431,6 +442,7 @@ #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) #endif + struct ArchInfo { const char* archName; cpu_type_t cpuType; diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index acbdf4f..3629af5 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -591,6 +591,8 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const bits |= MH_HAS_TLV_DESCRIPTORS; if ( _options.hasNonExecutableHeap() ) bits |= MH_NO_HEAP_EXECUTION; + if ( _options.markAppExtensionSafe() && (_options.outputKind() == Options::kDynamicLibrary) ) + bits |= MH_APP_EXTENSION_SAFE; } if ( _options.hasExecutableStack() ) bits |= MH_ALLOW_STACK_EXECUTION; @@ -750,6 +752,9 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* case ld::Section::typeTempLTO: assert(0 && "typeTempLTO should not make it to final linked image"); return S_REGULAR; + case ld::Section::typeTempAlias: + assert(0 && "typeAlias should not make it to final linked image"); + return S_REGULAR; case ld::Section::typeAbsoluteSymbols: assert(0 && "typeAbsoluteSymbols should not make it to final linked image"); return S_REGULAR; diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index f49f2e3..595b5d1 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -255,8 +255,17 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); len = OSSwapBigToHostInt32(archs[sliceToUse].size); if ( fileOffset+len > info.fileLen ) { - throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", + // file size was read awhile ago. If file is being written, wait a second to see if big enough now + sleep(1); + uint64_t newFileLen = info.fileLen; + struct stat statBuffer; + if ( stat(info.path, &statBuffer) == 0 ) { + newFileLen = statBuffer.st_size; + } + if ( fileOffset+len > newFileLen ) { + throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", fileOffset, fileOffset+len, info.fileLen); + } } // if requested architecture is page aligned within fat file, then remap just that portion of file if ( (fileOffset & 0x00000FFF) == 0 ) { @@ -301,11 +310,27 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } // see if it is a dynamic library - ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); - if ( dylibResult != NULL ) { - return dylibResult; + ld::dylib::File* dylibResult; + bool dylibsNotAllowed = false; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); + if ( dylibResult != NULL ) { + return dylibResult; + } + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + case Options::kKextBundle: + dylibsNotAllowed = true; + break; } + // see if it is a static library ::archive::ParserOptions archOpts; archOpts.objOpts = objOpts; @@ -350,6 +375,13 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } } + if ( dylibsNotAllowed ) { + cpu_type_t dummy1; + cpu_type_t dummy2; + if ( mach_o::dylib::isDylibFile(p, &dummy1, &dummy2) ) + throw "ignoring unexpected dylib file"; + } + // error handling if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { throwf("missing required architecture %s in file %s (%u slices)", _options.architectureName(), info.path, sliceCount); @@ -538,7 +570,7 @@ bool InputFiles::libraryAlreadyLoaded(const char* path) } -void InputFiles::addLinkerOptionLibraries(ld::Internal& state) +void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler) { if ( _options.outputKind() == Options::kObjectFile ) return; @@ -554,6 +586,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -574,10 +607,13 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) if ( ! this->libraryAlreadyLoaded(info.path) ) { info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); try { + // -force_load_swift_libs + info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0); ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); ld::archive::File* archiveReader = dynamic_cast(reader); if ( dylibReader != NULL ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -585,6 +621,10 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state) _searchLibraries.push_back(LibraryInfo(archiveReader)); if ( _options.dumpDependencyInfo() ) _options.dumpDependency(Options::depArchive, archiveReader->path()); + // -force_load_swift_libs + if (info.options.fForceLoad) { + archiveReader->forEachAtom(handler); + } } else { throwf("linker option dylib at %s is not a dylib", info.path); @@ -895,6 +935,9 @@ void InputFiles::parseWorkerThread() { warning("ignoring file %s, %s", entry.path, msg); } } + else if ( strstr(msg, "ignoring unexpected") != NULL ) { + warning("%s, %s", entry.path, msg); + } else { asprintf((char**)&exception, "%s file '%s'", msg, entry.path); } @@ -1127,7 +1170,7 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal } markExplicitlyLinkedDylibs(); - addLinkerOptionLibraries(state); + addLinkerOptionLibraries(state, handler); createIndirectDylibs(); createOpaqueFileSections(); @@ -1276,6 +1319,14 @@ static bool vectorContains(const std::vector& vec, ld::dylib:: return std::find(vec.begin(), vec.end(), key) != vec.end(); } +struct DylibByInstallNameSorter +{ + bool operator()(const ld::dylib::File* left, const ld::dylib::File* right) + { + return (strcmp(left->installPath(), right->installPath()) < 0); + } +}; + void InputFiles::dylibs(ld::Internal& state) { bool dylibsOK = false; @@ -1310,14 +1361,18 @@ void InputFiles::dylibs(ld::Internal& state) } // add implicitly linked dylibs if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { + std::vector implicitDylibs; for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { ld::dylib::File* dylibFile = it->second; if ( dylibFile->implicitlyLinked() && dylibsOK ) { - if ( ! vectorContains(state.dylibs, dylibFile) ) { - state.dylibs.push_back(dylibFile); + if ( ! vectorContains(implicitDylibs, dylibFile) ) { + implicitDylibs.push_back(dylibFile); } } } + // make implicit dylib order be deterministic by sorting by install_name + std::sort(implicitDylibs.begin(), implicitDylibs.end(), DylibByInstallNameSorter()); + state.dylibs.insert(state.dylibs.end(), implicitDylibs.begin(), implicitDylibs.end()); } //fprintf(stderr, "all dylibs:\n"); diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index b1e85cb..608ce39 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -74,7 +74,7 @@ class InputFiles : public ld::dylib::File::DylibHandler bool inferredArch() const { return _inferredArch; } - void addLinkerOptionLibraries(ld::Internal& state); + void addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler); void createIndirectDylibs(); // for -print_statistics diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index b9b1215..3389f5c 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -224,7 +224,7 @@ class SymbolTableAtom : public ClassicLinkEditAtom uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool); uint64_t valueForStab(const ld::relocatable::File::Stab& stab); uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab); - + bool isAltEntry(const ld::Atom* atom); mutable std::vector > _globals; mutable std::vector > _locals; @@ -247,6 +247,29 @@ template int SymbolTableAtom::_s_anonNameIndex = 1; +template +bool SymbolTableAtom::isAltEntry(const ld::Atom* atom) +{ + // alt entries have a group subordinate reference to the previous atom + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneGroupSubordinate ) { + if ( fit->binding == Fixup::bindingDirectlyBound ) { + const Atom* prevAtom = fit->u.target; + assert(prevAtom != NULL); + for (ld::Fixup::iterator fit2 = prevAtom->fixupsBegin(); fit2 != prevAtom->fixupsEnd(); ++fit2) { + if ( fit2->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit2->binding == Fixup::bindingDirectlyBound ) { + if ( fit2->u.target == atom ) + return true; + } + } + } + } + } + } + return false; +} + template bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) { @@ -312,6 +335,8 @@ bool SymbolTableAtom::addLocal(const ld::Atom* atom, StringPoolAtom* pool) desc |= N_WEAK_DEF; if ( atom->isThumb() ) desc |= N_ARM_THUMB_DEF; + if ( (this->_options.outputKind() == Options::kObjectFile) && this->_state.allObjectFilesScatterable && isAltEntry(atom) ) + desc |= N_ALT_ENTRY; entry.set_n_desc(desc); // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) @@ -387,6 +412,8 @@ void SymbolTableAtom::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) desc |= N_SYMBOL_RESOLVER; if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) ) desc |= N_NO_DEAD_STRIP; + if ( (this->_options.outputKind() == Options::kObjectFile) && this->_state.allObjectFilesScatterable && isAltEntry(atom) ) + desc |= N_ALT_ENTRY; if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) { desc |= N_WEAK_DEF; // support auto hidden weak symbols: .weak_def_can_be_hidden @@ -451,7 +478,13 @@ void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) // set n_type if ( this->_options.outputKind() == Options::kObjectFile ) { - if ( (atom->scope() == ld::Atom::scopeLinkageUnit) + if ( atom->section().type() == ld::Section::typeTempAlias ) { + if ( atom->scope() == ld::Atom::scopeLinkageUnit ) + entry.set_n_type(N_INDR | N_EXT | N_PEXT); + else + entry.set_n_type(N_INDR | N_EXT); + } + else if ( (atom->scope() == ld::Atom::scopeLinkageUnit) && (atom->definition() == ld::Atom::definitionTentative) ) entry.set_n_type(N_UNDF | N_EXT | N_PEXT); else @@ -500,8 +533,24 @@ void SymbolTableAtom::addImport(const ld::Atom* atom, StringPoolAtom* pool) // set n_value, zero for import proxy and size for tentative definition if ( atom->definition() == ld::Atom::definitionTentative ) entry.set_n_value(atom->size()); - else + else if ( atom->section().type() != ld::Section::typeTempAlias ) entry.set_n_value(0); + else { + assert(atom->fixupsBegin() != atom->fixupsEnd()); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + assert(fit->kind == ld::Fixup::kindNoneFollowOn); + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + entry.set_n_value(pool->add(fit->u.name)); + break; + case ld::Fixup::bindingsIndirectlyBound: + entry.set_n_value(pool->add((_state.indirectBindingTable[fit->u.bindingIndex])->name())); + break; + default: + assert(0 && "internal error: unexpected alias binding"); + } + } + } // add to array _imports.push_back(entry); @@ -775,13 +824,13 @@ uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) // for kext bundles the reloc base address starts at __TEXT segment return _options.baseAddress(); } - // for all other kinds, the x86_64 reloc base address starts at __DATA segment + // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA) for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + if ( !sect->isSectionHidden() && _options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE ) return sect->address; } - throw "__DATA segment not found"; + throw "writable (__DATA) segment not found"; } template @@ -892,10 +941,10 @@ uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) // for x86_64 the reloc base address starts at __DATA segment for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + if ( !sect->isSectionHidden() && _options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE ) return sect->address; } - throw "__DATA segment not found"; + throw "writable (__DATA) segment not found"; } template diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 057fad9..aa4f6ee 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -127,7 +127,7 @@ Options::Options(int argc, const char* argv[]) 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"), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL), fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL), fBaseWritableAddress(0), fSplitSegs(false), fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), @@ -176,7 +176,8 @@ Options::Options(int argc, const char* argv[]) fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), - fGenerateDtraceDOF(true), fAllowBranchIslands(true), + fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), + fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), @@ -539,6 +540,17 @@ bool Options::keepLocalSymbol(const char* symbolName) const throw "internal error"; } +const std::vector* Options::sectionOrder(const char* segName) const +{ + for (std::vector::const_iterator it=fSectionOrder.begin(); it != fSectionOrder.end(); ++it) { + if ( strcmp(it->segmentName, segName) == 0 ) + return &it->sectionOrder; + } + return NULL; +} + + + void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) { for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { @@ -1012,19 +1024,40 @@ void Options::SetWithWildcards::insert(const char* symbol) fRegular.insert(symbol); } -bool Options::SetWithWildcards::contains(const char* symbol) const +bool Options::SetWithWildcards::contains(const char* symbol, bool* matchBecauseOfWildcard) const { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = false; // first look at hash table on non-wildcard symbols if ( fRegular.find(symbol) != fRegular.end() ) return true; // next walk list of wild card symbols looking for a match for(std::vector::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { - if ( wildCardMatch(*it, symbol) ) + if ( wildCardMatch(*it, symbol) ) { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = true; return true; + } } return false; } +// Support "foo.o:_bar" to mean symbol _bar in file foo.o +bool Options::SetWithWildcards::containsWithPrefix(const char* symbol, const char* file, bool& wildCardMatch) const +{ + wildCardMatch = false; + if ( contains(symbol, &wildCardMatch) ) + return true; + if ( file == NULL ) + return false; + const char* s = strrchr(file, '/'); + if ( s != NULL ) + file = s+1; + char buff[strlen(file)+strlen(symbol)+2]; + sprintf(buff, "%s:%s", file, symbol); + return contains(buff, &wildCardMatch); +} + bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const { // look at hash table on non-wildcard symbols @@ -1753,6 +1786,59 @@ void Options::addSectionRename(const char* srcSegment, const char* srcSection, c } +void Options::addSegmentRename(const char* srcSegment, const char* dstSegment) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + + SegmentRename info; + info.fromSegment = srcSegment; + info.toSegment = dstSegment; + + fSegmentRenames.push_back(info); +} + + + +void Options::addSymbolMove(const char* dstSegment, const char* symbolList, + std::vector& list, const char* optionName) +{ + if ( strlen(dstSegment) > 16 ) + throwf("%s segment name max 16 chars", optionName); + + SymbolsMove tmp; + list.push_back(tmp); + SymbolsMove& info = list.back(); + info.toSegment = dstSegment; + loadExportFile(symbolList, optionName, info.symbols); +} + +bool Options::moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesData.begin(); it != fSymbolsMovesData.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + +bool Options::moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) { if ( strlen(segment) > 16 ) @@ -2904,6 +2990,7 @@ void Options::parse(int argc, const char* argv[]) fKextsUseStubs = true; } else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + snapshotArgCount = 0; ++i; // previously handled by buildSearchPaths() } @@ -2962,9 +3049,96 @@ void Options::parse(int argc, const char* argv[]) addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]); i += 4; } + else if ( strcmp(arg, "-rename_segment") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-rename_segment missing "; + addSegmentRename(argv[i+1], argv[i+2]); + i += 2; + } + else if ( strcmp(arg, "-move_to_ro_segment") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-move_to_ro_segment missing "; + addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesCode, "-move_to_ro_segment"); + i += 2; + } + else if ( strcmp(arg, "-move_to_rw_segment") == 0 ) { + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-move_to_rw_segment missing "; + addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesData, "-move_to_rw_segment"); + i += 2; + } + else if ( strcmp(arg, "-trace_symbol_layout") == 0 ) { + fTraceSymbolLayout = true; + } else if ( strcmp(arg, "-no_branch_islands") == 0 ) { fAllowBranchIslands = false; } + else if ( strcmp(arg, "-segment_order") == 0 ) { + // ex: -segment_order __TEXT:__DATA:__JUNK + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-segment_order missing colon separated "; + if ( !fSegmentOrder.empty() ) + throw "-segment_order used more than once"; + // break up into list of tokens at colon + char* buffer = strdup(optString); + char* start = buffer; + for (char* s = buffer; ; ++s) { + if ( *s == ':' ) { + *s = '\0'; + fSegmentOrder.push_back(start); + start = s+1; + } + else if ( *s == '\0' ) { + fSegmentOrder.push_back(start); + break; + } + } + } + else if ( strcmp(arg, "-section_order") == 0 ) { + // ex: -section_order __DATA __data:__const:__nl_pointers + if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) + throw "-section_order missing "; + const char* segName = argv[++i]; + const char* optString = argv[++i]; + if ( sectionOrder(segName) != NULL ) + throwf("-section_order %s ... used more than once", segName); + SectionOrderList dummy; + fSectionOrder.push_back(dummy); + SectionOrderList& entry = fSectionOrder.back(); + entry.segmentName = segName; + // break up into list of tokens at colon + char* buffer = strdup(optString); + char* start = buffer; + for (char* s = buffer; ; ++s) { + if ( *s == ':' ) { + *s = '\0'; + entry.sectionOrder.push_back(start); + start = s+1; + } + else if ( *s == '\0' ) { + entry.sectionOrder.push_back(start); + break; + } + } + } + else if ( strcmp(arg, "-application_extension") == 0 ) { + fMarkAppExtensionSafe = true; + fCheckAppExtensionSafe = true; + } + else if ( strcmp(arg, "-no_application_extension") == 0 ) { + fMarkAppExtensionSafe = false; + fCheckAppExtensionSafe = false; + } + else if ( strcmp(arg, "-add_ast_path") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-add_ast_path missing ::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, : ld::dylib::File(strdup(pth), mTime, ord), _macVersionMin(macMin), _iOSVersionMin(iOSMin), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), - _objcContraint(ld::File::objcConstraintNone), + _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), - _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), _indirectDylibsProcessed(false) + _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), + _indirectDylibsProcessed(false), _appExtensionSafe(false), + _macMinVersionInDylib(ld::macVersionUnset), _iOSMinVersionInDylib(ld::iOSVersionUnset) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -287,6 +297,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + _appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE); // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format const macho_dysymtab_command

* dynamicInfo = NULL; @@ -344,6 +355,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( _addVersionLoadCommand && !indirectDylib ) throw "building for iOS Simulator, but linking against dylib built for MacOSX"; } + _macMinVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); break; case LC_VERSION_MIN_IPHONEOS: if ( _macVersionMin != ld::macVersionUnset ) { @@ -351,6 +363,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( _addVersionLoadCommand && !indirectDylib ) throw "building for MacOSX, but linking against dylib built for iOS Simulator"; } + _iOSMinVersionInDylib = (ld::IOSVersionMin)((macho_version_min_command

*)cmd)->version(); break; case LC_CODE_SIGNATURE: codeSignature = (macho_linkedit_data_command

* )cmd; @@ -382,6 +395,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; else _objcContraint = ld::File::objcConstraintRetainRelease; + _swiftVersion = ((flags >> 8) & 0xFF); } else if ( sect->size() > 0 ) { warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); @@ -1074,17 +1088,6 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = CPU_SUBTYPE_ARM64_ALL; return true; } - if ( Parser::validFile(fileContent, false) ) { - *result = CPU_TYPE_POWERPC; - const macho_header >* header = (const macho_header >*)fileContent; - *subResult = header->cpusubtype(); - return true; - } - if ( Parser::validFile(fileContent, false) ) { - *result = CPU_TYPE_POWERPC64; - *subResult = CPU_SUBTYPE_POWERPC_ALL; - return true; - } return false; } diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index ad5720e..d3990e3 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -79,6 +79,7 @@ class File : public ld::relocatable::File _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), _objConstraint(ld::File::objcConstraintNone), + _swiftVersion(0), _cpuSubType(0), _canScatterAtoms(false) {} virtual ~File(); @@ -96,6 +97,7 @@ class File : public ld::relocatable::File virtual bool canScatterAtoms() const { return _canScatterAtoms; } virtual const char* translationUnitSource() const; virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } + virtual uint8_t swiftVersion() const { return _swiftVersion; } const uint8_t* fileContent() { return _fileContent; } private: @@ -109,8 +111,10 @@ class File : public ld::relocatable::File const uint8_t* _fileContent; Section** _sectionsArray; uint8_t* _atomsArray; + uint8_t* _aliasAtomsArray; uint32_t _sectionsArrayCount; uint32_t _atomsArrayCount; + uint32_t _aliasAtomsArrayCount; std::vector _fixups; std::vector _unwindInfos; std::vector _lineInfos; @@ -122,6 +126,7 @@ class File : public ld::relocatable::File const macho_section

* _dwarfDebugLineSect; const macho_section

* _dwarfDebugStringSect; ld::File::ObjcConstraint _objConstraint; + uint8_t _swiftVersion; uint32_t _cpuSubType; bool _canScatterAtoms; std::vector > _linkerOptions; @@ -182,6 +187,7 @@ class Section : public ld::Section class Atom* _beginAtoms; class Atom* _endAtoms; bool _hasAliases; + std::set*> _altEntries; }; @@ -191,7 +197,7 @@ class CFISection : public Section public: CFISection(Parser& parser, File& f, const macho_section* s) : Section(f, s) { } - uint32_t cfiCount(); + uint32_t cfiCount(Parser& parser); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCFI; } virtual uint32_t computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&); @@ -887,6 +893,39 @@ void Atom::verifyAlignment(const macho_section

&) const } +class AliasAtom : public ld::Atom +{ +public: + AliasAtom(const char* name, bool hidden, const ld::File* file, const char* aliasOfName) : + ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + (hidden ? ld::Atom::scopeLinkageUnit : ld::Atom::scopeGlobal), + ld::Atom::typeUnclassified, ld::Atom::symbolTableIn, + false, false, true, 0), + _file(file), + _name(name), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, ld::Fixup::bindingByNameUnbound, aliasOfName) { } + + virtual const ld::File* file() const { return _file; } + virtual const char* translationUnitSource() const + { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual ld::Fixup::iterator fixupsBegin() const { return &((ld::Fixup*)&_fixup)[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + static ld::Section _s_section; + + const ld::File* _file; + const char* _name; + ld::Fixup _fixup; +}; + +ld::Section AliasAtom::_s_section("__LD", "__aliases", ld::Section::typeTempAlias, true); + + template class Parser { @@ -984,6 +1023,7 @@ class Parser static bool isThumbFromSymbol(const macho_nlist

& sym); static bool weakImportFromSymbol(const macho_nlist

& sym); static bool resolverFromSymbol(const macho_nlist

& sym); + static bool altEntryFromSymbol(const macho_nlist

& sym); uint32_t symbolIndexFromIndirectSectionAddress(pint_t,const macho_section

*); const macho_section

* firstMachOSection() { return _sectionsStart; } const macho_section

* machOSectionFromSectionIndex(uint32_t index); @@ -1014,7 +1054,7 @@ class Parser bool forceDwarfConversion() { return _forceDwarfConversion; } bool verboseOptimizationHints() { return _verboseOptimizationHints; } bool neverConvertDwarf() { return _neverConvertDwarf; } - + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } const uint8_t* optimizationHintsStart() { return _lohStart; } @@ -1111,10 +1151,13 @@ class Parser void parseDebugInfo(); void parseStabs(); + void appendAliasAtoms(uint8_t* atomBuffer); static bool isConstFunStabs(const char *stabStr); bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); - const char* getDwarfString(uint64_t form, const uint8_t* p); + pint_t realAddr(pint_t addr); + const char* getDwarfString(uint64_t form, const uint8_t*& p); + uint64_t getDwarfOffset(uint64_t form, const uint8_t*& di, bool dwarf64); bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); @@ -1130,6 +1173,7 @@ class Parser File* _file; const macho_nlist

* _symbols; uint32_t _symbolCount; + uint32_t _indirectSymbolCount; const char* _strings; uint32_t _stringsSize; const uint32_t* _indirectTable; @@ -1174,7 +1218,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p bool neverConvertDwarf, bool verboseOptimizationHints) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), - _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), + _symbols(NULL), _symbolCount(0), _indirectSymbolCount(0), _strings(NULL), _stringsSize(0), _indirectTable(NULL), _indirectTableCount(0), _undefinedStartIndex(0), _undefinedEndIndex(0), _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), @@ -1555,6 +1599,18 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section< return false; } +template <> +typename arm::P::uint_t Parser::realAddr(typename arm::P::uint_t addr) +{ + return addr & (-2); +} + +template +typename A::P::uint_t Parser::realAddr(typename A::P::uint_t addr) +{ + return addr; +} + #define STACK_ALLOC_IF_SMALL(_type, _name, _actual_count, _maxCount) \ _type* _name = NULL; \ uint32_t _name##_count = 1; \ @@ -1619,7 +1675,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // stack allocate (if not too large) array of CFI_Atom_Info uint32_t countOfCFIs = 0; if ( _EHFrameSection != NULL ) - countOfCFIs = _EHFrameSection->cfiCount(); + countOfCFIs = _EHFrameSection->cfiCount(*this); STACK_ALLOC_IF_SMALL(typename CFISection::CFI_Atom_Info, cfiArray, countOfCFIs, 1024); // stack allocate (if not too large) a copy of __eh_frame to apply relocations to @@ -1655,7 +1711,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) if ( cfiArray[i].isCIE ) continue; if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) - cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.function.targetAddress; + cfiStartsArray[cfiStartsArrayCount++] = realAddr(cfiArray[i].u.fdeInfo.function.targetAddress); if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; ++countOfFDEs; @@ -1792,6 +1848,16 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) } } + // process indirect symbols which become AliasAtoms + _file->_aliasAtomsArray = NULL; + _file->_aliasAtomsArrayCount = 0; + if ( _indirectSymbolCount != 0 ) { + _file->_aliasAtomsArrayCount = _indirectSymbolCount; + _file->_aliasAtomsArray = new uint8_t[_file->_aliasAtomsArrayCount*sizeof(AliasAtom)]; + this->appendAliasAtoms(_file->_aliasAtomsArray); + } + + // parse dwarf debug info to get line info this->parseDebugInfo(); @@ -1799,7 +1865,6 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) } - template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } @@ -1950,7 +2015,11 @@ void Parser::prescanSymbolTable() } continue; } - + else if ( ((sym.n_type() & N_TYPE) == N_INDR) && ((sym.n_type() & N_EXT) != 0) ) { + _indirectSymbolCount++; + continue; + } + // count absolute symbols if ( (sym.n_type() & N_TYPE) == N_ABS ) { const char* absName = this->nameFromSymbol(sym); @@ -1987,6 +2056,34 @@ void Parser::prescanSymbolTable() } } +template +void Parser::appendAliasAtoms(uint8_t* p) +{ + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist

& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // only look at N_INDR symbols + if ( (sym.n_type() & N_TYPE) != N_INDR ) + continue; + + // skip non-external aliases + if ( (sym.n_type() & N_EXT) == 0 ) + continue; + + const char* symbolName = this->nameFromSymbol(sym); + const char* aliasOfName = &_strings[sym.n_value()]; + bool isHiddenVisibility = (sym.n_type() & N_PEXT); + AliasAtom* allocatedSpace = (AliasAtom*)p; + new (allocatedSpace) AliasAtom(symbolName, isHiddenVisibility, _file, aliasOfName); + p += sizeof(AliasAtom); + } +} + + + template int Parser::sectionIndexSorter(void* extra, const void* l, const void* r) { @@ -2206,6 +2303,7 @@ void Parser::makeSections() _file->_objConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else _file->_objConstraint = ld::File::objcConstraintRetainRelease; + _file->_swiftVersion = ((flags >> 8) & 0xFF); if ( sect->size() > 8 ) { warning("section %s/%s has unexpectedly large size %llu in %s", sect->segname(), Section::makeSectionName(sect), sect->size(), _file->path()); @@ -3011,6 +3109,12 @@ bool Parser::resolverFromSymbol(const macho_nlist

& sym) return ( sym.n_desc() & N_SYMBOL_RESOLVER ); } +template +bool Parser::altEntryFromSymbol(const macho_nlist

& sym) +{ + return ( sym.n_desc() & N_ALT_ENTRY ); +} + /* Skip over a LEB128 value (signed or unsigned). */ static void @@ -3144,21 +3248,51 @@ bool Parser::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t template -const char* Parser::getDwarfString(uint64_t form, const uint8_t* p) +const char* Parser::getDwarfString(uint64_t form, const uint8_t*& di) { - if ( form == DW_FORM_string ) - return (const char*)p; - else if ( form == DW_FORM_strp ) { - uint32_t offset = E::get32(*((uint32_t*)p)); - const char* dwarfStrings = (char*)_file->fileContent() + _file->_dwarfDebugStringSect->offset(); - if ( offset > _file->_dwarfDebugStringSect->size() ) { - warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->_path); - return NULL; - } - return &dwarfStrings[offset]; + uint32_t offset; + const char* dwarfStrings; + const char* result = NULL; + switch (form) { + case DW_FORM_string: + result = (const char*)di; + di += strlen(result) + 1; + break; + case DW_FORM_strp: + offset = E::get32(*((uint32_t*)di)); + dwarfStrings = (char*)_file->fileContent() + _file->_dwarfDebugStringSect->offset(); + if ( offset < _file->_dwarfDebugStringSect->size() ) + result = &dwarfStrings[offset]; + else + warning("dwarf DW_FORM_strp (offset=0x%08X) is too big in %s", offset, this->_path); + di += 4; + break; + default: + warning("unknown dwarf string encoding (form=%lld) in %s", form, this->_path); + break; } - warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->_path); - return NULL; + return result; +} + +template +uint64_t Parser::getDwarfOffset(uint64_t form, const uint8_t*& di, bool dwarf64) +{ + if ( form == DW_FORM_sec_offset ) + form = (dwarf64 ? DW_FORM_data8 : DW_FORM_data4); + uint64_t result = -1; + switch (form) { + case DW_FORM_data4: + result = A::P::E::get32(*(uint32_t*)di); + di += 4; + break; + case DW_FORM_data8: + result = A::P::E::get64(*(uint64_t*)di); + di += 8; + break; + default: + warning("unknown dwarf DW_FORM_ for DW_AT_stmt_list in %s", this->_path); + } + return result; } @@ -3400,6 +3534,7 @@ void Parser::parseStabs() case N_LSYM: case N_RSYM: case N_PSYM: + case N_AST: // not associated with an atom, just copy stab.string = symString; break; @@ -3676,20 +3811,23 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, return false; else if (attr == 0) return true; - if (form == DW_FORM_indirect) form = read_uleb128 (&di, end); - if (attr == DW_AT_name) - *name = getDwarfString(form, di); - else if (attr == DW_AT_comp_dir) - *comp_dir = getDwarfString(form, di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) - *stmt_list = A::P::E::get32(*(uint32_t*)di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) - *stmt_list = A::P::E::get64(*(uint64_t*)di); - if (! skip_form (&di, end, form, address_size, dwarf64)) - return false; + switch (attr) { + case DW_AT_name: + *name = getDwarfString(form, di); + break; + case DW_AT_comp_dir: + *comp_dir = getDwarfString(form, di); + break; + case DW_AT_stmt_list: + *stmt_list = getDwarfOffset(form, di, dwarf64); + break; + default: + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } } } @@ -3708,7 +3846,7 @@ const char* File::translationUnitSource() const return _dwarfTranslationUnitPath; } - + template bool File::forEachAtom(ld::File::AtomHandler& handler) const @@ -3719,7 +3857,13 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const handler.doAtom(*((Atom*)p)); p += sizeof(Atom); } - return (_atomsArrayCount != 0); + p = _aliasAtomsArray; + for(int i=_aliasAtomsArrayCount; i > 0; --i) { + handler.doAtom(*((AliasAtom*)p)); + p += sizeof(AliasAtom); + } + + return (_atomsArrayCount != 0) || (_aliasAtomsArrayCount != 0); } template @@ -3922,10 +4066,14 @@ uint32_t Section::sectionNum(class Parser& parser) const } // arm does not have zero cost exceptions -template <> uint32_t CFISection::cfiCount() { return 0; } +template <> +uint32_t CFISection::cfiCount(Parser& parser) +{ + return 0; +} template -uint32_t CFISection::cfiCount() +uint32_t CFISection::cfiCount(Parser& parser) { // create ObjectAddressSpace object for use by libunwind OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); @@ -4059,6 +4207,9 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, assert(count == 0); } + + + template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], @@ -4209,8 +4360,6 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const C } } - - #if SUPPORT_ARCH_arm64 template <> void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) @@ -4236,6 +4385,7 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, con } #endif + template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { @@ -4481,8 +4631,7 @@ const char* CUSection::personalityName(class Parser& parser, con else { const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); pint_t personalityAddr = *content; - Section* personalitySection = parser.sectionForAddress(personalityAddr); - assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); + assert((parser.sectionForAddress(personalityAddr)->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); // atoms may not be constructed yet, so scan symbol table for labels const char* name = parser.scanSymbolTableForAddress(personalityAddr); return name; @@ -4537,6 +4686,7 @@ const char* CUSection::personalityName(class Parser& parser, const } #endif + template const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info

* reloc) { @@ -4780,6 +4930,8 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, new (allocatedSpace) Atom(*this, parser, *label, size, isAlias); if ( isAlias ) this->_hasAliases = true; + if ( parser.altEntryFromSymbol(*label) ) + this->_altEntries.insert(allocatedSpace); } else { ld::Atom::SymbolTableInclusion inclusion = ld::Atom::symbolTableNotIn; @@ -6085,7 +6237,12 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati if ((instruction & 0xFE000000) == 0xFA000000) displacement += ((instruction & 0x01000000) >> 23); if ( reloc->r_extern() ) { - target.addend = srcAddr + displacement; + dstAddr = srcAddr + displacement; + // support large .o files + if ( srcAddr > 0x2000000 ) { + dstAddr -= ((srcAddr + 0x1FFFFFF) & 0xFC000000); + } + target.addend = dstAddr; if ( externSymbolIsThumbDef ) target.addend &= -2; // remove thumb bit } @@ -6132,7 +6289,11 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati dstAddr &= 0xFFFFFFFC; if ( reloc->r_extern() ) { - target.addend = dstAddr; + // support large .o files + if ( srcAddr > 0x1000000 ) { + dstAddr -= ((srcAddr + 0xFFFFFF) & 0xFE000000); + } + target.addend = (int64_t)(int32_t)dstAddr; } else { parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); @@ -6994,6 +7155,21 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } } + if ( !this->_altEntries.empty() && !this->addFollowOnFixups() ) { + if ( _altEntries.count(_beginAtoms) != 0 ) + warning("N_ALT_ENTRY bit set on first atom in section %s/%s", sect->segname(), Section::makeSectionName(sect)); + + Atom* end = &_endAtoms[-1]; + for(Atom* p = _beginAtoms; p < end; ++p) { + Atom* nextAtom = &p[1]; + if ( _altEntries.count(nextAtom) != 0 ) { + typename Parser::SourceLocation src(p, 0); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); + typename Parser::SourceLocation src2(nextAtom, 0); + parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinate, p); + } + } + } // track data-in-code if ( parser.hasDataInCodeLabels() && (this->type() == ld::Section::typeCode) ) { diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 5f5612c..8efd10a 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -71,8 +71,8 @@ class ARMtoARMBranchIslandAtom : public ld::Atom { _name(nm), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, target), _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { - if (_s_log) fprintf(stderr, "%s: ARM jump instruction branch island to final target %s\n", - target->name(), finalTarget.atom->name()); + if (_s_log) fprintf(stderr, "%p: ARM-to-ARM branch island to final target %s\n", + this, finalTarget.atom->name()); } virtual const ld::File* file() const { return NULL; } @@ -101,8 +101,10 @@ class ARMtoThumb1BranchIslandAtom : public ld::Atom { ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _finalTarget(finalTarget) { + if (_s_log) fprintf(stderr, "%p: ARM-to-thumb1 branch island to final target %s\n", + this, finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } @@ -115,8 +117,6 @@ class ARMtoThumb1BranchIslandAtom : public ld::Atom { int64_t displacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - (this->finalAddress() + 12); if ( _finalTarget.atom->isThumb() ) displacement |= 1; - if (_s_log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip @@ -126,7 +126,6 @@ class ARMtoThumb1BranchIslandAtom : public ld::Atom { private: const char* _name; - const ld::Atom* _target; TargetAndOffset _finalTarget; }; @@ -141,8 +140,8 @@ class Thumb2toThumbBranchIslandAtom : public ld::Atom { _name(nm), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressThumbBranch22, target), _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { - if (_s_log) fprintf(stderr, "%s: Thumb jump instruction branch island to final target %s\n", - target->name(), finalTarget.atom->name()); + if (_s_log) fprintf(stderr, "%p: Thumb-to-thumb branch island to final target %s\n", + this, finalTarget.atom->name()); } virtual const ld::File* file() const { return NULL; } @@ -175,7 +174,10 @@ class Thumb2toThumbBranchAbsoluteIslandAtom : public ld::Atom { _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbLow16), _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom), _fixup4(4, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbHigh16), - _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { } + _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%p: Thumb-to-thumb absolute branch island to final target %s\n", + this, finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } @@ -208,8 +210,10 @@ class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _finalTarget(finalTarget) { + if (_s_log) fprintf(stderr, "%p: NoPIC ARM-to-Thumb branch island to final target %s\n", + this, finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } @@ -222,8 +226,6 @@ class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { uint32_t targetAddr = _finalTarget.atom->finalAddress(); if ( _finalTarget.atom->isThumb() ) targetAddr |= 1; - if (_s_log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this } @@ -231,7 +233,6 @@ class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { private: const char* _name; - const ld::Atom* _target; TargetAndOffset _finalTarget; }; diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index ad8a504..3ddd9b8 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -108,6 +108,7 @@ class UnwindInfoAtom : public ld::Atom { void addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend); uint8_t* _pagesForDelete; + uint8_t* _pageAlignedPages; uint8_t* _pages; uint64_t _pagesSize; uint8_t* _header; @@ -129,8 +130,8 @@ template UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint64_t ehFrameSize) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), - _pagesForDelete(NULL), _pages(NULL), _pagesSize(0), _header(NULL), _headerSize(0) + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _pagesForDelete(NULL), _pageAlignedPages(NULL), _pages(NULL), _pagesSize(0), _header(NULL), _headerSize(0) { // build new compressed list by removing entries where next function has same encoding std::vector uniqueEntries; @@ -160,11 +161,12 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); assert(uniqueEntries.size() > 0); const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 2; - _pagesForDelete = (uint8_t*)calloc(pageCount,4096); + _pagesForDelete = (uint8_t*)calloc(pageCount+1,4096); if ( _pagesForDelete == NULL ) { warning("could not allocate space for compact unwind info"); return; } + _pageAlignedPages = (uint8_t*)((((uintptr_t)_pagesForDelete) + 4095) & -4096); // make last second level page smaller so that all other second level pages can be page aligned uint32_t maxLastPageSize = 4096 - (ehFrameSize % 4096); @@ -179,7 +181,7 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 uint8_t* secondLevelPagesStarts[pageCount*3]; unsigned int endIndex = uniqueEntries.size(); unsigned int secondLevelPageCount = 0; - uint8_t* pageEnd = &_pagesForDelete[pageCount*4096]; + uint8_t* pageEnd = &_pageAlignedPages[pageCount*4096]; uint32_t pageSize = maxLastPageSize; while ( endIndex > 0 ) { endIndex = makeCompressedSecondLevelPage(uniqueEntries, commonEncodings, pageSize, endIndex, pageEnd); @@ -193,9 +195,8 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 } } _pages = pageEnd; - _pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd; - - + _pagesSize = &_pageAlignedPages[pageCount*4096] - pageEnd; + // calculate section layout const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header

); const uint32_t commonEncodingsArrayCount = commonEncodings.size(); @@ -212,7 +213,7 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; // now that we know the size of the header, slide all existing fixups on the pages - const int32_t fixupSlide = headerEndSectionOffset + (_pagesForDelete - _pages); + const int32_t fixupSlide = headerEndSectionOffset + (_pageAlignedPages - _pages); for(std::vector::iterator it = _fixups.begin(); it != _fixups.end(); ++it) { it->offsetInAtom += fixupSlide; } @@ -547,11 +548,11 @@ unsigned int UnwindInfoAtom::makeRegularSecondLevelPage(const std::vectoraddRegularAddressFixup(offset, info.func); if ( encodingMeansUseDwarf(info.encoding) ) { // add fixup for dwarf offset part of page specific encoding - uint32_t encOffset = (uint8_t*)(&entryTable[i]) - _pagesForDelete; + uint32_t encOffset = (uint8_t*)(&entryTable[i]) - _pageAlignedPages; this->addRegularFDEOffsetFixup(encOffset, info.fde); } } @@ -678,11 +679,11 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< uint32_t entryIndex = i - endIndex + entryCount; E::set32(entiresArray[entryIndex], encodingIndex << 24); // add fixup for address part of entry - uint32_t offset = (uint8_t*)(&entiresArray[entryIndex]) - _pagesForDelete; + uint32_t offset = (uint8_t*)(&entiresArray[entryIndex]) - _pageAlignedPages; this->addCompressedAddressOffsetFixup(offset, info.func, firstFunc); if ( encodingMeansUseDwarf(info.encoding) ) { // add fixup for dwarf offset part of page specific encoding - uint32_t encOffset = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]) - _pagesForDelete; + uint32_t encOffset = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]) - _pageAlignedPages; this->addCompressedEncodingFixup(encOffset, info.fde); } } diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index d921a64..2bbf54a 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -65,7 +65,7 @@ template class ObjCImageInfoAtom : public ld::Atom { public: ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, - bool compaction, bool abi2); + bool compaction, bool abi2, uint8_t swiftVersion); virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return "objc image info"; } @@ -89,7 +89,7 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, - bool abi2) + bool abi2, uint8_t swiftVersion) : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) @@ -117,6 +117,9 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, break; } + // provide swift language version in final binary for runtime to inspect + value |= (swiftVersion << 8); + _content.version = 0; A::P::E::set32(_content.flags, value); } @@ -362,7 +365,7 @@ void ObjCData::setPointerInContent(ld::Internal& state, const ld::Atom* conte template class Category : public ObjCData { public: - static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend); static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom); @@ -374,9 +377,9 @@ class Category : public ObjCData { template -const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom) +const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend) { - return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t)); // category_t.cls + return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t), &hasAddend); // category_t.cls } template @@ -838,10 +841,17 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // ignore categories also in __objc_nlcatlist if ( nlcatListAtoms.count(categoryAtom) != 0 ) continue; - const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom); + const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom, hasAddend); assert(categoryOnClassAtom != NULL); + // only look at classes defined in this image if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) { - // only look at classes defined in this image + // for now, back off optimization on new style classes + if ( hasAddend != 0 ) + continue; + // don't apply categories to swift classes + if ( categoryOnClassAtom->hasFixupsOfKind(ld::Fixup::kindNoneGroupSubordinate) ) + continue; + CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); if ( pos == classToCategories.end() ) { classToCategories[categoryOnClassAtom] = new std::vector(); @@ -1171,25 +1181,25 @@ void doPass(const Options& opts, ld::Internal& state) #if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true)); + true, state.swiftVersion)); break; #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - opts.objCABIVersion2POverride() ? true : false)); + opts.objCABIVersion2POverride() ? true : false, state.swiftVersion)); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true)); + true, state.swiftVersion)); break; #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true)); + true, state.swiftVersion)); break; #endif default: diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index 684cb79..139e761 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -78,10 +78,11 @@ class Layout class Comparer { public: - Comparer(const Layout& l) : _layout(l) {} + Comparer(const Layout& l, ld::Internal& s) : _layout(l), _state(s) {} bool operator()(const ld::Atom* left, const ld::Atom* right); private: const Layout& _layout; + ld::Internal& _state; }; typedef std::unordered_map NameToAtom; @@ -114,7 +115,7 @@ class Layout bool Layout::_s_log = false; Layout::Layout(const Options& opts, ld::Internal& state) - : _options(opts), _state(state), _comparer(*this), _haveOrderFile(opts.orderedSymbolsCount() != 0) + : _options(opts), _state(state), _comparer(*this, state), _haveOrderFile(opts.orderedSymbolsCount() != 0) { } @@ -167,11 +168,21 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) bool leftIsAlias = left->isAlias(); if ( leftIsAlias ) { for (ld::Fixup::iterator fit=left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + const ld::Atom* target = NULL; if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { - assert(fit->binding == ld::Fixup::bindingDirectlyBound); - if ( fit->u.target == right ) + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = _state.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + default: + break; + } + if ( target == right ) return true; // left already before right - left = fit->u.target; // sort as if alias was its target + left = target; // sort as if alias was its target break; } } @@ -179,11 +190,21 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) bool rightIsAlias = right->isAlias(); if ( rightIsAlias ) { for (ld::Fixup::iterator fit=right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + const ld::Atom* target = NULL; if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { - assert(fit->binding == ld::Fixup::bindingDirectlyBound); - if ( fit->u.target == left ) + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = _state.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + default: + break; + } + if ( target == left ) return false; // need to swap, alias is after target - right = fit->u.target; // continue with sort as if right was target + right = target; // continue with sort as if right was target break; } } @@ -565,6 +586,18 @@ void Layout::buildOrdinalOverrideMap() void Layout::doPass() { + const bool log = false; + if ( log ) { + fprintf(stderr, "Unordered atoms:\n"); + 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; + fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); + } + } + } + // handle .o files that cannot have their atoms rearranged this->buildFollowOnTables(); @@ -574,19 +607,22 @@ void Layout::doPass() // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - //fprintf(stderr, "sorting section %s\n", sect->sectionName()); + if ( sect->type() == ld::Section::typeTempAlias ) + continue; + if ( log ) fprintf(stderr, "sorting section %s\n", sect->sectionName()); std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); } - //fprintf(stderr, "Sorted atoms:\n"); - //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; - // fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); - // } - //} - + if ( log ) { + fprintf(stderr, "Sorted atoms:\n"); + 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; + fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); + } + } + } } diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index c9eb46c..e957e2b 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -831,6 +831,12 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: printf(", then store as ARM64 12-bit page offset of TLVP"); break; + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of lea of TLVP"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: + printf(", then store as ARM64 12-bit page offset of lea of TLVP"); + break; case ld::Fixup::kindStoreARM64PointerToGOT: printf(", then store as 64-bit pointer to GOT entry"); break; @@ -990,12 +996,6 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreTargetAddressARM64PageOff12: printf("ARM64 store 12-bit page offset of %s", referenceTargetAtomName(ref)); break; - case ld::Fixup::kindStoreTargetAddressARM64TLVPage21: - printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref)); - break; - case ld::Fixup::kindStoreTargetAddressARM64TLVPageOff12: - printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref)); - break; case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: printf("ARM64 store 21-bit pcrel ADRP to GOT for %s", referenceTargetAtomName(ref)); break; @@ -1003,10 +1003,22 @@ void dumper::dumpFixup(const ld::Fixup* ref) printf("ARM64 store 12-bit page offset of GOT of %s", referenceTargetAtomName(ref)); break; case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: - printf("ARM64 store 21-bit pcrel ADRP for lea of %s", referenceTargetAtomName(ref)); + printf("ARM64 store 21-bit pcrel ADRP to GOT lea for %s", referenceTargetAtomName(ref)); break; case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: - printf("ARM64 store 12-bit page offset of lea of %s", referenceTargetAtomName(ref)); + printf("ARM64 store 12-bit page offset of GOT lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: + printf("ARM64 store 21-bit pcrel ADRP to lea for TLV for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: + printf("ARM64 store 12-bit page offset of lea for TLV of %s", referenceTargetAtomName(ref)); break; //default: // printf("unknown fixup"); diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 3da01c5..effd09b 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -221,7 +221,14 @@ const char* UnwindPrinter::functionName(pint_t addr, uint32_t* offset) for (uint32_t i=0; i < fSymbolCount; ++i) { uint8_t type = fSymbols[i].n_type(); if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) { - if ( fSymbols[i].n_value() == addr ) { + uint32_t value = fSymbols[i].n_value(); + if ( value == addr ) { + const char* r = &fStrings[fSymbols[i].n_strx()]; + return r; + } + if ( fSymbols[i].n_desc() & N_ARM_THUMB_DEF ) + value |= 1; + if ( value == addr ) { const char* r = &fStrings[fSymbols[i].n_strx()]; //fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r); return r; @@ -730,6 +737,8 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, c } #endif + + template <> const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) { @@ -759,6 +768,7 @@ const char* UnwindPrinter::personalityName(const macho_relocation_info bool UnwindPrinter::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr) { @@ -795,6 +805,7 @@ void UnwindPrinter::printObjectUnwindSection(bool showFunctionNames) } else { functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction); + funcAddress = entry->codeStart(); } if ( offsetInFunction == 0 ) printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr); @@ -990,7 +1001,7 @@ static void dump(const char* path, const std::set& onlyArchs, bool s if ( UnwindPrinter::validFile(p + offset) ) UnwindPrinter::make(p + offset, size, path, showFunctionNames); else - throw "in universal file, arm64 slice does not contain arm mach-o"; + throw "in universal file, arm64 slice does not contain arm64 mach-o"; break; #endif default: @@ -1062,6 +1073,7 @@ int main(int argc, const char* argv[]) #if SUPPORT_ARCH_arm64 onlyArchs.insert(CPU_TYPE_ARM64); #endif + onlyArchs.insert(CPU_TYPE_ARM); } // process each file diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 3f4647d..545b4a7 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -13,7 +13,7 @@ MYDIR=$(shell cd ../../bin;pwd) LD = ld OBJECTDUMP = ObjectDump MACHOCHECK = machocheck -OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/otool +OTOOL = xcrun otool REBASE = rebase DYLDINFO = dyldinfo @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path 2>/dev/null) +IOS_SDK = $(shell xcodebuild -sdk iphoneos8.0.internal -version Path 2>/dev/null) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) diff --git a/ld64/unit-tests/test-cases/alias-basic/Makefile b/ld64/unit-tests/test-cases/alias-basic/Makefile new file mode 100644 index 0000000..e457aeb --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-basic/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that code and data references can be redirected via aliases. +# + +CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang + +run: all + +all: + # verify aliases can redirect references to code and data + ${CC} -arch ${ARCH} ${CCFLAGS} main.c -c -o main.o + ${CC} -arch ${ARCH} ${ASMFLAGS} aliases.s -c -o aliases.o + ${CC} -arch ${ARCH} main.o aliases.o -o main.exe + nm -nm main.exe | grep _barHidden | grep " external " | ${FAIL_IF_STDIN} + # verify dead stripping can remove unused and undefined alias + ${CC} -arch ${ARCH} ${ASMFLAGS} aliases.s -c -o aliases2.o -DUNUSED_ALIAS=1 + ${CC} -arch ${ARCH} main.o aliases2.o -dead_strip -o main2.exe + nm -nm main2.exe | grep _barAlt | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main2.exe + +clean: + rm -rf *.o *.dump *.exe diff --git a/ld64/unit-tests/test-cases/alias-basic/aliases.s b/ld64/unit-tests/test-cases/alias-basic/aliases.s new file mode 100644 index 0000000..90c1149 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-basic/aliases.s @@ -0,0 +1,26 @@ + + .globl _main +_main = _mymain + + .globl _bar +_bar = _mybar + + .globl _barAlt +_barAlt = _mybar + + .private_extern _barHidden +_barHidden = _mybar + + .globl _barExtra +_barExtra = _barAlt + + .globl _result +_result = _myresult + + .globl _resultHidden +_resultHidden = _myresult + +#if UNUSED_ALIAS + .globl _unusedAlias +_unusedAlias = _unusedUndefined +#endif diff --git a/ld64/unit-tests/test-cases/alias-basic/main.c b/ld64/unit-tests/test-cases/alias-basic/main.c new file mode 100644 index 0000000..d89f421 --- /dev/null +++ b/ld64/unit-tests/test-cases/alias-basic/main.c @@ -0,0 +1,16 @@ +extern void bar(); +extern int result; + +int myresult = 1; + +int mymain() +{ + bar(); + return result; +} + +void mybar() +{ + +} + diff --git a/ld64/unit-tests/test-cases/alias-objects/Makefile b/ld64/unit-tests/test-cases/alias-objects/Makefile index f4bfdc8..7b7667d 100644 --- a/ld64/unit-tests/test-cases/alias-objects/Makefile +++ b/ld64/unit-tests/test-cases/alias-objects/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2014 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -31,14 +31,16 @@ include ${TESTROOT}/include/common.makefile # No differences means this test passes # +CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang + run: all all: - ${CC} ${ASMFLAGS} aliases.s -c -o aliases.${ARCH}.o - ${LD} -arch ${ARCH} -r -keep_private_externs aliases.${ARCH}.o -o aliases-r.${ARCH}.o - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.${ARCH}.o > aliases.${ARCH}.o.dump - ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.${ARCH}.o > aliases-r.${ARCH}.o.dump - ${PASS_IFF} diff aliases.${ARCH}.o.dump aliases-r.${ARCH}.o.dump + ${CC} -arch ${ARCH} ${ASMFLAGS} aliases.s -c -o aliases.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.o -o aliases-r.o + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.o > aliases.o.dump + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases-r.o > aliases-r.o.dump + ${PASS_IFF} diff aliases.o.dump aliases-r.o.dump clean: rm -rf *.o *.dump diff --git a/ld64/unit-tests/test-cases/alias-objects/aliases.s b/ld64/unit-tests/test-cases/alias-objects/aliases.s index 5e92d8d..b669233 100644 --- a/ld64/unit-tests/test-cases/alias-objects/aliases.s +++ b/ld64/unit-tests/test-cases/alias-objects/aliases.s @@ -31,14 +31,23 @@ _temp: nop _foo: nop nop +/* this should make an alias "_fooalt" for "_foo" */ .globl _fooalt .globl _fooalt2 -/* this should make an alias "_fooalt" for "_foo" */ _fooalt = _foo _fooalt2 = _foo + .global _myAlias +_myAlias = _myBase + + .global _myHiddenAlias + .private_extern _myHiddenAlias +_myHiddenAlias = _myHiddenBase + + + _bar: nop nop - .subsections_via_symbols \ No newline at end of file + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/alt-entry/Makefile b/ld64/unit-tests/test-cases/alt-entry/Makefile new file mode 100644 index 0000000..292be81 --- /dev/null +++ b/ld64/unit-tests/test-cases/alt-entry/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2014 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 -e works for dynamic executables. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -e _mymain -o main1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -e _foo libfoo.dylib -o main2 + + ${PASS_IFF_GOOD_MACHO} main1 + +clean: + rm -f main1 main2 libfoo.dylib diff --git a/ld64/unit-tests/test-cases/alt-entry/foo.c b/ld64/unit-tests/test-cases/alt-entry/foo.c new file mode 100644 index 0000000..1b1d09b --- /dev/null +++ b/ld64/unit-tests/test-cases/alt-entry/foo.c @@ -0,0 +1,7 @@ +#include + +int foo() +{ + fprintf(stdout, "hello foo\n"); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/alt-entry/main.c b/ld64/unit-tests/test-cases/alt-entry/main.c new file mode 100644 index 0000000..8af852a --- /dev/null +++ b/ld64/unit-tests/test-cases/alt-entry/main.c @@ -0,0 +1,7 @@ +#include + +int mymain() +{ + fprintf(stdout, "hello mymain\n"); + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/lto-r/Makefile b/ld64/unit-tests/test-cases/lto-r/Makefile new file mode 100644 index 0000000..49e0b68 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-r/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2014 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 -r mode preserves symbols with LTO +# + + +run: all + +all: + ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o + nm -nm foobar.o | grep _foo_hidden | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_static | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_weak_hidden | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_weak_global | grep "weak external " | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _foo_global | grep " external " | ${FAIL_IF_EMPTY} + nm -nm foobar.o | grep _bar | grep " external " | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r -keep_private_externs foo.o bar.o -o foobar2.o + nm -nm foobar2.o | grep _foo_hidden | grep "private external" | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_static | grep non-external | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_weak_hidden | grep "weak private external" | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_weak_global | grep "weak external " | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _foo_global | grep " external " | ${FAIL_IF_EMPTY} + nm -nm foobar2.o | grep _bar | grep " external " | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f foo.o bar.o foobar.o foobar2.o diff --git a/ld64/unit-tests/test-cases/lto-r/bar.c b/ld64/unit-tests/test-cases/lto-r/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-r/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/ld64/unit-tests/test-cases/lto-r/foo.c b/ld64/unit-tests/test-cases/lto-r/foo.c new file mode 100644 index 0000000..2918f9b --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-r/foo.c @@ -0,0 +1,38 @@ +static int var_static = 3; + +__attribute__((visibility("hidden"))) +int var_hidden = 4; + +int var_global = 5; + + +__attribute__((visibility("hidden"), weak)) +int var_weak_hidden = 4; + +__attribute__((weak)) +int var_weak_global = 5; + + + + +static int* foo_static() { return &var_static; } + +__attribute__((visibility("hidden"))) +int* foo_hidden() { return &var_hidden; } + + +int* foo_global() { return &var_global; } + + +__attribute__((visibility("hidden"),weak)) +int* foo_weak_hidden() { return &var_weak_hidden; } + + +__attribute__((weak)) +int* foo_weak_global() { return &var_weak_global; } + + +__attribute__((visibility("hidden"))) +void* keep[] = { &foo_static }; + + diff --git a/ld64/unit-tests/test-cases/lto-r/main.c b/ld64/unit-tests/test-cases/lto-r/main.c new file mode 100644 index 0000000..578d24b --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-r/main.c @@ -0,0 +1,15 @@ + +#include + + +void foo(int x) +{ + printf("hello, world %d\n", x); +} + +int main() +{ + foo(10); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/lto-rename_section/Makefile b/ld64/unit-tests/test-cases/lto-rename_section/Makefile new file mode 100644 index 0000000..17ba9c3 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_section/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -rename_sectione works with LTO +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_section,__DATA,__data,__RAM,__vars \ + -Wl,-rename_section,__TEXT,__text,__ROM,__code \ + -Wl,-rename_section,__TEXT,__eh_frame,__ROM,__eh_frame \ + -Wl,-rename_section,__TEXT,__cstring,__ROM,__const + size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + nm -m main.preload | grep __ROM | grep __code | grep _entry | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __RAM | grep __vars | grep _mystring | ${FAIL_IF_EMPTY} + size -l main.preload | grep __ROM | ${PASS_IFF_STDIN} + + + + +clean: + rm -f a.o b.o main.o main.preload diff --git a/ld64/unit-tests/test-cases/lto-rename_section/a.c b/ld64/unit-tests/test-cases/lto-rename_section/a.c new file mode 100644 index 0000000..c17b3e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_section/a.c @@ -0,0 +1,4 @@ + +extern const char* mystring; + +const char** myp = &mystring; diff --git a/ld64/unit-tests/test-cases/lto-rename_section/b.c b/ld64/unit-tests/test-cases/lto-rename_section/b.c new file mode 100644 index 0000000..b7ad5e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_section/b.c @@ -0,0 +1 @@ + const char* mystring = "hello"; diff --git a/ld64/unit-tests/test-cases/lto-rename_section/main.c b/ld64/unit-tests/test-cases/lto-rename_section/main.c new file mode 100644 index 0000000..8c9c61e --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_section/main.c @@ -0,0 +1,11 @@ + +extern const char** myp; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + return myp; +} + diff --git a/ld64/unit-tests/test-cases/lto-rename_segment/Makefile b/ld64/unit-tests/test-cases/lto-rename_segment/Makefile new file mode 100644 index 0000000..2f3286c --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_segment/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -rename_segment and -rename_section works with LTO +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto a.c -c -o a.o + ${CC} ${CCFLAGS} -flto b.c -c -o b.o + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_segment,__TEXT,__ROM \ + -Wl,-rename_segment,__DATA,__RAM \ + -Wl,-rename_section,__DATA,__data_extra,__RAM2,__data \ + -Wl,-exported_symbol,_get + size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + nm -m main.preload | grep __ROM | grep __text | grep _entry | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __ROM | grep __text | grep _get | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __RAM | grep __data | grep _mystring | ${FAIL_IF_EMPTY} + nm -m main.preload | grep __RAM2 | grep __data | grep _param | ${FAIL_IF_EMPTY} + size -l main.preload | grep __ROM | ${PASS_IFF_STDIN} + + +clean: + rm -f a.o b.o main.o main.preload diff --git a/ld64/unit-tests/test-cases/lto-rename_segment/a.c b/ld64/unit-tests/test-cases/lto-rename_segment/a.c new file mode 100644 index 0000000..c17b3e3 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_segment/a.c @@ -0,0 +1,4 @@ + +extern const char* mystring; + +const char** myp = &mystring; diff --git a/ld64/unit-tests/test-cases/lto-rename_segment/b.c b/ld64/unit-tests/test-cases/lto-rename_segment/b.c new file mode 100644 index 0000000..b7ad5e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_segment/b.c @@ -0,0 +1 @@ + const char* mystring = "hello"; diff --git a/ld64/unit-tests/test-cases/lto-rename_segment/main.c b/ld64/unit-tests/test-cases/lto-rename_segment/main.c new file mode 100644 index 0000000..c219503 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-rename_segment/main.c @@ -0,0 +1,18 @@ + +extern const char** myp; +extern const char* mystring; + + +__attribute__((section("__DATA,__data_extra"))) +int param = 0; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + param = i; + return myp; +} + +int get() { return param; } \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/lto-symbol-section-move/Makefile b/ld64/unit-tests/test-cases/lto-symbol-section-move/Makefile new file mode 100644 index 0000000..018087d --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-symbol-section-move/Makefile @@ -0,0 +1,60 @@ +## +# Copyright (c) 2014 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 interaction of -section_rename, -segment_rename, and -move_to_r._segment +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o -flto + ${CC} ${CCFLAGS} foo.c -c -o foo.o -flto + ${CC} ${CCFLAGS} other.c -c -o other.o -flto + ${LD} -arch ${ARCH} main.o foo.o other.o -preload -o main.preload \ + -e _foo -trace_symbol_layout \ + -move_to_ro_segment __ROM1 rom1.symbols \ + -move_to_rw_segment __RAM1 ram1.symbols + nm -m main.preload | grep _mainget | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _getpi | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _bar | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _foo | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _def | grep __RAM1 | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _ghi | grep __RAM1 | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com | grep __RAM1 | grep __bss | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _abc | grep __RAM1 | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com4 | grep __RAM1 | grep __bss | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _main | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _version | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _otherget | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _mylocal | grep __TEXT | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _x | grep __DATA | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _all | grep __DATA | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _x | grep __DATA | grep __data | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com5 | grep __DATA | grep __bss | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f main.preload main.o other.o foo.o diff --git a/ld64/unit-tests/test-cases/lto-symbol-section-move/foo.c b/ld64/unit-tests/test-cases/lto-symbol-section-move/foo.c new file mode 100644 index 0000000..9360585 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-symbol-section-move/foo.c @@ -0,0 +1,38 @@ +extern void* otherget(); +extern int main(); +extern const char* version(); +extern void* mainget(); + +extern int def; +extern int ghi; +extern int com; + +double getpi() { return 3.1415926535; } + +void bar() +{ +} + + + +extern void* __dso_handle; +void* x = &__dso_handle; + +int abc = 10; + + +int com3; +int com4; +int com5; + +extern void* foo(); + +void* all[] = { &main, &version, &mainget, &getpi, &otherget, + &bar, &foo, &x, &abc, &def, &ghi, &com, &com3, &com4, &com5 }; + + +void* foo() +{ + return all; +} + diff --git a/ld64/unit-tests/test-cases/lto-symbol-section-move/main.c b/ld64/unit-tests/test-cases/lto-symbol-section-move/main.c new file mode 100644 index 0000000..ce97d92 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-symbol-section-move/main.c @@ -0,0 +1,37 @@ +extern void* otherget(); + +void mm() +{ +} + +static void s1() { + mm(); +} + +static void s2() { + mm(); +} + +int main() +{ + s1(); + s2(); + return 0; +} + +const char* version() { return "1.0"; } + +static int mylocal() +{ + return 0; +} + +void* mainget() { return mylocal; } + + +int def = 20; + +int ghi = 30; + +int com; + diff --git a/ld64/unit-tests/test-cases/lto-symbol-section-move/other.c b/ld64/unit-tests/test-cases/lto-symbol-section-move/other.c new file mode 100644 index 0000000..37d5047 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-symbol-section-move/other.c @@ -0,0 +1,9 @@ + +static int mylocal() +{ + return 1; +} + +void* otherget() { return mylocal; } + + diff --git a/ld64/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols b/ld64/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols new file mode 100644 index 0000000..ac26378 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-symbol-section-move/ram1.symbols @@ -0,0 +1,5 @@ +main.o:* +_abc +_com4 + + diff --git a/ld64/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols b/ld64/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols new file mode 100644 index 0000000..ce8e335 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-symbol-section-move/rom1.symbols @@ -0,0 +1,6 @@ +foo.o:* +_mainget + + + + diff --git a/ld64/unit-tests/test-cases/preload-section_order/Makefile b/ld64/unit-tests/test-cases/preload-section_order/Makefile new file mode 100644 index 0000000..653ccf7 --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-section_order/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -section_order works +# + +run: all + +all: + ${CC} ${CCFLAGS} extra.s -c -o extra.o + ${CC} ${CCFLAGS} more.s -c -o more.o + ${CC} ${CCFLAGS} main.c -c -o main.o + # test basic re-order of sections from different files + ${CC} ${CCFLAGS} main.o more.o extra.o -Wl,-preload -Wl,-pie -o main1.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-section_order,__MYSEG,__my_yyy:__my_ccc:__my_aaa + ${OTOOL} -l main1.preload | grep "sectname __my_" > main1.found + ${FAIL_IF_ERROR} diff main1.found main1.expected + # test renaming and re-ordering + ${CC} ${CCFLAGS} main.o more.o extra.o -Wl,-preload -Wl,-pie -o main2.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_section,__MYSEG,__my_yyy,__MYSEG,__my_iii \ + -Wl,-rename_section,__MYSEG,__my_ccc,__MYSEG,__my_jjj \ + -Wl,-section_order,__MYSEG,__my_iii:__my_aaa:__my_jjj + ${OTOOL} -l main2.preload | grep "sectname __my_" > main2.found + ${PASS_IFF} diff main1.found main1.expected + + + +clean: + rm extra.o more.o main.o main1.preload main1.found main2.preload main2.found diff --git a/ld64/unit-tests/test-cases/preload-section_order/extra.s b/ld64/unit-tests/test-cases/preload-section_order/extra.s new file mode 100644 index 0000000..9e07959 --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-section_order/extra.s @@ -0,0 +1,15 @@ + + + + .section __MYSEG,__my_xxx +_x: .long 0 + + + .section __MYSEG,__my_yyy +_y: .long 0 + + + .section __MYSEG,__my_zzz +_z: .long 0 + + diff --git a/ld64/unit-tests/test-cases/preload-section_order/main.c b/ld64/unit-tests/test-cases/preload-section_order/main.c new file mode 100644 index 0000000..a9a516f --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-section_order/main.c @@ -0,0 +1,5 @@ + + +void entry() { +} + diff --git a/ld64/unit-tests/test-cases/preload-section_order/main1.expected b/ld64/unit-tests/test-cases/preload-section_order/main1.expected new file mode 100644 index 0000000..06f643b --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-section_order/main1.expected @@ -0,0 +1,7 @@ + sectname __my_yyy + sectname __my_ccc + sectname __my_aaa + sectname __my_bbb + sectname __my_ddd + sectname __my_xxx + sectname __my_zzz diff --git a/ld64/unit-tests/test-cases/preload-section_order/main2.expected b/ld64/unit-tests/test-cases/preload-section_order/main2.expected new file mode 100644 index 0000000..6ac3c0e --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-section_order/main2.expected @@ -0,0 +1,7 @@ + sectname __my_iii + sectname __my_aaa + sectname __my_jjj + sectname __my_bbb + sectname __my_ddd + sectname __my_xxx + sectname __my_zzz diff --git a/ld64/unit-tests/test-cases/preload-section_order/more.s b/ld64/unit-tests/test-cases/preload-section_order/more.s new file mode 100644 index 0000000..6db305b --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-section_order/more.s @@ -0,0 +1,19 @@ + + + .section __MYSEG,__my_aaa +_a: .long 0 + + + .section __MYSEG,__my_bbb +_b: .long 0 + + + .section __MYSEG,__my_ccc +_c: .long 0 + + + + .section __MYSEG,__my_ddd +_d: .long 0 + + diff --git a/ld64/unit-tests/test-cases/preload-segment_order/Makefile b/ld64/unit-tests/test-cases/preload-segment_order/Makefile new file mode 100644 index 0000000..b5b4994 --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-segment_order/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -segment_order works +# + +run: all + +all: + ${CC} ${CCFLAGS} a.c -c -o a.o -static + ${CC} ${CCFLAGS} b.c -c -o b.o -static + ${CC} ${CCFLAGS} main.c -c -o main.o -static + ${CC} ${CCFLAGS} main.o a.o b.o -Wl,-preload -Wl,-pie -o main.preload \ + -e _entry -nostdlib -Wl,-segalign,0x20 -Wl,-seg1addr,0x200 \ + -Wl,-rename_section,__TEXT,__text,__ROM,__code \ + -Wl,-rename_section,__TEXT,__eh_frame,__ROM,__eh_frame \ + -Wl,-rename_section,__TEXT,__cstring,__ROM2,__strings \ + -Wl,-rename_section,__DATA,__data,__RAM,__inited \ + -Wl,-rename_section,__DATA,__common,__ZF,__zf \ + -Wl,-segment_order,__ROM2:__ROM:__RAM:__ZF + ${OTOOL} -l main.preload | grep -A2 LC_SEGMENT | grep segname > main-segs.found + ${PASS_IFF} diff main-segs.found main-segs.expected + + + +clean: + rm a.o b.o main.o main.preload main-segs.found diff --git a/ld64/unit-tests/test-cases/preload-segment_order/a.c b/ld64/unit-tests/test-cases/preload-segment_order/a.c new file mode 100644 index 0000000..b07e94c --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-segment_order/a.c @@ -0,0 +1,13 @@ + +extern const char* mystring; + +const char** myp = &mystring; + +int com; + +const char* inc() { + ++com; + return ""; +} + + diff --git a/ld64/unit-tests/test-cases/preload-segment_order/b.c b/ld64/unit-tests/test-cases/preload-segment_order/b.c new file mode 100644 index 0000000..89db6f1 --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-segment_order/b.c @@ -0,0 +1,9 @@ +const char* mystring = "hello"; + +int var = 10; + +const char* incget() { + ++var; + return mystring; +} + diff --git a/ld64/unit-tests/test-cases/preload-segment_order/main-segs.expected b/ld64/unit-tests/test-cases/preload-segment_order/main-segs.expected new file mode 100644 index 0000000..c378179 --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-segment_order/main-segs.expected @@ -0,0 +1,4 @@ + segname __ROM2 + segname __ROM + segname __RAM + segname __ZF diff --git a/ld64/unit-tests/test-cases/preload-segment_order/main.c b/ld64/unit-tests/test-cases/preload-segment_order/main.c new file mode 100644 index 0000000..8c9c61e --- /dev/null +++ b/ld64/unit-tests/test-cases/preload-segment_order/main.c @@ -0,0 +1,11 @@ + +extern const char** myp; + + +const char** entry(int i) { + if ( i ) { + *myp = "help"; + } + return myp; +} + diff --git a/ld64/unit-tests/test-cases/section-labels/main.c b/ld64/unit-tests/test-cases/section-labels/main.c index a8c4bfa..c7c4564 100644 --- a/ld64/unit-tests/test-cases/section-labels/main.c +++ b/ld64/unit-tests/test-cases/section-labels/main.c @@ -28,14 +28,14 @@ struct stuff { int a; int b; }; struct stuff stuff1 __attribute__ ((section ("__DATA,__my"))) = { 1, 2}; struct stuff stuff2 __attribute__ ((section ("__DATA,__my"))) = { 3 ,4 }; -extern struct stuff* stuff_start __asm("section$start$__DATA$__my"); -extern struct stuff* stuff_end __asm("section$end$__DATA$__my"); +extern struct stuff stuff_start __asm("section$start$__DATA$__my"); +extern struct stuff stuff_end __asm("section$end$__DATA$__my"); int main() { struct stuff* p; - for (p = stuff_start; p < stuff_end; ++p) { + for (p = &stuff_start; p < &stuff_end; ++p) { p->a = 0; } return 0; diff --git a/ld64/unit-tests/test-cases/symbol-section-move/Makefile b/ld64/unit-tests/test-cases/symbol-section-move/Makefile new file mode 100644 index 0000000..32a24b4 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-section-move/Makefile @@ -0,0 +1,60 @@ +## +# Copyright (c) 2014 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 interaction of -section_rename, -segment_rename, and -move_to_r._segment +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} other.c -c -o other.o + ${LD} -arch ${ARCH} main.o other.o -preload -o main.preload \ + -e _foo -trace_symbol_layout \ + -move_to_ro_segment __ROM1 rom1.symbols \ + -rename_section __TEXT __cstring __ROM2 mycstrings \ + -rename_segment __TEXT __ROM3 \ + -move_to_rw_segment __RAM1 ram1.symbols \ + -rename_section __DATA __data __RAM2 mydata \ + -rename_segment __DATA __RAM3 \ + -segment_order __ROM1:__ROM2:__ROM3:__RAM1:__RAM2:__RAM3 + nm -m main.preload | grep _foo | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _s1 | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _mylocal | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} + size -l main.preload | grep __cstring | ${FAIL_IF_STDIN} + size -l main.preload | grep mycstrings | ${FAIL_IF_EMPTY} + size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + nm -m main.preload | grep _mm | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _main | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _abc | grep __RAM1 | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com | grep __RAM1 | ${FAIL_IF_EMPTY} + size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + nm -m main.preload | grep _def | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _ghi | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f main.preload main.o other.o diff --git a/ld64/unit-tests/test-cases/symbol-section-move/main.c b/ld64/unit-tests/test-cases/symbol-section-move/main.c new file mode 100644 index 0000000..407d4ad --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-section-move/main.c @@ -0,0 +1,54 @@ + +void mm() +{ +} + +static void s1() { + mm(); +} + +static void s2() { + mm(); +} + +int main() +{ + s1(); + s2(); + return 0; +} + +const char* version() { return "1.0"; } + +static int mylocal() +{ + return 0; +} + +void* mainget() { return mylocal; } + +double getpi() { return 3.1415926535; } + +void foo() +{ +} + +void bar() +{ +} + +extern void* __dso_handle; +void* x = &__dso_handle; + +int abc = 10; + +int def = 20; + +int ghi = 30; + +int com; + +int com3; +int com4; +int com5; + diff --git a/ld64/unit-tests/test-cases/symbol-section-move/other.c b/ld64/unit-tests/test-cases/symbol-section-move/other.c new file mode 100644 index 0000000..37d5047 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-section-move/other.c @@ -0,0 +1,9 @@ + +static int mylocal() +{ + return 1; +} + +void* otherget() { return mylocal; } + + diff --git a/ld64/unit-tests/test-cases/symbol-section-move/ram1.symbols b/ld64/unit-tests/test-cases/symbol-section-move/ram1.symbols new file mode 100644 index 0000000..4b641d7 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-section-move/ram1.symbols @@ -0,0 +1,3 @@ +_com +_abc + diff --git a/ld64/unit-tests/test-cases/symbol-section-move/rom1.symbols b/ld64/unit-tests/test-cases/symbol-section-move/rom1.symbols new file mode 100644 index 0000000..1d50eda --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-section-move/rom1.symbols @@ -0,0 +1,6 @@ +_foo +_s1 +main.o:_mylocal + + + diff --git a/ld64/unit-tests/test-cases/weak_import-undefined/Makefile b/ld64/unit-tests/test-cases/weak_import-undefined/Makefile index 56e888f..1d5fbba 100644 --- a/ld64/unit-tests/test-cases/weak_import-undefined/Makefile +++ b/ld64/unit-tests/test-cases/weak_import-undefined/Makefile @@ -37,4 +37,4 @@ all: ${PASS_IFF_GOOD_MACHO} weak clean: - rm -rf main + rm -rf weak From 59884973592f182e2f9baf6df366a75ded0304ba Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 Oct 2015 10:50:02 +0100 Subject: [PATCH 17/48] 242 --- ld64/ld64.xcodeproj/project.pbxproj | 15 +-- ld64/src/abstraction/MachOFileAbstraction.hpp | 39 +++++- ld64/src/create_configure | 2 +- ld64/src/ld/InputFiles.cpp | 3 +- ld64/src/ld/Options.cpp | 71 +++++++--- ld64/src/ld/Options.h | 1 + ld64/src/ld/Resolver.cpp | 14 +- ld64/src/ld/ld.hpp | 2 +- .../parsers/libunwind/DwarfInstructions.hpp | 31 +++++ ld64/src/ld/parsers/lto_file.cpp | 3 +- ld64/src/ld/parsers/lto_file.h | 1 + .../src/ld/parsers/macho_relocatable_file.cpp | 102 ++++++++++++++- ld64/src/ld/parsers/macho_relocatable_file.h | 1 + ld64/src/ld/passes/compact_unwind.cpp | 70 ++++++++++ ld64/src/other/ObjectDump.cpp | 1 + ld64/src/other/unwinddump.cpp | 123 ++++++++++++++++++ 16 files changed, 431 insertions(+), 48 deletions(-) diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 854356e..b4d1749 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 42; + objectVersion = 46; objects = { /* Begin PBXAggregateTarget section */ @@ -697,10 +697,11 @@ F9023C3006D5A227001BBF46 /* Project object */ = { isa = PBXProject; attributes = { + LastUpgradeCheck = 0600; ORGANIZATIONNAME = "Apple Inc."; }; buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; - compatibilityVersion = "Xcode 2.4"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( @@ -1244,18 +1245,15 @@ F933D92409291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; + ONLY_ACTIVE_ARCH = YES; }; name = Debug; }; F933D92509291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; }; @@ -1282,8 +1280,6 @@ F9849FF810B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; - ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; }; @@ -1496,6 +1492,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_ENABLE_FIX_AND_CONTINUE = NO; @@ -1524,6 +1521,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; @@ -1544,6 +1542,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_ENABLE_FIX_AND_CONTINUE = NO; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 8b58efd..ae61d7c 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -254,9 +254,16 @@ // hack until arm64 headers are worked out -#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) -#define CPU_SUBTYPE_ARM64_ALL 0 -#define CPU_SUBTYPE_ARM64_V8 1 +#ifndef CPU_TYPE_ARM64 + #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#endif +#ifndef CPU_SUBTYPE_ARM64_ALL + #define CPU_SUBTYPE_ARM64_ALL 0 +#endif +#ifndef CPU_SUBTYPE_ARM64_V8 + #define CPU_SUBTYPE_ARM64_V8 1 +#endif + #define ARM64_RELOC_UNSIGNED 0 // for pointers #define ARM64_RELOC_SUBTRACTOR 1 // must be followed by a ARM64_RELOC_UNSIGNED @@ -370,6 +377,8 @@ #define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF +#define UNW_ARM_D31 287 + #ifndef LC_SOURCE_VERSION #define LC_SOURCE_VERSION 0x2A @@ -442,6 +451,26 @@ #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) #endif +#define UNWIND_ARM_MODE_MASK 0x0F000000 +#define UNWIND_ARM_MODE_FRAME 0x01000000 +#define UNWIND_ARM_MODE_FRAME_D 0x02000000 +#define UNWIND_ARM_MODE_DWARF 0x04000000 + +#define UNWIND_ARM_FRAME_STACK_ADJUST_MASK 0x00C00000 + +#define UNWIND_ARM_FRAME_FIRST_PUSH_R4 0x00000001 +#define UNWIND_ARM_FRAME_FIRST_PUSH_R5 0x00000002 +#define UNWIND_ARM_FRAME_FIRST_PUSH_R6 0x00000004 + +#define UNWIND_ARM_FRAME_SECOND_PUSH_R8 0x00000008 +#define UNWIND_ARM_FRAME_SECOND_PUSH_R9 0x00000010 +#define UNWIND_ARM_FRAME_SECOND_PUSH_R10 0x00000020 +#define UNWIND_ARM_FRAME_SECOND_PUSH_R11 0x00000040 +#define UNWIND_ARM_FRAME_SECOND_PUSH_R12 0x00000080 + +#define UNWIND_ARM_FRAME_D_REG_COUNT_MASK 0x00000F00 + +#define UNWIND_ARM_DWARF_SECTION_OFFSET 0x00FFFFFF struct ArchInfo { const char* archName; @@ -508,10 +537,10 @@ static const ArchInfo archInfoArray[] = { #define SUPPORT_ARCH_arm_any 1 #endif #if SUPPORT_ARCH_arm64 - { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, "arm64-", "", false, false }, + { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, "arm64-", "aarch64-", false, false }, #endif #if SUPPORT_ARCH_arm64v8 - { "arm64v8", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8, "arm64v8-", "", true, false }, + { "arm64v8", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8, "arm64v8-", "aarch64-", true, false }, #endif { NULL, 0, 0, NULL, NULL, false, false } }; diff --git a/ld64/src/create_configure b/ld64/src/create_configure index 8ca92be..f991231 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -11,7 +11,7 @@ else fi if [ -z "${RC_SUPPORTED_ARCHS}" ]; then - RC_SUPPORTED_ARCHS="i386 x86_64 x86_64h armv6 armv7 armv7s armv7m arm64" + RC_SUPPORTED_ARCHS="i386 x86_64 x86_64h armv6 armv7 armv7s armv7m armv7k arm64" fi for ANARCH in ${RC_SUPPORTED_ARCHS} diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 595b5d1..30c0875 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -293,6 +293,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.forceDwarfConversion= (_options.outputKind() == Options::kDyld); objOpts.neverConvertDwarf = !_options.needsUnwindInfoSection(); objOpts.verboseOptimizationHints = _options.verboseOptimizationHints(); + objOpts.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); objOpts.subType = _options.subArchitecture(); ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { @@ -586,7 +587,6 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { if ( ! dylibReader->installPathVersionSpecific() ) { - dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -613,7 +613,6 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan ld::dylib::File* dylibReader = dynamic_cast(reader); ld::archive::File* archiveReader = dynamic_cast(reader); if ( dylibReader != NULL ) { - dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index aa4f6ee..228a0df 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -601,6 +601,11 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fArchitectureName = "unknown architecture"; } +bool Options::armUsesZeroCostExceptions() const +{ + return ( (fArchitecture == CPU_TYPE_ARM) && (fSubArchitecture == CPU_SUBTYPE_ARM_V7K) ); +} + void Options::parseArch(const char* arch) { if ( arch == NULL ) @@ -3889,8 +3894,26 @@ void Options::reconfigureDefaults() } break; case CPU_TYPE_ARM: - fAddCompactUnwindEncoding = false; - fRemoveDwarfUnwindIfCompactExists = false; + if ( armUsesZeroCostExceptions() ) { + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + fAddCompactUnwindEncoding = false; + break; + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDynamicExecutable: + fAddCompactUnwindEncoding = true; + break; + } + } + else { + fAddCompactUnwindEncoding = false; + fRemoveDwarfUnwindIfCompactExists = false; + } break; case 0: // if -arch is missing, assume we don't want compact unwind info @@ -4270,25 +4293,32 @@ void Options::reconfigureDefaults() // ARM64 needs 16KB page size for user land code // make armv7[s] use 16KB pages in user land code for iOS 8 or later if ( fSegmentAlignment == 4096 ) { - if ( (fArchitecture == CPU_TYPE_ARM64) - || ((fArchitecture == CPU_TYPE_ARM) && (fIOSVersionMin >= ld::iOS_8_0) && - ((fSubArchitecture == CPU_SUBTYPE_ARM_V7S) || (fSubArchitecture == CPU_SUBTYPE_ARM_V7))) ) { - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + if ( (fArchitecture == CPU_TYPE_ARM64) + || ((fArchitecture == CPU_TYPE_ARM) && (fIOSVersionMin >= ld::iOS_8_0) && + ((fSubArchitecture == CPU_SUBTYPE_ARM_V7S) || (fSubArchitecture == CPU_SUBTYPE_ARM_V7))) ) { fSegmentAlignment = 4096*4; - break; - case Options::kStaticExecutable: - case Options::kKextBundle: - case Options::kObjectFile: - case Options::kPreload: - break; - } + } + break; + case Options::kStaticExecutable: + case Options::kKextBundle: + // 16KB segments for arm64 kexts + if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= ld::iOS_9_0) ) { + fSegmentAlignment = 4096*4; + } + break; + case Options::kObjectFile: + case Options::kPreload: + break; } } + + // linker should not convert dwarf unwind if .o file has compact unwind section switch ( fOutputKind ) { case Options::kDynamicExecutable: @@ -4316,6 +4346,13 @@ void Options::reconfigureDefaults() break; } + // Make sure -image_base matches alignment + uint64_t alignedBaseAddress = (fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment); + if ( alignedBaseAddress != fBaseAddress ) { + warning("base address 0x%llX is not properly aligned. Changing it to 0x%llX", fBaseAddress, alignedBaseAddress); + fBaseAddress = alignedBaseAddress; + } + } void Options::checkIllegalOptionCombinations() diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 8395fc9..39ee6fe 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -390,6 +390,7 @@ class Options linkerOptions() const { return fLinkerOptions; } FileInfo findFramework(const char* frameworkName) const; FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const; + bool armUsesZeroCostExceptions() const; const std::vector& sectionRenames() const { return fSectionRenames; } const std::vector& segmentRenames() const { return fSegmentRenames; } bool moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const; diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index a5405a9..8a72b67 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -330,17 +330,14 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons } } -static void userReadableSwiftVersion(uint8_t value, char versionString[64]) +static void userReadableSwiftVersion(uint8_t value, char versionString[32]) { switch (value) { case 1: strcpy(versionString, "1.0"); break; - case 2: - strcpy(versionString, "1.1"); - break; default: - sprintf(versionString, "unknown ABI version 0x%02X", value); + sprintf(versionString, "0x%02X", value); } } @@ -403,8 +400,8 @@ void Resolver::doFile(const ld::File& file) _internal.swiftVersion = file.swiftVersion(); } else if ( file.swiftVersion() != _internal.swiftVersion ) { - char fileVersion[64]; - char otherVersion[64]; + char fileVersion[32]; + char otherVersion[32]; userReadableSwiftVersion(file.swiftVersion(), fileVersion); userReadableSwiftVersion(_internal.swiftVersion, otherVersion); if ( file.swiftVersion() > _internal.swiftVersion ) { @@ -513,7 +510,7 @@ void Resolver::doFile(const ld::File& file) if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) { // only warn about linking against embedded dylib if it is built for iOS 8 or later if ( dylibFile->iOSMinVersion() >= iOS_8_0 ) - throwf("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); + warning("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); } } if ( _options.sharedRegionEligible() ) { @@ -1550,6 +1547,7 @@ void Resolver::linkTimeOptimize() optOpt.needsUnwindInfoSection = _options.needsUnwindInfoSection(); optOpt.keepDwarfUnwind = _options.keepDwarfUnwind(); optOpt.verboseOptimizationHints = _options.verboseOptimizationHints(); + optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); optOpt.llvmOptions = &_options.llvmOptions(); diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 8c3de4f..889cf2c 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -168,7 +168,7 @@ enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000, - iOS_Future=0x10000000}; + iOS_9_0=0x00090000, iOS_Future=0x10000000}; namespace relocatable { // diff --git a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp index 1835540..c14c38f 100644 --- a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -167,6 +167,14 @@ class DwarfInstructions const Registers_arm64&, const typename CFI_Parser::PrologInfo& prolog, char warningBuffer[1024]); + // arm specific variants + static bool isReturnAddressRegister(int regNum, const Registers_arm&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_arm&); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_arm&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + }; @@ -1962,6 +1970,29 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo } + + +// +// arm specific functions +// + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_arm&) +{ + return UNWIND_ARM_MODE_DWARF; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + return UNWIND_ARM_MODE_DWARF; +} + + } // namespace libunwind diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index f2cb8ca..fbba237 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -302,6 +302,7 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.forceDwarfConversion = false; objOpts.neverConvertDwarf = false; objOpts.verboseOptimizationHints = options.verboseOptimizationHints; + objOpts.armUsesZeroCostExceptions = options.armUsesZeroCostExceptions; objOpts.subType = 0; @@ -485,8 +486,6 @@ void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, co switch ( severity ) { #if LTO_API_VERSION >= 10 case LTO_DS_REMARK: - fprintf(stderr, "ld: LTO remark: %s\n", message); - break; #endif case LTO_DS_NOTE: case LTO_DS_WARNING: diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index 332215d..dcd6e36 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -57,6 +57,7 @@ struct OptimizeOptions { bool needsUnwindInfoSection; bool keepDwarfUnwind; bool verboseOptimizationHints; + bool armUsesZeroCostExceptions; cpu_type_t arch; const char* mcpu; const std::vector* llvmOptions; diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index d3990e3..49d333e 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -435,6 +435,7 @@ class Literal4Section : public FixedSizeSection virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; + virtual bool ignoreLabel(const char* label) const; }; template @@ -454,6 +455,7 @@ class Literal8Section : public FixedSizeSection virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; + virtual bool ignoreLabel(const char* label) const; }; template @@ -473,6 +475,7 @@ class Literal16Section : public FixedSizeSection virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; + virtual bool ignoreLabel(const char* label) const; }; @@ -1054,6 +1057,7 @@ class Parser bool forceDwarfConversion() { return _forceDwarfConversion; } bool verboseOptimizationHints() { return _verboseOptimizationHints; } bool neverConvertDwarf() { return _neverConvertDwarf; } + bool armUsesZeroCostExceptions() { return _armUsesZeroCostExceptions; } macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } @@ -1204,6 +1208,7 @@ class Parser bool _forceDwarfConversion; bool _neverConvertDwarf; bool _verboseOptimizationHints; + bool _armUsesZeroCostExceptions; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1632,6 +1637,8 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // respond to -t option if ( opts.logAllFiles ) printf("%s\n", _path); + + _armUsesZeroCostExceptions = opts.armUsesZeroCostExceptions; // parse start of mach-o file if ( ! parseLoadCommands() ) @@ -4069,6 +4076,12 @@ uint32_t Section::sectionNum(class Parser& parser) const template <> uint32_t CFISection::cfiCount(Parser& parser) { + if ( parser.armUsesZeroCostExceptions() ) { + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + return libunwind::CFI_Parser::getCFICount(oas, + this->_machOSection->addr(), this->_machOSection->size()); + } return 0; } @@ -4203,8 +4216,22 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { - // arm does not use zero cost exceptions - assert(count == 0); + if ( !parser.armUsesZeroCostExceptions() ) { + // most arm do not use zero cost exceptions + assert(count == 0); + return; + } + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); } @@ -4385,6 +4412,29 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, con } #endif +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", personalityEncoding); + } +} + + template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) @@ -4686,6 +4736,35 @@ const char* CUSection::personalityName(class Parser& parser, const } #endif +#if SUPPORT_ARCH_arm_any +template <> +const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) +{ + if ( reloc->r_extern() ) { + assert((reloc->r_type() == ARM_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); + } + else { + // support __LD, __compact_unwind personality entries which are pointer to personality non-lazy pointer + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t nlPointerAddr = *content; + Section* nlSection = parser.sectionForAddress(nlPointerAddr); + if ( nlSection->type() == ld::Section::typeCode ) { + // personality function is defined in this .o file, so this is a direct reference to it + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(nlPointerAddr); + return name; + } + else { + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(nlPointerAddr, nlSection->machoSection()); + const macho_nlist

& nlSymbol = parser.symbolFromIndex(symIndex); + return parser.nameFromSymbol(nlSymbol); + } + } +} +#endif + template const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info

* reloc) @@ -4709,7 +4788,7 @@ bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) template <> bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) { - return false; + return ((enc & UNWIND_ARM_MODE_MASK) == UNWIND_ARM_MODE_DWARF); } #endif @@ -5069,6 +5148,11 @@ uint32_t ImplicitSizeSection::appendAtoms(class Parser& parser, uint8_t* p return count; } +template +bool Literal4Section::ignoreLabel(const char* label) const +{ + return (label[0] == 'L') || (label[0] == 'l'); +} template unsigned long Literal4Section::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const @@ -5094,6 +5178,12 @@ bool Literal4Section::canCoalesceWith(const class Atom* atom, const ld::At } +template +bool Literal8Section::ignoreLabel(const char* label) const +{ + return (label[0] == 'L') || (label[0] == 'l'); +} + template unsigned long Literal8Section::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const { @@ -5128,6 +5218,11 @@ bool Literal8Section::canCoalesceWith(const class Atom* atom, const ld::At return false; } +template +bool Literal16Section::ignoreLabel(const char* label) const +{ + return (label[0] == 'L') || (label[0] == 'l'); +} template unsigned long Literal16Section::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const @@ -5373,7 +5468,6 @@ ld::Atom::Scope NonLazyPointerSection::scopeAtAddress(Parser& parser, pint return ld::Atom::scopeLinkageUnit; } - template const uint8_t* CFStringSection::targetContent(const class Atom* atom, const ld::IndirectBindingTable& ind, ContentType* ct, unsigned int* count) diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index 6d20847..92e9042 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -40,6 +40,7 @@ struct ParserOptions { bool forceDwarfConversion; bool neverConvertDwarf; bool verboseOptimizationHints; + bool armUsesZeroCostExceptions; uint32_t subType; }; diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 3ddd9b8..6c1308d 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -304,6 +304,13 @@ bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); } +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_ARM_MODE_MASK) == UNWIND_ARM_MODE_DWARF); +} + + template void UnwindInfoAtom::compressDuplicates(const std::vector& entries, std::vector& uniqueEntries) { @@ -418,6 +425,22 @@ void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, con _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + if ( fromFunc->isThumb() ) { + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 1)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndianLow24of32)); + } + else { + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); + } +} + template <> void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) { @@ -439,6 +462,13 @@ void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + template <> void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) { @@ -460,6 +490,13 @@ void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::At _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + template <> void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) { @@ -481,6 +518,13 @@ void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld:: _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + template <> void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) { @@ -502,6 +546,13 @@ void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + template <> void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) { @@ -526,6 +577,14 @@ void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} + @@ -839,6 +898,12 @@ static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::In case CPU_TYPE_ARM64: state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + if ( opts.armUsesZeroCostExceptions() ) + state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); + break; #endif default: assert(0 && "no compact unwind for arch"); @@ -892,6 +957,8 @@ template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld: template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; #endif +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian32; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian32; template CompactUnwindAtom::CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom, uint32_t startOffset, @@ -953,6 +1020,9 @@ static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, cons state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; #endif + case CPU_TYPE_ARM: + state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); + break; } } diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index e957e2b..ceab63c 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -1242,6 +1242,7 @@ static ld::relocatable::File* createReader(const char* path) objOpts.keepDwarfUnwind = false; objOpts.forceDwarfConversion = false; objOpts.verboseOptimizationHints = true; + objOpts.armUsesZeroCostExceptions = true; objOpts.subType = sPreferredSubArch; #if 1 if ( ! foundFatSlice ) { diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index effd09b..f91ece3 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -163,6 +163,25 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) } #endif +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; +} + template UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames) : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL), @@ -737,6 +756,91 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, c } #endif +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + *str = '\0'; + switch ( encoding & UNWIND_ARM_MODE_MASK ) { + case UNWIND_ARM_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_ARM_DWARF_SECTION_OFFSET); + break; + case UNWIND_ARM_MODE_FRAME: + case UNWIND_ARM_MODE_FRAME_D: + switch ( encoding & UNWIND_ARM_FRAME_STACK_ADJUST_MASK ) { + case 0x00000000: + strcpy(str, "std frame: "); + break; + case 0x00400000: + strcat(str, "std frame(sp adj 4): "); + break; + case 0x00800000: + strcat(str, "std frame(sp adj 8): "); + break; + case 0x00C00000: + strcat(str, "std frame(sp adj 12): "); + break; + } + if ( encoding & UNWIND_ARM_FRAME_FIRST_PUSH_R4 ) + strcat(str, "r4 "); + if ( encoding & UNWIND_ARM_FRAME_FIRST_PUSH_R5 ) + strcat(str, "r5 "); + if ( encoding & UNWIND_ARM_FRAME_FIRST_PUSH_R6 ) + strcat(str, "r6 "); + + if ( encoding & 0x000000F8) + strcat(str, " / "); + if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R8 ) + strcat(str, "r8 "); + if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R9 ) + strcat(str, "r9 "); + if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R10 ) + strcat(str, "r10 "); + if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R11 ) + strcat(str, "r11 "); + if ( encoding & UNWIND_ARM_FRAME_SECOND_PUSH_R12 ) + strcat(str, "r12 "); + + if ( (encoding & UNWIND_ARM_MODE_MASK) == UNWIND_ARM_MODE_FRAME_D ) { + switch ( encoding & UNWIND_ARM_FRAME_D_REG_COUNT_MASK ) { + case 0x00000000: + strcat(str, " / d8 "); + break; + case 0x00000100: + strcat(str, " / d8,d10 "); + break; + case 0x00000200: + strcat(str, " / d8,d10,d12 "); + break; + case 0x00000300: + strcat(str, " / d8,d10,d12,d14 "); + break; + case 0x00000400: + strcat(str, " / d12,d14 / d8,d9,d10 "); + break; + case 0x00000500: + strcat(str, " / d14 / d8,d9,d10,d11,d12"); + break; + case 0x00000600: + strcat(str, " / d8,d9,d10,d11,d12,d13,d14 "); + break; + case 0x00000700: + strcat(str, " / d8,d9,d10,d11,d12,d13,d14 "); + break; + default: + strcat(str, " / unknown D register usage "); + break; + } + } + + break; + default: + if ( encoding == 0 ) + strcpy(str, "no unwind information"); + else + strcpy(str, "unsupported compact unwind"); + break; + } +} template <> @@ -768,6 +872,14 @@ const char* UnwindPrinter::personalityName(const macho_relocation_info +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 bool UnwindPrinter::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr) @@ -1004,6 +1116,12 @@ static void dump(const char* path, const std::set& onlyArchs, bool s throw "in universal file, arm64 slice does not contain arm64 mach-o"; break; #endif + 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); } @@ -1021,6 +1139,9 @@ static void dump(const char* path, const std::set& onlyArchs, bool s UnwindPrinter::make(p, length, path, showFunctionNames); } #endif + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) { + UnwindPrinter::make(p, length, path, showFunctionNames); + } else { throw "not a known file type"; } @@ -1051,6 +1172,8 @@ int main(int argc, const char* argv[]) else if ( strcmp(arch, "arm64") == 0 ) onlyArchs.insert(CPU_TYPE_ARM64); #endif + else if ( strcmp(arch, "armv7k") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM); else throwf("unknown architecture %s", arch); } From 4d96282289c03ba3467c96f62e17685b12d170e3 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 Oct 2015 10:50:59 +0100 Subject: [PATCH 18/48] 242.2 --- ld64/src/ld/passes/branch_island.cpp | 58 ++++++++++++++++++- ld64/src/ld/passes/compact_unwind.cpp | 3 +- ld64/src/ld/passes/got.cpp | 53 ++++++++++++++--- .../test-cases/branch-islands/space.s | 21 +++++++ 4 files changed, 125 insertions(+), 10 deletions(-) diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 8efd10a..a8fb325 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -61,6 +61,39 @@ static bool _s_log = false; static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); +#if SUPPORT_ARCH_arm64 + +class ARM64BranchIslandAtom : public ld::Atom { +public: + ARM64BranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, target), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%p: ARM64 branch island to final target %s\n", + this, finalTarget.atom->name()); + } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(buffer, 0, 0x14000000); + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const char* _name; + ld::Fixup _fixup1; + ld::Fixup _fixup2; +}; +#endif + class ARMtoARMBranchIslandAtom : public ld::Atom { public: @@ -274,6 +307,12 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int return new ARMtoARMBranchIslandAtom(name, nextTarget, finalTarget); } break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + return new ARM64BranchIslandAtom(name, nextTarget, finalTarget); + break; +#endif default: assert(0 && "unexpected branch kind"); break; @@ -293,6 +332,11 @@ static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool see else return 4000000; // thumb1 can branch +/- 4MB break; +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + return 128000000; // arm64 can branch +/- 128MB + break; +#endif } assert(0 && "unexpected architecture"); return 0x100000000LL; @@ -310,6 +354,11 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra else return 3500000; // 0.5MB of branch islands per 4MB break; +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + return 124*1024*1024; // 4MB of branch islands per 128MB + break; +#endif } assert(0 && "unexpected architecture"); return 0x100000000LL; @@ -483,6 +532,10 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif haveBranch = true; break; default: @@ -652,9 +705,12 @@ void doPass(const Options& opts, ld::Internal& state) if ( !opts.allowBranchIslands() ) return; - // only ARM needs branch islands + // only ARM[64] needs branch islands switch ( opts.architecture() ) { case CPU_TYPE_ARM: +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: +#endif break; default: return; diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 6c1308d..1d257ee 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -144,8 +144,7 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 std::map personalityIndexMap; makePersonalityIndexes(uniqueEntries, personalityIndexMap); if ( personalityIndexMap.size() > 3 ) { - warning("too many personality routines for compact unwind to encode"); - return; + throw "too many personality routines for compact unwind to encode"; } // put the most common encodings into the common table, but at most 127 of them diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 2b9da3a..66caf34 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -161,10 +161,43 @@ void doPass(const Options& opts, ld::Internal& internal) if ( opts.outputKind() == Options::kObjectFile ) return; + // pre-fill gotMap with existing non-lazy pointers + std::map gotMap; + for (ld::Internal::FinalSection* sect : internal.sections) { + if ( sect->type() != ld::Section::typeNonLazyPointer ) + continue; + for (const ld::Atom* atom : sect->atoms) { + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch (fit->kind) { + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = internal.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + default: + fprintf(stderr, "non-pointer is got entry\n"); + break; + } + break; + default: + break; + } + } + if ( target != NULL ) { + if (log) fprintf(stderr, "found existing got entry to %s\n", target->name()); + gotMap[target] = atom; + } + } + } + // 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; atomsReferencingGOT.reserve(128); for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { @@ -224,12 +257,13 @@ void doPass(const Options& opts, ld::Internal& internal) } else { // remember that we need to use GOT in this function - if ( log ) fprintf(stderr, "found GOT use in %s to %s\n", atom->name(), targetOfGOT->name()); + if ( log ) fprintf(stderr, "found GOT use in %s\n", atom->name()); if ( !atomUsesGOT ) { atomsReferencingGOT.push_back(atom); atomUsesGOT = true; } - gotMap[targetOfGOT] = NULL; + if ( gotMap.count(targetOfGOT) == 0 ) + gotMap[targetOfGOT] = NULL; // record weak_import attribute std::map::iterator pos = weakImportMap.find(targetOfGOT); if ( pos == weakImportMap.end() ) { @@ -282,11 +316,15 @@ void doPass(const Options& opts, ld::Internal& internal) #endif } - // make GOT entries - for (std::map::iterator it = gotMap.begin(); it != gotMap.end(); ++it) { - it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first], is64); + // make GOT entries + for (auto& entry : gotMap) { + if ( entry.second == NULL ) { + entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], is64); + if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first->name(), entry.first, entry.second); + } } - + + // update atoms to use GOT entries for (std::vector::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) { const ld::Atom* atom = *it; @@ -318,6 +356,7 @@ void doPass(const Options& opts, ld::Internal& internal) switch ( fitThatSetTarget->binding ) { case ld::Fixup::bindingsIndirectlyBound: case ld::Fixup::bindingDirectlyBound: + if ( log ) fprintf(stderr, "updating GOT use in %s to %s\n", atom->name(), targetOfGOT->name()); fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound; fitThatSetTarget->u.target = gotMap[targetOfGOT]; break; diff --git a/ld64/unit-tests/test-cases/branch-islands/space.s b/ld64/unit-tests/test-cases/branch-islands/space.s index a15a2c2..18ceb96 100644 --- a/ld64/unit-tests/test-cases/branch-islands/space.s +++ b/ld64/unit-tests/test-cases/branch-islands/space.s @@ -73,4 +73,25 @@ _space4: .space 2*1024*1024 #endif + +#if __arm64__ + + .text + .align 4 +_prejunk: + nop + nop + + // arm64 branches are +/- 128MB +_space1: + .space 120*1024*1024 +_space2: + .space 120*1024*1024 +_space3: + .space 120*1024*1024 + + +#endif + + .subsections_via_symbols From 0afb7f24275b6957dbca94eca83d6153a0b4f1c7 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 Oct 2015 10:54:07 +0100 Subject: [PATCH 19/48] 253.3 --- ld64/doc/man/man1/ld.1 | 32 +- ld64/ld64.xcodeproj/project.pbxproj | 36 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 43 +- ld64/src/create_configure | 22 +- ld64/src/ld/Bitcode.hpp | 87 ++ ld64/src/ld/HeaderAndLoadCommands.hpp | 126 +- ld64/src/ld/InputFiles.cpp | 59 +- ld64/src/ld/LinkEdit.hpp | 205 ++-- ld64/src/ld/LinkEditClassic.hpp | 14 +- ld64/src/ld/Options.cpp | 808 ++++++++++--- ld64/src/ld/Options.h | 95 +- ld64/src/ld/OutputFile.cpp | 354 +++++- ld64/src/ld/OutputFile.h | 20 +- ld64/src/ld/Resolver.cpp | 191 ++- ld64/src/ld/Resolver.h | 1 + ld64/src/ld/SymbolTable.cpp | 9 + ld64/src/ld/SymbolTable.h | 1 + ld64/src/ld/code-sign-blobs/memutils.h | 7 +- ld64/src/ld/ld.cpp | 130 +- ld64/src/ld/ld.hpp | 56 +- ld64/src/ld/lto_file.hpp | 642 ---------- ld64/src/ld/parsers/lto_file.cpp | 181 ++- ld64/src/ld/parsers/lto_file.h | 5 + ld64/src/ld/parsers/macho_dylib_file.cpp | 270 +++-- .../src/ld/parsers/macho_relocatable_file.cpp | 366 +++++- ld64/src/ld/parsers/macho_relocatable_file.h | 11 +- ld64/src/ld/parsers/opaque_section_file.cpp | 3 +- ld64/src/ld/parsers/textstub_dylib_file.cpp | 1072 +++++++++++++++++ ld64/src/ld/parsers/textstub_dylib_file.hpp | 43 + ld64/src/ld/passes/bitcode_bundle.cpp | 809 +++++++++++++ ld64/src/ld/passes/bitcode_bundle.h | 43 + ld64/src/ld/passes/branch_island.cpp | 5 +- ld64/src/ld/passes/branch_shim.cpp | 2 + ld64/src/ld/passes/dylibs.cpp | 1 + ld64/src/ld/passes/got.cpp | 25 +- ld64/src/ld/passes/huge.cpp | 2 + ld64/src/ld/passes/objc.cpp | 13 +- ld64/src/ld/passes/order.cpp | 14 +- ld64/src/ld/passes/stubs/stub_arm.hpp | 30 +- ld64/src/ld/passes/stubs/stub_arm64.hpp | 22 +- ld64/src/ld/passes/stubs/stubs.cpp | 13 +- ld64/src/ld/passes/tlvp.cpp | 7 +- ld64/src/other/ObjectDump.cpp | 4 +- ld64/src/other/dyldinfo.cpp | 344 ++++-- ld64/unit-tests/include/common.makefile | 13 +- .../dead_strip-live-if-ref-live/Makefile | 48 + .../dead_strip-live-if-ref-live/main.c | 29 + ld64/unit-tests/test-cases/tlv-basic/get.s | 113 ++ ld64/unit-tests/test-cases/tlv-basic/main.c | 19 +- 49 files changed, 5105 insertions(+), 1340 deletions(-) create mode 100644 ld64/src/ld/Bitcode.hpp delete mode 100644 ld64/src/ld/lto_file.hpp create mode 100644 ld64/src/ld/parsers/textstub_dylib_file.cpp create mode 100644 ld64/src/ld/parsers/textstub_dylib_file.hpp create mode 100644 ld64/src/ld/passes/bitcode_bundle.cpp create mode 100644 ld64/src/ld/passes/bitcode_bundle.h create mode 100644 ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile create mode 100644 ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index df4b5a4..1c118b8 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -117,6 +117,10 @@ Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when buildi The default. Implied by -dylib, -bundle, or -execute .It Fl static Produces a mach-o file that does not use the dyld. Only used building the kernel. +.It Fl preload +Produces a mach-o file in which the mach_header, load commands, and symbol table are +not in any segment. This output type is used for firmware or embedded development +where the segments are copied out of the mach-o into ROM/Flash. .It Fl arch Ar arch_name Specifies which architecture (e.g. ppc, ppc64, i386, x86_64) the output file should be. .It Fl o Ar path @@ -480,6 +484,7 @@ Logs a chain of references to .Ar symbol_name . Only applicable with -dead_strip . It can help debug why something that you think should be dead strip removed is not removed. +See -exported_symbols_list for syntax and use of wildcards. .It Fl print_statistics Logs information about the amount of memory and time the linker used. .It Fl t @@ -515,10 +520,31 @@ contains a list of non-global symbol names that should be remain in the output f symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use of wildcards. .El +.Ss Options for Bitcode build flow +.Bl -tag +.It Fl bitcode_bundle +Generates an embedded bitcode bundle in the output binary. The bitcode bundle is embedded in __LLVM, __bundle section. +This option requires all the object files, static libraries and user frameworks/dylibs contain bitcode. +Note: not all the linker options are supported to use together with -bitcode_bundle. +.It Fl bitcode_hide_symbol +Specifies this option together with -bitcode_bundle to hide all non-exported symbols from output bitcode bundle. +The hide symbol process might not be reversible. To obtain a reverse mapping file to recover all the symbols, use +-bitcode_symbol_map option. +.It Fl bitcode_symbol_map Ar path +Specifies the output for bitcode symbol reverse mapping (.bcsymbolmap). If +.Ar path +is an existing directory, UUID.bcsymbolmap will be written to that directory. +Otherwise, the reverse map will be written to a file at +.Ar path . +.El .Ss Rarely used Options .Bl -tag .It Fl v Prints the version of the linker. +.It Fl dirty_data_list Ar filename +Specifies a file containing the names of data symbols likely to be dirtied. +If the linker is creating a __DATA_DIRTY segment, those symbols will be moved +to that segment. .It Fl move_to_rw_segment Ar segment_name Ar filename Moves data symbols to another segment. The command line option specifies the target segment name and a path to a file containing a list of symbols to move. @@ -738,6 +764,10 @@ info in the temporary object file. 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). +.It Fl not_for_dyld_shared_cache +Normally, the linker will add extra info to dylibs with -install_name starting with /usr/lib or +/System/Library/ that allows the dylib to be placed into the dyld shared cache. Adding this option +tells the linker to not add that extra info. .El .Ss Obsolete Options .Bl -tag @@ -749,8 +779,6 @@ Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This o This is the default. This option is obsolete. .It Fl fvmlib Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. -.It Fl preload -Preload executables (MH_PRELOAD) are no longer supported. .It Fl sectobjectsymbols Ar segname Ar sectname Adding a local label at a section start is no longer supported. This option is obsolete. .It Fl nofixprebinding diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index b4d1749..1c5e622 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */; }; B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; }; F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; @@ -82,6 +83,7 @@ F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; + FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -241,6 +243,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bitcode_bundle.h; sourceTree = ""; }; + B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitcode_bundle.cpp; sourceTree = ""; }; + B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Bitcode.hpp; path = src/ld/Bitcode.hpp; sourceTree = ""; }; B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -333,6 +338,8 @@ F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = textstub_dylib_file.cpp; sourceTree = ""; usesTabs = 1; }; + FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = textstub_dylib_file.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -421,6 +428,8 @@ F9AA650B1051BD2B003E3539 /* passes */ = { isa = PBXGroup; children = ( + B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */, + B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */, F984A38010BB4B0D009E9878 /* branch_island.cpp */, F984A38110BB4B0D009E9878 /* branch_island.h */, F9AA44DA1294885F00CB8390 /* branch_shim.cpp */, @@ -466,6 +475,8 @@ F9AA65861051E750003E3539 /* parsers */ = { isa = PBXGroup; children = ( + FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */, + FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */, F91B7B0218987D5F0099486F /* libunwind */, F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, F9AA6785105700C2003E3539 /* opaque_section_file.h */, @@ -525,6 +536,7 @@ F9AA650B1051BD2B003E3539 /* passes */, F9AA65861051E750003E3539 /* parsers */, F933DC37092A82480083EAC8 /* Architectures.hpp */, + B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */, F9EA7582097882F3008B4F1D /* debugline.c */, F9EA7583097882F3008B4F1D /* debugline.h */, B3B672411406D42800A376BB /* Snapshot.cpp */, @@ -772,7 +784,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n"; + shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n"; showEnvVarsInLog = 0; }; F9CCF765144CE244007CB524 /* make configure.h */ = { @@ -894,6 +906,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */, F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, F9AA65891051E750003E3539 /* macho_relocatable_file.cpp in Sources */, @@ -918,6 +931,7 @@ F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */, F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */, B3B672421406D42800A376BB /* Snapshot.cpp in Sources */, + B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */, F9CC24191461FB4300A92174 /* blob.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1037,6 +1051,8 @@ F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1080,13 +1096,16 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; + ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CPLUSPLUSFLAGS)", ); OTHER_LDFLAGS = ( "-stdlib=libc++", + "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", + "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1100,6 +1119,8 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1151,7 +1172,9 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", + "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", + "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1168,6 +1191,7 @@ F933D92009291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1186,8 +1210,10 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; + ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1211,6 +1237,7 @@ F933D92109291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1221,6 +1248,7 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; OTHER_CPLUSPLUSFLAGS = ( @@ -1299,6 +1327,8 @@ F9849FFA10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1349,7 +1379,9 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", + "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", + "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1413,6 +1445,7 @@ F9849FFD10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1424,6 +1457,7 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; OTHER_CPLUSPLUSFLAGS = ( diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index ae61d7c..cfedc81 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -426,11 +426,18 @@ #define LOH_ARM64_ADRP_LDR_GOT 8 #endif +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 #endif - #ifndef CPU_SUBTYPE_ARM_V8 #define CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) #endif @@ -472,6 +479,40 @@ #define UNWIND_ARM_DWARF_SECTION_OFFSET 0x00FFFFFF + +// ( (delta-uleb128)+ )+ +#define DYLD_CACHE_ADJ_V1_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V1_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V1_ADRP 0x03 +#define DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT 0x10 // thru 0x1F +#define DYLD_CACHE_ADJ_V1_ARM_MOVT 0x20 // thru 0x2F + + +// Whole :== FromToSection+ +// FromToSection :== ToOffset+ +// ToOffset :== FromOffset+ +// FromOffset :== +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C + + + +// kind target-address fixup-addr [adj] + + + struct ArchInfo { const char* archName; cpu_type_t cpuType; diff --git a/ld64/src/create_configure b/ld64/src/create_configure index f991231..9ce654d 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -25,8 +25,24 @@ do fi done -echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h - +if [ -n "${RC_HIDE_TIDE}" ]; then + echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h + echo "#define SUPPORT_APPLE_TV 0" >> ${DERIVED_FILE_DIR}/configure.h +else + if [ -n "${DT_VARIANT}" -a "${DT_VARIANT}" != "PONDEROSA" ]; then + echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h + echo "#define SUPPORT_APPLE_TV 0" >> ${DERIVED_FILE_DIR}/configure.h + else + echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS} (tvOS)\"" >> ${DERIVED_FILE_DIR}/configure.h + echo "#define SUPPORT_APPLE_TV 1" >> ${DERIVED_FILE_DIR}/configure.h + fi +fi - +if [ -f "${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" ]; then + echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" > ${DERIVED_FILE_DIR}/linkExtras + echo "#define DEMANGLE_SWIFT 1" >> ${DERIVED_FILE_DIR}/configure.h +else + echo "" > ${DERIVED_FILE_DIR}/linkExtras +fi +echo "#define BITCODE_XAR_VERSION \"1.0\"" >> ${DERIVED_FILE_DIR}/configure.h diff --git a/ld64/src/ld/Bitcode.hpp b/ld64/src/ld/Bitcode.hpp new file mode 100644 index 0000000..68822d2 --- /dev/null +++ b/ld64/src/ld/Bitcode.hpp @@ -0,0 +1,87 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-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@ + */ + +#ifndef __BITCODE_HPP__ +#define __BITCODE_HPP__ + +#include + +namespace ld { + +class Bitcode { +public: + Bitcode(const uint8_t* content, uint32_t size) : _content(content), _size(size) { } + + virtual bool isMarker() const { return _size <= 1 ; } + virtual const uint8_t* getContent() const { return _content; } + virtual uint32_t getSize() const { return _size; } +private: + const uint8_t* _content; + uint32_t _size; +}; + +class LLVMBitcode : public Bitcode { +public: + LLVMBitcode(const uint8_t* content, uint32_t size, const uint8_t* cmd, uint32_t cmdSize) : + Bitcode(content, size), _cmdline(cmd), _cmdSize(cmdSize) { } + + virtual const uint8_t* getCmdline() const { return _cmdline; } + virtual uint32_t getCmdSize() const { return _cmdSize; } + virtual const char* getBitcodeName() const { return "llvm"; } +private: + const uint8_t* _cmdline; + uint32_t _cmdSize; +}; + +class ClangBitcode : public LLVMBitcode { +public: + ClangBitcode(const uint8_t* content, uint32_t size, const uint8_t* cmd, uint32_t cmdSize) : + LLVMBitcode(content, size, cmd, cmdSize) { } + virtual const char* getBitcodeName() const override { return "clang"; } +}; + +class SwiftBitcode : public LLVMBitcode { +public: + SwiftBitcode(const uint8_t* content, uint32_t size, const uint8_t* cmd, uint32_t cmdSize) : + LLVMBitcode(content, size, cmd, cmdSize) { } + virtual const char* getBitcodeName() const override { return "swift"; } +}; + +class AsmBitcode : public Bitcode { +public: + AsmBitcode(const uint8_t* content, uint32_t size) : Bitcode(content, size) { } + + virtual bool isMarker() const override { return false; } +}; + +class BundleBitcode : public Bitcode { +public: + BundleBitcode(const uint8_t* content, uint32_t size) : + Bitcode(content, size) { } +}; + +} + + +#endif /* defined(__BITCODE_HPP__) */ diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 3629af5..b47acff 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -49,6 +49,9 @@ class HeaderAndLoadCommandsAbtract : public ld::Atom virtual void setUUID(const uint8_t digest[16]) = 0; virtual void recopyUUIDCommand() = 0; + virtual const uint8_t* getUUID() const = 0; + virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd, + uint64_t& sectOffset, uint64_t& sectEnd) const = 0; }; template @@ -68,6 +71,9 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract // overrides of HeaderAndLoadCommandsAbtract virtual void setUUID(const uint8_t digest[16]) { memcpy(_uuid, digest, 16); } virtual void recopyUUIDCommand(); + virtual const uint8_t* getUUID() const { return &_uuid[0]; } + virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd, + uint64_t& sectOffset, uint64_t& sectEnd) const; private: typedef typename A::P P; @@ -107,7 +113,6 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract uint8_t* copySubUmbrellaLoadCommand(uint8_t* p, const char* name) const; uint8_t* copyFunctionStartsLoadCommand(uint8_t* p) const; uint8_t* copyDataInCodeLoadCommand(uint8_t* p) const; - uint8_t* copyDependentDRLoadCommand(uint8_t* p) const; uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; uint8_t* copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector&) const; uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const; @@ -137,7 +142,6 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract bool _hasFunctionStartsLoadCommand; bool _hasDataInCodeLoadCommand; bool _hasSourceVersionLoadCommand; - bool _hasDependentDRInfo; bool _hasOptimizationHints; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; @@ -223,11 +227,12 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: } _hasRPathLoadCommands = (_options.rpaths().size() != 0); _hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL); - _hasVersionLoadCommand = _options.addVersionLoadCommand(); + _hasVersionLoadCommand = _options.addVersionLoadCommand() || + (!state.objectFileFoundWithNoVersion && (_options.outputKind() == Options::kObjectFile) + && ((_options.platform() != Options::kPlatformUnknown) || (state.derivedPlatformLoadCommand != 0)) ); _hasFunctionStartsLoadCommand = _options.addFunctionStarts(); _hasDataInCodeLoadCommand = _options.addDataInCodeInfo(); _hasSourceVersionLoadCommand = _options.needsSourceVersionLoadCommand(); - _hasDependentDRInfo = _options.needsDependentDRInfo(); _dylibLoadCommmandsCount = _writer.dylibCount(); _allowableClientLoadCommmandsCount = _options.allowableClients().size(); _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); @@ -326,6 +331,31 @@ unsigned int HeaderAndLoadCommandsAtom::segmentCount() const return count; } +template +bool HeaderAndLoadCommandsAtom::bitcodeBundleCommand(uint64_t &cmdOffset, uint64_t &cmdEnd, + uint64_t §Offset, uint64_t §End) const +{ + if ( _options.outputKind() == Options::kObjectFile ) { + return false; + } + cmdOffset = sizeof(macho_header

); + const char* lastSegName = ""; + for (std::vector::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( strcmp(lastSegName, (*it)->segmentName()) != 0 ) { + lastSegName = (*it)->segmentName(); + cmdOffset += sizeof(macho_segment_command

); + } + if ( strcmp((*it)->segmentName(), "__LLVM") == 0 && strcmp((*it)->sectionName(), "__bundle") == 0 ) { + sectOffset = (*it)->fileOffset; + sectEnd = (*(it + 1))->fileOffset; + cmdEnd = cmdOffset + sizeof(macho_section

); + return true; + } + if ( ! (*it)->isSectionHidden() ) + cmdOffset += sizeof(macho_section

); + } + return false; +} template uint64_t HeaderAndLoadCommandsAtom::size() const @@ -427,9 +457,6 @@ uint64_t HeaderAndLoadCommandsAtom::size() const } } - if ( _hasDependentDRInfo ) - sz += sizeof(macho_linkedit_data_command

); - if ( _hasOptimizationHints ) sz += sizeof(macho_linkedit_data_command

); @@ -507,9 +534,6 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const } } - if ( _hasDependentDRInfo ) - ++count; - if ( _hasOptimizationHints ) ++count; @@ -692,6 +716,7 @@ struct SegInfo { SegInfo(const char* n, const Options&); const char* segName; uint32_t nonHiddenSectionCount; + uint32_t nonSectCreateSections; uint32_t maxProt; uint32_t initProt; std::vector sections; @@ -699,7 +724,7 @@ struct SegInfo { SegInfo::SegInfo(const char* n, const Options& opts) - : segName(n), nonHiddenSectionCount(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n)) + : segName(n), nonHiddenSectionCount(0), nonSectCreateSections(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n)) { } @@ -722,6 +747,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* return S_REGULAR | S_ATTR_NO_DEAD_STRIP; else if ( (strncmp(sect->sectionName(), "__objc_nlcatlist", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (_options.outputKind() == Options::kObjectFile) && !sect->atoms.empty() && sect->atoms.front()->dontDeadStripIfReferencesLive() ) + return S_REGULAR | S_ATTR_LIVE_SUPPORT; else return S_REGULAR; case ld::Section::typeCode: @@ -841,6 +868,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* return S_REGULAR; case ld::Section::typeDebug: return S_REGULAR | S_ATTR_DEBUG; + case ld::Section::typeSectCreate: + return S_REGULAR; } return S_REGULAR; } @@ -885,6 +914,9 @@ uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const } if ( ! sect->isSectionHidden() ) segs.back().nonHiddenSectionCount++; + if ( sect->type() != ld::Section::typeSectCreate ) + segs.back().nonSectCreateSections++; + segs.back().sections.push_back(sect); } // write out segment load commands for each section with trailing sections @@ -921,7 +953,8 @@ uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const segCmd->set_maxprot(si.maxProt); segCmd->set_initprot(si.initProt); segCmd->set_nsects(si.nonHiddenSectionCount); - segCmd->set_flags(0); + segCmd->set_flags(si.nonSectCreateSections ? 0 : SG_NORELOC); // FIXME, really should check all References + p += sizeof(macho_segment_command

); macho_section

* msect = (macho_section

*)p; for (std::vector::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) { @@ -1104,20 +1137,40 @@ 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::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

)); - cmd->set_version((uint32_t)macVersion); - cmd->set_sdk(_options.sdkVersion()); - } - else { - cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); - cmd->set_cmdsize(sizeof(macho_version_min_command

)); - cmd->set_version((uint32_t)iOSVersion); - cmd->set_sdk(_options.sdkVersion()); + switch (_options.platform()) { + case Options::kPlatformUnknown: + assert(_state.derivedPlatformLoadCommand != 0 && "unknown platform"); + cmd->set_cmd(_state.derivedPlatformLoadCommand); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(0); + break; + case Options::kPlatformOSX: + cmd->set_cmd(LC_VERSION_MIN_MACOSX); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; + case Options::kPlatformiOS: + cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; + case Options::kPlatformWatchOS: + cmd->set_cmd(LC_VERSION_MIN_WATCHOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + cmd->set_cmd(LC_VERSION_MIN_TVOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; +#endif } return p + sizeof(macho_version_min_command

); } @@ -1158,7 +1211,7 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) cons template <> uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const { - return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); + return this->alignedSize(16 + 42*4); // base size + x86_THREAD_STATE64_COUNT * 4 } template <> @@ -1169,8 +1222,8 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) c macho_thread_command

* cmd = (macho_thread_command

*)p; cmd->set_cmd(LC_UNIXTHREAD); cmd->set_cmdsize(threadLoadCommandSize()); - cmd->set_flavor(x86_THREAD_STATE64); - cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_flavor(4); // x86_THREAD_STATE64 + cmd->set_count(42); // x86_THREAD_STATE64_COUNT cmd->set_thread_register(16, start); // rip if ( _options.hasCustomStack() ) cmd->set_thread_register(7, _options.customStackAddr()); // r1 @@ -1411,18 +1464,6 @@ uint8_t* HeaderAndLoadCommandsAtom::copyLinkerOptionsLoadCommand(uint8_t* p, } -template -uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) const -{ - macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

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

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

); -} - - template uint8_t* HeaderAndLoadCommandsAtom::copyOptimizationHintsLoadCommand(uint8_t* p) const @@ -1546,9 +1587,6 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const } } - if ( _hasDependentDRInfo ) - p = this->copyDependentDRLoadCommand(p); - if ( _hasOptimizationHints ) p = this->copyOptimizationHintsLoadCommand(p); diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 30c0875..9eadc2e 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -58,6 +58,7 @@ #include "InputFiles.h" #include "macho_relocatable_file.h" #include "macho_dylib_file.h" +#include "textstub_dylib_file.hpp" #include "archive_file.h" #include "lto_file.h" #include "opaque_section_file.h" @@ -294,7 +295,17 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.neverConvertDwarf = !_options.needsUnwindInfoSection(); objOpts.verboseOptimizationHints = _options.verboseOptimizationHints(); objOpts.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); + objOpts.simulator = _options.targetIOSSimulator(); + objOpts.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); objOpts.subType = _options.subArchitecture(); + objOpts.platform = _options.platform(); + objOpts.minOSVersion = _options.minOSversion(); + // workaround for strip -S + // when ld -r has single input file, set the srcKind to kSourceSingle so __LLVM segment will be kept + if (_options.outputKind() == Options::kObjectFile && _options.getInputFiles().size() == 1) + objOpts.srcKind = ld::relocatable::File::kSourceSingle; + else + objOpts.srcKind = ld::relocatable::File::kSourceObj; ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { OSAtomicAdd64(len, &_totalObjectSize); @@ -310,7 +321,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib return objResult; } - // see if it is a dynamic library + // see if it is a dynamic library (or text-based dynamic library) ld::dylib::File* dylibResult; bool dylibsNotAllowed = false; switch ( _options.outputKind() ) { @@ -321,6 +332,10 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib if ( dylibResult != NULL ) { return dylibResult; } + dylibResult = textstub::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); + if ( dylibResult != NULL ) { + return dylibResult; + } break; case Options::kStaticExecutable: case Options::kDyld: @@ -331,7 +346,6 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib break; } - // see if it is a static library ::archive::ParserOptions archOpts; archOpts.objOpts = objOpts; @@ -341,6 +355,12 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib archOpts.objcABI2 = _options.objCABIVersion2POverride(); archOpts.verboseLoad = _options.whyLoad(); archOpts.logAllFiles = _options.logAllFiles(); + // Set ObjSource Kind, libclang_rt is compiler static library + const char* libName = strrchr(info.path, '/'); + if ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) ) + archOpts.objOpts.srcKind = ld::relocatable::File::kSourceCompilerArchive; + else + archOpts.objOpts.srcKind = ld::relocatable::File::kSourceArchive; ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts); if ( archiveResult != NULL ) { OSAtomicAdd64(len, &_totalArchiveSize); @@ -587,6 +607,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -613,6 +634,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan ld::dylib::File* dylibReader = dynamic_cast(reader); ld::archive::File* archiveReader = dynamic_cast(reader); if ( dylibReader != NULL ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -775,20 +797,27 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) _inferredArch = true; // scan all input files, looking for a thin .o file. // the first one found is presumably the architecture to link - uint8_t buffer[sizeof(mach_header_64)]; + uint8_t buffer[4096]; const std::vector& files = opts.getInputFiles(); for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { int fd = ::open(it->path, O_RDONLY, 0); if ( fd != -1 ) { - ssize_t amount = read(fd, buffer, sizeof(buffer)); - ::close(fd); - if ( amount >= (ssize_t)sizeof(buffer) ) { - cpu_type_t type; - cpu_subtype_t subtype; - if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype) ) { - opts.setArchitecture(type, subtype); - *archName = opts.architectureName(); - return; + struct stat stat_buf; + if ( fstat(fd, &stat_buf) != -1) { + ssize_t readAmount = stat_buf.st_size; + if ( 4096 < readAmount ) + readAmount = 4096; + ssize_t amount = read(fd, buffer, readAmount); + ::close(fd); + if ( amount >= readAmount ) { + cpu_type_t type; + cpu_subtype_t subtype; + Options::Platform platform; + if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype, &platform) ) { + opts.setArchitecture(type, subtype, platform); + *archName = opts.architectureName(); + return; + } } } } @@ -797,11 +826,11 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) // no thin .o files found, so default to same architecture this tool was built as warning("-arch not specified"); #if __i386__ - opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL); + opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, Options::kPlatformOSX); #elif __x86_64__ - opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL); + opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, Options::kPlatformOSX); #elif __arm__ - opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6); + opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, Options::kPlatformOSX); #else #error unknown default architecture #endif diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index 3420626..c4421ef 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -32,6 +32,7 @@ #include #include +#include #include "Options.h" #include "ld.hpp" @@ -81,6 +82,17 @@ class ByteStream { while( more ); } + void append_delta_encoded_uleb128_run(uint64_t start, const std::vector& locations) { + uint64_t lastAddr = start; + for(std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { + uint64_t nextAddr = *it; + uint64_t delta = nextAddr - lastAddr; + assert(delta != 0); + append_uleb128(delta); + lastAddr = nextAddr; + } + } + void append_string(const char* str) { for (const char* s = str; *s != '\0'; ++s) _data.push_back(*s); @@ -219,6 +231,8 @@ void RebaseInfoAtom::encode() const } mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); address += sizeof(pint_t); + if ( address >= curSegEnd ) + address = 0; } mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); @@ -969,6 +983,7 @@ void ExportInfoAtom::encode() const std::vector& exports = this->_writer._exportedAtoms; uint64_t imageBaseAddress = this->_writer.headerAndLoadCommandsSection->address; std::vector entries; + unsigned int padding = 0; entries.reserve(exports.size()); for (std::vector::const_iterator it = exports.begin(); it != exports.end(); ++it) { const ld::Atom* atom = *it; @@ -1035,6 +1050,13 @@ void ExportInfoAtom::encode() const entry.importName = NULL; entries.push_back(entry); } + + if (_options.sharedRegionEligible() && strncmp(atom->section().segmentName(), "__DATA", 6) == 0) { + // Maximum address is 64bit which is 10 bytes as a uleb128. Minimum is 1 byte + // Pad the section out so we can deal with addresses getting larger when __DATA segment + // is moved before __TEXT in dyld shared cache. + padding += 9; + } } // sort vector by -exported_symbols_order, and any others by address @@ -1043,6 +1065,10 @@ void ExportInfoAtom::encode() const // create trie mach_o::trie::makeTrie(entries, this->_encodedData.bytes()); + //Add additional data padding for the unoptimized shared cache + for (unsigned int i = 0; i < padding; ++i) + this->_encodedData.append_byte(0); + // align to pointer size this->_encodedData.pad_to_size(sizeof(pint_t)); @@ -1051,10 +1077,10 @@ void ExportInfoAtom::encode() const template -class SplitSegInfoAtom : public LinkEditAtom +class SplitSegInfoV1Atom : public LinkEditAtom { public: - SplitSegInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + SplitSegInfoV1Atom(const Options& opts, ld::Internal& state, OutputFile& writer) : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } // overrides of ld::Atom @@ -1083,10 +1109,10 @@ class SplitSegInfoAtom : public LinkEditAtom }; template -ld::Section SplitSegInfoAtom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); +ld::Section SplitSegInfoV1Atom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreX86PCRel32: @@ -1116,7 +1142,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind } template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreLittleEndian32: @@ -1132,7 +1158,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki } template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreLittleEndian32: @@ -1160,7 +1186,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki #if SUPPORT_ARCH_arm64 template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreARM64Page21: @@ -1188,7 +1214,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind #endif template -void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& locations) const +void SplitSegInfoV1Atom::uleb128EncodeAddresses(const std::vector& locations) const { pint_t addr = this->_options.baseAddress(); for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { @@ -1215,12 +1241,12 @@ void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& lo template -void SplitSegInfoAtom::encode() const +void SplitSegInfoV1Atom::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, it->extra); + this->addSplitSegInfo(it->fixupAddress, it->kind, it->extra); } // delta compress runs of addresses @@ -1298,6 +1324,108 @@ void SplitSegInfoAtom::encode() const _64bitPointerLocations.clear(); } + +template +class SplitSegInfoV2Atom : public LinkEditAtom +{ +public: + SplitSegInfoV2Atom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "split seg info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + + typedef uint32_t SectionIndexes; + typedef std::map > FromOffsetMap; + typedef std::map ToOffsetMap; + typedef std::map WholeMap; + + + static ld::Section _s_section; +}; + +template +ld::Section SplitSegInfoV2Atom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); + + +template +void SplitSegInfoV2Atom::encode() const +{ + // sort into group by adjustment kind + //fprintf(stderr, "_splitSegV2Infos.size=%lu\n", this->_writer._splitSegV2Infos.size()); + WholeMap whole; + for (const OutputFile::SplitSegInfoV2Entry& entry : this->_writer._splitSegV2Infos) { + //fprintf(stderr, "from=%d, to=%d\n", entry.fixupSectionIndex, entry.targetSectionIndex); + SectionIndexes index = entry.fixupSectionIndex << 16 | entry.targetSectionIndex; + ToOffsetMap& toOffsets = whole[index]; + FromOffsetMap& fromOffsets = toOffsets[entry.targetSectionOffset]; + fromOffsets[entry.referenceKind].push_back(entry.fixupSectionOffset); + } + + // Add marker that this is V2 data + this->_encodedData.reserve(8192); + this->_encodedData.append_byte(DYLD_CACHE_ADJ_V2_FORMAT); + + // stream out + // Whole :== FromToSection+ + this->_encodedData.append_uleb128(whole.size()); + for (auto& fromToSection : whole) { + uint8_t fromSectionIndex = fromToSection.first >> 16; + uint8_t toSectionIndex = fromToSection.first & 0xFFFF; + ToOffsetMap& toOffsets = fromToSection.second; + // FromToSection :== ToOffset+ + this->_encodedData.append_uleb128(fromSectionIndex); + this->_encodedData.append_uleb128(toSectionIndex); + this->_encodedData.append_uleb128(toOffsets.size()); + //fprintf(stderr, "from sect=%d, to sect=%d, count=%lu\n", fromSectionIndex, toSectionIndex, toOffsets.size()); + uint64_t lastToOffset = 0; + for (auto& fromToOffsets : toOffsets) { + uint64_t toSectionOffset = fromToOffsets.first; + FromOffsetMap& fromOffsets = fromToOffsets.second; + // ToOffset :== FromOffset+ + this->_encodedData.append_uleb128(toSectionOffset - lastToOffset); + this->_encodedData.append_uleb128(fromOffsets.size()); + for (auto& kindAndOffsets : fromOffsets) { + uint8_t kind = kindAndOffsets.first; + std::vector& fromOffsets = kindAndOffsets.second; + // FromOffset :== + this->_encodedData.append_uleb128(kind); + this->_encodedData.append_uleb128(fromOffsets.size()); + std::sort(fromOffsets.begin(), fromOffsets.end()); + uint64_t lastFromOffset = 0; + for (uint64_t offset : fromOffsets) { + this->_encodedData.append_uleb128(offset - lastFromOffset); + lastFromOffset = offset; + } + } + lastToOffset = toSectionOffset; + } + } + + + // always add zero byte to mark end + this->_encodedData.append_byte(0); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; +} + + + template class FunctionStartsAtom : public LinkEditAtom { @@ -1497,63 +1625,6 @@ void DataInCodeAtom::encode() const -// linker needs to cache "Designated Requirements" in linked binary -template -class DependentDRAtom : public LinkEditAtom -{ -public: - DependentDRAtom(const Options& opts, ld::Internal& state, OutputFile& writer) - : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } - - // overrides of ld::Atom - virtual const char* name() const { return "dependent dylib DR info"; } - // overrides of LinkEditAtom - virtual void encode() const; - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - static ld::Section _s_section; - -}; - -template -ld::Section DependentDRAtom::_s_section("__LINKEDIT", "__dependentDR", ld::Section::typeLinkEdit, true); - - -template -void DependentDRAtom::encode() const -{ - Security::SuperBlobCore, Security::kSecCodeMagicDRList, uint32_t>::Maker maker; - - uint32_t index = 0; - for(std::vector::iterator it=_state.dylibs.begin(); it != _state.dylibs.end(); ++it) { - const ld::dylib::File* dylib = *it; - Security::BlobCore* dylibDR = (Security::BlobCore*)dylib->codeSignatureDR(); - void* dup = NULL; - if ( dylibDR != NULL ) { - // Maker takes ownership of every blob added - // We need to make a copy here because dylib still owns the pointer returned by codeSignatureDR() - dup = ::malloc(dylibDR->length()); - ::memcpy(dup, dylibDR, dylibDR->length()); - } - maker.add(index, (Security::BlobCore*)dup); - ++index; - } - - Security::SuperBlob* topBlob = maker.make(); - const uint8_t* data = (uint8_t*)topBlob->data(); - for(size_t i=0; i < topBlob->length(); ++i) - _encodedData.append_byte(data[i]); - - this->_encodedData.pad_to_size(sizeof(pint_t)); - - this->_encoded = true; -} - - template class OptimizationHintsAtom : public LinkEditAtom diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 3389f5c..ee1c207 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -1614,9 +1614,17 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* { int len = 0; uint32_t otherHalf = 0; - uint32_t value = entry.toTarget->finalAddress()+entry.toAddend; - if ( entry.fromTarget != NULL ) - value -= (entry.fromTarget->finalAddress()+entry.fromAddend); + uint32_t value; + if ( entry.fromTarget != NULL ) { + // this is a sect-diff + value = (entry.toTarget->finalAddress()+entry.toAddend) - (entry.fromTarget->finalAddress()+entry.fromAddend); + } + else { + // this is an absolute address + value = entry.toAddend; + if ( !external ) + value += entry.toTarget->finalAddress(); + } switch ( entry.kind ) { case ld::Fixup::kindStoreARMLow16: len = 0; diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 228a0df..a638cb4 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -36,12 +36,19 @@ #include #include +#include +#include #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" #include "Snapshot.h" + +// from FunctionNameDemangle.h +extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length); + + // upward dependency on lto::version() namespace lto { extern const char* version(); @@ -143,11 +150,11 @@ Options::Options(int argc, const char* argv[]) fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), - fSharedRegionEligible(false), fPrintOrderFileStatistics(false), + fSharedRegionEligible(false), fSharedRegionEligibleForceOff(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false), - fUsingLazyDylibLinking(false), fEncryptable(true), + fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), @@ -171,16 +178,19 @@ Options::Options(int argc, const char* argv[]) fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false), fSourceVersionLoadCommand(false), fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), - fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false), fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), - fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), - fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), - fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), - fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), + fSharedRegionEncodingV2(false), fUseDataConstSegment(false), + fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), + fBundleBitcode(false), fHideSymbols(false), fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), + fIgnoreAutoLink(false), fPlatform(kPlatformUnknown), + fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) { this->checkForClassic(argc, argv); @@ -236,7 +246,7 @@ bool Options::interposable(const char* name) const bool Options::printWhyLive(const char* symbolName) const { - return ( fWhyLive.find(symbolName) != fWhyLive.end() ); + return fWhyLive.contains(symbolName); } @@ -325,7 +335,7 @@ uint32_t Options::maxSegProtection(const char* segName) const { // iPhoneOS always uses same protection for max and initial // simulator apps need to use MacOSX max-prot - if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fArchitecture != CPU_TYPE_I386) ) + if ( (fPlatform != kPlatformOSX) && !fTargetIOSSimulator ) return initialSegProtection(segName); for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -391,6 +401,17 @@ uint8_t Options::customSectionAlignment(const char* segName, const char* sectNam return 0; } +bool Options::segmentOrderAfterFixedAddressSegment(const char* segName) const +{ + bool nowPinned = false; + for (std::vector::const_iterator it=fSegmentOrder.begin(); it != fSegmentOrder.end(); ++it) { + if ( strcmp(*it, segName) == 0 ) + return nowPinned; + if ( hasCustomSegmentAddress(*it) ) + nowPinned = true; + } + return false; +} bool Options::hasExportedSymbolOrder() { @@ -549,9 +570,26 @@ const std::vector* Options::sectionOrder(const char* segName) const return NULL; } +uint32_t Options::minOSversion() const +{ + switch (fPlatform) { + case kPlatformiOS: + return iOSVersionMin(); + case kPlatformOSX: + return macosxVersionMin(); + case kPlatformWatchOS: + return watchOSVersionMin(); +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + return iOSVersionMin(); +#endif + default: + break; + } + return 0; +} - -void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) +void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::Platform platform) { for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) { @@ -560,10 +598,11 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fArchitectureName = t->archName; fHasPreferredSubType = t->isSubType; fArchSupportsThumb2 = t->supportsThumb2; + fPlatform = platform; switch ( type ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); @@ -575,7 +614,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) break; case CPU_TYPE_ARM: case CPU_TYPE_ARM64: - if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + if ( (fPlatform == kPlatformiOS) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); @@ -672,6 +711,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; + if ( checkForFile("%s/lib%s.tbd", dir, rootName, result) ) + return result; if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; } @@ -701,6 +742,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; + if ( lookForDylibs && checkForFile("%s/lib%s.tbd", dir, rootName, result) ) + return result; if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) @@ -732,31 +775,21 @@ Options::FileInfo Options::findFramework(const char* frameworkName) const Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const { - for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - // ??? Shouldn't we be using String here and just initializing it? - // ??? Use str.c_str () to pull out the string for the stat call. - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, rootName); - strcat(possiblePath, ".framework/"); - strcat(possiblePath, rootName); - if ( suffix != NULL ) { + for (const auto* path : fFrameworkSearchPaths) { + auto possiblePath = std::string(path).append("/").append(rootName).append(".framework/").append(rootName); + if ( suffix != nullptr ) { char realPath[PATH_MAX]; // no symlink in framework to suffix variants, so follow main symlink - if ( realpath(possiblePath, realPath) != NULL ) { - strcpy(possiblePath, realPath); - strcat(possiblePath, suffix); - } + if ( realpath(possiblePath.c_str(), realPath) != nullptr ) + possiblePath = std::string(realPath).append(suffix); } FileInfo result; - bool found = result.checkFileExists(*this, possiblePath); + bool found = result.checkFileExists(*this, (possiblePath + ".tbd").c_str()); + if ( !found ) + found = result.checkFileExists(*this, possiblePath.c_str()); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", - (found ? " " : " not "), possiblePath); + (found ? " " : " not "), possiblePath.c_str()); if ( found ) { return result; } @@ -768,68 +801,84 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } -Options::FileInfo Options::findFile(const char* path) const +Options::FileInfo Options::findFile(const std::string &path) const { FileInfo result; - // if absolute path and not a .o file, the use SDK prefix - if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { - const int pathLen = strlen(path); - for (std::vector::const_iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { - // ??? Shouldn't we be using String here? - const char* sdkPathDir = *it; - const int sdkPathDirLen = strlen(sdkPathDir); - char possiblePath[sdkPathDirLen+pathLen+4]; - strcpy(possiblePath, sdkPathDir); - if ( possiblePath[sdkPathDirLen-1] == '/' ) - possiblePath[sdkPathDirLen-1] = '\0'; - strcat(possiblePath, path); - if ( result.checkFileExists(*this, possiblePath) ) { + // if absolute path and not a .o file, then use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) { + auto tbdFile = path; + auto lastSlashIdx = tbdFile.find_last_of('/'); + auto lastDotIdx = tbdFile.find_last_of('.'); + if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) + tbdFile.erase(lastDotIdx, std::string::npos); + tbdFile.append(".tbd"); + + for (const auto* sdkPathDir : fSDKPaths) { + auto possiblePath = std::string(sdkPathDir) + tbdFile; + if ( result.checkFileExists(*this, possiblePath.c_str()) ) + return result; + possiblePath = std::string(sdkPathDir) + path; + if ( result.checkFileExists(*this, possiblePath.c_str()) ) return result; - } } } // try raw path - if ( result.checkFileExists(*this, path) ) { + { + std::string file = path; + auto lastDotIdx = file.find_last_of('.'); + if (lastDotIdx != std::string::npos) + file.erase(lastDotIdx, std::string::npos); + if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) + return result; + } + if ( result.checkFileExists(*this, path.c_str()) ) { return result; } + // try @executable_path substitution - if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { - char newPath[strlen(fExecutablePath) + strlen(path)]; + if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) { + char newPath[strlen(fExecutablePath) + path.size()]; strcpy(newPath, fExecutablePath); char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) + if ( addPoint != nullptr ) strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); + + std::string file = newPath; + auto lastDotIdx = file.find_last_of('.'); + if (lastDotIdx != std::string::npos) + file.erase(lastDotIdx, std::string::npos); + if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) { + return result; + } if ( result.checkFileExists(*this, newPath) ) { return result; } } // not found - throwf("file not found: %s", path); + throwf("file not found: %s", path.c_str()); } -Options::FileInfo Options::findFileUsingPaths(const char* path) const +Options::FileInfo Options::findFileUsingPaths(const std::string &path) const { FileInfo result; - const char* lastSlash = strrchr(path, '/'); - const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1]; + auto lastSlashPos = path.find_last_of('/'); + auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0; + auto leafName = path.substr(pos); // Is this in a framework? // /path/Foo.framework/Foo ==> true (Foo) // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) // /path/Foo.framework/Resources/Bar ==> false bool isFramework = false; - if ( lastSlash != NULL ) { - char frameworkDir[strlen(leafName) + 20]; - strcpy(frameworkDir, "/"); - strcat(frameworkDir, leafName); - strcat(frameworkDir, ".framework/"); - if ( strstr(path, frameworkDir) != NULL ) + if ( lastSlashPos != std::string::npos ) { + auto frameworkDir = std::string("/").append(leafName).append(".framework/"); + if ( path.rfind(frameworkDir) != std::string::npos ) isFramework = true; } @@ -838,35 +887,28 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) const // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, leafName); - strcat(possiblePath, ".framework"); - - //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName); - if ( checkForFile("%s/%s", possiblePath, leafName, result) ) + auto endPos = path.rfind(".framework"); + auto beginPos = path.find_last_of('/', endPos); + auto leafPath = path.substr(beginPos); + for (const auto* dir : fFrameworkSearchPaths) { + auto possiblePath = dir + leafPath; + if ( checkForFile("%s.%s", possiblePath.c_str(), "tbd", result) ) + return result; + if ( checkForFile("%s", possiblePath.c_str(), "", result) ) return result; } - } - else { + } else { // if this is a .dylib inside a framework, do not search -L paths - // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - int leafLen = strlen(leafName); - bool embeddedDylib = ( (leafLen > 6) - && (strcmp(&leafName[leafLen-6], ".dylib") == 0) - && (strstr(path, ".framework/") != NULL) ); + // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + bool embeddedDylib = ( (leafName.size() > 6) + && (leafName.find(".dylib", leafName.size()-6) != std::string::npos) + && (path.find(".framework/") != std::string::npos) ); if ( !embeddedDylib ) { - for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; + for (const auto* dir : fLibrarySearchPaths) { //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); - if ( checkForFile("%s/%s", dir, leafName, result) ) + if ( checkForFile("%s/%s", dir, std::string(leafName).append(".tbd").c_str(), result) ) + return result; + if ( checkForFile("%s/%s", dir, leafName.c_str(), result) ) return result; } } @@ -1070,6 +1112,23 @@ bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const } +std::vector Options::exportsData() const +{ + return fExportSymbols.data(); +} + + +std::vector Options::SetWithWildcards::data() const +{ + std::vector data; + for (NameSet::iterator it=regularBegin(); it != regularEnd(); ++it) { + data.push_back(*it); + } + for (std::vector::const_iterator it=fWildCard.begin(); it != fWildCard.end(); ++it) { + data.push_back(*it); + } + return data; +} bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const { @@ -1348,6 +1407,7 @@ void Options::setMacOSXVersionMin(const char* version) minorVersion = 255; } fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); + fPlatform = kPlatformOSX; } else { warning("unknown option to -macosx_version_min, not 10.x"); @@ -1368,18 +1428,52 @@ void Options::setIOSVersionMin(const char* version) unsigned int majorVersion = version[0] - '0'; unsigned int minorVersion = version[2] - '0'; fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + fPlatform = kPlatformiOS; +} + + +void Options::setWatchOSVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-watchos_version_min argument missing"; + if ( ! isdigit(version[0]) ) + throw "-watchos_version_min argument is not a number"; + if ( version[1] != '.' ) + throw "-watchos_version_min argument is missing period as second character"; + if ( ! isdigit(version[2]) ) + throw "-watchos_version_min argument is not a number"; + + unsigned int majorVersion = version[0] - '0'; + unsigned int minorVersion = version[2] - '0'; + fWatchOSVersionMin = (ld::WatchOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + fPlatform = kPlatformWatchOS; } + bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin) { if ( fMacVersionMin != ld::macVersionUnset ) { return ( fMacVersionMin >= requiredMacMin ); } + else if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediPhoneOSMin); + } else { - return ( fIOSVersionMin >= requirediPhoneOSMin); + return ( fIOSVersionMin >= requirediPhoneOSMin ); } } +bool Options::min_iOS(ld::IOSVersionMin requirediOSMin) +{ + if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediOSMin); + } + else { + return ( fIOSVersionMin >= requirediOSMin ); + } +} void Options::setWeakReferenceMismatchTreatment(const char* treatment) { @@ -1766,7 +1860,7 @@ void Options::addSection(const char* segment, const char* section, const char* p ::close(fd); // record section to create - ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; + ExtraSection info = { segment, section, path, (uint8_t*)p, (uint64_t)stat_buf.st_size }; fExtraSections.push_back(info); } @@ -1895,7 +1989,188 @@ void Options::warnObsolete(const char* arg) } +void Options::cannotBeUsedWithBitcode(const char* arg) +{ + if ( fBundleBitcode ) + throwf("%s and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together", arg); +} + +std::string Options::getVersionString32(uint32_t ver) const +{ + if (ver == 0 || ver >= 0x10000000) + return "0.0.0"; + + unsigned microVersion = ver & 0xFF; + unsigned minorVersion = (ver >> 8) & 0xFF; + unsigned majorVersion = (ver >> 16) & 0xFF; + std::stringstream versionString; + versionString << majorVersion << "." << minorVersion << "." << microVersion; + return versionString.str(); +} + +std::string Options::getVersionString64(uint64_t ver) const +{ + uint64_t a = (ver >> 40) & 0xFFFFFF; + uint64_t b = (ver >> 30) & 0x3FF; + uint64_t c = (ver >> 20) & 0x3FF; + uint64_t d = (ver >> 10) & 0x3FF; + uint64_t e = ver & 0x3FF; + std::stringstream versionString; + versionString << a << "." << b << "." << c << "." << d << "." << e; + return versionString.str(); +} + +std::string Options::getSDKVersionStr() const +{ + return getVersionString32(fSDKVersion); +} + +std::string Options::getPlatformStr() const +{ + switch (fPlatform) { + case Options::kPlatformOSX: + return "MacOSX"; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + return "iPhoneSimulator"; + else + return "iPhoneOS"; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + return "watchOS Simulator"; + else + return "watchOS"; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + return "AppleTVSimulator"; + else + return "AppleTVOS"; + break; +#endif + case Options::kPlatformUnknown: + return "Unknown"; + } +} + +std::vector Options::writeBitcodeLinkOptions() const +{ + std::vector linkCommand; + switch ( fOutputKind ) { + case Options::kDynamicLibrary: + linkCommand.push_back("-dylib"); + linkCommand.push_back("-compatibility_version"); + if ( fDylibCompatVersion != 0 ) { + linkCommand.push_back(getVersionString32(fDylibCompatVersion)); + } else { + linkCommand.push_back(getVersionString32(currentVersion32())); + } + if ( fDylibCurrentVersion != 0 ) { + linkCommand.push_back("-current_version"); + linkCommand.push_back(getVersionString64(fDylibCurrentVersion)); + } + linkCommand.push_back("-install_name"); + linkCommand.push_back(installPath()); + break; + case Options::kDynamicExecutable: + linkCommand.push_back("-execute"); + break; + case Options::kObjectFile: + linkCommand.push_back("-r"); + break; + default: + throwf("could not write bitcode options file output kind\n"); + } + if (!fImplicitlyLinkPublicDylibs) + linkCommand.push_back("-no_implicit_dylibs"); + + // Add deployment target. + // Platform is allowed to be unknown for "ld -r". + switch (fPlatform) { + case Options::kPlatformOSX: + linkCommand.push_back("-macosx_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fMacVersionMin)); + break; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + linkCommand.push_back("-ios_simulator_version_min"); + else + linkCommand.push_back("-ios_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + linkCommand.push_back("-watchos_simulator_version_min"); + else + linkCommand.push_back("-watchos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + linkCommand.push_back("-tvos_simulator_version_min"); + else + linkCommand.push_back("-tvos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#endif + case Options::kPlatformUnknown: + if ( fOutputKind != Options::kObjectFile ) { + throwf("platform is unknown for final bitcode bundle," + "deployment target and min version is required for -bitcode_bundle"); + } + break; + } + + + // entry name + if ( fEntryName ) { + linkCommand.push_back("-e"); + linkCommand.push_back(fEntryName); + } + + // Write rpaths + if (!fRPaths.empty()) { + for (std::vector::const_iterator it=fRPaths.begin(); it != fRPaths.end(); ++it) { + linkCommand.push_back("-rpath"); + linkCommand.push_back(*it); + } + } + + // Other bitcode compatiable options + if ( fObjCABIVersion1Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("1"); + } else if ( fObjCABIVersion2Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("2"); + } + if ( fExecutablePath ) { + linkCommand.push_back("-executable_path"); + linkCommand.push_back(fExecutablePath); + } + if ( fDeadStrip ) + linkCommand.push_back("-dead_strip"); + if ( fExportDynamic ) + linkCommand.push_back("-export_dynamic"); + if ( fMarkAppExtensionSafe && fCheckAppExtensionSafe ) + linkCommand.push_back("-application_extension"); + + if ( fSourceVersionLoadCommandForceOn ) + linkCommand.push_back("-add_source_version"); + if ( fSourceVersion != 0 ) { + linkCommand.push_back("-source_version"); + linkCommand.push_back(getVersionString64(fSourceVersion)); + } + + // linker flag added by swift driver + // rdar://problem/20108072 + if ( !fObjcCategoryMerging ) + linkCommand.push_back("-no_objc_category_merging"); + + return linkCommand; +} // // Process all command line arguments. @@ -1957,15 +2232,18 @@ void Options::parse(int argc, const char* argv[]) if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) { fOutputKind = kStaticExecutable; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dylib") == 0 ) { fOutputKind = kDynamicLibrary; } else if ( strcmp(arg, "-bundle") == 0 ) { fOutputKind = kDynamicBundle; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dylinker") == 0 ) { fOutputKind = kDyld; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-execute") == 0 ) { if ( fOutputKind != kStaticExecutable ) @@ -1973,12 +2251,14 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-preload") == 0 ) { fOutputKind = kPreload; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-r") == 0 ) { fOutputKind = kObjectFile; } else if ( strcmp(arg, "-kext") == 0 ) { fOutputKind = kKextBundle; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-o") == 0 ) { snapshotArgCount = 0; @@ -1992,6 +2272,7 @@ void Options::parse(int argc, const char* argv[]) info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-lto_library") == 0 ) { snapshotFileArgIndex = 1; @@ -2018,18 +2299,21 @@ void Options::parse(int argc, const char* argv[]) // Avoid lazy binding. else if ( strcmp(arg, "-bind_at_load") == 0 ) { fBindAtLoad = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { fNameSpace = kTwoLevelNameSpace; } else if ( strcmp(arg, "-flat_namespace") == 0 ) { fNameSpace = kFlatNameSpace; + cannotBeUsedWithBitcode(arg); } // Also sets a bit to ensure dyld causes everything // in the namespace to be flat. // ??? Deprecate else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { fNameSpace = kForceFlatNameSpace; + cannotBeUsedWithBitcode(arg); } // Similar to --whole-archive. else if ( strcmp(arg, "-all_load") == 0 ) { @@ -2070,13 +2354,16 @@ void Options::parse(int argc, const char* argv[]) snapshotFileArgIndex = 3; parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); i += 3; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file") == 0 ) { snapshotFileArgIndex = 1; parseOrderFile(argv[++i], false); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file_statistics") == 0 ) { fPrintOrderFileStatistics = true; + cannotBeUsedWithBitcode(arg); } // ??? Deprecate segcreate. // -sectcreate puts whole files into a section in the output. @@ -2106,6 +2393,7 @@ void Options::parse(int argc, const char* argv[]) warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment); fBaseAddress = temp; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-e") == 0 ) { fEntryName = argv[++i]; @@ -2120,7 +2408,8 @@ void Options::parse(int argc, const char* argv[]) loadFileList(path, baseOrdinal); } else if ( strcmp(arg, "-keep_private_externs") == 0 ) { - fKeepPrivateExterns = true; + cannotBeUsedWithBitcode(arg); + fKeepPrivateExterns = true; } else if ( strcmp(arg, "-final_output") == 0 ) { fFinalName = argv[++i]; @@ -2137,11 +2426,13 @@ void Options::parse(int argc, const char* argv[]) // do nothing, -interposable_list overrides -interposable" break; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-interposable_list") == 0 ) { snapshotFileArgIndex = 1; fInterposeMode = kInterposeSome; loadExportFile(argv[++i], "-interposable_list", fInterposeList); + cannotBeUsedWithBitcode(arg); } // Default for -interposable/-multi_module/-single_module. else if ( strcmp(arg, "-single_module") == 0 ) { @@ -2160,6 +2451,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbols_list and -exported_symbols_list"; fExportMode = kDontExportSome; loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbol") == 0 ) { if ( fExportMode == kDontExportSome ) @@ -2172,6 +2464,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbol and -exported_symbol"; fExportMode = kDontExportSome; fDontExportSymbols.insert(argv[++i]); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2179,6 +2472,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2186,6 +2480,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { @@ -2194,6 +2489,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { fForceSubtypeAll = true; fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); } // Similar to -weak-l but uses the absolute path name to the library. else if ( strcmp(arg, "-weak_library") == 0 ) { @@ -2203,6 +2499,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fWeakImport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-lazy_library") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2212,6 +2509,7 @@ void Options::parse(int argc, const char* argv[]) info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-framework") == 0 ) { snapshotArgCount = 0; @@ -2235,6 +2533,7 @@ void Options::parse(int argc, const char* argv[]) info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-search_paths_first") == 0 ) { // previously handled by buildSearchPaths() @@ -2244,6 +2543,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); + cannotBeUsedWithBitcode(arg); } // Debugging output flag. else if ( strcmp(arg, "-arch_multiple") == 0 ) { @@ -2261,10 +2561,12 @@ void Options::parse(int argc, const char* argv[]) case kWarning: fWarnTextRelocs = true; fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); break; case kSuppress: fWarnTextRelocs = false; fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); break; case kError: fWarnTextRelocs = false; @@ -2287,6 +2589,7 @@ void Options::parse(int argc, const char* argv[]) // later. Prebinding is less useful on 10.4 and greater. else if ( strcmp(arg, "-prebind") == 0 ) { fPrebind = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-noprebind") == 0 ) { warnObsolete(arg); @@ -2310,6 +2613,7 @@ void Options::parse(int argc, const char* argv[]) // ignore for snapshot because a stub dylib will be created in the snapshot snapshotArgCount = 0; addDylibOverride(argv[++i]); + cannotBeUsedWithBitcode(arg); } // What to expand @executable_path to if found in dependent dylibs else if ( strcmp(arg, "-executable_path") == 0 ) { @@ -2340,6 +2644,7 @@ void Options::parse(int argc, const char* argv[]) warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned); fSegmentAlignment = p2aligned; } + cannotBeUsedWithBitcode(arg); } // Puts a specified segment at a particular address that must // be a multiple of the segment alignment. @@ -2353,15 +2658,18 @@ void Options::parse(int argc, const char* argv[]) if ( seg.address != temp ) warning("-segaddr %s not %lld byte aligned", seg.name, fSegmentAlignment); fCustomSegmentAddresses.push_back(seg); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { fBaseAddress = parseAddress(argv[++i]); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { fBaseWritableAddress = parseAddress(argv[++i]); fSplitSegs = true; + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { @@ -2370,6 +2678,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-seg_addr_table missing argument"; fSegAddrTablePath = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { warnObsolete(arg); @@ -2383,6 +2692,7 @@ void Options::parse(int argc, const char* argv[]) seg.max = parseProtection(argv[++i]); seg.init = parseProtection(argv[++i]); fCustomSegmentProtections.push_back(seg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-pagezero_size") == 0 ) { const char* size = argv[++i]; @@ -2393,12 +2703,14 @@ void Options::parse(int argc, const char* argv[]) if ( (fZeroPageSize != temp) ) warning("-pagezero_size not page aligned, rounding down"); fZeroPageSize = temp; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-stack_addr") == 0 ) { const char* address = argv[++i]; if ( address == NULL ) throw "-stack_addr missing

"; fStackAddr = parseAddress(address); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-stack_size") == 0 ) { const char* size = argv[++i]; @@ -2411,15 +2723,18 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allow_heap_execute") == 0 ) { fDisableNonExecutableHeap = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sectalign") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectalign missing
"; addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); i += 3; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sectorder_detail") == 0 ) { warnObsolete(arg); @@ -2475,6 +2790,24 @@ void Options::parse(int argc, const char* argv[]) setIOSVersionMin(argv[++i]); fTargetIOSSimulator = true; } + else if ( strcmp(arg, "-watchos_version_min") == 0 ) { + setWatchOSVersionMin(argv[++i]); + } + else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) { + setWatchOSVersionMin(argv[++i]); + fTargetIOSSimulator = true; + } + #if SUPPORT_APPLE_TV + else if ( strcmp(arg, "-tvos_version_min") == 0 ) { + setIOSVersionMin(argv[++i]); + fPlatform = kPlatform_tvOS; + } + else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) { + setIOSVersionMin(argv[++i]); + fPlatform = kPlatform_tvOS; + fTargetIOSSimulator = true; + } + #endif else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); ++i; @@ -2515,12 +2848,14 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-u missing argument"; fInitialUndefines.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-U") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-U missing argument"; fAllowedUndefined.insert(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-s") == 0 ) { warnObsolete(arg); @@ -2573,9 +2908,15 @@ void Options::parse(int argc, const char* argv[]) if ( size == NULL ) throw "-headerpad missing argument"; fMinimumHeaderPad = parseAddress(size); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { - fMaxMinimumHeaderPad = true; + // ignore -headerpad_max_install_names when compiling with bitcode + // rdar://problem/20748962 + if ( fBundleBitcode ) + warning("-headerpad_max_install_names is ignored when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES)"); + else + fMaxMinimumHeaderPad = true; } else if ( strcmp(arg, "-t") == 0 ) { fLogAllFiles = true; @@ -2592,6 +2933,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-umbrella missing argument"; fUmbrellaName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allowable_client") == 0 ) { const char* name = argv[++i]; @@ -2600,6 +2942,7 @@ void Options::parse(int argc, const char* argv[]) throw "-allowable_client missing argument"; fAllowableClients.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-client_name") == 0 ) { const char* name = argv[++i]; @@ -2608,30 +2951,35 @@ void Options::parse(int argc, const char* argv[]) throw "-client_name missing argument"; fClientName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sub_umbrella") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-sub_umbrella missing argument"; fSubUmbellas.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sub_library") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-sub_library missing argument"; fSubLibraries.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-init") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-init missing argument"; fInitFunctionName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dot") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-dot missing argument"; fDotOutputFile = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-warn_commons") == 0 ) { fWarnCommons = true; @@ -2665,11 +3013,17 @@ void Options::parse(int argc, const char* argv[]) ++i; // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-bitcode_bundle") == 0 ) { + snapshotArgCount = 0; + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-no_uuid") == 0 ) { fUUIDMode = kUUIDNone; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-random_uuid") == 0 ) { fUUIDMode = kUUIDRandom; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dtrace") == 0 ) { snapshotFileArgIndex = 1; @@ -2677,6 +3031,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-dtrace missing argument"; fDtraceScriptName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-root_safe") == 0 ) { fRootSafe = true; @@ -2693,14 +3048,45 @@ void Options::parse(int argc, const char* argv[]) if ( pair.alias == NULL ) throw "missing argument to -alias"; fAliases.push_back(pair); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-alias_list") == 0 ) { snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; } + else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) { + fHideSymbols = true; + if ( !fBundleBitcode ) + warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); + } + else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { + fReverseMapPath = argv[++i]; + if ( fReverseMapPath == NULL ) + throw "missing argument to -bitcode_symbol_map"; + struct stat statbuf; + ::stat(fReverseMapPath, &statbuf); + if (S_ISDIR(statbuf.st_mode)) { + char tempPath[PATH_MAX]; + sprintf(tempPath, "%s/XXXXXX", fReverseMapPath); + int tempFile = ::mkstemp(tempPath); + if (tempFile == -1) + throwf("could not write file to symbol map directory: %s", fReverseMapPath); + ::close(tempFile); + fReverseMapTempPath = std::string(tempPath); + fReverseMapUUIDRename = true; + } else + fReverseMapTempPath = std::string(fReverseMapPath); + } + else if ( strcmp(argv[i], "-flto-codegen-only") == 0) { + fLTOCodegenOnly = true; + } + else if ( strcmp(argv[i], "-ignore_auto_link") == 0) { + fIgnoreAutoLink = true; + } else if ( strcmp(arg, "-rpath") == 0 ) { const char* path = argv[++i]; if ( path == NULL ) @@ -2724,6 +3110,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_pie") == 0 ) { fDisablePositionIndependentExecutable = true; + cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2732,6 +3119,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexport_library") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2740,6 +3128,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexport_framework") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2748,6 +3137,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-upward-l", 9) == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2756,6 +3146,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_library") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2764,6 +3155,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_framework") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2772,9 +3164,11 @@ void Options::parse(int argc, const char* argv[]) info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { fDeadStripDylibs = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { fImplicitlyLinkPublicDylibs = false; @@ -2783,28 +3177,38 @@ void Options::parse(int argc, const char* argv[]) // ignore } else if ( strcmp(arg, "-no_encryption") == 0 ) { - fEncryptable = false; + fEncryptableForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-encryptable") == 0 ) { + fEncryptableForceOn = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { fAddCompactUnwindEncoding = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = argv[++i]; if ( opts == NULL ) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mcpu") == 0 ) { const char* cpu = argv[++i]; if ( cpu == NULL ) throw "missing argument to -mcpu"; fLtoCpu = cpu; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_data") == 0 ) { fOrderData = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_page_size") == 0 ) { SegmentSize seg; @@ -2816,31 +3220,38 @@ void Options::parse(int argc, const char* argv[]) if ( (seg.size != temp) ) warning("-seg_page_size %s not 4K aligned, rounding down", seg.name); fCustomSegmentSizes.push_back(seg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) { fMarkDeadStrippableDylib = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { snapshotFileArgIndex = 1; loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { warnObsolete("-no_compact_linkedit"); } else if ( strcmp(arg, "-no_eh_labels") == 0 ) { fNoEHLabels = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { fWarnCompactUnwind = true; } else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { fOptimizeZeroFill = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) { fMergeZeroFill = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_abi_version") == 0 ) { const char* version = argv[++i]; @@ -2862,6 +3273,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { fObjcGcCompaction = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_gc") == 0 ) { fObjCGc = true; @@ -2869,6 +3281,7 @@ void Options::parse(int argc, const char* argv[]) warning("-objc_gc overriding -objc_gc_only"); fObjCGcOnly = false; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_gc_only") == 0 ) { fObjCGcOnly = true; @@ -2876,6 +3289,7 @@ void Options::parse(int argc, const char* argv[]) warning("-objc_gc_only overriding -objc_gc"); fObjCGc = false; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-demangle") == 0 ) { fDemangle = true; @@ -2887,6 +3301,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_version_load_command") == 0 ) { fVersionLoadCommandForcedOff = true; fVersionLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-function_starts") == 0 ) { fFunctionStartsForcedOn = true; @@ -2895,10 +3310,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_function_starts") == 0 ) { fFunctionStartsForcedOff = true; fFunctionStartsForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) { fDataInCodeInfoLoadCommandForcedOff = true; fDataInCodeInfoLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-data_in_code_info") == 0 ) { fDataInCodeInfoLoadCommandForcedOn = true; @@ -2915,22 +3332,26 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { const char* symbol = argv[++i]; if ( symbol == NULL ) throw "-force_symbol_weak missing "; fForceWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) { const char* symbol = argv[++i]; if ( symbol == NULL ) throw "-force_symbol_not_weak missing "; fForceNotWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2945,13 +3366,16 @@ void Options::parse(int argc, const char* argv[]) if ( strchr(envarg, '=') == NULL ) throw "-dyld_env missing ENV=VALUE"; fDyldEnvironExtras.push_back(envarg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) { fPageAlignDataAtoms = true; + cannotBeUsedWithBitcode(arg); } else if (strcmp(arg, "-debug_snapshot") == 0) { fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); } else if (strcmp(arg, "-snapshot_dir") == 0) { const char* path = argv[++i]; @@ -2960,12 +3384,15 @@ void Options::parse(int argc, const char* argv[]) fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fLinkSnapshot.setSnapshotPath(path); fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-new_main") == 0 ) { fEntryPointLoadCommandForceOn = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_new_main") == 0 ) { fEntryPointLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-source_version") == 0 ) { const char* vers = argv[++i]; @@ -2978,6 +3405,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_source_version") == 0 ) { fSourceVersionLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sdk_version") == 0 ) { const char* vers = argv[++i]; @@ -2986,13 +3414,14 @@ void Options::parse(int argc, const char* argv[]) fSDKVersion = parseVersionNumber32(vers); } else if ( strcmp(arg, "-dependent_dr_info") == 0 ) { - fDependentDRInfoForcedOn = true; + warnObsolete(arg); } else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) { - fDependentDRInfoForcedOff = true; + warnObsolete(arg); } else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { fKextsUseStubs = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(argv[i], "-dependency_info") == 0 ) { snapshotArgCount = 0; @@ -3027,23 +3456,28 @@ void Options::parse(int argc, const char* argv[]) } } fLinkerOptions.push_back(opts); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allow_simulator_linking_to_macosx_dylibs") == 0 ) { fAllowSimulatorToLinkWithMacOSX = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-keep_dwarf_unwind") == 0 ) { fKeepDwarfUnwindForcedOn = true; fKeepDwarfUnwindForcedOff = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_keep_dwarf_unwind") == 0 ) { fKeepDwarfUnwindForcedOn = false; fKeepDwarfUnwindForcedOff = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-verbose_optimization_hints") == 0 ) { fVerboseOptimizationHints = true; } else if ( strcmp(arg, "-ignore_optimization_hints") == 0 ) { fIgnoreOptimizationHints = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_dtrace_dof") == 0 ) { fGenerateDtraceDOF = false; @@ -3053,30 +3487,35 @@ void Options::parse(int argc, const char* argv[]) throw "-rename_section missing
"; addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]); i += 4; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-rename_segment") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-rename_segment missing "; addSegmentRename(argv[i+1], argv[i+2]); i += 2; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-move_to_ro_segment") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-move_to_ro_segment missing "; addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesCode, "-move_to_ro_segment"); i += 2; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-move_to_rw_segment") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-move_to_rw_segment missing "; addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesData, "-move_to_rw_segment"); i += 2; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-trace_symbol_layout") == 0 ) { fTraceSymbolLayout = true; } else if ( strcmp(arg, "-no_branch_islands") == 0 ) { fAllowBranchIslands = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-segment_order") == 0 ) { // ex: -segment_order __TEXT:__DATA:__JUNK @@ -3099,6 +3538,7 @@ void Options::parse(int argc, const char* argv[]) break; } } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-section_order") == 0 ) { // ex: -section_order __DATA __data:__const:__nl_pointers @@ -3126,6 +3566,7 @@ void Options::parse(int argc, const char* argv[]) break; } } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-application_extension") == 0 ) { fMarkAppExtensionSafe = true; @@ -3144,6 +3585,25 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_load_swift_libs") == 0 ) { fForceLoadSwiftLibs = true; } + else if ( strcmp(arg, "-not_for_dyld_shared_cache") == 0 ) { + fSharedRegionEligibleForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dirty_data_list") == 0 ) { + if ( argv[i+1] == NULL ) + throw "-dirty_data_list missing "; + addSymbolMove("__DATA_DIRTY", argv[i+1], fSymbolsMovesData, "-dirty_data_list"); + ++i; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-data_const") == 0 ) { + fUseDataConstSegmentForceOn = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_data_const") == 0 ) { + fUseDataConstSegmentForceOff = true; + cannotBeUsedWithBitcode(arg); + } // put this last so that it does not interfer with other options starting with 'i' else if ( strncmp(arg, "-i", 2) == 0 ) { const char* colon = strchr(arg, ':'); @@ -3291,6 +3751,9 @@ void Options::buildSearchPaths(int argc, const char* argv[]) throw "-dependency_info missing "; fDependencyInfoPath = path; } + else if ( strcmp(argv[i], "-bitcode_bundle") == 0 ) { + fBundleBitcode = true; + } } int standardLibraryPathsStartIndex = libraryPaths.size(); int standardFrameworkPathsStartIndex = frameworkPaths.size(); @@ -3544,21 +4007,20 @@ void Options::reconfigureDefaults() } // set default min OS version - if ( (fMacVersionMin == ld::macVersionUnset) - && (fIOSVersionMin == ld::iOSVersionUnset) ) { + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fWatchOSVersionMin == ld::wOSVersionUnset) ) { // 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 ) + const char* wOSVers = getenv("WATCHOS_DEPLOYMENT_TARGET"); + if ( macVers != NULL ) setMacOSXVersionMin(macVers); else if ( iPhoneVers != NULL ) setIOSVersionMin(iPhoneVers); else if ( iOSVers != NULL ) setIOSVersionMin(iOSVers); - else if ( iOSSimulatorVers != NULL ) - setIOSVersionMin(iOSSimulatorVers); + else if ( wOSVers != NULL ) + setWatchOSVersionMin(wOSVers); else { // if still nothing, set default based on architecture switch ( fArchitecture ) { @@ -3570,7 +4032,7 @@ void Options::reconfigureDefaults() setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + setMacOSXVersionMin("10.6"); #endif } break; @@ -3580,8 +4042,14 @@ void Options::reconfigureDefaults() warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); #else - warning("-ios_version_min not specified, assuming 6.0"); - setIOSVersionMin("6.0"); + if ( fSubArchitecture == CPU_SUBTYPE_ARM_V7K ) { + warning("-watchos_version_min not specified, assuming 2.0"); + setWatchOSVersionMin("2.0"); + } + else { + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); + } #endif } break; @@ -3596,19 +4064,19 @@ void Options::reconfigureDefaults() // adjust min based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: - if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) { + if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) { //warning("-macosx_version_min should be 10.4 or later for i386"); fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_X86_64: - if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) { + if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_ARM64: - if ( fIOSVersionMin < ld::iOS_7_0 ) { + if ( (fPlatform == kPlatformiOS) && (fIOSVersionMin < ld::iOS_7_0) ) { //warning("-mios_version_min should be 7.0 or later for arm64"); fIOSVersionMin = ld::iOS_7_0; } @@ -3661,12 +4129,12 @@ void Options::reconfigureDefaults() fUndefinedTreatment = kUndefinedDynamicLookup; break; case CPU_TYPE_ARM: - if ( fIOSVersionMin >= ld::iOS_5_0 ) { + if ( min_iOS(ld::iOS_5_0) ) { // iOS 5.0 and later use new MH_KEXT_BUNDLE type fMakeCompressedDyldInfo = false; fMakeCompressedDyldInfoForceOff = true; // kexts are PIC in iOS 6.0 and later - fAllowTextRelocs = (fIOSVersionMin < ld::iOS_6_0); + fAllowTextRelocs = !min_iOS(ld::iOS_6_0); fKextsUseStubs = !fAllowTextRelocs; fUndefinedTreatment = kUndefinedDynamicLookup; break; @@ -3841,7 +4309,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { if ( minOS(ld::mac10_5, ld::iOS_3_1) ) - if ( !fPrebind ) + if ( !fPrebind && !fSharedRegionEligibleForceOff ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) fSharedRegionEligible = true; @@ -3850,7 +4318,41 @@ void Options::reconfigureDefaults() // Enable dyld to be put into the dyld shared cache fSharedRegionEligible = true; } - + + // warn if -rpath is used with OS dylibs + if ( fSharedRegionEligible && !fRPaths.empty() ) + warning("-rpath cannot be used with dylibs that will be in the dyld shared cache"); + + // automatically use __DATA_CONST in iOS dylibs + if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0) && !fUseDataConstSegmentForceOff ) { + fUseDataConstSegment = true; + } + if ( fUseDataConstSegmentForceOn ) { + fUseDataConstSegment = true; + } + if ( fUseDataConstSegment ) { + addSectionRename("__DATA", "__got", "__DATA_CONST", "__got"); + addSectionRename("__DATA", "__la_symbol_ptr", "__DATA_CONST", "__la_symbol_ptr"); + addSectionRename("__DATA", "__nl_symbol_ptr", "__DATA_CONST", "__nl_symbol_ptr"); + addSectionRename("__DATA", "__const", "__DATA_CONST", "__const"); + addSectionRename("__DATA", "__cfstring", "__DATA_CONST", "__cfstring"); + addSectionRename("__DATA", "__mod_init_func", "__DATA_CONST", "__mod_init_func"); + addSectionRename("__DATA", "__mod_term_func", "__DATA_CONST", "__mod_term_func"); + addSectionRename("__DATA", "__objc_classlist", "__DATA_CONST", "__objc_classlist"); + addSectionRename("__DATA", "__objc_nlclslist", "__DATA_CONST", "__objc_nlclslist"); + addSectionRename("__DATA", "__objc_catlist", "__DATA_CONST", "__objc_catlist"); + addSectionRename("__DATA", "__objc_nlcatlist", "__DATA_CONST", "__objc_nlcatlist"); + addSectionRename("__DATA", "__objc_protolist", "__DATA_CONST", "__objc_protolist"); + addSectionRename("__DATA", "__objc_imageinfo", "__DATA_CONST", "__objc_imageinfo"); + addSectionRename("__DATA", "__objc_const", "__DATA_CONST", "__objc_const"); + } + + // Use V2 shared cache info when targetting newer OSs + if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0)) { + fSharedRegionEncodingV2 = true; + fIgnoreOptimizationHints = true; + } + // figure out if module table is needed for compatibility with old ld/dyld if ( fOutputKind == Options::kDynamicLibrary ) { switch ( fArchitecture ) { @@ -3935,12 +4437,16 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: // Add LC_ENCRYPTION_INFO load command to bundled frameworks - if ( fIOSVersionMin < ld::iOS_8_0 ) + if ( !min_iOS(ld::iOS_7_0) ) fEncryptable = false; break; } if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) ) fEncryptable = false; + if ( fEncryptableForceOn ) + fEncryptable = true; + else if ( fEncryptableForceOff ) + fEncryptable = false; // don't move inits in dyld because dyld wants certain // entries point at stable locations at the start of __text @@ -4045,7 +4551,7 @@ void Options::reconfigureDefaults() if ( (fArchitecture == CPU_TYPE_ARM) && fArchSupportsThumb2 && (fOutputKind == kDynamicExecutable) - && (fIOSVersionMin >= ld::iOS_4_3) ) { + && min_iOS(ld::iOS_4_3) ) { fPositionIndependentExecutable = true; } @@ -4086,7 +4592,10 @@ void Options::reconfigureDefaults() if ( fMacVersionMin >= ld::mac10_7 ) { fTLVSupport = true; } - else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= ld::iOS_8_0) ) { + else if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_8_0) ) { + fTLVSupport = true; + } + else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) { fTLVSupport = true; } @@ -4208,35 +4717,7 @@ void Options::reconfigureDefaults() fSourceVersionLoadCommand = false; break; } - - - // add LC_DYLIB_CODE_SIGN_DRS - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - if ( fDependentDRInfoForcedOn ) { - fDependentDRInfo = true; - } - else if ( fDependentDRInfoForcedOff ) { - fDependentDRInfo = false; - } - else { - if ( minOS(ld::mac10_8, ld::iOS_6_0) ) - fDependentDRInfo = true; - else - fDependentDRInfo = false; - } - break; - case Options::kKextBundle: - case Options::kDyld: - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kPreload: - fDependentDRInfo = false; - break; - } - + // if -sdk_version not on command line, infer from -syslibroot if ( (fSDKVersion == 0) && (fSDKPaths.size() > 0) ) { const char* sdkPath = fSDKPaths.front(); @@ -4275,9 +4756,7 @@ void Options::reconfigureDefaults() // allow trie based absolute symbols if targeting new enough OS if ( fMakeCompressedDyldInfo ) { if ( minOS(ld::mac10_9, ld::iOS_7_0) ) { - // Allow absolute symbols in export trie for device but not simulator - if ( !fTargetIOSSimulator ) - fAbsoluteSymbols = true; + fAbsoluteSymbols = true; } } @@ -4299,15 +4778,14 @@ void Options::reconfigureDefaults() case Options::kDynamicBundle: case Options::kDyld: if ( (fArchitecture == CPU_TYPE_ARM64) - || ((fArchitecture == CPU_TYPE_ARM) && (fIOSVersionMin >= ld::iOS_8_0) && - ((fSubArchitecture == CPU_SUBTYPE_ARM_V7S) || (fSubArchitecture == CPU_SUBTYPE_ARM_V7))) ) { + || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) { fSegmentAlignment = 4096*4; } break; case Options::kStaticExecutable: case Options::kKextBundle: // 16KB segments for arm64 kexts - if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= ld::iOS_9_0) ) { + if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_9_0) ) { fSegmentAlignment = 4096*4; } break; @@ -4353,6 +4831,21 @@ void Options::reconfigureDefaults() fBaseAddress = alignedBaseAddress; } + // If -dirty_data_list not specified, look in $SDKROOT/AppleInternal/DirtyDataFiles/.dirty for dirty data list + if ( fSymbolsMovesData.empty() && fUseDataConstSegment && ( fDylibInstallName != NULL) && !fSDKPaths.empty() ) { + const char* dylibLeaf = strrchr(fDylibInstallName, '/'); + if ( dylibLeaf ) { + char path[PATH_MAX]; + strlcpy(path , fSDKPaths.front(), sizeof(path)); + strlcat(path , "/AppleInternal/DirtyDataFiles", sizeof(path)); + strlcat(path , dylibLeaf, sizeof(path)); + strlcat(path , ".dirty", sizeof(path)); + FileInfo info; + if ( info.checkFileExists(*this, path) ) + addSymbolMove("__DATA_DIRTY", path, fSymbolsMovesData, "-dirty_data_list"); + } + } + } void Options::checkIllegalOptionCombinations() @@ -4798,8 +5291,9 @@ void Options::checkIllegalOptionCombinations() throw "-segment_order can only used used with -preload output"; // warn if building an embedded iOS dylib for pre-iOS 8 + // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest? if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) { - if ( (fIOSVersionMin < ld::iOS_8_0) && (fDylibInstallName[0] == '@') ) + if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff ) warning("embedded dylibs/frameworks only run on iOS 8 or later"); } } @@ -4937,14 +5431,28 @@ const char* Options::demangleSymbol(const char* sym) const if ( !fDemangle ) return sym; + static size_t size = 1024; + static char* buff = (char*)malloc(size); + +#if DEMANGLE_SWIFT + // only try to demangle symbols that look like Swift symbols + if ( strncmp(sym, "__T", 3) == 0 ) { + size_t demangledSize = fnd_get_demangled_name(&sym[1], buff, size); + if ( demangledSize > size ) { + size = demangledSize+2; + buff = (char*)realloc(buff, size); + demangledSize = fnd_get_demangled_name(&sym[1], buff, size); + } + if ( demangledSize != 0 ) + return buff; + } +#endif + // only try to demangle symbols that look like C++ symbols if ( strncmp(sym, "__Z", 3) != 0 ) return sym; - static size_t size = 1024; - static char* buff = (char*)malloc(size); int status; - char* result = abi::__cxa_demangle(&sym[1], buff, &size, &status); if ( result != NULL ) { // if demangling successful, keep buffer for next demangle diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 39ee6fe..bc70e7f 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -35,6 +35,7 @@ #include "ld.hpp" #include "Snapshot.h" +#include "MachOFileAbstraction.hpp" extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); @@ -86,6 +87,46 @@ class Options enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; +#if SUPPORT_APPLE_TV + enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS, kPlatform_tvOS }; +#else + enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS }; +#endif + + static Platform platformForLoadCommand(uint32_t lc) { + switch (lc) { + case LC_VERSION_MIN_MACOSX: + return kPlatformOSX; + case LC_VERSION_MIN_IPHONEOS: + return kPlatformiOS; + case LC_VERSION_MIN_WATCHOS: + return kPlatformWatchOS; + #if SUPPORT_APPLE_TV + case LC_VERSION_MIN_TVOS: + return kPlatform_tvOS; + #endif + } + assert(!lc && "unknown LC_VERSION_MIN load command"); + return kPlatformUnknown; + } + + static const char* platformName(Platform platform) { + switch (platform) { + case kPlatformOSX: + return "OSX"; + case kPlatformiOS: + return "iOS"; + case kPlatformWatchOS: + return "watchOS"; + #if SUPPORT_APPLE_TV + case kPlatform_tvOS: + return "tvOS"; + #endif + case kPlatformUnknown: + default: + return "(unknown)"; + } + } class FileInfo { public: @@ -208,7 +249,7 @@ class Options bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; } bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } const char* architectureName() const { return fArchitectureName; } - void setArchitecture(cpu_type_t, cpu_subtype_t subtype); + void setArchitecture(cpu_type_t, cpu_subtype_t subtype, Options::Platform platform); bool archSupportsThumb2() const { return fArchSupportsThumb2; } OutputKind outputKind() const { return fOutputKind; } bool prebind() const { return fPrebind; } @@ -233,6 +274,7 @@ class Options bool allGlobalsAreDeadStripRoots() const; bool shouldExport(const char*) const; bool shouldReExport(const char*) const; + std::vector exportsData() const; bool ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; } bool traceDylibs() const { return fTraceDylibs; } bool traceArchives() const { return fTraceArchives; } @@ -240,7 +282,10 @@ class Options UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; } ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; } ld::IOSVersionMin iOSVersionMin() const { return fIOSVersionMin; } + ld::WatchOSVersionMin watchOSVersionMin() const { return fWatchOSVersionMin; } + uint32_t minOSversion() const; bool minOS(ld::MacVersionMin mac, ld::IOSVersionMin iPhoneOS); + bool min_iOS(ld::IOSVersionMin requirediOSMin); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; } @@ -266,7 +311,7 @@ class Options CommonsMode commonsMode() const { return fCommonsMode; } bool warnCommons() const { return fWarnCommons; } bool keepRelocations(); - FileInfo findFile(const char* path) const; + FileInfo findFile(const std::string &path) const; UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } @@ -296,7 +341,7 @@ class Options const std::vector& dylibOverrides() const { return fDylibOverrides; } const char* generatedMapPath() const { return fMapPath; } bool positionIndependentExecutable() const { return fPositionIndependentExecutable; } - Options::FileInfo findFileUsingPaths(const char* path) const; + Options::FileInfo findFileUsingPaths(const std::string &path) const; bool deadStripDylibs() const { return fDeadStripDylibs; } bool allowedUndefined(const char* name) const { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } bool someAllowedUndefines() const { return (fAllowedUndefined.size() != 0); } @@ -311,6 +356,7 @@ class Options bool needsUnwindInfoSection() const { return fAddCompactUnwindEncoding; } const std::vector& llvmOptions() const{ return fLLVMOptions; } const std::vector& segmentOrder() const{ return fSegmentOrder; } + bool segmentOrderAfterFixedAddressSegment(const char* segName) const; const std::vector* sectionOrder(const char* segName) const; const std::vector& dyldEnvironExtras() const{ return fDyldEnvironExtras; } const std::vector& astFilePaths() const{ return fASTFilePaths; } @@ -346,7 +392,7 @@ class Options bool objcGc() const { return fObjCGc; } bool objcGcOnly() const { return fObjCGcOnly; } bool canUseThreadLocalVariables() const { return fTLVSupport; } - bool addVersionLoadCommand() const { return fVersionLoadCommand; } + bool addVersionLoadCommand() const { return fVersionLoadCommand && (fPlatform != kPlatformUnknown); } bool addFunctionStarts() const { return fFunctionStartsLoadCommand; } bool addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; } bool canReExportSymbols() const { return fCanReExportSymbols; } @@ -364,6 +410,15 @@ class Options bool markAppExtensionSafe() const { return fMarkAppExtensionSafe; } bool checkDylibsAreAppExtensionSafe() const { return fCheckAppExtensionSafe; } bool forceLoadSwiftLibs() const { return fForceLoadSwiftLibs; } + bool bundleBitcode() const { return fBundleBitcode; } + bool hideSymbols() const { return fHideSymbols; } + bool renameReverseSymbolMap() const { return fReverseMapUUIDRename; } + const char* reverseSymbolMapPath() const { return fReverseMapPath; } + std::string reverseMapTempPath() const { return fReverseMapTempPath; } + bool ltoCodegenOnly() const { return fLTOCodegenOnly; } + bool ignoreAutoLink() const { return fIgnoreAutoLink; } + bool sharedRegionEncodingV2() const { return fSharedRegionEncodingV2; } + bool useDataConstSegment() const { return fUseDataConstSegment; } bool hasWeakBitTweaks() const; bool forceWeak(const char* symbolName) const; bool forceNotWeak(const char* symbolName) const; @@ -375,7 +430,6 @@ class Options bool needsThreadLoadCommand() const { return fNeedsThreadLoadCommand; } bool needsEntryPointLoadCommand() const { return fEntryPointLoadCommand; } bool needsSourceVersionLoadCommand() const { return fSourceVersionLoadCommand; } - bool needsDependentDRInfo() const { return fDependentDRInfo; } bool canUseAbsoluteSymbols() const { return fAbsoluteSymbols; } bool allowSimulatorToLinkWithMacOSX() const { return fAllowSimulatorToLinkWithMacOSX; } uint64_t sourceVersion() const { return fSourceVersion; } @@ -395,6 +449,11 @@ class Options const std::vector& segmentRenames() const { return fSegmentRenames; } bool moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const; bool moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const; + Platform platform() const { return fPlatform; } + const std::vector& sdkPaths() const { return fSDKPaths; } + std::vector writeBitcodeLinkOptions() const; + std::string getSDKVersionStr() const; + std::string getPlatformStr() const; private: typedef std::unordered_map NameToOrder; @@ -414,6 +473,7 @@ class Options NameSet::iterator regularBegin() const { return fRegular.begin(); } NameSet::iterator regularEnd() const { return fRegular.end(); } void remove(const NameSet&); + std::vector data() const; private: static bool hasWildCards(const char*); bool wildCardMatch(const char* pattern, const char* candidate) const; @@ -437,6 +497,8 @@ class Options FileInfo& result) const; uint64_t parseVersionNumber64(const char*); uint32_t parseVersionNumber32(const char*); + std::string getVersionString32(uint32_t ver) const; + std::string getVersionString64(uint64_t ver) const; void parseSectionOrderFile(const char* segment, const char* section, const char* path); void parseOrderFile(const char* path, bool cstring); void addSection(const char* segment, const char* section, const char* path); @@ -450,6 +512,7 @@ class Options void setUndefinedTreatment(const char* treatment); void setMacOSXVersionMin(const char* version); void setIOSVersionMin(const char* version); + void setWatchOSVersionMin(const char* version); void setWeakReferenceMismatchTreatment(const char* treatment); void addDylibOverride(const char* paths); void addSectionAlignment(const char* segment, const char* section, const char* alignment); @@ -465,6 +528,7 @@ class Options void addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection); void addSegmentRename(const char* srcSegment, const char* dstSegment); void addSymbolMove(const char* dstSegment, const char* symbolList, std::vector& list, const char* optionName); + void cannotBeUsedWithBitcode(const char* arg); // ObjectFile::ReaderOptions fReaderOptions; @@ -550,6 +614,7 @@ class Options bool fStatistics; bool fPrintOptions; bool fSharedRegionEligible; + bool fSharedRegionEligibleForceOff; bool fPrintOrderFileStatistics; bool fReadOnlyx86Stubs; bool fPositionIndependentExecutable; @@ -562,6 +627,8 @@ class Options bool fKextsUseStubs; bool fUsingLazyDylibLinking; bool fEncryptable; + bool fEncryptableForceOn; + bool fEncryptableForceOff; bool fOrderData; bool fMarkDeadStrippableDylib; bool fMakeCompressedDyldInfo; @@ -621,9 +688,6 @@ class Options bool fSourceVersionLoadCommand; bool fSourceVersionLoadCommandForceOn; bool fSourceVersionLoadCommandForceOff; - bool fDependentDRInfo; - bool fDependentDRInfoForcedOn; - bool fDependentDRInfoForcedOff; bool fTargetIOSSimulator; bool fExportDynamic; bool fAbsoluteSymbols; @@ -639,14 +703,27 @@ class Options bool fMarkAppExtensionSafe; bool fCheckAppExtensionSafe; bool fForceLoadSwiftLibs; + bool fSharedRegionEncodingV2; + bool fUseDataConstSegment; + bool fUseDataConstSegmentForceOn; + bool fUseDataConstSegmentForceOff; + bool fBundleBitcode; + bool fHideSymbols; + bool fReverseMapUUIDRename; + const char* fReverseMapPath; + std::string fReverseMapTempPath; + bool fLTOCodegenOnly; + bool fIgnoreAutoLink; + Platform fPlatform; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; ld::IOSVersionMin fIOSVersionMin; + ld::WatchOSVersionMin fWatchOSVersionMin; std::vector fAliases; std::vector fInitialUndefines; NameSet fAllowedUndefined; - NameSet fWhyLive; + SetWithWildcards fWhyLive; std::vector fExtraSections; std::vector fSectionAlignments; std::vector fOrderedSymbols; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 1680ade..dd0e5b2 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -65,7 +66,6 @@ #include "LinkEdit.hpp" #include "LinkEditClassic.hpp" - namespace ld { namespace tool { @@ -82,7 +82,7 @@ OutputFile::OutputFile(const Options& opts) rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), lazyBindingSection(NULL), exportSection(NULL), splitSegInfoSection(NULL), functionStartsSection(NULL), - dataInCodeSection(NULL), optimizationHintsSection(NULL), dependentDRsSection(NULL), + dataInCodeSection(NULL), optimizationHintsSection(NULL), symbolTableSection(NULL), stringPoolSection(NULL), localRelocationsSection(NULL), externalRelocationsSection(NULL), sectionRelocationsSection(NULL), @@ -94,7 +94,6 @@ OutputFile::OutputFile(const Options& opts) _hasSplitSegInfo(opts.sharedRegionEligible()), _hasFunctionStartsInfo(opts.addFunctionStarts()), _hasDataInCodeInfo(opts.addDataInCodeInfo()), - _hasDependentDRInfo(opts.needsDependentDRInfo()), _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), @@ -120,7 +119,6 @@ OutputFile::OutputFile(const Options& opts) _splitSegInfoAtom(NULL), _functionStartsAtom(NULL), _dataInCodeAtom(NULL), - _dependentDRInfoAtom(NULL), _optimizationHintsAtom(NULL) { } @@ -156,7 +154,10 @@ void OutputFile::write(ld::Internal& state) this->synthesizeDebugNotes(state); this->buildSymbolTable(state); this->generateLinkEditInfo(state); - this->makeSplitSegInfo(state); + if ( _options.sharedRegionEncodingV2() ) + this->makeSplitSegInfoV2(state); + else + this->makeSplitSegInfo(state); this->updateLINKEDITAddresses(state); //this->dumpAtomsBySection(state, false); this->writeOutputFile(state); @@ -267,13 +268,7 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _optimizationHintsAtom->encode(); } - if ( _options.needsDependentDRInfo() ) { - // build dependent dylib DR info - assert(_dependentDRInfoAtom != NULL); - _dependentDRInfoAtom->encode(); - } - - // build classic symbol table + // build classic symbol table assert(_symbolTableAtom != NULL); _symbolTableAtom->encode(); assert(_indirectSymbolTableAtom != NULL); @@ -1233,6 +1228,8 @@ static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: return true; case ld::Fixup::kindSetTargetAddress: f = fixup; @@ -1244,6 +1241,8 @@ static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreARM64GOTLoadPage21: case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: return true; default: break; @@ -1265,6 +1264,8 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: return true; case ld::Fixup::kindSetTargetAddress: f = fixup; @@ -1276,6 +1277,8 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreARM64GOTLoadPageOff12: case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: return true; default: break; @@ -1295,7 +1298,6 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) break; \ } - void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer) { //fprintf(stderr, "applyFixUps() on %s\n", atom->name()); @@ -2098,10 +2100,25 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC); if ( alt.info.count > 2 ) setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD); - + + if ( _options.sharedRegionEligible() ) { + if ( _options.sharedRegionEncodingV2() ) { + // In v2 format, all references might be move at dyld shared cache creation time + usableSegment = false; + } + else { + // In v1 format, only references to something in __TEXT segment could be optimized + usableSegment = (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0); + } + } + else { + // main executables can optimize any reference + usableSegment = true; + } + switch ( alt.info.kind ) { case LOH_ARM64_ADRP_ADRP: - // processed in pass 2 beacuse some ADRP may have been removed + // processed in pass 2 because some ADRP may have been removed break; case LOH_ARM64_ADRP_LDR: LOH_ASSERT(alt.info.count == 1); @@ -2109,10 +2126,12 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(isPageOffsetKind(infoB.fixup)); LOH_ASSERT(infoA.target == infoB.target); LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); isADRP = parseADRP(infoA.instruction, adrpInfoA); LOH_ASSERT(isADRP); isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + // silently ignore LDRs transformed to ADD by TLV pass + if ( !isLDR && infoB.fixup->kind == ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12 ) + break; LOH_ASSERT(isLDR); LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg); LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF)); @@ -2122,7 +2141,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(infoA.instructionContent, makeNOP()); set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); if ( _options.verboseOptimizationHints() ) - fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress); + fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal, usableSegment=%d usableSegment\n", infoB.instructionAddress, usableSegment); } else { if ( _options.verboseOptimizationHints() ) @@ -2137,7 +2156,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(infoC.fixup == NULL); LOH_ASSERT(infoA.target == infoB.target); LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); isADRP = parseADRP(infoA.instruction, adrpInfoA); LOH_ASSERT(isADRP); isADD = parseADD(infoB.instruction, addInfoB); @@ -2193,7 +2211,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: isADD = parseADD(infoB.instruction, addInfoB); LOH_ASSERT(isADD); LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) { // can do T4 transformation and use ADR set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress)); @@ -2227,7 +2244,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(!ldrInfoB.isFloat); LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { // can do T5 transform @@ -2246,7 +2262,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) { @@ -2295,7 +2310,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(infoC.fixup == NULL); LOH_ASSERT(infoA.target == infoB.target); LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); isADRP = parseADRP(infoA.instruction, adrpInfoA); LOH_ASSERT(isADRP); isADD = parseADD(infoB.instruction, addInfoB); @@ -2348,7 +2362,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(ldrInfoB.size == 8); LOH_ASSERT(!ldrInfoB.isFloat); LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { // can do T5 transform @@ -2367,7 +2380,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { @@ -2410,7 +2422,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: isADRP = parseADRP(infoA.instruction, adrpInfoA); isADD = parseADD(infoB.instruction, addInfoB); isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); if ( isADRP ) { if ( isLDR ) { if ( usableSegment && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { @@ -2599,12 +2610,32 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } } - void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) { const bool log = false; if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { uint8_t digest[CC_MD5_DIGEST_LENGTH]; + std::vector> excludeRegions; + uint64_t bitcodeCmdOffset; + uint64_t bitcodeCmdEnd; + uint64_t bitcodeSectOffset; + uint64_t bitcodePaddingEnd; + if ( _headersAndLoadCommandAtom->bitcodeBundleCommand(bitcodeCmdOffset, bitcodeCmdEnd, + bitcodeSectOffset, bitcodePaddingEnd) ) { + // Exclude embedded bitcode bundle section which contains timestamps in XAR header + // Note the timestamp is in the compressed XML header which means it might change the size of + // bitcode section. The load command which include the size of the section and the padding after + // the bitcode section should also be excluded in the UUID computation. + // Bitcode section should appears before LINKEDIT + // Exclude section cmd + if ( log ) fprintf(stderr, "bundle cmd start=0x%08llX, bundle cmd end=0x%08llX\n", + bitcodeCmdOffset, bitcodeCmdEnd); + excludeRegions.emplace_back(std::pair(bitcodeCmdOffset, bitcodeCmdEnd)); + // Exclude section content + if ( log ) fprintf(stderr, "bundle start=0x%08llX, bundle end=0x%08llX\n", + bitcodeSectOffset, bitcodePaddingEnd); + excludeRegions.emplace_back(std::pair(bitcodeSectOffset, bitcodePaddingEnd)); + } uint32_t stabsStringsOffsetStart; uint32_t tabsStringsOffsetEnd; uint32_t stabsOffsetStart; @@ -2631,20 +2662,30 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); assert(firstStabNlistFileOffset <= firstStabStringFileOffset); - + excludeRegions.emplace_back(std::pair(firstStabNlistFileOffset, lastStabNlistFileOffset)); + excludeRegions.emplace_back(std::pair(firstStabStringFileOffset, lastStabStringFileOffset)); + } + if ( !excludeRegions.empty() ) { CC_MD5_CTX md5state; CC_MD5_Init(&md5state); - // checksum everything up to first stabs nlist - if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); - // checkusm everything after last stabs nlist and up to first stabs string - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); - // checksum everything after last stabs string to end of file - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); + // rdar://problem/19487042 include the output leaf file name in the hash + const char* lastSlash = strrchr(_options.outputFilePath(), '/'); + if ( lastSlash != NULL ) { + CC_MD5_Update(&md5state, lastSlash, strlen(lastSlash)); + } + uint64_t checksumStart = 0; + for ( auto& region : excludeRegions ) { + uint64_t regionStart = region.first; + uint64_t regionEnd = region.second; + assert(checksumStart <= regionStart && regionStart <= regionEnd && "Region overlapped"); + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", checksumStart, regionStart); + CC_MD5_Update(&md5state, &wholeBuffer[checksumStart], regionStart - checksumStart); + checksumStart = regionEnd; + } + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", checksumStart, _fileSize); + CC_MD5_Update(&md5state, &wholeBuffer[checksumStart], _fileSize-checksumStart); CC_MD5_Final(digest, &md5state); - if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], + if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7]); } else { @@ -2658,7 +2699,18 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) _headersAndLoadCommandAtom->recopyUUIDCommand(); } } - + +static int sDescriptorOfPathToRemove = -1; +static void removePathAndExit(int sig) +{ + if ( sDescriptorOfPathToRemove != -1 ) { + char path[MAXPATHLEN]; + if ( ::fcntl(sDescriptorOfPathToRemove, F_GETPATH, path) == 0 ) + ::unlink(path); + } + fprintf(stderr, "ld: interrupted\n"); + exit(1); +} void OutputFile::writeOutputFile(ld::Internal& state) { @@ -2724,12 +2776,16 @@ void OutputFile::writeOutputFile(ld::Internal& state) char tmpOutput[PATH_MAX]; uint8_t *wholeBuffer; if ( outputIsRegularFile && outputIsMappableFile ) { + // ld64 should clean up temporary files on SIGINT + ::signal(SIGINT, removePathAndExit); + strcpy(tmpOutput, _options.outputFilePath()); // If the path is too long to add a suffix for a temporary name then // just fall back to using the output path. if (strlen(tmpOutput)+strlen(filenameTemplate) < PATH_MAX) { strcat(tmpOutput, filenameTemplate); fd = mkstemp(tmpOutput); + sDescriptorOfPathToRemove = fd; } else { fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); @@ -2788,11 +2844,24 @@ void OutputFile::writeOutputFile(ld::Internal& state) if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); } + sDescriptorOfPathToRemove = -1; ::close(fd); // NFS: iOS incremental builds in Xcode 4.6 fail with codesign error // NFS seems to pad the end of the file sometimes. Calling trunc seems to correct it... ::truncate(_options.outputFilePath(), _fileSize); } + + // Rename symbol map file if needed + if ( _options.renameReverseSymbolMap() ) { + assert(_options.hideSymbols() && _options.reverseSymbolMapPath() != NULL && "Must hide symbol and specify a path"); + uuid_string_t UUIDString; + const uint8_t* rawUUID = _headersAndLoadCommandAtom->getUUID(); + uuid_unparse_upper(rawUUID, UUIDString); + char outputMapPath[PATH_MAX]; + sprintf(outputMapPath, "%s/%s.bcsymbolmap", _options.reverseSymbolMapPath(), UUIDString); + if ( ::rename(_options.reverseMapTempPath().c_str(), outputMapPath) != 0 ) + throwf("could not create bcsymbolmap file: %s", outputMapPath); + } } struct AtomByNameSorter @@ -3114,7 +3183,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3129,10 +3198,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -3176,7 +3241,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3191,10 +3256,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -3238,7 +3299,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + if ( _options.sharedRegionEncodingV2() ) + _splitSegInfoAtom = new SplitSegInfoV2Atom(_options, state, *this); + else + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3253,10 +3317,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -3300,7 +3360,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + if ( _options.sharedRegionEncodingV2() ) + _splitSegInfoAtom = new SplitSegInfoV2Atom(_options, state, *this); + else + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3315,10 +3378,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -4401,7 +4460,6 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio } - void OutputFile::makeSplitSegInfo(ld::Internal& state) { if ( !_options.sharedRegionEligible() ) @@ -4503,6 +4561,183 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) } } +void OutputFile::makeSplitSegInfoV2(ld::Internal& state) +{ + static const bool log = false; + if ( !_options.sharedRegionEligible() ) + return; + + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + bool codeSection = (sect->type() == ld::Section::typeCode); + if (log) fprintf(stderr, "sect: %s, address=0x%llX\n", sect->sectionName(), sect->address); + 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; + uint32_t picBase = 0; + uint64_t accumulator = 0; + bool thumbTarget; + bool hadSubtract = false; + uint8_t fromSectionIndex = atom->machoSection(); + uint8_t toSectionIndex; + uint8_t kind = 0; + uint64_t fromOffset = 0; + uint64_t toOffset = 0; + uint64_t addend = 0; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + fromTarget = NULL; + kind = 0; + addend = 0; + toSectionIndex = 255; + fromOffset = atom->finalAddress() + fit->offsetInAtom - sect->address; + } + if ( this->setsTarget(fit->kind) ) { + accumulator = addressOf(state, fit, &target); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + toOffset = accumulator - state.atomToSection[target]->address; + if ( target->definition() != ld::Atom::definitionProxy ) { + if ( target->section().type() == ld::Section::typeMachHeader ) + toSectionIndex = 0; + else + toSectionIndex = target->machoSection(); + } + } + switch ( fit->kind ) { + case ld::Fixup::kindSubtractTargetAddress: + accumulator -= addressOf(state, fit, &fromTarget); + hadSubtract = true; + break; + case ld::Fixup::kindAddAddend: + accumulator += fit->u.addend; + addend = fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + accumulator -= fit->u.addend; + picBase = fit->u.addend; + break; + case ld::Fixup::kindSetLazyOffset: + break; + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( kind != DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 ) { + if ( hadSubtract ) + kind = DYLD_CACHE_ADJ_V2_DELTA_32; + else + kind = DYLD_CACHE_ADJ_V2_POINTER_32; + } + break; + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( hadSubtract ) + kind = DYLD_CACHE_ADJ_V2_DELTA_64; + else + kind = DYLD_CACHE_ADJ_V2_POINTER_64; + break; + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64PCRelToGOT: +#endif + if ( (fromSectionIndex != toSectionIndex) || !codeSection ) + kind = DYLD_CACHE_ADJ_V2_DELTA_32; + break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM64_ADRP; + break; + case ld::Fixup::kindStoreARM64PageOff12: + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM64_OFF12; + break; + case ld::Fixup::kindStoreARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM64_BR26; + break; +#endif + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreARMLow16: + if ( (fromSectionIndex != toSectionIndex) && (fromTarget == atom) ) { + kind = DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT; + } + break; + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM_BR24; + break; + case ld::Fixup::kindStoreThumbLow16: + case ld::Fixup::kindStoreThumbHigh16: + if ( (fromSectionIndex != toSectionIndex) && (fromTarget == atom) ) { + kind = DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT; + } + break; + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_THUMB_BR22; + break; + case ld::Fixup::kindSetTargetImageOffset: + kind = DYLD_CACHE_ADJ_V2_IMAGE_OFF_32; + accumulator = addressOf(state, fit, &target); + assert(target != NULL); + toSectionIndex = target->machoSection(); + toOffset = accumulator - state.atomToSection[target]->address; + hadSubtract = true; + break; + default: + break; + } + if ( fit->lastInCluster() ) { + if ( (kind != 0) && (target != NULL) && (target->definition() != ld::Atom::definitionProxy) ) { + if ( !hadSubtract && addend ) + toOffset += addend; + assert(toSectionIndex != 255); + if (log) fprintf(stderr, "from (%d.%s + 0x%llX) to (%d.%s + 0x%llX), kind=%d, atomAddr=0x%llX, sectAddr=0x%llx\n", + fromSectionIndex, sect->sectionName(), fromOffset, toSectionIndex, state.atomToSection[target]->sectionName(), + toOffset, kind, atom->finalAddress(), sect->address); + _splitSegV2Infos.push_back(SplitSegInfoV2Entry(fromSectionIndex, fromOffset, toSectionIndex, toOffset, kind)); + } + } + } + } + } +} + void OutputFile::writeMapFile(ld::Internal& state) { @@ -4618,7 +4853,6 @@ void OutputFile::writeMapFile(ld::Internal& state) } } - // used to sort atoms with debug notes class DebugNoteSorter { diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index 6bb793b..cbb1cfe 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -83,7 +83,6 @@ class OutputFile ld::Internal::FinalSection* functionStartsSection; ld::Internal::FinalSection* dataInCodeSection; ld::Internal::FinalSection* optimizationHintsSection; - ld::Internal::FinalSection* dependentDRsSection; ld::Internal::FinalSection* symbolTableSection; ld::Internal::FinalSection* stringPoolSection; ld::Internal::FinalSection* localRelocationsSection; @@ -132,12 +131,23 @@ class OutputFile }; struct SplitSegInfoEntry { - SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k, uint32_t e=0) : address(a), kind(k), extra(e) {} - uint64_t address; + SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k, uint32_t e=0) + : fixupAddress(a), kind(k), extra(e) {} + uint64_t fixupAddress; ld::Fixup::Kind kind; uint32_t extra; }; + struct SplitSegInfoV2Entry { + SplitSegInfoV2Entry(uint8_t fi, uint64_t fo, uint8_t ti, uint64_t to, uint8_t k) + : fixupSectionOffset(fo), targetSectionOffset(to), fixupSectionIndex(fi), targetSectionIndex(ti), referenceKind(k) {} + uint64_t fixupSectionOffset; + uint64_t targetSectionOffset; + uint8_t fixupSectionIndex; + uint8_t targetSectionIndex; + uint8_t referenceKind; + }; + private: void writeAtoms(ld::Internal& state, uint8_t* wholeBuffer); void computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer); @@ -192,6 +202,7 @@ class OutputFile void makeSectionRelocations(ld::Internal& state); void makeDyldInfo(ld::Internal& state); void makeSplitSegInfo(ld::Internal& state); + void makeSplitSegInfoV2(ld::Internal& state); void writeMapFile(ld::Internal& state); uint64_t lookBackAddend(ld::Fixup::iterator fit); bool takesNoDiskSpace(const ld::Section* sect); @@ -271,7 +282,6 @@ class OutputFile const bool _hasSplitSegInfo; const bool _hasFunctionStartsInfo; const bool _hasDataInCodeInfo; - const bool _hasDependentDRInfo; bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; @@ -296,6 +306,7 @@ class OutputFile std::vector _lazyBindingInfo; std::vector _weakBindingInfo; std::vector _splitSegInfos; + std::vector _splitSegV2Infos; class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom; class RelocationsAtomAbstract* _sectionsRelocationsAtom; class RelocationsAtomAbstract* _localRelocsAtom; @@ -311,7 +322,6 @@ class OutputFile class LinkEditAtom* _splitSegInfoAtom; class LinkEditAtom* _functionStartsAtom; class LinkEditAtom* _dataInCodeAtom; - class LinkEditAtom* _dependentDRInfoAtom; class LinkEditAtom* _optimizationHintsAtom; }; diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 8a72b67..094c98d 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -53,6 +53,7 @@ #include "Options.h" #include "ld.hpp" +#include "Bitcode.hpp" #include "InputFiles.h" #include "SymbolTable.h" #include "Resolver.h" @@ -282,6 +283,8 @@ void Resolver::initializeState() _internal.objcObjectConstraint = ld::File::objcConstraintGC; _internal.cpuSubType = _options.subArchitecture(); + _internal.minOSVersion = _options.minOSversion(); + _internal.derivedPlatformLoadCommand = 0; // In -r mode, look for -linker_option additions if ( _options.outputKind() == Options::kObjectFile ) { @@ -330,14 +333,20 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons } } -static void userReadableSwiftVersion(uint8_t value, char versionString[32]) +static void userReadableSwiftVersion(uint8_t value, char versionString[64]) { switch (value) { case 1: strcpy(versionString, "1.0"); break; + case 2: + strcpy(versionString, "1.1"); + break; + case 3: + strcpy(versionString, "2.0"); + break; default: - sprintf(versionString, "0x%02X", value); + sprintf(versionString, "unknown ABI version 0x%02X", value); } } @@ -349,12 +358,67 @@ void Resolver::doFile(const ld::File& file) if ( objFile != NULL ) { // if file has linker options, process them ld::relocatable::File::LinkerOptionsList* lo = objFile->linkerOptions(); - if ( lo != NULL ) { + if ( lo != NULL && !_options.ignoreAutoLink() ) { for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { this->doLinkerOption(*it, file.path()); } } - + + // Resolve bitcode section in the object file + if ( _options.bundleBitcode() ) { + if ( objFile->getBitcode() == NULL ) { + // No bitcode section, figure out if the object file comes from LTO/compiler static library + if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO && + objFile->sourceKind() != ld::relocatable::File::kSourceCompilerArchive ) { + switch ( _options.platform() ) { + case Options::kPlatformOSX: + case Options::kPlatformUnknown: + warning("all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. ", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + case Options::kPlatformiOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + break; + case Options::kPlatformWatchOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + warning("URGENT: all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor. " + "Note: This will be an error in the future.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + #endif + } + } + } else { + // contains bitcode, check if it is just a marker + if ( objFile->getBitcode()->isMarker() ) { + // if the bitcode is just a marker, + // the executable will be created without bitcode section. + // Otherwise, create a marker. + if ( _options.outputKind() != Options::kDynamicExecutable && + _options.outputKind() != Options::kStaticExecutable ) + _internal.embedMarkerOnly = true; + // Issue a warning if the marker is in the static library and filesWithBitcode is not empty. + // That means there are object files actually compiled with full bitcode but the archive only has marker. + // Don't warn on normal object files because it can be a debug build using archives with full bitcode. + if ( !_internal.filesWithBitcode.empty() && objFile->sourceKind() == ld::relocatable::File::kSourceArchive ) + warning("full bitcode bundle could not be generated because '%s' was built only with bitcode marker. " + "The library must be generated from Xcode archive build with bitcode enabled (Xcode setting ENABLE_BITCODE)", objFile->path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + } else if ( !_internal.dropAllBitcode ) + _internal.filesWithBitcode.push_back(objFile); + } + } + // update which form of ObjC is being used switch ( file.objCConstraint() ) { case ld::File::objcConstraintNone: @@ -400,8 +464,8 @@ void Resolver::doFile(const ld::File& file) _internal.swiftVersion = file.swiftVersion(); } else if ( file.swiftVersion() != _internal.swiftVersion ) { - char fileVersion[32]; - char otherVersion[32]; + char fileVersion[64]; + char otherVersion[64]; userReadableSwiftVersion(file.swiftVersion(), fileVersion); userReadableSwiftVersion(_internal.swiftVersion, otherVersion); if ( file.swiftVersion() > _internal.swiftVersion ) { @@ -423,6 +487,18 @@ void Resolver::doFile(const ld::File& file) if ( ! objFile->canScatterAtoms() ) _internal.allObjectFilesScatterable = false; + // update minOSVersion off all .o files + uint32_t objMinOS = objFile->minOSVersion(); + if ( !objMinOS ) + _internal.objectFileFoundWithNoVersion = true; + + uint32_t objPlatformLC = objFile->platformLoadCommand(); + if ( (objPlatformLC != 0) && (_internal.derivedPlatformLoadCommand == 0) && (_options.outputKind() == Options::kObjectFile) ) + _internal.derivedPlatformLoadCommand = objPlatformLC; + + if ( (_options.outputKind() == Options::kObjectFile) && (objMinOS > _internal.minOSVersion) ) + _internal.minOSVersion = objMinOS; + // update cpu-sub-type cpu_subtype_t nextObjectSubType = file.cpuSubType(); switch ( _options.architecture() ) { @@ -464,6 +540,57 @@ void Resolver::doFile(const ld::File& file) } } if ( dylibFile != NULL ) { + // Check dylib for bitcode, if the library install path is relative path or @rpath, it has to contain bitcode + if ( _options.bundleBitcode() ) { + if ( dylibFile->getBitcode() == NULL && + dylibFile->installPath()[0] != '/' ) { + // Check if the dylib is from toolchain by checking the path + char tcLibPath[PATH_MAX]; + char ldPath[PATH_MAX]; + char tempPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + // toolchain library path should pointed to *.xctoolchain/usr/lib + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tempPath) != NULL ) { + char* lastSlash = strrchr(tempPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib"); + } + } + // Compare toolchain library path to the dylib path + if ( realpath(tempPath, tcLibPath) == NULL || + realpath(dylibFile->path(), tempPath) == NULL || + strncmp(tcLibPath, tempPath, strlen(tcLibPath)) != 0 ) { + switch ( _options.platform() ) { + case Options::kPlatformOSX: + case Options::kPlatformUnknown: + warning("all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + case Options::kPlatformiOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + break; + case Options::kPlatformWatchOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + warning("URGENT: all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor. " + "Note: This will be an error in the future.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + #endif + } + } + } + } + // update which form of ObjC dylibs are being linked switch ( dylibFile->objCConstraint() ) { case ld::File::objcConstraintNone: @@ -509,18 +636,19 @@ void Resolver::doFile(const ld::File& file) if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) { if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) { // only warn about linking against embedded dylib if it is built for iOS 8 or later - if ( dylibFile->iOSMinVersion() >= iOS_8_0 ) - warning("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); + if ( dylibFile->minOSVersion() >= iOS_8_0 ) + throwf("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); } } if ( _options.sharedRegionEligible() ) { assert(depInstallName != NULL); - if ( depInstallName[0] == '@' ) + if ( depInstallName[0] == '@' ) { warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache " - "cannot link with dylib that uses @rpath, @loaderpath, etc.", depInstallName, dylibFile->path()); - if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) ) + "cannot link with dylib that uses @rpath, @loader_path, etc.", depInstallName, dylibFile->path()); + } else if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) ) { warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache " "cannot link with dylibs that won't be in the shared cache", depInstallName, dylibFile->path()); + } } } @@ -631,6 +759,8 @@ void Resolver::doAtom(const ld::Atom& atom) // add to set of dead-strip-roots, all symbols that the compiler marks as don't strip if ( atom.dontDeadStrip() ) _deadStripRoots.insert(&atom); + else if ( atom.dontDeadStripIfReferencesLive() ) + _dontDeadStripIfReferencesLive.push_back(&atom); if ( atom.scope() == ld::Atom::scopeGlobal ) { // -exported_symbols_list that has wildcards and -dead_strip @@ -883,6 +1013,8 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressARM64Page21: case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: #endif if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() @@ -1015,6 +1147,38 @@ void Resolver::deadStripOptimize(bool force) this->markLive(**it, &rootChain); } + // special case atoms that need to be live if they reference something live + if ( ! _dontDeadStripIfReferencesLive.empty() ) { + for (std::vector::iterator it=_dontDeadStripIfReferencesLive.begin(); it != _dontDeadStripIfReferencesLive.end(); ++it) { + const Atom* liveIfRefLiveAtom = *it; + //fprintf(stderr, "live-if-live atom: %s\n", liveIfRefLiveAtom->name()); + if ( liveIfRefLiveAtom->live() ) + continue; + bool hasLiveRef = false; + for (ld::Fixup::iterator fit=liveIfRefLiveAtom->fixupsBegin(); fit != liveIfRefLiveAtom->fixupsEnd(); ++fit) { + const Atom* target = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + break; + default: + break; + } + if ( (target != NULL) && target->live() ) + hasLiveRef = true; + } + if ( hasLiveRef ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.referer = liveIfRefLiveAtom; + this->markLive(*liveIfRefLiveAtom, &rootChain); + } + } + } + // now remove all non-live atoms from _atoms const bool log = false; if ( log ) { @@ -1538,6 +1702,7 @@ void Resolver::linkTimeOptimize() optOpt.preserveAllGlobals = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList(); optOpt.verbose = _options.verbose(); optOpt.saveTemps = _options.saveTempFiles(); + optOpt.ltoCodegenOnly = _options.ltoCodegenOnly(); optOpt.pie = _options.positionIndependentExecutable(); optOpt.mainExecutable = _options.linkingMainExecutable();; optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable); @@ -1548,8 +1713,12 @@ void Resolver::linkTimeOptimize() optOpt.keepDwarfUnwind = _options.keepDwarfUnwind(); optOpt.verboseOptimizationHints = _options.verboseOptimizationHints(); optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); + optOpt.simulator = _options.targetIOSSimulator(); + optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); + optOpt.bitcodeBundle = _options.bundleBitcode(); optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); + optOpt.platform = _options.platform(); optOpt.llvmOptions = &_options.llvmOptions(); optOpt.initialUndefines = &_options.initialUndefines(); diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 975772b..6631d11 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -126,6 +126,7 @@ class Resolver : public ld::File::AtomHandler ld::Internal& _internal; std::vector _atoms; std::set _deadStripRoots; + std::vector _dontDeadStripIfReferencesLive; std::vector _atomsWithUnresolvedReferences; std::vector _aliasesFromCmdLine; SymbolTable _symbolTable; diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index ea552f4..6014779 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -813,6 +813,15 @@ SymbolTable::IndirectBindingSlot SymbolTable::findSlotForReferences(const ld::At slot = _indirectBindingTable.size(); _pointerToCStringTable[atom] = slot; break; + case ld::Section::typeTLVPointers: + pos = _threadPointerTable.find(atom); + if ( pos != _threadPointerTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _threadPointerTable[atom] = slot; + break; default: assert(0 && "section type does not support coalescing by references"); } diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h index 14c7a9e..ce2f1dc 100644 --- a/ld64/src/ld/SymbolTable.h +++ b/ld64/src/ld/SymbolTable.h @@ -155,6 +155,7 @@ class SymbolTable : public ld::IndirectBindingTable CStringToSlot _cstringTable; NameToMap _nonStdCStringSectionToMap; ReferencesToSlot _nonLazyPointerTable; + ReferencesToSlot _threadPointerTable; ReferencesToSlot _cfStringTable; ReferencesToSlot _objc2ClassRefTable; ReferencesToSlot _pointerToCStringTable; diff --git a/ld64/src/ld/code-sign-blobs/memutils.h b/ld64/src/ld/code-sign-blobs/memutils.h index 391ddc1..9e752f4 100644 --- a/ld64/src/ld/code-sign-blobs/memutils.h +++ b/ld64/src/ld/code-sign-blobs/memutils.h @@ -50,8 +50,11 @@ static const size_t systemAlignment = 4; // // Get the local alignment for a type, as used by the acting compiler. // -template -inline size_t alignof() { struct { char c; T t; } s; return sizeof(s) - sizeof(T); } +template +unsigned long myalignof() { + struct { char c; T t; } s; + return sizeof(s) - sizeof(T); +} // diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 4ef1f14..0b5bc4a 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -79,6 +79,7 @@ extern "C" double log2 ( double ); #include "passes/branch_shim.h" #include "passes/objc.h" #include "passes/dylibs.h" +#include "passes/bitcode_bundle.h" #include "parsers/archive_file.h" #include "parsers/macho_relocatable_file.h" @@ -112,6 +113,8 @@ class InternalState : public ld::Internal void setSectionSizesAndAlignments(); void sortSections(); void markAtomsOrdered() { _atomsOrderedInSections = true; } + bool hasReferenceToWeakExternal(const ld::Atom& atom); + virtual ~InternalState() {} private: @@ -137,6 +140,8 @@ class InternalState : public ld::Internal static ld::Section _s_DATA_nl_symbol_ptr; static ld::Section _s_DATA_common; static ld::Section _s_DATA_zerofill; + static ld::Section _s_DATA_DIRTY_data; + static ld::Section _s_DATA_CONST_const; }; bool hasZeroForFileOffset(const ld::Section* sect); @@ -164,6 +169,9 @@ ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld:: 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); +ld::Section InternalState::FinalSection::_s_DATA_DIRTY_data( "__DATA_DIRTY", "__data", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_DATA_CONST_const( "__DATA_CONST", "__const", ld::Section::typeUnclassified); + std::vector InternalState::FinalSection::_s_segmentsSeen; @@ -212,6 +220,14 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) return _s_TEXT_const; } + else if ( strcmp(sect.segmentName(), "__DATA_DIRTY") == 0 ) { + if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) + return _s_DATA_DIRTY_data; + } + else if ( strcmp(sect.segmentName(), "__DATA_CONST") == 0 ) { + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_DATA_CONST_const; + } break; case ld::Section::typeZeroFill: if ( mergeZeroFill ) @@ -304,7 +320,7 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint if ( sect.type() == ld::Section::typeLastSection ) return INT_MAX; const std::vector* sectionList = options.sectionOrder(sect.segmentName()); - if ( (options.outputKind() == Options::kPreload) && (sectionList != NULL) ) { + if ( ((options.outputKind() == Options::kPreload) || (options.outputKind() == Options::kDyld)) && (sectionList != NULL) ) { uint32_t count = 10; for (std::vector::const_iterator it=sectionList->begin(); it != sectionList->end(); ++it, ++count) { if ( strcmp(*it, sect.sectionName()) == 0 ) @@ -335,7 +351,7 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint return sectionsSeen+20; } } - else if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + else if ( strncmp(sect.segmentName(), "__DATA", 6) == 0 ) { switch ( sect.type() ) { case ld::Section::typeLazyPointerClose: return 8; @@ -492,6 +508,34 @@ static void validateFixups(const ld::Atom& atom) } #endif +bool InternalState::hasReferenceToWeakExternal(const ld::Atom& atom) +{ + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = indirectBindingTable[fit->u.bindingIndex]; + break; + } + if ( (target != NULL) && (target->definition() == ld::Atom::definitionRegular) + && (target->combine() == ld::Atom::combineByName) && (target->scope() == ld::Atom::scopeGlobal) ) { + return true; + } + } + return false; +} + ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) { ld::Internal::FinalSection* fs = NULL; @@ -515,7 +559,8 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) if ( _options.moveRwSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { if ( (sectType != ld::Section::typeZeroFill) && (sectType != ld::Section::typeUnclassified) - && (sectType != ld::Section::typeTentativeDefs) ) { + && (sectType != ld::Section::typeTentativeDefs) + && (sectType != ld::Section::typeDyldInfo) ) { if ( !wildCardMatch ) warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType); } @@ -526,7 +571,8 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) } } if ( (fs == NULL) && _options.moveRoSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { - if ( atom.section().type() != ld::Section::typeCode ) { + if ( (sectType != ld::Section::typeCode) + && (sectType != ld::Section::typeUnclassified) ) { if ( !wildCardMatch ) warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType); } @@ -543,9 +589,24 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) const std::vector& segRenames = _options.segmentRenames(); for ( std::vector::const_iterator it=sectRenames.begin(); it != sectRenames.end(); ++it) { if ( (strcmp(sectName, it->fromSection) == 0) && (strcmp(atom.section().segmentName(), it->fromSegment) == 0) ) { - if ( _options.traceSymbolLayout() ) - printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), it->toSegment, it->toSection); - fs = this->getFinalSection(it->toSegment, it->toSection, sectType); + if ( _options.useDataConstSegment() && (strcmp(sectName, "__const") == 0) + && (strcmp(atom.section().segmentName(), "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + fs = this->getFinalSection("__DATA", "__const_weak", sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/_const_weak\n", atom.name()); + } + else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST + fs = this->getFinalSection("__DATA", "__got_weak", sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name()); + } + else { + fs = this->getFinalSection(it->toSegment, it->toSection, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); + } } } if ( fs == NULL ) { @@ -558,8 +619,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) } } } - - + // if no override, use default location if ( fs == NULL ) { fs = this->getFinalSection(atom.section()); @@ -589,6 +649,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) // normal case fs->atoms.push_back(&atom); } + this->atomToSection[&atom] = fs; return fs; } @@ -731,7 +792,7 @@ void InternalState::setSectionSizesAndAlignments() bool pagePerAtom = false; uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; uint32_t atomModulus = atom->alignment().modulus; - if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { + if ( _options.pageAlignDataAtoms() && ( strncmp(atom->section().segmentName(), "__DATA", 6) == 0) ) { // most objc sections cannot be padded bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 ); if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 ) @@ -811,13 +872,22 @@ uint64_t InternalState::assignFileOffsets() uint64_t address = 0; const char* lastSegName = ""; uint64_t floatingAddressStart = _options.baseAddress(); + bool haveFixedSegments = false; + // mark all sections as not having an address yet + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + sect->alignmentPaddingBytes = 0; + sect->address = ULLONG_MAX; + } + // first pass, assign addresses to sections in segments with fixed start addresses if ( log ) fprintf(stderr, "Fixed address segments:\n"); for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) continue; + haveFixedSegments = true; if ( segmentsArePageAligned ) { if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { address = _options.customSegmentAddress(sect->segmentName()); @@ -850,8 +920,38 @@ uint64_t InternalState::assignFileOffsets() floatingAddressStart = address; } } - - // second pass, assign section address to sections in segments that are contiguous with previous segment + + // second pass, assign section addresses to sections in segments that are ordered after a segment with a fixed address + if ( haveFixedSegments && !_options.segmentOrder().empty() ) { + if ( log ) fprintf(stderr, "After Fixed address segments:\n"); + lastSegName = ""; + ld::Internal::FinalSection* lastSect = NULL; + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( (sect->address == ULLONG_MAX) && _options.segmentOrderAfterFixedAddressSegment(sect->segmentName()) ) { + address = lastSect->address + lastSect->size; + if ( (strcmp(lastSegName, sect->segmentName()) != 0) && segmentsArePageAligned ) { + // round up size of last segment + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + sect->alignmentPaddingBytes = (address - unalignedAddress); + sect->address = address; + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + lastSegName = sect->segmentName(); + lastSect = sect; + } + } + + // last pass, assign addresses to remaining sections address = floatingAddressStart; lastSegName = ""; ld::Internal::FinalSection* overlappingFixedSection = NULL; @@ -859,7 +959,7 @@ uint64_t InternalState::assignFileOffsets() if ( log ) fprintf(stderr, "Regular layout segments:\n"); for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; - if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) + if ( sect->address != ULLONG_MAX ) continue; if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { sect->alignmentPaddingBytes = 0; @@ -876,6 +976,7 @@ uint64_t InternalState::assignFileOffsets() lastSegName = sect->segmentName(); } } + // adjust section address based on alignment uint64_t unalignedAddress = address; uint64_t alignment = (1 << sect->alignment); @@ -1116,7 +1217,8 @@ int main(int argc, const char* argv[]) ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); ld::passes::compact_unwind::doPass(options, state); // must be after order pass - + ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib + // sort final sections state.sortSections(); diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 889cf2c..c8f272e 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -31,13 +31,19 @@ #include #include +#include +#include #include +#include #include #include "configure.h" namespace ld { +// Forward declaration for bitcode support +class Bitcode; + // // ld::File // @@ -149,8 +155,11 @@ class File virtual uint8_t swiftVersion() const { return 0; } virtual uint32_t cpuSubType() const { return 0; } virtual uint32_t subFileCount() const { return 1; } + virtual uint32_t minOSVersion() const { return 0; } + virtual uint32_t platformLoadCommand() const { return 0; } bool fileExists() const { return _modTime != 0; } Type type() const { return _type; } + virtual Bitcode* getBitcode() const { return NULL; } private: const char* _path; time_t _modTime; @@ -169,7 +178,9 @@ enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000, iOS_9_0=0x00090000, iOS_Future=0x10000000}; - +enum WatchOSVersionMin { wOSVersionUnset=0, wOS_1_0=0x00010000, wOS_2_0=0x00020000 }; + + namespace relocatable { // // ld::relocatable::File @@ -191,6 +202,7 @@ namespace relocatable { { public: enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + enum SourceKind { kSourceUnknown=0, kSourceObj, kSourceLTO, kSourceArchive, kSourceCompilerArchive, kSourceSingle }; struct Stab { const class Atom* atom; uint8_t type; @@ -211,6 +223,7 @@ namespace relocatable { virtual bool canScatterAtoms() const = 0; virtual bool hasLongBranchStubs() { return false; } virtual LinkerOptionsList* linkerOptions() const = 0; + virtual SourceKind sourceKind() const { return kSourceUnknown; } }; } // namespace relocatable @@ -269,13 +282,15 @@ namespace dylib { virtual bool hasWeakDefinition(const char* name) const = 0; virtual bool hasPublicInstallName() const = 0; virtual bool allSymbolsAreWeakImported() const = 0; - virtual const void* codeSignatureDR() const = 0; virtual bool installPathVersionSpecific() const { return false; } virtual bool appExtensionSafe() const = 0; - virtual MacVersionMin macMinVersion() const { return macVersionUnset; } - virtual IOSVersionMin iOSMinVersion() const { return iOSVersionUnset; } protected: + struct ReExportChain { ReExportChain* prev; const File* file; }; + virtual std::pair hasWeakDefinitionImpl(const char* name) const = 0; + virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const = 0; + virtual void assertNoReExportCycles(ReExportChain*) const = 0; + const char* _dylibInstallPath; uint32_t _dylibTimeStamp; uint32_t _dylibCurrentVersion; @@ -322,7 +337,7 @@ class Section typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols, typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers, - typeFirstSection, typeLastSection, typeDebug }; + typeFirstSection, typeLastSection, typeDebug, typeSectCreate }; Section(const char* sgName, const char* sctName, @@ -644,7 +659,7 @@ class Atom typeSectionEnd, typeBranchIsland, typeLazyPointer, typeStub, typeNonLazyPointer, typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, typeLTOtemporary, typeResolver, - typeTLV, typeTLVZeroFill, typeTLVInitialValue, typeTLVInitializerPointers }; + typeTLV, typeTLVZeroFill, typeTLVInitialValue, typeTLVInitializerPointers, typeTLVPointer }; enum SymbolTableInclusion { symbolTableNotIn, symbolTableNotInFinalLinkedImages, symbolTableIn, symbolTableInAndNeverStrip, symbolTableInAsAbsolute, @@ -679,7 +694,8 @@ class Atom _contentType(ct), _symbolTableInclusion(i), _scope(s), _mode(modeSectionOffset), _overridesADylibsWeakDef(false), _coalescedAway(false), - _live(false), _machoSection(0), _weakImportState(weakImportUnset) + _live(false), _dontDeadStripIfRefLive(false), + _machoSection(0), _weakImportState(weakImportUnset) { #ifndef NDEBUG switch ( _combine ) { @@ -703,6 +719,7 @@ class Atom ContentType contentType() const { return _contentType; } SymbolTableInclusion symbolTableInclusion() const{ return _symbolTableInclusion; } bool dontDeadStrip() const { return _dontDeadStrip; } + bool dontDeadStripIfReferencesLive() const { return _dontDeadStripIfRefLive; } bool isThumb() const { return _thumb; } bool isAlias() const { return _alias; } Alignment alignment() const { return Alignment(_alignmentPowerOf2, _alignmentModulus); } @@ -722,6 +739,7 @@ class Atom void setCoalescedAway() { _coalescedAway = true; } void setWeakImportState(bool w) { assert(_definition == definitionProxy); _weakImportState = ( w ? weakImportTrue : weakImportFalse); } void setAutoHide() { _autoHide = true; } + void setDontDeadStripIfReferencesLive() { _dontDeadStripIfRefLive = true; } void setLive() { _live = true; } void setLive(bool value) { _live = value; } void setMachoSection(unsigned x) { assert(x != 0); assert(x < 256); _machoSection = x; } @@ -794,6 +812,7 @@ class Atom bool _overridesADylibsWeakDef : 1; bool _coalescedAway : 1; bool _live : 1; + bool _dontDeadStripIfRefLive : 1; unsigned _machoSection : 8; WeakImportState _weakImportState : 2; }; @@ -824,6 +843,7 @@ struct CStringEquals typedef std::unordered_set CStringSet; + class Internal { public: @@ -848,6 +868,8 @@ class Internal bool hasExternalRelocs; }; + typedef std::map AtomToSection; + virtual uint64_t assignFileOffsets() = 0; virtual void setSectionSizesAndAlignments() = 0; virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0; @@ -858,19 +880,23 @@ class Internal lazyBindingHelper(NULL), compressedFastBinderProxy(NULL), objcObjectConstraint(ld::File::objcConstraintNone), objcDylibConstraint(ld::File::objcConstraintNone), - swiftVersion(0), cpuSubType(0), + swiftVersion(0), cpuSubType(0), minOSVersion(0), + objectFileFoundWithNoVersion(false), allObjectFilesScatterable(true), someObjectFileHasDwarf(false), usingHugeSections(false), hasThreadLocalVariableDefinitions(false), hasWeakExternalSymbols(false), - someObjectHasOptimizationHints(false) { } - + someObjectHasOptimizationHints(false), + dropAllBitcode(false), embedMarkerOnly(false) { } + std::vector sections; std::vector dylibs; std::vector stabs; + AtomToSection atomToSection; CStringSet linkerOptionLibraries; CStringSet linkerOptionFrameworks; std::vector indirectBindingTable; + std::vector filesWithBitcode; const ld::dylib::File* bundleLoader; const Atom* entryPoint; const Atom* classicBindingHelper; @@ -880,12 +906,18 @@ class Internal ld::File::ObjcConstraint objcDylibConstraint; uint8_t swiftVersion; uint32_t cpuSubType; + uint32_t minOSVersion; + uint32_t derivedPlatformLoadCommand; + bool objectFileFoundWithNoVersion; bool allObjectFilesScatterable; bool someObjectFileHasDwarf; bool usingHugeSections; bool hasThreadLocalVariableDefinitions; bool hasWeakExternalSymbols; bool someObjectHasOptimizationHints; + bool dropAllBitcode; + bool embedMarkerOnly; + std::string ltoBitcodePath; }; @@ -894,10 +926,6 @@ class Internal - - - - } // namespace ld #endif // __LD_HPP__ diff --git a/ld64/src/ld/lto_file.hpp b/ld64/src/ld/lto_file.hpp deleted file mode 100644 index 24d3f58..0000000 --- a/ld64/src/ld/lto_file.hpp +++ /dev/null @@ -1,642 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __LTO_READER_H__ -#define __LTO_READER_H__ - -#include -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ld.hpp" - -#include "llvm-c/lto.h" - - -namespace lto { - - -// -// ld64 only tracks non-internal symbols from an llvm bitcode file. -// We model this by having an InternalAtom which represent all internal functions and data. -// All non-interal symbols from a bitcode file are represented by an Atom -// and each Atom has a reference to the InternalAtom. The InternalAtom -// also has references to each symbol external to the bitcode file. -// -class InternalAtom : public ld::Atom -{ -public: - InternalAtom(class File& f); - // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } - virtual const char* name() const { return "import-atom"; } - virtual uint64_t size() const { return 0; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() { return &_undefs[0]; } - virtual ld::Fixup::iterator fixupsEnd() { return &_undefs[_undefs.size()]; } - - // for adding references to symbols outside bitcode file - void addReference(const char* name) - { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, - ld::Fixup::fixupNone, false, name)); } -private: - - ld::File& _file; - std::vector _undefs; -}; - - -// -// LLVM bitcode file -// -class File : public ld::relocatable::File -{ -public: - File(const char* path, time_t mTime, const uint8_t* content, - uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); - virtual ~File(); - - // overrides of ld::File - virtual bool forEachAtom(ld::File::AtomHandler&); - virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) - { return false; } - - // overrides of ld::relocatable::File - virtual bool objcReplacementClasses() { return false; } - virtual DebugInfoKind debugInfo() { return ld::relocatable::File::kDebugInfoNone; } - virtual std::vector* stabs() { return NULL; } - virtual bool canScatterAtoms() { return true; } - - lto_module_t module() { return _module; } - class InternalAtom& internalAtom() { return _internalAtom; } -private: - friend class Atom; - friend class InternalAtom; - - cpu_type_t _architecture; - class InternalAtom _internalAtom; - class Atom* _atomArray; - uint32_t _atomArrayCount; - lto_module_t _module; - ld::Section _section; -}; - -// -// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, -// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After -// optimization is performed, real Atoms are created for these symobls. However these real Atoms -// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate -// methods to real atom. -// -class Atom : public ld::Atom -{ -public: - Atom(File& f, const char* name, ld::Atom::Scope s, - ld::Atom::Definition d, ld::Atom::Alignment a); - - // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } - virtual const char* name() const { return _name; } - virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } - virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } - virtual void copyRawContent(uint8_t buffer[]) const - { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } - - ld::Atom* compiledAtom() { return _compiledAtom; } - void setCompiledAtom(ld::Atom& atom) - { _compiledAtom = &atom; } -private: - - File& _file; - const char* _name; - ld::Atom* _compiledAtom; -}; - - - - - - - -class Parser -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); - static const char* fileKind(const uint8_t* fileContent); - static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, uint32_t ordinal, cpu_type_t architecture); - static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } - static bool optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, - const std::set&, - std::vector& newDeadAtoms, - uint32_t nextInputOrdinal, - ld::OutFile* writer, ld::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - bool verbose, bool saveTemps, - const char* outputFilePath, - bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, - bool allowTextRelocs, cpu_type_t arch); - - static const char* ltoVersion() { return ::lto_get_version(); } - -private: - static const char* tripletPrefixForArch(cpu_type_t arch); - static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch); - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; - - class AtomSyncer : public ld::File::AtomHandler { - public: - AtomSyncer(std::vector& a, std::vector&na, - CStringToAtom la, CStringToAtom dla) : - additionalUndefines(a), newAtoms(na), llvmAtoms(la), deadllvmAtoms(dla) { } - virtual void doAtom(class ld::Atom&); - - std::vector& additionalUndefines; - std::vector& newAtoms; - CStringToAtom llvmAtoms; - CStringToAtom deadllvmAtoms; - }; - - static std::vector _s_files; -}; - -std::vector Parser::_s_files; - - -const char* Parser::tripletPrefixForArch(cpu_type_t arch) -{ - switch (arch) { - case CPU_TYPE_POWERPC: - return "powerpc-"; - case CPU_TYPE_POWERPC64: - return "powerpc64-"; - case CPU_TYPE_I386: - return "i386-"; - case CPU_TYPE_X86_64: - return "x86_64-"; - case CPU_TYPE_ARM: - return "arm"; - } - return ""; -} - -bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) -{ - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); -} - -const char* Parser::fileKind(const uint8_t* p) -{ - if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { - uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); - switch (arch) { - case CPU_TYPE_POWERPC: - return "ppc"; - case CPU_TYPE_I386: - return "i386"; - case CPU_TYPE_X86_64: - return "x86_64"; - case CPU_TYPE_ARM: - return "arm"; - } - return "unknown bitcode architecture"; - } - return NULL; -} - -File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - uint32_t ordinal, cpu_type_t architecture) -{ - File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); - _s_files.push_back(f); - return f; -} - - -ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - } - throw "LLVM LTO, file is not of required architecture"; -} - - - -File::File(const char* path, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ordinal, cpu_type_t arch) - : ld::relocatable::File(path,mTime,ordinal), _architecture(arch), _internalAtom(*this), - _atomArray(NULL), _atomArrayCount(0), _module(NULL), - _section("__TEXT_", "__tmp_lto", ld::Section::typeUnclassified) -{ - // create llvm module - _module = ::lto_module_create_from_memory(content, contentLength); - if ( _module == NULL ) - throwf("could not parse object file %s: %s", path, lto_get_error_message()); - - // create atom for each global symbol in module - uint32_t count = ::lto_module_get_num_symbols(_module); - _atomArray = (Atom*)malloc(sizeof(Atom)*count); - for (uint32_t i=0; i < count; ++i) { - const char* name = ::lto_module_get_symbol_name(_module, i); - lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i); - - // LTO doesn't like dtrace symbols - // ignore dtrace static probes for now - // later when codegen is done and a mach-o file is produces the probes will be processed - if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) - continue; - - ld::Atom::Definition def; - switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { - case LTO_SYMBOL_DEFINITION_REGULAR: - def = ld::Atom::definitionRegular; - break; - case LTO_SYMBOL_DEFINITION_TENTATIVE: - def = ld::Atom::definitionTentative; - break; - case LTO_SYMBOL_DEFINITION_WEAK: - def = ld::Atom::definitionRegular; - break; - case LTO_SYMBOL_DEFINITION_UNDEFINED: - case LTO_SYMBOL_DEFINITION_WEAKUNDEF: - def = ld::Atom::definitionProxy; - break; - default: - throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); - } - - // make LLVM atoms for definitions and a reference for undefines - if ( def != ld::Atom::definitionProxy ) { - ld::Atom::Scope scope; - switch ( attr & LTO_SYMBOL_SCOPE_MASK) { - case LTO_SYMBOL_SCOPE_INTERNAL: - scope = ld::Atom::scopeTranslationUnit; - break; - case LTO_SYMBOL_SCOPE_HIDDEN: - scope = ld::Atom::scopeLinkageUnit; - break; - case LTO_SYMBOL_SCOPE_DEFAULT: - scope = ld::Atom::scopeGlobal; - break; - default: - throwf("unknown scope for symbol %s in bitcode file %s", name, path); - } - // only make atoms for non-internal symbols - if ( scope == ld::Atom::scopeTranslationUnit ) - continue; - uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); - // make Atom using placement new operator - new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, alignment); - } - else { - // add to list of external references - _internalAtom.addReference(name); - } - } -} - -File::~File() -{ - if ( _module != NULL ) - ::lto_module_dispose(_module); -} - -bool File::forEachAtom(ld::File::AtomHandler& handler) -{ - handler.doAtom(_internalAtom); - for(uint32_t i=0; i < _atomArrayCount; ++i) { - handler.doAtom(_atomArray[i]); - } - return true; -} - -InternalAtom::InternalAtom(File& f) - : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, - ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, false, false, ld::Atom::Alignment(0)), - _file(f) -{ -} - -Atom::Atom(File& f, const char* name, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Alignment a) - : ld::Atom(f._section, d, ld::Atom::combineNever, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, a), - _file(f), _name(name), _compiledAtom(NULL) -{ -} - - - - -bool Parser::optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, - const std::set& deadAtoms, - std::vector& newlyDeadAtoms, - uint32_t nextInputOrdinal, - ld::OutFile* writer, ld::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - bool verbose, bool saveTemps, - const char* outputFilePath, - bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, - bool allowTextRelocs, cpu_type_t arch) -{ - // exit quickly if nothing to do - if ( _s_files.size() == 0 ) - return false; - - // print out LTO version string if -v was used - if ( verbose ) - fprintf(stderr, "%s\n", lto_get_version()); - - // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); - } - - // add any -mllvm command line options - for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { - ::lto_codegen_debug_options(generator, *it); - } - - // The atom graph uses directed edges (references). Collect all references where - // originating atom is not part of any LTO Reader. This allows optimizer to optimize an - // external (i.e. not originated from same .o file) reference if all originating atoms are also - // defined in llvm bitcode file. - CStringSet nonLLVMRefs; - CStringToAtom llvmAtoms; - bool hasNonllvmAtoms = false; - for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { - ld::Atom* atom = *it; - // only look at references that come from an atom that is not an llvm atom - if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { - // remember if we've seen any atoms not from an llvm reader and not from the writer -// if ( atom->getFile() != writer ) -// hasNonllvmAtoms = true; - for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { - if ( fit->binding != ld::Fixup::bindingByNameBound ) - continue; - // and reference an llvm atom - if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) - nonLLVMRefs.insert(fit->u.target->name()); - } - } - else { - llvmAtoms[atom->name()] = (Atom*)atom; - } - } - // if entry point is in a llvm bitcode file, it must be preserved by LTO - if ( entryPointAtom != NULL ) { - if ( entryPointAtom->contentType() == ld::Atom::typeLTOtemporary ) - nonLLVMRefs.insert(entryPointAtom->name()); - } - - // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions - // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced - // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead - // atom so that the linker can replace it with the mach-o one later. - CStringToAtom deadllvmAtoms; - for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { - ld::Atom* atom = *it; - if ( atom->contentType() == ld::Atom::typeLTOtemporary ) { - const char* name = atom->name(); - ::lto_codegen_add_must_preserve_symbol(generator, name); - deadllvmAtoms[name] = (Atom*)atom; - } - } - - - // tell code generator about symbols that must be preserved - for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { - const char* name = it->first; - Atom* atom = it->second; - // Include llvm Symbol in export list if it meets one of following two conditions - // 1 - atom scope is global (and not linkage unit). - // 2 - included in nonLLVMRefs set. - // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( (atom->scope() == ld::Atom::scopeGlobal) ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - } - - // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) - if ( relocatable && !hasNonllvmAtoms ) { - if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { - // HACK, no good way to tell linker we are all done, so just quit - exit(0); - } - warning("could not produce merged bitcode file"); - } - - // set code-gen model - lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - if ( mainExecutable ) { - if ( staticExecutable ) { - // darwin x86_64 "static" code model is really dynamic code model - if ( arch == CPU_TYPE_X86_64 ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_STATIC; - } - else { - if ( pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - } - } - else { - if ( allowTextRelocs ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - } - if ( ::lto_codegen_set_pic_model(generator, model) ) - throwf("could not create set codegen model: %s", lto_get_error_message()); - - // if requested, save off merged bitcode file - if ( saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } - -#if LTO_API_VERSION >= 3 - // find assembler next to linker - char path[PATH_MAX]; - uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { - char* lastSlash = strrchr(path, '/'); - if ( lastSlash != NULL ) { - strcpy(lastSlash+1, "as"); - struct stat statInfo; - if ( stat(path, &statInfo) == 0 ) - ::lto_codegen_set_assembler_path(generator, path); - } - } -#endif - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); - - // if requested, save off temp mach-o file - if ( saveTemps ) { - char tempMachoPath[MAXPATHLEN]; - strcpy(tempMachoPath, outputFilePath); - strcat(tempMachoPath, ".lto.o"); - int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if ( fd != -1) { - ::write(fd, machOFile, machOFileLen); - ::close(fd); - } - // save off merged bitcode file - char tempOptBitcodePath[MAXPATHLEN]; - strcpy(tempOptBitcodePath, outputFilePath); - strcat(tempOptBitcodePath, ".lto.opt.bc"); - ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); - } - - // parse generated mach-o file into a MachOReader - ld::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, arch); - - // sync generated mach-o atoms with existing atoms ld knows about - AtomSyncer syncer(additionalUndefines,newAtoms,llvmAtoms,deadllvmAtoms); - machoFile->forEachAtom(syncer); - - // Remove InternalAtoms from ld - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - newlyDeadAtoms.push_back(&((*it)->internalAtom())); - } - // Remove Atoms from ld if code generator optimized them away - for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { - // check if setRealAtom() called on this Atom - if ( li->second->compiledAtom() == NULL ) - newlyDeadAtoms.push_back(li->second); - } - - return true; -} - - -void Parser::AtomSyncer::doAtom(ld::Atom& machoAtom) -{ - // update proxy atoms to point to real atoms and find new atoms - const char* name = machoAtom.name(); - if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { - CStringToAtom::iterator pos = llvmAtoms.find(name); - if ( pos != llvmAtoms.end() ) { - // turn Atom into a proxy for this mach-o atom - pos->second->setCompiledAtom(machoAtom); - } - else { - // an atom of this name was not in the allAtoms list the linker gave us - if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { - // this corresponding to an atom that the linker coalesced away. - // Don't pass it back as a new atom - } - else - { - // this is something new that lto conjured up, tell ld its new - newAtoms.push_back(&machoAtom); - } - } - } - else { - // ld only knew about non-satic atoms, so this one must be new - newAtoms.push_back(&machoAtom); - } - - // adjust fixups to go through proxy atoms - for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) { - switch ( fit->binding ) { - case ld::Fixup::bindingNone: - break; - case ld::Fixup::bindingByNameUnbound: - // don't know if this target has been seen by linker before or if it is new - // be conservitive and tell linker it is new - additionalUndefines.push_back(fit->u.name); - break; - case ld::Fixup::bindingByNameBound: - break; - case ld::Fixup::bindingDirectlyBound: - // If mach-o atom is referencing another mach-o atom then - // reference is not going through Atom proxy. Fix it here to ensure that all - // llvm symbol references always go through Atom proxy. - break; - case ld::Fixup::bindingByContentBound: - break; - } - } - -} - - - -}; // namespace lto - - -#endif - diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index fbba237..26d70be 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -118,6 +119,9 @@ class File : public ld::relocatable::File _debugInfoModTime = modTime; _cpuSubType = subtype;} + static bool sSupportsLocalContext; + static bool sHasTriedLocalContext; + bool mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule); private: friend class Atom; friend class InternalAtom; @@ -128,6 +132,9 @@ class File : public ld::relocatable::File class Atom* _atomArray; uint32_t _atomArrayCount; lto_module_t _module; + const char* _path; + const uint8_t* _content; + uint32_t _contentLength; const char* _debugInfoPath; time_t _debugInfoModTime; ld::Section _section; @@ -303,8 +310,11 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.neverConvertDwarf = false; objOpts.verboseOptimizationHints = options.verboseOptimizationHints; objOpts.armUsesZeroCostExceptions = options.armUsesZeroCostExceptions; - + objOpts.simulator = options.simulator; + objOpts.ignoreMismatchPlatform = options.ignoreMismatchPlatform; + objOpts.platform = options.platform; objOpts.subType = 0; + objOpts.srcKind = ld::relocatable::File::kSourceLTO; // mach-o parsing is done in-memory, but need path for debug notes const char* path = "/tmp/lto.o"; @@ -326,7 +336,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) : ld::relocatable::File(pth,mTime,ordinal), _architecture(arch), _internalAtom(*this), - _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), + _atomArray(NULL), _atomArrayCount(0), _module(NULL), _path(pth), + _content(content), _contentLength(contentLength), _debugInfoPath(pth), _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), _debugInfo(ld::relocatable::File::kDebugInfoNone), _cpuSubType(0) @@ -334,9 +345,19 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 const bool log = false; // create llvm module +#if LTO_API_VERSION >= 11 + if ( sSupportsLocalContext || !sHasTriedLocalContext ) { + _module = ::lto_module_create_in_local_context(content, contentLength, pth); + } + if ( !sHasTriedLocalContext ) { + sHasTriedLocalContext = true; + sSupportsLocalContext = (_module != NULL); + } + if ( (_module == NULL) && !sSupportsLocalContext ) +#endif #if LTO_API_VERSION >= 9 _module = ::lto_module_create_from_memory_with_path(content, contentLength, pth); - if ( _module == NULL ) + if ( _module == NULL && !sSupportsLocalContext ) #endif _module = ::lto_module_create_from_memory(content, contentLength); if ( _module == NULL ) @@ -417,6 +438,11 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 if ( log ) fprintf(stderr, "\t%s (undefined)\n", name); } } + +#if LTO_API_VERSION >= 11 + if ( sSupportsLocalContext ) + this->release(); +#endif } File::~File() @@ -424,6 +450,34 @@ File::~File() this->release(); } +bool File::mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule) { +#if LTO_API_VERSION >= 11 + if ( sSupportsLocalContext ) { + assert(!_module && "Expected module to be disposed"); + _module = ::lto_module_create_in_codegen_context(_content, _contentLength, + _path, generator); + if ( _module == NULL ) + throwf("could not reparse object file %s: '%s', using libLTO version '%s'", + _path, ::lto_get_error_message(), ::lto_get_version()); + } +#endif + assert(_module && "Expected module to stick around"); +#if LTO_API_VERSION >= 13 + if (useSetModule) { + // lto_codegen_set_module will transfer ownership of the module to LTO code generator, + // so we don't need to release the module here. + ::lto_codegen_set_module(generator, _module); + return false; + } +#endif + if ( ::lto_codegen_add_module(generator, _module) ) + return true; + + // linker should release module as soon as possible + this->release(); + return false; +} + void File::release() { if ( _module != NULL ) @@ -486,6 +540,8 @@ void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, co switch ( severity ) { #if LTO_API_VERSION >= 10 case LTO_DS_REMARK: + fprintf(stderr, "ld: LTO remark: %s\n", message); + break; #endif case LTO_DS_NOTE: case LTO_DS_WARNING: @@ -518,7 +574,13 @@ bool Parser::optimize( const std::vector& allAtoms, fprintf(stderr, "%s\n", ::lto_get_version()); // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); + lto_code_gen_t generator = NULL; +#if LTO_API_VERSION >= 11 + if ( File::sSupportsLocalContext ) + generator = ::lto_codegen_create_in_local_context(); + else +#endif + generator = ::lto_codegen_create(); #if LTO_API_VERSION >= 7 lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); #endif @@ -526,14 +588,20 @@ bool Parser::optimize( const std::vector& allAtoms, // The order that files are merged must match command line order std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); ld::File::Ordinal lastOrdinal; + + // When flto_codegen_only is on and we have a single .bc file, use lto_codegen_set_module instead of + // lto_codegen_add_module, to make sure the the destination module will be the same as the input .bc file. + bool useSetModule = false; +#if LTO_API_VERSION >= 13 + useSetModule = (_s_files.size() == 1) && options.ltoCodegenOnly && (::lto_api_version() >= 13); +#endif for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { File* f = *it; assert(f->ordinal() > lastOrdinal); - if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); - if ( ::lto_codegen_add_module(generator, f->module()) ) + if ( logBitcodeFiles && !useSetModule) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( logBitcodeFiles && useSetModule) fprintf(stderr, "lto_codegen_set_module(%s)\n", f->path()); + if ( f->mergeIntoGenerator(generator, useSetModule) ) throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); - // linker should release module as soon as possible - f->release(); lastOrdinal = f->ordinal(); } @@ -653,6 +721,9 @@ bool Parser::optimize( const std::vector& allAtoms, // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) if ( options.relocatable && !hasNonllvmAtoms ) { +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, false); +#endif if ( ! ::lto_codegen_write_merged_modules(generator, options.outputFilePath) ) { // HACK, no good way to tell linker we are all done, so just quit exit(0); @@ -691,6 +762,9 @@ bool Parser::optimize( const std::vector& allAtoms, char tempBitcodePath[MAXPATHLEN]; strcpy(tempBitcodePath, options.outputFilePath); strcat(tempBitcodePath, ".lto.bc"); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, true); +#endif ::lto_codegen_write_merged_modules(generator, tempBitcodePath); } @@ -708,11 +782,62 @@ bool Parser::optimize( const std::vector& allAtoms, } } #endif - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + + // When lto API version is greater than or equal to 12, we use lto_codegen_optimize and lto_codegen_compile_optimized + // instead of lto_codegen_compile, and we save the merged bitcode file in between. + bool useSplitAPI = false; +#if LTO_API_VERSION >= 12 + if ( ::lto_api_version() >= 12) + useSplitAPI = true; +#endif + + size_t machOFileLen = 0; + const uint8_t* machOFile = NULL; + if ( useSplitAPI) { +#if LTO_API_VERSION >= 12 +#if LTO_API_VERSION >= 14 + if ( ::lto_api_version() >= 14 && options.ltoCodegenOnly) + lto_codegen_set_should_internalize(generator, false); +#endif + // run optimizer + if ( !options.ltoCodegenOnly && ::lto_codegen_optimize(generator) ) + throwf("could not do LTO optimization: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + + if ( options.saveTemps || options.bitcodeBundle ) { + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, options.outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, true); +#endif + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + if ( options.bitcodeBundle ) + state.ltoBitcodePath = tempOptBitcodePath; + } + + // run code generator + machOFile = (uint8_t*)::lto_codegen_compile_optimized(generator, &machOFileLen); +#endif + if ( machOFile == NULL ) + throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + } + else { + // run optimizer and code generator + machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + if ( options.saveTemps ) { + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, options.outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, true); +#endif + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } + } // if requested, save off temp mach-o file if ( options.saveTemps ) { @@ -724,11 +849,6 @@ bool Parser::optimize( const std::vector& allAtoms, ::write(fd, machOFile, machOFileLen); ::close(fd); } - // save off merged bitcode file - char tempOptBitcodePath[MAXPATHLEN]; - strcpy(tempOptBitcodePath, options.outputFilePath); - strcat(tempOptBitcodePath, ".lto.opt.bc"); - ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); } // if needed, save temp mach-o file to specific location @@ -860,7 +980,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) } else { // Don't unbind follow-on reference into by-name reference - if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { + if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) && (fit->u.target->scope() != ld::Atom::scopeTranslationUnit) ) { // target was coalesed away and replace by mach-o atom from a non llvm .o file fit->binding = ld::Fixup::bindingByNameUnbound; fit->u.name = targetName; @@ -888,6 +1008,8 @@ class Mutex { ~Mutex() { pthread_mutex_unlock(<o_lock); } }; pthread_mutex_t Mutex::lto_lock = PTHREAD_MUTEX_INITIALIZER; +bool File::sSupportsLocalContext = false; +bool File::sHasTriedLocalContext = false; // // Used by archive reader to see if member is an llvm bitcode file @@ -899,6 +1021,18 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar } +static ld::relocatable::File *parseImpl( + const uint8_t *fileContent, uint64_t fileLength, const char *path, + time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, + cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints) +{ + if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); + else + return NULL; +} + // // main function used by linker to instantiate ld::Files // @@ -907,11 +1041,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints) { + // Note: Once lto_module_create_in_local_context() and friends are thread safe + // this lock can be removed. Mutex lock; - if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); - else - return NULL; + return parseImpl(fileContent, fileLength, path, modTime, ordinal, + architecture, subarch, logAllFiles, + verboseOptimizationHints); } // diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index dcd6e36..6568232 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -48,6 +48,7 @@ struct OptimizeOptions { bool preserveAllGlobals; bool verbose; bool saveTemps; + bool ltoCodegenOnly; bool pie; bool mainExecutable; bool staticExecutable; @@ -58,8 +59,12 @@ struct OptimizeOptions { bool keepDwarfUnwind; bool verboseOptimizationHints; bool armUsesZeroCostExceptions; + bool simulator; + bool ignoreMismatchPlatform; + bool bitcodeBundle; cpu_type_t arch; const char* mcpu; + Options::Platform platform; const std::vector* llvmOptions; const std::vector* initialUndefines; }; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index a3c1456..0ba962c 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -1,4 +1,3 @@ - /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2005-2011 Apple Inc. All rights reserved. @@ -33,11 +32,13 @@ #include #include +#include #include #include #include #include "Architectures.hpp" +#include "Bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" #include "macho_dylib_file.h" @@ -143,8 +144,10 @@ class File : public ld::dylib::File File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool allowSimToMacOSX, bool addVers, - bool logAllFiles, const char* installPath, bool indirectDylib); + Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, + bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* installPath, + bool indirectDylib, bool ignoreMismatchPlatform); virtual ~File() {} // overrides of ld::File @@ -152,7 +155,9 @@ class File : public ld::dylib::File virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } virtual uint8_t swiftVersion() const { return _swiftVersion; } - + virtual uint32_t minOSVersion() const { return _minVersionInDylib; } + virtual uint32_t platformLoadCommand() const { return _platformInDylib; } + // overrides of ld::dylib::File virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); virtual bool providedExportAtom() const { return _providedAtom; } @@ -163,18 +168,12 @@ class File : public ld::dylib::File virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } virtual bool hasWeakDefinition(const char* name) const; virtual bool allSymbolsAreWeakImported() const; - virtual const void* codeSignatureDR() const { return _codeSignatureDR; } virtual bool installPathVersionSpecific() const { return _installPathOverride; } virtual bool appExtensionSafe() const { return _appExtensionSafe; }; - virtual ld::MacVersionMin macMinVersion() const { return _macMinVersionInDylib; } - virtual ld::IOSVersionMin iOSMinVersion() const { return _iOSMinVersionInDylib; } - + virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } protected: - - struct ReExportChain { ReExportChain* prev; File* file; }; - - void assertNoReExportCycles(ReExportChain*); + virtual void assertNoReExportCycles(ReExportChain*) const; private: typedef typename A::P P; @@ -192,13 +191,14 @@ class File : public ld::dylib::File return size_t(__h); }; }; - struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; }; + struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; uint64_t address; }; typedef std::unordered_map NameToAtomMap; typedef std::unordered_set NameSet; struct Dependent { const char* path; File* dylib; bool reExport; }; - bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; + virtual std::pair hasWeakDefinitionImpl(const char* name) const; + virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const; bool isPublicLocation(const char* pth); bool wrongOS() { return _wrongOS; } void addSymbol(const char* name, bool weak, bool tlv, pint_t address); @@ -211,9 +211,9 @@ class File : public ld::dylib::File static uint32_t parseVersionNumber32(const char* versionString); static const char* objCInfoSegmentName(); static const char* objCInfoSectionName(); - - const ld::MacVersionMin _macVersionMin; - const ld::IOSVersionMin _iOSVersionMin; + + const Options::Platform _platform; + const uint32_t _linkMinOSVersion; const bool _allowSimToMacOSXLinking; const bool _addVersionLoadCommand; bool _linkingFlat; @@ -228,7 +228,6 @@ class File : public ld::dylib::File NameSet _ignoreExports; const char* _parentUmbrella; ImportAtom* _importAtom; - const void* _codeSignatureDR; bool _noRexports; bool _hasWeakExports; bool _deadStrippable; @@ -239,8 +238,9 @@ class File : public ld::dylib::File bool _installPathOverride; bool _indirectDylibsProcessed; bool _appExtensionSafe; - ld::MacVersionMin _macMinVersionInDylib; - ld::IOSVersionMin _iOSMinVersionInDylib; + uint32_t _minVersionInDylib; + uint32_t _platformInDylib; + std::unique_ptr _bitcode; static bool _s_logHashtable; }; @@ -259,20 +259,20 @@ template const char* File::objCInfoSectionName() { return "__ima template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool allowSimToMacOSX, bool addVers, - bool logAllFiles, const char* targetInstallPath, bool indirectDylib) + Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform) : ld::dylib::File(strdup(pth), mTime, ord), - _macVersionMin(macMin), _iOSVersionMin(iOSMin), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), + _platform(platform), _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), - _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), + _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), _indirectDylibsProcessed(false), _appExtensionSafe(false), - _macMinVersionInDylib(ld::macVersionUnset), _iOSMinVersionInDylib(ld::iOSVersionUnset) + _minVersionInDylib(0), _platformInDylib(Options::kPlatformUnknown) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -302,11 +302,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format const macho_dysymtab_command

* dynamicInfo = NULL; const macho_dyld_info_command

* dyldInfo = NULL; - const macho_linkedit_data_command

* codeSignature = NULL; const macho_nlist

* symbolTable = NULL; const char* strings = NULL; bool compressedLinkEdit = false; uint32_t dependentLibCount = 0; + Options::Platform lcPlatform = Options::kPlatformUnknown; const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { macho_dylib_command

* dylibID; @@ -348,29 +348,24 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; case LC_SUB_CLIENT: _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); + // Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked + _hasPublicInstallName = false; break; case LC_VERSION_MIN_MACOSX: - if ( (_iOSVersionMin != ld::iOSVersionUnset) && !_allowSimToMacOSXLinking ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib ) - throw "building for iOS Simulator, but linking against dylib built for MacOSX"; - } - _macMinVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); - break; case LC_VERSION_MIN_IPHONEOS: - if ( _macVersionMin != ld::macVersionUnset ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib ) - throw "building for MacOSX, but linking against dylib built for iOS Simulator"; - } - _iOSMinVersionInDylib = (ld::IOSVersionMin)((macho_version_min_command

*)cmd)->version(); + case LC_VERSION_MIN_WATCHOS: + #if SUPPORT_APPLE_TV + case LC_VERSION_MIN_TVOS: + #endif + _minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); + _platformInDylib = cmd->cmd(); + lcPlatform = Options::platformForLoadCommand(_platformInDylib); break; case LC_CODE_SIGNATURE: - codeSignature = (macho_linkedit_data_command

* )cmd; break; case macho_segment_command

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

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { + if ( strncmp(((macho_segment_command

*)cmd)->segname(), objCInfoSegmentName(), 6) == 0 ) { const macho_segment_command

* segment = (macho_segment_command

*)cmd; const macho_section

* const sectionsStart = (macho_section

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

)); const macho_section

* const sectionsEnd = §ionsStart[segment->nsects()]; @@ -403,11 +398,88 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } } } + // Construct bitcode if there is a bitcode bundle section in the dylib + // Record the size of the section because the content is not checked + else if ( strcmp(((macho_segment_command

*)cmd)->segname(), "__LLVM") == 0 ) { + const macho_section

* const sect = (macho_section

*)((char*)cmd + sizeof(macho_segment_command

)); + if ( strncmp(sect->sectname(), "__bundle", 8) == 0 ) + _bitcode = std::unique_ptr(new ld::Bitcode(NULL, sect->size())); + } } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); if ( cmd > cmdsEnd ) throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); } + // arm/arm64 objects are default to ios platform if not set. + // rdar://problem/21746314 + if (lcPlatform == Options::kPlatformUnknown && + (std::is_same::value || std::is_same::value)) + lcPlatform = Options::kPlatformiOS; + + // check cross-linking + if ( lcPlatform != platform ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) { + if ( buildingForSimulator ) { + if ( !_allowSimToMacOSXLinking ) { + switch (platform) { + case Options::kPlatformOSX: + case Options::kPlatformiOS: + if ( lcPlatform == Options::kPlatformUnknown ) + break; + // fall through if the Platform is not Unknown + case Options::kPlatformWatchOS: + // WatchOS errors on cross-linking all the time. + throwf("building for %s simulator, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + // tvOS is a warning temporarily. rdar://problem/21746965 + if (platform == Options::kPlatform_tvOS) + warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path(), + Options::platformName(lcPlatform)); + break; + #endif + case Options::kPlatformUnknown: + // skip if the target platform is unknown + break; + } + } + } + else { + switch (platform) { + case Options::kPlatformOSX: + case Options::kPlatformiOS: + if ( lcPlatform == Options::kPlatformUnknown ) + break; + // fall through if the Platform is not Unknown + case Options::kPlatformWatchOS: + // WatchOS errors on cross-linking all the time. + throwf("building for %s, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + // tvOS is a warning temporarily. rdar://problem/21746965 + if (platform == Options::kPlatform_tvOS) + warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path(), + Options::platformName(lcPlatform)); + break; + #endif + case Options::kPlatformUnknown: + // skip if the target platform is unknown + break; + } + } + } + } // figure out if we need to examine dependent dylibs // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted @@ -441,7 +513,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } // verify MH_NO_REEXPORTED_DYLIBS bit was correct if ( compressedLinkEdit && !linkingFlatNamespace ) { - assert(reExportDylibCount != 0); + if ( reExportDylibCount == 0 ) + throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", pth); } // pass 3 add re-export info cmd = cmds; @@ -500,26 +573,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } _importAtom = new ImportAtom(*this, importNames); } - - // if the dylib is code signed, look for its Designated Requirement - if ( codeSignature != NULL ) { - const Security::BlobCore* overallSignature = (Security::BlobCore*)((char*)header + codeSignature->dataoff()); - typedef Security::SuperBlob EmbeddedSignatureBlob; - typedef Security::SuperBlob InternalRequirementsBlob; - const EmbeddedSignatureBlob* signature = EmbeddedSignatureBlob::specific(overallSignature); - if ( signature->validateBlob(codeSignature->datasize()) ) { - const InternalRequirementsBlob* ireq = signature->find(Security::cdRequirementsSlot); - if ( (ireq != NULL) && ireq->validateBlob() ) { - const Security::BlobCore* dr = ireq->find(Security::kSecDesignatedRequirementType); - if ( (dr != NULL) && dr->validateBlob(Security::kSecCodeMagicRequirement) ) { - // make copy because mapped file is about to be unmapped - _codeSignatureDR = ::malloc(dr->length()); - ::memcpy((void*)_codeSignatureDR, dr, dr->length()); - } - } - } - } - + // build hash table if ( dyldInfo != NULL ) buildExportHashTableFromExportInfo(dyldInfo, fileContent); @@ -530,6 +584,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } + // // Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz // @@ -634,15 +689,7 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address const char* symCond = strchr(symAction, '$'); if ( symCond != NULL ) { char curOSVers[16]; - if ( _macVersionMin != ld::macVersionUnset ) { - sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 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"); - } + sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { const char* symName = strchr(&symCond[1], '$'); if ( symName != NULL ) { @@ -705,29 +752,34 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const return false; } + +template +std::pair File::hasWeakDefinitionImpl(const char* name) const +{ + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) + return std::make_pair(true, pos->second.weakDef); + + // look in children that I re-export + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport ) { + auto ret = dep.dylib->hasWeakDefinitionImpl(name); + if ( ret.first ) + return ret; + } + } + return std::make_pair(false, false); +} + + template bool File::hasWeakDefinition(const char* name) const { // if supposed to ignore this export, then pretend I don't have it if ( _ignoreExports.count(name) != 0 ) return false; - - typename NameToAtomMap::const_iterator pos = _atoms.find(name); - if ( pos != _atoms.end() ) { - return pos->second.weakDef; - } - else { - // look in children that I re-export - for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - if ( it->reExport ) { - //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.weakDef; - } - } - } - return false; + + return hasWeakDefinitionImpl(name).second; } @@ -756,24 +808,24 @@ bool File::allSymbolsAreWeakImported() const template -bool File::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const +bool File::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const { if ( _ignoreExports.count(name) != 0 ) return false; // check myself - typename NameToAtomMap::iterator pos = _atoms.find(name); + const auto pos = _atoms.find(name); if ( pos != _atoms.end() ) { - *weakDef = pos->second.weakDef; - *tlv = pos->second.tlv; - *defAddress = pos->second.address; + weakDef = pos->second.weakDef; + tlv = pos->second.tlv; + defAddress = pos->second.address; return true; } // check dylibs I re-export - for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - if ( it->reExport && !it->dylib->implicitlyLinked() ) { - if ( it->dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport && !dep.dylib->implicitlyLinked() ) { + if ( dep.dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) return true; } } @@ -791,7 +843,7 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han AtomAndWeak bucket; - if ( this->containsOrReExports(name, &bucket.weakDef, &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; @@ -903,23 +955,23 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b } template -void File::assertNoReExportCycles(ReExportChain* prev) +void File::assertNoReExportCycles(ReExportChain* prev) const { // recursively check my re-exported dylibs ReExportChain chain; chain.prev = prev; chain.file = this; - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { - if ( it->reExport ) { - ld::File* child = it->dylib; + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport ) { + ld::File* child = dep.dylib; // check child is not already in chain - for (ReExportChain* p = prev; p != NULL; p = p->prev) { + for (ReExportChain* p = prev; p != nullptr; p = p->prev) { if ( p->file == child ) { throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); } } - if ( it->dylib != NULL ) - it->dylib->assertNoReExportCycles(&chain); + if ( dep.dylib != nullptr ) + dep.dylib->assertNoReExportCycles(&chain); } } } @@ -940,13 +992,15 @@ class Parser ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), - opts.macosxVersionMin(), - opts.iOSVersionMin(), + opts.platform(), + opts.minOSversion(), opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), - opts.logAllFiles(), + opts.targetIOSSimulator(), + opts.logAllFiles(), opts.installPath(), - indirectDylib); + indirectDylib, + opts.outputKind() == Options::kPreload); } }; diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 49d333e..c023f37 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -42,11 +42,13 @@ #include #include #include +#include #include "dwarf2.h" #include "debugline.h" #include "Architectures.hpp" +#include "Bitcode.hpp" #include "ld.hpp" #include "macho_relocatable_file.h" @@ -73,7 +75,7 @@ class File : public ld::relocatable::File File(const char* p, time_t mTime, const uint8_t* content, ld::File::Ordinal ord) : ld::relocatable::File(p,mTime,ord), _fileContent(content), _sectionsArray(NULL), _atomsArray(NULL), - _sectionsArrayCount(0), _atomsArrayCount(0), + _sectionsArrayCount(0), _atomsArrayCount(0), _aliasAtomsArrayCount(0), _debugInfoKind(ld::relocatable::File::kDebugInfoNone), _dwarfTranslationUnitPath(NULL), _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), @@ -81,14 +83,19 @@ class File : public ld::relocatable::File _objConstraint(ld::File::objcConstraintNone), _swiftVersion(0), _cpuSubType(0), - _canScatterAtoms(false) {} + _minOSVersion(0), + _platform(0), + _canScatterAtoms(false), + _srcKind(kSourceUnknown) {} virtual ~File(); // overrides of ld::File virtual bool forEachAtom(ld::File::AtomHandler&) const; virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const { return false; } - + virtual uint32_t minOSVersion() const { return _minOSVersion; } + virtual uint32_t platformLoadCommand() const { return _platform; } + // overrides of ld::relocatable::File virtual ObjcConstraint objCConstraint() const { return _objConstraint; } virtual uint32_t cpuSubType() const { return _cpuSubType; } @@ -98,6 +105,9 @@ class File : public ld::relocatable::File virtual const char* translationUnitSource() const; virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } virtual uint8_t swiftVersion() const { return _swiftVersion; } + virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } + virtual SourceKind sourceKind() const { return _srcKind; } + virtual void setSourceKind(SourceKind src) { _srcKind = src; } const uint8_t* fileContent() { return _fileContent; } private: @@ -128,8 +138,12 @@ class File : public ld::relocatable::File ld::File::ObjcConstraint _objConstraint; uint8_t _swiftVersion; uint32_t _cpuSubType; + uint32_t _minOSVersion; + uint32_t _platform; bool _canScatterAtoms; std::vector > _linkerOptions; + std::unique_ptr _bitcode; + SourceKind _srcKind; }; @@ -148,6 +162,7 @@ class Section : public ld::Section virtual ld::Atom::Alignment alignmentForAddress(pint_t addr); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeUnclassified; } virtual bool dontDeadStrip() { return (this->_machOSection->flags() & S_ATTR_NO_DEAD_STRIP); } + virtual bool dontDeadStripIfReferencesLive() { return ( (this->_machOSection != NULL) && (this->_machOSection->flags() & S_ATTR_LIVE_SUPPORT) ); } virtual Atom* findAtomByAddress(pint_t addr) { return this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); } virtual bool addFollowOnFixups() const { return ! _file.canScatterAtoms(); } virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, @@ -506,6 +521,30 @@ class NonLazyPointerSection : public FixedSizeSection static ld::Fixup::Kind fixupKind(); }; +template +class TLVPointerSection : public FixedSizeSection +{ +public: + TLVPointerSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeTLVPointer; } + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "tlv_lazy_ptr"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser&, pint_t); + virtual bool ignoreLabel(const char* label) const { return true; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + +private: + static const char* targetName(const class Atom* atom, const ld::IndirectBindingTable& ind, bool* isStatic); +}; + template class CFStringSection : public FixedSizeSection @@ -752,7 +791,7 @@ class Atom : public ld::Atom parser.combineFromSymbol(sym), parser.scopeFromSymbol(sym), parser.resolverFromSymbol(sym) ? ld::Atom::typeResolver : sct.contentType(), parser.inclusionFromSymbol(sym), - parser.dontDeadStripFromSymbol(sym) || sct.dontDeadStrip(), + (parser.dontDeadStripFromSymbol(sym) && !sct.dontDeadStripIfReferencesLive()) || sct.dontDeadStrip(), parser.isThumbFromSymbol(sym), alias, sct.alignmentForAddress(sym.n_value())), _size(sz), _objAddress(sym.n_value()), @@ -764,7 +803,9 @@ class Atom : public ld::Atom if ( _scope == ld::Atom::scopeGlobal && (sym.n_desc() & (N_WEAK_DEF|N_WEAK_REF)) == (N_WEAK_DEF|N_WEAK_REF) ) this->setAutoHide(); - this->verifyAlignment(*sct.machoSection()); + this->verifyAlignment(*sct.machoSection()); + if ( sct.dontDeadStripIfReferencesLive() ) + this->setDontDeadStripIfReferencesLive(); } private: @@ -936,15 +977,18 @@ class Parser static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); static const char* fileKind(const uint8_t* fileContent); + static Options::Platform findPlatform(const macho_header* header); static bool hasObjC2Categories(const uint8_t* fileContent); static bool hasObjC1Categories(const uint8_t* fileContent); + static bool getNonLocalSymbols(const uint8_t* fileContnet, std::vector &syms); static ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { Parser p(fileContent, fileLength, path, modTime, ordinal, opts.warnUnwindConversionProblems, opts.keepDwarfUnwind, opts.forceDwarfConversion, - opts.neverConvertDwarf, opts.verboseOptimizationHints); + opts.neverConvertDwarf, opts.verboseOptimizationHints, + opts.ignoreMismatchPlatform); return p.parse(opts); } @@ -1114,7 +1158,7 @@ class Parser sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList, sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs, sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs, - sectionTypeCompactUnwind }; + sectionTypeCompactUnwind, sectionTypeTLVPointers}; template struct MachOSectionAndSectionClass @@ -1141,10 +1185,11 @@ class Parser Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, bool warnUnwindConversionProblems, bool keepDwarfUnwind, - bool forceDwarfConversion, bool neverConvertDwarf, bool verboseOptimizationHints); + bool forceDwarfConversion, bool neverConvertDwarf, + bool verboseOptimizationHints, bool ignoreMismatchPlatform); ld::relocatable::File* parse(const ParserOptions& opts); - uint8_t loadCommandSizeMask(); - bool parseLoadCommands(); + static uint8_t loadCommandSizeMask(); + bool parseLoadCommands(Options::Platform platform, uint32_t minOSVersion, bool simulator, bool ignoreMismatchPlatform); void makeSections(); void prescanSymbolTable(); void makeSortedSymbolsArray(uint32_t symArray[], const uint32_t sectionArray[]); @@ -1209,6 +1254,7 @@ class Parser bool _neverConvertDwarf; bool _verboseOptimizationHints; bool _armUsesZeroCostExceptions; + bool _ignoreMismatchPlatform; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1220,7 +1266,7 @@ class Parser template Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion, - bool neverConvertDwarf, bool verboseOptimizationHints) + bool neverConvertDwarf, bool verboseOptimizationHints, bool ignoreMismatchPlatform) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _indirectSymbolCount(0), _strings(NULL), _stringsSize(0), @@ -1236,6 +1282,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion), _neverConvertDwarf(neverConvertDwarf), _verboseOptimizationHints(verboseOptimizationHints), + _ignoreMismatchPlatform(ignoreMismatchPlatform), _stubsSectionNum(0), _stubsMachOSection(NULL) { } @@ -1318,7 +1365,7 @@ template <> const char* Parser::fileKind(const uint8_t* fileContent) { const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) + if ( header->magic() != MH_MAGIC_64 ) return NULL; if ( header->cputype() != CPU_TYPE_X86_64 ) return NULL; @@ -1346,7 +1393,7 @@ template <> const char* Parser::fileKind(const uint8_t* fileContent) { const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) + if ( header->magic() != MH_MAGIC_64 ) return NULL; if ( header->cputype() != CPU_TYPE_ARM64 ) return NULL; @@ -1411,6 +1458,39 @@ bool Parser::hasObjC1Categories(const uint8_t* fileContent) return false; } + +template +bool Parser::getNonLocalSymbols(const uint8_t* fileContent, std::vector &syms) +{ + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

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

* symtab = (macho_symtab_command

*)cmd; + uint32_t symbolCount = symtab->nsyms(); + const macho_nlist

* symbols = (const macho_nlist

*)(fileContent + symtab->symoff()); + const char* strings = (char*)fileContent + symtab->stroff(); + for (uint32_t i = 0; i < symbolCount; ++i) { + // ignore stabs and count only ext symbols + if ( (symbols[i].n_type() & N_STAB) == 0 && + (symbols[i].n_type() & N_EXT) != 0 ) { + const char* symName = &strings[symbols[i].n_strx()]; + syms.push_back(symName); + } + } + return true; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return false; +} + + template int Parser::pointerSorter(const void* l, const void* r) { @@ -1634,6 +1714,9 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // create file object _file = new File(_path, _modTime, _fileContent, _ordinal); + // set input source + _file->setSourceKind(opts.srcKind); + // respond to -t option if ( opts.logAllFiles ) printf("%s\n", _path); @@ -1641,7 +1724,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _armUsesZeroCostExceptions = opts.armUsesZeroCostExceptions; // parse start of mach-o file - if ( ! parseLoadCommands() ) + if ( ! parseLoadCommands(opts.platform, opts.minOSVersion, opts.simulator, opts.ignoreMismatchPlatform) ) return _file; // make array of @@ -1797,7 +1880,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) p += sizeof(Atom); } assert(fixupOffset == _allFixups.size()); - _file->_fixups.reserve(fixupOffset); + _file->_fixups.resize(fixupOffset); // copy each fixup for each atom for(typename std::vector::iterator it=_allFixups.begin(); it != _allFixups.end(); ++it) { @@ -1871,6 +1954,13 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) return _file; } +static void versionToString(uint32_t value, char buffer[32]) +{ + if ( value & 0xFF ) + sprintf(buffer, "%d.%d.%d", value >> 16, (value >> 8) & 0xFF, value & 0xFF); + else + sprintf(buffer, "%d.%d", value >> 16, (value >> 8) & 0xFF); +} template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } @@ -1878,7 +1968,7 @@ template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template -bool Parser::parseLoadCommands() +bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOSVersion, bool simulator, bool ignoreMismatchPlatform) { const macho_header

* header = (const macho_header

*)_fileContent; @@ -1892,6 +1982,7 @@ bool Parser::parseLoadCommands() // an empty .o file with zero load commands will crash linker if ( cmd_count == 0 ) return false; + Options::Platform lcPlatform = Options::kPlatformUnknown; const macho_load_command

* const cmds = (macho_load_command

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

)); const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; @@ -1970,6 +2061,18 @@ bool Parser::parseLoadCommands() throw "LC_LINKER_OPTIMIZATION_HINTS table extends beyond end of file"; } break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_WATCHOS: + #if SUPPORT_APPLE_TV + case LC_VERSION_MIN_TVOS: + #endif + if ( ignoreMismatchPlatform ) + break; + _file->_platform = cmd->cmd(); + lcPlatform = Options::platformForLoadCommand(cmd->cmd()); + _file->_minOSVersion = ((macho_version_min_command

*)cmd)->version(); + break; default: if ( cmd->cmd() == macho_segment_command

::CMD ) { if ( segment != NULL ) @@ -1982,6 +2085,52 @@ bool Parser::parseLoadCommands() if ( cmd > cmdsEnd ) throwf("malformed mach-o file, load command #%d is outside size of load commands", i); } + // arm/arm64 objects are default to ios platform if not set. + // rdar://problem/21746314 + if (lcPlatform == Options::kPlatformUnknown && + (std::is_same::value || std::is_same::value)) + lcPlatform = Options::kPlatformiOS; + + // Check platform cross-linking. + if ( !ignoreMismatchPlatform ) { + if ( lcPlatform != platform ) { + switch (platform) { + case Options::kPlatformOSX: + case Options::kPlatformiOS: + if ( lcPlatform == Options::kPlatformUnknown ) + break; + // fall through if the Platform is not Unknown + case Options::kPlatformWatchOS: + // WatchOS errors on cross-linking all the time. + throwf("building for %s%s, but linking in object file built for %s,", + Options::platformName(platform), (simulator ? " simulator" : ""), + Options::platformName(lcPlatform)); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + // tvOS is a warning temporarily. rdar://problem/21746965 + if (platform == Options::kPlatform_tvOS) + warning("URGENT: building for %s%s, but linking in object file (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), (simulator ? " simulator" : ""), path(), + Options::platformName(lcPlatform)); + break; + #endif + case Options::kPlatformUnknown: + // skip if the target platform is unknown + break; + } + } + if ( linkMinOSVersion && (_file->_minOSVersion > linkMinOSVersion) ) { + char t1[32]; + char t2[32]; + versionToString(_file->_minOSVersion, t1); + versionToString(linkMinOSVersion, t2); + warning("object file (%s) was built for newer %s version (%s) than being linked (%s)", + _path, Options::platformName(lcPlatform), t1, t2); + } + } + // record range of sections if ( segment == NULL ) @@ -1992,6 +2141,35 @@ bool Parser::parseLoadCommands() return true; } +template +Options::Platform Parser::findPlatform(const macho_header

* header) +{ + const uint32_t cmd_count = header->ncmds(); + if ( cmd_count == 0 ) + return Options::kPlatformUnknown; + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > (uint8_t*)cmdsEnd ) + throwf("load command #%d extends beyond the end of the load commands", i); + switch (cmd->cmd()) { + case LC_VERSION_MIN_MACOSX: + return Options::kPlatformOSX; + case LC_VERSION_MIN_IPHONEOS: + return Options::kPlatformiOS; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return Options::kPlatformUnknown; +} + template void Parser::prescanSymbolTable() @@ -2258,6 +2436,13 @@ void Parser::makeSections() // allocate raw storage for all section objects on stack MachOSectionAndSectionClass

* machOSects = (MachOSectionAndSectionClass

*)machOSectsStorage; unsigned int count = 0; + // local variable for bitcode parsing + const macho_section

* bitcodeSect = NULL; + const macho_section

* cmdlineSect = NULL; + const macho_section

* swiftCmdlineSect = NULL; + const macho_section

* bundleSect = NULL; + bool bitcodeAsm = false; + for (uint32_t i=0; i < _machOSectionsCount; ++i) { const macho_section

* sect = &_sectionsStart[i]; if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { @@ -2285,6 +2470,23 @@ void Parser::makeSections() } } } + if ( strcmp(sect->segname(), "__LLVM") == 0 ) { + if ( strncmp(sect->sectname(), "__bitcode", 9) == 0 ) { + bitcodeSect = sect; + } else if ( strncmp(sect->sectname(), "__cmdline", 9) == 0 ) { + cmdlineSect = sect; + } else if ( strncmp(sect->sectname(), "__swift_cmdline", 15) == 0 ) { + swiftCmdlineSect = sect; + } else if ( strncmp(sect->sectname(), "__bundle", 8) == 0 ) { + bundleSect = sect; + } else if ( strncmp(sect->sectname(), "__asm", 5) == 0 ) { + bitcodeAsm = true; + } + // If it is not single input for ld -r, don't count the section + // otherwise, fall through and add it to the sections. + if (_file->sourceKind() != ld::relocatable::File::kSourceSingle) + continue; + } // ignore empty __OBJC sections if ( (sect->size() == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) continue; @@ -2348,6 +2550,10 @@ void Parser::makeSections() totalSectionsSize += sizeof(NonLazyPointerSection); machOSects[count++].type = sectionTypeNonLazy; break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + totalSectionsSize += sizeof(TLVPointerSection); + machOSects[count++].type = sectionTypeTLVPointers; + break; case S_LITERAL_POINTERS: if ( (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__cls_refs") == 0) ) { totalSectionsSize += sizeof(Objc1ClassReferences); @@ -2404,11 +2610,26 @@ void Parser::makeSections() totalSectionsSize += sizeof(TLVDefsSection); machOSects[count++].type = sectionTypeTLVDefs; break; - case S_THREAD_LOCAL_VARIABLE_POINTERS: default: throwf("unknown section type %d", sect->flags() & SECTION_TYPE); } } + + // Create bitcode + if ( bitcodeSect != NULL ) { + if ( cmdlineSect != NULL ) + _file->_bitcode = std::unique_ptr(new ld::ClangBitcode(&_fileContent[bitcodeSect->offset()], bitcodeSect->size(), + &_fileContent[cmdlineSect->offset()], cmdlineSect->size())); + else if ( swiftCmdlineSect != NULL ) + _file->_bitcode = std::unique_ptr(new ld::SwiftBitcode(&_fileContent[bitcodeSect->offset()], bitcodeSect->size(), + &_fileContent[swiftCmdlineSect->offset()], swiftCmdlineSect->size())); + else + throwf("Object file with bitcode missing cmdline options: %s", _file->path()); + } + else if ( bundleSect != NULL ) + _file->_bitcode = std::unique_ptr(new ld::BundleBitcode(&_fileContent[bundleSect->offset()], bundleSect->size())); + else if ( bitcodeAsm ) + _file->_bitcode = std::unique_ptr(new ld::AsmBitcode(_fileContent, _fileLength)); // sort by address (mach-o object files don't aways have sections sorted) ::qsort(machOSects, count, sizeof(MachOSectionAndSectionClass

), MachOSectionAndSectionClass

::sorter); @@ -2451,6 +2672,10 @@ void Parser::makeSections() *objects++ = new (space) NonLazyPointerSection(*this, *_file, machOSects[i].sect); space += sizeof(NonLazyPointerSection); break; + case sectionTypeTLVPointers: + *objects++ = new (space) TLVPointerSection(*this, *_file, machOSects[i].sect); + space += sizeof(TLVPointerSection); + break; case sectionTypeCFI: _EHFrameSection = new (space) CFISection(*this, *_file, machOSects[i].sect); *objects++ = _EHFrameSection; @@ -3481,7 +3706,7 @@ void Parser::parseDebugInfo() p += sizeof(Atom); } assert(liOffset == entries.size()); - _file->_lineInfos.reserve(liOffset); + _file->_lineInfos.resize(liOffset); // copy each line info for each atom for (typename std::vector >::iterator it = entries.begin(); it != entries.end(); ++it) { @@ -3853,8 +4078,6 @@ const char* File::translationUnitSource() const return _dwarfTranslationUnitPath; } - - template bool File::forEachAtom(ld::File::AtomHandler& handler) const { @@ -4014,6 +4237,8 @@ ld::Section::Type Section::sectionType(const macho_section* se return ld::Section::typeTLVZeroFill; case S_THREAD_LOCAL_VARIABLES: return ld::Section::typeTLVDefs; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + return ld::Section::typeTLVPointers; case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: return ld::Section::typeTLVInitializerPointers; } @@ -4728,6 +4953,7 @@ const char* CUSection::personalityName(class Parser& parser, const const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); pint_t personalityAddr = *content; Section* personalitySection = parser.sectionForAddress(personalityAddr); + (void)personalitySection; assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); // atoms may not be constructed yet, so scan symbol table for labels const char* name = parser.scanSymbolTableForAddress(personalityAddr); @@ -5468,6 +5694,73 @@ ld::Atom::Scope NonLazyPointerSection::scopeAtAddress(Parser& parser, pint return ld::Atom::scopeLinkageUnit; } + + +template +ld::Atom::Combine TLVPointerSection::combine(Parser& parser, pint_t addr) +{ + return ld::Atom::combineByNameAndReferences; +} + + +template +const char* TLVPointerSection::targetName(const class Atom* atom, const ld::IndirectBindingTable& ind, bool* isStatic) +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + assert(atom->fixupCount() == 1); + *isStatic = false; + ld::Fixup::iterator fit = atom->fixupsBegin(); + const char* name = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + name = fit->u.name; + break; + case ld::Fixup::bindingByContentBound: + name = fit->u.target->name(); + break; + case ld::Fixup::bindingsIndirectlyBound: + name = ind.indirectName(fit->u.bindingIndex); + break; + case ld::Fixup::bindingDirectlyBound: + name = fit->u.target->name(); + *isStatic = (fit->u.target->scope() == ld::Atom::scopeTranslationUnit); + break; + default: + assert(0); + } + assert(name != NULL); + return name; +} + +template +unsigned long TLVPointerSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + unsigned long hash = 9508; + bool isStatic; + for (const char* s = this->targetName(atom, ind, &isStatic); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template +bool TLVPointerSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + if ( rhs.section().type() != ld::Section::typeTLVPointers ) + return false; + assert(this->type() == rhs.section().type()); + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + bool thisIsStatic; + bool rhsIsStatic; + const char* thisName = this->targetName(atom, indirectBindingTable, &thisIsStatic); + const char* rhsName = this->targetName(rhsAtom, indirectBindingTable, &rhsIsStatic); + return !thisIsStatic && !rhsIsStatic && (strcmp(thisName, rhsName) == 0); +} + + template const uint8_t* CFStringSection::targetContent(const class Atom* atom, const ld::IndirectBindingTable& ind, ContentType* ct, unsigned int* count) @@ -6826,7 +7119,8 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relo case ARM64_RELOC_UNSIGNED: if ( reloc->r_pcrel() ) throw "pcrel and ARM64_RELOC_UNSIGNED not supported"; - target.addend = contentValue; + if ( reloc->r_extern() ) + target.addend = contentValue; switch ( reloc->r_length() ) { case 0: case 1: @@ -7451,28 +7745,34 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO // // used by linker to infer architecture when no -arch is on command line // -bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult) +bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, Options::Platform* platform) { if ( mach_o::relocatable::Parser::validFile(fileContent) ) { *result = CPU_TYPE_X86_64; const macho_header >* header = (const macho_header >*)fileContent; *subResult = header->cpusubtype(); + *platform = Parser::findPlatform(header); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_I386; *subResult = CPU_SUBTYPE_X86_ALL; + *platform = Parser::findPlatform(header); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { - *result = CPU_TYPE_ARM; const macho_header >* header = (const macho_header >*)fileContent; + *result = CPU_TYPE_ARM; *subResult = header->cpusubtype(); + *platform = Parser::findPlatform(header); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_ARM64; *subResult = CPU_SUBTYPE_ARM64_ALL; + *platform = Parser::findPlatform(header); return true; } return false; @@ -7528,6 +7828,26 @@ bool hasObjC1Categories(const uint8_t* fileContent) return false; } +// +// Used by bitcode obfuscator to get a list of non local symbols from object file +// +bool getNonLocalSymbols(const uint8_t* fileContent, std::vector &syms) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + return false; +} + } // namespace relocatable diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index 92e9042..40f02d6 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -41,7 +41,12 @@ struct ParserOptions { bool neverConvertDwarf; bool verboseOptimizationHints; bool armUsesZeroCostExceptions; + bool simulator; + bool ignoreMismatchPlatform; uint32_t subType; + Options::Platform platform; + uint32_t minOSVersion; + ld::relocatable::File::SourceKind srcKind; }; extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, @@ -50,13 +55,15 @@ extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLen extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts); -extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); +extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, Options::Platform* platform); extern bool hasObjC2Categories(const uint8_t* fileContent); extern bool hasObjC1Categories(const uint8_t* fileContent); -extern const char* archName(const uint8_t* fileContent); +extern const char* archName(const uint8_t* fileContent); + +bool getNonLocalSymbols(const uint8_t* fileContent, std::vector &syms); } // namespace relocatable } // namespace mach_o diff --git a/ld64/src/ld/parsers/opaque_section_file.cpp b/ld64/src/ld/parsers/opaque_section_file.cpp index e60332b..081e957 100644 --- a/ld64/src/ld/parsers/opaque_section_file.cpp +++ b/ld64/src/ld/parsers/opaque_section_file.cpp @@ -24,6 +24,7 @@ #include +#include #include "ld.hpp" #include "opaque_section_file.h" @@ -63,7 +64,7 @@ class File : public ld::File const char* symbolName="sect_create") : ld::File(pth, 0, ld::File::Ordinal::NullOrdinal(), Other), _atom(*this, symbolName, fileContent, fileLength), - _section(segmentName, sectionName, ld::Section::typeUnclassified) { } + _section(segmentName, sectionName, ld::Section::typeSectCreate) { } virtual ~File() { } virtual bool forEachAtom(ld::File::AtomHandler& h) const { h.doAtom(_atom); return true; } diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp new file mode 100644 index 0000000..cde3344 --- /dev/null +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -0,0 +1,1072 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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 +#include + +#include + +#include "Architectures.hpp" +#include "bitcode.hpp" +#include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" +#include "textstub_dylib_file.hpp" + +namespace { + +/// +/// A token is a light-weight reference to the content of an nmap'ed file. It +/// doesn't own the data and it doesn't make a copy of it. The referenced data +/// is only valid as long as the file is mapped in. +/// +class Token { + const char* _p; + size_t _size; + + int compareMemory(const char* lhs, const char* rhs, size_t size) const { + if (size == 0) + return 0; + return ::memcmp(lhs, rhs, size); + } + +public: + Token() : _p(nullptr), _size(0) {} + + Token(const char* p) : _p(p), _size(0) { + if (p) + _size = ::strlen(p); + } + + Token(const char* p, size_t s) : _p(p), _size(s) {} + + const char* data() const { return _p; } + + size_t size() const { return _size; } + + std::string str() const { return std::move(std::string(_p, _size)); } + + bool empty() const { return _size == 0; } + + bool operator==(Token other) const { + if (_size != other._size) + return false; + return compareMemory(_p, other._p, _size) == 0; + } + + bool operator!=(Token other) const { + return !(*this == other); + } +}; + +/// +/// Simple text-based dynamic library file tokenizer. +/// +class Tokenizer { + const char* _start; + const char* _current; + const char* _end; + Token _currentToken; + + void fetchNextToken(); + void scanToNextToken(); + void skip(unsigned distance) { + _current += distance; + assert(_current <= _end && "Skipped past the end"); + } + + const char* skipLineBreak(const char* pos) const; + bool isDelimiter(const char* pos) const; + +public: + Tokenizer(const char* data, uint64_t size) : _start(data), _current(data), _end(data + size) {} + + void reset() { + _current = _start; + fetchNextToken(); + } + + Token peek() { return _currentToken; } + Token next() { + Token token = peek(); + fetchNextToken(); + return token; + } +}; + +const char* Tokenizer::skipLineBreak(const char* pos) const +{ + if ( pos == _end ) + return pos; + + // Carriage return. + if ( *pos == 0x0D ) { + // line feed. + if ( pos + 1 != _end && *(pos + 1) == 0x0A) + return pos + 2; + return pos + 1; + } + + // line feed. + if ( *pos == 0x0A ) + return pos + 1; + + return pos; +} + +void Tokenizer::scanToNextToken() { + while (true) { + while ( isDelimiter(_current) ) + skip(1); + + const char* i = skipLineBreak(_current); + if ( i == _current ) + break; + + _current = i; + } +} + + +bool Tokenizer::isDelimiter(const char* pos) const { + if ( pos == _end ) + return false; + if ( *pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n' || *pos == ',' || *pos == ':' || *pos == '\'' || *pos == '\"' ) + return true; + return false; +} + +void Tokenizer::fetchNextToken() { + scanToNextToken(); + + if (_current == _end) { + _currentToken = Token(); + return; + } + + auto start = _current; + while ( !isDelimiter(_current) ) { + ++_current; + } + + _currentToken = Token(start, _current - start); +} + +/// +/// Representation of a parsed text-based dynamic library file. +/// +struct DynamicLibrary { + Token _installName; + uint32_t _currentVersion; + uint32_t _compatibilityVersion; + uint8_t _swiftVersion; + ld::File::ObjcConstraint _objcConstraint; + Options::Platform _platform; + std::vector _allowedClients; + std::vector _reexportedLibraries; + std::vector _symbols; + std::vector _classes; + std::vector _ivars; + std::vector _weakDefSymbols; + std::vector _tlvSymbols; + + DynamicLibrary() : _currentVersion(0x10000), _compatibilityVersion(0x10000), _swiftVersion(0), + _objcConstraint(ld::File::objcConstraintNone) {} +}; + +static uint32_t parseVersionNumber32(Token token) { + if ( token.size() >= 128 ) + throwf("malformed version number"); + + char buffer[128]; + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + char* end; + + // Make a null-terminated string. + ::memcpy(buffer, token.data(), token.size()); + buffer[token.size()] = '\0'; + + x = strtoul(buffer, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed 32-bit x.y.z version number: %s", buffer); + + return (x << 16) | ( y << 8 ) | z; +} + +/// +/// A simple text-based dynamic library file parser. +/// +class TBDFile { + Tokenizer _tokenizer; + + Token peek() { return _tokenizer.peek(); } + Token next() { return _tokenizer.next(); } + + void expectToken(Token str) { + Token token = next(); + if (token != str) + throwf("unexpected token: %s", token.str().c_str()); + } + + bool hasOptionalToken(Token str) { + auto token = peek(); + if ( token == str ) { + next(); + return true; + } + return false; + } + + + void parseFlowSequence(std::function func) { + expectToken("["); + + while ( true ) { + auto token = peek(); + if ( token == "]" ) + break; + + token = next(); + func(token); + } + + expectToken("]"); + } + + void parseAllowedClients(DynamicLibrary& lib) { + if ( !hasOptionalToken("allowed-clients") ) + return; + parseFlowSequence([&](Token name) { + lib._allowedClients.emplace_back(name); + }); + } + + void parseReexportedDylibs(DynamicLibrary& lib) { + if ( !hasOptionalToken("re-exports") ) + return; + parseFlowSequence([&](Token name) { + lib._reexportedLibraries.emplace_back(name); + }); + } + + void parseSymbols(DynamicLibrary& lib) { + if ( hasOptionalToken("symbols") ) { + parseFlowSequence([&](Token name) { + lib._symbols.emplace_back(name); + }); + } + + if ( hasOptionalToken("objc-classes") ) { + parseFlowSequence([&](Token name) { + lib._classes.emplace_back(name); + }); + } + + if ( hasOptionalToken("objc-ivars") ) { + parseFlowSequence([&](Token name) { + lib._ivars.emplace_back(name); + }); + } + + if ( hasOptionalToken("weak-def-symbols") ) { + parseFlowSequence([&](Token name) { + lib._weakDefSymbols.emplace_back(name); + }); + } + + if ( hasOptionalToken("thread-local-symbols") ) { + parseFlowSequence([&](Token name) { + lib._tlvSymbols.emplace_back(name); + }); + } + } + + bool parseArchFlowSequence(Token archName) { + expectToken("archs"); + + bool foundArch = false; + parseFlowSequence([&](Token name) { + if ( name == archName ) + foundArch = true; + }); + + return foundArch; + } + + void parsePlatform(DynamicLibrary& lib) { + expectToken("platform"); + + auto token = next(); + if (token == "macosx") + lib._platform = Options::kPlatformOSX; + else if (token == "ios") + lib._platform = Options::kPlatformiOS; + else if (token == "watchos") + lib._platform = Options::kPlatformWatchOS; +#if SUPPORT_APPLE_TV + else if (token == "tvos") + lib._platform = Options::kPlatform_tvOS; +#endif + else + lib._platform = Options::kPlatformUnknown; + } + + void parseInstallName(DynamicLibrary& lib) { + expectToken("install-name"); + + lib._installName = next(); + if ( lib._installName.empty() ) + throwf("no install name specified"); + } + + void parseCurrentVersion(DynamicLibrary& lib) { + if ( !hasOptionalToken("current-version") ) + return; + lib._currentVersion = parseVersionNumber32(next()); + } + + void parseCompatibilityVersion(DynamicLibrary& lib) { + if ( !hasOptionalToken("compatibility-version") ) + return; + lib._compatibilityVersion = parseVersionNumber32(next()); + } + + void parseSwiftVersion(DynamicLibrary& lib) { + if ( !hasOptionalToken("swift-version") ) + return; + auto token = next(); + if ( token == "1.0" ) + lib._swiftVersion = 1; + else if ( token == "1.1" ) + lib._swiftVersion = 2; + else if ( token == "2.0" ) + lib._swiftVersion = 3; + else + throwf("unsupported Swift ABI version: %s", token.str().c_str()); + } + + void parseObjCConstraint(DynamicLibrary& lib) { + if ( !hasOptionalToken("objc-constraint") ) + return; + auto token = next(); + if ( token == "none" ) + lib._objcConstraint = ld::File::objcConstraintNone; + else if ( token == "retain_release" ) + lib._objcConstraint = ld::File::objcConstraintRetainRelease; + else if ( token == "retain_release_for_simulator" ) + lib._objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + else if ( token == "retain_release_or_gc" ) + lib._objcConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( token == "gc" ) + lib._objcConstraint = ld::File::objcConstraintGC; + else + throwf("unexpected token: %s", token.str().c_str()); + } + void parseExportsBlock(DynamicLibrary& lib, Token archName) { + if ( !hasOptionalToken("exports") ) + return; + + if ( !hasOptionalToken("-") ) + return; + + while ( true ) { + if ( !parseArchFlowSequence(archName) ) { + Token token; + while ( true ) { + token = peek(); + if ( token == "archs" || token == "..." || token.empty() ) + break; + next(); + } + if (token == "..." || token.empty() ) + break; + + continue; + } + + parseAllowedClients(lib); + parseReexportedDylibs(lib); + parseSymbols(lib); + if ( !hasOptionalToken("-") ) + break; + } + } + + void parseDocument(DynamicLibrary& lib, Token archName) { + if ( !parseArchFlowSequence(archName) ) + throwf("invalid arch"); + + parsePlatform(lib); + parseInstallName(lib); + parseCurrentVersion(lib); + parseCompatibilityVersion(lib); + parseSwiftVersion(lib); + parseObjCConstraint(lib); + parseExportsBlock(lib, archName); + } + +public: + TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {} + + DynamicLibrary parseFileForArch(Token archName) { + _tokenizer.reset(); + DynamicLibrary lib; + expectToken("---"); + parseDocument(lib, archName); + expectToken("..."); + return std::move(lib); + } + + bool validForArch(Token archName) { + _tokenizer.reset(); + auto token = next(); + if ( token != "---" ) + return false; + return parseArchFlowSequence(archName); + } + + void dumpTokens() { + _tokenizer.reset(); + Token token; + do { + token = next(); + printf("token: %s\n", token.str().c_str()); + } while ( !token.empty() ); + } +}; + +} // end anonymous namespace + +namespace textstub { +namespace dylib { + +// forward reference +template class File; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template +class ExportAtom : public ld::Atom +{ +public: + ExportAtom(const File& f, const char* nm, bool weakDef, bool tlv) + : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, + (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), + ld::Atom::scopeLinkageUnit, + (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(nm) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return &_file; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + virtual ~ExportAtom() {} + + const File& _file; + const char* _name; +}; + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class File : public ld::dylib::File +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + File(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, + bool hoistImplicitPublicDylibs, Options::Platform platform, + cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* installPath, bool indirectDylib); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual ld::File::ObjcConstraint objCConstraint() const { return _objcConstraint; } + virtual uint8_t swiftVersion() const { return _swiftVersion; } + + // overrides of ld::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); + virtual bool providedExportAtom() const { return _providedAtom; } + virtual const char* parentUmbrella() const { return nullptr; } + virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : nullptr; } + virtual bool hasWeakExternals() const { return _hasWeakExports; } + virtual bool deadStrippable() const { return false; } + virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } + virtual bool hasWeakDefinition(const char* name) const; + virtual bool allSymbolsAreWeakImported() const; + virtual bool installPathVersionSpecific() const { return _installPathOverride; } + // All text-based stubs are per definition AppExtensionSafe. + virtual bool appExtensionSafe() const { return true; }; + virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } + + +protected: + virtual void assertNoReExportCycles(ReExportChain*) const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + friend class ExportAtom; + + struct CStringHash { + std::size_t operator()(const char* __s) const { + unsigned long __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return size_t(__h); + }; + }; + struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; }; + typedef std::unordered_map NameToAtomMap; + typedef std::unordered_set NameSet; + + struct Dependent { const char* path; File* dylib; }; + + virtual std::pair hasWeakDefinitionImpl(const char* name) const; + virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& address) const; + + void buildExportHashTable(const DynamicLibrary &lib); + bool isPublicLocation(const char* pth); + bool wrongOS() { return _wrongOS; } + void addSymbol(const char* name, bool weak, bool tlv); + + const Options::Platform _platform; + cpu_type_t _cpuType; + const uint32_t _linkMinOSVersion; + const bool _allowSimToMacOSXLinking; + const bool _addVersionLoadCommand; + bool _linkingFlat; + bool _implicitlyLinkPublicDylibs; + ld::File::ObjcConstraint _objcConstraint; + uint8_t _swiftVersion; + ld::Section _importProxySection; + ld::Section _flatDummySection; + std::vector _dependentDylibs; + std::vector _allowableClients; + mutable NameToAtomMap _atoms; + NameSet _ignoreExports; + bool _noRexports; + bool _hasWeakExports; + bool _hasPublicInstallName; + mutable bool _providedAtom; + bool _wrongOS; + bool _installPathOverride; + bool _indirectDylibsProcessed; + std::unique_ptr _bitcode; + static bool _s_logHashtable; +}; + +template +bool File::_s_logHashtable = false; + + +template +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, + ld::File::Ordinal ord, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, + Options::Platform platform, cpu_type_t cpuType, const char* archName, + uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, + bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath, + bool indirectDylib) + : ld::dylib::File(strdup(path), mTime, ord), _platform(platform), _cpuType(cpuType), + _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), + _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), + _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), + _objcConstraint(ld::File::objcConstraintNone), _swiftVersion(0), + _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), + _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), + _noRexports(false), _hasWeakExports(false), + _hasPublicInstallName(false), _providedAtom(false), _wrongOS(false), + _installPathOverride(false), _indirectDylibsProcessed(false), + _bitcode(new ld::Bitcode(nullptr, 0)) +{ + // write out path for -t option + if ( logAllFiles ) + printf("%s\n", path); + + TBDFile stub((const char*)fileContent, fileLength); + auto lib = stub.parseFileForArch(archName); + + _noRexports = lib._reexportedLibraries.empty(); + _hasWeakExports = !lib._weakDefSymbols.empty(); + _dylibInstallPath = strdup(lib._installName.str().c_str()); + _dylibCurrentVersion = lib._currentVersion; + _dylibCompatibilityVersion = lib._compatibilityVersion; + _swiftVersion = lib._swiftVersion; + _objcConstraint = lib._objcConstraint; + _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + + for (auto &client : lib._allowedClients) + _allowableClients.push_back(strdup(client.str().c_str())); + + // [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked + if ( !_allowableClients.empty() ) + _hasPublicInstallName = false; + + if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib ) { + if ( buildingForSimulator ) { + if ( !_allowSimToMacOSXLinking ) + throwf("building for %s simulator, but linking against dylib built for %s (%s).", + Options::platformName(platform), Options::platformName(lib._platform), path); + } else { + throwf("building for %s, but linking against dylib built for %s (%s).", + Options::platformName(platform), Options::platformName(lib._platform), path); + } + } + } + + _dependentDylibs.reserve(lib._reexportedLibraries.size()); + for ( auto& reexport : lib._reexportedLibraries ) { + Dependent entry; + entry.path = strdup(reexport.str().c_str()); + entry.dylib = nullptr; + if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, entry.path) != 0) ) + _dependentDylibs.push_back(entry); + } + + // build hash table + buildExportHashTable(lib); + + munmap((caddr_t)fileContent, fileLength); +} + +template +void File::buildExportHashTable(const DynamicLibrary& lib) { + if ( _s_logHashtable ) + fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path()); + + for (auto &sym : lib._symbols) + addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/false); + +#if SUPPORT_ARCH_i386 + if (_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) { + for (auto &sym : lib._classes) + addSymbol((".objc_class_name" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + } else { + for (auto &sym : lib._classes) { + addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + } + } +#else + for (auto &sym : lib._classes) { + addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + } +#endif + + for (auto &sym : lib._ivars) + addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + + for (auto &sym : lib._weakDefSymbols) + addSymbol(sym.str().c_str(), /*weak=*/true, /*tlv=*/false); + + for (auto &sym : lib._tlvSymbols) + addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true); +} + + +template +void File::addSymbol(const char* name, bool weakDef, bool tlv) +{ + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != nullptr ) { + char curOSVers[16]; + sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != nullptr ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( _s_logHashtable ) + fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + _ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef, false); + return; + } + else if ( strncmp(symAction, "install_name$", 13) == 0 ) { + _dylibInstallPath = strdup(symName); + _installPathOverride = true; + return; + } + else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { + _dylibCompatibilityVersion = parseVersionNumber32(symName); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( _ignoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = nullptr; + bucket.weakDef = weakDef; + bucket.tlv = tlv; + if ( _s_logHashtable ) + fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + _atoms[strdup(name)] = bucket; + } +} + + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + return false; +} + + +template +std::pair File::hasWeakDefinitionImpl(const char* name) const +{ + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) + return std::make_pair(true, pos->second.weakDef); + + // look in children that I re-export + for (const auto &dep : _dependentDylibs) { + auto ret = dep.dylib->hasWeakDefinitionImpl(name); + if ( ret.first ) + return ret; + } + return std::make_pair(false, false); +} + + +template +bool File::hasWeakDefinition(const char* name) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + return hasWeakDefinitionImpl(name).second; +} + + +// 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; + for (const auto &it : _atoms) { + const ld::Atom* atom = it.second.atom; + if ( atom != nullptr ) { + if ( atom->weakImported() ) + foundWeakImport = true; + else + foundNonWeakImport = true; + } + } + + // 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, uint64_t& addr) const +{ + if ( _ignoreExports.count(name) != 0 ) + return false; + + // check myself + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + weakDef = pos->second.weakDef; + tlv = pos->second.tlv; + addr = 0; + return true; + } + + // check dylibs I re-export + for (const auto& lib : _dependentDylibs) { + if ( !lib.dylib->implicitlyLinked() ) { + if ( lib.dylib->containsOrReExports(name, weakDef, tlv, addr) ) + return true; + } + } + + return false; +} + + +template +bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + + AtomAndWeak bucket; + uint64_t addr; + if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) { + bucket.atom = new ExportAtom(*this, name, bucket.weakDef, bucket.tlv); + _atoms[name] = bucket; + _providedAtom = true; + if ( _s_logHashtable ) + fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); + // call handler with new export atom + handler.doAtom(*bucket.atom); + return true; + } + + return false; +} + + + +template +bool File::isPublicLocation(const char* path) +{ + // -no_implicit_dylibs disables this optimization + if ( ! _implicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&path[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != nullptr ) { + int frameworkNameLen = frameworkDot - &path[27]; + if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +{ + // only do this once + if ( _indirectDylibsProcessed ) + return; + + const static bool log = false; + if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); + if ( _linkingFlat ) { + for (auto& lib : _dependentDylibs) { + lib.dylib = (File*)handler->findDylib(lib.path, this->path()); + } + } + else if ( _noRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (auto& lib : _dependentDylibs) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), lib.path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + lib.dylib = (File*)handler->findDylib(lib.path, this->path()); + if ( lib.dylib->hasPublicInstallName() && !lib.dylib->wrongOS() ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(lib.path, lib.dylib->installPath()) == 0) ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", lib.dylib->installPath()); + lib.dylib->setImplicitlyLinked(); + } + else if ( lib.dylib->explicitlyLinked() || lib.dylib->implicitlyLinked() ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } else { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib.path); + } + } else { + // add all child's symbols to me + if ( log ) + fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib.path); + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = nullptr; + chain.file = this; + this->assertNoReExportCycles(&chain); + + _indirectDylibsProcessed = true; +} + +template +void File::assertNoReExportCycles(ReExportChain* prev) const +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.file = this; + for (const auto& dep : _dependentDylibs) { + ld::File* child = dep.dylib; + // check child is not already in chain + for (ReExportChain* p = prev; p != nullptr; p = p->prev) { + if ( p->file == child ) + throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); + } + if ( dep.dylib != nullptr ) + dep.dylib->assertNoReExportCycles(&chain); + } +} + + +template +class Parser +{ +public: + typedef typename A::P P; + + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName); + static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, ld::File::Ordinal ordinal, const Options& opts, + bool indirectDylib) { + return new File(fileContent, fileLength, path, mTime, ordinal, + opts.flatNamespace(), + opts.implicitlyLinkIndirectPublicDylibs(), + opts.platform(), + opts.architecture(), + opts.architectureName(), + opts.minOSversion(), + opts.allowSimulatorToLinkWithMacOSX(), + opts.addVersionLoadCommand(), + opts.targetIOSSimulator(), + opts.logAllFiles(), + opts.installPath(), + indirectDylib); + } +}; + +template +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName) +{ + if ( path.find(".tbd", path.size()-4) == std::string::npos ) + return false; + + TBDFile stub((const char*)fileContent, fileLength); + if ( !stub.validForArch(archName) ) + throwf("missing required architecture %s in file %s", archName, path.c_str()); + + return true; +} + +// +// 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, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib) +{ + switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 + case CPU_TYPE_X86_64: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_i386 + case CPU_TYPE_I386: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif + } + return nullptr; +} + + +} // namespace dylib +} // namespace textstub + + diff --git a/ld64/src/ld/parsers/textstub_dylib_file.hpp b/ld64/src/ld/parsers/textstub_dylib_file.hpp new file mode 100644 index 0000000..e0a75f6 --- /dev/null +++ b/ld64/src/ld/parsers/textstub_dylib_file.hpp @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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 __TEXTSTUB_DYLIB_FILE_H__ +#define __TEXTSTUB_DYLIB_FILE_H__ + +#include "ld.hpp" +#include "Options.h" + +namespace textstub { +namespace dylib { + + +extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib); + +} // namespace dylib +} // namespace textstub + + +#endif // __TEXTSTUB_DYLIB_FILE_H__ diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp new file mode 100644 index 0000000..bafad0b --- /dev/null +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -0,0 +1,809 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llvm-c/lto.h" +// c header +extern "C" { +#include +} + +#include "bitcode_bundle.h" + +#include "Options.h" +#include "ld.hpp" +#include "Bitcode.hpp" +#include "macho_relocatable_file.h" + + +namespace ld { +namespace passes { +namespace bitcode_bundle { + +class BitcodeTempFile; + +class BitcodeAtom : public ld::Atom { + static ld::Section bitcodeBundleSection; +public: + BitcodeAtom(); + BitcodeAtom(BitcodeTempFile& tempfile); + ~BitcodeAtom() { free(_content); } + virtual ld::File* file() const { return NULL; } + virtual const char* name() const { return "bitcode bundle"; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { memcpy(buffer, _content, _size); } + virtual void setScope(Scope) { } + +private: + uint8_t* _content; + uint64_t _size; +}; + +ld::Section BitcodeAtom::bitcodeBundleSection("__LLVM", "__bundle", ld::Section::typeSectCreate); + +class BitcodeTempFile { +public: + BitcodeTempFile(const char* path, bool deleteAfterRead); + ~BitcodeTempFile(); + uint8_t* getContent() const { return _content; } + uint64_t getSize() const { return _size; } +private: + friend class BitcodeAtom; + const char* _path; + uint8_t* _content; + uint64_t _size; + bool _deleteAfterRead; +}; + +class BitcodeObfuscator { +public: + BitcodeObfuscator(); + ~BitcodeObfuscator(); + + void addMustPreserveSymbols(const char* name); + void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath); + void writeSymbolMap(const char* outputPath); +private: + typedef void (*lto_codegen_func_t) (lto_code_gen_t); + typedef void (*lto_codegen_output_t) (lto_code_gen_t, const char*); + + lto_code_gen_t _obfuscator; + lto_codegen_func_t _lto_hide_symbols; + lto_codegen_func_t _lto_reset_context; + lto_codegen_output_t _lto_write_reverse_map; +}; + +class FileHandler { + // generic handler for files in a bundle +public: + virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator) { } + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path) { }; + xar_file_t getXARFile() { return _xar_file; } + + FileHandler(char* content, size_t size) : + _parent(NULL), _xar_file(NULL), _file_buffer(content), _file_size(size) { } // eager construct + FileHandler(xar_t parent, xar_file_t xar_file) : + _parent(parent), _xar_file(xar_file), _file_buffer(NULL), _file_size(0) { } // lazy construct + virtual ~FileHandler() { } + +protected: + void initFile() { + if (!_file_buffer) { + if (xar_extract_tobuffersz(_parent, _xar_file, &_file_buffer, &_file_size) != 0) + throwf("could not extract files from bitcode bundle"); + } + } + void destroyFile() { + if (_parent) + free(_file_buffer); + } + + xar_t _parent; + xar_file_t _xar_file; + char* _file_buffer; + size_t _file_size; +}; + +class BundleHandler : public FileHandler { +public: + BundleHandler(char* bundleContent, size_t bundleSize, const Options& options) : + FileHandler(bundleContent, bundleSize), _xar(NULL), _temp_dir(NULL), _options(options) { } + BundleHandler(xar_t parent, xar_file_t xar_file, const Options& options) : + FileHandler(parent, xar_file), _xar(NULL), _temp_dir(NULL), _options(options) { } + + ~BundleHandler(); + + virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; + +private: + void init(); + void copyXARProp(xar_file_t src, xar_file_t dst); + + xar_t _xar; + char* _temp_dir; + const Options& _options; + std::vector _handlers; +}; + +class BitcodeHandler : public FileHandler { +public: + BitcodeHandler(char* content, size_t size) : FileHandler(content, size) { } + BitcodeHandler(xar_t parent, xar_file_t xar_file) : FileHandler(parent, xar_file) { } + + ~BitcodeHandler(); + + virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override { } // Don't need to preserve symbols + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; +}; + +class ObjectHandler : public FileHandler { +public: + ObjectHandler(char* content, size_t size) : + FileHandler(content, size) { } + ObjectHandler(xar_t parent, xar_file_t xar_file) : + FileHandler(parent, xar_file) { } + + ~ObjectHandler(); + + void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; + void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; + +}; + + +class BitcodeBundle { +public: + BitcodeBundle(const Options& opts, ld::Internal& internal) : + _options(opts), _state(internal) { } + ~BitcodeBundle() { } + void doPass(); + +private: + const Options& _options; + ld::Internal& _state; +}; + +BitcodeAtom::BitcodeAtom() +: ld::Atom(bitcodeBundleSection, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _size(1) +{ + // initialize a marker of 1 byte + _content = (uint8_t*)calloc(1,1); +} + +BitcodeAtom::BitcodeAtom(BitcodeTempFile& tempfile) + : ld::Atom(bitcodeBundleSection, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _content(tempfile._content), _size(tempfile._size) +{ + // Creating the Atom will transfer the ownership of the buffer from Tempfile to Atom + tempfile._content = NULL; +} + +BitcodeTempFile::BitcodeTempFile(const char* path, bool deleteAfterRead = true) + : _path(path), _deleteAfterRead(deleteAfterRead) +{ + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("could not open bitcode temp file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + _content = (uint8_t*)malloc(stat_buf.st_size); + if ( _content == NULL ) + throwf("could not process bitcode temp file: %s", path); + if ( read(fd, _content, stat_buf.st_size) != stat_buf.st_size ) + throwf("could not read bitcode temp file: %s", path); + ::close(fd); + _size = stat_buf.st_size; +} + +BitcodeTempFile::~BitcodeTempFile() +{ + free(_content); + if ( _deleteAfterRead ) { + if ( ::unlink(_path) != 0 ) + throwf("could not remove temp file: %s", _path); + } +} + +BitcodeObfuscator::BitcodeObfuscator() +{ + // check if apple internal libLTO is used + if ( ::lto_get_version() == NULL ) + throwf("libLTO is not loaded"); + _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols"); + _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map"); + _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context"); + if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL || + _lto_reset_context == NULL || ::lto_api_version() < 14 ) + throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); + _obfuscator = ::lto_codegen_create_in_local_context(); +#if LTO_API_VERSION >= 14 + lto_codegen_set_should_internalize(_obfuscator, false); +#endif +} + +BitcodeObfuscator::~BitcodeObfuscator() +{ + ::lto_codegen_dispose(_obfuscator); +} + +void BitcodeObfuscator::addMustPreserveSymbols(const char* name) +{ + ::lto_codegen_add_must_preserve_symbol(_obfuscator, name); +} + +void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath) +{ +#if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL + lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator); + if ( module == NULL ) + throwf("object contains invalid bitcode: %s", filePath); + ::lto_codegen_set_module(_obfuscator, module); + (*_lto_hide_symbols)(_obfuscator); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(_obfuscator, true); +#endif + ::lto_codegen_write_merged_modules(_obfuscator, outputPath); + (*_lto_reset_context)(_obfuscator); +#endif + return; +} + +void BitcodeObfuscator::writeSymbolMap(const char *outputPath) +{ + (*_lto_write_reverse_map)(_obfuscator, outputPath); +} + +BundleHandler::~BundleHandler() +{ + // free buffers + destroyFile(); + // free handlers + for (auto handler : _handlers) + delete handler; + + // delete temp file if not -save-temps + if ( _xar ) { + xar_close(_xar); + std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); + if ( !_options.saveTempFiles() && ::unlink(oldXARPath.c_str()) != 0) + warning("could not delete temp file: %s", oldXARPath.c_str()); + } + + if ( _temp_dir ) { + if ( !_options.saveTempFiles() && ::rmdir(_temp_dir) != 0 ) + warning("could not delete temp directory: %s", _temp_dir); + free(_temp_dir); + } +} + +BitcodeHandler::~BitcodeHandler() +{ + destroyFile(); +} + +ObjectHandler::~ObjectHandler() +{ + destroyFile(); +} + +void BundleHandler::init() +{ + if ( _xar != NULL ) + return; + + // make temp directory + const char* finalOutput = _options.outputFilePath(); + _temp_dir = (char*)malloc(PATH_MAX * sizeof(char)); + // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX + // If so, fall back to /tmp + if ( strlen(finalOutput) + 30 >= PATH_MAX ) + sprintf(_temp_dir, "/tmp/ld.bundle.XXXXXX"); + else + sprintf(_temp_dir, "%s.bundle.XXXXXX", finalOutput); + ::mkdtemp(_temp_dir); + + // write the bundle to the temp_directory + initFile(); + std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); + int f = ::open(oldXARPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if ( f == -1 ) + throwf("could not write file to temp directory: %s", _temp_dir); + if ( ::write(f, _file_buffer, _file_size) != (int)_file_size ) + throwf("failed to write content to temp file: %s", oldXARPath.c_str()); + ::close(f); + + // read the xar file + _xar = xar_open(oldXARPath.c_str(), READ); + + // Init the vector of handler + xar_iter_t iter = xar_iter_new(); + if ( !iter ) + throwf("could not aquire iterator for the bitcode bundle"); + for ( xar_file_t f = xar_file_first(_xar, iter); f; f = xar_file_next(iter) ) { + const char* filetype = NULL; + if ( xar_prop_get(f, "file-type", &filetype) != 0 ) + throwf("could not get the file type for the bitcode bundle"); + if ( strcmp(filetype, "Bundle") == 0 ) + _handlers.push_back(new BundleHandler(_xar, f, _options)); + else if ( strcmp(filetype, "Object") == 0 ) + _handlers.push_back(new ObjectHandler(_xar, f)); + else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 ) + _handlers.push_back(new BitcodeHandler(_xar, f)); + else + assert(0 && "Unknown file type"); + } + xar_iter_free(iter); +} + +void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) +{ + // copy the property in the XAR. + // Since XAR API can only get the first value from the key, + // Deleting the value after read. + int i = 0; + while (1) { + xar_iter_t p = xar_iter_new(); + const char* key = xar_prop_first(src, p); + for (int x = 0; x < i; x++) + key = xar_prop_next(p); + if ( !key ) + break; + const char* val = NULL; + xar_prop_get(src, key, &val); + if ( // Info from bitcode files + strcmp(key, "file-type") == 0 || + strcmp(key, "clang/cmd") == 0 || + strcmp(key, "swift/cmd") == 0 || + // Info from linker subdoc + strcmp(key, "version") == 0 || + strcmp(key, "architecture") == 0 || + strcmp(key, "hide-symbols") == 0 || + strcmp(key, "platform") == 0 || + strcmp(key, "sdkversion") == 0 || + strcmp(key, "dylibs/lib") == 0 || + strcmp(key, "link-options/option") == 0 ) { + xar_prop_create(dst, key, val); + xar_prop_unset(src, key); + } else + ++ i; + xar_iter_free(p); + } +} + +void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) +{ + // init the handler + if ( _xar == NULL ) + init(); + + // iterate through the XAR file and add symbols + for ( auto handler : _handlers ) + handler->populateMustPreserveSymbols(obfuscator); +} + +void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) +{ + initFile(); + // Parse the object file and add the symbols + std::vector symbols; + if ( mach_o::relocatable::getNonLocalSymbols((uint8_t*)_file_buffer, symbols) ) { + for ( auto sym : symbols ) + obfuscator->addMustPreserveSymbols(sym); + } +} + +void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +{ + // init the handler + if ( _xar == NULL ) + init(); + + // creating the new xar + xar_t x = xar_open(path, WRITE); + if (x == NULL) + throwf("could not open output bundle to write %s", path); + // Disable compression + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not disable compression for bitcode bundle"); + + // iterate through the XAR file and obfuscate + for ( auto handler : _handlers ) { + const char* name = NULL; + xar_file_t f = handler->getXARFile(); + if ( xar_prop_get(f, "name", &name) != 0 ) + throwf("could not get the name of the file from bitcode bundle"); + char outputPath[PATH_MAX]; + sprintf(outputPath, "%s/%s", _temp_dir, name); + handler->obfuscateAndWriteToPath(obfuscator, outputPath); + BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles()); + xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize()); + copyXARProp(f, bcEntry); + delete bcOut; + } + + // copy the subdoc as well + for ( xar_subdoc_t sub = xar_subdoc_first(_xar); sub; sub = xar_subdoc_next(sub) ) { + const char *name = xar_subdoc_name(sub); + xar_subdoc_t newDoc = xar_subdoc_new(x, name); + copyXARProp((xar_file_t) sub, (xar_file_t) newDoc); + } + xar_close(x); +} + +void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +{ + initFile(); + ld::Bitcode bc((uint8_t*)_file_buffer, _file_size); + obfuscator->bitcodeHideSymbols(&bc, path, path); +} + +void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +{ + initFile(); + int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if ( f == -1 || ::write(f, _file_buffer, _file_size) != (int)_file_size ) + throwf("failed to write content to temp file: %s", path); + ::close(f); +} + +void BitcodeBundle::doPass() +{ + if ( _state.embedMarkerOnly ) { + assert( _options.outputKind() != Options::kDynamicExecutable && + _options.outputKind() != Options::kStaticExecutable && + "Don't emit marker for executables"); + BitcodeAtom* marker = new BitcodeAtom(); + _state.addAtom(*marker); + return; + } + + if ( _state.filesWithBitcode.empty() && _state.ltoBitcodePath.empty() ) + return; + // Create tempdir, the temp directory should be OUTPUT/main.exe.bundle-XXXXXX + char tempdir[PATH_MAX]; + const char* finalOutput = _options.outputFilePath(); + // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX + // If so, fall back to /tmp + if ( strlen(finalOutput) + 30 >= PATH_MAX ) + sprintf(tempdir, "/tmp/ld.bundle.XXXXXX"); + else + sprintf(tempdir, "%s.bundle.XXXXXX", finalOutput); + ::mkdtemp(tempdir); + // A lookup map to look for BundlerHandler base on filename + std::unordered_map handlerMap; + + BitcodeObfuscator* obfuscator = _options.hideSymbols() ? new BitcodeObfuscator() : NULL; + // Build must keep symbols if we need to hide all the symbols + if ( _options.hideSymbols() ) { + // Go through all the atoms and decide if it should be obfuscated. + // The following symbols are kept: + // 1. entry point + // 2. undefined symbols + // 3. symbols must not be stripped + // 4. all the globals if the globals are dead_strip root (ex. dylibs) + // 5. there is an exported symbol list suggests the symbol should be exported + // 6. the special symbols supplied by linker + for ( auto § : _state.sections ) { + for ( auto &atom : sect->atoms ) { + if ( atom == _state.entryPoint || + atom->definition() == ld::Atom::definitionProxy || + atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || + ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || + ( _options.hasExportRestrictList() && _options.shouldExport(atom->name())) ) + obfuscator->addMustPreserveSymbols(atom->name()); + } + } + // If there are assembly sources, add globals and undefined symbols from them as well + for ( auto &f : _state.filesWithBitcode ) { + if ( ld::AsmBitcode* ab = dynamic_cast(f->getBitcode()) ) { + ObjectHandler objHandler((char*)ab->getContent(), ab->getSize()); + objHandler.populateMustPreserveSymbols(obfuscator); + } else if ( ld::BundleBitcode* bb = dynamic_cast(f->getBitcode()) ) { + BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options); + bh->populateMustPreserveSymbols(obfuscator); + handlerMap.emplace(std::string(f->path()), bh); + } + } + // special symbols supplied by linker + obfuscator->addMustPreserveSymbols("___dso_handle"); + obfuscator->addMustPreserveSymbols("__mh_execute_header"); + obfuscator->addMustPreserveSymbols("__mh_dylib_header"); + obfuscator->addMustPreserveSymbols("__mh_bundle_header"); + obfuscator->addMustPreserveSymbols("__mh_dylinker_header"); + obfuscator->addMustPreserveSymbols("__mh_object_header"); + obfuscator->addMustPreserveSymbols("__mh_preload_header"); + } + + // Open XAR output + xar_t x; + char outFile[PATH_MAX]; + sprintf(outFile, "%s/bundle.xar", tempdir); + + // By default, it uses gzip to compress and SHA1 as checksum + x = xar_open(outFile, WRITE); + if (x == NULL) + throwf("could not open output bundle to write %s", outFile); + // Disable compression + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not disable compression for bitcode bundle"); + + // Sort all the object file according to oridnal order + std::sort(_state.filesWithBitcode.begin(), _state.filesWithBitcode.end(), + [](const ld::relocatable::File* a, const ld::relocatable::File* b) { + return a->ordinal() < b->ordinal(); + }); + + // Copy each bitcode file into archive + int index = 1; + char formatString[10]; + sprintf(formatString, "%%0%ud", (unsigned int)log10(_state.filesWithBitcode.size()) + 1); + for ( auto &obj : _state.filesWithBitcode ) { + assert(obj->getBitcode() != NULL && "File should contain bitcode"); + char outFilePath[16]; + sprintf(outFilePath, formatString, index++); + if ( ld::LLVMBitcode* llvmbc = dynamic_cast(obj->getBitcode()) ) { + // Handle clang and swift bitcode + xar_file_t bcFile = NULL; + if ( _options.hideSymbols() && !llvmbc->isMarker() ) { // dont strip if it is just a marker + char tempfile[PATH_MAX]; + sprintf(tempfile, "%s/%s.bc", tempdir, outFilePath); + obfuscator->bitcodeHideSymbols(llvmbc, obj->path(), tempfile); + BitcodeTempFile* bcTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); + bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bcTemp->getContent(), bcTemp->getSize()); + delete bcTemp; + } else { + bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)const_cast(llvmbc->getContent()), llvmbc->getSize()); + } + if ( bcFile == NULL ) + throwf("could not add bitcode from %s to bitcode bundle", obj->path()); + if ( xar_prop_set(bcFile, "file-type", "Bitcode") != 0 ) + throwf("could not set bitcode property for %s in bitcode bundle", obj->path()); + // Write commandline options + std::string tagName = std::string(llvmbc->getBitcodeName()) + std::string("/cmd"); + for ( uint32_t i = 0; i < llvmbc->getCmdSize(); ++i ) { + if ( i == 0 || llvmbc->getCmdline()[i-1] == '\0' ) { + if ( xar_prop_create(bcFile, tagName.c_str(), (const char *)llvmbc->getCmdline() + i) ) + throwf("could not set cmdline to XAR file"); + } + } + } + else if ( ld::BundleBitcode* bundlebc = dynamic_cast(obj->getBitcode()) ) { + xar_file_t bundleFile = NULL; + if ( _options.hideSymbols() && !bundlebc->isMarker() ) { // dont strip if it is just a marker + char tempfile[PATH_MAX]; + sprintf(tempfile, "%s/%s.xar", tempdir, outFilePath); + auto search = handlerMap.find(std::string(obj->path())); + assert( search != handlerMap.end() && "Cannot find handler"); + search->second->obfuscateAndWriteToPath(obfuscator, tempfile); + BitcodeTempFile* bundleTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); + bundleFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bundleTemp->getContent(), bundleTemp->getSize()); + delete bundleTemp; + } else { + bundleFile = xar_add_frombuffer(x, NULL, outFilePath, + (char*)const_cast(bundlebc->getContent()), + bundlebc->getSize()); + } + if ( bundleFile == NULL ) + throwf("could not add bitcode from the bundle %s to bitcode bundle", obj->path()); + if ( xar_prop_set(bundleFile, "file-type", "Bundle") != 0 ) + throwf("could not set bundle property for %s in bitcode bundle", obj->path()); + } + else if ( ld::AsmBitcode* asmbc = dynamic_cast(obj->getBitcode()) ) { + xar_file_t objFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)asmbc->getContent(), asmbc->getSize()); + if ( objFile == NULL ) + throwf("could not add obj file %s to bitcode bundle", obj->path()); + if ( xar_prop_set(objFile, "file-type", "Object") != 0 ) + throwf("could not set object property for %s in bitcode bundle", obj->path()); + } + else { + assert(false && "Unknown bitcode"); + } + } + + // Write merged LTO bitcode + if ( !_state.ltoBitcodePath.empty() ) { + xar_file_t ltoFile = NULL; + BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles()); + if ( _options.hideSymbols() ) { + ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize()); + char ltoTempFile[PATH_MAX]; + sprintf(ltoTempFile, "%s/lto.bc", tempdir); + obfuscator->bitcodeHideSymbols(<oBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile); + BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles()); + ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize()); + delete ltoStrip; + } else { + ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize()); + } + if ( ltoFile == NULL ) + throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str()); + if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) + throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str()); + delete ltoTemp; + } + + // Common LinkOptions + std::vector linkCmd = _options.writeBitcodeLinkOptions(); + + // support -sectcreate option + for ( auto extraSect = _options.extraSectionsBegin(); extraSect != _options.extraSectionsEnd(); ++ extraSect ) { + std::string sectName = std::string(extraSect->segmentName) + std::string(",") + std::string(extraSect->sectionName); + BitcodeTempFile* sectFile = new BitcodeTempFile(extraSect->path, false); + xar_file_t sectXar = xar_add_frombuffer(x, NULL, sectName.c_str(), (char*)sectFile->getContent(), sectFile->getSize()); + if ( sectXar == NULL ) + throwf("could not encode sectcreate file %s into bitcode bundle", extraSect->path); + if ( xar_prop_set(sectXar, "file-type", "Section") != 0 ) + throwf("could not set bitcode property for %s", sectName.c_str()); + delete sectFile; + linkCmd.push_back("-sectcreate"); + linkCmd.push_back(extraSect->segmentName); + linkCmd.push_back(extraSect->sectionName); + linkCmd.push_back(sectName); + } + + // Write exports file + if ( _options.hasExportMaskList() ) { + linkCmd.push_back("-exported_symbols_list"); + linkCmd.push_back("exports.exp"); + const char* exportsPath = "exports.exp"; + std::vector exports = _options.exportsData(); + std::string exps; + for (std::vector::iterator it = exports.begin(); + it != exports.end(); ++ it) { + exps += *it; + exps += "\n"; + } + // always append an empty line so exps cannot be empty. rdar://problem/22404253 + exps += "\n"; + xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast(exps.data()), exps.size()); + if (exportsFile == NULL) + throwf("could not add exports list to bitcode bundle"); + if (xar_prop_set(exportsFile, "file-type", "Exports") != 0) + throwf("could not set exports property in bitcode bundle"); + } + + // Create subdoc to write link information + xar_subdoc_t linkXML = xar_subdoc_new(x, "Ld"); + if ( linkXML == NULL ) + throwf("could not create XML in bitcode bundle"); + + // Write version number + if ( xar_prop_create((xar_file_t)linkXML, "version", BITCODE_XAR_VERSION) != 0 ) + throwf("could not add version number to bitcode bundle"); + + // Arch + if ( xar_prop_create((xar_file_t)linkXML, "architecture", _options.architectureName()) != 0 ) + throwf("could not add achitecture name to bitcode bundle"); + + // Opt-out symbols + if ( _options.hideSymbols() ) { + if ( xar_prop_create((xar_file_t)linkXML, "hide-symbols", "1") != 0 ) + throwf("could not add property to bitcode bundle"); + } + + // Write SDK version + if ( _options.sdkPaths().size() > 1 ) + throwf("only one -syslibroot is accepted for bitcode bundle"); + if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 ) + throwf("could not add platform name to bitcode bundle"); + if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 ) + throwf("could not add SDK version to bitcode bundle"); + + // Write dylibs + const char* sdkRoot = NULL; + if ( !_options.sdkPaths().empty() ) + sdkRoot = _options.sdkPaths().front(); + if ( !_state.dylibs.empty() ) { + std::vector SDKPaths = _options.sdkPaths(); + char dylibPath[PATH_MAX]; + for ( auto &dylib : _state.dylibs ) { + // For every dylib/framework, figure out if it is coming from a SDK + // if it is coming from some SDK, we parse the path to figure out which SDK + // If -syslibroot is pointing to a SDK, it should end with PlatformX.Y.sdk/ + if (sdkRoot && strncmp(dylib->path(), sdkRoot, strlen(sdkRoot)) == 0) { + // dylib/framework from one of the -syslibroot + // The path start with a string template + strcpy(dylibPath, "{SDKPATH}/"); + // append the path of dylib/frameowrk in the SDK + strcat(dylibPath, dylib->path() + strlen(sdkRoot)); + } else { + // Not in any SDKs, then assume it is a user dylib/framework + // strip off all the path in the front + const char* dylib_name = strrchr(dylib->path(), '/'); + dylib_name = (dylib_name == NULL) ? dylib->path() : dylib_name + 1; + strcpy(dylibPath, dylib_name); + } + if ( dylib->forcedWeakLinked() ) { + if ( xar_prop_create((xar_file_t)linkXML, "dylibs/weak", dylibPath) != 0) + throwf("could not add dylib options to bitcode bundle"); + } else { + if ( xar_prop_create((xar_file_t)linkXML, "dylibs/lib", dylibPath) != 0) + throwf("could not add dylib options to bitcode bundle"); + } + } + } + + // Write link-line into archive + for ( auto &it : linkCmd ) { + if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0) + throwf("could not add link options to bitcode bundle"); + } + // Finish writing + xar_close(x); + + // Read the file back + BitcodeTempFile* xarTemp = new BitcodeTempFile(outFile, !_options.saveTempFiles()); + + // Create an Atom and add to the list + BitcodeAtom* bundleAtom = new BitcodeAtom(*xarTemp); + _state.addAtom(*bundleAtom); + + // write the reverse mapping file if required + if ( _options.hideSymbols() && !_options.reverseMapTempPath().empty() ) + obfuscator->writeSymbolMap(_options.reverseMapTempPath().c_str()); + + // Clean up local variables + delete xarTemp; + delete obfuscator; + for ( auto &entry: handlerMap ) + delete entry.second; + // delete temp directory if not using -save-temps + // only do so after all the BitcodeTempFiles are deleted. + if ( !_options.saveTempFiles() ) { + if ( ::rmdir(tempdir) != 0 ) + warning("temp directory cannot be removed: %s", tempdir); + } +} + + + +// called by linker to write bitcode bundle into a mach-o section +void doPass(const Options& opts, ld::Internal& internal) { + BitcodeBundle BB(opts, internal); + BB.doPass(); +} + + +} // namespace bitcode_bundle +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/bitcode_bundle.h b/ld64/src/ld/passes/bitcode_bundle.h new file mode 100644 index 0000000..c87a59e --- /dev/null +++ b/ld64/src/ld/passes/bitcode_bundle.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +#ifndef _BITCODE_BUNDLE_H_ +#define _BITCODE_BUNDLE_H_ + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace passes { +namespace bitcode_bundle { + +// called by linker to write bitcode bundle into a mach-o section +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace bitcode_bundle +} // namespace passes +} // namespace ld + +#endif /* defined(_BITCODE_BUNDLE_H_) */ diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index a8fb325..f7465d0 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -552,7 +552,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: if ( target->section().type() == ld::Section::typeStub ) dstAddr = totalTextSize; int64_t displacement = dstAddr - srcAddr; - TargetAndOffset finalTargetAndOffset = { target, addend }; + TargetAndOffset finalTargetAndOffset = { target, (uint32_t)addend }; const int64_t kBranchLimit = kBetweenRegions; if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { const ld::Atom* island; @@ -565,6 +565,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: island, island->name(), displacement); ++islandCount; regionsIslands[0]->push_back(island); + state.atomToSection[island] = textSection; } else { island = pos->second; @@ -588,6 +589,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: (*region)[finalTargetAndOffset] = island; if (_s_log) fprintf(stderr, "added forward branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); + state.atomToSection[island] = textSection; ++islandCount; nextTarget = island; } @@ -614,6 +616,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: (*region)[finalTargetAndOffset] = island; if (_s_log) fprintf(stderr, "added back branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); + state.atomToSection[island] = textSection; ++islandCount; prevTarget = island; } diff --git a/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp index 840a391..efc010c 100644 --- a/ld64/src/ld/passes/branch_shim.cpp +++ b/ld64/src/ld/passes/branch_shim.cpp @@ -328,6 +328,7 @@ void doPass(const Options& opts, ld::Internal& state) } shims.push_back(shim); thumbToAtomMap[target] = shim; + state.atomToSection[shim] = sect; } else { shim = pos->second; @@ -361,6 +362,7 @@ void doPass(const Options& opts, ld::Internal& state) shim = new ARMtoThumbShimAtom(target, *sect); shims.push_back(shim); atomToThumbMap[target] = shim; + state.atomToSection[shim] = sect; } else { shim = pos->second; diff --git a/ld64/src/ld/passes/dylibs.cpp b/ld64/src/ld/passes/dylibs.cpp index af1ecf6..f77f5cd 100644 --- a/ld64/src/ld/passes/dylibs.cpp +++ b/ld64/src/ld/passes/dylibs.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "ld.hpp" #include "dylibs.h" diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 66caf34..01c2e30 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -44,8 +44,8 @@ class File; // forward reference class GOTEntryAtom : public ld::Atom { public: - GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool is64) - : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool weakDef, bool is64) + : ld::Atom(weakDef ? _s_sectionWeak : _s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))), _fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target), @@ -68,13 +68,16 @@ class GOTEntryAtom : public ld::Atom { bool _is64; static ld::Section _s_section; + static ld::Section _s_sectionWeak; }; ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); +ld::Section GOTEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer); -static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable) +static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable, bool* targetIsExternalWeakDef) { + *targetIsExternalWeakDef = false; switch (fixup->kind) { case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: #if SUPPORT_ARCH_arm64 @@ -92,14 +95,15 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) { *optimizable = false; } - if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { + if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { // cannot do LEA optimization if target is weak exported symbol - if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) { + if ( ((targetOfGOT->definition() == ld::Atom::definitionRegular) || (targetOfGOT->definition() == ld::Atom::definitionProxy)) && (targetOfGOT->combine() == ld::Atom::combineByName) ) { switch ( opts.outputKind() ) { case Options::kDynamicExecutable: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kKextBundle: + *targetIsExternalWeakDef = true; *optimizable = false; break; case Options::kStaticExecutable: @@ -199,6 +203,7 @@ void doPass(const Options& opts, ld::Internal& internal) // don't create GOT atoms during this loop because that could invalidate the sections iterator std::vector atomsReferencingGOT; std::map weakImportMap; + std::map weakDefMap; atomsReferencingGOT.reserve(128); for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; @@ -223,7 +228,8 @@ void doPass(const Options& opts, ld::Internal& internal) break; } bool optimizable; - if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + bool targetIsExternalWeakDef; + if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) ) continue; if ( optimizable ) { // change from load of GOT entry to lea of target @@ -264,6 +270,8 @@ void doPass(const Options& opts, ld::Internal& internal) } if ( gotMap.count(targetOfGOT) == 0 ) gotMap[targetOfGOT] = NULL; + // record if target is weak def + weakDefMap[targetOfGOT] = targetIsExternalWeakDef; // record weak_import attribute std::map::iterator pos = weakImportMap.find(targetOfGOT); if ( pos == weakImportMap.end() ) { @@ -319,7 +327,7 @@ void doPass(const Options& opts, ld::Internal& internal) // make GOT entries for (auto& entry : gotMap) { if ( entry.second == NULL ) { - entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], is64); + entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], opts.useDataConstSegment() && weakDefMap[entry.first], is64); if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first->name(), entry.first, entry.second); } } @@ -348,7 +356,8 @@ void doPass(const Options& opts, ld::Internal& internal) break; } bool optimizable; - if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + bool targetIsExternalWeakDef; + if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) ) continue; if ( !optimizable ) { // GOT use not optimized away, update to bind to GOT entry diff --git a/ld64/src/ld/passes/huge.cpp b/ld64/src/ld/passes/huge.cpp index 51dda80..693976c 100644 --- a/ld64/src/ld/passes/huge.cpp +++ b/ld64/src/ld/passes/huge.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "ld.hpp" #include "huge.h" @@ -94,6 +95,7 @@ void doPass(const Options& opts, ld::Internal& state) const ld::Atom* atom = *ait; if ( atom->size() > 1024*1024 ) { hugeSection->atoms.push_back(atom); + state.atomToSection[atom] = hugeSection; if (log) fprintf(stderr, "moved to __huge: %s, size=%llu\n", atom->name(), atom->size()); *ait = NULL; // change atom to NULL for later bulk removal movedSome = true; diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index 2bbf54a..ad4673c 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -805,7 +805,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) std::set nlcatListAtoms; for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) { + if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) ) { for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* categoryListElementAtom = *ait; for (unsigned int offset=0; offset < categoryListElementAtom->size(); offset += sizeof(pint_t)) { @@ -865,7 +865,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) } } // record method list section - if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) ) methodListSection = sect; } @@ -885,9 +885,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newClassRO = Class::setInstanceMethodList(state, classAtom, newInstanceMethodListAtom, deadAtoms); // add new method list to final sections methodListSection->atoms.push_back(newInstanceMethodListAtom); + state.atomToSection[newInstanceMethodListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } } // if any category adds class methods, generate new merged method list, and replace @@ -897,9 +899,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newClassRO = Class::setClassMethodList(state, classAtom, newClassMethodListAtom, deadAtoms); // add new method list to final sections methodListSection->atoms.push_back(newClassMethodListAtom); + state.atomToSection[newClassMethodListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } } // if any category adds protocols, generate new merged protocol list, and replace @@ -910,13 +914,16 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newMetaClassRO = Class::setClassProtocolList(state, classAtom, newProtocolListAtom, deadAtoms); // add new protocol list to final sections methodListSection->atoms.push_back(newProtocolListAtom); + state.atomToSection[newProtocolListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } if ( newMetaClassRO != NULL ) { assert(strcmp(newMetaClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newMetaClassRO); + state.atomToSection[newMetaClassRO] = methodListSection; } } // if any category adds properties, generate new merged property list, and replace @@ -926,9 +933,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newClassRO = Class::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms); // add new property list to final sections methodListSection->atoms.push_back(newPropertyListAtom); + state.atomToSection[newPropertyListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } } diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index 139e761..a7d31f3 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -510,8 +510,14 @@ void Layout::buildOrdinalOverrideMap() switch ( atom->section().type() ) { case ld::Section::typeZeroFill: case ld::Section::typeTentativeDefs: - if ( atom->size() <= 512 ) - moveToData.insert(atom); + if ( atom->size() <= 512 ) { + const char* dstSeg; + bool wildCardMatch; + const ld::File* f = atom->file(); + const char* path = (f != NULL) ? f->path() : NULL; + if ( !_options.moveRwSymbol(atom->name(), path, dstSeg, wildCardMatch) ) + moveToData.insert(atom); + } break; default: break; @@ -579,6 +585,10 @@ void Layout::buildOrdinalOverrideMap() break; } } + // update atom-to-section map + for (std::set::iterator it=moveToData.begin(); it != moveToData.end(); ++it) { + _state.atomToSection[*it] = dataSect; + } } } diff --git a/ld64/src/ld/passes/stubs/stub_arm.hpp b/ld64/src/ld/passes/stubs/stub_arm.hpp index 5c8fc42..02ae454 100644 --- a/ld64/src/ld/passes/stubs/stub_arm.hpp +++ b/ld64/src/ld/passes/stubs/stub_arm.hpp @@ -234,8 +234,9 @@ class LazyPointerAtom : public ld::Atom { public: LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, bool stubToGlobalWeakDef, bool stubToResolver, - bool weakImport, bool close) - : ld::Atom(close ? _s_sectionClose : _s_section, ld::Atom::definitionRegular, + bool weakImport, bool close, bool usingDataConst) + : ld::Atom(selectSection(close, stubToGlobalWeakDef, stubToResolver, usingDataConst), + ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), @@ -243,7 +244,7 @@ class LazyPointerAtom : public ld::Atom { _resolverHelper(pass, stubTo, this), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)), - _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { _fixup2.weakImport = weakImport; pass.addAtom(*this); if ( stubToResolver ) pass.addAtom(_resolverHelper); @@ -261,6 +262,17 @@ class LazyPointerAtom : public ld::Atom { virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: + static ld::Section& selectSection(bool close, bool stubToGlobalWeakDef, bool stubToResolver, bool usingDataConst) { + if ( close ) + return _s_sectionClose; + else if ( stubToGlobalWeakDef && usingDataConst ) + return _s_sectionWeak; + else if ( stubToResolver && usingDataConst ) + return _s_sectionResolver; + else + return _s_section; + } + const ld::Atom& _stubTo; StubHelperAtom _helper; ResolverHelperAtom _resolverHelper; @@ -269,10 +281,14 @@ class LazyPointerAtom : public ld::Atom { static ld::Section _s_section; static ld::Section _s_sectionClose; + static ld::Section _s_sectionResolver; + static ld::Section _s_sectionWeak; }; ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); ld::Section LazyPointerAtom::_s_sectionClose("__DATA", "__lazy_symbol", ld::Section::typeLazyPointerClose); +ld::Section LazyPointerAtom::_s_sectionResolver("__DATA_DIRTY", "__la_resolver", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionWeak("__DATA", "__la_weak_ptr", ld::Section::typeLazyPointer); class NonLazyPointerAtom : public ld::Atom { @@ -364,12 +380,12 @@ ld::Section StubPICKextAtom::_s_section("__TEXT", "__stub", ld::Section::typeCod class StubPICAtom : public ld::Atom { public: StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool usingDataConst) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false, usingDataConst), _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), @@ -413,7 +429,7 @@ class StubNoPICAtom : public ld::Atom { ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false, false), _fixup(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } @@ -451,7 +467,7 @@ class StubCloseAtom : public ld::Atom { ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, true), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, true, false), _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMLoad12, &_lazyPointer) { pass.addAtom(*this); } diff --git a/ld64/src/ld/passes/stubs/stub_arm64.hpp b/ld64/src/ld/passes/stubs/stub_arm64.hpp index 63382e5..09de1fa 100644 --- a/ld64/src/ld/passes/stubs/stub_arm64.hpp +++ b/ld64/src/ld/passes/stubs/stub_arm64.hpp @@ -241,8 +241,9 @@ ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::S class LazyPointerAtom : public ld::Atom { public: LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) - : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed) + : ld::Atom(selectSection(stubToGlobalWeakDef, stubToResolver, dataConstUsed), + ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _stubTo(stubTo), @@ -269,6 +270,15 @@ class LazyPointerAtom : public ld::Atom { virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: + static ld::Section& selectSection(bool stubToGlobalWeakDef, bool stubToResolver, bool dataConstUsed) { + if ( stubToGlobalWeakDef && dataConstUsed ) + return _s_sectionWeak; + else if ( stubToResolver && dataConstUsed ) + return _s_sectionResolver; + else + return _s_section; + } + const ld::Atom& _stubTo; StubHelperAtom _helper; ResolverHelperAtom _resolverHelper; @@ -276,20 +286,24 @@ class LazyPointerAtom : public ld::Atom { ld::Fixup _fixup2; static ld::Section _s_section; + static ld::Section _s_sectionResolver; + static ld::Section _s_sectionWeak; }; ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionResolver("__DATA_DIRTY", "__la_resolver", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionWeak("__DATA", "__la_weak_ptr", ld::Section::typeLazyPointer); class StubAtom : public ld::Atom { public: StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, dataConstUsed), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer), _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer), _fixup3(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 0, 4) diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index c01f3f9..bee5f2f 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -164,16 +164,17 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) { - //fprintf(stderr, "makeStub(target=%p %s in sect %s)\n", &target, target.name(), target.section().sectionName()); - bool stubToGlobalWeakDef = ( (target.scope() == ld::Atom::scopeGlobal) - && (target.definition() == ld::Atom::definitionRegular) - && (target.combine() == ld::Atom::combineByName) ); + //fprintf(stderr, "makeStub(target=%p %s in sect %s, def=%d)\n", &target, target.name(), target.section().sectionName(), target.definition()); + bool stubToGlobalWeakDef = ( (target.combine() == ld::Atom::combineByName) && + (((target.definition() == ld::Atom::definitionRegular) && (target.scope() == ld::Atom::scopeGlobal)) + || (target.definition() == ld::Atom::definitionProxy)) ); bool forLazyDylib = false; const ld::dylib::File* dylib = dynamic_cast(target.file()); if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) forLazyDylib = true; bool stubToResolver = (target.contentType() == ld::Atom::typeResolver); + bool usingDataConst = _options.useDataConstSegment(); if ( usingCompressedLINKEDIT() && !forLazyDylib ) { if ( _internal->compressedFastBinderProxy == NULL ) @@ -209,7 +210,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText ) return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); else if ( _pic ) - return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst); else return new ld::passes::stubs::arm::StubNoPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); } @@ -226,7 +227,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) return new ld::passes::stubs::arm64::KextStubAtom(*this, target); else - return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst); break; #endif } diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index e84fc25..aec9562 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/src/ld/passes/tlvp.cpp @@ -48,7 +48,7 @@ class TLVEntryAtom : public ld::Atom { symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), _target(target) - { _fixup.weakImport = weakImport; internal.addAtom(*this); } + { _fixup.weakImport = weakImport; internal.addAtom(*this); } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _target->name(); } @@ -236,7 +236,7 @@ void doPass(const Options& opts, ld::Internal& internal) break; #if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: - it->fixupWithTLVStore->kind = ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21; + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21; break; case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12; @@ -265,6 +265,9 @@ void doPass(const Options& opts, ld::Internal& internal) continue; for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; + if ( ! opts.canUseThreadLocalVariables() ) { + throwf("targeted OS version does not support use of thread local variables in %s", atom->name()); + } for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { if ( fit->offsetInAtom != 0 ) { assert(fit->binding == ld::Fixup::bindingDirectlyBound && "thread variable def contains pointer to global"); diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index ceab63c..dfadf71 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -1244,11 +1244,13 @@ static ld::relocatable::File* createReader(const char* path) objOpts.verboseOptimizationHints = true; objOpts.armUsesZeroCostExceptions = true; objOpts.subType = sPreferredSubArch; + objOpts.srcKind = ld::relocatable::File::kSourceObj; #if 1 if ( ! foundFatSlice ) { cpu_type_t archOfObj; cpu_subtype_t subArchOfObj; - if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj) ) { + Options::Platform platform; + if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj, &platform) ) { objOpts.architecture = archOfObj; objOpts.subType = subArchOfObj; } diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 6ac3311..f19538f 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -104,12 +104,19 @@ class DyldInfoPrinter void printClassicLazyBindingInfo(); void printClassicBindingInfo(); void printSharedRegionInfo(); + const char* sharedRegionKindName(uint8_t kind); void printFunctionStartsInfo(); void printDylibsInfo(); void printDRInfo(); void printDataInCode(); void printFunctionStartLine(uint64_t addr); - const uint8_t* printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind); + const uint8_t* printSharedRegionV1InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end, uint8_t kind); + const uint8_t* printSharedRegionV2InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2InfoForEachULEB128AddressAndAdj(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2SectionPair(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2ToSectionOffset(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2Kind(const uint8_t* p, const uint8_t* end); + pint_t relocBase(); const char* relocTypeName(uint8_t r_type); uint8_t segmentIndexForAddress(pint_t addr); @@ -129,6 +136,7 @@ class DyldInfoPrinter const char* classicOrdinalName(int libraryOrdinal); pint_t* mappedAddressForVMAddress(pint_t vmaddress); const char* symbolNameForAddress(uint64_t); + const char* closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex=0); const char* fPath; @@ -149,8 +157,10 @@ class DyldInfoPrinter const macho_segment_command

* fFirstWritableSegment; bool fWriteableSegmentWithAddrOver4G; std::vector*>fSegments; + std::vector*> fSections; std::vector fDylibs; std::vector*> fDylibLoadCommands; + macho_section

fMachHeaderPseudoSection; }; @@ -286,7 +296,12 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen fPath = strdup(path); fHeader = (const macho_header

*)fileContent; - + + fMachHeaderPseudoSection.set_segname("__TEXT"); + fMachHeaderPseudoSection.set_sectname(""); + fMachHeaderPseudoSection.set_addr(0); + fSections.push_back(&fMachHeaderPseudoSection); + // get LC_DYLD_INFO const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header

) + fHeader->sizeofcmds(); @@ -318,6 +333,10 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen if ( segCmd->vmaddr() > 0x100000000ULL ) fWriteableSegmentWithAddrOver4G = true; } + 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) + fSections.push_back(sect); } break; case LC_LOAD_DYLIB: @@ -704,9 +723,10 @@ void DyldInfoPrinter::printRebaseInfoOpcodes() } else { printf("rebase opcodes:\n"); - const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off(); - const uint8_t* end = &p[fInfo->rebase_size()]; - + const uint8_t* start = (uint8_t*)fHeader + fInfo->rebase_off(); + const uint8_t* end = &start[fInfo->rebase_size()]; + const uint8_t* p = start; + uint8_t type = 0; uint64_t address = fBaseAddress; uint32_t count; @@ -716,44 +736,45 @@ void DyldInfoPrinter::printRebaseInfoOpcodes() while ( !done && (p < end) ) { uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; uint8_t opcode = *p & REBASE_OPCODE_MASK; + uint32_t opcodeOffset = p-start; ++p; switch (opcode) { case REBASE_OPCODE_DONE: done = true; - printf("REBASE_OPCODE_DONE()\n"); + printf("0x%04X REBASE_OPCODE_DONE()\n", opcodeOffset); break; case REBASE_OPCODE_SET_TYPE_IMM: type = immediate; - printf("REBASE_OPCODE_SET_TYPE_IMM(%d)\n", type); + printf("0x%04X REBASE_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type); break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segmentIndex = immediate; address = read_uleb128(p, end); - printf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", segmentIndex, address); + printf("0x%04X REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", opcodeOffset, segmentIndex, address); break; case REBASE_OPCODE_ADD_ADDR_ULEB: address = read_uleb128(p, end); - printf("REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", address); + printf("0x%04X REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", opcodeOffset, address); break; case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: address = immediate*sizeof(pint_t); - printf("REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", address); + printf("0x%04X REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", opcodeOffset, address); break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: - printf("REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", immediate); + printf("0x%04X REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", opcodeOffset, immediate); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: count = read_uleb128(p, end); - printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", count); + printf("0x%04X REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", opcodeOffset, count); break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: skip = read_uleb128(p, end) + sizeof(pint_t); - printf("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", skip); + printf("0x%04X REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", opcodeOffset, skip); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); - printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", count, skip); + printf("0x%04X REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", opcodeOffset, count, skip); break; default: throwf("bad rebase opcode %d", *p); @@ -1485,102 +1506,126 @@ void DyldInfoPrinter::printExportInfoNodes() template -const uint8_t* DyldInfoPrinter::printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind) +const uint8_t* DyldInfoPrinter::printSharedRegionV1InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end, uint8_t kind) { const char* kindStr = "??"; - switch (kind) { - case 1: + switch (kind ) { + case DYLD_CACHE_ADJ_V1_POINTER_32: kindStr = "32-bit pointer"; break; - case 2: + case DYLD_CACHE_ADJ_V1_POINTER_64: kindStr = "64-bit pointer"; break; - case 3: -#if SUPPORT_ARCH_arm64 - if ( fHeader->cputype() == CPU_TYPE_ARM64 ) - kindStr = "arm64 ADRP"; - else -#endif - kindStr = "ppc hi16"; - break; - case 4: - kindStr = "32-bit offset to IMPORT"; - break; - case 5: - kindStr = "thumb2 movw"; - break; - case 6: - kindStr = "ARM movw"; + case DYLD_CACHE_ADJ_V1_ADRP: + kindStr = "arm64 ADRP"; break; - case 0x10: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+0: kindStr = "thumb2 movt low high 4 bits=0"; break; - case 0x11: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+1: kindStr = "thumb2 movt low high 4 bits=1"; break; - case 0x12: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+2: kindStr = "thumb2 movt low high 4 bits=2"; break; - case 0x13: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+3: kindStr = "thumb2 movt low high 4 bits=3"; break; - case 0x14: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+4: kindStr = "thumb2 movt low high 4 bits=4"; break; - case 0x15: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+5: kindStr = "thumb2 movt low high 4 bits=5"; break; - case 0x16: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+6: kindStr = "thumb2 movt low high 4 bits=6"; break; - case 0x17: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+7: kindStr = "thumb2 movt low high 4 bits=7"; break; - case 0x18: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+8: kindStr = "thumb2 movt low high 4 bits=8"; break; - case 0x19: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+9: kindStr = "thumb2 movt low high 4 bits=9"; break; - case 0x1A: - kindStr = "thumb2 movt low high 4 bits=0xA"; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+10: + kindStr = "thumb2 movt low high 4 bits=10"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+11: + kindStr = "thumb2 movt low high 4 bits=11"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+12: + kindStr = "thumb2 movt low high 4 bits=12"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+13: + kindStr = "thumb2 movt low high 4 bits=13"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+14: + kindStr = "thumb2 movt low high 4 bits=14"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+15: + kindStr = "thumb2 movt low high 4 bits=15"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+0: + kindStr = "arm movt low high 4 bits=0"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+1: + kindStr = "arm movt low high 4 bits=1"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+2: + kindStr = "arm movt low high 4 bits=2"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+3: + kindStr = "arm movt low high 4 bits=3"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+4: + kindStr = "arm movt low high 4 bits=4"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+5: + kindStr = "arm movt low high 4 bits=5"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+6: + kindStr = "arm movt low high 4 bits=6"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+7: + kindStr = "arm movt low high 4 bits=7"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+8: + kindStr = "arm movt low high 4 bits=8"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+9: + kindStr = "arm movt low high 4 bits=9"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+10: + kindStr = "arm movt low high 4 bits=10"; break; - case 0x1B: - kindStr = "thumb2 movt low high 4 bits=0xB"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+11: + kindStr = "arm movt low high 4 bits=11"; break; - case 0x1C: - kindStr = "thumb2 movt low high 4 bits=0xC"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+12: + kindStr = "arm movt low high 4 bits=12"; break; - case 0x1D: - kindStr = "thumb2 movt low high 4 bits=0xD"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+13: + kindStr = "arm movt low high 4 bits=13"; break; - case 0x1E: - kindStr = "thumb2 movt low high 4 bits=0xE"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+14: + kindStr = "arm movt low high 4 bits=14"; break; - case 0x1F: - kindStr = "thumb2 movt low high 4 bits=0xF"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+15: + kindStr = "arm movt low high 4 bits=15"; break; + default: + kindStr = "<>"; } uint64_t address = 0; uint64_t delta = 0; - uint32_t shift = 0; - bool more = true; do { - uint8_t byte = *p++; - delta |= ((byte & 0x7F) << shift); - shift += 7; - if ( byte < 0x80 ) { - if ( delta != 0 ) { - address += delta; - printf("0x%0llX %s\n", address+fBaseAddress, kindStr); - delta = 0; - shift = 0; - } - else { - more = false; - } - } - } while (more); + delta = read_uleb128(p, end); + address += delta; + printf("0x%0llX %s\n", address+fBaseAddress, kindStr); + } while (delta); + return p; } @@ -1593,14 +1638,96 @@ void DyldInfoPrinter::printSharedRegionInfo() else { const uint8_t* infoStart = (uint8_t*)fHeader + fSharedRegionInfo->dataoff(); const uint8_t* infoEnd = &infoStart[fSharedRegionInfo->datasize()]; - for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { - uint8_t kind = *p++; - p = this->printSharedRegionInfoForEachULEB128Address(p, kind); + if ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ) { + ++infoStart; + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + const macho_section

* fromSection = fSections[fromSectionIndex]; + const macho_section

* toSection = fSections[toSectionIndex]; + printf("from sect=%s, to sect=%s, count=%lld:\n", fromSection->sectname(), toSection->sectname(), toOffsetCount); + uint64_t toSectionOffset = 0; + const char* lastFromSymbol = NULL; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + uint64_t symbolOffset; + const char* s = closestSymbolNameForAddress(fromSection->addr()+fromSectionOffset, &symbolOffset, fromSectionIndex); + if ( (s != lastFromSymbol) && (s != NULL) ) + printf(" %s:\n", s); + const char* toSymbol = closestSymbolNameForAddress(toSection->addr()+toSectionOffset, &symbolOffset, toSectionIndex); + printf(" from addr=0x%0llX %s to addr=0x%0llX", fromSection->addr()+fromSectionOffset, sharedRegionKindName(kind), toSection->addr()+toSectionOffset); + if ( toSymbol != NULL ) { + if ( symbolOffset == 0 ) + printf(" (%s)", toSymbol); + else + printf(" (%s + %lld)", toSymbol, symbolOffset); + } + printf("\n"); + lastFromSymbol = s; + } + } + } + } + } + else { + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + p = this->printSharedRegionV1InfoForEachULEB128Address(p, infoEnd, kind); + } } + } +} +template +const char* DyldInfoPrinter::sharedRegionKindName(uint8_t kind) +{ + switch (kind) { + default: + return "<>"; + case DYLD_CACHE_ADJ_V2_POINTER_32: + return "pointer32"; + case DYLD_CACHE_ADJ_V2_POINTER_64: + return "pointer64"; + case DYLD_CACHE_ADJ_V2_DELTA_32: + return "delta32"; + case DYLD_CACHE_ADJ_V2_DELTA_64: + return "delta64"; + case DYLD_CACHE_ADJ_V2_ARM64_ADRP: + return "adrp"; + case DYLD_CACHE_ADJ_V2_ARM64_OFF12: + return "off12"; + case DYLD_CACHE_ADJ_V2_ARM64_BR26: + return "br26"; + case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT: + return "movw/movt"; + case DYLD_CACHE_ADJ_V2_ARM_BR24: + return "br24"; + case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT: + return "movw/movt"; + case DYLD_CACHE_ADJ_V2_THUMB_BR22: + return "br22"; + case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: + return "off32"; } } + #if SUPPORT_ARCH_arm_any template <> void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) @@ -1686,7 +1813,6 @@ void DyldInfoPrinter::printDRInfo() const uint8_t* start = ((uint8_t*)fHeader + fDRInfo->dataoff()); //const uint8_t* end = ((uint8_t*)fHeader + fDRInfo->dataoff() + fDRInfo->datasize()); typedef Security::SuperBlob DRListSuperBlob; - typedef Security::SuperBlob InternalRequirementsSetBlob; const DRListSuperBlob* topBlob = (DRListSuperBlob*)start; if ( topBlob->validateBlob(fDRInfo->datasize()) ) { if ( topBlob->count() == fDylibLoadCommands.size() ) { @@ -1951,37 +2077,65 @@ void DyldInfoPrinter::printSymbolTableExportInfo() } template -const char* DyldInfoPrinter::symbolNameForAddress(uint64_t addr) +const char* DyldInfoPrinter::closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex) { + const macho_nlist

* bestSymbol = NULL; 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 closest match in globals + const macho_nlist

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

* const globalsEnd = &globalsStart[fDynamicSymbolTable->nextdefsym()]; + for (const macho_nlist

* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type() & N_TYPE) == N_SECT ) { + if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) { + if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) ) + bestSymbol = s; + } } } - // 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 closest match in locals + const macho_nlist

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

* const localsEnd = &localsStart[fDynamicSymbolTable->nlocalsym()]; + for (const macho_nlist

* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type() & N_TYPE) == N_SECT) && ((s->n_type() & N_STAB) == 0) ) { + if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) { + if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) ) + bestSymbol = s; + } } } } 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()]; + // find closest match in locals + const macho_nlist

* const allStart = &fSymbols[0]; + const macho_nlist

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

* s = allStart; s < allEnd; ++s) { + if ( ((s->n_type() & N_TYPE) == N_SECT) && ((s->n_type() & N_STAB) == 0) ) { + if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) { + if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) ) + bestSymbol = s; + } } } } + if ( bestSymbol != NULL ) { + *offset = addr - bestSymbol->n_value(); + return &fStrings[bestSymbol->n_strx()]; + } + *offset = 0; + return NULL; +} +template +const char* DyldInfoPrinter::symbolNameForAddress(uint64_t addr) +{ + uint64_t offset; + const char* s = closestSymbolNameForAddress(addr, &offset); + if ( (offset == 0) && (s != NULL) ) + return s; return "?"; } - + template void DyldInfoPrinter::printClassicBindingInfo() { @@ -2223,7 +2377,7 @@ static void usage() "\t-opcodes print opcodes used to generate the rebase and binding information\n" "\t-function_starts print table of function start addresses\n" "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n" - "\t-data_in_code print any data-in-code inforamtion\n" + "\t-data_in_code print any data-in-code information\n" ); } diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 545b4a7..f0cbbf1 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -55,7 +55,7 @@ ifeq ($(ARCH),ppc) SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk endif -CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} +CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} -mmacosx-version-min=10.8 CCFLAGS = -Wall ASMFLAGS = VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6 @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos8.0.internal -version Path 2>/dev/null) +IOS_SDK = $(shell xcodebuild -sdk iphoneos.internal -version Path 2>/dev/null) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) @@ -127,16 +127,13 @@ endif ifeq ($(ARCH),arm64) LDFLAGS := -syslibroot $(IOS_SDK) - CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) - #CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) - #CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/clang-loh -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) - CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=9.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=9.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) LD_NEW_LINKEDIT = -ios_version_min 7.0 - OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/otool - #OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool) + OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool) else FILEARCH = $(ARCH) endif diff --git a/ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile b/ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile new file mode 100644 index 0000000..0949507 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2014 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 + +# +# Sanity check -dead_strip does not remove initializers and terminators +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -dead_strip -o main + ${FAIL_IF_BAD_MACHO} main + nm main | grep _aa | ${FAIL_IF_EMPTY} + nm main | grep _aaInfo | ${FAIL_IF_EMPTY} + nm main | grep _bb | ${FAIL_IF_STDIN} + ${LD} -r main.o -o main-r.o + ${CC} ${CCFLAGS} main-r.o -dead_strip -o main-r + nm main-r | grep _aa | ${FAIL_IF_EMPTY} + nm main-r | grep _aaInfo | ${FAIL_IF_EMPTY} + nm main-r | grep _bb | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main-r + +clean: + rm -rf main.o main main-r.o main-r diff --git a/ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c b/ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c new file mode 100644 index 0000000..e038a28 --- /dev/null +++ b/ld64/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c @@ -0,0 +1,29 @@ +#include + +int aa = 10; +int bb = 20; +int cc = 30; + + +int main() +{ + printf("%p %p\n", &aa, &cc); + return 0; +} + + +struct MetaData { + void* addr; + unsigned long size; + const char* name; +}; + + +#define META_DATA(__x) \ + __attribute__((used, section("__DATA,__meta,regular,live_support"))) \ + static struct MetaData __x##Info = { &__x, sizeof(__x), #__x }; + + +META_DATA(aa); +META_DATA(bb); +META_DATA(cc); diff --git a/ld64/unit-tests/test-cases/tlv-basic/get.s b/ld64/unit-tests/test-cases/tlv-basic/get.s index 9255841..b0eb6de 100644 --- a/ld64/unit-tests/test-cases/tlv-basic/get.s +++ b/ld64/unit-tests/test-cases/tlv-basic/get.s @@ -159,4 +159,117 @@ _get_d: #endif + +#if __arm__ + + # _a is global TLV + .tlv + .globl _a +_a: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .long __tlv_bootstrap + .long 0 + .long _b$tlv$init + + # _c is a non-external TLV + .tlv +_c: .long __tlv_bootstrap + .long 0 + .long _c$tlv$init + + # _d is a non-external TLV + .tlv +_d: .long __tlv_bootstrap + .long 0 + .long _d$tlv$init + + + .text + + .globl _get_a + .align 2 + .code 16 + .thumb_func _get_a +_get_a: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(La-(LPC0_0+4)) + movt r0, :upper16:(La-(LPC0_0+4)) +LPC0_0: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .globl _get_b + .align 2 + .code 16 + .thumb_func _get_b +_get_b: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(Lb-(LPC0_1+4)) + movt r0, :upper16:(Lb-(LPC0_1+4)) +LPC0_1: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .globl _get_c + .align 2 + .code 16 + .thumb_func _get_c +_get_c: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(Lc-(LPC0_2+4)) + movt r0, :upper16:(Lc-(LPC0_2+4)) +LPC0_2: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .globl _get_d + .align 2 + .code 16 + .thumb_func _get_d +_get_d: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(Ld-(LPC0_3+4)) + movt r0, :upper16:(Ld-(LPC0_3+4)) +LPC0_3: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .section __DATA,__thread_ptr,thread_local_variable_pointers +La: + .long _a +Lb: + .long _b +Lc: + .long _c +Ld: + .long _d + + +#endif + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/tlv-basic/main.c b/ld64/unit-tests/test-cases/tlv-basic/main.c index fc56a13..b5fd7f6 100644 --- a/ld64/unit-tests/test-cases/tlv-basic/main.c +++ b/ld64/unit-tests/test-cases/tlv-basic/main.c @@ -22,6 +22,8 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include + // work around until compiler supports __thread extern int* get_a(); extern int* get_b(); @@ -30,10 +32,19 @@ extern int* get_d(); int main() { - get_a(); - get_b(); - get_c(); - get_d(); + int* p; + p = get_a(); + printf("&a=%p, a=%d\n", p, *p); + + p = get_b(); + printf("&b=%p, b=%d\n", p, *p); + + p = get_c(); + printf("&c=%p, c=%d\n", p, *p); + + p = get_d(); + printf("&d=%p, e=%d\n", p, *p); + return 0; } From f3180ded031d149fb261f0bab782fbf3229d6ae9 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 14 May 2016 15:12:42 +0100 Subject: [PATCH 20/48] 253.6 --- ld64/src/ld/InputFiles.cpp | 13 +++-- ld64/src/ld/Options.cpp | 39 ++++++++++++- ld64/src/ld/Options.h | 7 +++ ld64/src/ld/Resolver.cpp | 57 ++++++++----------- ld64/src/ld/ld.hpp | 2 +- ld64/src/ld/parsers/lto_file.cpp | 2 + ld64/src/ld/parsers/macho_dylib_file.cpp | 22 +++++-- .../src/ld/parsers/macho_relocatable_file.cpp | 24 +++++--- ld64/src/ld/parsers/macho_relocatable_file.h | 2 + ld64/src/ld/passes/bitcode_bundle.cpp | 42 ++++++++------ ld64/src/other/ObjectDump.cpp | 3 +- 11 files changed, 137 insertions(+), 76 deletions(-) diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 9eadc2e..839c5bc 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -300,12 +300,10 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.subType = _options.subArchitecture(); objOpts.platform = _options.platform(); objOpts.minOSVersion = _options.minOSversion(); - // workaround for strip -S - // when ld -r has single input file, set the srcKind to kSourceSingle so __LLVM segment will be kept - if (_options.outputKind() == Options::kObjectFile && _options.getInputFiles().size() == 1) - objOpts.srcKind = ld::relocatable::File::kSourceSingle; - else - objOpts.srcKind = ld::relocatable::File::kSourceObj; + objOpts.srcKind = ld::relocatable::File::kSourceObj; + objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData; + objOpts.usingBitcode = _options.bundleBitcode(); + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { OSAtomicAdd64(len, &_totalObjectSize); @@ -361,6 +359,9 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib archOpts.objOpts.srcKind = ld::relocatable::File::kSourceCompilerArchive; else archOpts.objOpts.srcKind = ld::relocatable::File::kSourceArchive; + archOpts.objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData; + archOpts.objOpts.usingBitcode = _options.bundleBitcode(); + ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts); if ( archiveResult != NULL ) { OSAtomicAdd64(len, &_totalArchiveSize); diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index a638cb4..8106e93 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -186,9 +186,10 @@ Options::Options(int argc, const char* argv[]) fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), fSharedRegionEncodingV2(false), fUseDataConstSegment(false), fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), - fBundleBitcode(false), fHideSymbols(false), fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), - fIgnoreAutoLink(false), fPlatform(kPlatformUnknown), - fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), + fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), + fIgnoreAutoLink(false), fAllowDeadDups(false), fBitcodeKind(kBitcodeProcess), + fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) @@ -3063,6 +3064,11 @@ void Options::parse(int argc, const char* argv[]) if ( !fBundleBitcode ) warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); } + else if ( strcmp(arg, "-bitcode_verify") == 0 ) { + fVerifyBitcode = true; + if ( !fBundleBitcode ) + warning("-bitcode_verify is ignored without -bitcode_bundle"); + } else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { fReverseMapPath = argv[++i]; if ( fReverseMapPath == NULL ) @@ -3087,6 +3093,24 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(argv[i], "-ignore_auto_link") == 0) { fIgnoreAutoLink = true; } + else if ( strcmp(argv[i], "-allow_dead_duplicates") == 0) { + fAllowDeadDups = true; + } + else if ( strcmp(argv[i], "-bitcode_process_mode") == 0 ) { + const char* bitcode_type = argv[++i]; + if ( bitcode_type == NULL ) + throw "missing argument to -bitcode_process_mode"; + else if ( strcmp(bitcode_type, "strip") == 0 ) + fBitcodeKind = kBitcodeStrip; + else if ( strcmp(bitcode_type, "marker") == 0 ) + fBitcodeKind = kBitcodeMarker; + else if ( strcmp(bitcode_type, "data") == 0 ) + fBitcodeKind = kBitcodeAsData; + else if ( strcmp(bitcode_type, "bitcode") == 0 ) + fBitcodeKind = kBitcodeProcess; + else + throw "unknown argument to -bitcode_process_mode {strip,marker,data,bitcode}"; + } else if ( strcmp(arg, "-rpath") == 0 ) { const char* path = argv[++i]; if ( path == NULL ) @@ -5290,6 +5314,15 @@ void Options::checkIllegalOptionCombinations() if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) ) throw "-segment_order can only used used with -preload output"; + if ( fBitcodeKind != kBitcodeProcess && + fOutputKind != Options::kObjectFile ) { + throw "-bitcode_process_mode can only be used together with -r"; + } + // auto fix up the process type for strip -S. + // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData. + if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess ) + fBitcodeKind = Options::kBitcodeAsData; + // warn if building an embedded iOS dylib for pre-iOS 8 // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest? if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) { diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index bc70e7f..e7ffff2 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -86,6 +86,7 @@ class Options enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; + enum BitcodeMode { kBitcodeProcess, kBitcodeAsData, kBitcodeMarker, kBitcodeStrip }; enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; #if SUPPORT_APPLE_TV enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS, kPlatform_tvOS }; @@ -412,11 +413,14 @@ class Options bool forceLoadSwiftLibs() const { return fForceLoadSwiftLibs; } bool bundleBitcode() const { return fBundleBitcode; } bool hideSymbols() const { return fHideSymbols; } + bool verifyBitcode() const { return fVerifyBitcode; } bool renameReverseSymbolMap() const { return fReverseMapUUIDRename; } const char* reverseSymbolMapPath() const { return fReverseMapPath; } std::string reverseMapTempPath() const { return fReverseMapTempPath; } bool ltoCodegenOnly() const { return fLTOCodegenOnly; } bool ignoreAutoLink() const { return fIgnoreAutoLink; } + bool allowDeadDuplicates() const { return fAllowDeadDups; } + BitcodeMode bitcodeKind() const { return fBitcodeKind; } bool sharedRegionEncodingV2() const { return fSharedRegionEncodingV2; } bool useDataConstSegment() const { return fUseDataConstSegment; } bool hasWeakBitTweaks() const; @@ -709,11 +713,14 @@ class Options bool fUseDataConstSegmentForceOff; bool fBundleBitcode; bool fHideSymbols; + bool fVerifyBitcode; bool fReverseMapUUIDRename; const char* fReverseMapPath; std::string fReverseMapTempPath; bool fLTOCodegenOnly; bool fIgnoreAutoLink; + bool fAllowDeadDups; + BitcodeMode fBitcodeKind; Platform fPlatform; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 094c98d..948906f 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -363,7 +363,6 @@ void Resolver::doFile(const ld::File& file) this->doLinkerOption(*it, file.path()); } } - // Resolve bitcode section in the object file if ( _options.bundleBitcode() ) { if ( objFile->getBitcode() == NULL ) { @@ -383,35 +382,25 @@ void Resolver::doFile(const ld::File& file) "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); break; case Options::kPlatformWatchOS: +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: +#endif throwf("'%s' does not contain bitcode. " "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); break; - #if SUPPORT_APPLE_TV - case Options::kPlatform_tvOS: - warning("URGENT: all bitcode will be dropped because '%s' was built without bitcode. " - "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor. " - "Note: This will be an error in the future.", file.path()); - _internal.filesWithBitcode.clear(); - _internal.dropAllBitcode = true; - break; - #endif } } } else { // contains bitcode, check if it is just a marker if ( objFile->getBitcode()->isMarker() ) { - // if the bitcode is just a marker, - // the executable will be created without bitcode section. - // Otherwise, create a marker. - if ( _options.outputKind() != Options::kDynamicExecutable && - _options.outputKind() != Options::kStaticExecutable ) - _internal.embedMarkerOnly = true; - // Issue a warning if the marker is in the static library and filesWithBitcode is not empty. - // That means there are object files actually compiled with full bitcode but the archive only has marker. - // Don't warn on normal object files because it can be a debug build using archives with full bitcode. - if ( !_internal.filesWithBitcode.empty() && objFile->sourceKind() == ld::relocatable::File::kSourceArchive ) - warning("full bitcode bundle could not be generated because '%s' was built only with bitcode marker. " - "The library must be generated from Xcode archive build with bitcode enabled (Xcode setting ENABLE_BITCODE)", objFile->path()); + // if -bitcode_verify_bundle is used, check if all the object files participate in the linking have full bitcode embedded. + // error on any marker encountered. + if ( _options.verifyBitcode() ) + throwf("bitcode bundle could not be generated because '%s' was built without full bitcode. " + "All object files and libraries for bitcode must be generated from Xcode Archive or Install build", + objFile->path()); + // update the internal state that marker is encountered. + _internal.embedMarkerOnly = true; _internal.filesWithBitcode.clear(); _internal.dropAllBitcode = true; } else if ( !_internal.dropAllBitcode ) @@ -542,8 +531,8 @@ void Resolver::doFile(const ld::File& file) if ( dylibFile != NULL ) { // Check dylib for bitcode, if the library install path is relative path or @rpath, it has to contain bitcode if ( _options.bundleBitcode() ) { - if ( dylibFile->getBitcode() == NULL && - dylibFile->installPath()[0] != '/' ) { + bool isSystemFramework = ( dylibFile->installPath() != NULL ) && ( dylibFile->installPath()[0] == '/' ); + if ( dylibFile->getBitcode() == NULL && !isSystemFramework ) { // Check if the dylib is from toolchain by checking the path char tcLibPath[PATH_MAX]; char ldPath[PATH_MAX]; @@ -574,21 +563,21 @@ void Resolver::doFile(const ld::File& file) "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); break; case Options::kPlatformWatchOS: +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: +#endif throwf("'%s' does not contain bitcode. " "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); break; - #if SUPPORT_APPLE_TV - case Options::kPlatform_tvOS: - warning("URGENT: all bitcode will be dropped because '%s' was built without bitcode. " - "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor. " - "Note: This will be an error in the future.", file.path()); - _internal.filesWithBitcode.clear(); - _internal.dropAllBitcode = true; - break; - #endif } } } + // Error on bitcode marker in non-system frameworks if -bitcode_verify is used + if ( _options.verifyBitcode() && !isSystemFramework && + dylibFile->getBitcode() != NULL && dylibFile->getBitcode()->isMarker() ) + throwf("bitcode bundle could not be generated because '%s' was built without full bitcode. " + "All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build", + dylibFile->path()); } // update which form of ObjC dylibs are being linked @@ -729,7 +718,7 @@ void Resolver::doAtom(const ld::Atom& atom) // tell symbol table about non-static atoms if ( atom.scope() != ld::Atom::scopeTranslationUnit ) { - _symbolTable.add(atom, _options.deadCodeStrip() && _completedInitialObjectFiles); + _symbolTable.add(atom, _options.deadCodeStrip() && (_completedInitialObjectFiles || _options.allowDeadDuplicates())); // add symbol aliases defined on the command line if ( _options.haveCmdLineAliases() ) { diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index c8f272e..e297036 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -202,7 +202,7 @@ namespace relocatable { { public: enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; - enum SourceKind { kSourceUnknown=0, kSourceObj, kSourceLTO, kSourceArchive, kSourceCompilerArchive, kSourceSingle }; + enum SourceKind { kSourceUnknown=0, kSourceObj, kSourceLTO, kSourceArchive, kSourceCompilerArchive }; struct Stab { const class Atom* atom; uint8_t type; diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 26d70be..d81e833 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -315,6 +315,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.platform = options.platform; objOpts.subType = 0; objOpts.srcKind = ld::relocatable::File::kSourceLTO; + objOpts.treateBitcodeAsData = false; + objOpts.usingBitcode = options.bitcodeBundle; // mach-o parsing is done in-memory, but need path for debug notes const char* path = "/tmp/lto.o"; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 0ba962c..573f06c 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -147,7 +147,7 @@ class File : public ld::dylib::File Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, - bool indirectDylib, bool ignoreMismatchPlatform); + bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode); virtual ~File() {} // overrides of ld::File @@ -238,6 +238,7 @@ class File : public ld::dylib::File bool _installPathOverride; bool _indirectDylibsProcessed; bool _appExtensionSafe; + bool _usingBitcode; uint32_t _minVersionInDylib; uint32_t _platformInDylib; std::unique_ptr _bitcode; @@ -260,7 +261,7 @@ template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, - bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform) + bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode) : ld::dylib::File(strdup(pth), mTime, ord), _platform(platform), _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), @@ -271,7 +272,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), - _indirectDylibsProcessed(false), _appExtensionSafe(false), + _indirectDylibsProcessed(false), _appExtensionSafe(false), _usingBitcode(usingBitcode), _minVersionInDylib(0), _platformInDylib(Options::kPlatformUnknown) { const macho_header

* header = (const macho_header

*)fileContent; @@ -437,7 +438,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: // tvOS is a warning temporarily. rdar://problem/21746965 - if (platform == Options::kPlatform_tvOS) + if ( usingBitcode ) + throwf("building for %s simulator, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + else warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " "Note: This will be an error in the future.", Options::platformName(platform), path(), @@ -466,7 +471,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: // tvOS is a warning temporarily. rdar://problem/21746965 - if (platform == Options::kPlatform_tvOS) + if ( _usingBitcode ) + throwf("building for %s, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + else warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " "Note: This will be an error in the future.", Options::platformName(platform), path(), @@ -1000,7 +1009,8 @@ class Parser opts.logAllFiles(), opts.installPath(), indirectDylib, - opts.outputKind() == Options::kPreload); + opts.outputKind() == Options::kPreload, + opts.bundleBitcode()); } }; diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index c023f37..2fa41b9 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -107,7 +107,6 @@ class File : public ld::relocatable::File virtual uint8_t swiftVersion() const { return _swiftVersion; } virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } virtual SourceKind sourceKind() const { return _srcKind; } - virtual void setSourceKind(SourceKind src) { _srcKind = src; } const uint8_t* fileContent() { return _fileContent; } private: @@ -1255,6 +1254,8 @@ class Parser bool _verboseOptimizationHints; bool _armUsesZeroCostExceptions; bool _ignoreMismatchPlatform; + bool _treateBitcodeAsData; + bool _usingBitcode; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1714,8 +1715,11 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // create file object _file = new File(_path, _modTime, _fileContent, _ordinal); - // set input source - _file->setSourceKind(opts.srcKind); + // set sourceKind + _file->_srcKind = opts.srcKind; + // set treatBitcodeAsData + _treateBitcodeAsData = opts.treateBitcodeAsData; + _usingBitcode = opts.usingBitcode; // respond to -t option if ( opts.logAllFiles ) @@ -2108,8 +2112,12 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS break; #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: - // tvOS is a warning temporarily. rdar://problem/21746965 - if (platform == Options::kPlatform_tvOS) + // Error when using bitcocde, warning otherwise. + if (_usingBitcode) + throwf("building for %s%s, but linking in object file built for %s,", + Options::platformName(platform), (simulator ? " simulator" : ""), + Options::platformName(lcPlatform)); + else warning("URGENT: building for %s%s, but linking in object file (%s) built for %s. " "Note: This will be an error in the future.", Options::platformName(platform), (simulator ? " simulator" : ""), path(), @@ -2471,6 +2479,7 @@ void Parser::makeSections() } } if ( strcmp(sect->segname(), "__LLVM") == 0 ) { + // Process bitcode segement if ( strncmp(sect->sectname(), "__bitcode", 9) == 0 ) { bitcodeSect = sect; } else if ( strncmp(sect->sectname(), "__cmdline", 9) == 0 ) { @@ -2482,9 +2491,8 @@ void Parser::makeSections() } else if ( strncmp(sect->sectname(), "__asm", 5) == 0 ) { bitcodeAsm = true; } - // If it is not single input for ld -r, don't count the section - // otherwise, fall through and add it to the sections. - if (_file->sourceKind() != ld::relocatable::File::kSourceSingle) + // If treat the bitcode as data, continue to parse as a normal section. + if ( !_treateBitcodeAsData ) continue; } // ignore empty __OBJC sections diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index 40f02d6..ca1e5cc 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -47,6 +47,8 @@ struct ParserOptions { Options::Platform platform; uint32_t minOSVersion; ld::relocatable::File::SourceKind srcKind; + bool treateBitcodeAsData; + bool usingBitcode; }; extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp index bafad0b..9e1da55 100644 --- a/ld64/src/ld/passes/bitcode_bundle.cpp +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -488,12 +488,19 @@ void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const void BitcodeBundle::doPass() { - if ( _state.embedMarkerOnly ) { - assert( _options.outputKind() != Options::kDynamicExecutable && - _options.outputKind() != Options::kStaticExecutable && - "Don't emit marker for executables"); - BitcodeAtom* marker = new BitcodeAtom(); - _state.addAtom(*marker); + if ( _options.bitcodeKind() == Options::kBitcodeStrip || + _options.bitcodeKind() == Options::kBitcodeAsData ) + // if emit no bitcode or emit bitcode segment as data, no need to generate bundle. + return; + else if ( _state.embedMarkerOnly || _options.bitcodeKind() == Options::kBitcodeMarker ) { + // if the bitcode is just a marker, + // the executable will be created without bitcode section. + // Otherwise, create a marker. + if( _options.outputKind() != Options::kDynamicExecutable && + _options.outputKind() != Options::kStaticExecutable ) { + BitcodeAtom* marker = new BitcodeAtom(); + _state.addAtom(*marker); + } return; } @@ -730,22 +737,23 @@ void BitcodeBundle::doPass() throwf("could not add SDK version to bitcode bundle"); // Write dylibs - const char* sdkRoot = NULL; - if ( !_options.sdkPaths().empty() ) - sdkRoot = _options.sdkPaths().front(); + char sdkRoot[PATH_MAX]; + if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) ) + strcpy(sdkRoot, "/"); if ( !_state.dylibs.empty() ) { - std::vector SDKPaths = _options.sdkPaths(); char dylibPath[PATH_MAX]; for ( auto &dylib : _state.dylibs ) { - // For every dylib/framework, figure out if it is coming from a SDK - // if it is coming from some SDK, we parse the path to figure out which SDK - // If -syslibroot is pointing to a SDK, it should end with PlatformX.Y.sdk/ - if (sdkRoot && strncmp(dylib->path(), sdkRoot, strlen(sdkRoot)) == 0) { - // dylib/framework from one of the -syslibroot + // For every dylib/framework, figure out if it is coming from a SDK. + // The dylib/framework from SDK must begin with '/' and user framework must begin with '@'. + if (dylib->installPath()[0] == '/') { + // Verify the path of the framework is within the SDK. + char dylibRealPath[PATH_MAX]; + if ( realpath(dylib->path(), dylibRealPath) != NULL && strncmp(sdkRoot, dylibRealPath, strlen(sdkRoot)) != 0 ) + warning("%s has install name beginning with \"/\" but it is not from the specified SDK", dylib->path()); // The path start with a string template - strcpy(dylibPath, "{SDKPATH}/"); + strcpy(dylibPath, "{SDKPATH}"); // append the path of dylib/frameowrk in the SDK - strcat(dylibPath, dylib->path() + strlen(sdkRoot)); + strcat(dylibPath, dylib->installPath()); } else { // Not in any SDKs, then assume it is a user dylib/framework // strip off all the path in the front diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index dfadf71..867f313 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -1244,7 +1244,8 @@ static ld::relocatable::File* createReader(const char* path) objOpts.verboseOptimizationHints = true; objOpts.armUsesZeroCostExceptions = true; objOpts.subType = sPreferredSubArch; - objOpts.srcKind = ld::relocatable::File::kSourceObj; + objOpts.treateBitcodeAsData = false; + objOpts.usingBitcode = true; #if 1 if ( ! foundFatSlice ) { cpu_type_t archOfObj; From 03d9477764e9bc84fcb0a0807bc6f6dbf659a1f9 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 14 May 2016 15:17:45 +0100 Subject: [PATCH 21/48] 253.9 --- ld64/src/ld/Options.cpp | 24 ++- ld64/src/ld/parsers/textstub_dylib_file.cpp | 4 + ld64/src/ld/passes/bitcode_bundle.cpp | 209 ++++++++++++++++++-- 3 files changed, 219 insertions(+), 18 deletions(-) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 8106e93..c915f5e 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -1779,6 +1779,12 @@ void Options::parseOrderFile(const char* path, bool cstring) else symbolStart = NULL; } + else if ( strncmp(symbolStart, "arm64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } if ( symbolStart != NULL ) { char* objFileName = NULL; char* colon = strstr(symbolStart, ".o:"); @@ -2360,7 +2366,6 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-order_file") == 0 ) { snapshotFileArgIndex = 1; parseOrderFile(argv[++i], false); - cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file_statistics") == 0 ) { fPrintOrderFileStatistics = true; @@ -2452,7 +2457,6 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbols_list and -exported_symbols_list"; fExportMode = kDontExportSome; loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); - cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbol") == 0 ) { if ( fExportMode == kDontExportSome ) @@ -2465,7 +2469,6 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbol and -exported_symbol"; fExportMode = kDontExportSome; fDontExportSymbols.insert(argv[++i]); - cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { snapshotFileArgIndex = 1; @@ -3061,13 +3064,9 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) { fHideSymbols = true; - if ( !fBundleBitcode ) - warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); } else if ( strcmp(arg, "-bitcode_verify") == 0 ) { fVerifyBitcode = true; - if ( !fBundleBitcode ) - warning("-bitcode_verify is ignored without -bitcode_bundle"); } else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { fReverseMapPath = argv[++i]; @@ -5323,6 +5322,17 @@ void Options::checkIllegalOptionCombinations() if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess ) fBitcodeKind = Options::kBitcodeAsData; + // warn about bitcode option combinations + if ( !fBundleBitcode ) { + if ( fVerifyBitcode ) + warning("-bitcode_verify is ignored without -bitcode_bundle"); + else if ( fHideSymbols ) + warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); + } + if ( fReverseMapPath != NULL && !fHideSymbols ) { + throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols"; + } + // warn if building an embedded iOS dylib for pre-iOS 8 // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest? if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) { diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index cde3344..825cb05 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -313,6 +313,10 @@ class TBDFile { bool parseArchFlowSequence(Token archName) { expectToken("archs"); + // x86_64h fails to link against text based stubs + if ( archName == "x86_64h" ) + archName = "x86_64"; + bool foundArch = false; parseFlowSequence([&](Token name) { if ( name == archName ) diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp index 9e1da55..2ad5a3e 100644 --- a/ld64/src/ld/passes/bitcode_bundle.cpp +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "llvm-c/lto.h" // c header @@ -93,23 +94,32 @@ class BitcodeObfuscator { ~BitcodeObfuscator(); void addMustPreserveSymbols(const char* name); + void addAsmSymbolsToMustPreserve(lto_module_t module); void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath); void writeSymbolMap(const char* outputPath); + const char* lookupHiddenName(const char* symbol); private: typedef void (*lto_codegen_func_t) (lto_code_gen_t); typedef void (*lto_codegen_output_t) (lto_code_gen_t, const char*); + typedef const char* (*lto_codegen_lookup_t) (lto_code_gen_t, const char*); + typedef unsigned int (*lto_module_num_symbols) (lto_module_t); + typedef const char* (*lto_module_symbol_name) (lto_module_t, unsigned int); lto_code_gen_t _obfuscator; lto_codegen_func_t _lto_hide_symbols; lto_codegen_func_t _lto_reset_context; lto_codegen_output_t _lto_write_reverse_map; + lto_codegen_lookup_t _lto_lookup_hidden_name; + lto_module_num_symbols _lto_get_asm_symbol_num; + lto_module_symbol_name _lto_get_asm_symbol_name; }; class FileHandler { // generic handler for files in a bundle public: virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator) { } - virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path) { }; + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path); + virtual const char* compressionMethod() { return XAR_OPT_VAL_NONE; } // no compression by default xar_file_t getXARFile() { return _xar_file; } FileHandler(char* content, size_t size) : @@ -165,7 +175,7 @@ class BitcodeHandler : public FileHandler { ~BitcodeHandler(); - virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override { } // Don't need to preserve symbols + virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; }; @@ -178,11 +188,23 @@ class ObjectHandler : public FileHandler { ~ObjectHandler(); - void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; - void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; + virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; }; +class SymbolListHandler : public FileHandler { +public: + SymbolListHandler(char* content, size_t size) : + FileHandler(content, size) { } + SymbolListHandler(xar_t parent, xar_file_t xar_file) : + FileHandler(parent, xar_file) { } + + ~SymbolListHandler(); + + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; + virtual const char* compressionMethod() override { return XAR_OPT_VAL_GZIP; } +}; + class BitcodeBundle { public: @@ -252,8 +274,12 @@ BitcodeObfuscator::BitcodeObfuscator() _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols"); _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map"); _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context"); + _lto_lookup_hidden_name = (lto_codegen_lookup_t) dlsym(RTLD_DEFAULT, "lto_codegen_lookup_hidden_name"); + _lto_get_asm_symbol_num = (lto_module_num_symbols) dlsym(RTLD_DEFAULT, "lto_module_get_num_asm_symbols"); + _lto_get_asm_symbol_name = (lto_module_symbol_name) dlsym(RTLD_DEFAULT, "lto_module_get_asm_symbol_name"); if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL || - _lto_reset_context == NULL || ::lto_api_version() < 14 ) + _lto_reset_context == NULL || _lto_lookup_hidden_name == NULL || + _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 ) throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); _obfuscator = ::lto_codegen_create_in_local_context(); #if LTO_API_VERSION >= 14 @@ -293,6 +319,18 @@ void BitcodeObfuscator::writeSymbolMap(const char *outputPath) (*_lto_write_reverse_map)(_obfuscator, outputPath); } +const char* BitcodeObfuscator::lookupHiddenName(const char *symbol) +{ + return (*_lto_lookup_hidden_name)(_obfuscator, symbol); +} + +void BitcodeObfuscator::addAsmSymbolsToMustPreserve(lto_module_t module) +{ + for (unsigned int i = 0; i < _lto_get_asm_symbol_num(module); ++ i) { + addMustPreserveSymbols(_lto_get_asm_symbol_name(module, i)); + } +} + BundleHandler::~BundleHandler() { // free buffers @@ -326,6 +364,11 @@ ObjectHandler::~ObjectHandler() destroyFile(); } +SymbolListHandler::~SymbolListHandler() +{ + destroyFile(); +} + void BundleHandler::init() { if ( _xar != NULL ) @@ -369,8 +412,10 @@ void BundleHandler::init() _handlers.push_back(new ObjectHandler(_xar, f)); else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 ) _handlers.push_back(new BitcodeHandler(_xar, f)); + else if ( strcmp(filetype, "Exports") == 0 || strcmp(filetype, "OrderFile") == 0) + _handlers.push_back(new SymbolListHandler(_xar, f)); else - assert(0 && "Unknown file type"); + _handlers.push_back(new FileHandler(_xar, f)); } xar_iter_free(iter); } @@ -421,6 +466,17 @@ void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) handler->populateMustPreserveSymbols(obfuscator); } +void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) +{ + initFile(); + + // init LTOModule and add asm labels + lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size); + obfuscator->addAsmSymbolsToMustPreserve(module); + lto_module_dispose(module); +} + + void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) { initFile(); @@ -456,7 +512,13 @@ void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const sprintf(outputPath, "%s/%s", _temp_dir, name); handler->obfuscateAndWriteToPath(obfuscator, outputPath); BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles()); + if ( xar_opt_set(x, XAR_OPT_COMPRESSION, handler->compressionMethod()) != 0 ) + throwf("could not set compression type for exports list"); xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize()); + if ( bcEntry == NULL ) + throwf("could not add file to the bundle"); + if ( xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0 ) + throwf("could not reset compression type for exports list"); copyXARProp(f, bcEntry); delete bcOut; } @@ -477,7 +539,33 @@ void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, cons obfuscator->bitcodeHideSymbols(&bc, path, path); } -void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +void SymbolListHandler::obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) +{ + initFile(); + // Obfuscate exported symbol list. + std::string exports_list; + for (size_t i = 0, start = 0; i < _file_size; ++i) { + if ( _file_buffer[i] == '\n' ) { + _file_buffer[i] = '\0'; + const char* hiddenName = obfuscator->lookupHiddenName(_file_buffer + start); + if ( hiddenName == NULL ) + exports_list += _file_buffer + start; + else + exports_list += hiddenName; + exports_list += "\n"; + start = i + 1; + } else if ( _file_buffer[i] == '*' ) { + throwf("illegal export list found. Please rebuild your static library using -exported_symbol[s_list] with the newest Xcode"); + } + } + exports_list += "\n"; + int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if ( f == -1 || ::write(f, exports_list.data(), exports_list.size()) != (int)exports_list.size() ) + throwf("failed to write content to temp file: %s", path); + ::close(f); +} + +void FileHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) { initFile(); int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); @@ -536,7 +624,7 @@ void BitcodeBundle::doPass() atom->definition() == ld::Atom::definitionProxy || atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || - ( _options.hasExportRestrictList() && _options.shouldExport(atom->name())) ) + ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) ) obfuscator->addMustPreserveSymbols(atom->name()); } } @@ -549,6 +637,9 @@ void BitcodeBundle::doPass() BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options); bh->populateMustPreserveSymbols(obfuscator); handlerMap.emplace(std::string(f->path()), bh); + } else if ( ld::LLVMBitcode* bitcode = dynamic_cast(f->getBitcode()) ) { + BitcodeHandler* bitcodeHandler = new BitcodeHandler((char*)bitcode->getContent(), bitcode->getSize()); + bitcodeHandler->populateMustPreserveSymbols(obfuscator); } } // special symbols supplied by linker @@ -689,24 +780,120 @@ void BitcodeBundle::doPass() } // Write exports file + // A vector of all the exported symbols. if ( _options.hasExportMaskList() ) { + std::vector exportedSymbols; + for ( auto § : _state.sections ) { + for ( auto &atom : sect->atoms ) { + // The symbols should be added to the export list is the ones that are: + // globalScope, in SymbolTable and should be exported suggested by export file. + if ( atom->scope() == ld::Atom::scopeGlobal && + atom->symbolTableInclusion() == ld::Atom::symbolTableIn && + _options.shouldExport(atom->name()) ) + exportedSymbols.push_back(atom->name()); + } + } linkCmd.push_back("-exported_symbols_list"); linkCmd.push_back("exports.exp"); const char* exportsPath = "exports.exp"; - std::vector exports = _options.exportsData(); std::string exps; - for (std::vector::iterator it = exports.begin(); - it != exports.end(); ++ it) { + for (std::vector::iterator it = exportedSymbols.begin(); + it != exportedSymbols.end(); ++ it) { exps += *it; exps += "\n"; } // always append an empty line so exps cannot be empty. rdar://problem/22404253 exps += "\n"; + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) + throwf("could not set compression type for exports list"); xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast(exps.data()), exps.size()); if (exportsFile == NULL) throwf("could not add exports list to bitcode bundle"); if (xar_prop_set(exportsFile, "file-type", "Exports") != 0) throwf("could not set exports property in bitcode bundle"); + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not reset compression type for exports list"); + } else if ( _options.hasExportRestrictList() ) { + // handle unexported list here + std::vector unexportedSymbols; + for ( auto § : _state.sections ) { + for ( auto &atom : sect->atoms ) { + // The unexported symbols should not include anything that is in TranslationUnit scope (static) or + // that cannot be in the SymbolTable + if ( atom->scope() != ld::Atom::scopeTranslationUnit && + atom->symbolTableInclusion() == ld::Atom::symbolTableIn && + !_options.shouldExport(atom->name()) ) + unexportedSymbols.push_back(atom->name()); + } + } + linkCmd.push_back("-unexported_symbols_list"); + linkCmd.push_back("unexports.exp"); + const char* unexportsPath = "unexports.exp"; + std::string unexps; + for (std::vector::iterator it = unexportedSymbols.begin(); + it != unexportedSymbols.end(); ++ it) { + // try obfuscate the name for symbols in unexported symbols list. They are likely to be obfsucated. + const char* sym_name = NULL; + if ( _options.hideSymbols() ) + sym_name = obfuscator->lookupHiddenName(*it); + if ( sym_name ) + unexps += sym_name; + else + unexps += *it; + unexps += "\n"; + } + unexps += "\n"; + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) + throwf("could not set compression type for exports list"); + xar_file_t unexportsFile = xar_add_frombuffer(x, NULL, unexportsPath, const_cast(unexps.data()), unexps.size()); + if (unexportsFile == NULL) + throwf("could not add unexports list to bitcode bundle"); + if (xar_prop_set(unexportsFile, "file-type", "Exports") != 0) + throwf("could not set exports property in bitcode bundle"); + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not reset compression type for exports list"); + } + + // Handle order file. We need to obfuscate all the entries in the order file + if ( _options.orderedSymbolsCount() > 0 ) { + std::string orderFile; + for ( auto entry = _options.orderedSymbolsBegin(); entry != _options.orderedSymbolsEnd(); ++ entry ) { + std::stringstream line; + if ( entry->objectFileName != NULL ) { + unsigned index = 0; + for ( auto &f : _state.filesWithBitcode ) { + const char* atomFullPath = f->path(); + const char* lastSlash = strrchr(atomFullPath, '/'); + if ( (lastSlash != NULL && strcmp(&lastSlash[1], entry->objectFileName) == 0) || + strcmp(atomFullPath, entry->objectFileName) == 0 ) + break; + ++ index; + } + if ( index >= _state.filesWithBitcode.size() ) + continue; + line << index << ".o:"; + } + const char* sym_name = NULL; + if ( _options.hideSymbols() ) + sym_name = obfuscator->lookupHiddenName(entry->symbolName); + if ( sym_name ) + line << sym_name; + else + line << entry->symbolName; + line << "\n"; + orderFile += line.str(); + } + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) + throwf("could not set compression type for order file"); + xar_file_t ordersFile = xar_add_frombuffer(x, NULL, "file.order", const_cast(orderFile.data()), orderFile.size()); + if (ordersFile == NULL) + throwf("could not add order file to bitcode bundle"); + if (xar_prop_set(ordersFile, "file-type", "OrderFile") != 0) + throwf("could not set order file property in bitcode bundle"); + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not reset compression type for order file"); + linkCmd.push_back("-order_file"); + linkCmd.push_back("file.order"); } // Create subdoc to write link information From 52706e6d254279dc5bd6f9e8c20530cf42f6ea88 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 15 Sep 2016 15:12:10 +0100 Subject: [PATCH 22/48] 264.3.102 --- ld64/doc/man/man1/ld.1 | 17 +- ld64/ld64.xcodeproj/project.pbxproj | 117 ++- ld64/src/abstraction/MachOTrie.hpp | 4 +- ld64/src/ld/InputFiles.cpp | 44 +- ld64/src/ld/InputFiles.h | 9 +- ld64/src/ld/LinkEditClassic.hpp | 19 +- ld64/src/ld/Options.cpp | 535 +++++++---- ld64/src/ld/Options.h | 28 +- ld64/src/ld/OutputFile.cpp | 42 +- ld64/src/ld/OutputFile.h | 2 +- ld64/src/ld/Resolver.cpp | 12 +- ld64/src/ld/SymbolTable.cpp | 12 + ld64/src/ld/SymbolTable.h | 1 + ld64/src/ld/ld.cpp | 17 +- ld64/src/ld/ld.hpp | 16 +- ld64/src/ld/parsers/generic_dylib_file.hpp | 565 +++++++++++ ld64/src/ld/parsers/lto_file.cpp | 10 +- ld64/src/ld/parsers/lto_file.h | 3 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 882 +++++------------- ld64/src/ld/parsers/macho_dylib_file.h | 6 +- .../src/ld/parsers/macho_relocatable_file.cpp | 290 +++--- ld64/src/ld/parsers/macho_relocatable_file.h | 1 + ld64/src/ld/parsers/textstub_dylib_file.cpp | 664 ++++--------- ld64/src/ld/passes/bitcode_bundle.cpp | 34 +- ld64/src/ld/passes/branch_island.cpp | 14 +- ld64/src/ld/passes/branch_shim.cpp | 2 +- ld64/src/ld/passes/code_dedup.cpp | 375 ++++++++ ld64/src/ld/passes/code_dedup.h | 45 + ld64/src/ld/passes/compact_unwind.cpp | 38 +- ld64/src/ld/passes/objc.cpp | 17 +- ld64/src/ld/passes/stubs/stubs.cpp | 10 +- ld64/src/other/machochecker.cpp | 435 +++++---- ld64/src/other/unwinddump.cpp | 2 +- ld64/unit-tests/run-all-unit-tests | 2 - .../AdrpLdrGotLdrField.s | 53 ++ .../linker-optimization-hints/Makefile | 366 +++++++- .../linker_options-library-chain/Makefile | 47 + .../linker_options-library-chain/bar.c | 1 + .../linker_options-library-chain/foo.c | 8 + .../linker_options-library-chain/main.c | 8 + .../linker_options-library-chain/subbar.c | 1 + .../objc-category-optimize-load/Makefile | 2 +- .../objc-category-optimize/Makefile | 8 +- 43 files changed, 2959 insertions(+), 1805 deletions(-) create mode 100644 ld64/src/ld/parsers/generic_dylib_file.hpp create mode 100644 ld64/src/ld/passes/code_dedup.cpp create mode 100644 ld64/src/ld/passes/code_dedup.h create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s create mode 100644 ld64/unit-tests/test-cases/linker_options-library-chain/Makefile create mode 100644 ld64/unit-tests/test-cases/linker_options-library-chain/bar.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library-chain/foo.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library-chain/main.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library-chain/subbar.c diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index 1c118b8..53fbaa5 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -362,7 +362,7 @@ The argument .Ar size is a hexadecimal number with an optional leading 0x. The .Ar size -should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. +should be a multiple of the architecture's page size (4KB or 16KB). .It Fl allow_stack_execute Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. .It Fl export_dynamic @@ -541,10 +541,21 @@ Otherwise, the reverse map will be written to a file at .Bl -tag .It Fl v Prints the version of the linker. +.It Fl no_deduplicate +Don't run deduplication pass in linker +.It Fl verbose_deduplicate +Prints names of functions that are eliminated by deduplication and total code savings size. .It Fl dirty_data_list Ar filename Specifies a file containing the names of data symbols likely to be dirtied. If the linker is creating a __DATA_DIRTY segment, those symbols will be moved to that segment. +.It Fl max_default_common_align Ar value +Any common symbols (aka tentative definitions, or uninitialized (zeroed) variables) that have no explicit alignment +are normally aligned to their next power of two size (e.g. a 240 byte array is 256 aligned). +This option lets you reduce the max alignment. For instance, a value of 0x40 would reduce +the alignment for a 240 byte array to 64 bytes (instead of 256). The value specified must be a hexadecimal power of two +If -max_default_common_align is not used, the default alignment is already +limited to 0x8 (2^3) bytes for -preload and 0x8000 (2^15) for all other output types. .It Fl move_to_rw_segment Ar segment_name Ar filename Moves data symbols to another segment. The command line option specifies the target segment name and a path to a file containing a list of symbols to move. @@ -760,6 +771,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 lto_library Ar path +When performing Link Time Optimization (LTO), the linker normally loads libLTO.dylib relative to the linker +binary (../lib/libLTO.dylib). This option allows the user to specify the path to a specific libLTO.dylib +to load instead. .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 diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index 1c5e622..c47b806 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -28,7 +28,6 @@ isa = PBXAggregateTarget; buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */; buildPhases = ( - F9871A3413340B4600DB3F24 /* Platform install */, ); dependencies = ( F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */, @@ -36,6 +35,7 @@ F9C12EEA0ED65765005BC69D /* PBXTargetDependency */, F9B8135D0EC2620E00F94C13 /* PBXTargetDependency */, F9A3DE160ED76D9A00C590B9 /* PBXTargetDependency */, + F9FF3BDD1C586D7C0015D843 /* PBXTargetDependency */, ); name = all; productName = all; @@ -83,6 +83,7 @@ F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; + F9FC510A1BC893C400FEC3F8 /* code_dedup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9FC51081BC8915A00FEC3F8 /* code_dedup.cpp */; }; FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */; }; /* End PBXBuildFile section */ @@ -183,13 +184,20 @@ remoteGlobalIDString = F9BA51600ECE58BE00D1D62E; remoteInfo = dyldinfo; }; + F9FF3BDC1C586D7C0015D843 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9EA72CA097454A6008B4F1D; + remoteInfo = machocheck; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ F97F5025070D0B6300B9FCD7 /* copy man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man1; + dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1"; dstSubfolderSpec = 0; files = ( F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */, @@ -200,7 +208,7 @@ F9A3DE140ED76D7700C590B9 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "/usr/local/include/mach-o"; + dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */, @@ -210,7 +218,7 @@ F9B1A25E0A3A44CB00DA8FAB /* install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man1; + dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1"; dstSubfolderSpec = 0; files = ( F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */, @@ -221,7 +229,7 @@ F9B813870EC2659600F94C13 /* install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = usr/share/man/man1; + dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1"; dstSubfolderSpec = 0; files = ( F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */, @@ -232,7 +240,7 @@ F9C12EA50ED63E05005BC69D /* install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = usr/share/man/man1; + dstPath = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/share/man/man1"; dstSubfolderSpec = 0; files = ( F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */, @@ -338,6 +346,9 @@ F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9FC51081BC8915A00FEC3F8 /* code_dedup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = code_dedup.cpp; sourceTree = ""; }; + F9FC51091BC8915A00FEC3F8 /* code_dedup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code_dedup.h; sourceTree = ""; }; + FA4843BE1B7279ED001C8025 /* generic_dylib_file.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = generic_dylib_file.hpp; sourceTree = ""; }; FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = textstub_dylib_file.cpp; sourceTree = ""; usesTabs = 1; }; FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = textstub_dylib_file.hpp; sourceTree = ""; }; /* End PBXFileReference section */ @@ -428,6 +439,8 @@ F9AA650B1051BD2B003E3539 /* passes */ = { isa = PBXGroup; children = ( + F9FC51081BC8915A00FEC3F8 /* code_dedup.cpp */, + F9FC51091BC8915A00FEC3F8 /* code_dedup.h */, B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */, B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */, F984A38010BB4B0D009E9878 /* branch_island.cpp */, @@ -475,8 +488,6 @@ F9AA65861051E750003E3539 /* parsers */ = { isa = PBXGroup; children = ( - FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */, - FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */, F91B7B0218987D5F0099486F /* libunwind */, F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, F9AA6785105700C2003E3539 /* opaque_section_file.h */, @@ -484,8 +495,11 @@ F9AA65D81051EC4A003E3539 /* archive_file.h */, F9AA65D91051EC4A003E3539 /* lto_file.cpp */, F9AA65DA1051EC4A003E3539 /* lto_file.h */, + FA4843BE1B7279ED001C8025 /* generic_dylib_file.hpp */, F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */, F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */, + FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */, + FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */, F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */, F9AA65881051E750003E3539 /* macho_relocatable_file.h */, ); @@ -772,21 +786,6 @@ shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; showEnvVarsInLog = 0; }; - F9871A3413340B4600DB3F24 /* Platform install */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "Platform install"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n"; - showEnvVarsInLog = 0; - }; F9CCF765144CE244007CB524 /* make configure.h */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -906,6 +905,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F9FC510A1BC893C400FEC3F8 /* code_dedup.cpp in Sources */, FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */, F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, @@ -1045,6 +1045,11 @@ target = F9BA51600ECE58BE00D1D62E /* dyldinfo */; targetProxy = F9F9AD67116D58AF0028EFAB /* PBXContainerItemProxy */; }; + F9FF3BDD1C586D7C0015D843 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9EA72CA097454A6008B4F1D /* machocheck */; + targetProxy = F9FF3BDC1C586D7C0015D843 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1060,9 +1065,11 @@ DEBUG_INFORMATION_FORMAT = dwarf; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = DEBUG; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -1092,7 +1099,7 @@ "$(DEVELOPER_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/include", ); - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; @@ -1110,6 +1117,7 @@ ); PREBINDING = NO; PRODUCT_NAME = ld; + SDKROOT = macosx.internal; SECTORDER_FLAGS = ""; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; @@ -1128,6 +1136,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = ( "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", @@ -1135,6 +1144,7 @@ "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", ); GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'"; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -1164,7 +1174,7 @@ "$(DEVELOPER_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/include", ); - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", @@ -1179,6 +1189,7 @@ ); PREBINDING = NO; PRODUCT_NAME = ld; + SDKROOT = macosx.internal; SECTORDER_FLAGS = ""; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; @@ -1212,7 +1223,7 @@ "$(DEVELOPER_DIR)/usr/local/include", "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", @@ -1225,6 +1236,7 @@ OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; + SDKROOT = macosx.internal; SECTORDER_FLAGS = ""; WARNING_CFLAGS = ( "-Wmost", @@ -1250,7 +1262,7 @@ "$(DEVELOPER_DIR)/usr/local/include", "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1262,6 +1274,7 @@ OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; + SDKROOT = macosx.internal; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -1336,12 +1349,14 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = ( "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", ); GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'"; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -1371,7 +1386,7 @@ "$(DEVELOPER_DIR)/usr/local/include", "$(DEVELOPER_DIR)/usr/include", ); - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", @@ -1386,6 +1401,7 @@ ); PREBINDING = NO; PRODUCT_NAME = ld; + SDKROOT = macosx.internal; SECTORDER_FLAGS = ""; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; @@ -1405,10 +1421,11 @@ GCC_MODEL_TUNING = G5; GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; PREBINDING = NO; PRODUCT_NAME = rebase; + SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; VALID_ARCHS = "i386 ppc x86_64"; @@ -1426,7 +1443,7 @@ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1437,6 +1454,7 @@ ); PREBINDING = NO; PRODUCT_NAME = unwinddump; + SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; }; @@ -1459,7 +1477,7 @@ "$(DEVELOPER_DIR)/usr/local/include", "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1471,6 +1489,7 @@ OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = ObjectDump; + SDKROOT = macosx.internal; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -1488,7 +1507,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1496,6 +1515,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = machocheck; + SDKROOT = macosx.internal; }; name = "Release-assert"; }; @@ -1508,7 +1528,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1516,6 +1536,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = dyldinfo; + SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; ZERO_LINK = NO; @@ -1532,7 +1553,7 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - INSTALL_PATH = /usr/local/lib; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1562,7 +1583,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - INSTALL_PATH = /usr/local/lib; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1582,7 +1603,7 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - INSTALL_PATH = /usr/local/lib; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/lib"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1625,7 +1646,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1633,6 +1654,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = unwinddump; + SDKROOT = macosx.internal; }; name = Debug; }; @@ -1647,7 +1669,7 @@ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1655,6 +1677,7 @@ OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; PREBINDING = NO; PRODUCT_NAME = unwinddump; + SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; }; @@ -1680,7 +1703,7 @@ GCC_WARN_UNUSED_LABEL = NO; GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INSTALL_PATH = /usr/local/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1688,6 +1711,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = dyldinfo; + SDKROOT = macosx.internal; WARNING_CFLAGS = "-Wall"; }; name = Debug; @@ -1701,7 +1725,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1709,6 +1733,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = dyldinfo; + SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; ZERO_LINK = NO; @@ -1724,7 +1749,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1732,6 +1757,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = machocheck; + SDKROOT = macosx.internal; }; name = Debug; }; @@ -1744,7 +1770,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1752,6 +1778,7 @@ OTHER_LDFLAGS = "-stdlib=libc++"; PREBINDING = NO; PRODUCT_NAME = machocheck; + SDKROOT = macosx.internal; }; name = Release; }; @@ -1765,9 +1792,10 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = "$(HOME)/bin"; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; PREBINDING = NO; PRODUCT_NAME = rebase; + SDKROOT = macosx.internal; }; name = Debug; }; @@ -1781,10 +1809,11 @@ GCC_MODEL_TUNING = G5; GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; HEADER_SEARCH_PATHS = ""; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; PREBINDING = NO; PRODUCT_NAME = rebase; + SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; VALID_ARCHS = "i386 ppc x86_64"; diff --git a/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp index b7c55c1..575a654 100644 --- a/ld64/src/abstraction/MachOTrie.hpp +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -351,6 +351,8 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* } output.push_back(e); } + if ( children > end ) + throw "malformed trie, terminalSize extends beyond trie data"; const uint8_t childrenCount = *children++; const uint8_t* s = children; for (uint8_t i=0; i < childrenCount; ++i) { @@ -383,7 +385,7 @@ inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector::iterator it=entries.begin(); it != entries.end(); ++it) output.push_back(it->entry); - delete cummulativeString; + delete [] cummulativeString; } diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 839c5bc..ab31d4a 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -303,6 +303,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.srcKind = ld::relocatable::File::kSourceObj; objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData; objOpts.usingBitcode = _options.bundleBitcode(); + objOpts.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign(); ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { @@ -318,7 +319,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib OSAtomicIncrement32(&_totalObjectLoaded); return objResult; } - + // see if it is a dynamic library (or text-based dynamic library) ld::dylib::File* dylibResult; bool dylibsNotAllowed = false; @@ -582,12 +583,37 @@ void InputFiles::markExplicitlyLinkedDylibs() } } -bool InputFiles::libraryAlreadyLoaded(const char* path) +bool InputFiles::frameworkAlreadyLoaded(const char* path, const char* frameworkName) +{ + for (ld::File* file : _inputFiles) { + if ( strcmp(path, file->path()) == 0 ) + return true; + } + for (ld::dylib::File* dylibx : _allDylibs) { + const char* fname = dylibx->frameworkName(); + if ( fname == NULL ) + continue; + if ( strcmp(frameworkName, fname) == 0 ) + return true; + } + return false; +} + +bool InputFiles::libraryAlreadyLoaded(const char* path) { - for (std::vector::const_iterator it = _inputFiles.begin(); it != _inputFiles.end(); ++it) { - if ( strcmp(path, (*it)->path()) == 0 ) + for (ld::File* file : _inputFiles) { + if ( strcmp(path, file->path()) == 0 ) + return true; + } + for (ld::dylib::File* dylib : _allDylibs) { + if ( strcmp(path, dylib->path()) == 0 ) return true; } + for (const LibraryInfo& libInfo : _searchLibraries) { + if ( strcmp(path, libInfo.archive()->path()) == 0 ) + return true; + } + return false; } @@ -600,8 +626,10 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan // process frameworks specified in .o linker options for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) { const char* frameworkName = *it; + if ( state.linkerOptionFrameworksProcessed.count(frameworkName) ) + continue; Options::FileInfo info = _options.findFramework(frameworkName); - if ( ! this->libraryAlreadyLoaded(info.path) ) { + if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) { info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); try { ld::File* reader = this->makeFile(info, true); @@ -621,10 +649,13 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan warning("Auto-Linking supplied '%s', %s", info.path, msg); } } + state.linkerOptionFrameworksProcessed.insert(frameworkName); } // process libraries specified in .o linker options for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) { const char* libName = *it; + if ( state.linkerOptionLibrariesProcessed.count(libName) ) + continue; Options::FileInfo info = _options.findLibrary(libName); if ( ! this->libraryAlreadyLoaded(info.path) ) { info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); @@ -656,6 +687,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan warning("Auto-Linking supplied '%s', %s", info.path, msg); } } + state.linkerOptionLibrariesProcessed.insert(libName); } } @@ -1407,7 +1439,7 @@ void InputFiles::dylibs(ld::Internal& state) //fprintf(stderr, "all dylibs:\n"); //for(std::vector::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it) { // const ld::dylib::File* dylib = *it; - // fprintf(stderr, " %p %s\n", dylib, dylib->path()); + // fprintf(stderr, " %p impl=%d %s\n", dylib, dylib->implicitlyLinked(), dylib->path()); //} // and -bundle_loader diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index 608ce39..e9927cd 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -97,7 +97,8 @@ class InputFiles : public ld::dylib::File::DylibHandler void checkDylibClientRestrictions(ld::dylib::File*); void createOpaqueFileSections(); bool libraryAlreadyLoaded(const char* path); - + bool frameworkAlreadyLoaded(const char* path, const char* frameworkName); + // for pipelined linking void waitForInputFiles(); static void waitForInputFiles(InputFiles *inputFiles); @@ -144,9 +145,9 @@ class InputFiles : public ld::dylib::File::DylibHandler LibraryInfo(ld::dylib::File* dylib) : _lib(dylib), _isDylib(true) {}; LibraryInfo(ld::archive::File* dylib) : _lib(dylib), _isDylib(false) {}; - bool isDylib() { return _isDylib; } - ld::dylib::File *dylib() { return (ld::dylib::File*)_lib; } - ld::archive::File *archive() { return (ld::archive::File*)_lib; } + bool isDylib() const { return _isDylib; } + ld::dylib::File *dylib() const { return (ld::dylib::File*)_lib; } + ld::archive::File *archive() const { return (ld::archive::File*)_lib; } }; std::vector _searchLibraries; }; diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index ee1c207..2eab13b 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -2002,7 +2002,6 @@ class IndirectSymbolTableAtom : public ClassicLinkEditAtom uint32_t symIndexOfLazyPointerAtom(const ld::Atom*); uint32_t symIndexOfNonLazyPointerAtom(const ld::Atom*); uint32_t symbolIndex(const ld::Atom*); - bool kextBundlesDontHaveIndirectSymbolTable(); std::vector _entries; @@ -2034,9 +2033,11 @@ uint32_t IndirectSymbolTableAtom::symIndexOfStubAtom(const ld::Atom* stubAtom { for (ld::Fixup::iterator fit = stubAtom->fixupsBegin(); fit != stubAtom->fixupsEnd(); ++fit) { if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { - assert((fit->u.target->contentType() == ld::Atom::typeLazyPointer) - || (fit->u.target->contentType() == ld::Atom::typeLazyDylibPointer)); - return symIndexOfLazyPointerAtom(fit->u.target); + ld::Atom::ContentType type = fit->u.target->contentType(); + if (( type == ld::Atom::typeLazyPointer) || (type == ld::Atom::typeLazyDylibPointer) ) + return symIndexOfLazyPointerAtom(fit->u.target); + if ( type == ld::Atom::typeNonLazyPointer ) + return symIndexOfNonLazyPointerAtom(fit->u.target); } } throw "internal error: stub missing fixup to lazy pointer"; @@ -2152,12 +2153,6 @@ void IndirectSymbolTableAtom::encodeNonLazyPointerSection(ld::Internal::Final } } -template -bool IndirectSymbolTableAtom::kextBundlesDontHaveIndirectSymbolTable() -{ - return true; -} - template void IndirectSymbolTableAtom::encode() { @@ -2165,8 +2160,8 @@ void IndirectSymbolTableAtom::encode() if ( (this->_options.outputKind() == Options::kStaticExecutable) && !_options.positionIndependentExecutable() ) return; - // x86_64 kext bundles should not have an indirect symbol table - if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() ) + // x86_64 kext bundles should not have an indirect symbol table unless using stubs + if ( (this->_options.outputKind() == Options::kKextBundle) && !this->_options.kextsUseStubs() ) return; // slidable static executables (-static -pie) should not have an indirect symbol table diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index c915f5e..0f5985d 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -157,7 +157,7 @@ Options::Options(int argc, const char* argv[]) fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), - fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), + fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), fUseSimplifiedDylibReExports(false), fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), @@ -187,12 +187,14 @@ Options::Options(int argc, const char* argv[]) fSharedRegionEncodingV2(false), fUseDataConstSegment(false), fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), - fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), + fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), + fReverseMapPath(NULL), fLTOCodegenOnly(false), fIgnoreAutoLink(false), fAllowDeadDups(false), fBitcodeKind(kBitcodeProcess), fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), - fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0), fFilePreference(kModTime), + fForceTextBasedStub(false) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -712,9 +714,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; - if ( checkForFile("%s/lib%s.tbd", dir, rootName, result) ) - return result; - if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( findFile(path, {".tbd"}, result) ) return result; } for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); @@ -743,9 +744,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; - if ( lookForDylibs && checkForFile("%s/lib%s.tbd", dir, rootName, result) ) - return result; - if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( lookForDylibs && findFile(path, {".tbd"}, result) ) return result; if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) return result; @@ -785,15 +785,8 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi possiblePath = std::string(realPath).append(suffix); } FileInfo result; - bool found = result.checkFileExists(*this, (possiblePath + ".tbd").c_str()); - if ( !found ) - found = result.checkFileExists(*this, possiblePath.c_str()); - if ( fTraceDylibSearching ) - printf("[Logging for XBS]%sfound framework: '%s'\n", - (found ? " " : " not "), possiblePath.c_str()); - if ( found ) { + if ( findFile(possiblePath, {".tbd"}, result) ) return result; - } } // try without suffix if ( suffix != NULL ) @@ -802,41 +795,88 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } +static std::string replace_extension(const std::string &path, const std::string &ext) +{ + auto result = path; + auto lastSlashIdx = result.find_last_of('/'); + auto lastDotIdx = result.find_last_of('.'); + if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) + result.erase(lastDotIdx, std::string::npos); + if ( ext.size() > 0 && ext[0] == '.' ) + result.append(ext); + else + result.append('.' + ext); + return result; +} + +bool Options::findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const +{ + FileInfo tbdInfo; + for ( const auto &ext : tbdExtensions ) { + auto newPath = replace_extension(path, ext); + bool found = tbdInfo.checkFileExists(*this, newPath.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str()); + if ( found ) { + if ( (fFilePreference == kTextBasedStub) || fForceTextBasedStub ) { + result = tbdInfo; + return true; + } else { + break; + } + } + } + + FileInfo dylibInfo; + { + bool found = dylibInfo.checkFileExists(*this, path.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str()); + if ( found && (fFilePreference == kMachO) ) { + result = dylibInfo; + return true; + } + } + + if ( !dylibInfo.missing() && tbdInfo.missing() ) { + result = dylibInfo; + return true; + } + else if ( dylibInfo.missing() && !tbdInfo.missing() ) { + result = tbdInfo; + return true; + } + else if ( !dylibInfo.missing() && !tbdInfo.missing() ) { + if ( dylibInfo.modTime == tbdInfo.modTime ) { + result = tbdInfo; + return true; + } + else { + // Disable false warning about out-of-sync .tbd files. + //warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); + result = dylibInfo; + return true; + } + } + + return false; +} + Options::FileInfo Options::findFile(const std::string &path) const { FileInfo result; // if absolute path and not a .o file, then use SDK prefix if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) { - auto tbdFile = path; - auto lastSlashIdx = tbdFile.find_last_of('/'); - auto lastDotIdx = tbdFile.find_last_of('.'); - if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) - tbdFile.erase(lastDotIdx, std::string::npos); - tbdFile.append(".tbd"); - for (const auto* sdkPathDir : fSDKPaths) { - auto possiblePath = std::string(sdkPathDir) + tbdFile; - if ( result.checkFileExists(*this, possiblePath.c_str()) ) - return result; - possiblePath = std::string(sdkPathDir) + path; - if ( result.checkFileExists(*this, possiblePath.c_str()) ) + auto possiblePath = std::string(sdkPathDir) + path; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } // try raw path - { - std::string file = path; - auto lastDotIdx = file.find_last_of('.'); - if (lastDotIdx != std::string::npos) - file.erase(lastDotIdx, std::string::npos); - if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) - return result; - } - if ( result.checkFileExists(*this, path.c_str()) ) { + if ( findFile(path, {".tbd"}, result) ) return result; - } - // try @executable_path substitution if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) { @@ -848,16 +888,8 @@ Options::FileInfo Options::findFile(const std::string &path) const else strcpy(newPath, &path[17]); - std::string file = newPath; - auto lastDotIdx = file.find_last_of('.'); - if (lastDotIdx != std::string::npos) - file.erase(lastDotIdx, std::string::npos); - if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) { - return result; - } - if ( result.checkFileExists(*this, newPath) ) { + if ( findFile(newPath, {".tbd"}, result) ) return result; - } } // not found @@ -893,9 +925,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const auto leafPath = path.substr(beginPos); for (const auto* dir : fFrameworkSearchPaths) { auto possiblePath = dir + leafPath; - if ( checkForFile("%s.%s", possiblePath.c_str(), "tbd", result) ) - return result; - if ( checkForFile("%s", possiblePath.c_str(), "", result) ) + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } else { @@ -907,9 +937,8 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const if ( !embeddedDylib ) { for (const auto* dir : fLibrarySearchPaths) { //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); - if ( checkForFile("%s/%s", dir, std::string(leafName).append(".tbd").c_str(), result) ) - return result; - if ( checkForFile("%s/%s", dir, leafName.c_str(), result) ) + std::string possiblePath = dir + std::string("/") + leafName; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } @@ -1395,58 +1424,32 @@ Options::Treatment Options::parseTreatment(const char* treatment) void Options::setMacOSXVersionMin(const char* version) { - if ( version == NULL ) - throw "-macosx_version_min argument missing"; - - if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - unsigned int minorVersion = 0; - for (int i=3; isdigit(version[i]); ++i) { - minorVersion = minorVersion*10 + (version[i] - '0'); - } - if ( minorVersion > 255 ) { - warning("Mac OS X minor version > 255 in '%s'", version); - minorVersion = 255; - } - fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); - fPlatform = kPlatformOSX; - } - else { - warning("unknown option to -macosx_version_min, not 10.x"); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-macosx_version_min value malformed: '%s'", version); } + fMacVersionMin = (ld::MacVersionMin)value; + fPlatform = kPlatformOSX; } void Options::setIOSVersionMin(const char* version) { - if ( version == NULL ) - throw "-ios_version_min argument missing"; - if ( ! isdigit(version[0]) ) - throw "-ios_version_min argument is not a number"; - if ( version[1] != '.' ) - throw "-ios_version_min argument is missing period as second character"; - if ( ! isdigit(version[2]) ) - throw "-ios_version_min argument is not a number"; - - unsigned int majorVersion = version[0] - '0'; - unsigned int minorVersion = version[2] - '0'; - fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-ios_version_min value malformed: '%s'", version); + } + fIOSVersionMin = (ld::IOSVersionMin)value; fPlatform = kPlatformiOS; } void Options::setWatchOSVersionMin(const char* version) { - if ( version == NULL ) - throw "-watchos_version_min argument missing"; - if ( ! isdigit(version[0]) ) - throw "-watchos_version_min argument is not a number"; - if ( version[1] != '.' ) - throw "-watchos_version_min argument is missing period as second character"; - if ( ! isdigit(version[2]) ) - throw "-watchos_version_min argument is not a number"; - - unsigned int majorVersion = version[0] - '0'; - unsigned int minorVersion = version[2] - '0'; - fWatchOSVersionMin = (ld::WatchOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-watchos_version_min value malformed: '%s'", version); + } + fWatchOSVersionMin = (ld::WatchOSVersionMin)value; fPlatform = kPlatformWatchOS; } @@ -2027,6 +2030,73 @@ std::string Options::getVersionString64(uint64_t ver) const return versionString.str(); } +// Convert X.Y[.Z] to 32-bit value xxxxyyzz +bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &result) +{ + result = 0; + + if ( versionStr.empty() ) + return false; + + size_t pos = versionStr.find('.'); + if ( pos == std::string::npos ) + return false; + + std::string majorStr = versionStr.substr(0, pos); + std::string rest = versionStr.substr(pos+1); + + try { + size_t majorEnd; + int majorValue = std::stoi(majorStr, &majorEnd); + if ( majorEnd != majorStr.size() ) + return false; + if ( majorValue < 0 ) + return false; + if ( majorValue > 65535 ) + return false; + + std::string minorStr; + std::string microStr; + pos = rest.find('.'); + if ( pos == std::string::npos ) { + minorStr = rest; + } + else { + minorStr = rest.substr(0, pos); + microStr = rest.substr(pos+1); + } + + size_t minorEnd; + int minorValue = std::stoi(minorStr, &minorEnd); + if ( minorEnd != minorStr.size() ) + return false; + if ( minorValue < 0 ) + return false; + if ( minorValue > 255 ) + return false; + + int microValue = 0; + if ( !microStr.empty() ) { + size_t microEnd; + microValue = std::stoi(microStr, µEnd); + if ( microEnd != microStr.size() ) + return false; + if ( microValue < 0 ) + return false; + if ( microValue > 255 ) + return false; + } + + result = (majorValue << 16) | (minorValue << 8) | microValue; + + return true; + } + catch (...) { + // std::stoi() throws exception on malformed input + return false; + } +} + std::string Options::getSDKVersionStr() const { return getVersionString32(fSDKVersion); @@ -2695,7 +2765,10 @@ void Options::parse(int argc, const char* argv[]) throw "-segprot missing segName max-prot init-prot"; seg.max = parseProtection(argv[++i]); seg.init = parseProtection(argv[++i]); - fCustomSegmentProtections.push_back(seg); + if ( strcmp(seg.name, "__LINKEDIT") == 0 ) + warning("-segprot cannot be used to modify __LINKEDIT protections"); + else + fCustomSegmentProtections.push_back(seg); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-pagezero_size") == 0 ) { @@ -2721,9 +2794,6 @@ void Options::parse(int argc, const char* argv[]) if ( size == NULL ) throw "-stack_size missing

"; fStackSize = parseAddress(size); - uint64_t temp = fStackSize & (-4096); // page align - if ( (fStackSize != temp) ) - warning("-stack_size not page aligned, rounding down"); } else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; @@ -2766,6 +2836,8 @@ void Options::parse(int argc, const char* argv[]) // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { const char* macVers = argv[++i]; + if ( macVers == NULL ) + throw "-macosx_version_min missing version argument"; const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { @@ -2788,26 +2860,44 @@ void Options::parse(int argc, const char* argv[]) } } else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_version_min missing version argument"; + setIOSVersionMin(vers); } else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_simulator_version_min missing version argument"; + setIOSVersionMin(vers); fTargetIOSSimulator = true; } else if ( strcmp(arg, "-watchos_version_min") == 0 ) { - setWatchOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_version_min missing version argument"; + setWatchOSVersionMin(vers); } else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) { - setWatchOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_simulator_version_min missing version argument"; + setWatchOSVersionMin(vers); fTargetIOSSimulator = true; } #if SUPPORT_APPLE_TV else if ( strcmp(arg, "-tvos_version_min") == 0 ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_version_min missing version argument"; + setIOSVersionMin(vers); fPlatform = kPlatform_tvOS; } else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_simulator_version_min missing version argument"; + setIOSVersionMin(vers); fPlatform = kPlatform_tvOS; fTargetIOSSimulator = true; } @@ -3627,6 +3717,43 @@ void Options::parse(int argc, const char* argv[]) fUseDataConstSegmentForceOff = true; cannotBeUsedWithBitcode(arg); } + else if ( strcmp(arg, "-no_deduplicate") == 0 ) { + fDeDupe = false; + } + else if ( strcmp(arg, "-verbose_deduplicate") == 0 ) { + fVerboseDeDupe = true; + } + else if ( strcmp(arg, "-max_default_common_align") == 0 ) { + const char* alignStr = argv[++i]; + if ( alignStr == NULL ) + throw "-max_default_common_align missing "; + // argument is a hexadecimal number + char* endptr; + unsigned long value = strtoul(alignStr, &endptr, 16); + if ( *endptr != '\0') + throw "argument for -max_default_common_align is not a hexadecimal number"; + if ( value > 0x8000 ) + throw "argument for -max_default_common_align must be less than or equal to 0x8000"; + if ( value == 0 ) { + warning("zero is not a valid -max_default_common_align"); + value = 1; + } + // alignment is power of 2 + uint8_t alignment = (uint8_t)__builtin_ctz(value); + if ( (unsigned long)(1 << alignment) != value ) { + warning("alignment for -max_default_common_align is not a power of two, using 0x%X", 1 << alignment); + } + fMaxDefaultCommonAlign = alignment; + } + else if ( strcmp(arg, "-prefer-mod-time-check") == 0 ) { + fFilePreference = kModTime; + } + else if ( strcmp(arg, "-prefer-text-based-stub-file") == 0 ) { + fFilePreference = kTextBasedStub; + } + else if ( strcmp(arg, "-prefer-macho-file") == 0 ) { + fFilePreference = kMachO; + } // put this last so that it does not interfer with other options starting with 'i' else if ( strncmp(arg, "-i", 2) == 0 ) { const char* colon = strchr(arg, ':'); @@ -3701,16 +3828,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } if ( libSearchDir[0] == '\0' ) throw "-L must be immediately followed by a directory path (no space)"; - struct stat statbuf; - if ( stat(libSearchDir, &statbuf) == 0 ) { - if ( statbuf.st_mode & S_IFDIR ) - libraryPaths.push_back(libSearchDir); - else - warning("path '%s' following -L not a directory", libSearchDir); - } - else { - warning("directory not found for option '-L%s'", libSearchDir); - } + libraryPaths.push_back(libSearchDir); } else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) { const char* frameworkSearchDir = &argv[i][2]; @@ -3724,16 +3842,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } if ( frameworkSearchDir[0] == '\0' ) throw "-F must be immediately followed by a directory path (no space)"; - struct stat statbuf; - if ( stat(frameworkSearchDir, &statbuf) == 0 ) { - if ( statbuf.st_mode & S_IFDIR ) - frameworkPaths.push_back(frameworkSearchDir); - else - warning("path '%s' following -F not a directory", frameworkSearchDir); - } - else { - warning("directory not found for option '-F%s'", frameworkSearchDir); - } + frameworkPaths.push_back(frameworkSearchDir); } else if ( strcmp(argv[i], "-Z") == 0 ) addStandardLibraryDirectories = false; @@ -3820,8 +3929,13 @@ void Options::buildSearchPaths(int argc, const char* argv[]) strcat(newPath, libDir); struct stat statBuffer; if ( stat(newPath, &statBuffer) == 0 ) { - fLibrarySearchPaths.push_back(strdup(newPath)); - sdkOverride = true; + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) { + warning("-syslibroot and -L combined path '%s' is not a directory", newPath); + } + else { + fLibrarySearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } } } } @@ -3831,11 +3945,21 @@ void Options::buildSearchPaths(int argc, const char* argv[]) // if one SDK is specified and a standard library path is not in the SDK, don't use it } else { - fLibrarySearchPaths.push_back(libDir); + struct stat statBuffer; + if ( stat(libDir, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) + warning("-L path '%s' is not a directory", libDir); + else + fLibrarySearchPaths.push_back(libDir); + } + else if ( !addStandardLibraryDirectories || (strcmp(libDir, "/usr/local/lib") != 0) ) { + warning("directory not found for option '-L%s'", libDir); + } } } } + // now merge sdk and framework paths to make real search paths fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); int frameIndex = 0; @@ -3859,8 +3983,13 @@ void Options::buildSearchPaths(int argc, const char* argv[]) strcat(newPath, frameworkDir); struct stat statBuffer; if ( stat(newPath, &statBuffer) == 0 ) { - fFrameworkSearchPaths.push_back(strdup(newPath)); - sdkOverride = true; + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) { + warning("-syslibroot and -F combined path '%s' is not a directory", newPath); + } + else { + fFrameworkSearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } } } } @@ -3870,7 +3999,16 @@ void Options::buildSearchPaths(int argc, const char* argv[]) // if one SDK is specified and a standard library path is not in the SDK, don't use it } else { - fFrameworkSearchPaths.push_back(frameworkDir); + struct stat statBuffer; + if ( stat(frameworkDir, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) + warning("-F path '%s' is not a directory", frameworkDir); + else + fFrameworkSearchPaths.push_back(frameworkDir); + } + else if ( !addStandardLibraryDirectories || (strcmp(frameworkDir, "/Library/Frameworks/") != 0) ) { + warning("directory not found for option '-F%s'", frameworkDir); + } } } } @@ -3932,6 +4070,9 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL) fAllowCpuSubtypeMismatches = true; + if (getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH") != NULL) + fEnforceDylibSubtypesMatch = true; + sWarningsSideFilePath = getenv("LD_WARN_FILE"); const char* customDyldPath = getenv("LD_DYLD_PATH"); @@ -3950,6 +4091,10 @@ void Options::parsePreCommandLineEnvironmentSettings() if (pipeFdString != NULL) { fPipelineFifo = pipeFdString; } + + // Workaround for rdar://problem/24301175 + if ((getenv("RC_XBS") != NULL) && !(getenv("RC_BUILDIT") != NULL)) + fForceTextBasedStub = true; } @@ -4521,10 +4666,13 @@ void Options::reconfigureDefaults() // only ARM and x86_64 enforces that cpu-sub-types must match switch ( fArchitecture ) { case CPU_TYPE_ARM: + break; case CPU_TYPE_X86_64: + fEnforceDylibSubtypesMatch = false; break; case CPU_TYPE_I386: case CPU_TYPE_ARM64: + fEnforceDylibSubtypesMatch = false; fAllowCpuSubtypeMismatches = true; break; } @@ -4621,6 +4769,12 @@ void Options::reconfigureDefaults() else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) { fTLVSupport = true; } + else if ( fTargetIOSSimulator && (fArchitecture == CPU_TYPE_X86_64) && min_iOS(ld::iOS_8_0) ) { + fTLVSupport = true; + } + else if ( fTargetIOSSimulator && (fArchitecture == CPU_TYPE_I386) && min_iOS(ld::iOS_9_0) ) { + fTLVSupport = true; + } // default to adding version load command for dynamic code, static code must opt-in switch ( fOutputKind ) { @@ -4869,6 +5023,13 @@ void Options::reconfigureDefaults() } } + // Reduce the default alignment of structures/arrays to save memory in embedded systems + if ( fMaxDefaultCommonAlign == 0 ) { + if ( fOutputKind == Options::kPreload ) + fMaxDefaultCommonAlign = 8; + else + fMaxDefaultCommonAlign = 15; + } } void Options::checkIllegalOptionCombinations() @@ -4876,9 +5037,24 @@ void Options::checkIllegalOptionCombinations() // check -undefined setting switch ( fUndefinedTreatment ) { case kUndefinedError: - case kUndefinedDynamicLookup: // always legal break; + case kUndefinedDynamicLookup: + switch (fPlatform) { + case kPlatformOSX: + break; + case kPlatformiOS: + case kPlatformWatchOS: + #if SUPPORT_APPLE_TV + case kPlatform_tvOS: + #endif + if ( fOutputKind != kKextBundle ) + warning("-undefined dynamic_lookup is deprecated on %s", platformName(fPlatform)); + break; + default: + break; + } + break; case kUndefinedWarning: case kUndefinedSuppress: // requires flat namespace @@ -4896,7 +5072,11 @@ void Options::checkIllegalOptionCombinations() const char* lastSlash = strrchr(info.path, '/'); if ( lastSlash == NULL ) lastSlash = info.path - 1; - if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { + std::string path(&lastSlash[1]); + auto idx = path.find(".tbd", path.size() - 4); + if (idx != std::string::npos) + path.erase(idx); + if ( path == subUmbrella ) { info.options.fReExport = true; found = true; fLinkSnapshot.recordSubUmbrella(info.path); @@ -4931,8 +5111,23 @@ void Options::checkIllegalOptionCombinations() } // sync reader options - if ( fNameSpace != kTwoLevelNameSpace ) + if ( fNameSpace != kTwoLevelNameSpace ) { fFlatNamespace = true; + switch (fPlatform) { + case kPlatformOSX: + break; + case kPlatformiOS: + case kPlatformWatchOS: + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + #endif + warning("-flat_namespace is deprecated on %s", platformName(fPlatform)); + break; + default: + break; + } + } + // check -stack_addr if ( fStackAddr != 0 ) { @@ -4956,37 +5151,54 @@ void Options::checkIllegalOptionCombinations() if ( fStackSize != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: - if ( fStackSize > 0xFFFFFFFF ) - throw "-stack_size must be < 4G for 32-bit processes"; - if ( fStackAddr == 0 ) { - fStackAddr = 0xC0000000; + if ( fPlatform == kPlatformOSX ) { + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4GB for 32-bit processes"; + if ( fStackAddr == 0 ) + fStackAddr = 0xC0000000; + if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) + warning("custom stack placement overlaps and will disable shared region"); + } + else { + if ( fStackSize > 0x1F000000 ) + throw "-stack_size must be < 496MB"; + if ( fStackAddr == 0 ) + fStackAddr = 0xC0000000; } - if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) - warning("custom stack placement overlaps and will disable shared region"); break; case CPU_TYPE_ARM: - if ( fStackSize > 0x2F000000 ) - throw "-stack_size must be < 752MB"; + if ( fStackSize > 0x1F000000 ) + throw "-stack_size must be < 496MB"; if ( fStackAddr == 0 ) - fStackAddr = 0x2F000000; - if ( fStackAddr > 0x30000000) - throw "-stack_addr must be < 0x30000000 for arm"; + fStackAddr = 0x1F000000; + if ( fStackAddr > 0x20000000) + throw "-stack_addr must be < 0x20000000 for arm"; break; case CPU_TYPE_X86_64: - if ( fStackAddr == 0 ) { - fStackAddr = 0x00007FFF5C000000LL; + if ( fPlatform == kPlatformOSX ) { + if ( fStackSize > 0x10000000000 ) + throw "-stack_size must be <= 1TB"; + if ( fStackAddr == 0 ) { + fStackAddr = 0x00007FFF5C000000LL; + } + } + else { + if ( fStackSize > 0x20000000 ) + throw "-stack_size must be <= 512MB"; + if ( fStackAddr == 0 ) { + fStackAddr = 0x120000000; } break; case CPU_TYPE_ARM64: if ( fStackSize > 0x20000000 ) - throw "-stack_size must be < 512MB"; - if ( fStackAddr == 0 ) { + throw "-stack_size must be <= 512MB"; + if ( fStackAddr == 0 ) fStackAddr = 0x120000000; } break; } - if ( (fStackSize & -4096) != fStackSize ) - throw "-stack_size must be multiples of 4K"; + if ( (fStackSize & (-fSegmentAlignment)) != fStackSize ) + throwf("-stack_size must be multiple of segment alignment (%lldKB)", fSegmentAlignment/1024); switch ( fOutputKind ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: @@ -5313,15 +5525,6 @@ void Options::checkIllegalOptionCombinations() if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) ) throw "-segment_order can only used used with -preload output"; - if ( fBitcodeKind != kBitcodeProcess && - fOutputKind != Options::kObjectFile ) { - throw "-bitcode_process_mode can only be used together with -r"; - } - // auto fix up the process type for strip -S. - // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData. - if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess ) - fBitcodeKind = Options::kBitcodeAsData; - // warn about bitcode option combinations if ( !fBundleBitcode ) { if ( fVerifyBitcode ) @@ -5332,9 +5535,17 @@ void Options::checkIllegalOptionCombinations() if ( fReverseMapPath != NULL && !fHideSymbols ) { throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols"; } + if ( fBitcodeKind != kBitcodeProcess && + fOutputKind != Options::kObjectFile ) { + throw "-bitcode_process_mode can only be used together with -r"; + } + // auto fix up the process type for strip -S. + // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData. + if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess ) + fBitcodeKind = Options::kBitcodeAsData; // warn if building an embedded iOS dylib for pre-iOS 8 - // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest? + // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or later" when building XCTest? if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) { if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff ) warning("embedded dylibs/frameworks only run on iOS 8 or later"); diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index e7ffff2..5155de4 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -147,6 +147,18 @@ class Options // the source, which dies with the stack frame. FileInfo(FileInfo const &other) : path(other.path), fileLen(other.fileLen), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; }; + FileInfo &operator=(FileInfo other) { + std::swap(path, other.path); + std::swap(fileLen, other.fileLen); + std::swap(modTime, other.modTime); + std::swap(options, other.options); + std::swap(ordinal, other.ordinal); + std::swap(fromFileList, other.fromFileList); + std::swap(inputFileSlot, other.inputFileSlot); + std::swap(readyToParse, other.readyToParse); + return *this; + } + // Create an empty FileInfo. The path can be set implicitly by checkFileExists(). FileInfo() : path(NULL), fileLen(0), modTime(0), options(), fromFileList(false) {}; @@ -248,6 +260,7 @@ class Options bool preferSubArchitecture() const { return fHasPreferredSubType; } cpu_subtype_t subArchitecture() const { return fSubArchitecture; } bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; } + bool enforceDylibSubtypesMatch() const { return fEnforceDylibSubtypesMatch; } bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } const char* architectureName() const { return fArchitectureName; } void setArchitecture(cpu_type_t, cpu_subtype_t subtype, Options::Platform platform); @@ -313,6 +326,7 @@ class Options bool warnCommons() const { return fWarnCommons; } bool keepRelocations(); FileInfo findFile(const std::string &path) const; + bool findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const; UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } @@ -415,6 +429,8 @@ class Options bool hideSymbols() const { return fHideSymbols; } bool verifyBitcode() const { return fVerifyBitcode; } bool renameReverseSymbolMap() const { return fReverseMapUUIDRename; } + bool deduplicateFunctions() const { return fDeDupe; } + bool verboseDeduplicate() const { return fVerboseDeDupe; } const char* reverseSymbolMapPath() const { return fReverseMapPath; } std::string reverseMapTempPath() const { return fReverseMapTempPath; } bool ltoCodegenOnly() const { return fLTOCodegenOnly; } @@ -458,6 +474,9 @@ class Options std::vector writeBitcodeLinkOptions() const; std::string getSDKVersionStr() const; std::string getPlatformStr() const; + uint8_t maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; } + + static uint32_t parseVersionNumber32(const char*); private: typedef std::unordered_map NameToOrder; @@ -465,6 +484,7 @@ class Options enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome }; + enum FilePreference { kModTime, kTextBasedStub, kMachO }; class SetWithWildcards { public: @@ -500,9 +520,9 @@ class Options bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const; uint64_t parseVersionNumber64(const char*); - uint32_t parseVersionNumber32(const char*); std::string getVersionString32(uint32_t ver) const; std::string getVersionString64(uint64_t ver) const; + bool parsePackedVersion32(const std::string& versionStr, uint32_t &result); void parseSectionOrderFile(const char* segment, const char* section, const char* path); void parseOrderFile(const char* path, bool cstring); void addSection(const char* segment, const char* section, const char* path); @@ -639,6 +659,7 @@ class Options bool fMakeCompressedDyldInfoForceOff; bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; + bool fEnforceDylibSubtypesMatch; bool fUseSimplifiedDylibReExports; bool fObjCABIVersion2Override; bool fObjCABIVersion1Override; @@ -715,6 +736,8 @@ class Options bool fHideSymbols; bool fVerifyBitcode; bool fReverseMapUUIDRename; + bool fDeDupe; + bool fVerboseDeDupe; const char* fReverseMapPath; std::string fReverseMapTempPath; bool fLTOCodegenOnly; @@ -758,6 +781,9 @@ class Options const char* fPipelineFifo; const char* fDependencyInfoPath; mutable int fDependencyFileDescriptor; + uint8_t fMaxDefaultCommonAlign; + FilePreference fFilePreference; + bool fForceTextBasedStub; }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index dd0e5b2..1d708da 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -1294,7 +1294,7 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) #define LOH_ASSERT(cond) \ if ( !(cond) ) { \ - warning("ignoring linker optimzation hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \ + warning("ignoring linker optimization hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \ break; \ } @@ -2235,7 +2235,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(isADRP); isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC); LOH_ASSERT(isLDR); - LOH_ASSERT(ldrInfoC.offset == 0); isADD = parseADD(infoB.instruction, addInfoB); isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); if ( isLDR ) { @@ -2244,8 +2243,8 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(!ldrInfoB.isFloat); LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress); - targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); - if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + targetFourByteAligned = ( ((infoA.targetAddress + ldrInfoC.offset) & 0x3) == 0 ); + if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress + ldrInfoC.offset) ) { // can do T5 transform set32LE(infoA.instructionContent, makeNOP()); set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); @@ -2264,11 +2263,11 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); - if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) { + if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress + ldrInfoC.offset) ) { // can do T1 transform set32LE(infoA.instructionContent, makeNOP()); set32LE(infoB.instructionContent, makeNOP()); - set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress)); + set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress + ldrInfoC.offset, infoC.instructionAddress)); if ( _options.verboseOptimizationHints() ) fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress); } @@ -2281,14 +2280,14 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress); } } - else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) { + else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && ((addInfoB.addend + ldrInfoC.offset) < 4096) ) { // can do T2 transform set32LE(infoB.instructionContent, makeNOP()); ldrInfoC.baseReg = adrpInfoA.destReg; - ldrInfoC.offset = addInfoB.addend; + ldrInfoC.offset += addInfoB.addend; set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); if ( _options.verboseOptimizationHints() ) { - fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress); + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T2 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress); } } else { @@ -2354,7 +2353,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(isADRP); isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore); LOH_ASSERT(isSTR); - LOH_ASSERT(ldrInfoC.offset == 0); isADD = parseADD(infoB.instruction, addInfoB); isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); if ( isLDR ) { @@ -2362,8 +2360,8 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(ldrInfoB.size == 8); LOH_ASSERT(!ldrInfoB.isFloat); LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); - targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); - if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + targetFourByteAligned = ( ((infoA.targetAddress + ldrInfoC.offset) & 0x3) == 0 ); + if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress + ldrInfoC.offset) ) { // can do T5 transform set32LE(infoA.instructionContent, makeNOP()); set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); @@ -2395,7 +2393,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // can do T2 transform set32LE(infoB.instructionContent, makeNOP()); ldrInfoC.baseReg = adrpInfoA.destReg; - ldrInfoC.offset = addInfoB.addend; + ldrInfoC.offset += addInfoB.addend; set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); if ( _options.verboseOptimizationHints() ) { fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress); @@ -3600,6 +3598,10 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARM64GOTLoadPageOff12: case ld::Fixup::kindStoreARM64GOTLeaPage21: case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: case ld::Fixup::kindStoreARM64PCRelToGOT: case ld::Fixup::kindStoreTargetAddressARM64Page21: case ld::Fixup::kindStoreTargetAddressARM64PageOff12: @@ -3607,6 +3609,10 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: #endif return true; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: @@ -3670,6 +3676,10 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: #endif return true; case ld::Fixup::kindStoreX86DtraceCallSiteNop: @@ -4529,9 +4539,12 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreARM64GOTLoadPage21: case ld::Fixup::kindStoreARM64GOTLeaPage21: case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: case ld::Fixup::kindStoreTargetAddressARM64Page21: case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: case ld::Fixup::kindStoreARM64PCRelToGOT: #endif assert(target != NULL); @@ -4666,9 +4679,12 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) case ld::Fixup::kindStoreARM64GOTLoadPage21: case ld::Fixup::kindStoreARM64GOTLeaPage21: case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: case ld::Fixup::kindStoreTargetAddressARM64Page21: case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: if ( fromSectionIndex != toSectionIndex ) kind = DYLD_CACHE_ADJ_V2_ARM64_ADRP; break; diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index cbb1cfe..5fd27d8 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -147,6 +147,7 @@ class OutputFile uint8_t targetSectionIndex; uint8_t referenceKind; }; + static void dumpAtomsBySection(ld::Internal& state, bool); private: void writeAtoms(ld::Internal& state, uint8_t* wholeBuffer); @@ -237,7 +238,6 @@ class OutputFile uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); - void dumpAtomsBySection(ld::Internal& state, bool); void synthesizeDebugNotes(ld::Internal& state); const char* assureFullPath(const char* path); void noteTextReloc(const ld::Atom* atom, const ld::Atom* target); diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 948906f..ec04b46 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -362,6 +362,11 @@ void Resolver::doFile(const ld::File& file) for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { this->doLinkerOption(*it, file.path()); } + // process any additional linker-options introduced by this new archive member being loaded + if ( _completedInitialObjectFiles ) { + _inputFiles.addLinkerOptionLibraries(_internal, *this); + _inputFiles.createIndirectDylibs(); + } } // Resolve bitcode section in the object file if ( _options.bundleBitcode() ) { @@ -618,7 +623,7 @@ void Resolver::doFile(const ld::File& file) break; } if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) { - warning("linking against dylib not safe for use in application extensions: %s", file.path()); + warning("linking against a dylib which is not safe for use in application extensions: %s", file.path()); } const char* depInstallName = dylibFile->installPath(); // embedded frameworks are only supported on iOS 8 and later @@ -944,6 +949,10 @@ void Resolver::resolveUndefines() } } + // After resolving all the undefs within the linkageUnit, record all the remaining undefs and all the proxies. + if (_options.bundleBitcode() && _options.hideSymbols()) + _symbolTable.mustPreserveForBitcode(_internal.allUndefProxies); + } @@ -1705,6 +1714,7 @@ void Resolver::linkTimeOptimize() optOpt.simulator = _options.targetIOSSimulator(); optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); optOpt.bitcodeBundle = _options.bundleBitcode(); + optOpt.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign(); optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); optOpt.platform = _options.platform(); diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index 6014779..4be0c5e 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -566,6 +566,18 @@ void SymbolTable::tentativeDefs(std::vector& tents) } +void SymbolTable::mustPreserveForBitcode(std::unordered_set& syms) +{ + // return all names in _byNameTable that have no associated atom + for (const auto &entry: _byNameTable) { + const char* name = entry.first; + const ld::Atom* atom = _indirectBindingTable[entry.second]; + if ( (atom == NULL) || (atom->definition() == ld::Atom::definitionProxy) ) + syms.insert(name); + } +} + + bool SymbolTable::hasName(const char* name) { NameToSlot::iterator pos = _byNameTable.find(name); diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h index ce2f1dc..2708f1f 100644 --- a/ld64/src/ld/SymbolTable.h +++ b/ld64/src/ld/SymbolTable.h @@ -120,6 +120,7 @@ class SymbolTable : public ld::IndirectBindingTable unsigned int updateCount() { return _indirectBindingTable.size(); } void undefines(std::vector& undefines); void tentativeDefs(std::vector& undefines); + void mustPreserveForBitcode(std::unordered_set& syms); void removeDeadAtoms(); bool hasName(const char* name); bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; } diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 0b5bc4a..a856598 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -80,6 +80,7 @@ extern "C" double log2 ( double ); #include "passes/objc.h" #include "passes/dylibs.h" #include "passes/bitcode_bundle.h" +#include "passes/code_dedup.h" #include "parsers/archive_file.h" #include "parsers/macho_relocatable_file.h" @@ -956,6 +957,7 @@ uint64_t InternalState::assignFileOffsets() lastSegName = ""; ld::Internal::FinalSection* overlappingFixedSection = NULL; ld::Internal::FinalSection* overlappingFlowSection = NULL; + ld::Internal::FinalSection* prevSect = NULL; if ( log ) fprintf(stderr, "Regular layout segments:\n"); for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; @@ -985,7 +987,16 @@ uint64_t InternalState::assignFileOffsets() // update section info sect->address = address; sect->alignmentPaddingBytes = (address - unalignedAddress); - + + // if first section is more aligned than segment, move segment start up to match + if ( (prevSect != NULL) && (prevSect->type() == ld::Section::typeFirstSection) && (strcmp(prevSect->segmentName(), sect->segmentName()) == 0) ) { + assert(prevSect->size == 0); + if ( prevSect->address != sect->address ) { + prevSect->alignmentPaddingBytes += (sect->address - prevSect->address); + prevSect->address = sect->address; + } + } + // sanity check size if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) && (_options.outputKind() != Options::kStaticExecutable) ) @@ -1021,6 +1032,7 @@ uint64_t InternalState::assignFileOffsets() // update running totals if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) address += sect->size; + prevSect = sect; } if ( overlappingFixedSection != NULL ) { fprintf(stderr, "Section layout:\n"); @@ -1213,7 +1225,8 @@ int main(int argc, const char* argv[]) ld::passes::dylibs::doPass(options, state); // must be after stubs and GOT passes ld::passes::order::doPass(options, state); state.markAtomsOrdered(); - ld::passes::branch_shim::doPass(options, state); // must be after stubs + ld::passes::dedup::doPass(options, state); + ld::passes::branch_shim::doPass(options, state); // must be after stubs ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); ld::passes::compact_unwind::doPass(options, state); // must be after order pass diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index e297036..89c091b 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -246,12 +246,13 @@ namespace dylib { }; File(const char* pth, time_t modTime, Ordinal ord) - : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL), + : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL), _frameworkName(NULL), _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0), _explicitlyLinked(false), _implicitlyLinked(false), _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false), _upward(false), _dead(false) { } const char* installPath() const { return _dylibInstallPath; } + const char* frameworkName() const { return _frameworkName; } uint32_t timestamp() const { return _dylibTimeStamp; } uint32_t currentVersion() const { return _dylibCurrentVersion; } uint32_t compatibilityVersion() const{ return _dylibCompatibilityVersion; } @@ -285,13 +286,9 @@ namespace dylib { virtual bool installPathVersionSpecific() const { return false; } virtual bool appExtensionSafe() const = 0; - protected: - struct ReExportChain { ReExportChain* prev; const File* file; }; - virtual std::pair hasWeakDefinitionImpl(const char* name) const = 0; - virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const = 0; - virtual void assertNoReExportCycles(ReExportChain*) const = 0; - + public: const char* _dylibInstallPath; + const char* _frameworkName; uint32_t _dylibTimeStamp; uint32_t _dylibCurrentVersion; uint32_t _dylibCompatibilityVersion; @@ -446,7 +443,7 @@ struct Fixup // data-in-code markers kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16, kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd, - // linker optimzation hints + // linker optimization hints kindLinkerOptimizationHint, // pointer store combinations kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32 @@ -895,8 +892,11 @@ class Internal AtomToSection atomToSection; CStringSet linkerOptionLibraries; CStringSet linkerOptionFrameworks; + CStringSet linkerOptionLibrariesProcessed; + CStringSet linkerOptionFrameworksProcessed; std::vector indirectBindingTable; std::vector filesWithBitcode; + std::unordered_set allUndefProxies; const ld::dylib::File* bundleLoader; const Atom* entryPoint; const Atom* classicBindingHelper; diff --git a/ld64/src/ld/parsers/generic_dylib_file.hpp b/ld64/src/ld/parsers/generic_dylib_file.hpp new file mode 100644 index 0000000..301074f --- /dev/null +++ b/ld64/src/ld/parsers/generic_dylib_file.hpp @@ -0,0 +1,565 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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 __GENERIC_DYLIB_FILE_H__ +#define __GENERIC_DYLIB_FILE_H__ + +#include "ld.hpp" +#include "Options.h" +#include +#include + +namespace generic { +namespace dylib { + +// forward reference +template class File; + +// +// An ExportAtom has no content. It exists so that the linker can track which +// imported symbols came from which dynamic libraries. +// +template +class ExportAtom final : public ld::Atom +{ +public: + ExportAtom(const File& f, const char* nm, bool weakDef, bool tlv, + typename A::P::uint_t address) + : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, + (weakDef ? ld::Atom::combineByName : ld::Atom::combineNever), + ld::Atom::scopeLinkageUnit, + (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), + _name(nm), + _address(address) + {} + + // overrides of ld::Atom + virtual const ld::File* file() const override final { return &_file; } + virtual const char* name() const override final { return _name; } + virtual uint64_t size() const override final { return 0; } + virtual uint64_t objectAddress() const override final { return _address; } + virtual void copyRawContent(uint8_t buffer[]) const override final { } + + virtual void setScope(Scope) { } + +private: + using pint_t = typename A::P::uint_t; + + virtual ~ExportAtom() {} + + const File& _file; + const char* _name; + pint_t _address; +}; + + +// +// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace +// the imports of all flat dylibs are checked +// +template +class ImportAtom final : public ld::Atom +{ +public: + ImportAtom(File& f, std::vector& imports); + + // overrides of ld::Atom + virtual ld::File* file() const override final { return &_file; } + virtual const char* name() const override final { return "import-atom"; } + virtual uint64_t size() const override final { return 0; } + virtual uint64_t objectAddress() const override final { return 0; } + virtual ld::Fixup::iterator fixupsBegin() const override final { return &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() const override final { return &_undefs[_undefs.size()]; } + virtual void copyRawContent(uint8_t buffer[]) const override final { } + + virtual void setScope(Scope) { } + +private: + virtual ~ImportAtom() {} + + File& _file; + mutable std::vector _undefs; +}; + +template +ImportAtom::ImportAtom(File& f, std::vector& imports) + : ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, + false, false, ld::Atom::Alignment(0)), + _file(f) +{ + for(auto *name : imports) + _undefs.emplace_back(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(name)); +} + +// +// A generic representation for the dynamic library files we support (Mach-O and text-based stubs). +// Common state and functionality is consolidated in this class. +// +template +class File : public ld::dylib::File +{ +public: + File(const char* path, time_t mTime, ld::File::Ordinal ordinal, Options::Platform platform, + uint32_t linkMinOSVersion, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, + bool allowSimToMacOSX, bool addVers); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const override final; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const override final; + virtual ld::File::ObjcConstraint objCConstraint() const override final { return _objcConstraint; } + virtual uint8_t swiftVersion() const override final { return _swiftVersion; } + virtual uint32_t minOSVersion() const override final { return _minVersionInDylib; } + virtual uint32_t platformLoadCommand() const override final { return _platformInDylib; } + virtual ld::Bitcode* getBitcode() const override final { return _bitcode.get(); } + + + // overrides of ld::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final; + virtual bool providedExportAtom() const override final { return _providedAtom; } + virtual const char* parentUmbrella() const override final { return _parentUmbrella; } + virtual const std::vector* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; } + virtual bool hasWeakExternals() const override final { return _hasWeakExports; } + virtual bool deadStrippable() const override final { return _deadStrippable; } + virtual bool hasWeakDefinition(const char* name) const override final; + virtual bool hasPublicInstallName() const override final { return _hasPublicInstallName; } + virtual bool allSymbolsAreWeakImported() const override final; + virtual bool installPathVersionSpecific() const override final { return _installPathOverride; } + virtual bool appExtensionSafe() const override final { return _appExtensionSafe; }; + + + bool wrongOS() const { return _wrongOS; } + +private: + using pint_t = typename A::P::uint_t; + + friend class ExportAtom; + friend class ImportAtom; + + struct CStringHash { + std::size_t operator()(const char* __s) const { + unsigned long __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return size_t(__h); + }; + }; + +protected: + struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; }; + struct Dependent { + const char* path; + File* dylib; + bool reExport; + + Dependent(const char* path, bool reExport) + : path(path), dylib(nullptr), reExport(reExport) {} + }; + struct ReExportChain { ReExportChain* prev; const File* file; }; + +private: + using NameToAtomMap = std::unordered_map; + using NameSet = std::unordered_set; + + std::pair hasWeakDefinitionImpl(const char* name) const; + bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, pint_t& addr) const; + void assertNoReExportCycles(ReExportChain*) const; + +protected: + bool isPublicLocation(const char* path) const; + void addSymbol(const char* name, bool weak = false, bool tlv = false, pint_t address = 0); + +private: + ld::Section _importProxySection; + ld::Section _flatDummySection; + mutable bool _providedAtom; + bool _indirectDylibsProcessed; + +protected: + mutable NameToAtomMap _atoms; + NameSet _ignoreExports; + std::vector _dependentDylibs; + ImportAtom* _importAtom; + std::vector _allowableClients; + const char* _parentUmbrella; + std::unique_ptr _bitcode; + const Options::Platform _platform; + ld::File::ObjcConstraint _objcConstraint; + const uint32_t _linkMinOSVersion; + uint32_t _minVersionInDylib; + uint32_t _platformInDylib; + uint8_t _swiftVersion; + bool _wrongOS; + bool _linkingFlat; + bool _noRexports; + bool _explictReExportFound; + bool _implicitlyLinkPublicDylibs; + bool _installPathOverride; + bool _hasWeakExports; + bool _deadStrippable; + bool _hasPublicInstallName; + bool _appExtensionSafe; + + const bool _allowSimToMacOSXLinking; + const bool _addVersionLoadCommand; + + static bool _s_logHashtable; +}; + +template +bool File::_s_logHashtable = false; + +template +File::File(const char* path, time_t mTime, ld::File::Ordinal ord, Options::Platform platform, + uint32_t linkMinOSVersion, bool linkingFlatNamespace, + bool hoistImplicitPublicDylibs, + bool allowSimToMacOSX, bool addVers) + : ld::dylib::File(path, mTime, ord), + _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), + _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), + _providedAtom(false), + _indirectDylibsProcessed(false), + _importAtom(nullptr), + _parentUmbrella(nullptr), + _platform(platform), + _objcConstraint(ld::File::objcConstraintNone), + _linkMinOSVersion(linkMinOSVersion), + _minVersionInDylib(0), + _platformInDylib(Options::kPlatformUnknown), + _swiftVersion(0), + _wrongOS(false), + _linkingFlat(linkingFlatNamespace), + _noRexports(false), + _explictReExportFound(false), + _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), + _installPathOverride(false), + _hasWeakExports(false), + _deadStrippable(false), + _hasPublicInstallName(false), + _appExtensionSafe(false), + _allowSimToMacOSXLinking(allowSimToMacOSX), + _addVersionLoadCommand(addVers) +{ +} + +template +std::pair File::hasWeakDefinitionImpl(const char* name) const +{ + const auto pos = _atoms.find(name); + if ( pos != this->_atoms.end() ) + return std::make_pair(true, pos->second.weakDef); + + // look in re-exported libraries. + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport ) { + auto ret = dep.dylib->hasWeakDefinitionImpl(name); + if ( ret.first ) + return ret; + } + } + return std::make_pair(false, false); +} + +template +bool File::hasWeakDefinition(const char* name) const +{ + // If we are supposed to ignore this export, then pretend we don't have it. + if ( _ignoreExports.count(name) != 0 ) + return false; + + return hasWeakDefinitionImpl(name).second; +} + +template +bool File::containsOrReExports(const char* name, bool& weakDef, bool& tlv, pint_t& addr) const +{ + if ( _ignoreExports.count(name) != 0 ) + return false; + + // check myself + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + weakDef = pos->second.weakDef; + tlv = pos->second.tlv; + addr = pos->second.address; + return true; + } + + // check dylibs I re-export + for (const auto& dep : _dependentDylibs) { + if ( dep.reExport && !dep.dylib->implicitlyLinked() ) { + if ( dep.dylib->containsOrReExports(name, weakDef, tlv, addr) ) + return true; + } + } + + return false; +} + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + + // if doing flatnamespace and need all this dylib's imports resolve + // add atom which references alls undefines in this dylib + if ( _importAtom != nullptr ) { + handler.doAtom(*_importAtom); + return true; + } + return false; +} + +template +bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // If we are supposed to ignore this export, then pretend we don't have it. + if ( _ignoreExports.count(name) != 0 ) + return false; + + + AtomAndWeak bucket; + if ( 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()); + // call handler with new export atom + handler.doAtom(*bucket.atom); + return true; + } + + return false; +} + +template +void File::assertNoReExportCycles(ReExportChain* prev) const +{ + // recursively check my re-exported dylibs + ReExportChain chain = { prev, this }; + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport ) { + auto* child = dep.dylib; + // check child is not already in chain + for (auto* p = prev; p != nullptr; p = p->prev) { + if ( p->file == child ) { + throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); + } + } + if ( dep.dylib != nullptr ) + dep.dylib->assertNoReExportCycles(&chain); + } + } +} + +template +void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +{ + // only do this once + if ( _indirectDylibsProcessed ) + return; + + const static bool log = false; + if ( log ) + fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); + if ( _linkingFlat ) { + for (auto &dep : _dependentDylibs) + dep.dylib = (File*)handler->findDylib(dep.path, this->path()); + } + else if ( _noRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (auto &dep : this->_dependentDylibs) { + if ( dep.reExport ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), dep.path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + dep.dylib = (File*)handler->findDylib(dep.path, this->path()); + if ( dep.dylib->hasPublicInstallName() && !dep.dylib->wrongOS() ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(dep.path, dep.dylib->installPath()) == 0) ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", dep.dylib->installPath()); + dep.dylib->setImplicitlyLinked(); + } + else if ( dep.dylib->explicitlyLinked() || dep.dylib->implicitlyLinked() ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } + else { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), dep.path); + } + } + else { + // add all child's symbols to me + if ( log ) + fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), dep.path); + } + } + else if ( !_explictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + dep.dylib = (File*)handler->findDylib(dep.path, this->path()); + const char* parentUmbrellaName = dep.dylib->parentUmbrella(); + if ( parentUmbrellaName != nullptr ) { + const char* parentName = this->path(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != nullptr) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + dep.reExport = true; + if ( log ) + fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), dep.path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain = { nullptr, this }; + this->assertNoReExportCycles(&chain); + + _indirectDylibsProcessed = true; +} + +template +bool File::isPublicLocation(const char* path) const +{ + // -no_implicit_dylibs disables this optimization + if ( ! _implicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&path[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != nullptr ) { + int frameworkNameLen = frameworkDot - &path[27]; + if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) +{ + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != nullptr ) { + char curOSVers[16]; + sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != nullptr ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( _s_logHashtable ) + fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + _ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef); + return; + } + else if ( strncmp(symAction, "install_name$", 13) == 0 ) { + _dylibInstallPath = strdup(symName); + _installPathOverride = true; + // CoreGraphics redirects to ApplicationServices, but with wrong compat version + if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) + _dylibCompatibilityVersion = Options::parseVersionNumber32("1.0"); + return; + } + else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { + _dylibCompatibilityVersion = Options::parseVersionNumber32(symName); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( _ignoreExports.count(name) == 0 ) { + AtomAndWeak bucket = { nullptr, weakDef, tlv, address }; + if ( this->_s_logHashtable ) + fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + _atoms[strdup(name)] = bucket; + } +} + +// 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 (const auto &it : _atoms) { + auto* atom = it.second.atom; + if ( atom != nullptr ) { + 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; +} + + +} // end namespace dylib +} // end namespace generic + +#endif // __GENERIC_DYLIB_FILE_H__ diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index d81e833..f0f5f10 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -317,7 +317,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.srcKind = ld::relocatable::File::kSourceLTO; objOpts.treateBitcodeAsData = false; objOpts.usingBitcode = options.bitcodeBundle; - + objOpts.maxDefaultCommonAlignment = options.maxDefaultCommonAlignment; + // mach-o parsing is done in-memory, but need path for debug notes const char* path = "/tmp/lto.o"; time_t modTime = 0; @@ -1022,7 +1023,6 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar return Parser::validFile(fileContent, fileLength, architecture, subarch); } - static ld::relocatable::File *parseImpl( const uint8_t *fileContent, uint64_t fileLength, const char *path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, @@ -1043,6 +1043,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints) { + // do light weight check before acquiring lock + if ( fileLength < 4 ) + return NULL; + if ( (fileContent[0] != 0xDE) || (fileContent[1] != 0xC0) || (fileContent[2] != 0x17) || (fileContent[3] != 0x0B) ) + return NULL; + // Note: Once lto_module_create_in_local_context() and friends are thread safe // this lock can be removed. Mutex lock; diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index 6568232..38cfcd1 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -37,7 +37,7 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); -extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, +extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints); @@ -62,6 +62,7 @@ struct OptimizeOptions { bool simulator; bool ignoreMismatchPlatform; bool bitcodeBundle; + uint8_t maxDefaultCommonAlignment; cpu_type_t arch; const char* mcpu; Options::Platform platform; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 573f06c..24d69c8 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -34,113 +34,30 @@ #include #include #include -#include -#include #include "Architectures.hpp" #include "Bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" +#include "generic_dylib_file.hpp" #include "macho_dylib_file.h" #include "../code-sign-blobs/superblob.h" namespace mach_o { namespace dylib { - -// forward reference -template class File; - - -// -// An ExportAtom has no content. It exists so that the linker can track which imported -// symbols came from which dynamic libraries. -// -template -class ExportAtom : public ld::Atom -{ -public: - ExportAtom(const File& f, const char* nm, bool weakDef, - bool tlv, typename A::P::uint_t address) - : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, - (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), - ld::Atom::scopeLinkageUnit, - (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), - _file(f), _name(nm), _address(address) {} - // overrides of ld::Atom - virtual const ld::File* file() const { return &_file; } - virtual const char* name() const { return _name; } - virtual uint64_t size() const { return 0; } - virtual uint64_t objectAddress() const { return _address; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - -protected: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - - virtual ~ExportAtom() {} - - const File& _file; - const char* _name; - pint_t _address; -}; - - - -// -// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace -// the imports of all flat dylibs are checked -// -template -class ImportAtom : public ld::Atom -{ -public: - ImportAtom(File& f, std::vector& imports); - - // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual const char* name() const { return "import-atom"; } - virtual uint64_t size() const { return 0; } - 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 &_undefs[0]; } - virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } - -protected: - typedef typename A::P P; - - virtual ~ImportAtom() {} - - - File& _file; - mutable std::vector _undefs; -}; - -template -ImportAtom::ImportAtom(File& f, std::vector& imports) -: ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, - ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), _file(f) -{ - for(std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { - _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(*it))); - } -} - - - // // The reader for a dylib extracts all exported symbols names from the memory-mapped // dylib, builds a hash table, then unmaps the file. This is an important memory // savings for large dylibs. // template -class File : public ld::dylib::File +class File final : public generic::dylib::File { + using Base = generic::dylib::File; + public: - static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + static bool validFile(const uint8_t* fileContent, bool executableOrDylib, bool subTypeMustMatch=false); File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, @@ -148,106 +65,26 @@ class File : public ld::dylib::File bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode); - virtual ~File() {} - - // overrides of ld::File - virtual bool forEachAtom(ld::File::AtomHandler&) const; - virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; - virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } - virtual uint8_t swiftVersion() const { return _swiftVersion; } - virtual uint32_t minOSVersion() const { return _minVersionInDylib; } - virtual uint32_t platformLoadCommand() const { return _platformInDylib; } - - // overrides of ld::dylib::File - virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); - virtual bool providedExportAtom() const { return _providedAtom; } - virtual const char* parentUmbrella() const { return _parentUmbrella; } - virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : NULL; } - virtual bool hasWeakExternals() const { return _hasWeakExports; } - virtual bool deadStrippable() const { return _deadStrippable; } - virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } - virtual bool hasWeakDefinition(const char* name) const; - virtual bool allSymbolsAreWeakImported() const; - virtual bool installPathVersionSpecific() const { return _installPathOverride; } - virtual bool appExtensionSafe() const { return _appExtensionSafe; }; - virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } - -protected: - virtual void assertNoReExportCycles(ReExportChain*) const; + virtual ~File() noexcept {} private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - friend class ExportAtom; - friend class ImportAtom; - - struct CStringHash { - std::size_t operator()(const char* __s) const { - unsigned long __h = 0; - for ( ; *__s; ++__s) - __h = 5 * __h + *__s; - return size_t(__h); - }; - }; - struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; uint64_t address; }; - typedef std::unordered_map NameToAtomMap; - typedef std::unordered_set NameSet; + using P = typename A::P; + using E = typename A::P::E; - struct Dependent { const char* path; File* dylib; bool reExport; }; - - virtual std::pair hasWeakDefinitionImpl(const char* name) const; - virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const; - bool isPublicLocation(const char* pth); - bool wrongOS() { return _wrongOS; } - void addSymbol(const char* name, bool weak, bool tlv, pint_t address); - void addDyldFastStub(); - void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + void addDyldFastStub(); + void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, const uint8_t* fileContent); - void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, const macho_nlist

* symbolTable, const char* strings, const uint8_t* fileContent); - static uint32_t parseVersionNumber32(const char* versionString); - static const char* objCInfoSegmentName(); - static const char* objCInfoSectionName(); + static const char* objCInfoSegmentName(); + static const char* objCInfoSectionName(); - const Options::Platform _platform; - const uint32_t _linkMinOSVersion; - const bool _allowSimToMacOSXLinking; - const bool _addVersionLoadCommand; - bool _linkingFlat; - bool _implicitlyLinkPublicDylibs; - ld::File::ObjcConstraint _objcContraint; - uint8_t _swiftVersion; - ld::Section _importProxySection; - ld::Section _flatDummySection; - std::vector _dependentDylibs; - std::vector _allowableClients; - mutable NameToAtomMap _atoms; - NameSet _ignoreExports; - const char* _parentUmbrella; - ImportAtom* _importAtom; - bool _noRexports; - bool _hasWeakExports; - bool _deadStrippable; - bool _hasPublicInstallName; - mutable bool _providedAtom; - bool _explictReExportFound; - bool _wrongOS; - bool _installPathOverride; - bool _indirectDylibsProcessed; - bool _appExtensionSafe; - bool _usingBitcode; - uint32_t _minVersionInDylib; - uint32_t _platformInDylib; - std::unique_ptr _bitcode; - - static bool _s_logHashtable; -}; -template -bool File::_s_logHashtable = false; + uint64_t _fileLength; + uint32_t _linkeditStartOffset; + +}; template <> const char* File::objCInfoSegmentName() { return "__DATA"; } template <> const char* File::objCInfoSegmentName() { return "__DATA"; } @@ -258,22 +95,13 @@ template <> const char* File::objCInfoSectionName() { return "__objc_imagei template const char* File::objCInfoSectionName() { return "__image_info"; } template -File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, - bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, - bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode) - : ld::dylib::File(strdup(pth), mTime, ord), - _platform(platform), _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), - _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), - _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0), - _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), - _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), - _parentUmbrella(NULL), _importAtom(NULL), - _noRexports(false), _hasWeakExports(false), - _deadStrippable(false), _hasPublicInstallName(false), - _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), - _indirectDylibsProcessed(false), _appExtensionSafe(false), _usingBitcode(usingBitcode), - _minVersionInDylib(0), _platformInDylib(Options::kPlatformUnknown) +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, + ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, + bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, + const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode) + : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace, + hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -282,7 +110,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // write out path for -t option if ( logAllFiles ) - printf("%s\n", pth); + printf("%s\n", path); // a "blank" stub has zero load commands if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { @@ -293,32 +121,33 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // optimize the case where we know there is no reason to look at indirect dylibs - _noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) - || (header->filetype() == MH_BUNDLE) - || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader - _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); - _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); - _appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE); + this->_noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) + || (header->filetype() == MH_BUNDLE) + || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader + this->_hasWeakExports = (header->flags() & MH_WEAK_DEFINES); + this->_deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + this->_appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE); // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format - const macho_dysymtab_command

* dynamicInfo = NULL; - const macho_dyld_info_command

* dyldInfo = NULL; - const macho_nlist

* symbolTable = NULL; - const char* strings = NULL; + const macho_dysymtab_command

* dynamicInfo = nullptr; + const macho_dyld_info_command

* dyldInfo = nullptr; + const macho_nlist

* symbolTable = nullptr; + const macho_symtab_command

* symtab = nullptr; + const char* strings = nullptr; bool compressedLinkEdit = false; uint32_t dependentLibCount = 0; Options::Platform lcPlatform = Options::kPlatformUnknown; const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { macho_dylib_command

* dylibID; - const macho_symtab_command

* symtab; + uint32_t cmdLength = cmd->cmdsize(); switch (cmd->cmd()) { case LC_SYMTAB: symtab = (macho_symtab_command

*)cmd; symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); strings = (char*)header + symtab->stroff(); if ( (symtab->stroff() + symtab->strsize()) > fileLength ) - throwf("mach-o string pool extends beyond end of file in %s", pth); + throwf("mach-o string pool extends beyond end of file in %s", path); break; case LC_DYSYMTAB: dynamicInfo = (macho_dysymtab_command

*)cmd; @@ -330,27 +159,31 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; case LC_ID_DYLIB: dylibID = (macho_dylib_command

*)cmd; - _dylibInstallPath = strdup(dylibID->name()); - _dylibTimeStamp = dylibID->timestamp(); - _dylibCurrentVersion = dylibID->current_version(); - _dylibCompatibilityVersion = dylibID->compatibility_version(); - _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + if ( dylibID->name_offset() > cmdLength ) + throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID->name_offset(), cmdLength); + if ( (dylibID->name_offset() + strlen(dylibID->name()) + 1) > cmdLength ) + throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command"); + this->_dylibInstallPath = strdup(dylibID->name()); + this->_dylibTimeStamp = dylibID->timestamp(); + this->_dylibCurrentVersion = dylibID->current_version(); + this->_dylibCompatibilityVersion = dylibID->compatibility_version(); + this->_hasPublicInstallName = this->isPublicLocation(this->_dylibInstallPath); break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: ++dependentLibCount; break; case LC_REEXPORT_DYLIB: - _explictReExportFound = true; + this->_explictReExportFound = true; ++dependentLibCount; break; case LC_SUB_FRAMEWORK: - _parentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + this->_parentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); break; case LC_SUB_CLIENT: - _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); + this->_allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); // Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked - _hasPublicInstallName = false; + this->_hasPublicInstallName = false; break; case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: @@ -358,9 +191,9 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, #if SUPPORT_APPLE_TV case LC_VERSION_MIN_TVOS: #endif - _minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); - _platformInDylib = cmd->cmd(); - lcPlatform = Options::platformForLoadCommand(_platformInDylib); + this->_minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); + this->_platformInDylib = cmd->cmd(); + lcPlatform = Options::platformForLoadCommand(this->_platformInDylib); break; case LC_CODE_SIGNATURE: break; @@ -384,17 +217,17 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( (sect->size() >= 8) && (contents[0] == 0) ) { uint32_t flags = E::get32(contents[1]); if ( (flags & 4) == 4 ) - _objcContraint = ld::File::objcConstraintGC; + this->_objcConstraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) - _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + this->_objcConstraint = ld::File::objcConstraintRetainReleaseOrGC; else if ( (flags & 32) == 32 ) - _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; + this->_objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else - _objcContraint = ld::File::objcConstraintRetainRelease; - _swiftVersion = ((flags >> 8) & 0xFF); + this->_objcConstraint = ld::File::objcConstraintRetainRelease; + this->_swiftVersion = ((flags >> 8) & 0xFF); } else if ( sect->size() > 0 ) { - warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); + warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), path); } } } @@ -404,12 +237,15 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, else if ( strcmp(((macho_segment_command

*)cmd)->segname(), "__LLVM") == 0 ) { const macho_section

* const sect = (macho_section

*)((char*)cmd + sizeof(macho_segment_command

)); if ( strncmp(sect->sectname(), "__bundle", 8) == 0 ) - _bitcode = std::unique_ptr(new ld::Bitcode(NULL, sect->size())); + this->_bitcode = std::unique_ptr(new ld::Bitcode(NULL, sect->size())); + } + else if ( strcmp(((macho_segment_command

*)cmd)->segname(), "__LINKEDIT") == 0 ) { + _linkeditStartOffset = ((macho_segment_command

*)cmd)->fileoff(); } } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + cmd = (const macho_load_command

*)(((char*)cmd)+cmdLength); if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); } // arm/arm64 objects are default to ios platform if not set. // rdar://problem/21746314 @@ -419,10 +255,10 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // check cross-linking if ( lcPlatform != platform ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) { + this->_wrongOS = true; + if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) { if ( buildingForSimulator ) { - if ( !_allowSimToMacOSXLinking ) { + if ( !this->_allowSimToMacOSXLinking ) { switch (platform) { case Options::kPlatformOSX: case Options::kPlatformiOS: @@ -430,10 +266,16 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; // fall through if the Platform is not Unknown case Options::kPlatformWatchOS: - // WatchOS errors on cross-linking all the time. - throwf("building for %s simulator, but linking against dylib built for %s,", + // WatchOS errors on cross-linking when building for bitcode + if ( usingBitcode ) + throwf("building for %s simulator, but linking against dylib built for %s,", Options::platformName(platform), Options::platformName(lcPlatform)); + else + warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path, + Options::platformName(lcPlatform)); break; #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: @@ -445,7 +287,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, else warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " "Note: This will be an error in the future.", - Options::platformName(platform), path(), + Options::platformName(platform), path, Options::platformName(lcPlatform)); break; #endif @@ -463,22 +305,28 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; // fall through if the Platform is not Unknown case Options::kPlatformWatchOS: - // WatchOS errors on cross-linking all the time. - throwf("building for %s, but linking against dylib built for %s,", + // WatchOS errors on cross-linking when building for bitcode + if ( usingBitcode ) + throwf("building for %s, but linking against dylib built for %s,", Options::platformName(platform), Options::platformName(lcPlatform)); + else + warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path, + Options::platformName(lcPlatform)); break; #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: // tvOS is a warning temporarily. rdar://problem/21746965 - if ( _usingBitcode ) + if ( usingBitcode ) throwf("building for %s, but linking against dylib built for %s,", Options::platformName(platform), Options::platformName(lcPlatform)); else warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " "Note: This will be an error in the future.", - Options::platformName(platform), path(), + Options::platformName(platform), path, Options::platformName(lcPlatform)); break; #endif @@ -493,15 +341,17 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // figure out if we need to examine dependent dylibs // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted bool processDependentLibraries = true; - if ( compressedLinkEdit && _noRexports && !linkingFlatNamespace) + if ( compressedLinkEdit && this->_noRexports && !linkingFlatNamespace) processDependentLibraries = false; if ( processDependentLibraries ) { // pass 2 builds list of all dependent libraries - _dependentDylibs.reserve(dependentLibCount); + this->_dependentDylibs.reserve(dependentLibCount); cmd = cmds; unsigned int reExportDylibCount = 0; for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize(); + const macho_dylib_command

* dylibCmd = (macho_dylib_command

*)cmd; switch (cmd->cmd()) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: @@ -510,20 +360,22 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; case LC_REEXPORT_DYLIB: ++reExportDylibCount; - Dependent entry; - entry.path = strdup(((macho_dylib_command

*)cmd)->name()); - entry.dylib = NULL; - entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); - if ( (targetInstallPath == NULL) || (strcmp(targetInstallPath, entry.path) != 0) ) - _dependentDylibs.push_back(entry); + if ( dylibCmd->name_offset() > cmdLength ) + throwf("malformed mach-o: LC_*_DYLIB load command has offset (%u) outside its size (%u)", dylibCmd->name_offset(), cmdLength); + if ( (dylibCmd->name_offset() + strlen(dylibCmd->name()) + 1) > cmdLength ) + throwf("malformed mach-o: LC_*_DYLIB load command string extends beyond end of load command"); + const char *path = strdup(dylibCmd->name()); + bool reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) ) + this->_dependentDylibs.emplace_back(path, reExport); break; } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + cmd = (const macho_load_command

*)(((char*)cmd)+cmdLength); } // verify MH_NO_REEXPORTED_DYLIBS bit was correct if ( compressedLinkEdit && !linkingFlatNamespace ) { if ( reExportDylibCount == 0 ) - throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", pth); + throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", path); } // pass 3 add re-export info cmd = cmds; @@ -533,44 +385,65 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, switch (cmd->cmd()) { case LC_SUB_UMBRELLA: frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - const char* dylibName = it->path; + for (auto &dep : this->_dependentDylibs) { + const char* dylibName = dep.path; const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - it->reExport = true; + if ( (lastSlash != nullptr) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + dep.reExport = true; } break; case LC_SUB_LIBRARY: dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - const char* dylibName = it->path; + for (auto &dep : this->_dependentDylibs) { + const char* dylibName = dep.path; const char* lastSlash = strrchr(dylibName, '/'); const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) + if ( lastSlash == nullptr ) leafStart = dylibName; const char* firstDot = strchr(leafStart, '.'); int len = strlen(leafStart); - if ( firstDot != NULL ) + if ( firstDot != nullptr ) len = firstDot - leafStart; if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - it->reExport = true; + dep.reExport = true; } break; } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); } } - + + // if framework, capture framework name + if ( this->_dylibInstallPath != NULL ) { + const char* lastSlash = strrchr(this->_dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + const char* leafName = lastSlash+1; + char frname[strlen(leafName)+32]; + strcpy(frname, leafName); + strcat(frname, ".framework/"); + + if ( strstr(this->_dylibInstallPath, frname) != NULL ) + this->_frameworkName = leafName; + } + } + // validate minimal load commands - if ( (_dylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) - throwf("dylib %s missing LC_ID_DYLIB load command", pth); - if ( dyldInfo == NULL ) { - if ( symbolTable == NULL ) + if ( (this->_dylibInstallPath == nullptr) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", path); + if ( dyldInfo == nullptr ) { + if ( symbolTable == nullptr ) throw "binary missing LC_SYMTAB load command"; - if ( dynamicInfo == NULL ) + if ( dynamicInfo == nullptr ) throw "binary missing LC_DYSYMTAB load command"; } - + + if ( symtab != nullptr ) { + if ( symtab->symoff() < _linkeditStartOffset ) + throwf("malformed mach-o, symbol table not in __LINKEDIT"); + if ( symtab->stroff() < _linkeditStartOffset ) + throwf("malformed mach-o, symbol table strings not in __LINKEDIT"); + } + // if linking flat and this is a flat dylib, create one atom that references all imported symbols if ( linkingFlatNamespace && linkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { std::vector importNames; @@ -580,11 +453,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, for (const macho_nlist

* sym=start; sym < end; ++sym) { importNames.push_back(&strings[sym->n_strx()]); } - _importAtom = new ImportAtom(*this, importNames); + this->_importAtom = new generic::dylib::ImportAtom(*this, importNames); } // build hash table - if ( dyldInfo != NULL ) + if ( dyldInfo != nullptr ) buildExportHashTableFromExportInfo(dyldInfo, fileContent); else buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); @@ -593,49 +466,27 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } - -// -// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz -// -template -uint32_t File::parseVersionNumber32(const char* versionString) -{ - uint32_t x = 0; - uint32_t y = 0; - uint32_t z = 0; - char* end; - x = strtoul(versionString, &end, 10); - if ( *end == '.' ) { - y = strtoul(&end[1], &end, 10); - if ( *end == '.' ) { - z = strtoul(&end[1], &end, 10); - } - } - if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed 32-bit x.y.z version number: %s", versionString); - - return (x << 16) | ( y << 8 ) | z; -} - template -void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, - const macho_nlist

* symbolTable, const char* strings, - const uint8_t* fileContent) +void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, + const char* strings, const uint8_t* fileContent) { if ( dynamicInfo->tocoff() == 0 ) { - if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); + if ( this->_s_logHashtable ) + fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; - _atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count + this->_atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count for (const macho_nlist

* sym=start; sym < end; ++sym) { this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); } } else { int32_t count = dynamicInfo->ntoc(); - _atoms.reserve(count); // set initial bucket count - if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); + this->_atoms.reserve(count); // set initial bucket count + if ( this->_s_logHashtable ) + fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); + const auto* toc = reinterpret_cast(fileContent + dynamicInfo->tocoff()); for (int32_t i = 0; i < count; ++i) { const uint32_t index = E::get32(toc[i].symbol_index); const macho_nlist

* sym = &symbolTable[index]; @@ -644,26 +495,29 @@ void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

_dylibInstallPath != nullptr) && (strcmp(this->_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) addDyldFastStub(); } template -void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, - const uint8_t* fileContent) +void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent) { - if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); + if ( this->_s_logHashtable ) + fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); if ( dyldInfo->export_size() > 0 ) { const uint8_t* start = fileContent + dyldInfo->export_off(); const uint8_t* end = &start[dyldInfo->export_size()]; + if ( (dyldInfo->export_off() + dyldInfo->export_size()) > _fileLength ) + throwf("malformed mach-o dylib, exports trie extends beyond end of file, "); std::vector list; parseTrie(start, end, list); - for (std::vector::iterator it=list.begin(); it != list.end(); ++it) - this->addSymbol(it->name, - it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, - (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, - it->address); + for (const auto &entry : list) + this->addSymbol(entry.name, + entry.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, + (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, + entry.address); } } @@ -671,13 +525,13 @@ void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

void File::addDyldFastStub() { - addSymbol("dyld_stub_binder", false, false, 0); + addSymbol("dyld_stub_binder"); } template <> void File::addDyldFastStub() { - addSymbol("dyld_stub_binder", false, false, 0); + addSymbol("dyld_stub_binder"); } template @@ -686,341 +540,34 @@ void File::addDyldFastStub() // do nothing } -template -void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) -{ - //fprintf(stderr, "addSymbol() %s\n", name); - // symbols that start with $ld$ are meta-data to the static linker - // need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { - // $ld$ $ $ - const char* symAction = &name[4]; - const char* symCond = strchr(symAction, '$'); - if ( symCond != NULL ) { - char curOSVers[16]; - sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); - if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( _s_logHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); - _ignoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef, false, 0); - return; - } - else if ( strncmp(symAction, "install_name$", 13) == 0 ) { - _dylibInstallPath = symName; - _installPathOverride = true; - // CoreGraphics redirects to ApplicationServices, but with wrong compat version - if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) - _dylibCompatibilityVersion = parseVersionNumber32("1.0"); - return; - } - else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { - _dylibCompatibilityVersion = parseVersionNumber32(symName); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->path()); - } - } - } - } - else { - warning("bad symbol condition: %s in dylib %s", name, this->path()); - } - } - - // add symbol as possible export if we are not supposed to ignore it - if ( _ignoreExports.count(name) == 0 ) { - AtomAndWeak bucket; - bucket.atom = NULL; - 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()); - _atoms[strdup(name)] = bucket; - } -} - - -template -bool File::forEachAtom(ld::File::AtomHandler& handler) const -{ - handler.doFile(*this); - // if doing flatnamespace and need all this dylib's imports resolve - // add atom which references alls undefines in this dylib - if ( _importAtom != NULL ) { - handler.doAtom(*_importAtom); - return true; - } - return false; -} - - -template -std::pair File::hasWeakDefinitionImpl(const char* name) const -{ - const auto pos = _atoms.find(name); - if ( pos != _atoms.end() ) - return std::make_pair(true, pos->second.weakDef); - - // look in children that I re-export - for (const auto &dep : _dependentDylibs) { - if ( dep.reExport ) { - auto ret = dep.dylib->hasWeakDefinitionImpl(name); - if ( ret.first ) - return ret; - } - } - return std::make_pair(false, false); -} - - -template -bool File::hasWeakDefinition(const char* name) const -{ - // if supposed to ignore this export, then pretend I don't have it - if ( _ignoreExports.count(name) != 0 ) - return false; - - return hasWeakDefinitionImpl(name).second; -} - - -// 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, uint64_t& defAddress) const -{ - if ( _ignoreExports.count(name) != 0 ) - return false; - - // check myself - const auto pos = _atoms.find(name); - if ( pos != _atoms.end() ) { - weakDef = pos->second.weakDef; - tlv = pos->second.tlv; - defAddress = pos->second.address; - return true; - } - - // check dylibs I re-export - for (const auto &dep : _dependentDylibs) { - if ( dep.reExport && !dep.dylib->implicitlyLinked() ) { - if ( dep.dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) - return true; - } - } - - return false; -} - - -template -bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const -{ - // if supposed to ignore this export, then pretend I don't have it - if ( _ignoreExports.count(name) != 0 ) - return false; - - - AtomAndWeak bucket; - 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()); - // call handler with new export atom - handler.doAtom(*bucket.atom); - return true; - } - - return false; -} - - - -template -bool File::isPublicLocation(const char* pth) -{ - // -no_implicit_dylibs disables this optimization - if ( ! _implicitlyLinkPublicDylibs ) - return false; - - // /usr/lib is a public location - if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) - return true; - - // /System/Library/Frameworks/ is a public location - if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { - const char* frameworkDot = strchr(&pth[27], '.'); - // but only top level framework - // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true - // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false - if ( frameworkDot != NULL ) { - int frameworkNameLen = frameworkDot - &pth[27]; - if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) - return true; - } - } - - return false; -} - -template -void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) -{ - // only do this once - if ( _indirectDylibsProcessed ) - return; - const static bool log = false; - if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); - if ( _linkingFlat ) { - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { - it->dylib = (File*)handler->findDylib(it->path, this->path()); - } - } - else if ( _noRexports ) { - // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do - } - else { - // two-level, might have re-exports - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { - if ( it->reExport ) { - if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); - // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - it->dylib = (File*)handler->findDylib(it->path, this->path()); - if ( it->dylib->hasPublicInstallName() && !it->dylib->wrongOS() ) { - // promote this child to be automatically added as a direct dependent if this already is - if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { - if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); - it->dylib->setImplicitlyLinked(); - } - else if ( it->dylib->explicitlyLinked() || it->dylib->implicitlyLinked() ) { - if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); - } - else { - if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), it->path); - } - } - else { - // add all child's symbols to me - if ( log ) fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), it->path); - } - } - else if ( !_explictReExportFound ) { - // see if child contains LC_SUB_FRAMEWORK with my name - it->dylib = (File*)handler->findDylib(it->path, this->path()); - const char* parentUmbrellaName = it->dylib->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - const char* parentName = this->path(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { - // add all child's symbols to me - it->reExport = true; - if ( log ) fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), it->path); - } - } - } - } - } - - // check for re-export cycles - ReExportChain chain; - chain.prev = NULL; - chain.file = this; - this->assertNoReExportCycles(&chain); - - _indirectDylibsProcessed = true; -} - -template -void File::assertNoReExportCycles(ReExportChain* prev) const -{ - // recursively check my re-exported dylibs - ReExportChain chain; - chain.prev = prev; - chain.file = this; - for (const auto &dep : _dependentDylibs) { - if ( dep.reExport ) { - ld::File* child = dep.dylib; - // check child is not already in chain - for (ReExportChain* p = prev; p != nullptr; p = p->prev) { - if ( p->file == child ) { - throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); - } - } - if ( dep.dylib != nullptr ) - dep.dylib->assertNoReExportCycles(&chain); - } - } -} - - template class Parser { public: - typedef typename A::P P; - - static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); - static const char* fileKind(const uint8_t* fileContent); - static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t mTime, - ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { - return new File(fileContent, fileLength, path, mTime, - ordinal, opts.flatNamespace(), - opts.linkingMainExecutable(), - opts.implicitlyLinkIndirectPublicDylibs(), - opts.platform(), - opts.minOSversion(), - opts.allowSimulatorToLinkWithMacOSX(), - opts.addVersionLoadCommand(), - opts.targetIOSSimulator(), - opts.logAllFiles(), - opts.installPath(), - indirectDylib, - opts.outputKind() == Options::kPreload, - opts.bundleBitcode()); - } + using P = typename A::P; + + static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch=false, uint32_t subType=0); + static const char* fileKind(const uint8_t* fileContent); + static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, ld::File::Ordinal ordinal, const Options& opts, + bool indirectDylib) + { + return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), + opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), + opts.platform(), opts.minOSversion(), + opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), + opts.targetIOSSimulator(), opts.logAllFiles(), opts.installPath(), + indirectDylib, opts.outputKind() == Options::kPreload, opts.bundleBitcode()); + } }; template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) return false; if ( header->cputype() != CPU_TYPE_I386 ) @@ -1045,9 +592,9 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor } template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) return false; if ( header->cputype() != CPU_TYPE_X86_64 ) @@ -1072,13 +619,15 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyli } template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) return false; if ( header->cputype() != CPU_TYPE_ARM ) return false; + if ( subTypeMustMatch && (header->cpusubtype() != subType) ) + return false; switch ( header->filetype() ) { case MH_DYLIB: case MH_DYLIB_STUB: @@ -1101,9 +650,9 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) return false; if ( header->cputype() != CPU_TYPE_ARM64 ) @@ -1132,7 +681,7 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* { if ( Parser::validFile(fileContent, false) ) { *result = CPU_TYPE_X86_64; - const macho_header >* header = (const macho_header >*)fileContent; + const auto* header = reinterpret_cast>*>(fileContent); *subResult = header->cpusubtype(); return true; } @@ -1143,7 +692,7 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* } if ( Parser::validFile(fileContent, false) ) { *result = CPU_TYPE_ARM; - const macho_header >* header = (const macho_header >*)fileContent; + const auto* header = reinterpret_cast>*>(fileContent); *subResult = header->cpusubtype(); return true; } @@ -1158,34 +707,34 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_I386 ) - return NULL; + return nullptr; return "i386"; } template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_X86_64 ) - return NULL; + return nullptr; return "x86_64"; } template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_ARM ) - return NULL; - for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + return nullptr; + for (const auto* t = archInfoArray; t->archName != nullptr; ++t) { if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) { return t->archName; } @@ -1197,11 +746,11 @@ const char* Parser::fileKind(const uint8_t* fileContent) template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_ARM64 ) - return NULL; + return nullptr; return "arm64"; } #endif @@ -1225,48 +774,47 @@ const char* archName(const uint8_t* fileContent) return Parser::fileKind(fileContent); } #endif - return NULL; + return nullptr; } // // 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, ld::File::Ordinal ordinal, - bool bundleLoader, bool indirectDylib) +ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib) { + bool subTypeMustMatch = opts.enforceDylibSubtypesMatch(); switch ( opts.architecture() ) { #if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif } - return NULL; + return nullptr; } }; // namespace dylib }; // namespace mach_o - - diff --git a/ld64/src/ld/parsers/macho_dylib_file.h b/ld64/src/ld/parsers/macho_dylib_file.h index 4d093f1..ba7b8ff 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.h +++ b/ld64/src/ld/parsers/macho_dylib_file.h @@ -37,9 +37,9 @@ extern bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subt extern const char* archName(const uint8_t* fileContent); -extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, const Options& opts, ld::File::Ordinal ordinal, - bool bundleLoader, bool indirectDylib); +extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib); } // namespace dylib } // namespace mach_o diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 2fa41b9..ddf687a 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -1089,7 +1089,8 @@ class Parser TargetDesc& target); uint32_t tentativeDefinitionCount() { return _tentativeDefinitionCount; } uint32_t absoluteSymbolCount() { return _absoluteSymbolCount; } - + + uint32_t fileLength() const { return _fileLength; } bool hasStubsSection() { return (_stubsSectionNum != 0); } unsigned int stubsSectionNum() { return _stubsSectionNum; } void addDtraceExtraInfos(const SourceLocation& src, const char* provider); @@ -1101,6 +1102,8 @@ class Parser bool verboseOptimizationHints() { return _verboseOptimizationHints; } bool neverConvertDwarf() { return _neverConvertDwarf; } bool armUsesZeroCostExceptions() { return _armUsesZeroCostExceptions; } + uint8_t maxDefaultCommonAlignment() { return _maxDefaultCommonAlignment; } + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } @@ -1256,6 +1259,7 @@ class Parser bool _ignoreMismatchPlatform; bool _treateBitcodeAsData; bool _usingBitcode; + uint8_t _maxDefaultCommonAlignment; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1474,11 +1478,11 @@ bool Parser::getNonLocalSymbols(const uint8_t* fileContent, std::vectornsyms(); const macho_nlist

* symbols = (const macho_nlist

*)(fileContent + symtab->symoff()); const char* strings = (char*)fileContent + symtab->stroff(); - for (uint32_t i = 0; i < symbolCount; ++i) { + for (uint32_t j = 0; j < symbolCount; ++j) { // ignore stabs and count only ext symbols - if ( (symbols[i].n_type() & N_STAB) == 0 && - (symbols[i].n_type() & N_EXT) != 0 ) { - const char* symName = &strings[symbols[i].n_strx()]; + if ( (symbols[j].n_type() & N_STAB) == 0 && + (symbols[j].n_type() & N_EXT) != 0 ) { + const char* symName = &strings[symbols[j].n_strx()]; syms.push_back(symName); } } @@ -1726,6 +1730,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) printf("%s\n", _path); _armUsesZeroCostExceptions = opts.armUsesZeroCostExceptions; + _maxDefaultCommonAlignment = opts.maxDefaultCommonAlignment; // parse start of mach-o file if ( ! parseLoadCommands(opts.platform, opts.minOSVersion, opts.simulator, opts.ignoreMismatchPlatform) ) @@ -2077,12 +2082,13 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS lcPlatform = Options::platformForLoadCommand(cmd->cmd()); _file->_minOSVersion = ((macho_version_min_command

*)cmd)->version(); break; + case macho_segment_command

::CMD: + if ( segment != NULL ) + throw "more than one LC_SEGMENT found in object file"; + segment = (macho_segment_command

*)cmd; + break; default: - if ( cmd->cmd() == macho_segment_command

::CMD ) { - if ( segment != NULL ) - throw "more than one LC_SEGMENT found in object file"; - segment = (macho_segment_command

*)cmd; - } + // ignore unknown load commands break; } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); @@ -2105,10 +2111,16 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS break; // fall through if the Platform is not Unknown case Options::kPlatformWatchOS: - // WatchOS errors on cross-linking all the time. - throwf("building for %s%s, but linking in object file built for %s,", + // Error when using bitcocde, warning otherwise. + if (_usingBitcode) + throwf("building for %s%s, but linking in object file built for %s,", Options::platformName(platform), (simulator ? " simulator" : ""), Options::platformName(lcPlatform)); + else + warning("URGENT: building for %s%s, but linking in object file (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), (simulator ? " simulator" : ""), path(), + Options::platformName(lcPlatform)); break; #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: @@ -2145,7 +2157,8 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS throw "missing LC_SEGMENT"; _sectionsStart = (macho_section

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

)); _machOSectionsCount = segment->nsects(); - + if ( (sizeof(macho_segment_command

) + _machOSectionsCount * sizeof(macho_section

)) > segment->cmdsize() ) + throw "too many sections for size of LC_SEGMENT command"; return true; } @@ -2453,6 +2466,10 @@ void Parser::makeSections() for (uint32_t i=0; i < _machOSectionsCount; ++i) { const macho_section

* sect = &_sectionsStart[i]; + uint8_t sectionType = (sect->flags() & SECTION_TYPE); + if ( (sect->offset() + sect->size() > _fileLength) && (sectionType != S_ZEROFILL) && (sectionType != S_THREAD_LOCAL_ZEROFILL) ) + throwf("section %s/%s extends beyond end of file,", sect->segname(), sect->sectname()); + if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { if ( strcmp(sect->segname(), "__DWARF") == 0 ) { // note that .o file has dwarf @@ -3152,8 +3169,8 @@ uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uin ++alignP2; } // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignP2 > 15 ) - alignP2 = 15; + if ( alignP2 > parser.maxDefaultCommonAlignment() ) + alignP2 = parser.maxDefaultCommonAlignment(); Atom* allocatedSpace = (Atom*)p; new (allocatedSpace) Atom(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size, ld::Atom::definitionTentative, ld::Atom::combineByName, @@ -3950,6 +3967,7 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, const uint8_t * debug_info; const uint8_t * debug_abbrev; const uint8_t * di; + const uint8_t * next_cu; const uint8_t * da; const uint8_t * end; const uint8_t * enda; @@ -3967,108 +3985,122 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, if ( (_file->_dwarfDebugInfoSect == NULL) || (_file->_dwarfDebugAbbrevSect == NULL) ) return false; - debug_info = (uint8_t*)_file->fileContent() + _file->_dwarfDebugInfoSect->offset(); - debug_abbrev = (uint8_t*)_file->fileContent() + _file->_dwarfDebugAbbrevSect->offset(); - di = debug_info; - if (_file->_dwarfDebugInfoSect->size() < 12) - /* Too small to be a real debug_info section. */ - return false; - sz = A::P::E::get32(*(uint32_t*)di); - di += 4; - dwarf64 = sz == 0xffffffff; - if (dwarf64) - sz = A::P::E::get64(*(uint64_t*)di), di += 8; - else if (sz > 0xffffff00) - /* Unknown dwarf format. */ - return false; - - /* Verify claimed size. */ - if (sz + (di - debug_info) > _file->_dwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + /* Too small to be a real debug_info section. */ return false; - vers = A::P::E::get16(*(uint16_t*)di); - if (vers < 2 || vers > 4) - /* DWARF version wrong for this code. - Chances are we could continue anyway, but we don't know for sure. */ - return false; - di += 2; - - /* Find the debug_abbrev section. */ - abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); - di += dwarf64 ? 8 : 4; - - if (abbrev_base > _file->_dwarfDebugAbbrevSect->size()) - return false; - da = debug_abbrev + abbrev_base; - enda = debug_abbrev + _file->_dwarfDebugAbbrevSect->size(); - - address_size = *di++; - - /* Find the abbrev number we're looking for. */ - end = di + sz; - abbrev = read_uleb128 (&di, end); - if (abbrev == (uint64_t) -1) - return false; - - /* Skip through the debug_abbrev section looking for that abbrev. */ - for (;;) - { - uint64_t this_abbrev = read_uleb128 (&da, enda); - uint64_t attr; - - if (this_abbrev == abbrev) - /* This is almost always taken. */ - break; - skip_leb128 (&da, enda); /* Skip the tag. */ - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - do { - attr = read_uleb128 (&da, enda); - skip_leb128 (&da, enda); - } while (attr != 0 && attr != (uint64_t) -1); - if (attr != 0) - return false; - } - - /* Check that the abbrev is one for a DW_TAG_compile_unit. */ - if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) - return false; - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - /* Now, go through the DIE looking for DW_AT_name, - DW_AT_comp_dir, and DW_AT_stmt_list. */ - for (;;) - { - uint64_t attr = read_uleb128 (&da, enda); - uint64_t form = read_uleb128 (&da, enda); - - if (attr == (uint64_t) -1) - return false; - else if (attr == 0) - return true; - if (form == DW_FORM_indirect) - form = read_uleb128 (&di, end); - - switch (attr) { - case DW_AT_name: - *name = getDwarfString(form, di); - break; - case DW_AT_comp_dir: - *comp_dir = getDwarfString(form, di); - break; - case DW_AT_stmt_list: - *stmt_list = getDwarfOffset(form, di, dwarf64); - break; - default: - if (! skip_form (&di, end, form, address_size, dwarf64)) - return false; - } - } + debug_info = (uint8_t*)_file->fileContent() + _file->_dwarfDebugInfoSect->offset(); + debug_abbrev = (uint8_t*)_file->fileContent() + _file->_dwarfDebugAbbrevSect->offset(); + next_cu = debug_info; + + while ((uint64_t)(next_cu - debug_info) < _file->_dwarfDebugInfoSect->size()) { + di = next_cu; + sz = A::P::E::get32(*(uint32_t*)di); + di += 4; + dwarf64 = sz == 0xffffffff; + if (dwarf64) + sz = A::P::E::get64(*(uint64_t*)di), di += 8; + else if (sz > 0xffffff00) + /* Unknown dwarf format. */ + return false; + + /* Verify claimed size. */ + if (sz + (di - debug_info) > _file->_dwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + return false; + + next_cu = di + sz; + + vers = A::P::E::get16(*(uint16_t*)di); + if (vers < 2 || vers > 4) + /* DWARF version wrong for this code. + Chances are we could continue anyway, but we don't know for sure. */ + return false; + di += 2; + + /* Find the debug_abbrev section. */ + abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); + di += dwarf64 ? 8 : 4; + + if (abbrev_base > _file->_dwarfDebugAbbrevSect->size()) + return false; + da = debug_abbrev + abbrev_base; + enda = debug_abbrev + _file->_dwarfDebugAbbrevSect->size(); + + address_size = *di++; + + /* Find the abbrev number we're looking for. */ + end = di + sz; + abbrev = read_uleb128 (&di, end); + if (abbrev == (uint64_t) -1) + return false; + + /* Skip through the debug_abbrev section looking for that abbrev. */ + for (;;) + { + uint64_t this_abbrev = read_uleb128 (&da, enda); + uint64_t attr; + + if (this_abbrev == abbrev) + /* This is almost always taken. */ + break; + skip_leb128 (&da, enda); /* Skip the tag. */ + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + do { + attr = read_uleb128 (&da, enda); + skip_leb128 (&da, enda); + } while (attr != 0 && attr != (uint64_t) -1); + if (attr != 0) + return false; + } + + /* Check that the abbrev is one for a DW_TAG_compile_unit. */ + if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) + return false; + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + /* Now, go through the DIE looking for DW_AT_name, + DW_AT_comp_dir, and DW_AT_stmt_list. */ + bool skip_to_next_cu = false; + while (!skip_to_next_cu) { + + uint64_t attr = read_uleb128 (&da, enda); + uint64_t form = read_uleb128 (&da, enda); + + if (attr == (uint64_t) -1) + return false; + else if (attr == 0) + return true; + if (form == DW_FORM_indirect) + form = read_uleb128 (&di, end); + + switch (attr) { + case DW_AT_name: + *name = getDwarfString(form, di); + /* Swift object files may contain two CUs: One + describes the Swift code, one is created by the + clang importer. Skip over the CU created by the + clang importer as it may be empty. */ + if (std::string(*name) == "") + skip_to_next_cu = true; + break; + case DW_AT_comp_dir: + *comp_dir = getDwarfString(form, di); + break; + case DW_AT_stmt_list: + *stmt_list = getDwarfOffset(form, di, dwarf64); + break; + default: + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } + } + } + return false; } @@ -4366,8 +4398,9 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { + const uint32_t sectionSize = this->_machOSection->size(); // copy __eh_frame data to buffer - memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), sectionSize); // and apply relocations const macho_relocation_info

* relocs = (macho_relocation_info

*)(file().fileContent() + this->_machOSection->reloff()); @@ -4393,6 +4426,8 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address()); break; } + if ( reloc->r_address() > sectionSize ) + throwf("malformed __eh_frame relocation, offset (0x%08X) is beyond end of section,", reloc->r_address()); uint64_t* p64; uint32_t* p32; switch ( reloc->r_length() ) { @@ -4476,7 +4511,8 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // copy __eh_frame data to buffer - memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + const uint32_t sectionSize = this->_machOSection->size(); + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), sectionSize); // and apply relocations const macho_relocation_info

* relocs = (macho_relocation_info

*)(file().fileContent() + this->_machOSection->reloff()); @@ -4508,6 +4544,8 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address()); break; } + if ( reloc->r_address() > sectionSize ) + throwf("malformed __eh_frame relocation, offset (0x%08X) is beyond end of section,", reloc->r_address()); switch ( reloc->r_length() ) { case 3: E::set64(*p64, value + addend64); @@ -5070,9 +5108,14 @@ void CUSection::parse(class Parser& parser, uint32_t cnt, Info array[]) } // scan relocs, extern relocs are needed for personality references (possibly for function/lsda refs??) + const uint32_t sectionSize = this->_machOSection->size(); 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_address() & R_SCATTERED ) + continue; + if ( reloc->r_address() > sectionSize ) + throwf("malformed __compact_unwind relocation, offset (0x%08X) is beyond end of section,", reloc->r_address()); 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() ) { @@ -6309,10 +6352,14 @@ bool Section::addRelocFixup(class Parser& parser, const macho_re } else { parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget); - useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit); + useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit) || ((toTarget.atom->combine() == ld::Atom::combineByNameAndContent) || (toTarget.atom->combine() == ld::Atom::combineByNameAndReferences)); + } + if ( useDirectBinding ) { + if ( (toTarget.atom->combine() == ld::Atom::combineByNameAndContent) || (toTarget.atom->combine() == ld::Atom::combineByNameAndReferences) ) + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, toTarget.atom); + else + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); } - if ( useDirectBinding ) - parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); else parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name); parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend); @@ -7519,6 +7566,8 @@ template void Section::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) { const macho_section

* sect = this->machoSection(); + if ( sect->reloff() + (sect->nreloc() * sizeof(macho_relocation_info

)) > parser.fileLength() ) + throwf("relocations for section %s/%s extends beyond end of file,", sect->segname(), Section::makeSectionName(sect) ); const macho_relocation_info

* relocs = (macho_relocation_info

*)(file().fileContent() + sect->reloff()); const uint32_t relocCount = sect->nreloc(); for (uint32_t r = 0; r < relocCount; ++r) { @@ -7558,7 +7607,8 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI Atom* end = &_endAtoms[-1]; for(Atom* p = _beginAtoms; p < end; ++p) { Atom* nextAtom = &p[1]; - if ( _altEntries.count(nextAtom) != 0 ) { + // support alt_entry aliases (alias process already added followOn, don't repeat) + if ( (_altEntries.count(nextAtom) != 0) && (p->_objAddress != nextAtom->_objAddress) ) { typename Parser::SourceLocation src(p, 0); parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); typename Parser::SourceLocation src2(nextAtom, 0); diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index ca1e5cc..e31f7f9 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -49,6 +49,7 @@ struct ParserOptions { ld::relocatable::File::SourceKind srcKind; bool treateBitcodeAsData; bool usingBitcode; + uint8_t maxDefaultCommonAlignment; }; extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index 825cb05..fb452d8 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -32,6 +32,7 @@ #include "bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" +#include "generic_dylib_file.hpp" #include "textstub_dylib_file.hpp" namespace { @@ -65,7 +66,7 @@ class Token { size_t size() const { return _size; } - std::string str() const { return std::move(std::string(_p, _size)); } + std::string str() const { return std::string(_p, _size); } bool empty() const { return _size == 0; } @@ -195,33 +196,6 @@ struct DynamicLibrary { _objcConstraint(ld::File::objcConstraintNone) {} }; -static uint32_t parseVersionNumber32(Token token) { - if ( token.size() >= 128 ) - throwf("malformed version number"); - - char buffer[128]; - uint32_t x = 0; - uint32_t y = 0; - uint32_t z = 0; - char* end; - - // Make a null-terminated string. - ::memcpy(buffer, token.data(), token.size()); - buffer[token.size()] = '\0'; - - x = strtoul(buffer, &end, 10); - if ( *end == '.' ) { - y = strtoul(&end[1], &end, 10); - if ( *end == '.' ) { - z = strtoul(&end[1], &end, 10); - } - } - if ( (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed 32-bit x.y.z version number: %s", buffer); - - return (x << 16) | ( y << 8 ) | z; -} - /// /// A simple text-based dynamic library file parser. /// @@ -310,20 +284,24 @@ class TBDFile { } } - bool parseArchFlowSequence(Token archName) { + std::vector parseArchFlowSequence() { + std::vector availabledArchitectures; expectToken("archs"); + parseFlowSequence([&](Token name) { + availabledArchitectures.emplace_back(name.str()); + }); + return availabledArchitectures; + } - // x86_64h fails to link against text based stubs - if ( archName == "x86_64h" ) - archName = "x86_64"; + bool parseArchFlowSequence(std::string &selectedArchName) { + auto availabledArchitectures = parseArchFlowSequence(); - bool foundArch = false; - parseFlowSequence([&](Token name) { - if ( name == archName ) - foundArch = true; - }); + for (const auto &archName : availabledArchitectures) { + if (archName == selectedArchName) + return true; + } - return foundArch; + return false; } void parsePlatform(DynamicLibrary& lib) { @@ -352,6 +330,18 @@ class TBDFile { throwf("no install name specified"); } + uint32_t parseVersionNumber32(Token token) { + if ( token.size() >= 128 ) + throwf("malformed version number"); + + // Make a null-terminated string. + char buffer[128]; + ::memcpy(buffer, token.data(), token.size()); + buffer[token.size()] = '\0'; + + return Options::parseVersionNumber32(buffer); + } + void parseCurrentVersion(DynamicLibrary& lib) { if ( !hasOptionalToken("current-version") ) return; @@ -395,7 +385,7 @@ class TBDFile { else throwf("unexpected token: %s", token.str().c_str()); } - void parseExportsBlock(DynamicLibrary& lib, Token archName) { + void parseExportsBlock(DynamicLibrary& lib, std::string &selectedArchName) { if ( !hasOptionalToken("exports") ) return; @@ -403,7 +393,7 @@ class TBDFile { return; while ( true ) { - if ( !parseArchFlowSequence(archName) ) { + if ( !parseArchFlowSequence(selectedArchName) ) { Token token; while ( true ) { token = peek(); @@ -425,8 +415,47 @@ class TBDFile { } } - void parseDocument(DynamicLibrary& lib, Token archName) { - if ( !parseArchFlowSequence(archName) ) + std::vector getCompatibleArchList(std::string &requestedArchName) { + if (requestedArchName == "i386") + return {"i386"}; + else if (requestedArchName == "x86_64" || requestedArchName == "x86_64h") + return {"x86_64", "x86_64h"}; + else if (requestedArchName == "armv7" || requestedArchName == "armv7s") + return {"armv7", "armv7s"}; + else if (requestedArchName == "armv7k") + return {"armv7k"}; + else if (requestedArchName == "arm64") + return {"arm64"}; + else + return {}; + } + + std::string parseAndSelectArchitecture(std::string &requestedArchName) { + auto availabledArchitectures = parseArchFlowSequence(); + + // First try to find an exact match (cpu type and sub-cpu type). + if (std::find(availabledArchitectures.begin(), availabledArchitectures.end(), requestedArchName) + != availabledArchitectures.end()) + return requestedArchName; + + // If there is no exact match, then try to find an ABI compatible slice. + auto compatibleArchitectures = getCompatibleArchList(requestedArchName); + std::vector result; + std::sort(availabledArchitectures.begin(), availabledArchitectures.end()); + std::sort(compatibleArchitectures.begin(), compatibleArchitectures.end()); + std::set_intersection(availabledArchitectures.begin(), availabledArchitectures.end(), + compatibleArchitectures.begin(), compatibleArchitectures.end(), + std::back_inserter(result)); + + if (result.empty()) + return std::string(); + else + return result.front(); + } + + void parseDocument(DynamicLibrary& lib, std::string &requestedArchName) { + auto selectedArchName = parseAndSelectArchitecture(requestedArchName); + if (selectedArchName.empty()) throwf("invalid arch"); parsePlatform(lib); @@ -435,27 +464,27 @@ class TBDFile { parseCompatibilityVersion(lib); parseSwiftVersion(lib); parseObjCConstraint(lib); - parseExportsBlock(lib, archName); + parseExportsBlock(lib, selectedArchName); } public: TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {} - DynamicLibrary parseFileForArch(Token archName) { + DynamicLibrary parseFileForArch(std::string requestedArchName) { _tokenizer.reset(); DynamicLibrary lib; expectToken("---"); - parseDocument(lib, archName); + parseDocument(lib, requestedArchName); expectToken("..."); - return std::move(lib); + return lib; } - bool validForArch(Token archName) { + bool validForArch(std::string requestedArchName) { _tokenizer.reset(); auto token = next(); if ( token != "---" ) return false; - return parseArchFlowSequence(archName); + return !parseAndSelectArchitecture(requestedArchName).empty(); } void dumpTokens() { @@ -473,52 +502,16 @@ class TBDFile { namespace textstub { namespace dylib { -// forward reference -template class File; - - -// -// An ExportAtom has no content. It exists so that the linker can track which imported -// symbols came from which dynamic libraries. -// -template -class ExportAtom : public ld::Atom -{ -public: - ExportAtom(const File& f, const char* nm, bool weakDef, bool tlv) - : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, - (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), - ld::Atom::scopeLinkageUnit, - (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), - _file(f), _name(nm) {} - // overrides of ld::Atom - virtual const ld::File* file() const { return &_file; } - virtual const char* name() const { return _name; } - virtual uint64_t size() const { return 0; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - -protected: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - - virtual ~ExportAtom() {} - - const File& _file; - const char* _name; -}; - - // // The reader for a dylib extracts all exported symbols names from the memory-mapped // dylib, builds a hash table, then unmaps the file. This is an important memory // savings for large dylibs. // template -class File : public ld::dylib::File +class File final : public generic::dylib::File { + using Base = generic::dylib::File; + public: static bool validFile(const uint8_t* fileContent, bool executableOrDylib); File(const uint8_t* fileContent, uint64_t fileLength, const char* path, @@ -527,91 +520,13 @@ class File : public ld::dylib::File cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, bool indirectDylib); - virtual ~File() {} - - // overrides of ld::File - virtual bool forEachAtom(ld::File::AtomHandler&) const; - virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; - virtual ld::File::ObjcConstraint objCConstraint() const { return _objcConstraint; } - virtual uint8_t swiftVersion() const { return _swiftVersion; } - - // overrides of ld::dylib::File - virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); - virtual bool providedExportAtom() const { return _providedAtom; } - virtual const char* parentUmbrella() const { return nullptr; } - virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : nullptr; } - virtual bool hasWeakExternals() const { return _hasWeakExports; } - virtual bool deadStrippable() const { return false; } - virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } - virtual bool hasWeakDefinition(const char* name) const; - virtual bool allSymbolsAreWeakImported() const; - virtual bool installPathVersionSpecific() const { return _installPathOverride; } - // All text-based stubs are per definition AppExtensionSafe. - virtual bool appExtensionSafe() const { return true; }; - virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } - - -protected: - virtual void assertNoReExportCycles(ReExportChain*) const; + virtual ~File() noexcept {} private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - friend class ExportAtom; - - struct CStringHash { - std::size_t operator()(const char* __s) const { - unsigned long __h = 0; - for ( ; *__s; ++__s) - __h = 5 * __h + *__s; - return size_t(__h); - }; - }; - struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; }; - typedef std::unordered_map NameToAtomMap; - typedef std::unordered_set NameSet; - - struct Dependent { const char* path; File* dylib; }; - - virtual std::pair hasWeakDefinitionImpl(const char* name) const; - virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& address) const; - - void buildExportHashTable(const DynamicLibrary &lib); - bool isPublicLocation(const char* pth); - bool wrongOS() { return _wrongOS; } - void addSymbol(const char* name, bool weak, bool tlv); - - const Options::Platform _platform; - cpu_type_t _cpuType; - const uint32_t _linkMinOSVersion; - const bool _allowSimToMacOSXLinking; - const bool _addVersionLoadCommand; - bool _linkingFlat; - bool _implicitlyLinkPublicDylibs; - ld::File::ObjcConstraint _objcConstraint; - uint8_t _swiftVersion; - ld::Section _importProxySection; - ld::Section _flatDummySection; - std::vector _dependentDylibs; - std::vector _allowableClients; - mutable NameToAtomMap _atoms; - NameSet _ignoreExports; - bool _noRexports; - bool _hasWeakExports; - bool _hasPublicInstallName; - mutable bool _providedAtom; - bool _wrongOS; - bool _installPathOverride; - bool _indirectDylibsProcessed; - std::unique_ptr _bitcode; - static bool _s_logHashtable; -}; - -template -bool File::_s_logHashtable = false; + void buildExportHashTable(const DynamicLibrary &lib); + cpu_type_t _cpuType; +}; template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, @@ -620,18 +535,14 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath, bool indirectDylib) - : ld::dylib::File(strdup(path), mTime, ord), _platform(platform), _cpuType(cpuType), - _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), - _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), - _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), - _objcConstraint(ld::File::objcConstraintNone), _swiftVersion(0), - _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), - _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), - _noRexports(false), _hasWeakExports(false), - _hasPublicInstallName(false), _providedAtom(false), _wrongOS(false), - _installPathOverride(false), _indirectDylibsProcessed(false), - _bitcode(new ld::Bitcode(nullptr, 0)) + : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace, + hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), + _cpuType(cpuType) { + this->_bitcode = std::unique_ptr(new ld::Bitcode(nullptr, 0)); + // Text stubs are implicit app extension safe. + this->_appExtensionSafe = true; + // write out path for -t option if ( logAllFiles ) printf("%s\n", path); @@ -639,27 +550,66 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, TBDFile stub((const char*)fileContent, fileLength); auto lib = stub.parseFileForArch(archName); - _noRexports = lib._reexportedLibraries.empty(); - _hasWeakExports = !lib._weakDefSymbols.empty(); - _dylibInstallPath = strdup(lib._installName.str().c_str()); - _dylibCurrentVersion = lib._currentVersion; - _dylibCompatibilityVersion = lib._compatibilityVersion; - _swiftVersion = lib._swiftVersion; - _objcConstraint = lib._objcConstraint; - _hasPublicInstallName = isPublicLocation(_dylibInstallPath); - - for (auto &client : lib._allowedClients) - _allowableClients.push_back(strdup(client.str().c_str())); + this->_noRexports = lib._reexportedLibraries.empty(); + this->_hasWeakExports = !lib._weakDefSymbols.empty(); + this->_dylibInstallPath = strdup(lib._installName.str().c_str()); + this->_dylibCurrentVersion = lib._currentVersion; + this->_dylibCompatibilityVersion = lib._compatibilityVersion; + this->_swiftVersion = lib._swiftVersion; + this->_objcConstraint = lib._objcConstraint; + this->_hasPublicInstallName = this->isPublicLocation(this->_dylibInstallPath); + + // if framework, capture framework name + const char* lastSlash = strrchr(this->_dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + const char* leafName = lastSlash+1; + char frname[strlen(leafName)+32]; + strcpy(frname, leafName); + strcat(frname, ".framework/"); + + if ( strstr(this->_dylibInstallPath, frname) != NULL ) + this->_frameworkName = leafName; + } + + // TEMPORARY HACK BEGIN: Support ancient re-export command LC_SUB_FRAMEWORK. + // [TAPI] Support LC_SUB_FRAMEWORK as re-export indicator. + auto installName = std::string(this->_dylibInstallPath); + + // All sub-frameworks of ApplicationServices use LC_SUB_FRAMEWORK. + if (installName.find("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/") == 0 && + installName.find(".dylib") == std::string::npos) { + this->_parentUmbrella = "ApplicationServices"; + } else if (installName.find("/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/") == 0) { + this->_parentUmbrella = "Carbon"; + } else if (installName.find("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/") == 0 && + installName.find(".dylib") == std::string::npos) { + this->_parentUmbrella = "CoreServices"; + } else if (installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib") == 0 || + installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib") == 0 || + installName.find("System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib") == 0) { + this->_parentUmbrella = "vecLib"; + } else if (installName.find("/System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore") == 0) { + this->_parentUmbrella = "WebKit"; + } else if (installName.find("/usr/lib/system/") == 0 && + installName != "/usr/lib/system/libkxld.dylib") { + this->_parentUmbrella = "System"; + } + // TEMPORARY HACK END + + for (auto &client : lib._allowedClients) { + if ((this->_parentUmbrella != nullptr) && (client.str() != this->_parentUmbrella)) + this->_allowableClients.push_back(strdup(client.str().c_str())); + } // [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked - if ( !_allowableClients.empty() ) - _hasPublicInstallName = false; + if ( !this->_allowableClients.empty() ) + this->_hasPublicInstallName = false; if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib ) { + this->_wrongOS = true; + if ( this->_addVersionLoadCommand && !indirectDylib ) { if ( buildingForSimulator ) { - if ( !_allowSimToMacOSXLinking ) + if ( !this->_allowSimToMacOSXLinking ) throwf("building for %s simulator, but linking against dylib built for %s (%s).", Options::platformName(platform), Options::platformName(lib._platform), path); } else { @@ -669,13 +619,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, } } - _dependentDylibs.reserve(lib._reexportedLibraries.size()); - for ( auto& reexport : lib._reexportedLibraries ) { - Dependent entry; - entry.path = strdup(reexport.str().c_str()); - entry.dylib = nullptr; - if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, entry.path) != 0) ) - _dependentDylibs.push_back(entry); + this->_dependentDylibs.reserve(lib._reexportedLibraries.size()); + for ( const auto& reexport : lib._reexportedLibraries ) { + const char *path = strdup(reexport.str().c_str()); + if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) ) + this->_dependentDylibs.emplace_back(path, true); } // build hash table @@ -686,324 +634,51 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, template void File::buildExportHashTable(const DynamicLibrary& lib) { - if ( _s_logHashtable ) + if (this->_s_logHashtable ) fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path()); for (auto &sym : lib._symbols) - addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/false); + this->addSymbol(sym.str().c_str()); #if SUPPORT_ARCH_i386 - if (_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) { + if (this->_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) { for (auto &sym : lib._classes) - addSymbol((".objc_class_name" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + this->addSymbol((".objc_class_name" + sym.str()).c_str()); } else { for (auto &sym : lib._classes) { - addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); - addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str()); + this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str()); } } #else for (auto &sym : lib._classes) { - addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); - addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str()); + this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str()); } #endif for (auto &sym : lib._ivars) - addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + this->addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str()); for (auto &sym : lib._weakDefSymbols) - addSymbol(sym.str().c_str(), /*weak=*/true, /*tlv=*/false); + this->addSymbol(sym.str().c_str(), /*weak=*/true); for (auto &sym : lib._tlvSymbols) - addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true); -} - - -template -void File::addSymbol(const char* name, bool weakDef, bool tlv) -{ - // symbols that start with $ld$ are meta-data to the static linker - // need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { - // $ld$ $ $ - const char* symAction = &name[4]; - const char* symCond = strchr(symAction, '$'); - if ( symCond != nullptr ) { - char curOSVers[16]; - sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); - if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { - const char* symName = strchr(&symCond[1], '$'); - if ( symName != nullptr ) { - ++symName; - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( _s_logHashtable ) - fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); - _ignoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef, false); - return; - } - else if ( strncmp(symAction, "install_name$", 13) == 0 ) { - _dylibInstallPath = strdup(symName); - _installPathOverride = true; - return; - } - else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { - _dylibCompatibilityVersion = parseVersionNumber32(symName); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->path()); - } - } - } - } - else { - warning("bad symbol condition: %s in dylib %s", name, this->path()); - } - } - - // add symbol as possible export if we are not supposed to ignore it - if ( _ignoreExports.count(name) == 0 ) { - AtomAndWeak bucket; - bucket.atom = nullptr; - bucket.weakDef = weakDef; - bucket.tlv = tlv; - if ( _s_logHashtable ) - fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); - _atoms[strdup(name)] = bucket; - } -} - - -template -bool File::forEachAtom(ld::File::AtomHandler& handler) const -{ - handler.doFile(*this); - return false; + this->addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true); } - -template -std::pair File::hasWeakDefinitionImpl(const char* name) const -{ - const auto pos = _atoms.find(name); - if ( pos != _atoms.end() ) - return std::make_pair(true, pos->second.weakDef); - - // look in children that I re-export - for (const auto &dep : _dependentDylibs) { - auto ret = dep.dylib->hasWeakDefinitionImpl(name); - if ( ret.first ) - return ret; - } - return std::make_pair(false, false); -} - - -template -bool File::hasWeakDefinition(const char* name) const -{ - // if supposed to ignore this export, then pretend I don't have it - if ( _ignoreExports.count(name) != 0 ) - return false; - - return hasWeakDefinitionImpl(name).second; -} - - -// 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; - for (const auto &it : _atoms) { - const ld::Atom* atom = it.second.atom; - if ( atom != nullptr ) { - if ( atom->weakImported() ) - foundWeakImport = true; - else - foundNonWeakImport = true; - } - } - - // 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, uint64_t& addr) const -{ - if ( _ignoreExports.count(name) != 0 ) - return false; - - // check myself - const auto pos = _atoms.find(name); - if ( pos != _atoms.end() ) { - weakDef = pos->second.weakDef; - tlv = pos->second.tlv; - addr = 0; - return true; - } - - // check dylibs I re-export - for (const auto& lib : _dependentDylibs) { - if ( !lib.dylib->implicitlyLinked() ) { - if ( lib.dylib->containsOrReExports(name, weakDef, tlv, addr) ) - return true; - } - } - - return false; -} - - -template -bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const -{ - // if supposed to ignore this export, then pretend I don't have it - if ( _ignoreExports.count(name) != 0 ) - return false; - - - AtomAndWeak bucket; - uint64_t addr; - if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) { - bucket.atom = new ExportAtom(*this, name, bucket.weakDef, bucket.tlv); - _atoms[name] = bucket; - _providedAtom = true; - if ( _s_logHashtable ) - fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); - // call handler with new export atom - handler.doAtom(*bucket.atom); - return true; - } - - return false; -} - - - -template -bool File::isPublicLocation(const char* path) -{ - // -no_implicit_dylibs disables this optimization - if ( ! _implicitlyLinkPublicDylibs ) - return false; - - // /usr/lib is a public location - if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) ) - return true; - - // /System/Library/Frameworks/ is a public location - if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { - const char* frameworkDot = strchr(&path[27], '.'); - // but only top level framework - // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true - // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false - if ( frameworkDot != nullptr ) { - int frameworkNameLen = frameworkDot - &path[27]; - if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) - return true; - } - } - - return false; -} - -template -void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) -{ - // only do this once - if ( _indirectDylibsProcessed ) - return; - - const static bool log = false; - if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); - if ( _linkingFlat ) { - for (auto& lib : _dependentDylibs) { - lib.dylib = (File*)handler->findDylib(lib.path, this->path()); - } - } - else if ( _noRexports ) { - // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do - } - else { - // two-level, might have re-exports - for (auto& lib : _dependentDylibs) { - if ( log ) - fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), lib.path); - // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - lib.dylib = (File*)handler->findDylib(lib.path, this->path()); - if ( lib.dylib->hasPublicInstallName() && !lib.dylib->wrongOS() ) { - // promote this child to be automatically added as a direct dependent if this already is - if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(lib.path, lib.dylib->installPath()) == 0) ) { - if ( log ) - fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", lib.dylib->installPath()); - lib.dylib->setImplicitlyLinked(); - } - else if ( lib.dylib->explicitlyLinked() || lib.dylib->implicitlyLinked() ) { - if ( log ) - fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); - } else { - if ( log ) - fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib.path); - } - } else { - // add all child's symbols to me - if ( log ) - fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib.path); - } - } - } - - // check for re-export cycles - ReExportChain chain; - chain.prev = nullptr; - chain.file = this; - this->assertNoReExportCycles(&chain); - - _indirectDylibsProcessed = true; -} - -template -void File::assertNoReExportCycles(ReExportChain* prev) const -{ - // recursively check my re-exported dylibs - ReExportChain chain; - chain.prev = prev; - chain.file = this; - for (const auto& dep : _dependentDylibs) { - ld::File* child = dep.dylib; - // check child is not already in chain - for (ReExportChain* p = prev; p != nullptr; p = p->prev) { - if ( p->file == child ) - throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); - } - if ( dep.dylib != nullptr ) - dep.dylib->assertNoReExportCycles(&chain); - } -} - - template class Parser { public: - typedef typename A::P P; + using P = typename A::P; - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName); + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, + const std::string &path, const char* archName); static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, const Options& opts, - bool indirectDylib) { + bool indirectDylib) + { return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), opts.implicitlyLinkIndirectPublicDylibs(), @@ -1021,7 +696,8 @@ class Parser }; template -bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName) +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, + const char* archName) { if ( path.find(".tbd", path.size()-4) == std::string::npos ) return false; @@ -1072,5 +748,3 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch } // namespace dylib } // namespace textstub - - diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp index 2ad5a3e..dce3783 100644 --- a/ld64/src/ld/passes/bitcode_bundle.cpp +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -268,6 +268,9 @@ BitcodeTempFile::~BitcodeTempFile() BitcodeObfuscator::BitcodeObfuscator() { +#if LTO_API_VERSION < 11 + throwf("compile-time libLTO (%d) didn't support -bitcode_hide_symbols", LTO_API_VERSION); +#else // check if apple internal libLTO is used if ( ::lto_get_version() == NULL ) throwf("libLTO is not loaded"); @@ -282,11 +285,13 @@ BitcodeObfuscator::BitcodeObfuscator() _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 ) throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); _obfuscator = ::lto_codegen_create_in_local_context(); -#if LTO_API_VERSION >= 14 + #if LTO_API_VERSION >= 14 lto_codegen_set_should_internalize(_obfuscator, false); + #endif #endif } + BitcodeObfuscator::~BitcodeObfuscator() { ::lto_codegen_dispose(_obfuscator); @@ -302,7 +307,8 @@ void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator); if ( module == NULL ) - throwf("object contains invalid bitcode: %s", filePath); + throwf("could not reparse object file %s in bitcode bundle: '%s', using libLTO version '%s'", + filePath, ::lto_get_error_message(), ::lto_get_version()); ::lto_codegen_set_module(_obfuscator, module); (*_lto_hide_symbols)(_obfuscator); #if LTO_API_VERSION >= 15 @@ -397,6 +403,8 @@ void BundleHandler::init() // read the xar file _xar = xar_open(oldXARPath.c_str(), READ); + if ( _xar == NULL ) + throwf("malformed bundle format"); // Init the vector of handler xar_iter_t iter = xar_iter_new(); @@ -431,8 +439,10 @@ void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) const char* key = xar_prop_first(src, p); for (int x = 0; x < i; x++) key = xar_prop_next(p); - if ( !key ) + if ( !key ) { + xar_iter_free(p); break; + } const char* val = NULL; xar_prop_get(src, key, &val); if ( // Info from bitcode files @@ -471,7 +481,14 @@ void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) initFile(); // init LTOModule and add asm labels +#if LTO_API_VERSION < 11 lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size); +#else + lto_module_t module = lto_module_create_in_local_context(_file_buffer, _file_size, "bitcode bundle temp file"); +#endif + if ( module == NULL ) + throwf("could not reparse object file in bitcode bundle: '%s', using libLTO version '%s'", + ::lto_get_error_message(), ::lto_get_version()); obfuscator->addAsmSymbolsToMustPreserve(module); lto_module_dispose(module); } @@ -617,14 +634,16 @@ void BitcodeBundle::doPass() // 3. symbols must not be stripped // 4. all the globals if the globals are dead_strip root (ex. dylibs) // 5. there is an exported symbol list suggests the symbol should be exported - // 6. the special symbols supplied by linker + // 6. weak external symbols (not auto-hide) + // 7. the special symbols supplied by linker for ( auto § : _state.sections ) { for ( auto &atom : sect->atoms ) { if ( atom == _state.entryPoint || atom->definition() == ld::Atom::definitionProxy || atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || - ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) ) + ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) || + ( atom->combine() == ld::Atom::combineByName && atom->scope() == ld::Atom::scopeGlobal && !atom->autoHide() ) ) obfuscator->addMustPreserveSymbols(atom->name()); } } @@ -650,6 +669,11 @@ void BitcodeBundle::doPass() obfuscator->addMustPreserveSymbols("__mh_dylinker_header"); obfuscator->addMustPreserveSymbols("__mh_object_header"); obfuscator->addMustPreserveSymbols("__mh_preload_header"); + + // add all the Proxy Atom linker ever created and all the undefs that are possibily dead-stripped. + for (auto sym : _state.allUndefProxies) + obfuscator->addMustPreserveSymbols(sym); + _state.allUndefProxies.clear(); } // Open XAR output diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index f7465d0..65e3cf2 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -389,7 +389,7 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra // -static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection) +static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection, unsigned stubCount) { // assign section offsets to each atom in __text section, watch for thumb branches, and find total size bool hasThumbBranches = false; @@ -448,7 +448,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: (const_cast(atom))->setSectionOffset(offset); offset += atom->size(); } - uint64_t totalTextSize = offset; + uint64_t totalTextSize = offset + stubCount*16; if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches ) return; if (_s_log) fprintf(stderr, "ld: section %s size=%llu, might need branch islands\n", textSection->sectionName(), totalTextSize); @@ -723,11 +723,19 @@ void doPass(const Options& opts, ld::Internal& state) buildAddressMap(opts, state); } + // scan sections for number of stubs + unsigned stubCount = 0; + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeStub ) + stubCount = sect->atoms.size(); + } + // scan sections and add island to each code section for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; if ( sect->type() == ld::Section::typeCode ) - makeIslandsForSection(opts, state, sect); + makeIslandsForSection(opts, state, sect, stubCount); } } diff --git a/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp index efc010c..7be33d2 100644 --- a/ld64/src/ld/passes/branch_shim.cpp +++ b/ld64/src/ld/passes/branch_shim.cpp @@ -266,7 +266,7 @@ static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const // -// The tail-call optimzation may result in a function ending in a jump (b) +// The tail-call optimization may result in a function ending in a jump (b) // to another functions. At compile time the compiler does not know // if the target of the jump will be in the same mode (arm vs thumb). // The arm/thumb instruction set has a way to change modes in a bl(x) diff --git a/ld64/src/ld/passes/code_dedup.cpp b/ld64/src/ld/passes/code_dedup.cpp new file mode 100644 index 0000000..e067acf --- /dev/null +++ b/ld64/src/ld/passes/code_dedup.cpp @@ -0,0 +1,375 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ld.hpp" +#include "code_dedup.h" + +namespace ld { +namespace passes { +namespace dedup { + + + +class DeDupAliasAtom : public ld::Atom +{ +public: + DeDupAliasAtom(const ld::Atom* dupOf, const ld::Atom* replacement) : + ld::Atom(dupOf->section(), ld::Atom::definitionRegular, ld::Atom::combineNever, + dupOf->scope(), dupOf->contentType(), ld::Atom::symbolTableIn, + false, false, true, 0), + _dedupOf(dupOf), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, ld::Fixup::bindingDirectlyBound, replacement) { + if ( dupOf->autoHide() ) + setAutoHide(); + } + + virtual const ld::File* file() const { return _dedupOf->file(); } + virtual const char* translationUnitSource() const + { return NULL; } + virtual const char* name() const { return _dedupOf->name(); } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual ld::Fixup::iterator fixupsBegin() const { return &((ld::Fixup*)&_fixup)[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom* _dedupOf; + ld::Fixup _fixup; +}; + + +namespace { + typedef std::unordered_map CachedHashes; + + ld::Internal* sState = nullptr; + CachedHashes sSavedHashes; + unsigned long sHashCount = 0; + unsigned long sFixupCompareCount = 0; +}; + + +// A helper for std::unordered_map<> that hashes the instructions of a function +struct atom_hashing { + + static unsigned long hash(const ld::Atom* atom) { + auto pos = sSavedHashes.find(atom); + if ( pos != sSavedHashes.end() ) + return pos->second; + + const unsigned instructionBytes = atom->size(); + const uint8_t* instructions = atom->rawContentPointer(); + unsigned long hash = instructionBytes; + for (unsigned i=0; i < instructionBytes; ++i) { + hash = (hash * 33) + instructions[i]; + } + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const Atom* target = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = sState->indirectBindingTable[fit->u.bindingIndex]; + break; + default: + break; + } + // don't include calls to auto-hide functions in hash because they might be de-dup'ed + switch ( fit->kind ) { +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + if ( target && target->autoHide() ) + continue; // don't include + break; + default: + break; + } + if ( target != NULL ) { + const char* name = target->name(); + if ( target->contentType() == ld::Atom::typeCString ) + name = (const char*)target->rawContentPointer(); + for (const char* s = name; *s != '\0'; ++s) + hash = (hash * 33) + *s; + } + } + ++sHashCount; + sSavedHashes[atom] = hash; + return hash; + } + + unsigned long operator()(const ld::Atom* atom) const { + return hash(atom); + } +}; + + +// A helper for std::unordered_map<> that compares functions +struct atom_equal { + + struct VisitedSet { + std::unordered_set atoms1; + std::unordered_set atoms2; + }; + + static bool sameFixups(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) { + ++sFixupCompareCount; + //fprintf(stderr, "sameFixups(%s,%s)\n", atom1->name(), atom2->name()); + Fixup::iterator f1 = atom1->fixupsBegin(); + Fixup::iterator end1 = atom1->fixupsEnd(); + Fixup::iterator f2 = atom2->fixupsBegin(); + Fixup::iterator end2 = atom2->fixupsEnd(); + // two atoms must have same number of fixups + if ( (end1 - f1) != (end2 - f2) ) + return false; + // if no fixups, fixups are equal + if ( f1 == end1 ) + return true; + // all fixups must be the same + do { + if ( f1->offsetInAtom != f2->offsetInAtom ) + return false; + if ( f1->kind != f2->kind ) + return false; + if ( f1->clusterSize != f2->clusterSize ) + return false; + if ( f1->binding != f2->binding ) + return false; + const Atom* target1 = NULL; + const Atom* target2 = NULL; + switch ( f1->binding ) { + case ld::Fixup::bindingDirectlyBound: + target1 = f1->u.target; + target2 = f2->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target1 = sState->indirectBindingTable[f1->u.bindingIndex]; + target2 = sState->indirectBindingTable[f2->u.bindingIndex]; + break; + case ld::Fixup::bindingNone: + break; + default: + return false; + } + if ( target1 != target2 ) { + // targets must match unless they are both calls to functions that will de-dup together + #if SUPPORT_ARCH_arm64 + if ( (f1->kind != ld::Fixup::kindStoreTargetAddressARM64Branch26) && (f1->kind != ld::Fixup::kindStoreTargetAddressX86BranchPCRel32) ) + #else + if ( f1->kind != ld::Fixup::kindStoreTargetAddressX86BranchPCRel32 ) + #endif + return false; + if ( target1->section().type() != target2->section().type() ) + return false; + if ( target1->section().type() != ld::Section::typeCode ) + return false; + // to support co-recursive functions, don't call equals() on targets we've already visited + if ( ((visited.atoms1.count(target1) == 0) || (visited.atoms2.count(target2) == 0)) && !equal(target1, target2, visited) ) + return false; + } + + ++f1; + ++f2; + } while (f1 != end1); + + return true; + } + + static bool equal(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) { + if ( atom_hashing::hash(atom1) != atom_hashing::hash(atom2) ) + return false; + visited.atoms1.insert(atom1); + visited.atoms2.insert(atom2); + return sameFixups(atom1, atom2, visited); + } + + bool operator()(const ld::Atom* atom1, const ld::Atom* atom2) const { + VisitedSet visited; + return equal(atom1, atom2, visited); + } +}; + + + +void doPass(const Options& opts, ld::Internal& state) +{ + const bool log = false; + + // only de-duplicate in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // only de-duplicate for architectures that use relocations that don't store bits in instructions + if ( (opts.architecture() != CPU_TYPE_ARM64) && (opts.architecture() != CPU_TYPE_X86_64) ) + return; + + // support -no_deduplicate to suppress this pass + if ( ! opts.deduplicateFunctions() ) + return; + + const bool verbose = opts.verboseDeduplicate(); + + // find __text section + ld::Internal::FinalSection* textSection = NULL; + for (ld::Internal::FinalSection* sect : state.sections) { + if ( (sect->type() == ld::Section::typeCode) && (strcmp(sect->sectionName(), "__text") == 0) ) { + textSection = sect; + break; + } + } + if ( textSection == NULL ) + return; + + // build map of auto-hide functions and their duplicates + // the key for the map is always the first element in the value vector + // the key is always earlier in the atoms list then matching other atoms + sState = &state; + std::unordered_map, atom_hashing, atom_equal> map; + std::unordered_set masterAtoms; + for (const ld::Atom* atom : textSection->atoms) { + // ignore empty (alias) atoms + if ( atom->size() == 0 ) + continue; + if ( atom->autoHide() ) + map[atom].push_back(atom); + } + + if ( log ) { + for (auto& entry : map) { + if ( entry.second.size() > 1 ) { + printf("Found following matching functions:\n"); + for (const ld::Atom* atom : entry.second) { + printf(" %p %s\n", atom, atom->name()); + } + } + } + fprintf(stderr, "duplicate sets count:\n"); + for (auto& entry : map) + fprintf(stderr, " %p -> %lu\n", entry.first, entry.second.size()); + } + + // construct alias atoms to replace atoms found to be duplicates + unsigned atomsBeingComparedCount = 0; + uint64_t dedupSavings = 0; + std::vector& textAtoms = textSection->atoms; + std::unordered_map replacementMap; + for (auto& entry : map) { + const ld::Atom* masterAtom = entry.first; + std::vector& dups = entry.second; + atomsBeingComparedCount += dups.size(); + if ( dups.size() == 1 ) + continue; + if ( verbose ) { + dedupSavings += ((dups.size() - 1) * masterAtom->size()); + fprintf(stderr, "deduplicate the following %lu functions (%llu bytes apiece):\n", dups.size(), masterAtom->size()); + } + for (const ld::Atom* dupAtom : dups) { + if ( verbose ) + fprintf(stderr, " %s\n", dupAtom->name()); + if ( dupAtom == masterAtom ) + continue; + const ld::Atom* aliasAtom = new DeDupAliasAtom(dupAtom, masterAtom); + auto pos = std::find(textAtoms.begin(), textAtoms.end(), masterAtom); + if ( pos != textAtoms.end() ) { + textAtoms.insert(pos, aliasAtom); + state.atomToSection[aliasAtom] = textSection; + replacementMap[dupAtom] = aliasAtom; + } + } + } + if ( verbose ) { + fprintf(stderr, "deduplication saved %llu bytes of __text\n", dedupSavings); + } + + if ( log ) { + fprintf(stderr, "replacement map:\n"); + for (auto& entry : replacementMap) + fprintf(stderr, " %p -> %p\n", entry.first, entry.second); + } + + // walk all atoms and replace references to dups with references to alias + for (ld::Internal::FinalSection* sect : state.sections) { + for (const ld::Atom* atom : sect->atoms) { + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + std::unordered_map::iterator pos; + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + pos = replacementMap.find(state.indirectBindingTable[fit->u.bindingIndex]); + if ( pos != replacementMap.end() ) + state.indirectBindingTable[fit->u.bindingIndex] = pos->second; + break; + case ld::Fixup::bindingDirectlyBound: + pos = replacementMap.find(fit->u.target); + if ( pos != replacementMap.end() ) + fit->u.target = pos->second; + break; + default: + break; + } + } + } + } + + if ( log ) { + fprintf(stderr, "atoms before pruning:\n"); + for (const ld::Atom* atom : textSection->atoms) + fprintf(stderr, " %p (size=%llu) %sp\n", atom, atom->size(), atom->name()); + } + // remove replaced atoms from section + textSection->atoms.erase(std::remove_if(textSection->atoms.begin(), textSection->atoms.end(), + [&](const ld::Atom* atom) { + return (replacementMap.count(atom) != 0); + }), + textSection->atoms.end()); + + for (auto& entry : replacementMap) + state.atomToSection.erase(entry.first); + + if ( log ) { + fprintf(stderr, "atoms after pruning:\n"); + for (const ld::Atom* atom : textSection->atoms) + fprintf(stderr, " %p (size=%llu) %sp\n", atom, atom->size(), atom->name()); + } + + //fprintf(stderr, "hash-count=%lu, fixup-compares=%lu, atom-count=%u\n", sHashCount, sFixupCompareCount, atomsBeingComparedCount); +} + + +} // namespace dedup +} // namespace passes +} // namespace ld diff --git a/ld64/src/ld/passes/code_dedup.h b/ld64/src/ld/passes/code_dedup.h new file mode 100644 index 0000000..0b803ba --- /dev/null +++ b/ld64/src/ld/passes/code_dedup.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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 __DEDUPE_H__ +#define __DEDUPE_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace dedup { + +// called by linker to merge duplicate functions +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace huge +} // namespace passes +} // namespace ld + +#endif // __DEDUPE_H__ diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 1d257ee..d2847bb 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -630,11 +630,10 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< // keep adding entries to page until: // 1) encoding table plus entry table plus header exceed page size // 2) the file offset delta from the first to last function > 24 bits - // 3) custom encoding index reachs 255 + // 3) custom encoding index reaches 255 // 4) run out of uniqueInfos to encode std::map pageSpecificEncodings; uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t); - std::vector encodingIndexes; int index = endIndex-1; int entryCount = 0; uint64_t lastEntryAddress = uniqueInfos[index].funcTentAddress; @@ -646,6 +645,7 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< std::map::const_iterator pos = commonEncodings.find(info.encoding); if ( pos != commonEncodings.end() ) { encodingIndex = pos->second; + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): funcIndex=%d, re-use commonEncodings[%d]=0x%08X\n", index, encodingIndex, info.encoding); } else { // no commmon entry, so add one on this page @@ -657,12 +657,13 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< std::map::iterator ppos = pageSpecificEncodings.find(encoding); if ( ppos != pageSpecificEncodings.end() ) { encodingIndex = pos->second; + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): funcIndex=%d, re-use pageSpecificEncodings[%d]=0x%08X\n", index, encodingIndex, encoding); } else { encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); if ( encodingIndex <= 255 ) { pageSpecificEncodings[encoding] = encodingIndex; - if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): pageSpecificEncodings[%d]=0x%08X\n", encodingIndex, encoding); + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): funcIndex=%d, pageSpecificEncodings[%d]=0x%08X\n", index, encodingIndex, encoding); } else { canDo = false; // case 3) @@ -671,8 +672,6 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< } } } - if ( canDo ) - encodingIndexes.push_back(encodingIndex); // compute function offset uint32_t funcOffsetWithInPage = lastEntryAddress - info.funcTentAddress; if ( funcOffsetWithInPage > 0x00FFFF00 ) { @@ -680,16 +679,16 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< canDo = false; // case 2) if (_s_log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount); } - else { - ++entryCount; - } // check room for entry - if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { + if ( (pageSpecificEncodings.size()+entryCount) > space4 ) { canDo = false; // case 1) --entryCount; if (_s_log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); } //if (_s_log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); + if ( canDo ) { + ++entryCount; + } } // check for cases where it would be better to use a regular (non-compressed) page @@ -725,6 +724,7 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< uint8_t encodingIndex; if ( encodingMeansUseDwarf(info.encoding) ) { // dwarf entries are always in page specific encodings + assert(pageSpecificEncodings.find(info.encoding+i) != pageSpecificEncodings.end()); encodingIndex = pageSpecificEncodings[info.encoding+i]; } else { @@ -760,16 +760,22 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< - - -static uint64_t calculateEHFrameSize(const ld::Internal& state) +static uint64_t calculateEHFrameSize(ld::Internal& state) { + bool allCIEs = true; uint64_t size = 0; - for (std::vector::const_iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; + for (ld::Internal::FinalSection* sect : state.sections) { if ( sect->type() == ld::Section::typeCFI ) { - for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - size += (*ait)->size(); + for (const ld::Atom* atom : sect->atoms) { + size += atom->size(); + if ( strcmp(atom->name(), "CIE") != 0 ) + allCIEs = false; + } + if ( allCIEs ) { + // Linker generates eh_frame data even when there's only an unused CIEs in it + sect->atoms.clear(); + state.sections.erase(std::remove(state.sections.begin(), state.sections.end(), sect), state.sections.end()); + return 0; } } } diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index ad4673c..c65ca21 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -755,6 +755,21 @@ bool OptimizeCategories::hasProperties(ld::Internal& state, const std::vector } +static const ld::Atom* fixClassAliases(const ld::Atom* classAtom) +{ + if ( (classAtom->size() != 0) || (classAtom->definition() == ld::Atom::definitionProxy) ) + return classAtom; + + for (ld::Fixup::iterator fit=classAtom->fixupsBegin(); fit != classAtom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->offsetInAtom == 0); + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + return fit->u.target; + } + } + + return classAtom; +} // // Helper for std::remove_if @@ -841,7 +856,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // ignore categories also in __objc_nlcatlist if ( nlcatListAtoms.count(categoryAtom) != 0 ) continue; - const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom, hasAddend); + const ld::Atom* categoryOnClassAtom = fixClassAliases(Category::getClass(state, categoryAtom, hasAddend)); assert(categoryOnClassAtom != NULL); // only look at classes defined in this image if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) { diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index bee5f2f..abed7e3 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -135,8 +135,12 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) if ( (target->definition() == ld::Atom::definitionRegular) && (target->combine() == ld::Atom::combineByName) && ((target->symbolTableInclusion() == ld::Atom::symbolTableIn) - || (target->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) + || (target->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { + // don't make stubs for auto-hide symbols + if ( target->autoHide() && (!_options.hasExportMaskList() || !_options.shouldExport(target->name())) ) + return NULL; return target; + } // create stub if target is interposable if ( _options.interposable(target->name()) ) return target; @@ -174,8 +178,10 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) forLazyDylib = true; bool stubToResolver = (target.contentType() == ld::Atom::typeResolver); +#if SUPPORT_ARCH_arm_any || SUPPORT_ARCH_arm64 bool usingDataConst = _options.useDataConstSegment(); - +#endif + 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()); diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index aec6ebe..0465cd5 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -100,8 +101,8 @@ class MachOChecker { public: static bool validFile(const uint8_t* fileContent); - static MachOChecker* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) - { return new MachOChecker(fileContent, fileLength, path); } + static MachOChecker* make(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot) + { return new MachOChecker(fileContent, fileLength, path, verifierDstRoot); } virtual ~MachOChecker() {} @@ -126,7 +127,7 @@ class MachOChecker typedef std::unordered_set StringSet; - MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); + MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot); void checkMachHeader(); void checkLoadCommands(); void checkSection(const macho_segment_command

* segCmd, const macho_section

* sect); @@ -137,6 +138,11 @@ class MachOChecker void checkRelocations(); void checkExternalReloation(const macho_relocation_info

* reloc); void checkLocalReloation(const macho_relocation_info

* reloc); + void verify(); + void verifyInstallName(); + void verifyNoRpaths(); + void verifyNoFlatLookups(); + pint_t relocBase(); bool addressInWritableSegment(pint_t address); bool hasTextRelocInRange(pint_t start, pint_t end); @@ -145,12 +151,14 @@ class MachOChecker bool addressIsBindingSite(pint_t addr); pint_t getInitialStackPointer(const macho_thread_command

*); pint_t getEntryPoint(const macho_thread_command

*); - + const char* archName(); const char* fPath; + const char* fDstRoot; const macho_header

* fHeader; uint32_t fLength; + const char* fInstallName; const char* fStrings; const char* fStringsEnd; const macho_nlist

* fSymbols; @@ -164,6 +172,7 @@ class MachOChecker uint32_t fExternalRelocationsCount; bool fWriteableSegmentWithAddrOver4G; bool fSlidableImage; + bool fHasLC_RPATH; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; const macho_segment_command

* fTEXTSegment; @@ -173,43 +182,6 @@ class MachOChecker }; - -template <> -bool MachOChecker::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 MachOChecker::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 MachOChecker::validFile(const uint8_t* fileContent) { @@ -246,6 +218,7 @@ bool MachOChecker::validFile(const uint8_t* fileContent) return false; } +#if SUPPORT_ARCH_arm_any template <> bool MachOChecker::validFile(const uint8_t* fileContent) { @@ -263,6 +236,7 @@ bool MachOChecker::validFile(const uint8_t* fileContent) } return false; } +#endif #if SUPPORT_ARCH_arm64 template <> @@ -284,27 +258,12 @@ bool MachOChecker::validFile(const uint8_t* fileContent) } #endif -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } -template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } -#if SUPPORT_ARCH_arm64 template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } -#endif -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) @@ -324,25 +283,12 @@ arm::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_comm return threadInfo->thread_register(13); } -#if SUPPORT_ARCH_arm64 template <> arm64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) { throw "LC_UNIXTHREAD not supported for arm64"; } -#endif -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) @@ -362,19 +308,47 @@ arm::P::uint_t MachOChecker::getEntryPoint(const macho_thread_commandthread_register(15); } -#if SUPPORT_ARCH_arm64 template <> arm64::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) { throw "LC_UNIXTHREAD not supported for arm64"; } -#endif + + +template +const char* MachOChecker::archName() +{ + switch ( fHeader->cputype() ) { + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + if ( fHeader->cpusubtype() == CPU_SUBTYPE_X86_64_H ) + return "x86_64h"; + else + return "x86_64"; + case CPU_TYPE_ARM: + switch ( fHeader->cpusubtype() ) { + case CPU_SUBTYPE_ARM_V7: + return "armv7"; + case CPU_SUBTYPE_ARM_V7S: + return "armv7s"; + case CPU_SUBTYPE_ARM_V7K: + return "armv7k"; + } + return "arm"; + case CPU_TYPE_ARM64: + return "arm64"; + } + return "unknown"; +} + + 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), +MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot) + : fHeader(NULL), fLength(fileLength), fInstallName(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), - fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), + fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fHasLC_RPATH(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fTEXTSegment(NULL), fDyldInfo(NULL), fSectionCount(0) { // sanity check @@ -382,6 +356,7 @@ MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c throw "not a mach-o file that can be checked"; fPath = strdup(path); + fDstRoot = verifierDstRoot ? strdup(verifierDstRoot) : NULL; fHeader = (const macho_header

*)fileContent; // sanity check header @@ -397,6 +372,9 @@ MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c checkSymbolTable(); checkInitTerms(); + + if ( verifierDstRoot != NULL ) + verify(); } @@ -407,7 +385,7 @@ void MachOChecker::checkMachHeader() throw "sizeofcmds in mach_header is larger than file"; uint32_t flags = fHeader->flags(); - const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFE000000; + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFC000000; if ( flags & invalidBits ) throw "invalid bits in mach_header flags"; if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) @@ -438,6 +416,7 @@ void MachOChecker::checkLoadCommands() const uint32_t cmd_count = fHeader->ncmds(); const macho_load_command

* const cmds = (macho_load_command

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

)); const macho_load_command

* cmd = cmds; + const macho_dylib_command

* dylibID; for (uint32_t i = 0; i < cmd_count; ++i) { uint32_t size = cmd->cmdsize(); if ( (size & this->loadCommandSizeMask()) != 0 ) @@ -452,9 +431,8 @@ void MachOChecker::checkLoadCommands() case LC_SYMTAB: case LC_DYSYMTAB: case LC_LOAD_DYLIB: - case LC_ID_DYLIB: - case LC_LOAD_DYLINKER: case LC_ID_DYLINKER: + case LC_LOAD_DYLINKER: case macho_routines_command

::CMD: case LC_SUB_FRAMEWORK: case LC_SUB_CLIENT: @@ -469,13 +447,23 @@ void MachOChecker::checkLoadCommands() case LC_LOAD_UPWARD_DYLIB: case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: - case LC_RPATH: case LC_FUNCTION_STARTS: case LC_DYLD_ENVIRONMENT: case LC_DATA_IN_CODE: case LC_DYLIB_CODE_SIGN_DRS: case LC_SOURCE_VERSION: break; + case LC_RPATH: + fHasLC_RPATH = true; + break; + case LC_ID_DYLIB: + dylibID = (macho_dylib_command

*)cmd; + if ( dylibID->name_offset() > size ) + throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID->name_offset(), size); + if ( (dylibID->name_offset() + strlen(dylibID->name()) + 1) > size ) + throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command"); + fInstallName = dylibID->name(); + break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: fDyldInfo = (macho_dyld_info_command

*)cmd; @@ -533,7 +521,7 @@ void MachOChecker::checkLoadCommands() else { throw "overlapping segment vm addresses"; } - segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); + segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); } // see if this overlaps another segment file offset range uint64_t startOffset = segCmd->fileoff(); @@ -550,7 +538,7 @@ void MachOChecker::checkLoadCommands() else { throw "overlapping segment file data"; } - segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); + segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); // check is within file bounds if ( (startOffset > fLength) || (endOffset > fLength) ) throw "segment file data is past end of file"; @@ -690,6 +678,16 @@ void MachOChecker::checkLoadCommands() } } + // verify dylib has LC_ID_DYLIB + if ( fHeader->filetype() == MH_DYLIB ) { + if ( fInstallName == NULL ) + throw "MH_DYLIB missing LC_ID_DYLIB"; + } + else { + if ( fInstallName != NULL ) + throw "LC_ID_DYLIB found but file type is not MH_DYLIB"; + } + // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO cmd = cmds; bool foundDynamicSymTab = false; @@ -702,8 +700,8 @@ void MachOChecker::checkLoadCommands() fSymbols = (const macho_nlist

*)((char*)fHeader + symtab->symoff()); if ( symtab->symoff() < linkEditSegment->fileoff() ) throw "symbol table not in __LINKEDIT"; - if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist

*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "symbol table end not in __LINKEDIT"; + if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist

*)) > symtab->stroff() ) + throw "symbol table overlaps string pool"; if ( (symtab->symoff() % sizeof(pint_t)) != 0 ) throw "symbol table start not pointer aligned"; fStrings = (char*)fHeader + symtab->stroff(); @@ -837,7 +835,85 @@ void MachOChecker::checkSection(const macho_segment_command

* segCmd, const } +template +void MachOChecker::verify() +{ + bool sharedCacheCandidate = false; + if ( fInstallName != NULL ) { + if ( (strncmp(fInstallName, "/usr/lib/", 9) == 0) || (strncmp(fInstallName, "/System/Library/", 16) == 0) ) { + sharedCacheCandidate = true; + verifyInstallName(); + verifyNoRpaths(); + } + } + verifyNoFlatLookups(); +} + + +template +void MachOChecker::verifyInstallName() +{ + // Don't allow @rpath to be used as -install_name for OS dylibs + if ( strncmp(fInstallName, "@rpath/", 7) == 0 ) { + printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName()); + } + else { + // Verify -install_name match actual path of dylib + const char* installPathWithinDstRoot = &fPath[strlen(fDstRoot)]; + if ( strcmp(installPathWithinDstRoot, fInstallName) != 0 ) { + // see if install name is a symlink to actual file + bool symlinkToDylib = false; + char absDstPath[PATH_MAX]; + if ( realpath(fDstRoot, absDstPath) != NULL ) { + char fullInstallNamePath[PATH_MAX]; + strlcpy(fullInstallNamePath, absDstPath, PATH_MAX); + strlcat(fullInstallNamePath, fInstallName, PATH_MAX); + char absInstallNamePath[PATH_MAX]; + if ( realpath(fullInstallNamePath, absInstallNamePath) != NULL ) { + char absFPath[PATH_MAX]; + if ( realpath(fPath, absFPath) != NULL ) { + if ( strcmp(absInstallNamePath, absFPath) == 0 ) + symlinkToDylib = true; + } + } + } + if ( !symlinkToDylib ) + printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName()); + } + } +} + +template +void MachOChecker::verifyNoRpaths() +{ + // Don't allow OS dylibs to add rpaths + if ( fHasLC_RPATH ) { + printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName()); + } +} + + +template +void MachOChecker::verifyNoFlatLookups() +{ + if ( (fHeader->flags() & MH_TWOLEVEL) == 0 ) { + printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName()); + return; + } + + if ( fDynamicSymbolTable != NULL ) { + const macho_nlist

* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()]; + const macho_nlist

* const undefinesEnd = &undefinesStart[fDynamicSymbolTable->nundefsym()]; + for(const macho_nlist

* sym = undefinesStart; sym < undefinesEnd; ++sym) { + //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]); + if ( GET_LIBRARY_ORDINAL(sym->n_desc()) == DYNAMIC_LOOKUP_ORDINAL ) { + const char* symName = &fStrings[sym->n_strx()]; + printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName, archName()); + } + } + } +} template void MachOChecker::checkIndirectSymbolTable() @@ -879,6 +955,44 @@ void MachOChecker::checkIndirectSymbolTable() } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } + + + if ( fDynamicSymbolTable->ilocalsym() != 0 ) + throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable->ilocalsym()); + + if ( fDynamicSymbolTable->ilocalsym() > fSymbolCount ) + throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->ilocalsym(), fSymbolCount); + if ( fDynamicSymbolTable->ilocalsym() + fDynamicSymbolTable->nlocalsym() > fSymbolCount ) { + throwf("local symbols out of range (%d+%d > %d) in indirect symbol table", + fDynamicSymbolTable->ilocalsym(), fDynamicSymbolTable->nlocalsym(), fSymbolCount); + } + + if ( fDynamicSymbolTable->iextdefsym() > fSymbolCount ) + throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->iextdefsym(), fSymbolCount); + if ( fDynamicSymbolTable->iextdefsym() != fDynamicSymbolTable->ilocalsym() + fDynamicSymbolTable->nlocalsym() ) { + throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table", + fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->ilocalsym(), fDynamicSymbolTable->nlocalsym() ); + } + if ( fDynamicSymbolTable->iextdefsym() + fDynamicSymbolTable->nextdefsym() > fSymbolCount ) { + throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table", + fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->nextdefsym(), fSymbolCount); + } + + if ( fDynamicSymbolTable->iundefsym() > fSymbolCount ) + throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable->iundefsym(), fSymbolCount); + if ( fDynamicSymbolTable->iundefsym() != fDynamicSymbolTable->iextdefsym() + fDynamicSymbolTable->nextdefsym() ) { + throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table", + fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->iextdefsym(), fDynamicSymbolTable->nextdefsym()); + } + if ( fDynamicSymbolTable->iundefsym() + fDynamicSymbolTable->nundefsym() > fSymbolCount ) { + throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table", + fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->nundefsym(), fSymbolCount); + } + + if ( fDynamicSymbolTable->iundefsym() + fDynamicSymbolTable->nundefsym() != fSymbolCount ) { + throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table", + fDynamicSymbolTable->iundefsym(), fDynamicSymbolTable->nundefsym(), fSymbolCount ); + } } @@ -985,23 +1099,6 @@ void MachOChecker::checkInitTerms() } -template <> -ppc::P::uint_t MachOChecker::relocBase() -{ - if ( fHeader->flags() & MH_SPLIT_SEGS ) - return fFirstWritableSegment->vmaddr(); - else - return fFirstSegment->vmaddr(); -} - -template <> -ppc64::P::uint_t MachOChecker::relocBase() -{ - if ( fWriteableSegmentWithAddrOver4G ) - return fFirstWritableSegment->vmaddr(); - else - return fFirstSegment->vmaddr(); -} template <> x86::P::uint_t MachOChecker::relocBase() @@ -1028,15 +1125,11 @@ arm::P::uint_t MachOChecker::relocBase() return fFirstSegment->vmaddr(); } -#if SUPPORT_ARCH_arm64 template <> arm64::P::uint_t MachOChecker::relocBase() { return fFirstWritableSegment->vmaddr(); } -#endif - - template bool MachOChecker::addressInWritableSegment(pint_t address) @@ -1068,37 +1161,6 @@ bool MachOChecker::addressInWritableSegment(pint_t address) } -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 2 ) - throw "bad external relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "external relocation address not in writable segment"; - // FIX: check r_symbol -} - -template <> -void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 3 ) - throw "bad external relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown external relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad external relocation pc_rel"; - if ( reloc->r_extern() == 0 ) - throw "local relocation found with external relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "external relocation address not in writable segment"; - // FIX: check r_symbol -} template <> void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) @@ -1139,7 +1201,7 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* r { if ( reloc->r_length() != 2 ) throw "bad external relocation length"; - if ( reloc->r_type() != ARM_RELOC_VANILLA ) + if ( reloc->r_type() != ARM_RELOC_VANILLA ) throw "unknown external relocation type"; if ( reloc->r_pcrel() != 0 ) throw "bad external relocation pc_rel"; @@ -1160,38 +1222,6 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* #endif -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_address() & R_SCATTERED ) { - // scattered - const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; - // FIX - - } - else { - // FIX - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); - } -} - - -template <> -void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) -{ - if ( reloc->r_length() != 3 ) - throw "bad local relocation length"; - if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) - throw "unknown local relocation type"; - if ( reloc->r_pcrel() != 0 ) - throw "bad local relocation pc_rel"; - if ( reloc->r_extern() != 0 ) - throw "external relocation found with local relocations"; - if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "local relocation address not in writable segment"; -} - template <> void MachOChecker::checkLocalReloation(const macho_relocation_info

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

* re } #endif - template void MachOChecker::checkRelocations() { @@ -1585,7 +1614,7 @@ bool MachOChecker::addressIsBindingSite(pint_t targetAddr) } -static void check(const char* path) +static void check(const char* path, const char* verifierDstRoot) { struct stat stat_buf; @@ -1610,37 +1639,33 @@ static void check(const char* path) unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype); switch(cputype) { - case CPU_TYPE_POWERPC: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, ppc slice does not contain ppc mach-o"; - break; case CPU_TYPE_I386: if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); + MachOChecker::make(p + offset, size, path, verifierDstRoot); else throw "in universal file, i386 slice does not contain i386 mach-o"; break; - case CPU_TYPE_POWERPC64: - if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); - else - throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; - break; case CPU_TYPE_X86_64: if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); + MachOChecker::make(p + offset, size, path, verifierDstRoot); else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( MachOChecker::validFile(p + offset) ) - MachOChecker::make(p + offset, size, path); + MachOChecker::make(p + offset, size, path, verifierDstRoot); else throw "in universal file, arm slice does not contain arm mach-o"; break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( MachOChecker::validFile(p + offset) ) + MachOChecker::make(p + offset, size, path, verifierDstRoot); + else + throw "in universal file, arm64 slice does not contain arm mach-o"; + break; #endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); @@ -1648,25 +1673,19 @@ static void check(const char* path) } } else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); - } - else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); + MachOChecker::make(p, length, path, verifierDstRoot); } else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); + MachOChecker::make(p, length, path, verifierDstRoot); } #if SUPPORT_ARCH_arm_any else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); + MachOChecker::make(p, length, path, verifierDstRoot); } #endif #if SUPPORT_ARCH_arm64 else if ( MachOChecker::validFile(p) ) { - MachOChecker::make(p, length, path); + MachOChecker::make(p, length, path, verifierDstRoot); } #endif else { @@ -1682,6 +1701,7 @@ static void check(const char* path) int main(int argc, const char* argv[]) { bool progress = false; + const char* verifierDstRoot = NULL; int result = 0; for(int i=1; i < argc; ++i) { const char* arg = argv[i]; @@ -1689,6 +1709,18 @@ int main(int argc, const char* argv[]) if ( strcmp(arg, "-progress") == 0 ) { progress = true; } + else if ( strcmp(arg, "-verifier_dstroot") == 0 ) { + verifierDstRoot = argv[++i]; + } + else if ( strcmp(arg, "-verifier_error_list") == 0 ) { + printf("os_dylib_rpath_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name that is an absolute path - not an @rpath\n"); + printf("os_dylib_bad_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name matching their file system location\n"); + printf("os_dylib_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)\n"); + printf("os_dylib_flat_namespace\tOS dylibs should not be built with -flat_namespace\n"); + printf("os_dylib_undefined_dynamic_lookup\tOS dylibs should not be built with -undefined dynamic_lookup\n"); + printf("os_dylib_malformed\the mach-o is malformed\n"); + return 0; + } else { throwf("unknown option: %s\n", arg); } @@ -1696,12 +1728,17 @@ int main(int argc, const char* argv[]) else { bool success = true; try { - check(arg); + check(arg, verifierDstRoot); } catch (const char* msg) { - fprintf(stderr, "machocheck failed: %s %s\n", arg, msg); - result = 1; - success = false; + if ( verifierDstRoot ) { + printf("os_dylib_malformed\twarn\t%s\n", msg); + } + else { + fprintf(stderr, "machocheck failed: %s\n", msg); + result = 1; + success = false; + } } if ( success && progress ) printf("ok: %s\n", arg); @@ -1711,5 +1748,3 @@ int main(int argc, const char* argv[]) return result; } - - diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index f91ece3..bc925fb 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -240,7 +240,7 @@ const char* UnwindPrinter::functionName(pint_t addr, uint32_t* offset) 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) ) { - uint32_t value = fSymbols[i].n_value(); + pint_t value = fSymbols[i].n_value(); if ( value == addr ) { const char* r = &fStrings[fSymbols[i].n_strx()]; return r; diff --git a/ld64/unit-tests/run-all-unit-tests b/ld64/unit-tests/run-all-unit-tests index 9285128..cdab927 100755 --- a/ld64/unit-tests/run-all-unit-tests +++ b/ld64/unit-tests/run-all-unit-tests @@ -5,8 +5,6 @@ unset RC_TRACE_ARCHIVES unset LD_TRACE_DYLIBS unset LD_TRACE_ARCHIVES -export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib -export MACOSX_DEPLOYMENT_TARGET=10.7 # cd into test-cases directory cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s new file mode 100644 index 0000000..477cdab --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdrField.s @@ -0,0 +1,53 @@ + +#ifndef TARGET + #define TARGET _foo +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] +#if LOAD_GPR_8 +L3: ldr b2, [x1, #8] +#elif LOAD_GPR_16 +L3: ldr h2, [x1, #8] +#elif LOAD_GPR_32 +L3: ldr w2, [x1, #8] +#elif LOAD_GPR_64 +L3: ldr x2, [x1, #8] +#elif LOAD_FPR_32 +L3: ldr s2, [x1, #8] +#elif LOAD_FPR_64 +L3: ldr d2, [x1, #8] +#elif LOAD_VEC_128 +L3: ldr q2, [x1, #16] +#endif + nop + + .loh AdrpLdrGotLdr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONST + .const + .align 4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0,0,0,0 + + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile b/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile index de5da0d..8662288 100644 --- a/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile @@ -34,7 +34,7 @@ all-x86_64: skip all-armv6: skip all-armv7: skip -all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpAddStr AdrpLdrGotStr AdrpLdrGot +all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpLdrGotLdrField AdrpAddStr AdrpLdrGotStr AdrpLdrGot main.o: ${CC} ${CCFLAGS} main.s -c -o main.o @@ -81,14 +81,14 @@ AdrpAddLdr-ldr: main.o ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g8.o main.o -o AdrpAddLdr-ldr-g8.exe ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\s*b1, \[x0\]' | ${FAIL_IF_EMPTY} ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 16-bit load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g16.o main.o -o AdrpAddLdr-ldr-g16.exe ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\s*h1, \[x0\]' | ${FAIL_IF_EMPTY} ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load @@ -101,7 +101,7 @@ AdrpAddLdr-ldr: main.o # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g64.o main.o -o AdrpAddLdr-ldr-g64.exe - ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY} +# ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY} ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} @@ -228,51 +228,51 @@ AdrpAddLdr-seg: # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 8-bit load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 ${CC} ${CCFLAGS} AdrpAddLdr-seg-g8.o -dynamiclib -o AdrpAddLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr b1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr\s*b1, \[x0,' | ${FAIL_IF_EMPTY} # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 16-bit load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 ${CC} ${CCFLAGS} AdrpAddLdr-seg-g16.o -dynamiclib -o AdrpAddLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr h1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr\s*h1, \[x0,' | ${FAIL_IF_EMPTY} # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 ${CC} ${CCFLAGS} AdrpAddLdr-seg-g32.o -dynamiclib -o AdrpAddLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr w1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr\s*w1, \[x0,' | ${FAIL_IF_EMPTY} # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 ${CC} ${CCFLAGS} AdrpAddLdr-seg-g64.o -dynamiclib -o AdrpAddLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr x1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr\s*x1, \[x0,' | ${FAIL_IF_EMPTY} # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit fp load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 ${CC} ${CCFLAGS} AdrpAddLdr-seg-f32.o -dynamiclib -o AdrpAddLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr s1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr\s*s1, \[x0,' | ${FAIL_IF_EMPTY} # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit fp load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 ${CC} ${CCFLAGS} AdrpAddLdr-seg-f64.o -dynamiclib -o AdrpAddLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr d1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr\s*d1, \[x0,' | ${FAIL_IF_EMPTY} # test ADRP/ADD/LD -> ADRP/LD when target is in movable segmentfor 128-bit vec load ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 ${CC} ${CCFLAGS} AdrpAddLdr-seg-v128.o -dynamiclib -o AdrpAddLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib - ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} - ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN} - ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr q1, \[x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add\s*x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr\s*q1, \[x0,' | ${FAIL_IF_EMPTY} @@ -812,6 +812,322 @@ AdrpLdrGotLdr-farunaligned: main.o ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} +AdrpLdrGotLdrField: AdrpLdrGotLdrField-extern AdrpLdrGotLdrField-externfargot AdrpLdrGotLdrField-near AdrpLdrGotLdrField-far AdrpLdrGotLdrField-nearunaligned AdrpLdrGotLdrField-farunaligned + true + +AdrpLdrGotLdrField-extern: main.o + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g8.o main.o -o AdrpLdrGotLdrField-extern-g8.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g8.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g16.o main.o -o AdrpLdrGotLdrField-extern-g16.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g16.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g32.o main.o -o AdrpLdrGotLdrField-extern-g32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g32.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-g64.o main.o -o AdrpLdrGotLdrField-extern-g64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g64.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-f32.o main.o -o AdrpLdrGotLdrField-extern-f32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-f32.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-f64.o main.o -o AdrpLdrGotLdrField-extern-f64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-f64.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-extern-v128.o main.o -o AdrpLdrGotLdrField-extern-v128.exe + ${OTOOL} -tV AdrpLdrGotLdrField-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-v128.exe | grep 'ldr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-extern-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdrField-externfargot: main.o + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g8.o main.o -o AdrpLdrGotLdrField-externfargot-g8.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g8.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g16.o main.o -o AdrpLdrGotLdrField-externfargot-g16.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g16.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot farfor 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g32.o main.o -o AdrpLdrGotLdrField-externfargot-g32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-g64.o main.o -o AdrpLdrGotLdrField-externfargot-g64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-f32.o main.o -o AdrpLdrGotLdrField-externfargot-f32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-f64.o main.o -o AdrpLdrGotLdrField-externfargot-f64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-externfargot-v128.o main.o -o AdrpLdrGotLdrField-externfargot-v128.exe + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-v128.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-externfargot-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdrField-near: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g8.o main.o -o AdrpLdrGotLdrField-near-g8.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g8.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g16.o main.o -o AdrpLdrGotLdrField-near-g16.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g16.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g32.o main.o -o AdrpLdrGotLdrField-near-g32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'ldr\s*w2, _foo' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g32.exe | grep 'ldr\s*w2,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-g64.o main.o -o AdrpLdrGotLdrField-near-g64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'ldr\s*x2, _foo' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-g64.exe | grep 'ldr\s*x2,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-f32.o main.o -o AdrpLdrGotLdrField-near-f32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'ldr\s*s2, _foo' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-f32.exe | grep 'ldr\s*s2,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-f64.o main.o -o AdrpLdrGotLdrField-near-f64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'ldr\s*d2, _foo' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-f64.exe | grep 'ldr\s*d2,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-near-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-near-v128.o main.o -o AdrpLdrGotLdrField-near-v128.exe + ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'ldr\s*x1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'ldr\s*q2, _foo' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-near-v128.exe | grep 'ldr\s*q2,' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdrField-nearunaligned: main.o + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 8-bit unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g8.o main.o -o AdrpLdrGotLdrField-nearunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g8.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g8.exe | grep 'ldr\s*b2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 16-bit unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g16.o main.o -o AdrpLdrGotLdrField-nearunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g16.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 32-bit unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g32.o main.o -o AdrpLdrGotLdrField-nearunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g32.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 64-bit unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-g64.o main.o -o AdrpLdrGotLdrField-nearunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g64.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 32-bit FP unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-f32.o main.o -o AdrpLdrGotLdrField-nearunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f32.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 64-bit FP unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-f64.o main.o -o AdrpLdrGotLdrField-nearunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f64.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> ADR/LDR when target in __TEXT for 12-bit vec unaligned load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-nearunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-nearunaligned-v128.o main.o -o AdrpLdrGotLdrField-nearunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-v128.exe | grep 'adr\s*x1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-nearunaligned-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdrField-far: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g8.o main.o -o AdrpLdrGotLdrField-far-g8.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-g8.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g8.exe | grep 'ldr\s*b2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g16.o main.o -o AdrpLdrGotLdrField-far-g16.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-g16.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g16.exe | grep 'ldr\s*h2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g32.o main.o -o AdrpLdrGotLdrField-far-g32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-g32.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g32.exe | grep 'ldr\s*w2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-g64.o main.o -o AdrpLdrGotLdrField-far-g64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-g64.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-g64.exe | grep 'ldr\s*x2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-f32.o main.o -o AdrpLdrGotLdrField-far-f32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-f32.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-f32.exe | grep 'ldr\s*s2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-f64.o main.o -o AdrpLdrGotLdrField-far-f64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-f64.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-f64.exe | grep 'ldr\s*d2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-far-v128.o main.o -o AdrpLdrGotLdrField-far-v128.exe + ${OTOOL} -tV AdrpLdrGotLdrField-far-v128.exe | grep 'adrp\s*x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-far-v128.exe | grep 'ldr\s*q2, \[x0,' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdrField-farunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g8.o main.o -o AdrpLdrGotLdrField-farunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g8.exe | grep 'ldr\s*b2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g16.o main.o -o AdrpLdrGotLdrField-farunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g16.exe | grep 'ldr\s*h2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g32.o main.o -o AdrpLdrGotLdrField-farunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g32.exe | grep 'ldr\s*w2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-g64.o main.o -o AdrpLdrGotLdrField-farunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-g64.exe | grep 'ldr\s*x2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-f32.o main.o -o AdrpLdrGotLdrField-farunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f32.exe | grep 'ldr\s*s2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-f64.o main.o -o AdrpLdrGotLdrField-farunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-f64.exe | grep 'ldr\s*d2, \[x1, #8\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdrField.s -c -o AdrpLdrGotLdrField-farunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdrField-farunaligned-v128.o main.o -o AdrpLdrGotLdrField-farunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdrField-farunaligned-v128.exe | grep 'ldr\s*q2, \[x1, #16\]' | ${FAIL_IF_EMPTY} + + + + AdrpAddStr: AdrpAddStr-base AdrpAddStr-seg AdrpAddStr-align AdrpAddStr-addend AdrpAddStr-faraddend true diff --git a/ld64/unit-tests/test-cases/linker_options-library-chain/Makefile b/ld64/unit-tests/test-cases/linker_options-library-chain/Makefile new file mode 100644 index 0000000..240f553 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library-chain/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2013 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 + +# +# main2.o has hint to link with libfoo +# libfoo.a(libfoo2.o) has link to link with libbar.dylib +# + +run: all + +all: + ${CC} ${CCFLAGS} subbar.c -dynamiclib -o libsubbar.dylib + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -Wl,-reexport_library,libsubbar.dylib + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r foo.o -add_linker_option -lbar -o foo2.o + libtool -static foo2.o -o libfoo.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option -lfoo -o main2.o + ${CC} ${CCFLAGS} main2.o -o main -L. + ${DYLDINFO} -lazy_bind main | grep _bar | grep libbar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libsubbar.dylib libbar.dylib foo.o foo2.o libfoo.a main.o main2.o main + diff --git a/ld64/unit-tests/test-cases/linker_options-library-chain/bar.c b/ld64/unit-tests/test-cases/linker_options-library-chain/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library-chain/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/unit-tests/test-cases/linker_options-library-chain/foo.c b/ld64/unit-tests/test-cases/linker_options-library-chain/foo.c new file mode 100644 index 0000000..ac077b9 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library-chain/foo.c @@ -0,0 +1,8 @@ + +extern void bar(); +extern void subbar(); + +void foo() { + bar(); + subbar(); +} diff --git a/ld64/unit-tests/test-cases/linker_options-library-chain/main.c b/ld64/unit-tests/test-cases/linker_options-library-chain/main.c new file mode 100644 index 0000000..4f56fe0 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library-chain/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/linker_options-library-chain/subbar.c b/ld64/unit-tests/test-cases/linker_options-library-chain/subbar.c new file mode 100644 index 0000000..3619a80 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library-chain/subbar.c @@ -0,0 +1 @@ +void subbar() { } diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile index 7dcafac..be39fa7 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -43,7 +43,7 @@ all-armv6: all-rest all-armv7: all-rest all-rest: - # check optimzation of category methods + # check optimization of category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m -framework Foundation -o libfoo.dylib size -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} libfoo.dylib diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile index de4ea0c..37c69f1 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile @@ -51,19 +51,19 @@ all-rest: ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -Wl,-no_objc_category_merging -o libno.dylib size -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN} otool -ov libno.dylib | grep -A17 __objc_classlist | grep -A16 '_OBJC_CLASS_$$_Foo' | grep "count 1" | ${FAIL_IF_EMPTY} - # check optimzation of category methods + # check optimization of category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -o libfoo.dylib size -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 4" | ${FAIL_IF_EMPTY} - # check optimzation of protocol and category methods + # check optimization of protocol and category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROTOCOLS -framework Foundation -o libfoo2.dylib size -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo2.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} - # check optimzation of properties and category methods + # check optimization of properties and category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROPERTIES -framework Foundation -o libfoo3.dylib size -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo3.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} - # check optimzation of category methods and no base methods + # check optimization of category methods and no base methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DNO_BASE_METHODS -framework Foundation -o libfoo4.dylib size -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo4.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 3" | ${FAIL_IF_EMPTY} From fee8dba4849dbe409d271c76b673f2dd029b9f29 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 21 Dec 2016 20:51:23 +0000 Subject: [PATCH 23/48] 274.1 --- ld64/.gitignore | 2 + ld64/doc/man/man1/ld.1 | 463 ++++----- ld64/ld64.xcodeproj/project.pbxproj | 217 ++++- ld64/src/abstraction/MachOFileAbstraction.hpp | 1 - ld64/src/create_configure | 4 +- ld64/src/ld/Architectures.hpp | 1 + ld64/src/ld/HeaderAndLoadCommands.hpp | 52 +- ld64/src/ld/InputFiles.cpp | 217 +++-- ld64/src/ld/InputFiles.h | 7 +- ld64/src/ld/LinkEditClassic.hpp | 62 +- ld64/src/ld/Options.cpp | 296 ++++-- ld64/src/ld/Options.h | 30 +- ld64/src/ld/OutputFile.cpp | 346 ++++++- ld64/src/ld/OutputFile.h | 2 + ld64/src/ld/Resolver.cpp | 62 +- ld64/src/ld/Resolver.h | 3 +- ld64/src/ld/ld.cpp | 226 +++-- ld64/src/ld/ld.hpp | 38 +- ld64/src/ld/parsers/archive_file.cpp | 165 ++-- ld64/src/ld/parsers/generic_dylib_file.hpp | 74 +- ld64/src/ld/parsers/lto_file.cpp | 892 ++++++++++++++---- ld64/src/ld/parsers/lto_file.h | 7 + ld64/src/ld/parsers/macho_dylib_file.cpp | 79 +- .../src/ld/parsers/macho_relocatable_file.cpp | 73 +- ld64/src/ld/parsers/textstub_dylib_file.cpp | 718 +++----------- ld64/src/ld/passes/bitcode_bundle.cpp | 41 +- ld64/src/ld/passes/compact_unwind.cpp | 7 + ld64/src/ld/passes/dylibs.cpp | 4 +- ld64/src/ld/passes/huge.cpp | 45 +- ld64/src/ld/passes/objc.cpp | 472 +++++---- ld64/src/ld/passes/stubs/stub_arm64.hpp | 2 +- ld64/src/ld/passes/stubs/stubs.cpp | 2 +- ld64/src/other/ObjectDump.cpp | 3 + ld64/src/other/dyldinfo.cpp | 13 +- ld64/src/other/machochecker.cpp | 24 +- ld64/src/other/objcimageinfo.cpp | 414 ++++++++ ld64/src/other/unwinddump.cpp | 5 +- ld64/unit-tests/include/common.makefile | 54 +- .../test-cases/alias-basic/Makefile | 2 - .../test-cases/alias-objects/Makefile | 1 - .../test-cases/archive-image_info/Makefile | 2 +- .../test-cases/cfstring-coalesce/Makefile | 4 +- .../test-cases/cfstring-utf16/Makefile | 4 +- .../test-cases/cstring-alt-segment/Makefile | 4 +- .../cstring-custom-section/Makefile | 8 +- .../unit-tests/test-cases/dead_strip/Makefile | 2 +- .../test-cases/dirty-data-alt-entry/1.dirty | 1 + .../test-cases/dirty-data-alt-entry/2.dirty | 1 + .../test-cases/dirty-data-alt-entry/Makefile | 47 + .../test-cases/dirty-data-alt-entry/test.s | 23 + .../test-cases/dwarf-ignore/Makefile | 2 +- .../test-cases/dwarf-strip-objc/Makefile | 2 +- .../test-cases/eh-coalescing/Makefile | 2 +- .../test-cases/lto-archive-objc/Makefile | 23 + .../test-cases/lto-archive-objc/bar.m | 9 + .../test-cases/lto-archive-objc/baz.m | 11 + .../test-cases/lto-archive-objc/foo.c | 8 + .../test-cases/lto-archive-objc/main.c | 6 + .../test-cases/lto-objc-image-info/Makefile | 2 +- .../test-cases/lto-rename_section/Makefile | 6 +- .../test-cases/lto-rename_segment/Makefile | 6 +- .../merge_zero_fill_sections/Makefile | 6 +- .../test-cases/no_zero_fill_sections/Makefile | 4 +- ld64/unit-tests/test-cases/objc-abi/Makefile | 7 +- .../Makefile | 132 +++ .../cat.m | 24 + .../class.m | 9 + .../objc-category-optimize-load/Makefile | 24 +- .../objc-category-optimize/Makefile | 36 +- .../test-cases/objc-category-optimize/cat1.m | 4 + .../test-cases/objc-category-warning/Makefile | 20 +- .../test-cases/objc-class-alias/Makefile | 11 +- .../test-cases/objc-gc-checks/Makefile | 80 +- .../objc-literal-pointers-strip/Makefile | 11 +- .../re-export-relative-paths/Makefile | 8 +- .../test-cases/re-export-relative-paths/baz.c | 5 + .../test-cases/sectcreate-dead_strip/Makefile | 4 +- .../test-cases/slow-x86-stubs/Makefile | 4 +- .../stripped-indirect-symbol-table/Makefile | 4 +- .../symbol-move-and-section-rename/Makefile | 57 ++ .../symbol-move-and-section-rename/foo.list | 2 + .../symbol-move-and-section-rename/foo1.c | 1 + .../symbol-move-and-section-rename/foo2.c | 1 + .../symbol-move-and-section-rename/hot.list | 4 + .../symbol-move-and-section-rename/main.c | 30 + .../symbol-move-and-section-rename/spec.list | 4 + .../test-cases/symbol-section-move/Makefile | 8 +- ld64/unit-tests/test-cases/tlv-dylib/foo.c | 2 +- ld64/unit-tests/test-cases/tlv-dylib/main.c | 6 +- ld64/unit-tests/test-cases/utf16-nul/Makefile | 2 +- .../test-cases/weak_import-disable/Makefile | 29 + .../test-cases/weak_import-disable/foo.c | 12 + .../test-cases/weak_import-disable/main.c | 15 + .../test-cases/weak_import-disable/main2.c | 12 + .../unit-tests/test-cases/zero-fill3/Makefile | 2 +- 95 files changed, 3991 insertions(+), 1888 deletions(-) create mode 100644 ld64/.gitignore create mode 100644 ld64/src/other/objcimageinfo.cpp create mode 100644 ld64/unit-tests/test-cases/dirty-data-alt-entry/1.dirty create mode 100644 ld64/unit-tests/test-cases/dirty-data-alt-entry/2.dirty create mode 100644 ld64/unit-tests/test-cases/dirty-data-alt-entry/Makefile create mode 100644 ld64/unit-tests/test-cases/dirty-data-alt-entry/test.s create mode 100644 ld64/unit-tests/test-cases/lto-archive-objc/Makefile create mode 100644 ld64/unit-tests/test-cases/lto-archive-objc/bar.m create mode 100644 ld64/unit-tests/test-cases/lto-archive-objc/baz.m create mode 100644 ld64/unit-tests/test-cases/lto-archive-objc/foo.c create mode 100644 ld64/unit-tests/test-cases/lto-archive-objc/main.c create mode 100644 ld64/unit-tests/test-cases/objc-category-class-property-mismatch/Makefile create mode 100644 ld64/unit-tests/test-cases/objc-category-class-property-mismatch/cat.m create mode 100644 ld64/unit-tests/test-cases/objc-category-class-property-mismatch/class.m create mode 100644 ld64/unit-tests/test-cases/re-export-relative-paths/baz.c create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/Makefile create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo.list create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo1.c create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo2.c create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/hot.list create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/main.c create mode 100644 ld64/unit-tests/test-cases/symbol-move-and-section-rename/spec.list create mode 100644 ld64/unit-tests/test-cases/weak_import-disable/Makefile create mode 100644 ld64/unit-tests/test-cases/weak_import-disable/foo.c create mode 100644 ld64/unit-tests/test-cases/weak_import-disable/main.c create mode 100644 ld64/unit-tests/test-cases/weak_import-disable/main2.c diff --git a/ld64/.gitignore b/ld64/.gitignore new file mode 100644 index 0000000..9190838 --- /dev/null +++ b/ld64/.gitignore @@ -0,0 +1,2 @@ +build +DerivedData diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index 53fbaa5..30692dc 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -6,14 +6,14 @@ .Nd "linker" .Sh SYNOPSIS .Nm -files... -.Op options -.Op Fl o Ar outputfile +files... +.Op options +.Op Fl o Ar outputfile .Sh DESCRIPTION The .Nm ld command combines several object files and libraries, resolves references, and -produces an ouput file. +produces an ouput file. .Nm ld can produce a final linked image (executable, dylib, or bundle), or with the -r option, produce another object file. If the -o option is not used, the output @@ -21,7 +21,7 @@ file produced is named "a.out". .Ss Universal The linker accepts universal (multiple-architecture) input files, but always creates a "thin" (single-architecture), standard Mach-O output file. -The architecture for the output file is specified using the -arch option. +The architecture for the output file is specified using the -arch option. If this option is not used, .Nm ld attempts to determine the output architecture by examining the object @@ -29,76 +29,76 @@ files in command line order. The first "thin" architecture determines that of the output file. If no input object file is a "thin" file, the native 32-bit architecture for the host is used. .Pp -Usually, +Usually, .Nm ld -is not used directly. Instead the +is not used directly. Instead the .Xr gcc(1) compiler driver invokes .Nm ld. -The compiler driver can be passed multiple -arch options and it will create a -universal final linked image by invoking +The compiler driver can be passed multiple -arch options and it will create a +universal final linked image by invoking .Nm ld multiple times and then running .Xr lipo(1) merge the outputs into a universal file. .Ss Layout The object files are loaded in the order in which they are specified on the -command line. The segments and the sections in those segments will appear in -the output file in the order they are encountered in the object files being linked. +command line. The segments and the sections in those segments will appear in +the output file in the order they are encountered in the object files being linked. All zero fill sections will appear after all non-zero fill sections in their segments. -Sections created from files with the -sectcreate option will be laid out at after -sections from .o files. The use of the -order_file option will alter the layout +Sections created from files with the -sectcreate option will be laid out at after +sections from .o files. The use of the -order_file option will alter the layout rules above, and move the symbols specified to start of their section. .Ss Libraries A static library (aka static archive) is a collection of .o files with a table of contents -that lists the global symbols in the .o files. +that lists the global symbols in the .o files. .Nm ld will only pull .o files out of a static library if needed to resolve some symbol reference. -Unlike traditional linkers, +Unlike traditional linkers, .Nm ld will continually search a static library while linking. There is no need to specify a static -library multiple times on the command line. +library multiple times on the command line. .Pp A dynamic library (aka dylib or framework) is a final linked image. Putting a dynamic -library on the command line causes two things: 1) The generated final linked image +library on the command line causes two things: 1) The generated final linked image will have encoded that it depends on that dynamic library. 2) Exported symbols from the -dynamic library are used to resolve references. +dynamic library are used to resolve references. .Pp -Both dynamic and static libraries are searched as they appear on the command line. +Both dynamic and static libraries are searched as they appear on the command line. .Ss Search paths .Nm ld maintains a list of directories to search for a library or framework to use. The default library search path is /usr/lib then /usr/local/lib. The -L option will add a new library search path. The default framework search path is /Library/Frameworks then /System/Library/Frameworks. (Note: previously, /Network/Library/Frameworks was at the end of the default path. If you need -that functionality, you need to explicitly add -F/Network/Library/Frameworks). +that functionality, you need to explicitly add -F/Network/Library/Frameworks). The -F option will add a new framework search path. The -Z option will remove the standard search paths. The -syslibroot option will prepend a prefix to all search paths. .Ss Two-level namespace -By default all references resolved to a dynamic library record the library to which +By default all references resolved to a dynamic library record the library to which they were resolved. At runtime, dyld uses that information to directly resolve symbols. The alternative is to use the -flat_namespace option. With flat namespace, the library is not recorded. At runtime, dyld will search each dynamic library in load -order when resolving symbols. This is slower, but more like how other operating systems -resolve symbols. +order when resolving symbols. This is slower, but more like how other operating systems +resolve symbols. .Ss Indirect dynamic libraries If the command line specifies to link against dylib A, and when dylib A was built it linked -against dylib B, then B is considered an indirect dylib. -When linking for two-level namespace, ld does not look at indirect dylibs, except when -re-exported by a direct dylibs. On the other hand when linking for flat namespace, +against dylib B, then B is considered an indirect dylib. +When linking for two-level namespace, ld does not look at indirect dylibs, except when +re-exported by a direct dylibs. On the other hand when linking for flat namespace, ld does load all indirect dylibs and uses them to resolve references. -Even though indirect dylibs are specified via a full path, +Even though indirect dylibs are specified via a full path, .Nm ld first uses the specified search paths to locate each indirect dylib. If one cannot be found using the search paths, the full path is used. .Ss Dynamic libraries undefines -When linking for two-level namespace, -.Nm ld +When linking for two-level namespace, +.Nm ld does not verify that undefines in dylibs actually -exist. But when linking for flat namespace, +exist. But when linking for flat namespace, .Nm ld -does check that all undefines from all loaded dylibs have a matching definition. +does check that all undefines from all loaded dylibs have a matching definition. This is sometimes used to force selected functions to be loaded from a static library. .Sh OPTIONS .Ss Options that control the kind of output @@ -110,13 +110,13 @@ Produce a mach-o shared library that has file type MH_DYLIB. .It Fl bundle Produce a mach-o bundle that has file type MH_BUNDLE. .It Fl r -Merges object files to produce another mach-o object file with file type MH_OBJECT. +Merges object files to produce another mach-o object file with file type MH_OBJECT. .It Fl dylinker Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when building dyld. .It Fl dynamic The default. Implied by -dylib, -bundle, or -execute .It Fl static -Produces a mach-o file that does not use the dyld. Only used building the kernel. +Produces a mach-o file that does not use the dyld. Only used building the kernel. .It Fl preload Produces a mach-o file in which the mach_header, load commands, and symbol table are not in any segment. This output type is used for firmware or embedded development @@ -137,44 +137,44 @@ This is the same as the -lx but forces the library and all references to it to b That is, the library is allowed to be missing at runtime. .It Fl weak_library Ar path_to_library This is the same as listing a file name path to a library on the link line except that it forces the -library and all references to it to be marked as weak imports. +library and all references to it to be marked as weak imports. .It Fl reexport-l Ns Ar x -This is the same as the -lx but specifies that the all symbols in library x should be available to +This is the same as the -lx but specifies that the all symbols in library x should be available to clients linking to the library being created. This was previously done with a separate -sub_library option. .It Fl reexport_library Ar path_to_library -This is the same as listing a file name path to a library on the link line and it specifies that the +This is the same as listing a file name path to a library on the link line and it specifies that the all symbols in library path should be available to clients linking to the library being created. This was previously done with a separate -sub_library option. .It Fl lazy-l Ns Ar x This is the same as the -lx but it is only for shared libraries and the linker -will construct glue code so that the shared library is not loaded until +will construct glue code so that the shared library is not loaded until the first function in it is called. .It Fl lazy_library Ar path_to_library This is the same as listing a file name path to a shared library on the link line -except that the linker will construct glue code so that the shared library is not +except that the linker will construct glue code so that the shared library is not loaded until the first function in it is called. .It Fl upward-l Ns Ar x -This is the same as the -lx but specifies that the dylib is an upward dependency. +This is the same as the -lx but specifies that the dylib is an upward dependency. .It Fl upward_library Ar path_to_library -This is the same as listing a file name path to a library on the link line but also marks +This is the same as listing a file name path to a library on the link line but also marks the dylib as an upward dependency. .It Fl L Ns dir -Add -.Ar dir -to the list of directories in which to search for libraries. +Add +.Ar dir +to the list of directories in which to search for libraries. Directories specified with -L are searched in the order they appear on the command line and before the default search path. In Xcode4 and later, there can be a space between the -L and directory. .It Fl Z Do not search the standard directories when searching for libraries and frameworks. .It Fl syslibroot Ar rootdir -Prepend -.Ar rootdir +Prepend +.Ar rootdir to all search paths when searching for libraries or frameworks. .It Fl search_paths_first -This is now the default (in Xcode4 tools). When processing -lx the linker now searches each directory -in its library search paths for `libx.dylib' then `libx.a' before the moving on to the next path -in the library search path. +This is now the default (in Xcode4 tools). When processing -lx the linker now searches each directory +in its library search paths for `libx.dylib' then `libx.a' before the moving on to the next path +in the library search path. .It Fl search_dylibs_first Changes the searching behavior for libraries. The default is that when processing -lx the linker searches each directory in its library search paths for `libx.dylib' then `libx.a'. @@ -186,24 +186,24 @@ This option tells the linker to search for `name.framework/name' the framework s If the optional suffix is specified the framework is first searched for the name with the suffix and then without (e.g. look for `name.framework/name_suffix' first, if not there try `name.framework/name'). .It Fl weak_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] but forces the framework and all -references to it to be marked as weak imports. +This is the same as the -framework name[,suffix] but forces the framework and all +references to it to be marked as weak imports. .It Fl reexport_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] but also specifies that the +This is the same as the -framework name[,suffix] but also specifies that the all symbols in that framework should be available to clients linking to the library being created. This was previously done with a separate -sub_umbrella option. .It Fl lazy_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] except that the linker will -construct glue code so that the framework is not +This is the same as the -framework name[,suffix] except that the linker will +construct glue code so that the framework is not loaded until the first function in it is called. You cannot directly access data or Objective-C classes in a framework linked this way. .It Fl upward_framework Ar name[,suffix] -This is the same as the -framework name[,suffix] but also specifies that the -framework is an upward dependency. +This is the same as the -framework name[,suffix] but also specifies that the +framework is an upward dependency. .It Fl F Ns dir -Add +Add .Ar dir -to the list of directories in which to search for frameworks. +to the list of directories in which to search for frameworks. Directories specified with -F are searched in the order they appear on the command line and before the default search path. In Xcode4 and later, there can be a space between the -F and directory. @@ -218,42 +218,42 @@ archives to be loaded. This option allows you to target a specific archive. .Ss Options that control additional content .Bl -tag .It Fl sectcreate Ar segname sectname file -The section -.Ar sectname -in the segment -.Ar segname -is created from the contents of file -.Ar file. -The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname) +The section +.Ar sectname +in the segment +.Ar segname +is created from the contents of file +.Ar file. +The combination of segname and sectname must be unique Ð there cannot already be a section (segname,sectname) from any other input. .It Fl filelist Ar file[,dirname] -Specifies that the linker should link the files listed in -.Ar file . -This is an alternative to listing the files on the command line. +Specifies that the linker should link the files listed in +.Ar file . +This is an alternative to listing the files on the command line. The file names are listed one per line separated only by newlines. (Spaces and tabs are assumed to be part of the file name.) -If the optional directory name, -.Ar dirname +If the optional directory name, +.Ar dirname is specified, it is prepended to each name in the list file. .It Fl dtrace Ar file -Enables dtrace static probes when producing a final linked image. The file +Enables dtrace static probes when producing a final linked image. The file .Ar file must be a DTrace script which declares the static probes. .El -.Ss Options that control optimizations +.Ss Options that control optimizations .Bl -tag .It Fl dead_strip -Remove functions and data that are unreachable by the entry point or exported symbols. +Remove functions and data that are unreachable by the entry point or exported symbols. .It Fl order_file Ar file Alters the order in which functions and data are laid out. For each section in the output file, -any symbol in that section that are specified in the order file +any symbol in that section that are specified in the order file .Ar file -is moved to the start of its section and laid out in the same order as in the order file +is moved to the start of its section and laid out in the same order as in the order file .Ar file . Order files are text files with one symbol name per line. Lines starting with a # are comments. -A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo). -This is useful for static functions/data that occur in multiple files. +A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo). +This is useful for static functions/data that occur in multiple files. A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). -This enables you to have one order file that works for multiple architectures. +This enables you to have one order file that works for multiple architectures. Literal c-strings may be ordered by by quoting the string (e.g. "Hello, world\\n") in the order file. .It Fl no_order_inits When the -order_file option is not used, the linker lays out functions in object file order and @@ -279,22 +279,22 @@ Specifies the perferred load address for a dylib or bundle. The argument is a hexadecimal number with an optional leading 0x. By choosing non-overlapping address for all dylibs and bundles that a program loads, launch time can be improved because dyld will not need to "rebase" the image (that is, adjust pointers within the image to work at the loaded address). -It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs. +It is often easier to not use this option, but instead use the rebase(1) tool, and give it a list of dylibs. It will then choose non-overlapping addresses for the list and rebase them all. This option is also called -seg1addr for compatibility. .It Fl no_implicit_dylibs -When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs +When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs that are implicitly linked to make the two-level namespace -encoding more efficient for dyld. For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. +encoding more efficient for dyld. For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. If you link with -framework Cocoa and use a symbol from Foundation, the linker will implicitly add a load -command to load Foundation and encode the symbol as coming from Foundation. If you use this option, +command to load Foundation and encode the symbol as coming from Foundation. If you use this option, the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation. .It Fl exported_symbols_order Ar file When targeting Mac OS X 10.6 or later, the format of the exported symbol information can be optimized to make lookups of popular symbols faster. This option is used to pass a file containing a list of the symbols most frequently used by clients of the dynamic library being built. Not all exported symbols -need to be listed. +need to be listed. .It Fl no_zero_fill_sections 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 @@ -306,32 +306,32 @@ Disables linker creation of branch islands which allows images to be created tha maximum branch distance. Useful with -preload when code is in multiple sections but all are within the branch range. .El -.Ss Options when creating a dynamic library (dylib) +.Ss Options when creating a dynamic library (dylib) .Bl -tag .It Fl install_name Ar name -Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library -will record that path as the way dyld should locate this library. If this option is not specified, then +Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library +will record that path as the way dyld should locate this library. If this option is not specified, then the -o path will be used. This option is also called -dylib_install_name for compatibility. .It Fl mark_dead_strippable_dylib -Specifies that the dylib being built can be dead strip by any client. That is, the dylib has +Specifies that the dylib being built can be dead strip by any client. That is, the dylib has no initialization side effects. So if a client links against the dylib, but never uses any symbol from it, the linker can optimize away the use of the dylib. .It Fl compatibility_version Ar number -Specifies the compatibility version number of the library. When a library is loaded by dyld, the +Specifies the compatibility version number of the library. When a library is loaded by dyld, the compatibility version is checked and if the program's version is greater that the library's version, it is an error. -The format of -.Ar number -is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, -and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. -If the compatibility version number is not specified, it has a value of 0 and no checking is done when the library is used. +The format of +.Ar number +is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, +and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. +If the compatibility version number is not specified, it has a value of 0 and no checking is done when the library is used. This option is also called -dylib_compatibility_version for compatibility. .It Fl current_version Ar number -Specifies the current version number of the library. The current version of the library can be obtained -programmatically by the user of the library so it can determine exactly which version of the library it is using. -The format of -.Ar number -is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, -and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. +Specifies the current version number of the library. The current version of the library can be obtained +programmatically by the user of the library so it can determine exactly which version of the library it is using. +The format of +.Ar number +is X[.Y[.Z]] where X must be a positive non-zero number less than or equal to 65535, +and .Y and .Z are optional and if present must be non-negative numbers less than or equal to 255. If the version number is not specified, it has a value of 0. This option is also called -dylib_current_version for compatibility. .El @@ -339,8 +339,8 @@ This option is also called -dylib_current_version for compatibility. .Bl -tag .It Fl pie This makes a special kind of main executable that is position independent (PIE). On Mac OS X 10.5 and later, the OS -the OS will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled -with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some +the OS will load a PIE at a random address each time it is executed. You cannot create a PIE from .o files compiled +with -mdynamic-no-pic. That means the codegen is less optimal, but the address randomization adds some security. When targeting Mac OS X 10.7 or later PIE is the default for main executables. .It Fl no_pie Do not make a position independent executable (PIE). This is the default, when targeting 10.6 and earlier. @@ -348,11 +348,11 @@ Do not make a position independent executable (PIE). This is the default, when By default the linker creates an unreadable segment starting at address zero named __PAGEZERO. Its existence will cause a bus error if a NULL pointer is dereferenced. The argument .Ar size -is a hexadecimal number with an optional leading 0x. If +is a hexadecimal number with an optional leading 0x. If .Ar size -is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size +is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size is 4KB. On 64-bit architectures, the default size is 4GB. The ppc64 architecture has some special cases. Since Mac -OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless +OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless -macosx_version_min is 10.5 or later. Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero size is set to 4KB and then a new unreadable trailing segment is created after the code, filling up the lower 4GB. @@ -360,31 +360,31 @@ size is set to 4KB and then a new unreadable trailing segment is created after t Specifies the maximum stack size for the main thread in a program. Without this option a program has a 8MB stack. The argument .Ar size -is a hexadecimal number with an optional leading 0x. The +is a hexadecimal number with an optional leading 0x. The .Ar size should be a multiple of the architecture's page size (4KB or 16KB). -.It Fl allow_stack_execute +.It Fl allow_stack_execute Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. .It Fl export_dynamic Preserves all global symbols in main executables during LTO. Without this option, Link Time Optimization is allowed to inline and remove global functions. This option is used when a main executable may load -a plug-in which requires certain symbols from the main executable. +a plug-in which requires certain symbols from the main executable. .El .Ss Options when creating a bundle .Bl -tag .It Fl bundle_loader Ar executable -This specifies the -.Ar executable -that will be loading the bundle output file being linked. -Undefined symbols from the bundle are checked against the specified -.Ar executable -like it was one of the +This specifies the +.Ar executable +that will be loading the bundle output file being linked. +Undefined symbols from the bundle are checked against the specified +.Ar executable +like it was one of the dynamic libraries the bundle was linked with. .El .Ss Options when creating an object file .Bl -tag .It Fl keep_private_externs -Don't turn private external (aka visibility=hidden) symbols into static symbols, +Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d Force definition of common symbols. That is, transform tentative definitions into real definitions. @@ -392,85 +392,85 @@ Force definition of common symbols. That is, transform tentative definitions in .Ss Options that control symbol resolution .Bl -tag .It Fl exported_symbols_list Ar filename -The specified -.Ar filename -contains a list of global symbol names that will remain as global symbols in the output file. -All other global symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) +The specified +.Ar filename +contains a list of global symbol names that will remain as global symbols in the output file. +All other global symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) and will not be global in the output file. The symbol names listed in filename must be one per line. -Leading and trailing white space are not part of the symbol name. +Leading and trailing white space are not part of the symbol name. Lines starting with # are ignored, as are lines with only white space. Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches -any single lower case letter from 'a' to 'z'. +any single lower case letter from 'a' to 'z'. .It Fl exported_symbol Ar symbol -The specified +The specified .Ar symbol -is added to the list of global symbols names that will remain as global symbols in the output file. This -option can be used multiple times. For short lists, this can be more convenient than creating a file and using +is added to the list of global symbols names that will remain as global symbols in the output file. This +option can be used multiple times. For short lists, this can be more convenient than creating a file and using -exported_symbols_list. .It Fl unexported_symbols_list Ar file -The specified -.Ar filename -contains a list of global symbol names that will not remain as global symbols in the output file. +The specified +.Ar filename +contains a list of global symbol names that will not remain as global symbols in the output file. The symbols will be treated as if they were marked as __private_extern__ (aka visibility=hidden) and will not be global -in the output file. The symbol names listed in filename must be one per line. -Leading and trailing white space are not part of the symbol name. +in the output file. The symbol names listed in filename must be one per line. +Leading and trailing white space are not part of the symbol name. Lines starting with # are ignored, as are lines with only white space. Some wildcards (similar to shell file matching) are supported. The * matches zero or more characters. The ? matches one character. [abc] matches one character which must be an 'a', 'b', or 'c'. [a-z] matches -any single lower case letter from 'a' to 'z'. +any single lower case letter from 'a' to 'z'. .It Fl unexported_symbol Ar symbol -The specified +The specified .Ar symbol -is added to the list of global symbols names that will not remain as global symbols in the output file. This -option can be used multiple times. For short lists, this can be more convenient than creating a file and using +is added to the list of global symbols names that will not remain as global symbols in the output file. This +option can be used multiple times. For short lists, this can be more convenient than creating a file and using -unexported_symbols_list. .It Fl reexported_symbols_list Ar file -The specified -.Ar filename +The specified +.Ar filename contains a list of symbol names that are implemented in a dependent dylib and should be re-exported -through the dylib being created. +through the dylib being created. .It Fl alias Ar symbol_name Ar alternate_symbol_name -Create an alias named +Create an alias named .Ar alternate_symbol_name for the symbol .Ar symbol_name . By default the alias symbol has global visibility. This option was previous the -idef:indir option. .It Fl alias_list Ar filename -The specified +The specified .Ar filename contains a list of aliases. The symbol name and its alias are on one line, separated by whitespace. Lines starting with # are ignored. -.It Fl flat_namespace +.It Fl flat_namespace Alters how symbols are resolved at build time and runtime. With -two_levelnamespace (the default), the linker -only searches dylibs on the command line for symbols, and records in which dylib they were found. With -flat_namespace, +only searches dylibs on the command line for symbols, and records in which dylib they were found. With -flat_namespace, the linker searches all dylibs on the command line and all dylibs those original dylibs depend on. The linker does not record which dylib an external symbol came from, so at runtime dyld again searches all images and uses the first definition it finds. In addition, any undefines in loaded flat_namespace dylibs must be resolvable -at build time. +at build time. .It Fl u Ar symbol_name -Specified that symbol +Specified that symbol .Ar symbol_name must be defined for the link to succeed. This is useful to force selected functions to be loaded from a static library. .It Fl U Ar symbol_name -Specified that it is ok for +Specified that it is ok for .Ar symbol_name to have no definition. With -two_levelnamespace, the resulting symbol will be marked dynamic_lookup which means dyld will search all loaded images. .It Fl undefined Ar treatment Specifies how undefined symbols are to be treated. Options are: error, warning, suppress, or dynamic_lookup. The -default is error. +default is error. .It Fl rpath Ar path -Add +Add .Ar path to the runpath search path list for image being created. At runtime, dyld uses the runpath when searching for dylibs whose load path begins with @rpath/. .It Fl commons Ar treatment -Specifies how commons (aka tentative definitions) are resolved with respect to dylibs. Options are: +Specifies how commons (aka tentative definitions) are resolved with respect to dylibs. Options are: ignore_dylibs, use_dylibs, error. The default is ignore_dylibs which means the linker will turn a tentative definition in an object file into a real definition and not even check dylibs for conflicts. The dylibs -option means the linker should check linked dylibs for definitions and use them to replace tentative definitions +option means the linker should check linked dylibs for definitions and use them to replace tentative definitions from object files. The error option means the linker should issue an error whenever a tentative definition in an object file conflicts with an external symbol in a linked dylib. See also -warn_commons. .El @@ -480,7 +480,7 @@ object file conflicts with an external symbol in a linked dylib. See also -warn Log why each object file in a static library is loaded. That is, what symbol was needed. Also called -whyload for compatibility. .It Fl why_live Ar symbol_name -Logs a chain of references to +Logs a chain of references to .Ar symbol_name . Only applicable with -dead_strip . It can help debug why something that you think should be dead strip removed is not removed. @@ -508,15 +508,15 @@ that will be automatically removed when linked into a final linked image. This allows dead code stripping, which uses symbols to break up code and data, to work properly and provides the security of having source symbol names removed. .It Fl non_global_symbols_strip_list Ar filename -The specified -.Ar filename -contains a list of non-global symbol names that should be removed from the output file's symbol table. All other +The specified +.Ar filename +contains a list of non-global symbol names that should be removed from the output file's symbol table. All other non-global symbol names will remain in the output files symbol table. See -exported_symbols_list for syntax and use of wildcards. .It Fl non_global_symbols_no_strip_list Ar filename -The specified -.Ar filename -contains a list of non-global symbol names that should be remain in the output file's symbol table. All other +The specified +.Ar filename +contains a list of non-global symbol names that should be remain in the output file's symbol table. All other symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use of wildcards. .El @@ -526,7 +526,7 @@ of wildcards. Generates an embedded bitcode bundle in the output binary. The bitcode bundle is embedded in __LLVM, __bundle section. This option requires all the object files, static libraries and user frameworks/dylibs contain bitcode. Note: not all the linker options are supported to use together with -bitcode_bundle. -.It Fl bitcode_hide_symbol +.It Fl bitcode_hide_symbols Specifies this option together with -bitcode_bundle to hide all non-exported symbols from output bitcode bundle. The hide symbol process might not be reversible. To obtain a reverse mapping file to recover all the symbols, use -bitcode_symbol_map option. @@ -541,6 +541,9 @@ Otherwise, the reverse map will be written to a file at .Bl -tag .It Fl v Prints the version of the linker. +.It Fl no_weak_imports +Error if any symbols are weak imports (i.e. allowed to be unresolved (NULL) at runtime). Useful for config based +projects that assume they are built and run on the same OS version. .It Fl no_deduplicate Don't run deduplication pass in linker .It Fl verbose_deduplicate @@ -561,34 +564,33 @@ Moves data symbols to another segment. The command line option specifies the target segment name and a path to a file containing a list of symbols to move. Comments can be added to the symbol file by starting a line with a #. If there are multiple instances of a symbol name (for instance a "static int foo=5;" in multiple files) -the symbol name in the symbol list file can be prefixed with the object file name +the symbol name in the symbol list file can be prefixed with the object file name (e.g. "init.o:_foo") to move a specific instance. -.It Fl move_to_ro_section Ar segment_name Ar section_name Ar filename +.It Fl move_to_ro_segment Ar segment_name Ar filename Moves code symbols to another segment. The command line option specifies the target segment name and a path to a file containing a list of symbols to move. Comments can be added to the symbol file by starting a line with a #. If there are multiple instances of a symbol name (for instance a "static int foo() {}" in multiple files) -the symbol name in the symbol list file can be prefixed with the object file name +the symbol name in the symbol list file can be prefixed with the object file name (e.g. "init.o:_foo") to move a specific instance. .It Fl rename_section Ar orgSegment orgSection newSegment newSection Renames section orgSegment/orgSection to newSegment/newSection. -.It Fl rename_segment Ar orgSegment newSegment +.It Fl rename_segment Ar orgSegment newSegment Renames all sections with orgSegment segment name to have newSegment segment name. .It Fl trace_symbol_layout For using in debugging -rename_section, -rename_segment, -move_to_ro_segment, and -move_to_rw_segment. This option prints out a line show where and why each symbol was moved. -Note: These options do not chain. For each symbol, the linker first checks --move_to_ro_segment and -move_to_rw_segment. If the symbol is not moved, -it checks for an applicable -rename_section. Only if the symbol still has -not been moved, does the linker look for an applicable -rename_segment option. +Note: These options do chain. For each symbol, the linker first checks +-move_to_ro_segment and -move_to_rw_segment. Next it applies any -rename_section options, +and lastly and -rename_segment options. .It Fl section_order Ar segname Ar colon_separated_section_list Only for use with -preload. Specifies the order that sections with the specified segment should be layout out. -For example: "-section_order __ROM __text:__const:__cstring". +For example: "-section_order __ROM __text:__const:__cstring". .It Fl segment_order Ar colon_separated_segment_list Only for use with -preload. Specifies the order segments should be layout out. -For example: "-segment_order __ROM:__ROM2:__RAM". +For example: "-segment_order __ROM:__ROM2:__RAM". .It Fl allow_heap_execute -Normally i386 main executables will be marked so that the Mac OS X 10.7 and later kernel +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 behavior and allows instructions on any page to be executed. .It Fl application_extension @@ -596,7 +598,7 @@ Specifies that the code is being linked for use in an application extension. Th will then validiate that any dynamic libraries linked against are safe for use in application extensions. .It Fl no_application_extension -Specifies that the code is being linked is not safe for use in an application extension. +Specifies that the code is being linked is not safe for use in an application extension. For instance, can be used when creating a framework that should not be used in an application extension. .It Fl fatal_warnings @@ -608,22 +610,22 @@ linker but are needed by earlier linker tools. .It Fl warn_compact_unwind When producing a final linked image, the linker processes the __eh_frame section and produces an __unwind_info section. Most FDE entries in the __eh_frame can be represented -by a 32-bit value in the __unwind_info section. The option issues a warning for +by a 32-bit value in the __unwind_info section. The option issues a warning for any function whose FDE cannot be expressed in the compact unwind format. .It Fl warn_weak_exports Issue a warning if the resulting final linked image contains weak external symbols. Such symbols require dyld to do extra work at launch time to coalesce those symbols. .It Fl objc_gc_compaction -Marks the Objective-C image info in the final linked image with the bit that says that the +Marks the Objective-C image info in the final linked image with the bit that says that the code was built to work the compacting garbage collection. .It Fl objc_gc Verifies all code was compiled with -fobjc-gc or -fobjc-gc-only. .It Fl objc_gc_only Verifies all code was compiled with -fobjc-gc-only. .It Fl dead_strip_dylibs -Remove dylibs that are unreachable by the entry point or exported symbols. That is, +Remove dylibs that are unreachable by the entry point or exported symbols. That is, suppresses the generation of load command commands for dylibs which supplied no -symbols during the link. This option should not be used when linking against a dylib which +symbols during the link. This option should not be used when linking against a dylib which is required at runtime for some indirect reason such as the dylib has an important initializer. .It Fl allow_sub_type_mismatches Normally the linker considers different cpu-subtype for ARM (e.g. armv4t and armv6) to be different @@ -636,32 +638,32 @@ Sets the MH_ROOT_SAFE bit in the mach header of the output file. .It Fl setuid_safe Sets the MH_SETUID_SAFE bit in the mach header of the output file. .It Fl interposable -Indirects access to all to exported symbols when creating a dynamic library. +Indirects access to all to exported symbols when creating a dynamic library. .It Fl init Ar symbol_name -The specified symbol_name will be run as the first initializer. Only used when creating a dynamic library. +The specified symbol_name will be run as the first initializer. Only used when creating a dynamic library. .It Fl sub_library Ar library_name -The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc. -Only used when creating a dynamic library. +The specified dylib will be re-exported. For example the library_name for /usr/lib/libobjc_profile.A.dylib would be libobjc. +Only used when creating a dynamic library. .It Fl sub_umbrella Ar framework_name -The specified framework will be re-exported. Only used when creating a dynamic library. +The specified framework will be re-exported. Only used when creating a dynamic library. .It Fl allowable_client Ar name -Restricts what can link against the dynamic library being created. By default any code +Restricts what can link against the dynamic library being created. By default any code can link against any dylib. But if a dylib is supposed to be private to a small set of clients, you can formalize that by adding a -allowable_client for each client. If a client is libfoo.1.dylib its -allowable_client name would be "foo". If a client is Foo.framework its -allowable_client name would be "Foo". For the degenerate -case where you want no one to ever link against a dylib, you can set the --allowable_client to "!". +case where you want no one to ever link against a dylib, you can set the +-allowable_client to "!". .It Fl client_name Ar name -Enables a bundle to link against a dylib that was built with -allowable_client. +Enables a bundle to link against a dylib that was built with -allowable_client. The name specified must match one of the -allowable_client names specified when the dylib was created. .It Fl umbrella Ar framework_name Specifies that the dylib being linked is re-exported through an umbrella framework of the specified name. .It Fl headerpad Ar size -Specifies the minimum space for future expansion of the load commands. Only useful if intend to run +Specifies the minimum space for future expansion of the load commands. Only useful if intend to run install_name_tool to alter the load commands later. Size is a hexadecimal number. .It Fl headerpad_max_install_names -Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. +Automatically adds space for future expansion of load commands such that all paths could expand to MAXPATHLEN. Only useful if intend to run install_name_tool to alter the load commands later. .It Fl bind_at_load Sets a bit in the mach header of the resulting binary which tells dyld to bind all symbols when the binary is loaded, rather than lazily. @@ -669,43 +671,43 @@ Sets a bit in the mach header of the resulting binary which tells dyld to bind a Sets a bit in the mach header of the resulting binary which tells dyld to not only use flat namespace for the binary, but force flat namespace binding on all dylibs and bundles loaded in the process. Can only be used when linking main executables. .It Fl sectalign Ar segname Ar sectname Ar value -The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal -number that must be an integral power of 2. +The section named sectname in the segment segname will have its alignment set to value, where value is a hexadecimal +number that must be an integral power of 2. .It Fl stack_addr Ar address Specifies the initial address of the stack pointer value, where value is a hexadecimal number rounded to a page boundary. .It Fl segprot Ar segname Ar max_prot Ar init_prot -Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively. +Specifies the maximum and initial virtual memory protection of the named segment, name, to be max and init ,respectively. The values for max and init are any combination of the characters `r' (for read), `w' (for write), `x' (for execute) and `-' (no access). .It Fl seg_addr_table Ar filename -Specifies a file containing base addresses for dynamic libraries. Each line of the file is a hexadecimal base address +Specifies a file containing base addresses for dynamic libraries. Each line of the file is a hexadecimal base address followed by whitespace then the install name of the corresponding dylib. The # character denotes a comment. .It Fl segs_read_write_addr Ar address -Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address +Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address specified is a hexadecimal number that indicates the base address for the read-write segments. .It Fl segs_read_only_addr Ar address -Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address +Allows a dynamic library to be built where the read-only and read-write segments are not contiguous. The address specified is a hexadecimal number that indicates the base address for the read-only segments. .It Fl segaddr Ar name Ar address -Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number +Specifies the starting address of the segment named name to be address. The address must be a hexadecimal number that is a multiple of 4K page size. .It Fl seg_page_size Ar name Ar size -Specifies the page size used by the specified segment. By default the page size is 4096 for all segments. +Specifies the page size used by the specified segment. By default the page size is 4096 for all segments. The linker will lay out segments such that size of a segment is always an even multiple of its page size. .It Fl dylib_file Ar install_name:file_name -Specifies that a dynamic shared library is in a different location than its standard location. Use this option -when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other -than its default location. install_name specifies the path where the library normally resides. file_name specifies -the path of the library you want to use instead. For example, if you link to a library that depends upon the dynamic -library libsys and you have libsys installed in a nondefault location, you would use this option: +Specifies that a dynamic shared library is in a different location than its standard location. Use this option +when you link with a library that is dependent on a dynamic library, and the dynamic library is in a location other +than its default location. install_name specifies the path where the library normally resides. file_name specifies +the path of the library you want to use instead. For example, if you link to a library that depends upon the dynamic +library libsys and you have libsys installed in a nondefault location, you would use this option: -dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib. .It Fl prebind -The created output file will be in the prebound format. This was used in Mac OS X 10.3 and earlier to improve launch performance. +The created output file will be in the prebound format. This was used in Mac OS X 10.3 and earlier to improve launch performance. .It Fl weak_reference_mismatches Ar treatment -Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another. The valid +Specifies what to do if a symbol is weak-imported in one object file but not weak-imported in another. The valid treatments are: error, weak, or non-weak. The default is non-weak. .It Fl read_only_relocs Ar treatment -Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages. The compiler will -normally never generate such code. +Enables the use of relocations which will cause dyld to modify (copy-on-write) read-only pages. The compiler will +normally never generate such code. .It Fl force_cpusubtype_ALL The is only applicable with -arch ppc. It tells the linker to ignore the PowerPC cpu requirements (e.g. G3, G4 or G5) encoded in the object files and mark the resulting binary as runnable on any PowerPC cpu. @@ -714,20 +716,20 @@ Only used when building dyld. .It Fl no_arch_warnings Suppresses warning messages about files that have the wrong architecture for the -arch flag .It Fl arch_errors_fatal -Turns into errors, warnings about files that have the wrong architecture for the -arch flag. +Turns into errors, warnings about files that have the wrong architecture for the -arch flag. .It Fl e Ar symbol_name Specifies the entry point of a main executable. By default the entry name is "start" which is found in crt1.o which contains the glue code need to set up and call main(). .It Fl w Suppress all warning messages .It Fl final_output Ar name -Specifies the install name of a dylib if -install_name is not used. This option is used by gcc driver when it is invoked -with multiple -arch arguments. +Specifies the install name of a dylib if -install_name is not used. This option is used by gcc driver when it is invoked +with multiple -arch arguments. .It Fl arch_multiple -Specifes that the linker should augment error and warning messages with the architecture name. This option is used by gcc -driver when it is invoked with multiple -arch arguments. +Specifes that the linker should augment error and warning messages with the architecture name. This option is used by gcc +driver when it is invoked with multiple -arch arguments. .It Fl twolevel_namespace_hints -Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the +Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the libraries being linked against have not changed. .It Fl dot Ar path Create a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. @@ -743,17 +745,17 @@ in a header file. .It Fl read_only_stubs [i386 only] Makes the __IMPORT segment of a final linked images read-only. This option makes a program slightly more secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code. The downside -is the dyld must use mprotect() to temporarily make the segment writable while it is binding the stubs. +is the dyld must use mprotect() to temporarily make the segment writable while it is binding the stubs. .It Fl slow_stubs -[i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which -calls through a lazy pointer in the __DATA segment. +[i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which +calls through a lazy pointer in the __DATA segment. .It Fl interposable_list Ar filename -The specified -.Ar filename +The specified +.Ar filename contains a list of global symbol names that should always be accessed indirectly. For instance, if libSystem.dylib is linked such that _malloc is interposable, then calls to malloc() from within libSystem will go through a dyld -stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc -interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed +stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc +interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed (not interposed) because they would be direct calls. .It Fl no_function_starts By default the linker creates a compress table of function start addresses in the LINKEDIT of @@ -768,16 +770,27 @@ being linked for the optimization to occur. Using this option disables that beh .It Fl object_path_lto Ar filename When performing Link Time Optimization (LTO) and a temporary mach-o object file is needed, if this option is used, the temporary file will be stored at the specified path and remain after the link -is complete. Without the option, the linker picks a path and deletes the object file before the linker +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 lto_library Ar path When performing Link Time Optimization (LTO), the linker normally loads libLTO.dylib relative to the linker binary (../lib/libLTO.dylib). This option allows the user to specify the path to a specific libLTO.dylib to load instead. +.It Fl cache_path_lto Ar path +When performing Incremental Link Time Optimization (LTO), use this directory as a cache for incremental rebuild. +.It Fl prune_interval_lto Ar seconds +When performing Incremental Link Time Optimization (LTO), the cache will pruned after the specified interval. +.It Fl prune_after_lto Ar seconds +When pruning the cache for Incremental Link Time Optimization (LTO), the cache entries are removed after the +specificied interval. +.It Fl max_relative_cache_size_lto Ar percent +When performing Incremental Link Time Optimization (LTO), the cache will be pruned to not go over this percentage +of the free space. I.e. a value of 100 would indicate that the cache may fill the disk, and a value of 50 would +indicate that the cache size will be kept under the free disk space. .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 +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). .It Fl not_for_dyld_shared_cache Normally, the linker will add extra info to dylibs with -install_name starting with /usr/lib or @@ -787,7 +800,7 @@ tells the linker to not add that extra info. .Ss Obsolete Options .Bl -tag .It Fl segalign Ar value -All segments must be page aligned. +All segments must be page aligned. .It Fl seglinkedit Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. .It Fl noseglinkedit @@ -808,13 +821,13 @@ When using -prebind, the linker allows overlapping by default, so this option is LD_PREBIND is no longer supported as a way to force on prebinding, so there no longer needs to be a command line way to override LD_PREBIND. This option is obsolete. .It Fl sect_diff_relocs Ar treatment -This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into +This option was an attempt to warn about linking .o files compiled without -mdynamic-no-pic into a main executable, but the false positive rate generated too much noise to make the option useful. This option is obsolete. .It Fl run_init_lazily This option was removed in Mac OS X 10.2. .It Fl single_module -This is now the default so does not need to be specified. +This is now the default so does not need to be specified. .It Fl multi_module Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. .It Fl no_dead_strip_inits_and_terms @@ -824,7 +837,7 @@ Obsolete incremental load format. This option is obsolete. .It Fl b Used with -A option to strip base file's symbols. This option is obsolete. ..It Fl M -Obsolete option to produce a load map. Use -map option instead. +Obsolete option to produce a load map. Use -map option instead. .It Fl Sn Don't strip any symbols. This is the default. This option is obsolete. .It Fl Si @@ -835,15 +848,15 @@ This style of debugging is obsolete in Mac OS X 10.5. This option is obsolete. .It Fl X Strip local symbols that begin with 'L'. This is the default. This option is obsolete. .It Fl s -Completely strip the output, including removing the symbol table. This file format variant is no longer supported. +Completely strip the output, including removing the symbol table. This file format variant is no longer supported. This option is obsolete. .It Fl m Don't treat multiple definitions as an error. This is no longer supported. This option is obsolete. .It Fl y Ns symbol -Display each file in which +Display each file in which .Ar symbol is used. This was previously used to debug where an undefined symbol was used, but the linker now -automatically prints out all usages. The -why_live option can also be used to display what kept +automatically prints out all usages. The -why_live option can also be used to display what kept a symbol from being dead striped. This option is obsolete. .It Fl Y Ar number Used to control how many occurrences of each symbol specified with -y would be shown. This option is obsolete. @@ -854,23 +867,23 @@ bit has been obsolete since Mac OS X 10.4. This option is obsolete. Previously provided a way to warn or error if any of the symbol definitions in the output file matched any definitions in dynamic library being linked. This option is obsolete. .It Fl multiply_defined Ar treatment -Previously provided a way to warn or error if any of the symbols used from a dynamic library were also +Previously provided a way to warn or error if any of the symbols used from a dynamic library were also available in another linked dynamic library. This option is obsolete. .It Fl private_bundle -Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle +Previously prevented errors when -flat_namespace, -bundle, and -bundle_loader were used and the bundle contained a definition that conflicted with a symbol in the main executable. The linker no longer errors on such conflicts. This option is obsolete. .It Fl noall_load This is the default. This option is obsolete. .It Fl seg_addr_table_filename Ar path -Use +Use .Ar path instead of the install name of the library for matching an entry in the seg_addr_table. This option is obsolete. .It Fl sectorder Ar segname sectname orderfile Replaced by more general -order_file option. .It Fl sectorder_detail -Produced extra logging about which entries from a sectorder entries were used. Replaced by -order_file_statistics. -This option is obsolete. +Produced extra logging about which entries from a sectorder entries were used. Replaced by -order_file_statistics. +This option is obsolete. .El .Sh SEE ALSO as(1), ar(1), cc(1), nm(1), otool(1) lipo(1), diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index c47b806..600aeac 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ F9EA73970974999B008B4F1D /* PBXTargetDependency */, F9B693890EC4D28C00076912 /* PBXTargetDependency */, F9F9AD68116D58AF0028EFAB /* PBXTargetDependency */, + 83046A911C90066900024A7E /* PBXTargetDependency */, ); name = "unit-tests"; productName = "unit-tests"; @@ -43,6 +44,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 83046A851C8FF2F700024A7E /* objcimageinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83046A841C8FF2D000024A7E /* objcimageinfo.cpp */; }; B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */; }; B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; }; F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; @@ -107,6 +109,13 @@ /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ + 83046A901C90066900024A7E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 83046A771C8FF23E00024A7E; + remoteInfo = objcimageinfo; + }; F96904880A4333AC00B77D2A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9023C3006D5A227001BBF46 /* Project object */; @@ -251,6 +260,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 83046A831C8FF23E00024A7E /* objcimageinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = objcimageinfo; sourceTree = BUILT_PRODUCTS_DIR; }; + 83046A841C8FF2D000024A7E /* objcimageinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = objcimageinfo.cpp; path = src/other/objcimageinfo.cpp; sourceTree = ""; }; B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bitcode_bundle.h; sourceTree = ""; }; B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitcode_bundle.cpp; sourceTree = ""; }; B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Bitcode.hpp; path = src/ld/Bitcode.hpp; sourceTree = ""; }; @@ -354,6 +365,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 83046A7E1C8FF23E00024A7E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9023C3706D5A23E001BBF46 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -420,6 +438,7 @@ F9B670080DDA176100E6D0DA /* unwinddump */, F9BA51610ECE58BE00D1D62E /* dyldinfo */, F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */, + 83046A831C8FF23E00024A7E /* objcimageinfo */, ); name = Products; sourceTree = ""; @@ -571,6 +590,7 @@ F9EC78050A2F8674002A3E39 /* rebase.cpp */, F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */, F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */, + 83046A841C8FF2D000024A7E /* objcimageinfo.cpp */, ); name = other; sourceTree = ""; @@ -591,6 +611,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 83046A771C8FF23E00024A7E /* objcimageinfo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83046A7F1C8FF23E00024A7E /* Build configuration list for PBXNativeTarget "objcimageinfo" */; + buildPhases = ( + 83046A8F1C8FF68D00024A7E /* make configure.h */, + 83046A791C8FF23E00024A7E /* Sources */, + 83046A7E1C8FF23E00024A7E /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = objcimageinfo; + productName = ObjectDump; + productReference = 83046A831C8FF23E00024A7E /* objcimageinfo */; + productType = "com.apple.product-type.tool"; + }; F9023C3806D5A23E001BBF46 /* ld */ = { isa = PBXNativeTarget; buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; @@ -600,6 +637,7 @@ F9023C3606D5A23E001BBF46 /* Sources */, F9023C3706D5A23E001BBF46 /* Frameworks */, F97F5025070D0B6300B9FCD7 /* copy man page */, + F94E0A911CAC6B870092DC75 /* Add libtapi symlink */, ); buildRules = ( F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, @@ -746,6 +784,7 @@ F9EC77ED0A2F85F6002A3E39 /* rebase */, F9B670010DDA176100E6D0DA /* unwinddump */, F971EED206D5ACF60041D381 /* ObjectDump */, + 83046A771C8FF23E00024A7E /* objcimageinfo */, F9EA72CA097454A6008B4F1D /* machocheck */, F9BA51600ECE58BE00D1D62E /* dyldinfo */, F9A3DDC90ED762B700C590B9 /* libprunetrie */, @@ -755,6 +794,22 @@ /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ + 83046A8F1C8FF68D00024A7E /* make configure.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make configure.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/src/create_configure\n"; + showEnvVarsInLog = 0; + }; B3C7A09714295B60005FC714 /* make compile_stub string */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -772,6 +827,21 @@ shellScript = "echo \"static const char *compile_stubs = \" > $DERIVED_FILE_DIR/compile_stubs.h\ncat compile_stubs | sed s/\\\"/\\\\\\\\\\\"/g | sed s/^/\\\"/ | sed s/\\$/\\\\\\\\n\\\"/ >> $DERIVED_FILE_DIR/compile_stubs.h\necho \";\" >> $DERIVED_FILE_DIR/compile_stubs.h"; showEnvVarsInLog = 0; }; + F94E0A911CAC6B870092DC75 /* Add libtapi symlink */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Add libtapi symlink"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n cd \"${TARGET_BUILD_DIR}\"\n cd ..\n mkdir -p lib\n cd lib\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib\"\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libtapi.dylib\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; F96D5367094A2754008E9EE8 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -901,6 +971,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 83046A791C8FF23E00024A7E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83046A851C8FF2F700024A7E /* objcimageinfo.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9023C3606D5A23E001BBF46 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -990,6 +1068,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 83046A911C90066900024A7E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 83046A771C8FF23E00024A7E /* objcimageinfo */; + targetProxy = 83046A901C90066900024A7E /* PBXContainerItemProxy */; + }; F96904890A4333AC00B77D2A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9EC77ED0A2F85F6002A3E39 /* rebase */; @@ -1053,6 +1136,94 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 83046A801C8FF23E00024A7E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", + ); + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = objcimageinfo; + SDKROOT = macosx.internal; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Debug; + }; + 83046A811C8FF23E00024A7E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", + ); + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; + PRODUCT_NAME = objcimageinfo; + SDKROOT = macosx.internal; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + 83046A821C8FF23E00024A7E /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", + ); + INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; + PRODUCT_NAME = objcimageinfo; + SDKROOT = macosx.internal; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = "Release-assert"; + }; F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1096,8 +1267,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(DT_TOOLCHAIN_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/include", + "$(TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; @@ -1114,11 +1284,14 @@ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", + "-L$(DT_TOOLCHAIN_DIR)/usr/lib", + "-ltapi", ); PREBINDING = NO; PRODUCT_NAME = ld; SDKROOT = macosx.internal; SECTORDER_FLAGS = ""; + TOOLCHAINS = osx; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; }; @@ -1171,8 +1344,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(DT_TOOLCHAIN_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/include", + "$(TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; @@ -1186,6 +1358,8 @@ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", + "-L$(DT_TOOLCHAIN_DIR)/usr/lib", + "-ltapi", ); PREBINDING = NO; PRODUCT_NAME = ld; @@ -1193,6 +1367,7 @@ SECTORDER_FLAGS = ""; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; + TOOLCHAINS = osx; VALID_ARCHS = "x86_64 i386 ppc"; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; @@ -1203,6 +1378,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1219,9 +1395,9 @@ GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/src/ld", - "$(DEVELOPER_DIR)/usr/local/include", "$(DT_TOOLCHAIN_DIR)/usr/local/include", + "$(TOOLCHAIN_DIR)/usr/local/include", + "$(SRCROOT)/src/ld", ); INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; ONLY_ACTIVE_ARCH = NO; @@ -1250,6 +1426,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1258,9 +1435,9 @@ GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/src/ld", - "$(DEVELOPER_DIR)/usr/local/include", "$(DT_TOOLCHAIN_DIR)/usr/local/include", + "$(TOOLCHAIN_DIR)/usr/local/include", + "$(SRCROOT)/src/ld", ); INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( @@ -1289,6 +1466,7 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx.internal; }; name = Debug; }; @@ -1297,6 +1475,7 @@ buildSettings = { GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; + SDKROOT = macosx.internal; }; name = Release; }; @@ -1323,6 +1502,7 @@ buildSettings = { GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; + SDKROOT = macosx.internal; }; name = "Release-assert"; }; @@ -1383,8 +1563,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(DT_TOOLCHAIN_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/local/include", - "$(DEVELOPER_DIR)/usr/include", + "$(TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/bin"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; @@ -1398,6 +1577,8 @@ "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", + "-L$(DT_TOOLCHAIN_DIR)/usr/lib", + "-ltapi", ); PREBINDING = NO; PRODUCT_NAME = ld; @@ -1405,6 +1586,7 @@ SECTORDER_FLAGS = ""; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; + TOOLCHAINS = osx; VALID_ARCHS = "x86_64 i386 ppc"; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; @@ -1464,6 +1646,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1473,9 +1656,9 @@ GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/src/ld", - "$(DEVELOPER_DIR)/usr/local/include", "$(DT_TOOLCHAIN_DIR)/usr/local/include", + "$(TOOLCHAIN_DIR)/usr/local/include", + "$(SRCROOT)/src/ld", ); INSTALL_PATH = "$(DT_VARIANT)/$(DT_TOOLCHAIN_DIR)/usr/local/bin"; OTHER_CPLUSPLUSFLAGS = ( @@ -1823,6 +2006,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 83046A7F1C8FF23E00024A7E /* Build configuration list for PBXNativeTarget "objcimageinfo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83046A801C8FF23E00024A7E /* Debug */, + 83046A811C8FF23E00024A7E /* Release */, + 83046A821C8FF23E00024A7E /* Release-assert */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = "Release-assert"; + }; F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index cfedc81..a417877 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -264,7 +264,6 @@ #define CPU_SUBTYPE_ARM64_V8 1 #endif - #define ARM64_RELOC_UNSIGNED 0 // for pointers #define ARM64_RELOC_SUBTRACTOR 1 // must be followed by a ARM64_RELOC_UNSIGNED #define ARM64_RELOC_BRANCH26 2 // a B/BL instruction with 26-bit displacement diff --git a/ld64/src/create_configure b/ld64/src/create_configure index 9ce654d..3643079 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -16,8 +16,8 @@ fi for ANARCH in ${RC_SUPPORTED_ARCHS} do - KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h," - FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` + KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h," + FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` if [ $FOUND ]; then echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h else diff --git a/ld64/src/ld/Architectures.hpp b/ld64/src/ld/Architectures.hpp index fdf795b..eca8c56 100644 --- a/ld64/src/ld/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -61,6 +61,7 @@ struct arm64 typedef Pointer64 P; }; + #endif // __ARCHITECTURES__ diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index b47acff..454eb1f 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -52,6 +52,9 @@ class HeaderAndLoadCommandsAbtract : public ld::Atom virtual const uint8_t* getUUID() const = 0; virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd, uint64_t& sectOffset, uint64_t& sectEnd) const = 0; + virtual void linkeditCmdInfo(uint64_t& offset, uint64_t& size) const = 0; + virtual void symbolTableCmdInfo(uint64_t& offset, uint64_t& size) const = 0; + }; template @@ -74,7 +77,10 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract virtual const uint8_t* getUUID() const { return &_uuid[0]; } virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd, uint64_t& sectOffset, uint64_t& sectEnd) const; - + virtual void linkeditCmdInfo(uint64_t& offset, uint64_t& size) const; + virtual void symbolTableCmdInfo(uint64_t& offset, uint64_t& size) const; + + private: typedef typename A::P P; typedef typename A::P::E E; @@ -91,9 +97,9 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract uint32_t commandsCount() const; uint32_t threadLoadCommandSize() const; uint8_t* copySingleSegmentLoadCommand(uint8_t* p) const; - uint8_t* copySegmentLoadCommands(uint8_t* p) const; + uint8_t* copySegmentLoadCommands(uint8_t* p, uint8_t* base) const; uint8_t* copyDyldInfoLoadCommand(uint8_t* p) const; - uint8_t* copySymbolTableLoadCommand(uint8_t* p) const; + uint8_t* copySymbolTableLoadCommand(uint8_t* p, uint8_t* base) const; uint8_t* copyDynamicSymbolTableLoadCommand(uint8_t* p) const; uint8_t* copyDyldLoadCommand(uint8_t* p) const; uint8_t* copyDylibIDLoadCommand(uint8_t* p) const; @@ -150,6 +156,8 @@ class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract std::vector _subUmbrellaNames; uint8_t _uuid[16]; mutable macho_uuid_command

* _uuidCmdInOutputBuffer; + mutable uint32_t _linkeditCmdOffset; + mutable uint32_t _symboltableCmdOffset; std::vector< std::vector > _linkerOptions; static ld::Section _s_section; @@ -169,7 +177,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn, false, false, false, (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(12) ), - _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL) + _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL), _linkeditCmdOffset(0), _symboltableCmdOffset(0) { bzero(_uuid, 16); _hasDyldInfoLoadCommand = opts.makeCompressedDyldInfo(); @@ -201,15 +209,13 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: break; } } - for (CStringSet::const_iterator it = _state.linkerOptionFrameworks.begin(); it != _state.linkerOptionFrameworks.end(); ++it) { - const char* frameWorkName = *it; + for (const char* frameworkName : _state.unprocessedLinkerOptionFrameworks) { std::vector* lo = new std::vector(); lo->push_back("-framework"); - lo->push_back(frameWorkName); + lo->push_back(frameworkName); _linkerOptions.push_back(*lo); }; - for (CStringSet::const_iterator it = _state.linkerOptionLibraries.begin(); it != _state.linkerOptionLibraries.end(); ++it) { - const char* libName = *it; + for (const char* libName : _state.unprocessedLinkerOptionLibraries) { std::vector* lo = new std::vector(); char * s = new char[strlen(libName)+3]; strcpy(s, "-l"); @@ -357,6 +363,21 @@ bool HeaderAndLoadCommandsAtom::bitcodeBundleCommand(uint64_t &cmdOffset, uin return false; } +template +void HeaderAndLoadCommandsAtom::linkeditCmdInfo(uint64_t &offset, uint64_t &size) const +{ + offset = _linkeditCmdOffset; + size = sizeof(macho_segment_command

); +} + +template +void HeaderAndLoadCommandsAtom::symbolTableCmdInfo(uint64_t &offset, uint64_t &size) const +{ + offset = _symboltableCmdOffset; + size = sizeof(macho_symtab_command

); +} + + template uint64_t HeaderAndLoadCommandsAtom::size() const { @@ -635,7 +656,6 @@ template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CP template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM64; } - template <> uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const { @@ -894,7 +914,7 @@ bool HeaderAndLoadCommandsAtom::sectionTakesNoDiskSpace(ld::Internal::FinalSe template -uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const +uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p, uint8_t* base) const { // group sections into segments std::vector segs; @@ -954,7 +974,8 @@ uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const segCmd->set_initprot(si.initProt); segCmd->set_nsects(si.nonHiddenSectionCount); segCmd->set_flags(si.nonSectCreateSections ? 0 : SG_NORELOC); // FIXME, really should check all References - + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + _linkeditCmdOffset = p - base; p += sizeof(macho_segment_command

); macho_section

* msect = (macho_section

*)p; for (std::vector::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) { @@ -982,8 +1003,9 @@ uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const template -uint8_t* HeaderAndLoadCommandsAtom::copySymbolTableLoadCommand(uint8_t* p) const +uint8_t* HeaderAndLoadCommandsAtom::copySymbolTableLoadCommand(uint8_t* p, uint8_t* base) const { + _symboltableCmdOffset = p - base; // build LC_SYMTAB command macho_symtab_command

* symbolTableCmd = (macho_symtab_command

*)p; symbolTableCmd->set_cmd(LC_SYMTAB); @@ -1498,7 +1520,7 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _options.outputKind() == Options::kObjectFile ) p = this->copySingleSegmentLoadCommand(p); else - p = this->copySegmentLoadCommands(p); + p = this->copySegmentLoadCommands(p, buffer); if ( _hasDylibIDLoadCommand ) p = this->copyDylibIDLoadCommand(p); @@ -1507,7 +1529,7 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const p = this->copyDyldInfoLoadCommand(p); if ( _hasSymbolTableLoadCommand ) - p = this->copySymbolTableLoadCommand(p); + p = this->copySymbolTableLoadCommand(p, buffer); if ( _hasDynamicSymbolTableLoadCommand ) p = this->copyDynamicSymbolTableLoadCommand(p); diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index ab31d4a..e8e6bcc 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -365,6 +365,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts); if ( archiveResult != NULL ) { + OSAtomicAdd64(len, &_totalArchiveSize); OSAtomicIncrement32(&_totalArchivesLoaded); return archiveResult; @@ -417,7 +418,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib } } -void InputFiles::logDylib(ld::File* file, bool indirect) +void InputFiles::logDylib(ld::File* file, bool indirect, bool speculative) { if ( _options.traceDylibs() ) { const char* fullPath = file->path(); @@ -429,11 +430,19 @@ void InputFiles::logDylib(ld::File* file, bool indirect) // don't log upward dylibs when XBS is computing dependencies logTraceInfo("[Logging for XBS] Used upward dynamic library: %s\n", fullPath); } + else if ( (dylib != NULL ) && dylib->speculativelyLoaded() ) { + logTraceInfo("[Logging for XBS] Speculatively loaded dynamic library: %s\n", fullPath); + } else { - if ( indirect ) - logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); - else + if ( indirect ) { + if ( speculative ) + logTraceInfo("[Logging for XBS] Speculatively loaded indirect dynamic library: %s\n", fullPath); + else + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + } + else { logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } } } @@ -459,7 +468,7 @@ void InputFiles::logDylib(ld::File* file, bool indirect) void InputFiles::logArchive(ld::File* file) const { - if ( _options.traceArchives() && (_archiveFilesLogged.count(file) == 0) ) { + if ( (_options.traceArchives() || _options.traceEmitJSON()) && (_archiveFilesLogged.count(file) == 0) ) { // LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive _archiveFilesLogged.insert(file); const char* fullPath = file->path(); @@ -467,6 +476,9 @@ void InputFiles::logArchive(ld::File* file) const if ( realpath(fullPath, realName) != NULL ) fullPath = realName; logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + + std::string archivePath(fullPath); + _archiveFilePaths.push_back(archivePath); } } @@ -505,7 +517,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const } -ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath) +ld::dylib::File* InputFiles::findDylib(const char* installPath, const ld::dylib::File* fromDylib, bool speculative) { //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath); @@ -526,7 +538,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from if ( dylibReader != NULL ) { addDylib(dylibReader, info); //_installPathToDylibs[strdup(installPath)] = dylibReader; - this->logDylib(dylibReader, true); + this->logDylib(dylibReader, true, speculative); return dylibReader; } else @@ -537,20 +549,9 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from } } } - char newPath[MAXPATHLEN]; - // handle @loader_path - if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { - strcpy(newPath, fromPath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &installPath[13]); - else - strcpy(newPath, &installPath[13]); - installPath = newPath; - } - // note: @executable_path case is handled inside findFileUsingPaths() - // search for dylib using -F and -L paths - Options::FileInfo info = _options.findFileUsingPaths(installPath); + + // search for dylib using -F and -L paths and expanding @ paths + Options::FileInfo info = _options.findIndirectDylib(installPath, fromDylib); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; info.options.fIndirectDylib = true; @@ -561,7 +562,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); //_installPathToDylibs[strdup(installPath)] = dylibReader; addDylib(dylibReader, info); - this->logDylib(dylibReader, true); + this->logDylib(dylibReader, true, speculative); return dylibReader; } else @@ -614,80 +615,96 @@ bool InputFiles::libraryAlreadyLoaded(const char* path) return true; } + char realDylibPath[PATH_MAX]; + if ( (realpath(path, realDylibPath) != NULL) && (strcmp(path, realDylibPath) != 0) ) { + return libraryAlreadyLoaded(realDylibPath); + } + return false; } void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler) { - if ( _options.outputKind() == Options::kObjectFile ) - return; - - // process frameworks specified in .o linker options - for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) { - const char* frameworkName = *it; - if ( state.linkerOptionFrameworksProcessed.count(frameworkName) ) - continue; - Options::FileInfo info = _options.findFramework(frameworkName); - if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) { - info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); - try { - ld::File* reader = this->makeFile(info, true); - ld::dylib::File* dylibReader = dynamic_cast(reader); - if ( dylibReader != NULL ) { - if ( ! dylibReader->installPathVersionSpecific() ) { + if ( _options.outputKind() == Options::kObjectFile ) + return; + + while (! state.unprocessedLinkerOptionLibraries.empty() || ! state.unprocessedLinkerOptionFrameworks.empty()) { + + // process frameworks specified in .o linker options + CStringSet newFrameworks = std::move(state.unprocessedLinkerOptionFrameworks); + state.unprocessedLinkerOptionFrameworks.clear(); + for (const char* frameworkName : newFrameworks) { + if ( state.linkerOptionFrameworks.count(frameworkName) ) + continue; + Options::FileInfo info = _options.findFramework(frameworkName); + if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) { + _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + info.ordinal = _linkerOptionOrdinal; + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->forEachAtom(handler); + dylibReader->setImplicitlyLinked(); + dylibReader->setSpeculativelyLoaded(); + this->addDylib(dylibReader, info); + } + } + else { + throwf("framework linker option at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + warning("Auto-Linking supplied '%s', %s", info.path, msg); + } + } + state.linkerOptionFrameworks.insert(frameworkName); + } + + // process libraries specified in .o linker options + // fixme optimize with std::move? + CStringSet newLibraries = std::move(state.unprocessedLinkerOptionLibraries); + state.unprocessedLinkerOptionLibraries.clear(); + for (const char* libName : newLibraries) { + if ( state.linkerOptionLibraries.count(libName) ) + continue; + Options::FileInfo info = _options.findLibrary(libName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + info.ordinal = _linkerOptionOrdinal; + try { + // -force_load_swift_libs + info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0); + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + ld::archive::File* archiveReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); + dylibReader->setSpeculativelyLoaded(); this->addDylib(dylibReader, info); } - } - else { - throwf("framework linker option at %s is not a dylib", info.path); - } - } - catch (const char* msg) { - warning("Auto-Linking supplied '%s', %s", info.path, msg); - } - } - state.linkerOptionFrameworksProcessed.insert(frameworkName); - } - // process libraries specified in .o linker options - for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) { - const char* libName = *it; - if ( state.linkerOptionLibrariesProcessed.count(libName) ) - continue; - Options::FileInfo info = _options.findLibrary(libName); - if ( ! this->libraryAlreadyLoaded(info.path) ) { - info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); - try { - // -force_load_swift_libs - info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0); - ld::File* reader = this->makeFile(info, true); - ld::dylib::File* dylibReader = dynamic_cast(reader); - ld::archive::File* archiveReader = dynamic_cast(reader); - if ( dylibReader != NULL ) { - dylibReader->forEachAtom(handler); - dylibReader->setImplicitlyLinked(); - this->addDylib(dylibReader, info); - } - else if ( archiveReader != NULL ) { - _searchLibraries.push_back(LibraryInfo(archiveReader)); - if ( _options.dumpDependencyInfo() ) - _options.dumpDependency(Options::depArchive, archiveReader->path()); - // -force_load_swift_libs - if (info.options.fForceLoad) { - archiveReader->forEachAtom(handler); + else if ( archiveReader != NULL ) { + _searchLibraries.push_back(LibraryInfo(archiveReader)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archiveReader->path()); + // -force_load_swift_libs + if (info.options.fForceLoad) { + archiveReader->forEachAtom(handler); + } } - } - else { - throwf("linker option dylib at %s is not a dylib", info.path); - } - } - catch (const char* msg) { - warning("Auto-Linking supplied '%s', %s", info.path, msg); - } + else { + throwf("linker option dylib at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + warning("Auto-Linking supplied '%s', %s", info.path, msg); + } + } + state.linkerOptionLibraries.insert(libName); } - state.linkerOptionLibrariesProcessed.insert(libName); } } @@ -889,6 +906,7 @@ InputFiles::InputFiles(Options& opts, const char** archName) pthread_mutex_init(&_parseLock, NULL); pthread_cond_init(&_parseWorkReady, NULL); pthread_cond_init(&_newFileAvailable, NULL); + _neededFileSlot = -1; #endif const std::vector& files = _options.getInputFiles(); if ( files.size() == 0 ) @@ -986,7 +1004,7 @@ void InputFiles::parseWorkerThread() { if (_s_logPThreads) printf("parsing index %u\n", slot); try { file = makeFile(entry, false); - } + } catch (const char *msg) { if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { if ( _options.ignoreOtherArchInputFiles() ) { @@ -1086,7 +1104,7 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& // log direct readers if ( ! info.options.fIndirectDylib ) - this->logDylib(reader, false); + this->logDylib(reader, false, false); // update stats _totalDylibsLoaded++; @@ -1212,7 +1230,7 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal { ld::archive::File* archive = (ld::archive::File*)file; // force loaded archives should be in LD_TRACE - if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) + if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && (_options.traceArchives() || _options.traceEmitJSON()) ) logArchive(archive); _searchLibraries.push_back(LibraryInfo(archive)); if ( _options.dumpDependencyInfo() ) @@ -1309,16 +1327,17 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc ld::archive::File *archiveFile = lib.archive(); if ( dataSymbolOnly ) { if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) { - if ( _options.traceArchives() ) + if ( _options.traceArchives() || _options.traceEmitJSON()) logArchive(archiveFile); _options.snapshot().recordArchive(archiveFile->path()); + // DALLAS _state.archives.push_back(archiveFile); // found data definition in static library, done return true; } } else { if ( archiveFile->justInTimeforEachAtom(name, handler) ) { - if ( _options.traceArchives() ) + if ( _options.traceArchives() || _options.traceEmitJSON()) logArchive(archiveFile); _options.snapshot().recordArchive(archiveFile->path()); // found definition in static library, done @@ -1433,6 +1452,18 @@ void InputFiles::dylibs(ld::Internal& state) } // make implicit dylib order be deterministic by sorting by install_name std::sort(implicitDylibs.begin(), implicitDylibs.end(), DylibByInstallNameSorter()); + + if ( _options.traceDylibs() ) { + for (ld::dylib::File* dylib : implicitDylibs) { + if ( dylib->speculativelyLoaded() && !dylib->explicitlyLinked() && dylib->providedExportAtom() ) { + const char* fullPath = dylib->path(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } + } + } state.dylibs.insert(state.dylibs.end(), implicitDylibs.begin(), implicitDylibs.end()); } @@ -1450,6 +1481,14 @@ void InputFiles::dylibs(ld::Internal& state) throw "dynamic main executables must link with libSystem.dylib"; } +void InputFiles::archives(ld::Internal& state) +{ + for (const std::string path : _archiveFilePaths) { + + state.archivePaths.push_back(path); + } +} + } // namespace tool } // namespace ld diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index e9927cd..21f878a 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -60,7 +60,7 @@ class InputFiles : public ld::dylib::File::DylibHandler InputFiles(Options& opts, const char** archName); // implementation from ld::dylib::File::DylibHandler - virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath); + virtual ld::dylib::File* findDylib(const char* installPath, const ld::dylib::File* fromDylib, bool speculative); // iterates all atoms in initial files void forEachInitialAtom(ld::File::AtomHandler&, ld::Internal& state); @@ -72,6 +72,8 @@ class InputFiles : public ld::dylib::File::DylibHandler // copy dylibs to link with in command line order void dylibs(ld::Internal& state); + void archives(ld::Internal& state); + bool inferredArch() const { return _inferredArch; } void addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHandler& handler); @@ -91,7 +93,7 @@ class InputFiles : public ld::dylib::File::DylibHandler ld::File* makeFile(const Options::FileInfo& info, bool indirectDylib); ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info); void logTraceInfo (const char* format, ...) const; - void logDylib(ld::File*, bool indirect); + void logDylib(ld::File*, bool indirect, bool speculative); void logArchive(ld::File*) const; void markExplicitlyLinkedDylibs(); void checkDylibClientRestrictions(ld::dylib::File*); @@ -113,6 +115,7 @@ class InputFiles : public ld::dylib::File::DylibHandler const Options& _options; std::vector _inputFiles; mutable std::set _archiveFilesLogged; + mutable std::vector _archiveFilePaths; InstallNameToDylib _installPathToDylibs; std::set _allDylibs; ld::dylib::File* _bundleLoader; diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 2eab13b..ce7c820 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -669,39 +669,18 @@ bool SymbolTableAtom::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, template void SymbolTableAtom::encode() { - uint32_t symbolIndex = 0; + // Note: We lay out the symbol table so that the strings for the stabs (local) symbols are at the + // end of the string pool. The stabs strings are not used when calculated the UUID for the image. + // If the stabs strings were not last, the string offsets for all other symbols may very which would alter the UUID. - // make nlist entries for all local symbols - std::vector& localAtoms = this->_writer._localAtoms; - std::vector& globalAtoms = this->_writer._exportedAtoms; - _locals.reserve(localAtoms.size()+this->_state.stabs.size()); - this->_writer._localSymbolsStartIndex = 0; - // make nlist entries for all debug notes - _stabsIndexStart = symbolIndex; - _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset(); - for (std::vector::const_iterator sit=this->_state.stabs.begin(); sit != this->_state.stabs.end(); ++sit) { - macho_nlist

entry; - entry.set_n_type(sit->type); - entry.set_n_sect(sectionIndexForStab(*sit)); - entry.set_n_desc(sit->desc); - entry.set_n_value(valueForStab(*sit)); - entry.set_n_strx(stringOffsetForStab(*sit, this->_writer._stringPoolAtom)); - _locals.push_back(entry); - ++symbolIndex; - } - _stabsIndexEnd = symbolIndex; - _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset(); - for (std::vector::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) { - const ld::Atom* atom = *it; - if ( this->addLocal(atom, this->_writer._stringPoolAtom) ) - this->_writer._atomToSymbolIndex[atom] = symbolIndex++; - } - this->_writer._localSymbolsCount = symbolIndex; - + // reserve space for local symbols + uint32_t localsCount = _state.stabs.size() + this->_writer._localAtoms.size(); // make nlist entries for all global symbols + std::vector& globalAtoms = this->_writer._exportedAtoms; _globals.reserve(globalAtoms.size()); - this->_writer._globalSymbolsStartIndex = symbolIndex; + uint32_t symbolIndex = localsCount; + this->_writer._globalSymbolsStartIndex = localsCount; for (std::vector::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) { const ld::Atom* atom = *it; this->addGlobal(atom, this->_writer._stringPoolAtom); @@ -718,6 +697,31 @@ void SymbolTableAtom::encode() this->_writer._atomToSymbolIndex[*it] = symbolIndex++; } this->_writer._importSymbolsCount = symbolIndex - this->_writer._importSymbolsStartIndex; + + // go back to start and make nlist entries for all local symbols + std::vector& localAtoms = this->_writer._localAtoms; + _locals.reserve(localsCount); + symbolIndex = 0; + this->_writer._localSymbolsStartIndex = 0; + _stabsIndexStart = 0; + _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset(); + for (const ld::relocatable::File::Stab& stab : _state.stabs) { + macho_nlist

entry; + entry.set_n_type(stab.type); + entry.set_n_sect(sectionIndexForStab(stab)); + entry.set_n_desc(stab.desc); + entry.set_n_value(valueForStab(stab)); + entry.set_n_strx(stringOffsetForStab(stab, this->_writer._stringPoolAtom)); + _locals.push_back(entry); + ++symbolIndex; + } + _stabsIndexEnd = symbolIndex; + _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset(); + for (const ld::Atom* atom : localAtoms) { + if ( this->addLocal(atom, this->_writer._stringPoolAtom) ) + this->_writer._atomToSymbolIndex[atom] = symbolIndex++; + } + this->_writer._localSymbolsCount = symbolIndex; } template diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 0f5985d..faf9c16 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -125,6 +126,7 @@ bool Options::FileInfo::checkFileExists(const Options& options, const char *p) } if ( options.dumpDependencyInfo() ) options.dumpDependency(Options::depNotFound, p); +// fprintf(stderr, "not found: %s\n", p); return false; } @@ -165,7 +167,7 @@ Options::Options(int argc, const char* argv[]) fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false), - fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), + fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false), fOutputSlidable(false), fWarnWeakExports(false), fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), fDemangle(false), fTLVSupport(false), @@ -185,16 +187,15 @@ Options::Options(int argc, const char* argv[]) fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), fSharedRegionEncodingV2(false), fUseDataConstSegment(false), - fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), + fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false), fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), - fIgnoreAutoLink(false), fAllowDeadDups(false), fBitcodeKind(kBitcodeProcess), + fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fBitcodeKind(kBitcodeProcess), fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), - fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), - fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0), fFilePreference(kModTime), - fForceTextBasedStub(false) + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fMaxDefaultCommonAlign(0) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -320,12 +321,15 @@ uint32_t Options::initialSegProtection(const char* segName) const return it->init; } } - if ( strcmp(segName, "__PAGEZERO") == 0 ) { - return 0; + if ( strcmp(segName, "__TEXT") == 0 ) { + return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) ); } - else if ( strcmp(segName, "__TEXT") == 0 ) { + else if ( strcmp(segName, "__TEXT_EXEC") == 0 ) { return VM_PROT_READ | VM_PROT_EXECUTE; } + else if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } else if ( strcmp(segName, "__LINKEDIT") == 0 ) { return VM_PROT_READ; } @@ -392,6 +396,9 @@ bool Options::hasCustomSectionAlignment(const char* segName, const char* sectNam if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) return true; } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return true; + return false; } @@ -401,6 +408,9 @@ uint8_t Options::customSectionAlignment(const char* segName, const char* sectNam if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) return it->alignment; } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return __builtin_ctz(fSegmentAlignment); + return 0; } @@ -605,7 +615,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P switch ( type ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) ) { + if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); @@ -617,7 +627,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P break; case CPU_TYPE_ARM: case CPU_TYPE_ARM64: - if ( (fPlatform == kPlatformiOS) && (fOutputKind != Options::kObjectFile) ) { + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); @@ -817,52 +827,67 @@ bool Options::findFile(const std::string &path, const std::vector & bool found = tbdInfo.checkFileExists(*this, newPath.c_str()); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str()); - if ( found ) { - if ( (fFilePreference == kTextBasedStub) || fForceTextBasedStub ) { - result = tbdInfo; - return true; - } else { - break; - } - } + if ( found ) + break; } + // If we found a text-based stub file, check if it should be used. + if ( !tbdInfo.missing() ) { + if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) { + result = tbdInfo; + return true; + } + } FileInfo dylibInfo; { bool found = dylibInfo.checkFileExists(*this, path.c_str()); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str()); - if ( found && (fFilePreference == kMachO) ) { - result = dylibInfo; - return true; - } } - if ( !dylibInfo.missing() && tbdInfo.missing() ) { - result = dylibInfo; + // There is only a text-based stub file. + if ( !tbdInfo.missing() && dylibInfo.missing() ) { + result = tbdInfo; return true; } - else if ( dylibInfo.missing() && !tbdInfo.missing() ) { - result = tbdInfo; + // There is only a dynamic library file. + else if ( tbdInfo.missing() && !dylibInfo.missing() ) { + result = dylibInfo; return true; } - else if ( !dylibInfo.missing() && !tbdInfo.missing() ) { - if ( dylibInfo.modTime == tbdInfo.modTime ) { + // There are both - a text-based stub file and a dynamic library file. + else if ( !tbdInfo.missing() && !dylibInfo.missing() ) { + // If the files are still in synv we can use and should use the text-based stub file. + if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) { result = tbdInfo; - return true; } + // Otherwise issue a warning and fall-back to the dynamic library file. else { - // Disable false warning about out-of-sync .tbd files. - //warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); + warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); result = dylibInfo; - return true; + } + return true; } return false; } -Options::FileInfo Options::findFile(const std::string &path) const +static bool startsWith(const std::string& str, const std::string& prefix) +{ + return (str.compare(0, prefix.length(), prefix) == 0); +} + +static std::string getDirPath(const std::string& path) +{ + std::string::size_type lastSlashPos = path.find_last_of('/'); + if ( lastSlashPos == std::string::npos ) + return "./"; + else + return path.substr(0, lastSlashPos+1); +} + +Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::File* fromDylib) const { FileInfo result; @@ -874,35 +899,59 @@ Options::FileInfo Options::findFile(const std::string &path) const return result; } } + + // expand @ variables + if ( path[0] == '@' ) { + if ( startsWith(path, "@executable_path/") && (fExecutablePath != nullptr) ) { + std::string exeBasedPath = getDirPath(fExecutablePath) + &path[17]; + if ( findFile(exeBasedPath, {".tbd"}, result) ) + return result; + } + else if ( startsWith(path, "@loader_path/") && (fromDylib != nullptr) ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) { + std::string loaderBasedPath = getDirPath(fromDylib->path()) + &path[13]; + if ( findFile(loaderBasedPath, {".tbd"}, result) ) + return result; + } + } + else if ( startsWith(path, "@rpath/") ) { + // first search any LC_RPATH supplied by dyld that re-exports dylib to be found + if ( fromDylib != nullptr ) { + for (const char* rp : fromDylib->rpaths() ) { + std::string rpath = rp; + // handle dylib that has LC_RPATH = @loader_path/blah + if ( startsWith(rpath, "@loader_path/") ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) + rpath = getDirPath(absPath) + &rpath[13]; + else + rpath = getDirPath(fromDylib->path()) + &rpath[13]; + } + std::string rpathBasedPath = rpath + "/" + &path[6]; + if ( findFile(rpathBasedPath, {".tbd"}, result) ) + return result; + } + } + } + } + // try raw path if ( findFile(path, {".tbd"}, result) ) return result; - // try @executable_path substitution - if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) { - char newPath[strlen(fExecutablePath) + path.size()]; - strcpy(newPath, fExecutablePath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != nullptr ) - strcpy(&addPoint[1], &path[17]); - else - strcpy(newPath, &path[17]); - - if ( findFile(newPath, {".tbd"}, result) ) - return result; - } - // not found throwf("file not found: %s", path.c_str()); } -Options::FileInfo Options::findFileUsingPaths(const std::string &path) const +// search for indirect dylib first using -F and -L paths first +Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const { FileInfo result; - auto lastSlashPos = path.find_last_of('/'); + auto lastSlashPos = installName.find_last_of('/'); auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0; - auto leafName = path.substr(pos); + auto leafName = installName.substr(pos); // Is this in a framework? // /path/Foo.framework/Foo ==> true (Foo) @@ -911,7 +960,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const bool isFramework = false; if ( lastSlashPos != std::string::npos ) { auto frameworkDir = std::string("/").append(leafName).append(".framework/"); - if ( path.rfind(frameworkDir) != std::string::npos ) + if ( installName.rfind(frameworkDir) != std::string::npos ) isFramework = true; } @@ -920,9 +969,9 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - auto endPos = path.rfind(".framework"); - auto beginPos = path.find_last_of('/', endPos); - auto leafPath = path.substr(beginPos); + auto endPos = installName.rfind(".framework"); + auto beginPos = installName.find_last_of('/', endPos); + auto leafPath = installName.substr(beginPos); for (const auto* dir : fFrameworkSearchPaths) { auto possiblePath = dir + leafPath; if ( findFile(possiblePath, {".tbd"}, result) ) @@ -933,7 +982,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard bool embeddedDylib = ( (leafName.size() > 6) && (leafName.find(".dylib", leafName.size()-6) != std::string::npos) - && (path.find(".framework/") != std::string::npos) ); + && (installName.find(".framework/") != std::string::npos) ); if ( !embeddedDylib ) { for (const auto* dir : fLibrarySearchPaths) { //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); @@ -945,7 +994,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const } // If we didn't find it fall back to findFile. - return findFile(path); + return findFile(installName, fromDylib); } @@ -1716,6 +1765,19 @@ void Options::parseOrderFile(const char* path, bool cstring) // order files override auto-ordering fAutoOrderInitializers = false; + // ld64 should prefer OrderFiles from the SDK over the ones in / + for (const char* sdkPath : fSDKPaths) { + char fullPath[PATH_MAX]; + strlcpy(fullPath, sdkPath, PATH_MAX); + strlcat(fullPath, "/", PATH_MAX); + strlcat(fullPath, path, PATH_MAX); + struct stat statBuffer; + if ( stat(fullPath, &statBuffer) == 0 ) { + path = strdup(fullPath); + break; + } + } + // read in whole file int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) @@ -2357,6 +2419,40 @@ void Options::parse(int argc, const char* argv[]) if ( fOverridePathlibLTO == NULL ) throw "missing argument to -lto_library"; } + else if ( strcmp(arg, "-cache_path_lto") == 0 ) { + fLtoCachePath = argv[++i]; + if ( fLtoCachePath == NULL ) + throw "missing argument to -cache_path_lto"; + } + else if ( strcmp(arg, "-prune_interval_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_interval_lto"; + char* endptr; + fLtoPruneInterval = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_interval_lto"; + } + else if ( strcmp(arg, "-prune_after_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_after_lto"; + char* endptr; + fLtoPruneAfter = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_after_lto"; + } + else if ( strcmp(arg, "-max_relative_cache_size_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -max_relative_cache_size_lto"; + char* endptr; + fLtoMaxCacheSize = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -max_relative_cache_size_lto"; + if (fLtoMaxCacheSize > 100) + throw "Expect a value between 0 and 100 for -max_relative_cache_size_lto"; + } else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { snapshotArgCount = 0; FileInfo info = findLibrary(&arg[2]); @@ -3717,6 +3813,14 @@ void Options::parse(int argc, const char* argv[]) fUseDataConstSegmentForceOff = true; cannotBeUsedWithBitcode(arg); } + else if ( strcmp(arg, "-text_exec") == 0 ) { + fUseTextExecSegment = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-add_split_seg_info") == 0) { + fSharedRegionEligible = true; + cannotBeUsedWithBitcode(arg); + } else if ( strcmp(arg, "-no_deduplicate") == 0 ) { fDeDupe = false; } @@ -3745,14 +3849,8 @@ void Options::parse(int argc, const char* argv[]) } fMaxDefaultCommonAlign = alignment; } - else if ( strcmp(arg, "-prefer-mod-time-check") == 0 ) { - fFilePreference = kModTime; - } - else if ( strcmp(arg, "-prefer-text-based-stub-file") == 0 ) { - fFilePreference = kTextBasedStub; - } - else if ( strcmp(arg, "-prefer-macho-file") == 0 ) { - fFilePreference = kMachO; + else if ( strcmp(argv[i], "-no_weak_imports") == 0 ) { + fAllowWeakImports = false; } // put this last so that it does not interfer with other options starting with 'i' else if ( strncmp(arg, "-i", 2) == 0 ) { @@ -3856,6 +3954,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) const char* ltoVers = lto::version(); if ( ltoVers != NULL ) fprintf(stderr, "LTO support using: %s\n", ltoVers); + fprintf(stderr, "TAPI support using: %s\n", tapi::Version::getFullVersionAsString().c_str()); exit(0); } } @@ -4039,6 +4138,11 @@ void Options::parsePreCommandLineEnvironmentSettings() fTraceDylibs = true; fTraceIndirectDylibs = true; } + + if ((getenv("LD_TRACE_DEPENDENTS") != NULL)) { + + fTraceEmitJSON = true; + } if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { fTraceDylibSearching = true; @@ -4047,7 +4151,7 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_PRINT_OPTIONS") != NULL) fPrintOptions = true; - if (fTraceDylibs || fTraceArchives) + if (fTraceDylibs || fTraceArchives || fTraceEmitJSON) fTraceOutputFile = getenv("LD_TRACE_FILE"); if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) @@ -4091,10 +4195,6 @@ void Options::parsePreCommandLineEnvironmentSettings() if (pipeFdString != NULL) { fPipelineFifo = pipeFdString; } - - // Workaround for rdar://problem/24301175 - if ((getenv("RC_XBS") != NULL) && !(getenv("RC_BUILDIT") != NULL)) - fForceTextBasedStub = true; } @@ -4476,7 +4576,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { - if ( minOS(ld::mac10_5, ld::iOS_3_1) ) + if ( minOS(ld::mac10_5, ld::iOS_3_1) && !fTargetIOSSimulator ) if ( !fPrebind && !fSharedRegionEligibleForceOff ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) @@ -4498,6 +4598,12 @@ void Options::reconfigureDefaults() if ( fUseDataConstSegmentForceOn ) { fUseDataConstSegment = true; } + // A -kext for iOS 10 ==> -data_const, -text_exec, -add_split_seg_info + if ( (fOutputKind == Options::kKextBundle) && minOS(ld::mac10_Future, ld::iOS_10_0) && (fArchitecture == CPU_TYPE_ARM64) ) { + fUseDataConstSegment = true; + fUseTextExecSegment = true; + fSharedRegionEligible = true; + } if ( fUseDataConstSegment ) { addSectionRename("__DATA", "__got", "__DATA_CONST", "__got"); addSectionRename("__DATA", "__la_symbol_ptr", "__DATA_CONST", "__la_symbol_ptr"); @@ -4514,10 +4620,17 @@ void Options::reconfigureDefaults() addSectionRename("__DATA", "__objc_imageinfo", "__DATA_CONST", "__objc_imageinfo"); addSectionRename("__DATA", "__objc_const", "__DATA_CONST", "__objc_const"); } + if ( fUseTextExecSegment ) { + addSectionRename("__TEXT", "__text", "__TEXT_EXEC", "__text"); + addSectionRename("__TEXT", "__stubs", "__TEXT_EXEC", "__stubs"); + } // Use V2 shared cache info when targetting newer OSs - if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0)) { + if ( fSharedRegionEligible && minOS(ld::mac10_12, ld::iOS_9_0)) { fSharedRegionEncodingV2 = true; + // only use v2 for Swift dylibs on Mac OS X + if ( (fPlatform == kPlatformOSX) && (strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) != 0) ) + fSharedRegionEncodingV2 = false; fIgnoreOptimizationHints = true; } @@ -4609,7 +4722,8 @@ void Options::reconfigureDefaults() fEncryptable = false; break; } - if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) ) + if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) + ) fEncryptable = false; if ( fEncryptableForceOn ) fEncryptable = true; @@ -4673,7 +4787,6 @@ void Options::reconfigureDefaults() case CPU_TYPE_I386: case CPU_TYPE_ARM64: fEnforceDylibSubtypesMatch = false; - fAllowCpuSubtypeMismatches = true; break; } @@ -4712,7 +4825,7 @@ void Options::reconfigureDefaults() fCanUseUpwardDylib = true; // MacOSX 10.7 defaults to PIE - if ( ((fArchitecture == CPU_TYPE_X86_64) || (fArchitecture == CPU_TYPE_I386)) + if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_7) ) { fPositionIndependentExecutable = true; @@ -4726,6 +4839,10 @@ void Options::reconfigureDefaults() fPositionIndependentExecutable = true; } + // x86_64 defaults PIE (regardless of minOS version) + if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_6) ) + fPositionIndependentExecutable = true; + // Simulator defaults to PIE if ( fTargetIOSSimulator && (fOutputKind == kDynamicExecutable) ) fPositionIndependentExecutable = true; @@ -4735,8 +4852,12 @@ void Options::reconfigureDefaults() fPositionIndependentExecutable = false; // arm64 is always PIE - if ( (fArchitecture == CPU_TYPE_ARM64) && (fOutputKind == kDynamicExecutable) ) { + if ( ((fArchitecture == CPU_TYPE_ARM64) + ) + && (fOutputKind == kDynamicExecutable) ) { fPositionIndependentExecutable = true; + if ( fDisablePositionIndependentExecutable ) + warning("-no_pie ignored for arm64"); } // set fOutputSlidable @@ -4763,7 +4884,9 @@ void Options::reconfigureDefaults() if ( fMacVersionMin >= ld::mac10_7 ) { fTLVSupport = true; } - else if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_8_0) ) { + else if ( ((fArchitecture == CPU_TYPE_ARM64) + ) + && min_iOS(ld::iOS_8_0) ) { fTLVSupport = true; } else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) { @@ -4954,7 +5077,8 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: - if ( (fArchitecture == CPU_TYPE_ARM64) + if ( ((fArchitecture == CPU_TYPE_ARM64) + ) || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) { fSegmentAlignment = 4096*4; } @@ -4962,7 +5086,9 @@ void Options::reconfigureDefaults() case Options::kStaticExecutable: case Options::kKextBundle: // 16KB segments for arm64 kexts - if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_9_0) ) { + if ( ((fArchitecture == CPU_TYPE_ARM64) + ) + && min_iOS(ld::iOS_9_0) ) { fSegmentAlignment = 4096*4; } break; @@ -5398,7 +5524,7 @@ void Options::checkIllegalOptionCombinations() // zero page size not specified on command line, set default switch (fArchitecture) { case CPU_TYPE_I386: - case CPU_TYPE_ARM: + case CPU_TYPE_ARM: // first 4KB for 32-bit architectures fZeroPageSize = 0x1000; break; @@ -5521,8 +5647,8 @@ void Options::checkIllegalOptionCombinations() if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) ) throw "-dyld_env can only used used when created main executables"; - // -segment_order can only be used with -preload - if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) ) + // -segment_order can only be used with -preload or -static + if ( !fSegmentOrder.empty() && ((fOutputKind != Options::kPreload) && (fOutputKind != kStaticExecutable)) ) throw "-segment_order can only used used with -preload output"; // warn about bitcode option combinations @@ -5550,6 +5676,12 @@ void Options::checkIllegalOptionCombinations() if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff ) warning("embedded dylibs/frameworks only run on iOS 8 or later"); } + + + // produce nicer error when no input + if ( fInputFiles.empty() ) { + throw "no object files specified"; + } } diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 5155de4..e257e30 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -160,10 +160,10 @@ class Options } // Create an empty FileInfo. The path can be set implicitly by checkFileExists(). - FileInfo() : path(NULL), fileLen(0), modTime(0), options(), fromFileList(false) {}; + FileInfo() : path(NULL), fileLen(0), modTime(-1), options(), fromFileList(false) {}; // Create a FileInfo for a specific path, but does not stat the file. - FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(0), options(), fromFileList(false) {}; + FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(-1), options(), fromFileList(false) {}; ~FileInfo() { if (path) ::free((void*)path); } @@ -175,7 +175,7 @@ class Options // Returns true if a previous call to checkFileExists() succeeded. // Returns false if the file does not exist of checkFileExists() has never been called. - bool missing() const { return modTime==0; } + bool missing() const { return modTime == -1; } }; struct ExtraSection { @@ -292,6 +292,7 @@ class Options bool ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; } bool traceDylibs() const { return fTraceDylibs; } bool traceArchives() const { return fTraceArchives; } + bool traceEmitJSON() const { return fTraceEmitJSON; } bool deadCodeStrip() const { return fDeadStrip; } UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; } ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; } @@ -325,7 +326,7 @@ class Options CommonsMode commonsMode() const { return fCommonsMode; } bool warnCommons() const { return fWarnCommons; } bool keepRelocations(); - FileInfo findFile(const std::string &path) const; + FileInfo findFile(const std::string &path, const ld::dylib::File* fromDylib=nullptr) const; bool findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const; UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); @@ -356,7 +357,7 @@ class Options const std::vector& dylibOverrides() const { return fDylibOverrides; } const char* generatedMapPath() const { return fMapPath; } bool positionIndependentExecutable() const { return fPositionIndependentExecutable; } - Options::FileInfo findFileUsingPaths(const std::string &path) const; + Options::FileInfo findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const; bool deadStripDylibs() const { return fDeadStripDylibs; } bool allowedUndefined(const char* name) const { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } bool someAllowedUndefines() const { return (fAllowedUndefined.size() != 0); } @@ -411,6 +412,10 @@ class Options bool addFunctionStarts() const { return fFunctionStartsLoadCommand; } bool addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; } bool canReExportSymbols() const { return fCanReExportSymbols; } + const char* ltoCachePath() const { return fLtoCachePath; } + int ltoPruneInterval() const { return fLtoPruneInterval; } + int ltoPruneAfter() const { return fLtoPruneAfter; } + unsigned ltoMaxCacheSize() const { return fLtoMaxCacheSize; } const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } const char* overridePathlibLTO() const { return fOverridePathlibLTO; } const char* mcpuLTO() const { return fLtoCpu; } @@ -436,9 +441,11 @@ class Options bool ltoCodegenOnly() const { return fLTOCodegenOnly; } bool ignoreAutoLink() const { return fIgnoreAutoLink; } bool allowDeadDuplicates() const { return fAllowDeadDups; } + bool allowWeakImports() const { return fAllowWeakImports; } BitcodeMode bitcodeKind() const { return fBitcodeKind; } bool sharedRegionEncodingV2() const { return fSharedRegionEncodingV2; } bool useDataConstSegment() const { return fUseDataConstSegment; } + bool useTextExecSegment() const { return fUseTextExecSegment; } bool hasWeakBitTweaks() const; bool forceWeak(const char* symbolName) const; bool forceNotWeak(const char* symbolName) const; @@ -475,6 +482,8 @@ class Options std::string getSDKVersionStr() const; std::string getPlatformStr() const; uint8_t maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; } + bool hasDataSymbolMoves() const { return !fSymbolsMovesData.empty(); } + bool hasCodeSymbolMoves() const { return !fSymbolsMovesCode.empty(); } static uint32_t parseVersionNumber32(const char*); @@ -484,7 +493,6 @@ class Options enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; enum InterposeMode { kInterposeNone, kInterposeAllExternal, kInterposeSome }; - enum FilePreference { kModTime, kTextBasedStub, kMachO }; class SetWithWildcards { public: @@ -611,6 +619,10 @@ class Options const char* fSegAddrTablePath; const char* fMapPath; const char* fDyldInstallPath; + const char* fLtoCachePath; + int fLtoPruneInterval; + int fLtoPruneAfter; + unsigned fLtoMaxCacheSize; const char* fTempLtoObjectPath; const char* fOverridePathlibLTO; const char* fLtoCpu; @@ -687,6 +699,7 @@ class Options bool fTraceDylibs; bool fTraceIndirectDylibs; bool fTraceArchives; + bool fTraceEmitJSON; bool fOutputSlidable; bool fWarnWeakExports; bool fObjcGcCompaction; @@ -732,6 +745,7 @@ class Options bool fUseDataConstSegment; bool fUseDataConstSegmentForceOn; bool fUseDataConstSegmentForceOff; + bool fUseTextExecSegment; bool fBundleBitcode; bool fHideSymbols; bool fVerifyBitcode; @@ -743,6 +757,7 @@ class Options bool fLTOCodegenOnly; bool fIgnoreAutoLink; bool fAllowDeadDups; + bool fAllowWeakImports; BitcodeMode fBitcodeKind; Platform fPlatform; DebugInfoStripping fDebugInfoStripping; @@ -774,7 +789,6 @@ class Options std::vector fSegmentRenames; std::vector fSymbolsMovesData; std::vector fSymbolsMovesCode; - std::vector fSymbolsMovesZeroFill; bool fSaveTempFiles; mutable Snapshot fLinkSnapshot; bool fSnapshotRequested; @@ -782,8 +796,6 @@ class Options const char* fDependencyInfoPath; mutable int fDependencyFileDescriptor; uint8_t fMaxDefaultCommonAlign; - FilePreference fFilePreference; - bool fForceTextBasedStub; }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 1d708da..732eb54 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include #include @@ -162,6 +164,7 @@ void OutputFile::write(ld::Internal& state) //this->dumpAtomsBySection(state, false); this->writeOutputFile(state); this->writeMapFile(state); + this->writeJSONEntry(state); } bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start, uint64_t* end, uint32_t* index) @@ -354,6 +357,12 @@ void OutputFile::setLoadCommandsPadding(ld::Internal& state) case Options::kPreload: // mach-o MH_PRELOAD files need no padding between load commands and first section paddingSize = 0; + case Options::kKextBundle: + if ( _options.useTextExecSegment() ) { + paddingSize = 32; + break; + } + // else fall into default case default: // work backwards from end of segment and lay out sections so that extra room goes to padding atom uint64_t addr = 0; @@ -495,6 +504,30 @@ uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup throw "unexpected binding"; } +uint64_t OutputFile::addressAndTarget(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target) +{ + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + *target = fixup->u.target; + return (*target)->finalAddress(); + case ld::Fixup::bindingsIndirectlyBound: + *target = state.indirectBindingTable[fixup->u.bindingIndex]; + #ifndef NDEBUG + if ( ! (*target)->finalAddressMode() ) { + throwf("reference to symbol (which has not been assigned an address) %s", (*target)->name()); + } + #endif + return (*target)->finalAddress(); + } + throw "unexpected binding"; +} + + uint64_t OutputFile::sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup) { const ld::Atom* target = NULL; @@ -2012,7 +2045,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: { // GOT entry was optimized away, change LDR instruction to a ADD instruction = get32LE(fixUpLocation); - if ( (instruction & 0xFFC00000) != 0xF9400000 ) + if ( (instruction & 0xBFC00000) != 0xB9400000 ) throwf("GOT load reloc does not point to a LDR instruction in %s", atom->name()); uint32_t offset = accumulator & 0x00000FFF; uint32_t imm12 = offset << 10; @@ -2027,7 +2060,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: { // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD instruction = get32LE(fixUpLocation); - if ( (instruction & 0xFFC00000) != 0xF9400000 ) + if ( (instruction & 0xBFC00000) != 0xB9400000 ) throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name()); uint32_t offset = accumulator & 0x00000FFF; uint32_t imm12 = offset << 10; @@ -2038,7 +2071,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindStoreARM64PointerToGOT: set64LE(fixUpLocation, accumulator); break; - case ld::Fixup::kindStoreARM64PCRelToGOT: + case ld::Fixup::kindStoreARM64PCRelToGOT: if ( fit->contentAddendOnly ) delta = accumulator; else @@ -2655,13 +2688,22 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; - if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); - if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); - if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); - if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); + if ( log ) fprintf(stderr, "stabNlist offset=0x%08llX, size=0x%08llX\n", firstStabNlistFileOffset, lastStabNlistFileOffset-firstStabNlistFileOffset); + if ( log ) fprintf(stderr, "stabString offset=0x%08llX, size=0x%08llX\n", firstStabStringFileOffset, lastStabStringFileOffset-firstStabStringFileOffset); assert(firstStabNlistFileOffset <= firstStabStringFileOffset); excludeRegions.emplace_back(std::pair(firstStabNlistFileOffset, lastStabNlistFileOffset)); excludeRegions.emplace_back(std::pair(firstStabStringFileOffset, lastStabStringFileOffset)); + // exclude LINKEDIT LC_SEGMENT (size field depends on stabs size) + uint64_t linkeditSegCmdOffset; + uint64_t linkeditSegCmdSize; + _headersAndLoadCommandAtom->linkeditCmdInfo(linkeditSegCmdOffset, linkeditSegCmdSize); + excludeRegions.emplace_back(std::pair(linkeditSegCmdOffset, linkeditSegCmdOffset+linkeditSegCmdSize)); + if ( log ) fprintf(stderr, "linkedit SegCmdOffset=0x%08llX, size=0x%08llX\n", linkeditSegCmdOffset, linkeditSegCmdSize); + uint64_t symbolTableCmdOffset; + uint64_t symbolTableCmdSize; + _headersAndLoadCommandAtom->symbolTableCmdInfo(symbolTableCmdOffset, symbolTableCmdSize); + excludeRegions.emplace_back(std::pair(symbolTableCmdOffset, symbolTableCmdOffset+symbolTableCmdSize)); + if ( log ) fprintf(stderr, "linkedit SegCmdOffset=0x%08llX, size=0x%08llX\n", symbolTableCmdOffset, symbolTableCmdSize); } if ( !excludeRegions.empty() ) { CC_MD5_CTX md5state; @@ -2671,6 +2713,7 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) if ( lastSlash != NULL ) { CC_MD5_Update(&md5state, lastSlash, strlen(lastSlash)); } + std::sort(excludeRegions.begin(), excludeRegions.end()); uint64_t checksumStart = 0; for ( auto& region : excludeRegions ) { uint64_t regionStart = region.first; @@ -2863,13 +2906,24 @@ void OutputFile::writeOutputFile(ld::Internal& state) } struct AtomByNameSorter -{ - bool operator()(const ld::Atom* left, const ld::Atom* right) - { - return (strcmp(left->name(), right->name()) < 0); - } +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) const + { + return (strcmp(left->name(), right->name()) < 0); + } + + bool operator()(const ld::Atom* left, const char* right) const + { + return (strcmp(left->name(), right) < 0); + } + + bool operator()(const char* left, const ld::Atom* right) const + { + return (strcmp(left, right->name()) < 0); + } }; + class NotInSet { public: @@ -2904,7 +2958,9 @@ void OutputFile::buildSymbolTable(ld::Internal& state) // in -r mode, clarify symbolTableNotInFinalLinkedImages if ( _options.outputKind() == Options::kObjectFile ) { - if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) { + if ( (_options.architecture() == CPU_TYPE_X86_64) + || (_options.architecture() == CPU_TYPE_ARM64) + ) { // x86_64 .o files need labels on anonymous literal strings if ( (sect->type() == ld::Section::typeCString) && (atom->combine() == ld::Atom::combineByNameAndContent) ) { (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); @@ -3055,6 +3111,59 @@ void OutputFile::buildSymbolTable(ld::Internal& state) // sort by name std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter()); std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter()); + + std::map> addedSymbols; + std::map> hiddenSymbols; + for (const auto *atom : _exportedAtoms) { + // The exported symbols have already been sorted. Early exit the loop + // once we see a symbol that is lexicographically past the special + // linker symbol. + if (atom->name()[0] > '$') + break; + + std::string name(atom->name()); + if (name.rfind("$ld$add$", 7) == 0) { + auto pos = name.find_first_of('$', 10); + if (pos == std::string::npos) { + warning("bad special linker symbol '%s'", atom->name()); + continue; + } + auto &&symbolName = name.substr(pos+1); + auto it = addedSymbols.emplace(symbolName, std::initializer_list{name}); + if (!it.second) + it.first->second.emplace_back(name); + } else if (name.rfind("$ld$hide$", 8) == 0) { + auto pos = name.find_first_of('$', 11); + if (pos == std::string::npos) { + warning("bad special linker symbol '%s'", atom->name()); + continue; + } + auto &&symbolName = name.substr(pos+1); + auto it = hiddenSymbols.emplace(symbolName, std::initializer_list{name}); + if (!it.second) + it.first->second.emplace_back(name); + } + } + + for (const auto &it : addedSymbols) { + if (!std::binary_search(_exportedAtoms.begin(), _exportedAtoms.end(), it.first.c_str(), AtomByNameSorter())) + continue; + for (const auto &symbol : it.second) + warning("linker symbol '%s' adds already existing symbol '%s'", symbol.c_str(), it.first.c_str()); + } + + auto it = hiddenSymbols.begin(); + while (it != hiddenSymbols.end()) { + if (std::binary_search(_exportedAtoms.begin(), _exportedAtoms.end(), it->first.c_str(), AtomByNameSorter())) + it = hiddenSymbols.erase(it); + else + ++it; + } + + for (const auto &it : hiddenSymbols) { + for (const auto &symbol : it.second) + warning("linker symbol '%s' hides a non-existent symbol '%s'", symbol.c_str(), it.first.c_str()); + } } void OutputFile::addPreloadLinkEdit(ld::Internal& state) @@ -3181,7 +3290,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); + if ( _options.sharedRegionEncodingV2() ) + _splitSegInfoAtom = new SplitSegInfoV2Atom(_options, state, *this); + else + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3239,7 +3351,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); + if ( _options.sharedRegionEncodingV2() ) + _splitSegInfoAtom = new SplitSegInfoV2Atom(_options, state, *this); + else + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3770,7 +3885,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; // record end of last __TEXT section encrypted iPhoneOS apps. - if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) ) { + if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) && (strcmp(sect->sectionName(), "__oslogstring") != 0) ) { _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); } bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer) @@ -3908,14 +4023,17 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable) && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { if ( ! this->pieDisabled ) { + switch ( _options.architecture()) { +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: +#endif #if SUPPORT_ARCH_arm64 - if ( _options.architecture() == CPU_TYPE_ARM64 ) { + { const char* demangledName = strdup(_options.demangleSymbol(atom->name())); throwf("Absolute addressing not allowed in arm64 code but used in '%s' referencing '%s'", demangledName, _options.demangleSymbol(target->name())); } - else #endif - { + default: warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " "but used in %s from %s. " "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", @@ -3962,9 +4080,9 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } // Have direct reference to weak-global. This should be an indrect reference const char* demangledName = strdup(_options.demangleSymbol(atom->name())); - warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " + warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - demangledName, _options.demangleSymbol(target->name())); + demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path()); } return; } @@ -3991,9 +4109,9 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } // Have direct reference to weak-global. This should be an indrect reference const char* demangledName = strdup(_options.demangleSymbol(atom->name())); - warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " + warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - demangledName, _options.demangleSymbol(target->name())); + demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path()); } return; } @@ -4134,7 +4252,8 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s if ( _options.sharedRegionEligible() ) { // when range checking, ignore high byte of arm64 addends uint64_t checkAddend = addend; - if ( _options.architecture() == CPU_TYPE_ARM64 ) + if ( (_options.architecture() == CPU_TYPE_ARM64) + ) checkAddend &= 0x0FFFFFFFFFFFFFFFULL; if ( checkAddend != 0 ) { // make sure the addend does not cause the pointer to point outside the target's segment @@ -4342,7 +4461,9 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget) { - if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) { + if ( (_options.architecture() == CPU_TYPE_X86_64) + || (_options.architecture() == CPU_TYPE_ARM64) + ) { // x86_64 and ARM64 use external relocations for everthing that has a symbol return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); } @@ -4428,7 +4549,9 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio bool minusTargetUsesExternalReloc = (minusTarget != NULL) && this->useExternalSectionReloc(atom, minusTarget, fixupWithMinusTarget); // in x86_64 and arm64 .o files an external reloc means the content contains just the addend - if ( (_options.architecture() == CPU_TYPE_X86_64) ||(_options.architecture() == CPU_TYPE_ARM64) ) { + if ( (_options.architecture() == CPU_TYPE_X86_64) + || (_options.architecture() == CPU_TYPE_ARM64) + ) { if ( targetUsesExternalReloc ) { fixupWithTarget->contentAddendOnly = true; fixupWithStore->contentAddendOnly = true; @@ -4492,7 +4615,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) if ( fit->firstInCluster() ) target = NULL; if ( this->setsTarget(fit->kind) ) { - accumulator = addressOf(state, fit, &target); + accumulator = addressOf(state, fit, &target); thumbTarget = targetIsThumb(state, fit); if ( thumbTarget ) accumulator |= 1; @@ -4562,7 +4685,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) } break; case ld::Fixup::kindSetTargetImageOffset: - accumulator = addressOf(state, fit, &target); + accumulator = addressOf(state, fit, &target); assert(target != NULL); hadSubtract = true; break; @@ -4603,6 +4726,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { if ( fit->firstInCluster() ) { target = NULL; + hadSubtract = false; fromTarget = NULL; kind = 0; addend = 0; @@ -4610,7 +4734,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) fromOffset = atom->finalAddress() + fit->offsetInAtom - sect->address; } if ( this->setsTarget(fit->kind) ) { - accumulator = addressOf(state, fit, &target); + accumulator = addressAndTarget(state, fit, &target); thumbTarget = targetIsThumb(state, fit); if ( thumbTarget ) accumulator |= 1; @@ -4624,7 +4748,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) } switch ( fit->kind ) { case ld::Fixup::kindSubtractTargetAddress: - accumulator -= addressOf(state, fit, &fromTarget); + accumulator -= addressAndTarget(state, fit, &fromTarget); hadSubtract = true; break; case ld::Fixup::kindAddAddend: @@ -4729,7 +4853,7 @@ void OutputFile::makeSplitSegInfoV2(ld::Internal& state) break; case ld::Fixup::kindSetTargetImageOffset: kind = DYLD_CACHE_ADJ_V2_IMAGE_OFF_32; - accumulator = addressOf(state, fit, &target); + accumulator = addressAndTarget(state, fit, &target); assert(target != NULL); toSectionIndex = target->machoSection(); toOffset = accumulator - state.atomToSection[target]->address; @@ -4781,7 +4905,7 @@ void OutputFile::writeMapFile(ld::Internal& state) continue; for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - const ld::File* reader = atom->file(); + const ld::File* reader = atom->originalFile(); if ( reader == NULL ) continue; ld::File::Ordinal readerOrdinal = reader->ordinal(); @@ -4792,6 +4916,17 @@ void OutputFile::writeMapFile(ld::Internal& state) } } } + for (const ld::Atom* atom : state.deadAtoms) { + const ld::File* reader = atom->originalFile(); + if ( reader == NULL ) + continue; + ld::File::Ordinal readerOrdinal = reader->ordinal(); + std::map::iterator pos = readerToOrdinal.find(reader); + if ( pos == readerToOrdinal.end() ) { + readerToOrdinal[reader] = readerOrdinal; + ordinalToReader[readerOrdinal] = reader; + } + } fprintf(mapFile, "# Object files:\n"); fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); uint32_t fileIndex = 1; @@ -4826,7 +4961,21 @@ void OutputFile::writeMapFile(ld::Internal& state) continue; if ( atom->contentType() == ld::Atom::typeCString ) { strcpy(buffer, "literal string: "); - strlcat(buffer, (char*)atom->rawContentPointer(), 4096); + const char* s = (char*)atom->rawContentPointer(); + char* e = &buffer[4094]; + for (char* b = &buffer[strlen(buffer)]; b < e;) { + char c = *s++; + if ( c == '\n' ) { + *b++ = '\\'; + *b++ = 'n'; + } + else { + *b++ = c; + } + if ( c == '\0' ) + break; + } + buffer[4095] = '\0'; name = buffer; } else if ( (atom->contentType() == ld::Atom::typeCFI) && (strcmp(name, "FDE") == 0) ) { @@ -4858,7 +5007,41 @@ void OutputFile::writeMapFile(ld::Internal& state) name = buffer; } fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->finalAddress(), atom->size(), - readerToFileOrdinal[atom->file()], name); + readerToFileOrdinal[atom->originalFile()], name); + } + } + // preload check is hack until 26613948 is fixed + if ( _options.deadCodeStrip() && (_options.outputKind() != Options::kPreload) ) { + fprintf(mapFile, "\n"); + fprintf(mapFile, "# Dead Stripped Symbols:\n"); + fprintf(mapFile, "# \tSize \tFile Name\n"); + for (const ld::Atom* atom : state.deadAtoms) { + char buffer[4096]; + const char* name = atom->name(); + // don't add auto-stripped aliases to .map file + if ( (atom->size() == 0) && (atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) ) + continue; + if ( atom->contentType() == ld::Atom::typeCString ) { + strcpy(buffer, "literal string: "); + const char* s = (char*)atom->rawContentPointer(); + char* e = &buffer[4094]; + for (char* b = &buffer[strlen(buffer)]; b < e;) { + char c = *s++; + if ( c == '\n' ) { + *b++ = '\\'; + *b++ = 'n'; + } + else { + *b++ = c; + } + if ( c == '\0' ) + break; + } + buffer[4095] = '\0'; + name = buffer; + } + fprintf(mapFile, "<> \t0x%08llX\t[%3u] %s\n", atom->size(), + readerToFileOrdinal[atom->originalFile()], name); } } fclose(mapFile); @@ -4869,6 +5052,101 @@ void OutputFile::writeMapFile(ld::Internal& state) } } +void OutputFile::writeJSONEntry(ld::Internal& state) +{ + if ( _options.traceEmitJSON() && (_options.UUIDMode() != Options::kUUIDNone) && (_options.traceOutputFile() != NULL) ) { + + // Convert the UUID to a string. + const uint8_t* uuid = _headersAndLoadCommandAtom->getUUID(); + uuid_string_t uuidString; + + uuid_unparse(uuid, uuidString); + + // Enumerate the dylibs. + std::vector dynamicList; + std::vector upwardList; + std::vector reexportList; + + for (const ld::dylib::File* dylib : _dylibsToLoad) { + + if (dylib->willBeUpwardDylib()) { + + upwardList.push_back(dylib); + } else if (dylib->willBeReExported()) { + + reexportList.push_back(dylib); + } else { + + dynamicList.push_back(dylib); + } + } + + /* + * Build the JSON entry. + */ + + std::string jsonEntry = "{"; + + jsonEntry += "\"uuid\":\"" + std::string(uuidString) + "\","; + + // installPath() returns -final_output for non-dylibs + const char* lastNameSlash = strrchr(_options.installPath(), '/'); + const char* leafName = (lastNameSlash != NULL) ? lastNameSlash+1 : _options.outputFilePath(); + jsonEntry += "\"name\":\"" + std::string(leafName) + "\","; + + jsonEntry += "\"arch\":\"" + std::string(_options.architectureName()) + "\""; + + if (dynamicList.size() > 0) { + jsonEntry += ",\"dynamic\":["; + for (const ld::dylib::File* dylib : dynamicList) { + jsonEntry += "\"" + std::string(dylib->path()) + "\""; + if ((dylib != dynamicList.back())) { + jsonEntry += ","; + } + } + jsonEntry += "]"; + } + + if (upwardList.size() > 0) { + jsonEntry += ",\"upward-dynamic\":["; + for (const ld::dylib::File* dylib : upwardList) { + jsonEntry += "\"" + std::string(dylib->path()) + "\""; + if ((dylib != upwardList.back())) { + jsonEntry += ","; + } + } + jsonEntry += "]"; + } + + if (reexportList.size() > 0) { + jsonEntry += ",\"re-exports\":["; + for (const ld::dylib::File* dylib : reexportList) { + jsonEntry += "\"" + std::string(dylib->path()) + "\""; + if ((dylib != reexportList.back())) { + jsonEntry += ","; + } + } + jsonEntry += "]"; + } + + if (state.archivePaths.size() > 0) { + jsonEntry += ",\"archives\":["; + for (const std::string& archivePath : state.archivePaths) { + jsonEntry += "\"" + std::string(archivePath) + "\""; + if ((archivePath != state.archivePaths.back())) { + jsonEntry += ","; + } + } + jsonEntry += "]"; + } + jsonEntry += "}\n"; + + // Write the JSON entry to the trace file. + std::ofstream out(_options.traceOutputFile(), ios::app); + out << jsonEntry; + } +} + // used to sort atoms with debug notes class DebugNoteSorter { diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index 5fd27d8..81f2cfb 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -189,6 +189,7 @@ class OutputFile void updateLINKEDITAddresses(ld::Internal& state); void applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer); uint64_t addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target); + uint64_t addressAndTarget(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, bool thumb); @@ -205,6 +206,7 @@ class OutputFile void makeSplitSegInfo(ld::Internal& state); void makeSplitSegInfoV2(ld::Internal& state); void writeMapFile(ld::Internal& state); + void writeJSONEntry(ld::Internal& state); uint64_t lookBackAddend(ld::Fixup::iterator fit); bool takesNoDiskSpace(const ld::Section* sect); bool hasZeroForFileOffset(const ld::Section* sect); diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index ec04b46..1c8f16e 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -311,8 +311,10 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons { if ( linkerOption.size() == 1 ) { const char* lo1 = linkerOption.front(); - if ( strncmp(lo1, "-l", 2) == 0 ) { - _internal.linkerOptionLibraries.insert(&lo1[2]); + if ( strncmp(lo1, "-l", 2) == 0) { + if (_internal.linkerOptionLibraries.count(&lo1[2]) == 0) { + _internal.unprocessedLinkerOptionLibraries.insert(&lo1[2]); + } } else { warning("unknown linker option from object file ignored: '%s' in %s", lo1, fileName); @@ -321,8 +323,10 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons else if ( linkerOption.size() == 2 ) { const char* lo2a = linkerOption[0]; const char* lo2b = linkerOption[1]; - if ( strcmp(lo2a, "-framework") == 0 ) { - _internal.linkerOptionFrameworks.insert(lo2b); + if ( strcmp(lo2a, "-framework") == 0) { + if (_internal.linkerOptionFrameworks.count(lo2b) == 0) { + _internal.unprocessedLinkerOptionFrameworks.insert(lo2b); + } } else { warning("unknown linker option from object file ignored: '%s' '%s' from %s", lo2a, lo2b, fileName); @@ -345,6 +349,9 @@ static void userReadableSwiftVersion(uint8_t value, char versionString[64]) case 3: strcpy(versionString, "2.0"); break; + case 4: + strcpy(versionString, "3.0"); + break; default: sprintf(versionString, "unknown ABI version 0x%02X", value); } @@ -622,6 +629,28 @@ void Resolver::doFile(const ld::File& file) } break; } + + // verify dylibs use same version of Swift language + if ( file.swiftVersion() != 0 ) { + if ( _internal.swiftVersion == 0 ) { + _internal.swiftVersion = file.swiftVersion(); + } + else if ( file.swiftVersion() != _internal.swiftVersion ) { + char fileVersion[64]; + char otherVersion[64]; + userReadableSwiftVersion(file.swiftVersion(), fileVersion); + userReadableSwiftVersion(_internal.swiftVersion, otherVersion); + if ( file.swiftVersion() > _internal.swiftVersion ) { + throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } + else { + throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } + } + } + if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) { warning("linking against a dylib which is not safe for use in application extensions: %s", file.path()); } @@ -730,9 +759,14 @@ void Resolver::doAtom(const ld::Atom& atom) const std::vector& aliases = _options.cmdLineAliases(); for (std::vector::const_iterator it=aliases.begin(); it != aliases.end(); ++it) { if ( strcmp(it->realName, atom.name()) == 0 ) { - const AliasAtom* alias = new AliasAtom(atom, it->alias); - _aliasesFromCmdLine.push_back(alias); - this->doAtom(*alias); + if ( strcmp(it->realName, it->alias) == 0 ) { + warning("ignoring alias of itself '%s'", it->realName); + } + else { + const AliasAtom* alias = new AliasAtom(atom, it->alias); + _aliasesFromCmdLine.push_back(alias); + this->doAtom(*alias); + } } } } @@ -1188,11 +1222,13 @@ void Resolver::deadStripOptimize(bool force) } if ( _haveLLVMObjs && !force ) { + std::copy_if(_atoms.begin(), _atoms.end(), std::back_inserter(_internal.deadAtoms), NotLiveLTO() ); // don't remove combinable atoms, they may come back in lto output _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end()); _symbolTable.removeDeadAtoms(); } else { + std::copy_if(_atoms.begin(), _atoms.end(), std::back_inserter(_internal.deadAtoms), NotLive() ); _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); } @@ -1697,6 +1733,10 @@ void Resolver::linkTimeOptimize() lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); optOpt.tmpObjectFilePath = _options.tempLtoObjectPath(); + optOpt.ltoCachePath = _options.ltoCachePath(); + optOpt.ltoPruneInterval = _options.ltoPruneInterval(); + optOpt.ltoPruneAfter = _options.ltoPruneAfter(); + optOpt.ltoMaxCacheSize = _options.ltoMaxCacheSize(); optOpt.preserveAllGlobals = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList(); optOpt.verbose = _options.verbose(); optOpt.saveTemps = _options.saveTempFiles(); @@ -1718,6 +1758,7 @@ void Resolver::linkTimeOptimize() optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); optOpt.platform = _options.platform(); + optOpt.minOSVersion = _options.minOSversion(); optOpt.llvmOptions = &_options.llvmOptions(); optOpt.initialUndefines = &_options.initialUndefines(); @@ -1827,6 +1868,12 @@ void Resolver::tweakWeakness() } } +void Resolver::buildArchivesList() +{ + // Determine which archives were linked and update the internal state. + _inputFiles.archives(_internal); +} + void Resolver::dumpAtoms() { fprintf(stderr, "Resolver all atoms:\n"); @@ -1853,6 +1900,7 @@ void Resolver::resolve() this->fillInInternalState(); this->tweakWeakness(); _symbolTable.checkDuplicateSymbols(); + this->buildArchivesList(); } diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 6631d11..06b7a8b 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -72,7 +72,7 @@ class Resolver : public ld::File::AtomHandler void resolve(); - + private: struct WhyLiveBackChain { @@ -102,6 +102,7 @@ class Resolver : public ld::File::AtomHandler void remainingUndefines(std::vector&); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); void tweakWeakness(); + void buildArchivesList(); void doLinkerOption(const std::vector& linkerOption, const char* fileName); void dumpAtoms(); diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index a856598..a8d2276 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -118,6 +118,8 @@ class InternalState : public ld::Internal virtual ~InternalState() {} private: + bool inMoveRWChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch); + bool inMoveROChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch); class FinalSection : public ld::Internal::FinalSection { @@ -161,6 +163,7 @@ class InternalState : public ld::Internal SectionInToOut _sectionInToFinalMap; const Options& _options; bool _atomsOrderedInSections; + std::unordered_map _pendingSegMove; }; ld::Section InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified); @@ -196,7 +199,7 @@ InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sect _segmentOrder(segmentOrder(sect, opts)), _sectionOrder(sectionOrder(sect, sectionsSeen, opts)) { - //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n", + //fprintf(stderr, "FinalSection(%16s, %16s) _segmentOrder=%3d, _sectionOrder=0x%08X\n", // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder); } @@ -290,18 +293,33 @@ uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, cons if ( strcmp(sect.segmentName(), "__DATA") == 0 ) return order.size()+2; } + else if ( options.outputKind() == Options::kStaticExecutable ) { + const std::vector& order = options.segmentOrder(); + for (size_t i=0; i != order.size(); ++i) { + if ( strcmp(sect.segmentName(), order[i]) == 0 ) + return i+1; + } + if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) + return 0; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return order.size()+1; + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) + return order.size()+2; + } else { if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) return 0; if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) return 1; + if ( strcmp(sect.segmentName(), "__TEXT_EXEC") == 0 ) + return 2; // in -r mode, want __DATA last so zerofill sections are at end if ( strcmp(sect.segmentName(), "__DATA") == 0 ) - return (options.outputKind() == Options::kObjectFile) ? 5 : 2; + return (options.outputKind() == Options::kObjectFile) ? 6 : 3; if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) - return 3; - if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) return 4; + if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) + return 5; } // layout non-standard segments in order seen (+100 to shift beyond standard segments) for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) { @@ -336,16 +354,21 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint return 10; else return 11; + case ld::Section::typeNonStdCString: + if ( (strcmp(sect.sectionName(), "__oslogstring") == 0) && options.makeEncryptable() ) + return INT_MAX-1; + else + return sectionsSeen+20; case ld::Section::typeStub: return 12; case ld::Section::typeStubHelper: return 13; case ld::Section::typeLSDA: - return INT_MAX-3; + return INT_MAX-4; case ld::Section::typeUnwindInfo: - return INT_MAX-2; + return INT_MAX-3; case ld::Section::typeCFI: - return INT_MAX-1; + return INT_MAX-2; case ld::Section::typeStubClose: return INT_MAX; default: @@ -367,15 +390,15 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint case ld::Section::typeTerminatorPointers: return 13; case ld::Section::typeTLVInitialValues: - return INT_MAX-4; // need TLV zero-fill to follow TLV init values + return INT_MAX-259; // need TLV zero-fill to follow TLV init values case ld::Section::typeTLVZeroFill: - return INT_MAX-3; + return INT_MAX-258; case ld::Section::typeZeroFill: // make sure __huge is always last zerofill section if ( strcmp(sect.sectionName(), "__huge") == 0 ) return INT_MAX-1; else - return INT_MAX-2; + return INT_MAX-256+sectionsSeen; // zero fill need to be last and in "seen" order default: // __DATA,__const section should be near __mod_init_func not __data if ( strcmp(sect.sectionName(), "__const") == 0 ) @@ -418,7 +441,7 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint } // make sure zerofill in any other section is at end of segment if ( sect.type() == ld::Section::typeZeroFill ) - return INT_MAX-1; + return INT_MAX-256+sectionsSeen; return sectionsSeen+20; } @@ -537,10 +560,90 @@ bool InternalState::hasReferenceToWeakExternal(const ld::Atom& atom) return false; } +bool InternalState::inMoveRWChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch) +{ + if ( !_options.hasDataSymbolMoves() ) + return false; + + auto pos = _pendingSegMove.find(&atom); + if ( pos != _pendingSegMove.end() ) { + dstSeg = pos->second; + return true; + } + + bool result = false; + if ( _options.moveRwSymbol(atom.name(), filePath, dstSeg, wildCardMatch) ) + result = true; + + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + if ( inMoveRWChain(*(fit->u.target), filePath, dstSeg, wildCardMatch) ) + result = true; + } + } + } + + if ( result ) { + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + _pendingSegMove[fit->u.target] = dstSeg; + } + } + } + } + + return result; +} + + +bool InternalState::inMoveROChain(const ld::Atom& atom, const char* filePath, const char*& dstSeg, bool& wildCardMatch) +{ + if ( !_options.hasCodeSymbolMoves() ) + return false; + + auto pos = _pendingSegMove.find(&atom); + if ( pos != _pendingSegMove.end() ) { + dstSeg = pos->second; + return true; + } + + bool result = false; + if ( _options.moveRoSymbol(atom.name(), filePath, dstSeg, wildCardMatch) ) + result = true; + + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + if ( inMoveROChain(*(fit->u.target), filePath, dstSeg, wildCardMatch) ) + result = true; + } + } + } + + if ( result ) { + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + _pendingSegMove[fit->u.target] = dstSeg; + } + } + } + } + + return result; +} + + + + ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) { + //fprintf(stderr, "addAtom: %s\n", atom.name()); ld::Internal::FinalSection* fs = NULL; - const char* sectName = atom.section().sectionName(); + const char* curSectName = atom.section().sectionName(); + const char* curSegName = atom.section().segmentName(); ld::Section::Type sectType = atom.section().type(); const ld::File* f = atom.file(); const char* path = (f != NULL) ? f->path() : NULL; @@ -548,16 +651,15 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) // tentative defintions don't have a real section name yet sectType = ld::Section::typeZeroFill; if ( _options.mergeZeroFill() ) - sectName = FinalSection::_s_DATA_zerofill.sectionName(); + curSectName = FinalSection::_s_DATA_zerofill.sectionName(); else - sectName = FinalSection::_s_DATA_common.sectionName(); + curSectName = FinalSection::_s_DATA_common.sectionName(); } // Support for -move_to_r._segment if ( atom.symbolTableInclusion() == ld::Atom::symbolTableIn ) { const char* dstSeg; - //fprintf(stderr, "%s\n", atom.name()); bool wildCardMatch; - if ( _options.moveRwSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { + if ( inMoveRWChain(atom, path, dstSeg, wildCardMatch) ) { if ( (sectType != ld::Section::typeZeroFill) && (sectType != ld::Section::typeUnclassified) && (sectType != ld::Section::typeTentativeDefs) @@ -566,60 +668,59 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType); } else { + curSegName = dstSeg; if ( _options.traceSymbolLayout() ) - printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName); - fs = this->getFinalSection(dstSeg, sectName, sectType); + printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), curSegName, curSectName); + fs = this->getFinalSection(curSegName, curSectName, sectType); } } - if ( (fs == NULL) && _options.moveRoSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { + if ( (fs == NULL) && inMoveROChain(atom, path, dstSeg, wildCardMatch) ) { if ( (sectType != ld::Section::typeCode) && (sectType != ld::Section::typeUnclassified) ) { if ( !wildCardMatch ) warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType); } else { + curSegName = dstSeg; if ( _options.traceSymbolLayout() ) - printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName); - fs = this->getFinalSection(dstSeg, sectName, ld::Section::typeCode); + printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), curSegName, curSectName); + fs = this->getFinalSection(curSegName, curSectName, ld::Section::typeCode); } } } // support for -rename_section and -rename_segment - if ( fs == NULL ) { - const std::vector& sectRenames = _options.sectionRenames(); - const std::vector& segRenames = _options.segmentRenames(); - for ( std::vector::const_iterator it=sectRenames.begin(); it != sectRenames.end(); ++it) { - if ( (strcmp(sectName, it->fromSection) == 0) && (strcmp(atom.section().segmentName(), it->fromSegment) == 0) ) { - if ( _options.useDataConstSegment() && (strcmp(sectName, "__const") == 0) - && (strcmp(atom.section().segmentName(), "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) { - // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST - fs = this->getFinalSection("__DATA", "__const_weak", sectType); - if ( _options.traceSymbolLayout() ) - printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/_const_weak\n", atom.name()); - } - else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) { - // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST - fs = this->getFinalSection("__DATA", "__got_weak", sectType); - if ( _options.traceSymbolLayout() ) - printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name()); - } - else { - fs = this->getFinalSection(it->toSegment, it->toSection, sectType); - if ( _options.traceSymbolLayout() ) - printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); - } + for (const Options::SectionRename& rename : _options.sectionRenames()) { + if ( (strcmp(curSectName, rename.fromSection) == 0) && (strcmp(curSegName, rename.fromSegment) == 0) ) { + if ( _options.useDataConstSegment() && (strcmp(curSectName, "__const") == 0) && (strcmp(curSegName, "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + curSectName = "__const_weak"; + fs = this->getFinalSection(curSegName, curSectName, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__const_weak\n", atom.name()); } - } - if ( fs == NULL ) { - for ( std::vector::const_iterator it=segRenames.begin(); it != segRenames.end(); ++it) { - if ( strcmp(atom.section().segmentName(), it->fromSegment) == 0 ) { - if ( _options.traceSymbolLayout() ) - printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), it->toSegment, sectName); - fs = this->getFinalSection(it->toSegment, sectName, sectType); - } + else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST + curSectName = "__got_weak"; + fs = this->getFinalSection("__DATA", curSectName, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name()); + } + else { + curSegName = rename.toSegment; + curSectName = rename.toSection; + fs = this->getFinalSection(rename.toSegment, rename.toSection, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); } } } + for (const Options::SegmentRename& rename : _options.segmentRenames()) { + if ( strcmp(curSegName, rename.fromSegment) == 0 ) { + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), rename.toSegment, curSectName); + fs = this->getFinalSection(rename.toSegment, curSectName, sectType); + } + } // if no override, use default location if ( fs == NULL ) { @@ -628,7 +729,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) printf("symbol '%s', use default mapping to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); } - //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs); + //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalseg=%s\n", &atom, atom.name(), atom.section().sectionName(), fs->segmentName()); #ifndef NDEBUG validateFixups(atom); #endif @@ -861,6 +962,21 @@ void InternalState::setSectionSizesAndAlignments() this->hasThreadLocalVariableDefinitions = true; } } + + // All __thread_data and __thread_bss sections must have same alignment + uint8_t maxThreadAlign = 0; + for (ld::Internal::FinalSection* sect : sections) { + if ( (sect->type() == ld::Section::typeTLVInitialValues) || (sect->type() == ld::Section::typeTLVZeroFill) ) { + if ( sect->alignment > maxThreadAlign ) + maxThreadAlign = sect->alignment; + } + } + for (ld::Internal::FinalSection* sect : sections) { + if ( (sect->type() == ld::Section::typeTLVInitialValues) || (sect->type() == ld::Section::typeTLVZeroFill) ) { + sect->alignment = maxThreadAlign; + } + } + } uint64_t InternalState::assignFileOffsets() @@ -1270,7 +1386,9 @@ int main(int argc, const char* argv[]) } } catch (const char* msg) { - if ( archInferred ) + if ( strstr(msg, "malformed") != NULL ) + fprintf(stderr, "ld: %s\n", msg); + else if ( archInferred ) fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); else if ( showArch ) fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 89c091b..0db1b7e 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -93,7 +93,7 @@ class File Ordinal (uint64_t ordinal) : _ordinal(ordinal) {} - enum { kArgListPartition=0, kIndirectDylibPartition=1, kLTOPartition = 2, kLinkerOptionPartition = 2, InvalidParition=0xffff }; + enum { kArgListPartition=0, kIndirectDylibPartition=1, kLTOPartition = 2, kLinkerOptionPartition = 3, InvalidParition=0xffff }; Ordinal(uint16_t partition, uint16_t majorIndex, uint16_t minorIndex, uint16_t counter) { _ordinal = ((uint64_t)partition<<48) | ((uint64_t)majorIndex<<32) | ((uint64_t)minorIndex<<16) | ((uint64_t)counter<<0); } @@ -102,14 +102,14 @@ class File const uint16_t majorIndex() const { return (_ordinal>>32)&0xffff; } const uint16_t minorIndex() const { return (_ordinal>>16)&0xffff; } const uint16_t counter() const { return (_ordinal>>00)&0xffff; } - + const Ordinal nextMajorIndex() const { assert(majorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<32)); } const Ordinal nextMinorIndex() const { assert(minorIndex() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<16)); } const Ordinal nextCounter() const { assert(counter() < 0xffff); return Ordinal(_ordinal+((uint64_t)1<<0)); } public: Ordinal() : _ordinal(0) {}; - + static const Ordinal NullOrdinal() { return Ordinal((uint64_t)0); } const bool validOrdinal() const { return _ordinal != 0; } @@ -126,7 +126,7 @@ class File // Thus, an object pulled in from a .a that was listed in a file list could use all three fields. static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(kArgListPartition, argIndex, 0, 0); }; const Ordinal nextFileListOrdinal() const { return nextMinorIndex(); } - const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(kArgListPartition, majorIndex(), minorIndex(), index); } + const Ordinal archiveOrdinalWithMemberIndex(uint16_t memberIndex) const { return Ordinal(partition(), majorIndex(), minorIndex(), memberIndex); } // For indirect libraries the partition is IndirectDylibPartition and the counter is used or order the libraries. static const ld::File::Ordinal indirectDylibBase() { return Ordinal(kIndirectDylibPartition, 0, 0, 0); } @@ -137,8 +137,8 @@ class File // For linker options embedded in object files static const ld::File::Ordinal linkeOptionBase() { return Ordinal(kIndirectDylibPartition, 1, 0, 0); } - const Ordinal nextLinkerOptionOrdinal() { nextCounter(); return *this; }; - + const Ordinal nextLinkerOptionOrdinal() { return nextCounter(); }; + }; typedef enum { Reloc, Dylib, Archive, Other } Type; @@ -152,6 +152,7 @@ class File virtual bool forEachAtom(AtomHandler&) const = 0; virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0; virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; } + virtual bool objcHasCategoryClassPropertiesField() const { return false; } virtual uint8_t swiftVersion() const { return 0; } virtual uint32_t cpuSubType() const { return 0; } virtual uint32_t subFileCount() const { return 1; } @@ -173,11 +174,11 @@ class File // enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800, - mac10_9=0x000A0900, mac10_Future=0x10000000 }; + mac10_9=0x000A0900, mac10_12=0x000A0C00, mac10_Future=0x10000000 }; enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000, - iOS_9_0=0x00090000, iOS_Future=0x10000000}; + iOS_9_0=0x00090000, iOS_10_0=0x000A0000, iOS_Future=0x10000000}; enum WatchOSVersionMin { wOSVersionUnset=0, wOS_1_0=0x00010000, wOS_2_0=0x00020000 }; @@ -242,13 +243,13 @@ namespace dylib { { public: virtual ~DylibHandler() {} - virtual File* findDylib(const char* installPath, const char* fromPath) = 0; + virtual File* findDylib(const char* installPath, const ld::dylib::File* fromDylib, bool speculative) = 0; }; File(const char* pth, time_t modTime, Ordinal ord) : ld::File(pth, modTime, ord, Dylib), _dylibInstallPath(NULL), _frameworkName(NULL), _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0), - _explicitlyLinked(false), _implicitlyLinked(false), + _explicitlyLinked(false), _implicitlyLinked(false), _speculativelyLoaded(false), _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false), _upward(false), _dead(false) { } const char* installPath() const { return _dylibInstallPath; } @@ -260,7 +261,9 @@ namespace dylib { bool explicitlyLinked() const { return _explicitlyLinked; } void setImplicitlyLinked() { _implicitlyLinked = true; } bool implicitlyLinked() const { return _implicitlyLinked; } - + void setSpeculativelyLoaded() { _speculativelyLoaded = true; } + bool speculativelyLoaded() const { return _speculativelyLoaded; } + // attributes of how dylib will be used when linked void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; } bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; } @@ -278,6 +281,7 @@ namespace dylib { virtual bool providedExportAtom() const = 0; virtual const char* parentUmbrella() const = 0; virtual const std::vector* allowableClients() const = 0; + virtual const std::vector& rpaths() const = 0; virtual bool hasWeakExternals() const = 0; virtual bool deadStrippable() const = 0; virtual bool hasWeakDefinition(const char* name) const = 0; @@ -294,6 +298,7 @@ namespace dylib { uint32_t _dylibCompatibilityVersion; bool _explicitlyLinked; bool _implicitlyLinked; + bool _speculativelyLoaded; bool _lazyLoadedDylib; bool _forcedWeakLinked; bool _reExported; @@ -748,6 +753,9 @@ class Atom bool finalAddressMode() const { return (_mode == modeFinalAddress); } #endif virtual const File* file() const = 0; + // Return the original file this atom belongs to, for instance for an LTO atom, + // file() would return the LTO MachO file instead of the original bitcode file. + virtual const ld::File* originalFile() const { return file(); } virtual const char* translationUnitSource() const { return NULL; } virtual const char* name() const = 0; virtual uint64_t objectAddress() const = 0; @@ -888,14 +896,16 @@ class Internal std::vector sections; std::vector dylibs; + std::vector archivePaths; std::vector stabs; AtomToSection atomToSection; + CStringSet unprocessedLinkerOptionLibraries; + CStringSet unprocessedLinkerOptionFrameworks; CStringSet linkerOptionLibraries; CStringSet linkerOptionFrameworks; - CStringSet linkerOptionLibrariesProcessed; - CStringSet linkerOptionFrameworksProcessed; std::vector indirectBindingTable; std::vector filesWithBitcode; + std::vector deadAtoms; std::unordered_set allUndefProxies; const ld::dylib::File* bundleLoader; const Atom* entryPoint; @@ -917,7 +927,7 @@ class Internal bool someObjectHasOptimizationHints; bool dropAllBitcode; bool embedMarkerOnly; - std::string ltoBitcodePath; + std::vector ltoBitcodePath; }; diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 9004530..a1fec1a 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -45,7 +45,6 @@ namespace archive { -typedef const struct ranlib* ConstRanLibPtr; // forward reference template class File; @@ -112,26 +111,30 @@ class File : public ld::archive::File struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint32_t index;}; bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const; - typedef std::unordered_map NameToEntryMap; + typedef std::unordered_map NameToOffsetMap; typedef typename A::P P; typedef typename A::P::E E; typedef std::map MemberToStateMap; - const struct ranlib* ranlibHashSearch(const char* name) const; MemberState& makeObjectFileForMember(const Entry* member) const; bool memberHasObjCCategories(const Entry* member) const; void dumpTableOfContents(); void buildHashTable(); - +#ifdef SYMDEF_64 + void buildHashTable64(); +#endif const uint8_t* _archiveFileContent; uint64_t _archiveFilelength; const struct ranlib* _tableOfContents; +#ifdef SYMDEF_64 + const struct ranlib_64* _tableOfContents64; +#endif uint32_t _tableOfContentCount; const char* _tableOfContentStrings; mutable MemberToStateMap _instantiatedEntries; - NameToEntryMap _hashTable; + NameToOffsetMap _hashTable; const bool _forceLoadAll; const bool _forceLoadObjC; const bool _forceLoadThis; @@ -225,7 +228,6 @@ template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM64; } - template bool File::validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) { @@ -256,6 +258,10 @@ bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const m // skip option table-of-content member if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) continue; +#ifdef SYMDEF_64 + if ( (p==start) && ((strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0)) ) + continue; +#endif // archive is valid if first .o file is valid return (validMachOFile(p->content(), p->contentSize(), opts) || validLTOFile(p->content(), p->contentSize(), opts)); } @@ -269,7 +275,11 @@ File::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, ld::File::Ordinal ord, const ParserOptions& opts) : ld::archive::File(strdup(pth), modTime, ord), _archiveFileContent(fileContent), _archiveFilelength(fileLength), - _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL), + _tableOfContents(NULL), +#ifdef SYMDEF_64 + _tableOfContents64(NULL), +#endif + _tableOfContentCount(0), _tableOfContentStrings(NULL), _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC), _forceLoadThis(opts.forceLoadThisArchive), _objc2ABI(opts.objcABI2), _verboseLoad(opts.verboseLoad), _logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts) @@ -292,6 +302,19 @@ File::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, throw "malformed archive, perhaps wrong architecture"; this->buildHashTable(); } +#ifdef SYMDEF_64 + else if ( (strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0) ) { + const uint8_t* contents = firstMember->content(); + uint64_t ranlibArrayLen = E::get64(*((uint64_t*)contents)); + _tableOfContents64 = (const struct ranlib_64*)&contents[8]; + _tableOfContentCount = ranlibArrayLen / sizeof(struct ranlib_64); + _tableOfContentStrings = (const char*)&contents[ranlibArrayLen+16]; + if ( ((uint8_t*)(&_tableOfContents[_tableOfContentCount]) > &fileContent[fileLength]) + || ((uint8_t*)_tableOfContentStrings > &fileContent[fileLength]) ) + throw "malformed archive, perhaps wrong architecture"; + this->buildHashTable64(); + } +#endif else throw "archive has no table of contents"; } @@ -428,15 +451,19 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const p->getName(memberName, sizeof(memberName)); if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) continue; +#ifdef SYMDEF_64 + if ( (p==start) && ((strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0)) ) + continue; +#endif MemberState& state = this->makeObjectFileForMember(p); didSome |= loadMember(state, handler, "%s forced load of %s(%s)\n", _forceLoadThis ? "-force_load" : "-all_load", this->path(), memberName); } } else if ( _forceLoadObjC ) { // call handler on all .o files in this archive containing objc classes - 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)]; + for (const auto& entry : _hashTable) { + if ( (strncmp(entry.first, ".objc_c", 7) == 0) || (strncmp(entry.first, "_OBJC_CLASS_$_", 14) == 0) ) { + const Entry* member = (Entry*)&_archiveFileContent[entry.second]; MemberState& state = this->makeObjectFileForMember(member); char memberName[256]; member->getName(memberName, sizeof(memberName)); @@ -452,14 +479,31 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const // skip table-of-content member if ( (member==start) && ((strcmp(mname, SYMDEF_SORTED) == 0) || (strcmp(mname, SYMDEF) == 0)) ) continue; - MemberState& state = this->makeObjectFileForMember(member); - // only look at files not already loaded - if ( ! state.loaded ) { - if ( this->memberHasObjCCategories(member) ) { +#ifdef SYMDEF_64 + if ( (member==start) && ((strcmp(mname, SYMDEF_64_SORTED) == 0) || (strcmp(mname, SYMDEF_64) == 0)) ) + continue; +#endif + if ( validMachOFile(member->content(), member->contentSize(), _objOpts) ) { + MemberState& state = this->makeObjectFileForMember(member); + // only look at files not already loaded + if ( ! state.loaded ) { + if ( this->memberHasObjCCategories(member) ) { + MemberState& state = this->makeObjectFileForMember(member); + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); + } + } + } + else if ( validLTOFile(member->content(), member->contentSize(), _objOpts) ) { + if ( lto::hasObjCCategory(member->content(), member->contentSize()) ) { MemberState& state = this->makeObjectFileForMember(member); - char memberName[256]; - member->getName(memberName, sizeof(memberName)); - didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); + // only look at files not already loaded + if ( ! state.loaded ) { + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); + } } } } @@ -475,16 +519,16 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han 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); - char memberName[256]; - member->getName(memberName, sizeof(memberName)); - return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); - } - //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); - return false; + const auto& pos = _hashTable.find(name); + if ( pos == _hashTable.end() ) + return false; + + // do a hash search of table of contents looking for requested symbol + const Entry* member = (Entry*)&_archiveFileContent[pos->second]; + MemberState& state = this->makeObjectFileForMember(member); + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); } class CheckIsDataSymbolHandler : public ld::File::AtomHandler @@ -514,38 +558,27 @@ bool File::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHand 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() ) { - char memberName[256]; - member->getName(memberName, sizeof(memberName)); - return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); - } + const auto& pos = _hashTable.find(name); + if ( pos == _hashTable.end() ) + return false; + + const Entry* member = (Entry*)&_archiveFileContent[pos->second]; + 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() ) { + char memberName[256]; + member->getName(memberName, sizeof(memberName)); + return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); } } + //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); return false; } - -typedef const struct ranlib* ConstRanLibPtr; - -template -ConstRanLibPtr File::ranlibHashSearch(const char* name) const -{ - typename NameToEntryMap::const_iterator pos = _hashTable.find(name); - if ( pos != _hashTable.end() ) - return pos->second; - else - return NULL; -} - template void File::buildHashTable() { @@ -554,16 +587,38 @@ void File::buildHashTable() for (int i = _tableOfContentCount-1; i >= 0; --i) { const struct ranlib* entry = &_tableOfContents[i]; const char* entryName = &_tableOfContentStrings[E::get32(entry->ran_un.ran_strx)]; - if ( E::get32(entry->ran_off) > _archiveFilelength ) { + uint64_t offset = E::get32(entry->ran_off); + if ( offset > _archiveFilelength ) { throwf("malformed archive TOC entry for %s, offset %d is beyond end of file %lld\n", entryName, entry->ran_off, _archiveFilelength); } //const Entry* member = (Entry*)&_archiveFileContent[E::get32(entry->ran_off)]; //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); - _hashTable[entryName] = entry; + _hashTable[entryName] = offset; + } +} + +#ifdef SYMDEF_64 +template +void File::buildHashTable64() +{ + // walk through list backwards, adding/overwriting entries + // this assures that with duplicates those earliest in the list will be found + for (int i = _tableOfContentCount-1; i >= 0; --i) { + const struct ranlib_64* entry = &_tableOfContents64[i]; + const char* entryName = &_tableOfContentStrings[E::get64(entry->ran_un.ran_strx)]; + uint64_t offset = E::get64(entry->ran_off); + if ( offset > _archiveFilelength ) { + throwf("malformed archive TOC entry for %s, offset %lld is beyond end of file %lld\n", + entryName, entry->ran_off, _archiveFilelength); + } + + //fprintf(stderr, "adding hash %d: %s -> 0x%0llX\n", i, entryName, offset); + _hashTable[entryName] = offset; } } +#endif template void File::dumpTableOfContents() diff --git a/ld64/src/ld/parsers/generic_dylib_file.hpp b/ld64/src/ld/parsers/generic_dylib_file.hpp index 301074f..529e4ab 100644 --- a/ld64/src/ld/parsers/generic_dylib_file.hpp +++ b/ld64/src/ld/parsers/generic_dylib_file.hpp @@ -124,7 +124,7 @@ class File : public ld::dylib::File { public: File(const char* path, time_t mTime, ld::File::Ordinal ordinal, Options::Platform platform, - uint32_t linkMinOSVersion, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, + uint32_t linkMinOSVersion, bool allowWeakImports, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, bool allowSimToMacOSX, bool addVers); // overrides of ld::File @@ -142,6 +142,7 @@ class File : public ld::dylib::File virtual bool providedExportAtom() const override final { return _providedAtom; } virtual const char* parentUmbrella() const override final { return _parentUmbrella; } virtual const std::vector* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; } + virtual const std::vector& rpaths() const override final { return _rpaths; } virtual bool hasWeakExternals() const override final { return _hasWeakExports; } virtual bool deadStrippable() const override final { return _deadStrippable; } virtual bool hasWeakDefinition(const char* name) const override final; @@ -190,7 +191,6 @@ class File : public ld::dylib::File protected: bool isPublicLocation(const char* path) const; - void addSymbol(const char* name, bool weak = false, bool tlv = false, pint_t address = 0); private: ld::Section _importProxySection; @@ -204,6 +204,7 @@ class File : public ld::dylib::File std::vector _dependentDylibs; ImportAtom* _importAtom; std::vector _allowableClients; + std::vector _rpaths; const char* _parentUmbrella; std::unique_ptr _bitcode; const Options::Platform _platform; @@ -222,7 +223,7 @@ class File : public ld::dylib::File bool _deadStrippable; bool _hasPublicInstallName; bool _appExtensionSafe; - + const bool _allowWeakImports; const bool _allowSimToMacOSXLinking; const bool _addVersionLoadCommand; @@ -234,7 +235,7 @@ bool File::_s_logHashtable = false; template File::File(const char* path, time_t mTime, ld::File::Ordinal ord, Options::Platform platform, - uint32_t linkMinOSVersion, bool linkingFlatNamespace, + uint32_t linkMinOSVersion, bool allowWeakImports, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, bool allowSimToMacOSX, bool addVers) : ld::dylib::File(path, mTime, ord), @@ -260,6 +261,7 @@ File::File(const char* path, time_t mTime, ld::File::Ordinal ord, Options::P _deadStrippable(false), _hasPublicInstallName(false), _appExtensionSafe(false), + _allowWeakImports(allowWeakImports), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers) { @@ -388,7 +390,7 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); if ( _linkingFlat ) { for (auto &dep : _dependentDylibs) - dep.dylib = (File*)handler->findDylib(dep.path, this->path()); + dep.dylib = (File*)handler->findDylib(dep.path, this, false); } else if ( _noRexports ) { // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do @@ -400,7 +402,7 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), dep.path); // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - dep.dylib = (File*)handler->findDylib(dep.path, this->path()); + dep.dylib = (File*)handler->findDylib(dep.path, this, this->speculativelyLoaded()); if ( dep.dylib->hasPublicInstallName() && !dep.dylib->wrongOS() ) { // promote this child to be automatically added as a direct dependent if this already is if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(dep.path, dep.dylib->installPath()) == 0) ) { @@ -425,7 +427,7 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b } else if ( !_explictReExportFound ) { // see if child contains LC_SUB_FRAMEWORK with my name - dep.dylib = (File*)handler->findDylib(dep.path, this->path()); + dep.dylib = (File*)handler->findDylib(dep.path, this, this->speculativelyLoaded()); const char* parentUmbrellaName = dep.dylib->parentUmbrella(); if ( parentUmbrellaName != nullptr ) { const char* parentName = this->path(); @@ -477,64 +479,6 @@ bool File::isPublicLocation(const char* path) const return false; } -template -void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) -{ - // symbols that start with $ld$ are meta-data to the static linker - // need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { - // $ld$ $ $ - const char* symAction = &name[4]; - const char* symCond = strchr(symAction, '$'); - if ( symCond != nullptr ) { - char curOSVers[16]; - sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); - if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { - const char* symName = strchr(&symCond[1], '$'); - if ( symName != nullptr ) { - ++symName; - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( _s_logHashtable ) - fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); - _ignoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else if ( strncmp(symAction, "install_name$", 13) == 0 ) { - _dylibInstallPath = strdup(symName); - _installPathOverride = true; - // CoreGraphics redirects to ApplicationServices, but with wrong compat version - if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) - _dylibCompatibilityVersion = Options::parseVersionNumber32("1.0"); - return; - } - else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { - _dylibCompatibilityVersion = Options::parseVersionNumber32(symName); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->path()); - } - } - } - } - else { - warning("bad symbol condition: %s in dylib %s", name, this->path()); - } - } - - // add symbol as possible export if we are not supposed to ignore it - if ( _ignoreExports.count(name) == 0 ) { - AtomAndWeak bucket = { nullptr, weakDef, tlv, address }; - if ( this->_s_logHashtable ) - fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); - _atoms[strdup(name)] = bucket; - } -} - // If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB template bool File::allSymbolsAreWeakImported() const diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index f0f5f10..a5ab821 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -63,14 +65,13 @@ class InternalAtom : public ld::Atom public: InternalAtom(class File& f); // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual const char* name() const { return "import-atom"; } - virtual uint64_t size() const { return 0; } - 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 &_undefs[0]; } - virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } + ld::File* file() const override { return &_file; } + const char* name() const override { return "import-atom"; } + uint64_t size() const override { return 0; } + uint64_t objectAddress() const override { return 0; } + void copyRawContent(uint8_t buffer[]) const override { } + ld::Fixup::iterator fixupsBegin() const override { return &_undefs[0]; } + ld::Fixup::iterator fixupsEnd() const override { return &_undefs[_undefs.size()]; } // for adding references to symbols outside bitcode file void addReference(const char* nm) @@ -91,24 +92,25 @@ class File : public ld::relocatable::File public: File(const char* path, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch); - virtual ~File(); + ~File() override; // overrides of ld::File - virtual bool forEachAtom(ld::File::AtomHandler&) const; - virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const + bool forEachAtom(ld::File::AtomHandler&) const override; + bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const override { return false; } - virtual uint32_t cpuSubType() const { return _cpuSubType; } + uint32_t cpuSubType() const override { return _cpuSubType; } // overrides of ld::relocatable::File - virtual DebugInfoKind debugInfo() const { return _debugInfo; } - virtual const char* debugInfoPath() const { return _debugInfoPath; } - virtual time_t debugInfoModificationTime() const + DebugInfoKind debugInfo() const override { return _debugInfo; } + const char* debugInfoPath() const override { return _debugInfoPath; } + time_t debugInfoModificationTime() const override { return _debugInfoModTime; } - virtual const std::vector* stabs() const { return NULL; } - virtual bool canScatterAtoms() const { return true; } - virtual LinkerOptionsList* linkerOptions() const { return NULL; } - - + const std::vector* stabs() const override { return NULL; } + bool canScatterAtoms() const override { return true; } + LinkerOptionsList* linkerOptions() const override { return NULL; } + bool isThinLTO() const { return _isThinLTO; } + void setIsThinLTO(bool ThinLTO) { _isThinLTO = ThinLTO; } + // fixme rdar://24734472 objCConstraint() and objcHasCategoryClassProperties() void release(); lto_module_t module() { return _module; } class InternalAtom& internalAtom() { return _internalAtom; } @@ -122,11 +124,15 @@ class File : public ld::relocatable::File static bool sSupportsLocalContext; static bool sHasTriedLocalContext; bool mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule); +#if LTO_API_VERSION >= 18 + void addToThinGenerator(thinlto_code_gen_t generator); +#endif private: friend class Atom; friend class InternalAtom; friend class Parser; - + + bool _isThinLTO; cpu_type_t _architecture; class InternalAtom _internalAtom; class Atom* _atomArray; @@ -157,31 +163,32 @@ class Atom : public ld::Atom ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a, bool ah); // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual const char* translationUnitSource() const + const ld::File* file() const override { return (_compiledAtom ? _compiledAtom->file() : &_file ); } + const ld::File* originalFile() const override { return &_file; } + const char* translationUnitSource() const override { return (_compiledAtom ? _compiledAtom->translationUnitSource() : NULL); } - virtual const char* name() const { return _name; } - virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } - virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } - virtual void copyRawContent(uint8_t buffer[]) const + const char* name() const override { return _name; } + uint64_t size() const override { return (_compiledAtom ? _compiledAtom->size() : 0); } + uint64_t objectAddress() const override { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } + void copyRawContent(uint8_t buffer[]) const override { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } - virtual const uint8_t* rawContentPointer() const + const uint8_t* rawContentPointer() const override { return (_compiledAtom ? _compiledAtom->rawContentPointer() : NULL); } - virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const override { return (_compiledAtom ? _compiledAtom->contentHash(ibt) : 0); } - virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const override { return (_compiledAtom ? _compiledAtom->canCoalesceWith(rhs,ibt) : false); } - virtual ld::Fixup::iterator fixupsBegin() const + ld::Fixup::iterator fixupsBegin() const override { return (_compiledAtom ? _compiledAtom->fixupsBegin() : (ld::Fixup*)&_file._fixupToInternal); } - virtual ld::Fixup::iterator fixupsEnd() const + ld::Fixup::iterator fixupsEnd() const override { return (_compiledAtom ? _compiledAtom->fixupsEnd() : &((ld::Fixup*)&_file._fixupToInternal)[1]); } - virtual ld::Atom::UnwindInfo::iterator beginUnwind() const + ld::Atom::UnwindInfo::iterator beginUnwind() const override { return (_compiledAtom ? _compiledAtom->beginUnwind() : NULL); } - virtual ld::Atom::UnwindInfo::iterator endUnwind() const + ld::Atom::UnwindInfo::iterator endUnwind() const override { return (_compiledAtom ? _compiledAtom->endUnwind() : NULL); } - virtual ld::Atom::LineInfo::iterator beginLineInfo() const + ld::Atom::LineInfo::iterator beginLineInfo() const override { return (_compiledAtom ? _compiledAtom->beginLineInfo() : NULL); } - virtual ld::Atom::LineInfo::iterator endLineInfo() const + ld::Atom::LineInfo::iterator endLineInfo() const override { return (_compiledAtom ? _compiledAtom->endLineInfo() : NULL); } const ld::Atom* compiledAtom() { return _compiledAtom; } @@ -216,11 +223,13 @@ class Parser std::vector& newAtoms, std::vector& additionalUndefines); - static const char* ltoVersion() { return ::lto_get_version(); } + static const char* ltoVersion() { return ::lto_get_version(); } private: + static const char* tripletPrefixForArch(cpu_type_t arch); - static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options); + static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const std::string &path, const OptimizeOptions& options, + ld::File::Ordinal ordinal); #if LTO_API_VERSION >= 7 static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t, const char*, void*); #endif @@ -231,23 +240,70 @@ class Parser class AtomSyncer : public ld::File::AtomHandler { public: AtomSyncer(std::vector& a, std::vector&na, - CStringToAtom la, CStringToAtom dla, const OptimizeOptions& options) : - _options(options), _additionalUndefines(a), _newAtoms(na), _llvmAtoms(la), _deadllvmAtoms(dla) { } - virtual void doAtom(const class ld::Atom&); - virtual void doFile(const class ld::File&) { } + const CStringToAtom &la, const CStringToAtom &dla, const OptimizeOptions& options) : + _options(options), _additionalUndefines(a), _newAtoms(na), _llvmAtoms(la), _deadllvmAtoms(dla), _lastProxiedAtom(NULL), _lastProxiedFile(NULL) {} + void doAtom(const class ld::Atom&) override; + void doFile(const class ld::File&) override { } - const OptimizeOptions& _options; std::vector& _additionalUndefines; std::vector& _newAtoms; - CStringToAtom _llvmAtoms; - CStringToAtom _deadllvmAtoms; + const CStringToAtom &_llvmAtoms; + const CStringToAtom &_deadllvmAtoms; + const ld::Atom* _lastProxiedAtom; + const ld::File* _lastProxiedFile; }; + static void setPreservedSymbols(const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + CStringToAtom &deadllvmAtoms, + CStringToAtom &llvmAtoms, + lto_code_gen_t generator); + + static std::tuple codegen(const OptimizeOptions& options, + ld::Internal& state, + lto_code_gen_t generator, + std::string& object_path); + + + static void loadMachO(ld::relocatable::File* machoFile, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines, + CStringToAtom &llvmAtoms, + CStringToAtom &deadllvmAtoms); + + static bool optimizeLTO(const std::vector files, + const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines); + + static bool optimizeThinLTO(const std::vector& Files, + const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines); + + static thinlto_code_gen_t init_thinlto_codegen(const std::vector& files, + const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + CStringToAtom& deadllvmAtoms, + CStringToAtom& llvmAtoms); + static std::vector _s_files; + static bool _s_llvmOptionsProcessed; }; std::vector Parser::_s_files; +bool Parser::_s_llvmOptionsProcessed = false; bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) @@ -298,7 +354,8 @@ File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* } -ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options) +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const std::string &path, const OptimizeOptions& options, + ld::File::Ordinal ordinal) { mach_o::relocatable::ParserOptions objOpts; objOpts.architecture = options.arch; @@ -313,23 +370,23 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.simulator = options.simulator; objOpts.ignoreMismatchPlatform = options.ignoreMismatchPlatform; objOpts.platform = options.platform; + objOpts.minOSVersion = options.minOSVersion; objOpts.subType = 0; objOpts.srcKind = ld::relocatable::File::kSourceLTO; objOpts.treateBitcodeAsData = false; objOpts.usingBitcode = options.bitcodeBundle; objOpts.maxDefaultCommonAlignment = options.maxDefaultCommonAlignment; - // mach-o parsing is done in-memory, but need path for debug notes - const char* path = "/tmp/lto.o"; + const char *object_path = path.c_str(); + if (path.empty()) + object_path = "/tmp/lto.o"; + time_t modTime = 0; - if ( options.tmpObjectFilePath != NULL ) { - path = options.tmpObjectFilePath; - struct stat statBuffer; - if ( stat(options.tmpObjectFilePath, &statBuffer) == 0 ) - modTime = statBuffer.st_mtime; - } - - ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, ld::File::Ordinal::LTOOrdinal(), objOpts); + struct stat statBuffer; + if ( stat(object_path, &statBuffer) == 0 ) + modTime = statBuffer.st_mtime; + + ld::relocatable::File* result = mach_o::relocatable::parse(p, len, strdup(object_path), modTime, ordinal, objOpts); if ( result != NULL ) return result; throw "LLVM LTO, file is not of required architecture"; @@ -338,7 +395,7 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) - : ld::relocatable::File(pth,mTime,ordinal), _architecture(arch), _internalAtom(*this), + : ld::relocatable::File(pth,mTime,ordinal), _isThinLTO(false), _architecture(arch), _internalAtom(*this), _atomArray(NULL), _atomArrayCount(0), _module(NULL), _path(pth), _content(content), _contentLength(contentLength), _debugInfoPath(pth), _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), @@ -367,7 +424,11 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 throwf("could not parse object file %s: '%s', using libLTO version '%s'", pth, ::lto_get_error_message(), ::lto_get_version()); if ( log ) fprintf(stderr, "bitcode file: %s\n", pth); - + +#if LTO_API_VERSION >= 18 + _isThinLTO = ::lto_module_is_thinlto(_module); +#endif + // create atom for each global symbol in module uint32_t count = ::lto_module_get_num_symbols(_module); _atomArray = (Atom*)malloc(sizeof(Atom)*count); @@ -481,6 +542,13 @@ bool File::mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule) { return false; } +#if LTO_API_VERSION >= 18 +void File::addToThinGenerator(thinlto_code_gen_t generator) { + assert(!_module && "Expected module to be disposed"); + ::thinlto_codegen_add_module(generator, _path, (const char *)_content, _contentLength); +} +#endif + void File::release() { if ( _module != NULL ) @@ -556,79 +624,27 @@ void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, co } #endif -bool Parser::optimize( const std::vector& allAtoms, - ld::Internal& state, - const OptimizeOptions& options, - ld::File::AtomHandler& handler, - std::vector& newAtoms, - std::vector& additionalUndefines) -{ - const bool logMustPreserve = false; - const bool logExtraOptions = false; - const bool logBitcodeFiles = false; - const bool logAtomsBeforeSync = false; - - // exit quickly if nothing to do - if ( _s_files.size() == 0 ) - return false; - - // print out LTO version string if -v was used - if ( options.verbose ) - fprintf(stderr, "%s\n", ::lto_get_version()); - - // create optimizer and add each Reader - lto_code_gen_t generator = NULL; -#if LTO_API_VERSION >= 11 - if ( File::sSupportsLocalContext ) - generator = ::lto_codegen_create_in_local_context(); - else -#endif - generator = ::lto_codegen_create(); -#if LTO_API_VERSION >= 7 - lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); -#endif - - // The order that files are merged must match command line order - std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); - ld::File::Ordinal lastOrdinal; - - // When flto_codegen_only is on and we have a single .bc file, use lto_codegen_set_module instead of - // lto_codegen_add_module, to make sure the the destination module will be the same as the input .bc file. - bool useSetModule = false; -#if LTO_API_VERSION >= 13 - useSetModule = (_s_files.size() == 1) && options.ltoCodegenOnly && (::lto_api_version() >= 13); -#endif - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - File* f = *it; - assert(f->ordinal() > lastOrdinal); - if ( logBitcodeFiles && !useSetModule) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); - if ( logBitcodeFiles && useSetModule) fprintf(stderr, "lto_codegen_set_module(%s)\n", f->path()); - if ( f->mergeIntoGenerator(generator, useSetModule) ) - throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); - lastOrdinal = f->ordinal(); - } - - // add any -mllvm command line options - for (std::vector::const_iterator it=options.llvmOptions->begin(); it != options.llvmOptions->end(); ++it) { - if ( logExtraOptions ) fprintf(stderr, "passing option to llvm: %s\n", *it); - ::lto_codegen_debug_options(generator, *it); - } - // Need a way for LTO to get cpu variants (until that info is in bitcode) - if ( options.mcpu != NULL ) - ::lto_codegen_set_cpu(generator, options.mcpu); +/// Instruct libLTO about the list of symbols to preserve, compute deadllvmAtoms and llvmAtoms +void Parser::setPreservedSymbols( const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + CStringToAtom &deadllvmAtoms, + CStringToAtom &llvmAtoms, + lto_code_gen_t generator) { + const bool logMustPreserve = false; - // The atom graph uses directed edges (references). Collect all references where - // originating atom is not part of any LTO Reader. This allows optimizer to optimize an - // external (i.e. not originated from same .o file) reference if all originating atoms are also + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also // defined in llvm bitcode file. CStringSet nonLLVMRefs; - CStringToAtom llvmAtoms; - bool hasNonllvmAtoms = false; + bool hasNonllvmAtoms = false; for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { const ld::Atom* atom = *it; - // only look at references that come from an atom that is not an llvm atom - if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { + // only look at references that come from an atom that is not an LTO atom + if (atom->contentType() != ld::Atom::typeLTOtemporary || + ((lto::File *)atom->file())->isThinLTO()) { if ( (atom->section().type() != ld::Section::typeMachHeader) && (atom->definition() != ld::Atom::definitionProxy) ) { hasNonllvmAtoms = true; } @@ -637,7 +653,7 @@ bool Parser::optimize( const std::vector& allAtoms, switch ( fit->binding ) { case ld::Fixup::bindingDirectlyBound: // that reference an llvm atom - if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) + if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) nonLLVMRefs.insert(fit->u.target->name()); break; case ld::Fixup::bindingsIndirectlyBound: @@ -655,15 +671,14 @@ bool Parser::optimize( const std::vector& allAtoms, } // if entry point is in a llvm bitcode file, it must be preserved by LTO if ( state.entryPoint!= NULL ) { - if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary ) + if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary ) nonLLVMRefs.insert(state.entryPoint->name()); } - + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced - // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead // atom so that the linker can replace it with the mach-o one later. - CStringToAtom deadllvmAtoms; for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { const ld::Atom* atom = *it; if ( atom->coalescedAway() && (atom->contentType() == ld::Atom::typeLTOtemporary) ) { @@ -680,7 +695,7 @@ bool Parser::optimize( const std::vector& allAtoms, if ( llvmAtom->coalescedAway() ) { const char* name = llvmAtom->name(); if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) { - if ( logMustPreserve ) + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name); ::lto_codegen_add_must_preserve_symbol(generator, name); deadllvmAtoms[name] = (Atom*)llvmAtom; @@ -692,7 +707,7 @@ bool Parser::optimize( const std::vector& allAtoms, } } } - + // tell code generator about symbols that must be preserved for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { const char* name = it->first; @@ -701,7 +716,7 @@ bool Parser::optimize( const std::vector& allAtoms, // 1 - atom scope is global (and not linkage unit). // 2 - included in nonLLVMRefs set. // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) { + if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) { if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name); ::lto_codegen_add_must_preserve_symbol(generator, name); } @@ -715,15 +730,15 @@ bool Parser::optimize( const std::vector& allAtoms, ::lto_codegen_add_must_preserve_symbol(generator, name); } } - + // tell code generator to preserve initial undefines for( std::vector::const_iterator it=options.initialUndefines->begin(); it != options.initialUndefines->end(); ++it) { if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because it is an initial undefine\n", *it); ::lto_codegen_add_must_preserve_symbol(generator, *it); } - // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) - if ( options.relocatable && !hasNonllvmAtoms ) { + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( options.relocatable && !hasNonllvmAtoms ) { #if LTO_API_VERSION >= 15 ::lto_codegen_set_should_embed_uselists(generator, false); #endif @@ -732,44 +747,56 @@ bool Parser::optimize( const std::vector& allAtoms, exit(0); } warning("could not produce merged bitcode file"); - } - - // set code-gen model - lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + } + +} + +// Retrieve the codegen model from the options +static lto_codegen_model getCodeModel(const OptimizeOptions& options) { if ( options.mainExecutable ) { if ( options.staticExecutable ) { // x86_64 "static" or any "-static -pie" is really dynamic code model if ( (options.arch == CPU_TYPE_X86_64) || options.pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + return LTO_CODEGEN_PIC_MODEL_DYNAMIC; else - model = LTO_CODEGEN_PIC_MODEL_STATIC; + return LTO_CODEGEN_PIC_MODEL_STATIC; } else { if ( options.pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + return LTO_CODEGEN_PIC_MODEL_DYNAMIC; else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + return LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; } } else { if ( options.allowTextRelocs ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + return LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + return LTO_CODEGEN_PIC_MODEL_DYNAMIC; } - if ( ::lto_codegen_set_pic_model(generator, model) ) + +} + +std::tuple Parser::codegen(const OptimizeOptions& options, + ld::Internal& state, + lto_code_gen_t generator, + std::string& object_path) { + uint8_t *machOFile; + size_t machOFileLen; + + if ( ::lto_codegen_set_pic_model(generator, getCodeModel(options)) ) throwf("could not create set codegen model: %s", lto_get_error_message()); - // if requested, save off merged bitcode file - if ( options.saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, options.outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); + // if requested, save off merged bitcode file + if ( options.saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, options.outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); #if LTO_API_VERSION >= 15 - ::lto_codegen_set_should_embed_uselists(generator, true); + ::lto_codegen_set_should_embed_uselists(generator, true); #endif - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } #if LTO_API_VERSION >= 3 // find assembler next to linker @@ -794,13 +821,11 @@ bool Parser::optimize( const std::vector& allAtoms, useSplitAPI = true; #endif - size_t machOFileLen = 0; - const uint8_t* machOFile = NULL; if ( useSplitAPI) { #if LTO_API_VERSION >= 12 #if LTO_API_VERSION >= 14 - if ( ::lto_api_version() >= 14 && options.ltoCodegenOnly) - lto_codegen_set_should_internalize(generator, false); + if ( ::lto_api_version() >= 14 && options.ltoCodegenOnly) + lto_codegen_set_should_internalize(generator, false); #endif // run optimizer if ( !options.ltoCodegenOnly && ::lto_codegen_optimize(generator) ) @@ -816,7 +841,7 @@ bool Parser::optimize( const std::vector& allAtoms, #endif ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); if ( options.bitcodeBundle ) - state.ltoBitcodePath = tempOptBitcodePath; + state.ltoBitcodePath.push_back(tempOptBitcodePath); } // run code generator @@ -841,13 +866,13 @@ bool Parser::optimize( const std::vector& allAtoms, ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); } } - - // if requested, save off temp mach-o file - if ( options.saveTemps ) { - char tempMachoPath[MAXPATHLEN]; - strcpy(tempMachoPath, options.outputFilePath); - strcat(tempMachoPath, ".lto.o"); - int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + + // if requested, save off temp mach-o file + if ( options.saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, options.outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); if ( fd != -1) { ::write(fd, machOFile, machOFileLen); ::close(fd); @@ -855,20 +880,30 @@ bool Parser::optimize( const std::vector& allAtoms, } // if needed, save temp mach-o file to specific location - if ( options.tmpObjectFilePath != NULL ) { - int fd = ::open(options.tmpObjectFilePath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( !object_path.empty() ) { + int fd = ::open(object_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); if ( fd != -1) { ::write(fd, machOFile, machOFileLen); ::close(fd); } else { - warning("could not write LTO temp file '%s', errno=%d", options.tmpObjectFilePath, errno); + warning("could not write LTO temp file '%s', errno=%d", object_path.c_str(), errno); } } - - // parse generated mach-o file into a MachOReader - ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, options); - + return std::make_tuple(machOFile, machOFileLen); +} + +/// Load the MachO located in buffer \p machOFile with size \p machOFileLen. +/// The loaded atoms are sync'ed using all the supplied lists. +void Parser::loadMachO(ld::relocatable::File* machoFile, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines, + CStringToAtom &llvmAtoms, + CStringToAtom &deadllvmAtoms) { + const bool logAtomsBeforeSync = false; + // sync generated mach-o atoms with existing atoms ld knows about if ( logAtomsBeforeSync ) { fprintf(stderr, "llvmAtoms:\n"); @@ -886,11 +921,96 @@ bool Parser::optimize( const std::vector& allAtoms, } AtomSyncer syncer(additionalUndefines, newAtoms, llvmAtoms, deadllvmAtoms, options); machoFile->forEachAtom(syncer); - - // Remove InternalAtoms from ld - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - (*it)->internalAtom().setCoalescedAway(); + + // notify about file level attributes + handler.doFile(*machoFile); +} + +// Full LTO processing +bool Parser::optimizeLTO(const std::vector files, + const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines) { + const bool logExtraOptions = false; + const bool logBitcodeFiles = false; + + if (files.empty()) + return true; + + // create optimizer and add each Reader + lto_code_gen_t generator = NULL; +#if LTO_API_VERSION >= 11 + if ( File::sSupportsLocalContext ) + generator = ::lto_codegen_create_in_local_context(); + else +#endif + generator = ::lto_codegen_create(); +#if LTO_API_VERSION >= 7 + lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); +#endif + + ld::File::Ordinal lastOrdinal; + + // When flto_codegen_only is on and we have a single .bc file, use lto_codegen_set_module instead of + // lto_codegen_add_module, to make sure the the destination module will be the same as the input .bc file. + bool useSetModule = false; +#if LTO_API_VERSION >= 13 + useSetModule = (files.size() == 1) && options.ltoCodegenOnly && (::lto_api_version() >= 13); +#endif + for (auto *f : files) { + assert(f->ordinal() > lastOrdinal); + if ( logBitcodeFiles && !useSetModule ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( logBitcodeFiles && useSetModule ) fprintf(stderr, "lto_codegen_set_module(%s)\n", f->path()); + if ( f->mergeIntoGenerator(generator, useSetModule) ) + throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); + lastOrdinal = f->ordinal(); + } + + // add any -mllvm command line options + if ( !_s_llvmOptionsProcessed ) { + for (const char* opt : *options.llvmOptions) { + if ( logExtraOptions ) fprintf(stderr, "passing option to llvm: %s\n", opt); + ::lto_codegen_debug_options(generator, opt); + } + _s_llvmOptionsProcessed = true; } + + // Need a way for LTO to get cpu variants (until that info is in bitcode) + if ( options.mcpu != NULL ) + ::lto_codegen_set_cpu(generator, options.mcpu); + + // Compute the preserved symbols + CStringToAtom deadllvmAtoms, llvmAtoms; + setPreservedSymbols(allAtoms, state, options, deadllvmAtoms, llvmAtoms, generator); + + size_t machOFileLen = 0; + const uint8_t* machOFile = NULL; + + // mach-o parsing is done in-memory, but need path for debug notes + std::string object_path; + if ( options.tmpObjectFilePath != NULL ) { + object_path = options.tmpObjectFilePath; + // If the path exists and is a directory (for instance if some files + // were processed with ThinLTO before), we create the LTO file inside + // the directory. + struct stat statBuffer; + if( stat(object_path.c_str(), &statBuffer) == 0 && S_ISDIR(statBuffer.st_mode) ) { + object_path += "/lto.o"; + } + } + + // Codegen Now + std::tie(machOFile, machOFileLen) = codegen(options, state, generator, object_path); + + // parse generated mach-o file into a MachOReader + ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, object_path, options, ld::File::Ordinal::LTOOrdinal()); + + // Load the generated MachO file + loadMachO(machoFile, options, handler, newAtoms, additionalUndefines, llvmAtoms, deadllvmAtoms); + // Remove Atoms from ld if code generator optimized them away for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { // check if setRealAtom() called on this Atom @@ -899,49 +1019,407 @@ bool Parser::optimize( const std::vector& allAtoms, li->second->setCoalescedAway(); } } - - // notify about file level attributes - handler.doFile(*machoFile); - + // if final mach-o file has debug info, update original bitcode files to match - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - (*it)->setDebugInfo(machoFile->debugInfo(), machoFile->path(), - machoFile->modificationTime(), machoFile->cpuSubType()); + for (auto *f : files) { + f->setDebugInfo(machoFile->debugInfo(), machoFile->path(), machoFile->modificationTime(), machoFile->cpuSubType()); } - + return true; } +// Create the ThinLTO codegenerator +thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector& files, + const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + CStringToAtom& deadllvmAtoms, + CStringToAtom& llvmAtoms) { + const bool logMustPreserve = false; + + thinlto_code_gen_t thingenerator = ::thinlto_create_codegen(); + + // Caching control + if (options.ltoCachePath && !options.bitcodeBundle) { + struct stat statBuffer; + if( stat(options.ltoCachePath, &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) { + if ( mkdir(options.ltoCachePath, 0700) !=0 ) { + warning("unable to create ThinLTO cache directory: %s", options.ltoCachePath); + } + } + thinlto_codegen_set_cache_dir(thingenerator, options.ltoCachePath); + thinlto_codegen_set_cache_pruning_interval(thingenerator, options.ltoPruneInterval); + thinlto_codegen_set_cache_entry_expiration(thingenerator, options.ltoPruneAfter); + thinlto_codegen_set_final_cache_size_relative_to_available_space(thingenerator, options.ltoMaxCacheSize); + } + + // if requested, ask the code generator to save off intermediate bitcode files + if ( options.saveTemps ) { + std::string tempPath = options.outputFilePath; + tempPath += ".thinlto.bcs/"; + struct stat statBuffer; + if( stat(tempPath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) { + if ( mkdir(tempPath.c_str(), 0700) !=0 ) { + warning("unable to create ThinLTO output directory for temporary bitcode files: %s", tempPath.c_str()); + } + } + thinlto_codegen_set_savetemps_dir(thingenerator, tempPath.c_str()); + } + + // Set some codegen options + if ( thinlto_codegen_set_pic_model(thingenerator, getCodeModel(options)) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // Expose reachability informations for internalization in LTO + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringSet LLVMRefs; + for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + const ld::Atom* atom = *it; + const ld::Atom* target; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + // that reference a ThinLTO llvm atom + target = fit->u.target; + if ( target->contentType() == ld::Atom::typeLTOtemporary && + ((lto::File *)target->file())->isThinLTO() && + atom->file() != target->file() + ) { + if (atom->contentType() != ld::Atom::typeLTOtemporary || + !((lto::File *)atom->file())->isThinLTO()) + nonLLVMRefs.insert(target->name()); + else + LLVMRefs.insert(target->name()); + if ( logMustPreserve ) + fprintf(stderr, "Found a reference from %s -> %s\n", atom->name(), target->name()); + } + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + if ( (target != NULL) && (target->contentType() == ld::Atom::typeLTOtemporary) && + ((lto::File *)target->file())->isThinLTO() && + atom->file() != target->file() + ) { + if (atom->contentType() != ld::Atom::typeLTOtemporary || + !((lto::File *)atom->file())->isThinLTO()) + nonLLVMRefs.insert(target->name()); + else + LLVMRefs.insert(target->name()); + if ( logMustPreserve ) + fprintf(stderr, "Found a reference from %s -> %s\n", atom->name(), target->name()); + } + default: + break; + } + } + if (atom->contentType() == ld::Atom::typeLTOtemporary && + ((lto::File *)atom->file())->isThinLTO()) { + llvmAtoms[atom->name()] = (Atom*)atom; + } + } + // if entry point is in a llvm bitcode file, it must be preserved by LTO + if ( state.entryPoint != NULL ) { + if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(state.entryPoint->name()); + } + for (auto file : files) { + for(uint32_t i=0; i < file->_atomArrayCount; ++i) { + Atom* llvmAtom = &file->_atomArray[i]; + if ( llvmAtom->coalescedAway() ) { + const char* name = llvmAtom->name(); + if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) { + if ( logMustPreserve ) + fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name); + ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name)); + deadllvmAtoms[name] = (Atom*)llvmAtom; + } + } + else if ( options.linkerDeadStripping && !llvmAtom->live() ) { + const char* name = llvmAtom->name(); + deadllvmAtoms[name] = (Atom*)llvmAtom; + } + } + } + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name); + ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name)); + } + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced from outside of ThinLTO\n", name); + ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name)); + } + else if ( LLVMRefs.find(name) != LLVMRefs.end() ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced from another file\n", name); + ::thinlto_codegen_add_cross_referenced_symbol(thingenerator, name, strlen(name)); + } else { + if ( logMustPreserve ) fprintf(stderr, "NOT preserving(%s)\n", name); + } +// FIXME: to be implemented +// else if ( options.relocatable && hasNonllvmAtoms ) { +// // ld -r mode but merging in some mach-o files, so need to keep libLTO from optimizing away anything +// if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because -r mode disable LTO dead stripping\n", name); +// ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name)); +// } + } + + return thingenerator; +} + +// Full LTO processing +bool Parser::optimizeThinLTO(const std::vector& files, + const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines) { + const bool logBitcodeFiles = false; + + if (files.empty()) + return true; + +#if LTO_API_VERSION >= 18 + + if (::lto_api_version() < 18) + throwf("lto: could not use -thinlto because libLTO is too old (version '%d', >=18 is required)", ::lto_api_version()); + + // Handle -mllvm options + if ( !_s_llvmOptionsProcessed ) { + thinlto_debug_options(options.llvmOptions->data(), options.llvmOptions->size()); + _s_llvmOptionsProcessed = true; + } + + // Create the ThinLTO codegenerator + CStringToAtom deadllvmAtoms; + CStringToAtom llvmAtoms; + thinlto_code_gen_t thingenerator = init_thinlto_codegen(files, allAtoms, state, options, deadllvmAtoms, llvmAtoms); + + + ld::File::Ordinal lastOrdinal; + for (auto *f : files) { + if ( logBitcodeFiles) fprintf(stderr, "thinlto_codegen_add_module(%s)\n", f->path()); + f->addToThinGenerator(thingenerator); + lastOrdinal = f->ordinal(); + } + +#if LTO_API_VERSION >= 19 + // In the bitcode bundle case, we first run the generator with codegen disabled + // and get the bitcode output. These files are added for later bundling, and a + // new codegenerator is setup with these as input, and the optimizer disabled. + if (options.bitcodeBundle) { + // Bitcode Bundle case + thinlto_codegen_disable_codegen(thingenerator, true); + // Process the optimizer only + thinlto_codegen_process(thingenerator); + auto numObjects = thinlto_module_get_num_objects(thingenerator); + // Save the codegenerator + thinlto_code_gen_t bitcode_generator = thingenerator; + // Create a new codegen generator for the codegen part. + thingenerator = init_thinlto_codegen(files, allAtoms, state, options, deadllvmAtoms, llvmAtoms); + // Disable the optimizer + thinlto_codegen_set_codegen_only(thingenerator, true); + + // Save bitcode files for later, and add them to the codegen generator. + for (unsigned bufID = 0; bufID < numObjects; ++bufID) { + auto machOFile = thinlto_module_get_object(bitcode_generator, bufID); + std::string tempMachoPath = options.outputFilePath; + tempMachoPath += "."; + tempMachoPath += std::to_string(bufID); + tempMachoPath += ".thinlto.o.bc"; + state.ltoBitcodePath.push_back(tempMachoPath); + int fd = ::open(tempMachoPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1 ) { + ::write(fd, machOFile.Buffer, machOFile.Size); + ::close(fd); + } else { + throwf("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str()); + } + + // Add the optimized bitcode to the codegen generator now. + ::thinlto_codegen_add_module(thingenerator, tempMachoPath.c_str(), (const char *)machOFile.Buffer, machOFile.Size); + } + } + + if (options.ltoCodegenOnly) + // Disable the optimizer + thinlto_codegen_set_codegen_only(thingenerator, true); +#endif + + // run code generator + thinlto_codegen_process(thingenerator); + auto numObjects = thinlto_module_get_num_objects(thingenerator); + if (!numObjects) + throwf("could not do ThinLTO codegen (thinlto_codegen_process didn't produce any object): '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + + // if requested, save off objects files + if ( options.saveTemps ) { + for (unsigned bufID = 0; bufID < numObjects; ++bufID) { + auto machOFile = thinlto_module_get_object(thingenerator, bufID); + std::string tempMachoPath = options.outputFilePath; + tempMachoPath += "."; + tempMachoPath += std::to_string(bufID); + tempMachoPath += ".thinlto.o"; + int fd = ::open(tempMachoPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1 ) { + ::write(fd, machOFile.Buffer, machOFile.Size); + ::close(fd); + } else { + warning("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str()); + } + } + } + + + // mach-o parsing is done in-memory, but need path for debug notes + std::string macho_dirpath = "/tmp/thinlto.o"; + if ( options.tmpObjectFilePath != NULL ) { + macho_dirpath = options.tmpObjectFilePath; + struct stat statBuffer; + if( stat(macho_dirpath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) { + if ( mkdir(macho_dirpath.c_str(), 0700) !=0 ) { + warning("unable to create ThinLTO output directory for temporary object files: %s", macho_dirpath.c_str()); + } + } + } + + auto ordinal = ld::File::Ordinal::LTOOrdinal().nextFileListOrdinal(); + for (unsigned bufID = 0; bufID < numObjects; ++bufID) { + auto machOFile = thinlto_module_get_object(thingenerator, bufID); + if (!machOFile.Size) { + warning("Ignoring empty buffer generated by ThinLTO"); + continue; + } + + // mach-o parsing is done in-memory, but need path for debug notes + std::string tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o"; + + // parse generated mach-o file into a MachOReader + ld::relocatable::File* machoFile = parseMachOFile((const uint8_t *)machOFile.Buffer, machOFile.Size, tmp_path, options, ordinal); + ordinal = ordinal.nextFileListOrdinal(); + + // if needed, save temp mach-o file to specific location + if ( options.tmpObjectFilePath != NULL ) { + int fd = ::open(tmp_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, (const uint8_t *)machOFile.Buffer, machOFile.Size); + ::close(fd); + } + else { + warning("could not write ThinLTO temp file '%s', errno=%d", tmp_path.c_str(), errno); + } + } + + // Load the generated MachO file + loadMachO(machoFile, options, handler, newAtoms, additionalUndefines, llvmAtoms, deadllvmAtoms); + } + + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->compiledAtom() == NULL ) { + //fprintf(stderr, "llvm optimized away %p %s\n", li->second, li->second->name()); + li->second->setCoalescedAway(); + } + } + + return true; +#else // ! (LTO_API_VERSION >= 18) + throwf("lto: could not use -thinlto because ld was built against a version of libLTO too old (version '%d', >=18 is required)", LTO_API_VERSION); +#endif +} + +bool Parser::optimize( const std::vector& allAtoms, + ld::Internal& state, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector& newAtoms, + std::vector& additionalUndefines) +{ + + // exit quickly if nothing to do + if ( _s_files.size() == 0 ) + return false; + + // print out LTO version string if -v was used + if ( options.verbose ) + fprintf(stderr, "%s\n", ::lto_get_version()); + + // The order that files are merged must match command line order + std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); + +#if LTO_API_VERSION >= 19 + // If ltoCodegenOnly is set, we don't want to merge any bitcode files and perform FullLTO + // we just take the ThinLTO path (optimization will be disabled anyway). + if (options.ltoCodegenOnly) { + for (auto *file : _s_files) { + file->setIsThinLTO(true); + } + } +#endif + + std::vector theLTOFiles; + std::vector theThinLTOFiles; + for (auto *file : _s_files) { + if (file->isThinLTO()) { + theThinLTOFiles.push_back(file); + } else { + theLTOFiles.push_back(file); + } + } + + auto result = optimizeThinLTO(theThinLTOFiles, allAtoms, state, options, handler, newAtoms, additionalUndefines) && + optimizeLTO(theLTOFiles, allAtoms, state, options, handler, newAtoms, additionalUndefines); + + // Remove InternalAtoms from ld + for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + (*it)->internalAtom().setCoalescedAway(); + } + + return result; +} + void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) { static const bool log = false; - static const ld::Atom* lastProxiedAtom = NULL; - static const ld::File* lastProxiedFile = NULL; // update proxy atoms to point to real atoms and find new atoms const char* name = machoAtom.name(); - CStringToAtom::iterator pos = _llvmAtoms.find(name); + CStringToAtom::const_iterator pos = _llvmAtoms.find(name); if ( pos != _llvmAtoms.end() ) { // turn Atom into a proxy for this mach-o atom pos->second->setCompiledAtom(machoAtom); - lastProxiedAtom = &machoAtom; - lastProxiedFile = pos->second->file(); + _lastProxiedAtom = &machoAtom; + _lastProxiedFile = pos->second->file(); if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p synced to lto atom %p (name=%s)\n", &machoAtom, pos->second, machoAtom.name()); } else { // an atom of this name was not in the allAtoms list the linker gave us - if ( _deadllvmAtoms.find(name) != _deadllvmAtoms.end() ) { + auto llvmAtom = _deadllvmAtoms.find(name); + if ( llvmAtom != _deadllvmAtoms.end() ) { // this corresponding to an atom that the linker coalesced away or marked not-live if ( _options.linkerDeadStripping ) { // llvm seems to want this atom and -dead_strip is enabled, so it will be deleted if not needed, so add back - Atom* llvmAtom = _deadllvmAtoms[name]; - llvmAtom->setCompiledAtom(machoAtom); + llvmAtom->second->setCompiledAtom(machoAtom); _newAtoms.push_back(&machoAtom); - if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p but adding back (name=%s)\n", &machoAtom, llvmAtom, machoAtom.name()); + if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p but adding back (name=%s)\n", &machoAtom, llvmAtom->second, machoAtom.name()); } else { // Don't pass it back as a new atom - if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, _deadllvmAtoms[name], machoAtom.name()); + if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, llvmAtom->second, machoAtom.name()); } } else @@ -949,9 +1427,10 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) // this is something new that lto conjured up, tell ld its new _newAtoms.push_back(&machoAtom); // if new static atom in same section as previous non-static atom, assign to same file as previous - if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) { + if ( (_lastProxiedAtom != NULL) && (_lastProxiedAtom->section() == machoAtom.section()) ) { ld::Atom* ma = const_cast(&machoAtom); - ma->setFile(lastProxiedFile); + ma->setFile(_lastProxiedFile); + if (log) fprintf(stderr, "AtomSyncer, mach-o atom %s is proxied to %s (path=%s)\n", machoAtom.name(), _lastProxiedAtom->name(), _lastProxiedFile->path()); } if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p is totally new (name=%s)\n", &machoAtom, machoAtom.name()); } @@ -975,7 +1454,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) // llvm symbol references always go through Atom proxy. { const char* targetName = fit->u.target->name(); - CStringToAtom::iterator post = _llvmAtoms.find(targetName); + CStringToAtom::const_iterator post = _llvmAtoms.find(targetName); if ( post != _llvmAtoms.end() ) { const ld::Atom* t = post->second; if (log) fprintf(stderr, " updating direct reference to %p to be ref to %p: %s\n", fit->u.target, t, targetName); @@ -1023,6 +1502,21 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar return Parser::validFile(fileContent, fileLength, architecture, subarch); } +// +// Used by archive reader to see if member defines a Category (for -ObjC semantics) +// +bool hasObjCCategory(const uint8_t* fileContent, uint64_t fileLength) +{ +#if LTO_API_VERSION >= 20 + // note: if run with older libLTO.dylib that does not implement + // lto_module_has_objc_category, the call will return 0 which is "false" + return lto_module_has_objc_category(fileContent, fileLength); +#else + return false; +#endif +} + + static ld::relocatable::File *parseImpl( const uint8_t *fileContent, uint64_t fileLength, const char *path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index 38cfcd1..b760177 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -37,6 +37,8 @@ extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); +extern bool hasObjCCategory(const uint8_t* fileContent, uint64_t fileLength); + extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, @@ -45,6 +47,10 @@ extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLen struct OptimizeOptions { const char* outputFilePath; const char* tmpObjectFilePath; + const char* ltoCachePath; + int ltoPruneInterval; + int ltoPruneAfter; + unsigned ltoMaxCacheSize; bool preserveAllGlobals; bool verbose; bool saveTemps; @@ -66,6 +72,7 @@ struct OptimizeOptions { cpu_type_t arch; const char* mcpu; Options::Platform platform; + uint32_t minOSVersion; const std::vector* llvmOptions; const std::vector* initialUndefines; }; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 24d69c8..56b327a 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -61,8 +61,8 @@ class File final : public generic::dylib::File File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, - bool addVers, bool buildingForSimulator, + Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode); virtual ~File() noexcept {} @@ -70,6 +70,7 @@ class File final : public generic::dylib::File private: using P = typename A::P; using E = typename A::P::E; + using pint_t = typename A::P::uint_t; void addDyldFastStub(); void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, @@ -77,6 +78,7 @@ class File final : public generic::dylib::File void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, const macho_nlist

* symbolTable, const char* strings, const uint8_t* fileContent); + void addSymbol(const char* name, bool weakDef = false, bool tlv = false, pint_t address = 0); static const char* objCInfoSegmentName(); static const char* objCInfoSectionName(); @@ -97,11 +99,11 @@ template const char* File::objCInfoSectionName() { return "__ima template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, - bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, + bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode) - : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace, - hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0) + : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace, + hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -185,6 +187,9 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, // Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked this->_hasPublicInstallName = false; break; + case LC_RPATH: + this->_rpaths.push_back(strdup(((macho_rpath_command

*)cmd)->path())); + break; case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: case LC_VERSION_MIN_WATCHOS: @@ -521,6 +526,67 @@ void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

+void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) +{ + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != nullptr ) { + char curOSVers[16]; + sprintf(curOSVers, "$os%d.%d$", (this->_linkMinOSVersion >> 16), ((this->_linkMinOSVersion >> 8) & 0xFF)); + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != nullptr ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( this->_s_logHashtable ) + fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + this->_ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef); + return; + } + else if ( strncmp(symAction, "weak$", 5) == 0 ) { + if ( !this->_allowWeakImports ) + this->_ignoreExports.insert(strdup(symName)); + } + else if ( strncmp(symAction, "install_name$", 13) == 0 ) { + this->_dylibInstallPath = symName; + this->_installPathOverride = true; + // CoreGraphics redirects to ApplicationServices, but with wrong compat version + if ( strcmp(this->_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) + this->_dylibCompatibilityVersion = Options::parseVersionNumber32("1.0"); + return; + } + else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { + this->_dylibCompatibilityVersion = Options::parseVersionNumber32(symName); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( this->_ignoreExports.count(name) == 0 ) { + typename Base::AtomAndWeak bucket = { nullptr, weakDef, tlv, address }; + if ( this->_s_logHashtable ) + fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + this->_atoms[strdup(name)] = bucket; + } +} template <> void File::addDyldFastStub() @@ -554,7 +620,7 @@ class Parser { return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), - opts.platform(), opts.minOSversion(), + opts.platform(), opts.minOSversion(), opts.allowWeakImports(), opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), opts.targetIOSSimulator(), opts.logAllFiles(), opts.installPath(), indirectDylib, opts.outputKind() == Options::kPreload, opts.bundleBitcode()); @@ -755,6 +821,7 @@ const char* Parser::fileKind(const uint8_t* fileContent) } #endif + // // used by linker is error messages to describe mismatched files // diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index ddf687a..776e695 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -86,7 +86,8 @@ class File : public ld::relocatable::File _minOSVersion(0), _platform(0), _canScatterAtoms(false), - _srcKind(kSourceUnknown) {} + _objcHasCategoryClassPropertiesField(false), + _srcKind(kSourceUnknown) { } virtual ~File(); // overrides of ld::File @@ -98,6 +99,8 @@ class File : public ld::relocatable::File // overrides of ld::relocatable::File virtual ObjcConstraint objCConstraint() const { return _objConstraint; } + virtual bool objcHasCategoryClassPropertiesField() const + { return _objcHasCategoryClassPropertiesField; } virtual uint32_t cpuSubType() const { return _cpuSubType; } virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } virtual const std::vector* stabs() const { return &_stabs; } @@ -140,6 +143,7 @@ class File : public ld::relocatable::File uint32_t _minOSVersion; uint32_t _platform; bool _canScatterAtoms; + bool _objcHasCategoryClassPropertiesField; std::vector > _linkerOptions; std::unique_ptr _bitcode; SourceKind _srcKind; @@ -385,6 +389,10 @@ class TLVDefsSection : public SymboledSection TLVDefsSection(Parser& parser, File& f, const macho_section* s) : SymboledSection(parser, f, s) { } + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + private: }; @@ -530,6 +538,7 @@ class TLVPointerSection : public FixedSizeSection typedef typename A::P::uint_t pint_t; typedef typename A::P P; + virtual void makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeTLVPointer; } virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } virtual const char* unlabeledAtomName(Parser&, pint_t) { return "tlv_lazy_ptr"; } @@ -930,6 +939,7 @@ void Atom::verifyAlignment(const macho_section

& sect) const } #endif + template void Atom::verifyAlignment(const macho_section

&) const { @@ -1406,6 +1416,7 @@ const char* Parser::fileKind(const uint8_t* fileContent) } #endif + template bool Parser::hasObjC2Categories(const uint8_t* fileContent) { @@ -2525,6 +2536,7 @@ void Parser::makeSections() // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 // #define OBJC_IMAGE_IS_SIMULATED 32 + // #define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES 64 // const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset()); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -2538,6 +2550,7 @@ void Parser::makeSections() else _file->_objConstraint = ld::File::objcConstraintRetainRelease; _file->_swiftVersion = ((flags >> 8) & 0xFF); + _file->_objcHasCategoryClassPropertiesField = (flags & 64); if ( sect->size() > 8 ) { warning("section %s/%s has unexpectedly large size %llu in %s", sect->segname(), Section::makeSectionName(sect), sect->size(), _file->path()); @@ -3167,10 +3180,10 @@ uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uin alignP2 = 63 - (uint8_t)__builtin_clzll(size); if ( size != (1ULL << alignP2) ) ++alignP2; + // limit default alignment of large commons + if ( alignP2 > parser.maxDefaultCommonAlignment() ) + alignP2 = parser.maxDefaultCommonAlignment(); } - // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignP2 > parser.maxDefaultCommonAlignment() ) - alignP2 = parser.maxDefaultCommonAlignment(); Atom* allocatedSpace = (Atom*)p; new (allocatedSpace) Atom(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size, ld::Atom::definitionTentative, ld::Atom::combineByName, @@ -3270,10 +3283,11 @@ uint32_t Parser::symbolIndexFromIndirectSectionAddress(pint_t addr, const mac break; case S_LAZY_SYMBOL_POINTERS: case S_NON_LAZY_SYMBOL_POINTERS: + case S_THREAD_LOCAL_VARIABLE_POINTERS: elementSize = sizeof(pint_t); break; default: - throw "section does not use inirect symbol table"; + throw "section does not use indirect symbol table"; } uint32_t indexInSection = (addr - sect->addr()) / elementSize; uint32_t indexIntoIndirectTable = sect->reserved1() + indexInSection; @@ -4387,6 +4401,7 @@ bool CFISection::needsRelocating() return true; } + template bool CFISection::needsRelocating() { @@ -4611,7 +4626,6 @@ template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } - template <> void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { @@ -4683,6 +4697,7 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, con } #endif + template <> void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { @@ -5008,6 +5023,7 @@ const char* CUSection::personalityName(class Parser& parser, const } #endif + #if SUPPORT_ARCH_arm_any template <> const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) @@ -5072,6 +5088,7 @@ bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) } #endif + template int CUSection::infoSorter(const void* l, const void* r) { @@ -5313,6 +5330,7 @@ ld::Atom::SymbolTableInclusion ImplicitSizeSection::symbolTableInclusion( return ld::Atom::symbolTableInWithRandomAutoStripLabel; } + template ld::Atom::SymbolTableInclusion ImplicitSizeSection::symbolTableInclusion() { @@ -5753,6 +5771,47 @@ ld::Atom::Combine TLVPointerSection::combine(Parser& parser, pint_t addr) return ld::Atom::combineByNameAndReferences; } +template <> +void TLVPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) +{ + // add references for each thread local pointer atom based on indirect symbol table + const macho_section

* sect = this->machoSection(); + const pint_t endAddr = sect->addr() + sect->size(); + for (pint_t addr = sect->addr(); addr < endAddr; addr += sizeof(pint_t)) { + typename Parser::SourceLocation src; + typename Parser::TargetDesc target; + src.atom = this->findAtomByAddress(addr); + src.offsetInAtom = 0; + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + target.atom = NULL; + target.name = NULL; + target.weakImport = false; + target.addend = 0; + if ( symIndex == INDIRECT_SYMBOL_LOCAL ) { + throwf("unexpected INDIRECT_SYMBOL_LOCAL in section %s", this->sectionName()); + } + else { + const macho_nlist

& sym = parser.symbolFromIndex(symIndex); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) { + throwf("unexpected pointer to local symbol in section %s", this->sectionName()); + } + else { + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + assert(src.atom->combine() == ld::Atom::combineByNameAndReferences); + } + } + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + } +} + +template +void TLVPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) +{ + assert(0 && "should not have thread-local-pointer sections in .o files"); +} + template const char* TLVPointerSection::targetName(const class Atom* atom, const ld::IndirectBindingTable& ind, bool* isStatic) @@ -7406,6 +7465,7 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relo } #endif + template bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) { @@ -7557,6 +7617,7 @@ void Section::addLOH(class Parser& parser, int kind, int count, co } #endif + template void Section::addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]) { diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index fb452d8..5854462 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -25,7 +25,7 @@ #include #include - +#include #include #include "Architectures.hpp" @@ -35,469 +35,6 @@ #include "generic_dylib_file.hpp" #include "textstub_dylib_file.hpp" -namespace { - -/// -/// A token is a light-weight reference to the content of an nmap'ed file. It -/// doesn't own the data and it doesn't make a copy of it. The referenced data -/// is only valid as long as the file is mapped in. -/// -class Token { - const char* _p; - size_t _size; - - int compareMemory(const char* lhs, const char* rhs, size_t size) const { - if (size == 0) - return 0; - return ::memcmp(lhs, rhs, size); - } - -public: - Token() : _p(nullptr), _size(0) {} - - Token(const char* p) : _p(p), _size(0) { - if (p) - _size = ::strlen(p); - } - - Token(const char* p, size_t s) : _p(p), _size(s) {} - - const char* data() const { return _p; } - - size_t size() const { return _size; } - - std::string str() const { return std::string(_p, _size); } - - bool empty() const { return _size == 0; } - - bool operator==(Token other) const { - if (_size != other._size) - return false; - return compareMemory(_p, other._p, _size) == 0; - } - - bool operator!=(Token other) const { - return !(*this == other); - } -}; - -/// -/// Simple text-based dynamic library file tokenizer. -/// -class Tokenizer { - const char* _start; - const char* _current; - const char* _end; - Token _currentToken; - - void fetchNextToken(); - void scanToNextToken(); - void skip(unsigned distance) { - _current += distance; - assert(_current <= _end && "Skipped past the end"); - } - - const char* skipLineBreak(const char* pos) const; - bool isDelimiter(const char* pos) const; - -public: - Tokenizer(const char* data, uint64_t size) : _start(data), _current(data), _end(data + size) {} - - void reset() { - _current = _start; - fetchNextToken(); - } - - Token peek() { return _currentToken; } - Token next() { - Token token = peek(); - fetchNextToken(); - return token; - } -}; - -const char* Tokenizer::skipLineBreak(const char* pos) const -{ - if ( pos == _end ) - return pos; - - // Carriage return. - if ( *pos == 0x0D ) { - // line feed. - if ( pos + 1 != _end && *(pos + 1) == 0x0A) - return pos + 2; - return pos + 1; - } - - // line feed. - if ( *pos == 0x0A ) - return pos + 1; - - return pos; -} - -void Tokenizer::scanToNextToken() { - while (true) { - while ( isDelimiter(_current) ) - skip(1); - - const char* i = skipLineBreak(_current); - if ( i == _current ) - break; - - _current = i; - } -} - - -bool Tokenizer::isDelimiter(const char* pos) const { - if ( pos == _end ) - return false; - if ( *pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n' || *pos == ',' || *pos == ':' || *pos == '\'' || *pos == '\"' ) - return true; - return false; -} - -void Tokenizer::fetchNextToken() { - scanToNextToken(); - - if (_current == _end) { - _currentToken = Token(); - return; - } - - auto start = _current; - while ( !isDelimiter(_current) ) { - ++_current; - } - - _currentToken = Token(start, _current - start); -} - -/// -/// Representation of a parsed text-based dynamic library file. -/// -struct DynamicLibrary { - Token _installName; - uint32_t _currentVersion; - uint32_t _compatibilityVersion; - uint8_t _swiftVersion; - ld::File::ObjcConstraint _objcConstraint; - Options::Platform _platform; - std::vector _allowedClients; - std::vector _reexportedLibraries; - std::vector _symbols; - std::vector _classes; - std::vector _ivars; - std::vector _weakDefSymbols; - std::vector _tlvSymbols; - - DynamicLibrary() : _currentVersion(0x10000), _compatibilityVersion(0x10000), _swiftVersion(0), - _objcConstraint(ld::File::objcConstraintNone) {} -}; - -/// -/// A simple text-based dynamic library file parser. -/// -class TBDFile { - Tokenizer _tokenizer; - - Token peek() { return _tokenizer.peek(); } - Token next() { return _tokenizer.next(); } - - void expectToken(Token str) { - Token token = next(); - if (token != str) - throwf("unexpected token: %s", token.str().c_str()); - } - - bool hasOptionalToken(Token str) { - auto token = peek(); - if ( token == str ) { - next(); - return true; - } - return false; - } - - - void parseFlowSequence(std::function func) { - expectToken("["); - - while ( true ) { - auto token = peek(); - if ( token == "]" ) - break; - - token = next(); - func(token); - } - - expectToken("]"); - } - - void parseAllowedClients(DynamicLibrary& lib) { - if ( !hasOptionalToken("allowed-clients") ) - return; - parseFlowSequence([&](Token name) { - lib._allowedClients.emplace_back(name); - }); - } - - void parseReexportedDylibs(DynamicLibrary& lib) { - if ( !hasOptionalToken("re-exports") ) - return; - parseFlowSequence([&](Token name) { - lib._reexportedLibraries.emplace_back(name); - }); - } - - void parseSymbols(DynamicLibrary& lib) { - if ( hasOptionalToken("symbols") ) { - parseFlowSequence([&](Token name) { - lib._symbols.emplace_back(name); - }); - } - - if ( hasOptionalToken("objc-classes") ) { - parseFlowSequence([&](Token name) { - lib._classes.emplace_back(name); - }); - } - - if ( hasOptionalToken("objc-ivars") ) { - parseFlowSequence([&](Token name) { - lib._ivars.emplace_back(name); - }); - } - - if ( hasOptionalToken("weak-def-symbols") ) { - parseFlowSequence([&](Token name) { - lib._weakDefSymbols.emplace_back(name); - }); - } - - if ( hasOptionalToken("thread-local-symbols") ) { - parseFlowSequence([&](Token name) { - lib._tlvSymbols.emplace_back(name); - }); - } - } - - std::vector parseArchFlowSequence() { - std::vector availabledArchitectures; - expectToken("archs"); - parseFlowSequence([&](Token name) { - availabledArchitectures.emplace_back(name.str()); - }); - return availabledArchitectures; - } - - bool parseArchFlowSequence(std::string &selectedArchName) { - auto availabledArchitectures = parseArchFlowSequence(); - - for (const auto &archName : availabledArchitectures) { - if (archName == selectedArchName) - return true; - } - - return false; - } - - void parsePlatform(DynamicLibrary& lib) { - expectToken("platform"); - - auto token = next(); - if (token == "macosx") - lib._platform = Options::kPlatformOSX; - else if (token == "ios") - lib._platform = Options::kPlatformiOS; - else if (token == "watchos") - lib._platform = Options::kPlatformWatchOS; -#if SUPPORT_APPLE_TV - else if (token == "tvos") - lib._platform = Options::kPlatform_tvOS; -#endif - else - lib._platform = Options::kPlatformUnknown; - } - - void parseInstallName(DynamicLibrary& lib) { - expectToken("install-name"); - - lib._installName = next(); - if ( lib._installName.empty() ) - throwf("no install name specified"); - } - - uint32_t parseVersionNumber32(Token token) { - if ( token.size() >= 128 ) - throwf("malformed version number"); - - // Make a null-terminated string. - char buffer[128]; - ::memcpy(buffer, token.data(), token.size()); - buffer[token.size()] = '\0'; - - return Options::parseVersionNumber32(buffer); - } - - void parseCurrentVersion(DynamicLibrary& lib) { - if ( !hasOptionalToken("current-version") ) - return; - lib._currentVersion = parseVersionNumber32(next()); - } - - void parseCompatibilityVersion(DynamicLibrary& lib) { - if ( !hasOptionalToken("compatibility-version") ) - return; - lib._compatibilityVersion = parseVersionNumber32(next()); - } - - void parseSwiftVersion(DynamicLibrary& lib) { - if ( !hasOptionalToken("swift-version") ) - return; - auto token = next(); - if ( token == "1.0" ) - lib._swiftVersion = 1; - else if ( token == "1.1" ) - lib._swiftVersion = 2; - else if ( token == "2.0" ) - lib._swiftVersion = 3; - else - throwf("unsupported Swift ABI version: %s", token.str().c_str()); - } - - void parseObjCConstraint(DynamicLibrary& lib) { - if ( !hasOptionalToken("objc-constraint") ) - return; - auto token = next(); - if ( token == "none" ) - lib._objcConstraint = ld::File::objcConstraintNone; - else if ( token == "retain_release" ) - lib._objcConstraint = ld::File::objcConstraintRetainRelease; - else if ( token == "retain_release_for_simulator" ) - lib._objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator; - else if ( token == "retain_release_or_gc" ) - lib._objcConstraint = ld::File::objcConstraintRetainReleaseOrGC; - else if ( token == "gc" ) - lib._objcConstraint = ld::File::objcConstraintGC; - else - throwf("unexpected token: %s", token.str().c_str()); - } - void parseExportsBlock(DynamicLibrary& lib, std::string &selectedArchName) { - if ( !hasOptionalToken("exports") ) - return; - - if ( !hasOptionalToken("-") ) - return; - - while ( true ) { - if ( !parseArchFlowSequence(selectedArchName) ) { - Token token; - while ( true ) { - token = peek(); - if ( token == "archs" || token == "..." || token.empty() ) - break; - next(); - } - if (token == "..." || token.empty() ) - break; - - continue; - } - - parseAllowedClients(lib); - parseReexportedDylibs(lib); - parseSymbols(lib); - if ( !hasOptionalToken("-") ) - break; - } - } - - std::vector getCompatibleArchList(std::string &requestedArchName) { - if (requestedArchName == "i386") - return {"i386"}; - else if (requestedArchName == "x86_64" || requestedArchName == "x86_64h") - return {"x86_64", "x86_64h"}; - else if (requestedArchName == "armv7" || requestedArchName == "armv7s") - return {"armv7", "armv7s"}; - else if (requestedArchName == "armv7k") - return {"armv7k"}; - else if (requestedArchName == "arm64") - return {"arm64"}; - else - return {}; - } - - std::string parseAndSelectArchitecture(std::string &requestedArchName) { - auto availabledArchitectures = parseArchFlowSequence(); - - // First try to find an exact match (cpu type and sub-cpu type). - if (std::find(availabledArchitectures.begin(), availabledArchitectures.end(), requestedArchName) - != availabledArchitectures.end()) - return requestedArchName; - - // If there is no exact match, then try to find an ABI compatible slice. - auto compatibleArchitectures = getCompatibleArchList(requestedArchName); - std::vector result; - std::sort(availabledArchitectures.begin(), availabledArchitectures.end()); - std::sort(compatibleArchitectures.begin(), compatibleArchitectures.end()); - std::set_intersection(availabledArchitectures.begin(), availabledArchitectures.end(), - compatibleArchitectures.begin(), compatibleArchitectures.end(), - std::back_inserter(result)); - - if (result.empty()) - return std::string(); - else - return result.front(); - } - - void parseDocument(DynamicLibrary& lib, std::string &requestedArchName) { - auto selectedArchName = parseAndSelectArchitecture(requestedArchName); - if (selectedArchName.empty()) - throwf("invalid arch"); - - parsePlatform(lib); - parseInstallName(lib); - parseCurrentVersion(lib); - parseCompatibilityVersion(lib); - parseSwiftVersion(lib); - parseObjCConstraint(lib); - parseExportsBlock(lib, selectedArchName); - } - -public: - TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {} - - DynamicLibrary parseFileForArch(std::string requestedArchName) { - _tokenizer.reset(); - DynamicLibrary lib; - expectToken("---"); - parseDocument(lib, requestedArchName); - expectToken("..."); - return lib; - } - - bool validForArch(std::string requestedArchName) { - _tokenizer.reset(); - auto token = next(); - if ( token != "---" ) - return false; - return !parseAndSelectArchitecture(requestedArchName).empty(); - } - - void dumpTokens() { - _tokenizer.reset(); - Token token; - do { - token = next(); - printf("token: %s\n", token.str().c_str()); - } while ( !token.empty() ); - } -}; - -} // end anonymous namespace namespace textstub { namespace dylib { @@ -513,51 +50,94 @@ class File final : public generic::dylib::File using Base = generic::dylib::File; public: - static bool validFile(const uint8_t* fileContent, bool executableOrDylib); - File(const uint8_t* fileContent, uint64_t fileLength, const char* path, + File(const char* path, const uint8_t* fileContent, uint64_t fileLength, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, - bool hoistImplicitPublicDylibs, Options::Platform platform, - cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, + cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, bool indirectDylib); virtual ~File() noexcept {} private: - void buildExportHashTable(const DynamicLibrary &lib); - - cpu_type_t _cpuType; + void buildExportHashTable(const tapi::LinkerInterfaceFile* file); }; +static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) { + switch (constraint) { + case tapi::ObjCConstraint::None: + return ld::File::objcConstraintNone; + case tapi::ObjCConstraint::Retain_Release: + return ld::File::objcConstraintRetainRelease; + case tapi::ObjCConstraint::Retain_Release_For_Simulator: + return ld::File::objcConstraintRetainReleaseForSimulator; + case tapi::ObjCConstraint::Retain_Release_Or_GC: + return ld::File::objcConstraintRetainReleaseOrGC; + case tapi::ObjCConstraint::GC: + return ld::File::objcConstraintGC; + } + + return ld::File::objcConstraintNone; +} + +static Options::Platform mapPlatform(tapi::Platform platform) { + switch (platform) { + case tapi::Platform::Unknown: + return Options::kPlatformUnknown; + case tapi::Platform::OSX: + return Options::kPlatformOSX; + case tapi::Platform::iOS: + return Options::kPlatformiOS; + case tapi::Platform::watchOS: + return Options::kPlatformWatchOS; + case tapi::Platform::tvOS: + return Options::kPlatform_tvOS; + } + + return Options::kPlatformUnknown; +} + template -File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, - ld::File::Ordinal ord, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, - Options::Platform platform, cpu_type_t cpuType, const char* archName, - uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, + File::File(const char* path, const uint8_t* fileContent, uint64_t fileLength, + time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform, + uint32_t linkMinOSVersion, bool allowWeakImports, cpu_type_t cpuType, cpu_subtype_t cpuSubType, + bool enforceDylibSubtypesMatch, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath, bool indirectDylib) - : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, linkingFlatNamespace, - hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), - _cpuType(cpuType) + : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace, + hoistImplicitPublicDylibs, allowSimToMacOSX, addVers) { - this->_bitcode = std::unique_ptr(new ld::Bitcode(nullptr, 0)); - // Text stubs are implicit app extension safe. - this->_appExtensionSafe = true; + auto matchingType = enforceDylibSubtypesMatch ? + tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; + + std::string errorMessage; + auto file = std::unique_ptr( + tapi::LinkerInterfaceFile::create(path, fileContent, fileLength, cpuType, + cpuSubType, matchingType, + tapi::PackedVersion32(linkMinOSVersion), errorMessage)); + + if (file == nullptr) + throw strdup(errorMessage.c_str()); + + // unmap file - it is no longer needed. + munmap((caddr_t)fileContent, fileLength); // write out path for -t option if ( logAllFiles ) printf("%s\n", path); - TBDFile stub((const char*)fileContent, fileLength); - auto lib = stub.parseFileForArch(archName); - - this->_noRexports = lib._reexportedLibraries.empty(); - this->_hasWeakExports = !lib._weakDefSymbols.empty(); - this->_dylibInstallPath = strdup(lib._installName.str().c_str()); - this->_dylibCurrentVersion = lib._currentVersion; - this->_dylibCompatibilityVersion = lib._compatibilityVersion; - this->_swiftVersion = lib._swiftVersion; - this->_objcConstraint = lib._objcConstraint; - this->_hasPublicInstallName = this->isPublicLocation(this->_dylibInstallPath); + this->_bitcode = std::unique_ptr(new ld::Bitcode(nullptr, 0)); + this->_noRexports = !file->hasReexportedLibraries(); + this->_hasWeakExports = file->hasWeakDefinedExports(); + this->_dylibInstallPath = strdup(file->getInstallName().c_str()); + this->_installPathOverride = file->isInstallNameVersionSpecific(); + this->_dylibCurrentVersion = file->getCurrentVersion(); + this->_dylibCompatibilityVersion = file->getCompatibilityVersion(); + this->_swiftVersion = file->getSwiftVersion(); + this->_objcConstraint = mapObjCConstraint(file->getObjCConstraint()); + this->_parentUmbrella = file->getParentFrameworkName().empty() ? nullptr : strdup(file->getParentFrameworkName().c_str()); + this->_appExtensionSafe = file->isApplicationExtensionSafe(); // if framework, capture framework name const char* lastSlash = strrchr(this->_dylibInstallPath, '/'); @@ -571,100 +151,69 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, this->_frameworkName = leafName; } - // TEMPORARY HACK BEGIN: Support ancient re-export command LC_SUB_FRAMEWORK. - // [TAPI] Support LC_SUB_FRAMEWORK as re-export indicator. - auto installName = std::string(this->_dylibInstallPath); - - // All sub-frameworks of ApplicationServices use LC_SUB_FRAMEWORK. - if (installName.find("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/") == 0 && - installName.find(".dylib") == std::string::npos) { - this->_parentUmbrella = "ApplicationServices"; - } else if (installName.find("/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/") == 0) { - this->_parentUmbrella = "Carbon"; - } else if (installName.find("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/") == 0 && - installName.find(".dylib") == std::string::npos) { - this->_parentUmbrella = "CoreServices"; - } else if (installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib") == 0 || - installName.find("/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib") == 0 || - installName.find("System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib") == 0) { - this->_parentUmbrella = "vecLib"; - } else if (installName.find("/System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/WebCore") == 0) { - this->_parentUmbrella = "WebKit"; - } else if (installName.find("/usr/lib/system/") == 0 && - installName != "/usr/lib/system/libkxld.dylib") { - this->_parentUmbrella = "System"; - } - // TEMPORARY HACK END - - for (auto &client : lib._allowedClients) { - if ((this->_parentUmbrella != nullptr) && (client.str() != this->_parentUmbrella)) - this->_allowableClients.push_back(strdup(client.str().c_str())); - } + for (auto &client : file->allowableClients()) + this->_allowableClients.push_back(strdup(client.c_str())); // [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked - if ( !this->_allowableClients.empty() ) - this->_hasPublicInstallName = false; + this->_hasPublicInstallName = file->hasAllowableClients() ? false : this->isPublicLocation(file->getInstallName().c_str()); + + for (const auto &client : file->allowableClients()) + this->_allowableClients.emplace_back(strdup(client.c_str())); - if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) { + auto dylibPlatform = mapPlatform(file->getPlatform()); + if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) { this->_wrongOS = true; if ( this->_addVersionLoadCommand && !indirectDylib ) { if ( buildingForSimulator ) { if ( !this->_allowSimToMacOSXLinking ) throwf("building for %s simulator, but linking against dylib built for %s (%s).", - Options::platformName(platform), Options::platformName(lib._platform), path); + Options::platformName(platform), Options::platformName(dylibPlatform), path); } else { throwf("building for %s, but linking against dylib built for %s (%s).", - Options::platformName(platform), Options::platformName(lib._platform), path); + Options::platformName(platform), Options::platformName(dylibPlatform), path); } } } - this->_dependentDylibs.reserve(lib._reexportedLibraries.size()); - for ( const auto& reexport : lib._reexportedLibraries ) { - const char *path = strdup(reexport.str().c_str()); + for (const auto& reexport : file->reexportedLibraries()) { + const char *path = strdup(reexport.c_str()); if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) ) this->_dependentDylibs.emplace_back(path, true); } - // build hash table - buildExportHashTable(lib); + for (const auto& symbol : file->ignoreExports()) + this->_ignoreExports.insert(strdup(symbol.c_str())); - munmap((caddr_t)fileContent, fileLength); + // if linking flat and this is a flat dylib, create one atom that references all imported symbols. + if ( linkingFlatNamespace && linkingMainExecutable && (file->hasTwoLevelNamespace() == false) ) { + std::vector importNames; + importNames.reserve(file->undefineds().size()); + // We do not need to strdup the name, because that will be done by the + // ImportAtom constructor. + for (const auto &sym : file->undefineds()) + importNames.emplace_back(sym.getName().c_str()); + this->_importAtom = new generic::dylib::ImportAtom(*this, importNames); + } + + // build hash table + buildExportHashTable(file.get()); } template -void File::buildExportHashTable(const DynamicLibrary& lib) { +void File::buildExportHashTable(const tapi::LinkerInterfaceFile* file) { if (this->_s_logHashtable ) fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path()); - for (auto &sym : lib._symbols) - this->addSymbol(sym.str().c_str()); + for (const auto &sym : file->exports()) { + const char* name = sym.getName().c_str(); + bool weakDef = sym.isWeakDefined(); + bool tlv = sym.isThreadLocalValue(); -#if SUPPORT_ARCH_i386 - if (this->_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) { - for (auto &sym : lib._classes) - this->addSymbol((".objc_class_name" + sym.str()).c_str()); - } else { - for (auto &sym : lib._classes) { - this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str()); - this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str()); - } + typename Base::AtomAndWeak bucket = { nullptr, weakDef, tlv, 0 }; + if ( this->_s_logHashtable ) + fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + this->_atoms[strdup(name)] = bucket; } -#else - for (auto &sym : lib._classes) { - this->addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str()); - this->addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str()); - } -#endif - - for (auto &sym : lib._ivars) - this->addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str()); - - for (auto &sym : lib._weakDefSymbols) - this->addSymbol(sym.str().c_str(), /*weak=*/true); - - for (auto &sym : lib._tlvSymbols) - this->addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true); } template @@ -673,19 +222,21 @@ class Parser public: using P = typename A::P; - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, - const std::string &path, const char* archName); - static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t mTime, ld::File::Ordinal ordinal, const Options& opts, + static ld::dylib::File* parse(const char* path, const uint8_t* fileContent, + uint64_t fileLength, time_t mTime, + ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { - return new File(fileContent, fileLength, path, mTime, ordinal, + return new File(path, fileContent, fileLength,mTime, ordinal, opts.flatNamespace(), + opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), opts.platform(), - opts.architecture(), - opts.architectureName(), opts.minOSversion(), + opts.allowWeakImports(), + opts.architecture(), + opts.subArchitecture(), + opts.enforceDylibSubtypesMatch(), opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), opts.targetIOSSimulator(), @@ -695,20 +246,6 @@ class Parser } }; -template -bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, - const char* archName) -{ - if ( path.find(".tbd", path.size()-4) == std::string::npos ) - return false; - - TBDFile stub((const char*)fileContent, fileLength); - if ( !stub.validForArch(archName) ) - throwf("missing required architecture %s in file %s", archName, path.c_str()); - - return true; -} - // // main function used by linker to instantiate ld::Files // @@ -716,30 +253,27 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib) { + switch ( opts.architecture() ) { #if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: - if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) + return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) + return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) + return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: - if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); - break; + if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) + return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); #endif } return nullptr; diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp index dce3783..527869a 100644 --- a/ld64/src/ld/passes/bitcode_bundle.cpp +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -657,10 +657,17 @@ void BitcodeBundle::doPass() bh->populateMustPreserveSymbols(obfuscator); handlerMap.emplace(std::string(f->path()), bh); } else if ( ld::LLVMBitcode* bitcode = dynamic_cast(f->getBitcode()) ) { - BitcodeHandler* bitcodeHandler = new BitcodeHandler((char*)bitcode->getContent(), bitcode->getSize()); - bitcodeHandler->populateMustPreserveSymbols(obfuscator); + BitcodeHandler bitcodeHandler((char*)bitcode->getContent(), bitcode->getSize()); + bitcodeHandler.populateMustPreserveSymbols(obfuscator); } } + // add must preserve symbols from lto input. + for ( auto &f : _state.ltoBitcodePath ) { + BitcodeTempFile ltoTemp(f.c_str(), false); // Keep the temp file because it needs to be read in later in the pass. + BitcodeHandler bitcodeHandler((char*)ltoTemp.getContent(), ltoTemp.getSize()); + bitcodeHandler.populateMustPreserveSymbols(obfuscator); + } + // special symbols supplied by linker obfuscator->addMustPreserveSymbols("___dso_handle"); obfuscator->addMustPreserveSymbols("__mh_execute_header"); @@ -762,26 +769,30 @@ void BitcodeBundle::doPass() } } - // Write merged LTO bitcode + // Write merged LTO bitcode files if ( !_state.ltoBitcodePath.empty() ) { - xar_file_t ltoFile = NULL; - BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles()); - if ( _options.hideSymbols() ) { + int count = 0; + for (auto &path : _state.ltoBitcodePath) { + std::string xar_name = "lto.o." + std::to_string(count++); + xar_file_t ltoFile = NULL; + BitcodeTempFile* ltoTemp = new BitcodeTempFile(path.c_str(), !_options.saveTempFiles()); + if ( _options.hideSymbols() ) { ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize()); char ltoTempFile[PATH_MAX]; sprintf(ltoTempFile, "%s/lto.bc", tempdir); - obfuscator->bitcodeHideSymbols(<oBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile); + obfuscator->bitcodeHideSymbols(<oBitcode, path.c_str(), ltoTempFile); BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles()); - ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize()); + ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoStrip->getContent(), ltoStrip->getSize()); delete ltoStrip; - } else { - ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize()); + } else { + ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoTemp->getContent(), ltoTemp->getSize()); + } + if ( ltoFile == NULL ) + throwf("could not add lto file %s to bitcode bundle", path.c_str()); + if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) + throwf("could not set bitcode property for %s in bitcode bundle", path.c_str()); + delete ltoTemp; } - if ( ltoFile == NULL ) - throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str()); - if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) - throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str()); - delete ltoTemp; } // Common LinkOptions diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index d2847bb..8b2afbc 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -303,6 +303,7 @@ bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); } + template <> bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) { @@ -424,6 +425,7 @@ void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, con _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); } + template <> void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) { @@ -461,6 +463,7 @@ void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } + template <> void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) { @@ -489,6 +492,7 @@ void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::At _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } + template <> void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) { @@ -517,6 +521,7 @@ void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld:: _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } + template <> void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) { @@ -545,6 +550,7 @@ void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } + template <> void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) { @@ -576,6 +582,7 @@ void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); } + template <> void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) { diff --git a/ld64/src/ld/passes/dylibs.cpp b/ld64/src/ld/passes/dylibs.cpp index f77f5cd..7edc85c 100644 --- a/ld64/src/ld/passes/dylibs.cpp +++ b/ld64/src/ld/passes/dylibs.cpp @@ -94,9 +94,11 @@ void doPass(const Options& opts, ld::Internal& state) targetIsWeakImport = fit->weakImport; break; default: - break; + break; } if ( (target != NULL) && (target->definition() == ld::Atom::definitionProxy) ) { + if ( targetIsWeakImport && !opts.allowWeakImports() ) + throwf("weak import of symbol '%s' not supported because of option: -no_weak_imports", target->name()); ld::Atom::WeakImportState curWI = target->weakImportState(); if ( curWI == ld::Atom::weakImportUnset ) { // first use of this proxy, set weak-import based on this usage diff --git a/ld64/src/ld/passes/huge.cpp b/ld64/src/ld/passes/huge.cpp index 693976c..3797a23 100644 --- a/ld64/src/ld/passes/huge.cpp +++ b/ld64/src/ld/passes/huge.cpp @@ -47,10 +47,53 @@ class NullAtom } }; +class DataPadAtom : public ld::Atom { +public: + DataPadAtom(ld::Internal& state) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) + { state.addAtom(*this); } + + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "padding"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + +protected: + virtual ~DataPadAtom() {} + + static ld::Section _s_section; +}; + +ld::Section DataPadAtom::_s_section("__DATA", "__data", ld::Section::typeUnclassified); + + void doPass(const Options& opts, ld::Internal& state) { const bool log = false; - + + // add __data section if __DATA segment was gutted by dirty data removal + if ( (opts.outputKind() == Options::kDynamicLibrary) && opts.useDataConstSegment() && opts.hasDataSymbolMoves() ) { + uint64_t dataAtomsSize = 0; + bool foundSegmentDATA_DIRTY = false; + for (ld::Internal::FinalSection* sect : state.sections) { + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) { + for (const ld::Atom* atom : sect->atoms) { + dataAtomsSize += atom->size(); + } + } + else if ( strcmp(sect->segmentName(), "__DATA_DIRTY") == 0 ) { + foundSegmentDATA_DIRTY = true; + } + } + if ( foundSegmentDATA_DIRTY && (dataAtomsSize == 0) ) { + new DataPadAtom(state); + } + } + // only make make __huge section in final linked images if ( opts.outputKind() == Options::kObjectFile ) return; diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index c65ca21..e2afca1 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -50,11 +50,12 @@ struct objc_image_info { uint32_t flags; }; -#define OBJC_IMAGE_SUPPORTS_GC (1<<1) -#define OBJC_IMAGE_REQUIRES_GC (1<<2) -#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) -#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) -#define OBJC_IMAGE_IS_SIMULATED (1<<5) +#define OBJC_IMAGE_SUPPORTS_GC (1<<1) +#define OBJC_IMAGE_REQUIRES_GC (1<<2) +#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) +#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) +#define OBJC_IMAGE_IS_SIMULATED (1<<5) +#define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES (1<<6) @@ -65,7 +66,7 @@ template class ObjCImageInfoAtom : public ld::Atom { public: ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, - bool compaction, bool abi2, uint8_t swiftVersion); + bool compaction, bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion); virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return "objc image info"; } @@ -89,7 +90,7 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, - bool abi2, uint8_t swiftVersion) + bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion) : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) @@ -117,6 +118,10 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, break; } + if ( hasCategoryClassProperties ) { + value |= OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES; + } + // provide swift language version in final binary for runtime to inspect value |= (swiftVersion << 8); @@ -206,9 +211,12 @@ ld::Section ProtocolListAtom::_s_section("__DATA", "__objc_const", ld::Sectio template class PropertyListAtom : public ld::Atom { public: + enum class PropertyKind { ClassProperties, InstanceProperties }; + PropertyListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, - const std::vector* categories, - std::set& deadAtoms); + const std::vector* categories, + std::set& deadAtoms, + PropertyKind kind); virtual const ld::File* file() const { return _file; } virtual const char* name() const { return "objc merged property list"; } @@ -273,6 +281,8 @@ class ClassROOverlayAtom : public ld::Atom { private: typedef typename A::P::uint_t pint_t; + void addFixupAtOffset(uint32_t offset); + const ld::Atom* _atom; std::vector _fixups; }; @@ -314,7 +324,7 @@ const ld::Atom* ObjCData::getPointerInContent(ld::Internal& state, const ld:: if ( hasAddend != NULL ) *hasAddend = false; for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) { - if ( fit->offsetInAtom == offset ) { + if ( (fit->offsetInAtom == offset) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { switch ( fit->binding ) { case ld::Fixup::bindingsIndirectlyBound: target = state.indirectBindingTable[fit->u.bindingIndex]; @@ -369,7 +379,8 @@ class Category : public ObjCData { static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom); - static const ld::Atom* getProperties(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getInstanceProperties(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getClassProperties(ld::Internal& state, const ld::Atom* contentAtom); static uint32_t size() { return 6*sizeof(pint_t); } private: typedef typename A::P::uint_t pint_t; @@ -401,11 +412,20 @@ const ld::Atom* Category::getProtocols(ld::Internal& state, const ld::Atom* c } template -const ld::Atom* Category::getProperties(ld::Internal& state, const ld::Atom* contentAtom) +const ld::Atom* Category::getInstanceProperties(ld::Internal& state, const ld::Atom* contentAtom) { return ObjCData::getPointerInContent(state, contentAtom, 5*sizeof(pint_t)); // category_t.instanceProperties } +template +const ld::Atom* Category::getClassProperties(ld::Internal& state, const ld::Atom* contentAtom) +{ + // Only specially-marked files have this field. + if (!contentAtom->file()->objcHasCategoryClassPropertiesField()) return NULL; + + return ObjCData::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties +} + template class MethodList : public ObjCData { @@ -446,10 +466,12 @@ class PropertyList : public ObjCData { template class Class : public ObjCData { public: + static const ld::Atom* getMetaClass(ld::Internal& state, const ld::Atom* classAtom); static const ld::Atom* getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom); static const ld::Atom* getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom); static const ld::Atom* getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom); static const ld::Atom* getClassMethodList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getClassPropertyList(ld::Internal& state, const ld::Atom* classAtom); static const ld::Atom* setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, const ld::Atom* methodListAtom, std::set& deadAtoms); static const ld::Atom* setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, @@ -460,74 +482,117 @@ class Class : public ObjCData { const ld::Atom* methodListAtom, std::set& deadAtoms); static const ld::Atom* setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom, const ld::Atom* protocolListAtom, std::set& deadAtoms); - static uint32_t size() { return 5*sizeof(pint_t); } - static unsigned int class_ro_header_size(); + static const ld::Atom* setClassPropertyList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* propertyListAtom, std::set& deadAtoms); + static uint32_t size() { return sizeof(Content); } + private: + friend class ClassROOverlayAtom; + typedef typename A::P::uint_t pint_t; + static const ld::Atom* getROData(ld::Internal& state, const ld::Atom* classAtom); + + struct Content { + pint_t isa; + pint_t superclass; + pint_t method_cache; + pint_t vtable; + pint_t data; + }; + + struct ROContent { + uint32_t flags; + uint32_t instanceStart; + // Note there is 4-bytes of alignment padding between instanceSize + // and ivarLayout on 64-bit archs, but no padding on 32-bit archs. + // This union is a way to model that. + union { + uint32_t instanceSize; + pint_t pad; + } instanceSize; + pint_t ivarLayout; + pint_t name; + pint_t baseMethods; + pint_t baseProtocols; + pint_t ivars; + pint_t weakIvarLayout; + pint_t baseProperties; + }; }; -template <> unsigned int Class::class_ro_header_size() { return 16; } -template <> unsigned int Class::class_ro_header_size() { return 12;} -template <> unsigned int Class::class_ro_header_size() { return 12; } +#define GET_FIELD(state, classAtom, field) \ + ObjCData::getPointerInContent(state, classAtom, offsetof(Content, field)) +#define SET_FIELD(state, classAtom, field, valueAtom) \ + ObjCData::setPointerInContent(state, classAtom, offsetof(Content, field), valueAtom) +#define GET_RO_FIELD(state, classAtom, field) \ + ObjCData::getPointerInContent(state, getROData(state, classAtom), offsetof(ROContent, field)) +#define SET_RO_FIELD(state, classROAtom, field, valueAtom) \ + ObjCData::setPointerInContent(state, getROData(state, classAtom), offsetof(ROContent, field), valueAtom) template -const ld::Atom* Class::getROData(ld::Internal& state, const ld::Atom* classAtom) +const ld::Atom* Class::getMetaClass(ld::Internal& state, const ld::Atom* classAtom) { - return ObjCData::getPointerInContent(state, classAtom, 4*sizeof(pint_t)); // class_t.data + const ld::Atom* metaClassAtom = GET_FIELD(state, classAtom, isa); + assert(metaClassAtom != NULL); + return metaClassAtom; +} +template +const ld::Atom* Class::getROData(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = GET_FIELD(state, classAtom, data); + assert(classROAtom != NULL); + return classROAtom; } template const ld::Atom* Class::getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom) { - const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data - assert(classROAtom != NULL); - return ObjCData::getPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t)); // class_ro_t.baseMethods + return GET_RO_FIELD(state, classAtom, baseMethods); } template const ld::Atom* Class::getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom) { - const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data - assert(classROAtom != NULL); - return ObjCData::getPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t)); // class_ro_t.baseProtocols + return GET_RO_FIELD(state, classAtom, baseProtocols); } template const ld::Atom* Class::getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom) { - const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data - assert(classROAtom != NULL); - return ObjCData::getPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t)); // class_ro_t.baseProperties + return GET_RO_FIELD(state, classAtom, baseProperties); } template const ld::Atom* Class::getClassMethodList(ld::Internal& state, const ld::Atom* classAtom) { - const ld::Atom* metaClassAtom = ObjCData::getPointerInContent(state, classAtom, 0); // class_t.isa - assert(metaClassAtom != NULL); - return Class::getInstanceMethodList(state, metaClassAtom); + return Class::getInstanceMethodList(state, getMetaClass(state, classAtom)); +} + +template +const ld::Atom* Class::getClassPropertyList(ld::Internal& state, const ld::Atom* classAtom) +{ + return Class::getInstancePropertyList(state, getMetaClass(state, classAtom)); } template const ld::Atom* Class::setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, const ld::Atom* methodListAtom, std::set& deadAtoms) { - const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data - assert(classROAtom != NULL); // if the base class does not already have a method list, we need to create an overlay if ( getInstanceMethodList(state, classAtom) == NULL ) { - ClassROOverlayAtom* overlay = new ClassROOverlayAtom(classROAtom); + const ld::Atom* oldROAtom = getROData(state, classAtom); + deadAtoms.insert(oldROAtom); + ClassROOverlayAtom* overlay = new ClassROOverlayAtom(oldROAtom); //fprintf(stderr, "replace class RO atom %p with %p for method list in class atom %s\n", classROAtom, overlay, classAtom->name()); overlay->addMethodListFixup(); - ObjCData::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data - deadAtoms.insert(classROAtom); - ObjCData::setPointerInContent(state, overlay, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods + SET_FIELD(state, classAtom, data, overlay); + SET_RO_FIELD(state, classAtom, baseMethods, methodListAtom); return overlay; } - ObjCData::setPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods + SET_RO_FIELD(state, classAtom, baseMethods, methodListAtom); return NULL; // means classRO atom was not replaced } @@ -535,20 +600,19 @@ template const ld::Atom* Class::setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, const ld::Atom* protocolListAtom, std::set& deadAtoms) { - const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data - assert(classROAtom != NULL); // if the base class does not already have a protocol list, we need to create an overlay if ( getInstanceProtocolList(state, classAtom) == NULL ) { - ClassROOverlayAtom* overlay = new ClassROOverlayAtom(classROAtom); + const ld::Atom* oldROAtom = getROData(state, classAtom); + deadAtoms.insert(oldROAtom); + ClassROOverlayAtom* overlay = new ClassROOverlayAtom(oldROAtom); //fprintf(stderr, "replace class RO atom %p with %p for protocol list in class atom %s\n", classROAtom, overlay, classAtom->name()); overlay->addProtocolListFixup(); - ObjCData::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data - deadAtoms.insert(classROAtom); - ObjCData::setPointerInContent(state, overlay, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols + SET_FIELD(state, classAtom, data, overlay); + SET_RO_FIELD(state, classAtom, baseProtocols, protocolListAtom); return overlay; } //fprintf(stderr, "set class RO atom %p protocol list in class atom %s\n", classROAtom, classAtom->name()); - ObjCData::setPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols + SET_RO_FIELD(state, classAtom, baseProtocols, protocolListAtom); return NULL; // means classRO atom was not replaced } @@ -557,9 +621,8 @@ const ld::Atom* Class::setClassProtocolList(ld::Internal& state, const ld::At const ld::Atom* protocolListAtom, std::set& deadAtoms) { // meta class also points to same protocol list as class - const ld::Atom* metaClassAtom = ObjCData::getPointerInContent(state, classAtom, 0); // class_t.isa + const ld::Atom* metaClassAtom = getMetaClass(state, classAtom); //fprintf(stderr, "setClassProtocolList(), classAtom=%p %s, metaClass=%p %s\n", classAtom, classAtom->name(), metaClassAtom, metaClassAtom->name()); - assert(metaClassAtom != NULL); return setInstanceProtocolList(state, metaClassAtom, protocolListAtom, deadAtoms); } @@ -569,19 +632,18 @@ template const ld::Atom* Class::setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom, const ld::Atom* propertyListAtom, std::set& deadAtoms) { - const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data - assert(classROAtom != NULL); // if the base class does not already have a property list, we need to create an overlay if ( getInstancePropertyList(state, classAtom) == NULL ) { - ClassROOverlayAtom* overlay = new ClassROOverlayAtom(classROAtom); + const ld::Atom* oldROAtom = getROData(state, classAtom); + deadAtoms.insert(oldROAtom); + ClassROOverlayAtom* overlay = new ClassROOverlayAtom(oldROAtom); //fprintf(stderr, "replace class RO atom %p with %p for property list in class atom %s\n", classROAtom, overlay, classAtom->name()); overlay->addPropertyListFixup(); - ObjCData::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data - deadAtoms.insert(classROAtom); - ObjCData::setPointerInContent(state, overlay, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties + SET_FIELD(state, classAtom, data, overlay); + SET_RO_FIELD(state, classAtom, baseProperties, propertyListAtom); return overlay; } - ObjCData::setPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties + SET_RO_FIELD(state, classAtom, baseProperties, propertyListAtom); return NULL; // means classRO atom was not replaced } @@ -590,86 +652,67 @@ const ld::Atom* Class::setClassMethodList(ld::Internal& state, const ld::Atom const ld::Atom* methodListAtom, std::set& deadAtoms) { // class methods is just instance methods of metaClass - const ld::Atom* metaClassAtom = ObjCData::getPointerInContent(state, classAtom, 0); // class_t.isa - assert(metaClassAtom != NULL); - return setInstanceMethodList(state, metaClassAtom, methodListAtom, deadAtoms); + return setInstanceMethodList(state, getMetaClass(state, classAtom), methodListAtom, deadAtoms); } - - -template <> -void ClassROOverlayAtom::addMethodListFixup() -{ - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 2*8; // class_ro_t.baseMethods - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); -} - -template <> -void ClassROOverlayAtom::addMethodListFixup() +template +const ld::Atom* Class::setClassPropertyList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* propertyListAtom, std::set& deadAtoms) { - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 2*4; // class_ro_t.baseMethods - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); + // class properties is just instance properties of metaClass + return setInstancePropertyList(state, getMetaClass(state, classAtom), propertyListAtom, deadAtoms); } -template <> -void ClassROOverlayAtom::addMethodListFixup() -{ - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 2*4; // class_ro_t.baseMethods - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); -} +#undef GET_FIELD +#undef SET_FIELD +#undef GET_RO_FIELD +#undef SET_RO_FIELD +template +ld::Fixup::Kind pointerFixupKind(); -template <> -void ClassROOverlayAtom::addProtocolListFixup() -{ - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 3*8; // class_ro_t.baseProtocols - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +template <> +ld::Fixup::Kind pointerFixupKind>() { + return ld::Fixup::kindStoreTargetAddressBigEndian32; } - -template <> -void ClassROOverlayAtom::addProtocolListFixup() -{ - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +template <> +ld::Fixup::Kind pointerFixupKind>() { + return ld::Fixup::kindStoreTargetAddressBigEndian64; +} +template <> +ld::Fixup::Kind pointerFixupKind>() { + return ld::Fixup::kindStoreTargetAddressLittleEndian32; +} +template <> +ld::Fixup::Kind pointerFixupKind>() { + return ld::Fixup::kindStoreTargetAddressLittleEndian64; } -template <> -void ClassROOverlayAtom::addProtocolListFixup() +template +void ClassROOverlayAtom::addFixupAtOffset(uint32_t offset) { const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, pointerFixupKind(), targetAtom)); } -template <> -void ClassROOverlayAtom::addPropertyListFixup() +template +void ClassROOverlayAtom::addMethodListFixup() { - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 6*8; // class_ro_t.baseProperties - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); + addFixupAtOffset(offsetof(typename Class::ROContent, baseMethods)); } -template <> -void ClassROOverlayAtom::addPropertyListFixup() +template +void ClassROOverlayAtom::addProtocolListFixup() { - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 6*4; // class_ro_t.baseProperties - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); + addFixupAtOffset(offsetof(typename Class::ROContent, baseProtocols)); } -template <> -void ClassROOverlayAtom::addPropertyListFixup() +template +void ClassROOverlayAtom::addPropertyListFixup() { - const ld::Atom* targetAtom = this; // temporary - uint32_t offset = Class::class_ro_header_size() + 6*4; // class_ro_t.baseProperties - _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); + addFixupAtOffset(offsetof(typename Class::ROContent, baseProperties)); } @@ -685,8 +728,8 @@ class OptimizeCategories { static bool hasInstanceMethods(ld::Internal& state, const std::vector* categories); static bool hasClassMethods(ld::Internal& state, const std::vector* categories); static bool hasProtocols(ld::Internal& state, const std::vector* categories); - static bool hasProperties(ld::Internal& state, const std::vector* categories); - + static bool hasInstanceProperties(ld::Internal& state, const std::vector* categories); + static bool hasClassProperties(ld::Internal& state, const std::vector* categories); static unsigned int class_ro_baseMethods_offset(); private: @@ -741,11 +784,26 @@ bool OptimizeCategories::hasProtocols(ld::Internal& state, const std::vector< template -bool OptimizeCategories::hasProperties(ld::Internal& state, const std::vector* categories) +bool OptimizeCategories::hasInstanceProperties(ld::Internal& state, const std::vector* categories) { for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { const ld::Atom* categoryAtom = *it; - const ld::Atom* propertyListAtom = Category::getProperties(state, categoryAtom); + const ld::Atom* propertyListAtom = Category::getInstanceProperties(state, categoryAtom); + if ( propertyListAtom != NULL ) { + if ( PropertyList::count(state, propertyListAtom) > 0 ) + return true; + } + } + return false; +} + + +template +bool OptimizeCategories::hasClassProperties(ld::Internal& state, const std::vector* categories) +{ + for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* propertyListAtom = Category::getClassProperties(state, categoryAtom); if ( propertyListAtom != NULL ) { if ( PropertyList::count(state, propertyListAtom) > 0 ) return true; @@ -813,6 +871,7 @@ class OptimizedAway { std::sort(atoms.begin(), atoms.end(), AtomSorter()); } + template void OptimizeCategories::doit(const Options& opts, ld::Internal& state) { @@ -941,10 +1000,10 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) state.atomToSection[newMetaClassRO] = methodListSection; } } - // if any category adds properties, generate new merged property list, and replace - if ( OptimizeCategories::hasProperties(state, categories) ) { + // if any category adds instance properties, generate new merged property list, and replace + if ( OptimizeCategories::hasInstanceProperties(state, categories) ) { const ld::Atom* basePropertyListAtom = Class::getInstancePropertyList(state, classAtom); - const ld::Atom* newPropertyListAtom = new PropertyListAtom(state, basePropertyListAtom, categories, deadAtoms); + const ld::Atom* newPropertyListAtom = new PropertyListAtom(state, basePropertyListAtom, categories, deadAtoms, PropertyListAtom::PropertyKind::InstanceProperties); const ld::Atom* newClassRO = Class::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms); // add new property list to final sections methodListSection->atoms.push_back(newPropertyListAtom); @@ -955,7 +1014,20 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) state.atomToSection[newClassRO] = methodListSection; } } - + // if any category adds class properties, generate new merged property list, and replace + if ( OptimizeCategories::hasClassProperties(state, categories) ) { + const ld::Atom* basePropertyListAtom = Class::getClassPropertyList(state, classAtom); + const ld::Atom* newPropertyListAtom = new PropertyListAtom(state, basePropertyListAtom, categories, deadAtoms, PropertyListAtom::PropertyKind::ClassProperties); + const ld::Atom* newClassRO = Class::setClassPropertyList(state, classAtom, newPropertyListAtom, deadAtoms); + // add new property list to final sections + methodListSection->atoms.push_back(newPropertyListAtom); + state.atomToSection[newPropertyListAtom] = methodListSection; + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; + } + } } // remove dead atoms @@ -967,6 +1039,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) } + template MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta, const std::vector* categories, std::set& deadAtoms) @@ -1120,7 +1193,7 @@ ProtocolListAtom::ProtocolListAtom(ld::Internal& state, const ld::Atom* baseP template PropertyListAtom::PropertyListAtom(ld::Internal& state, const ld::Atom* basePropertyList, - const std::vector* categories, std::set& deadAtoms) + const std::vector* categories, std::set& deadAtoms, PropertyKind kind) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _propertyCount(0) @@ -1135,7 +1208,7 @@ PropertyListAtom::PropertyListAtom(ld::Internal& state, const ld::Atom* baseP fixupCount = basePropertyList->fixupsEnd() - basePropertyList->fixupsBegin(); } for (std::vector::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { - const ld::Atom* categoryPropertyListAtom = Category::getProperties(state, *ait); + const ld::Atom* categoryPropertyListAtom = kind == PropertyKind::ClassProperties ? Category::getClassProperties(state, *ait) : Category::getInstanceProperties(state, *ait); if ( categoryPropertyListAtom != NULL ) { _propertyCount += PropertyList::count(state, categoryPropertyListAtom); fixupCount += (categoryPropertyListAtom->fixupsEnd() - categoryPropertyListAtom->fixupsBegin()); @@ -1152,7 +1225,7 @@ PropertyListAtom::PropertyListAtom(ld::Internal& state, const ld::Atom* baseP _fixups.reserve(fixupCount); uint32_t slide = 0; for (std::vector::const_iterator it=categories->begin(); it != categories->end(); ++it) { - const ld::Atom* categoryPropertyListAtom = Category::getProperties(state, *it); + const ld::Atom* categoryPropertyListAtom = kind == PropertyKind::ClassProperties ? Category::getClassProperties(state, *it) : Category::getInstanceProperties(state, *it); if ( categoryPropertyListAtom != NULL ) { for (ld::Fixup::iterator fit=categoryPropertyListAtom->fixupsBegin(); fit != categoryPropertyListAtom->fixupsEnd(); ++fit) { ld::Fixup fixup = *fit; @@ -1179,85 +1252,112 @@ PropertyListAtom::PropertyListAtom(ld::Internal& state, const ld::Atom* baseP } +template +void scanCategories(ld::Internal& state, + bool& haveCategoriesWithNonNullClassProperties, + bool& haveCategoriesWithoutClassPropertyStorage) +{ + haveCategoriesWithNonNullClassProperties = false; + haveCategoriesWithoutClassPropertyStorage = false; + + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeObjC2CategoryList ) { + for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* categoryListElementAtom = *ait; + bool hasAddend; + const ld::Atom* categoryAtom = ObjCData::getPointerInContent(state, categoryListElementAtom, 0, &hasAddend); + + if (Category::getClassProperties(state, categoryAtom)) { + haveCategoriesWithNonNullClassProperties = true; + // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->file()->path()); + } + + if (!categoryAtom->file()->objcHasCategoryClassPropertiesField()) { + haveCategoriesWithoutClassPropertyStorage = true; + // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->file()->path()); + } + } + } + } +} +template void doPass(const Options& opts, ld::Internal& state) -{ - // only make image info section if objc was used - if ( state.objcObjectConstraint != ld::File::objcConstraintNone ) { - - // verify dylibs are GC compatible with object files - if ( state.objcObjectConstraint != state.objcDylibConstraint ) { - if ( (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease) - && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) { - throw "Linked dylibs built for retain/release but object files built for GC-only"; - } - else if ( (state.objcDylibConstraint == ld::File::objcConstraintGC) - && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) { - throw "Linked dylibs built for GC-only but object files built for retain/release"; - } +{ + // Do nothing if the output has no ObjC content. + if ( state.objcObjectConstraint == ld::File::objcConstraintNone ) { + return; + } + + // verify dylibs are GC compatible with object files + if ( state.objcObjectConstraint != state.objcDylibConstraint ) { + if ( (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease) + && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) { + throw "Linked dylibs built for retain/release but object files built for GC-only"; + } + else if ( (state.objcDylibConstraint == ld::File::objcConstraintGC) + && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) { + throw "Linked dylibs built for GC-only but object files built for retain/release"; } - - const bool compaction = opts.objcGcCompaction(); - - // add image info atom - switch ( opts.architecture() ) { -#if SUPPORT_ARCH_x86_64 - case CPU_TYPE_X86_64: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true, state.swiftVersion)); - break; -#endif -#if SUPPORT_ARCH_i386 - case CPU_TYPE_I386: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - opts.objCABIVersion2POverride() ? true : false, state.swiftVersion)); - break; -#endif -#if SUPPORT_ARCH_arm_any - case CPU_TYPE_ARM: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true, state.swiftVersion)); - break; -#endif -#if SUPPORT_ARCH_arm64 - case CPU_TYPE_ARM64: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - true, state.swiftVersion)); - break; -#endif - default: - assert(0 && "unknown objc arch"); - } } - + if ( opts.objcCategoryMerging() ) { // optimize classes defined in this linkage unit by merging in categories also in this linkage unit - switch ( opts.architecture() ) { + OptimizeCategories::doit(opts, state); + } + + // Search for surviving categories that have a non-null class properties field. + // Search for surviving categories that do not have storage for the class properties field. + bool haveCategoriesWithNonNullClassProperties; + bool haveCategoriesWithoutClassPropertyStorage; + scanCategories(state, haveCategoriesWithNonNullClassProperties, haveCategoriesWithoutClassPropertyStorage); + + // Complain about mismatched category ABI. + // These can't be combined into a single linkage unit because there is only one size indicator for all categories in the file. + // If there is a mismatch then we don't set the HasCategoryClassProperties bit in the output file, + // which has at runtime causes any class property metadata that was present to be ignored. + if (haveCategoriesWithNonNullClassProperties && haveCategoriesWithoutClassPropertyStorage) { + warning("Some object files have incompatible Objective-C category definitions. Some category metadata may be lost. All files containing Objective-C categories should be built using the same compiler."); + } + + // add image info atom + // The HasCategoryClassProperties bit is set as often as possible. + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, opts.objcGcCompaction(), isObjC2, + !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion)); +} + + +void doPass(const Options& opts, ld::Internal& state) +{ + switch ( opts.architecture() ) { #if SUPPORT_ARCH_x86_64 - case CPU_TYPE_X86_64: - OptimizeCategories::doit(opts, state); - break; + case CPU_TYPE_X86_64: + doPass(opts, state); + break; #endif #if SUPPORT_ARCH_i386 - case CPU_TYPE_I386: - if ( opts.objCABIVersion2POverride() ) - OptimizeCategories::doit(opts, state); - break; + case CPU_TYPE_I386: + if (opts.objCABIVersion2POverride()) { + doPass(opts, state); + } else { + doPass(opts, state); + } + break; #endif #if SUPPORT_ARCH_arm_any - case CPU_TYPE_ARM: - OptimizeCategories::doit(opts, state); - break; + case CPU_TYPE_ARM: + doPass(opts, state); + break; #endif #if SUPPORT_ARCH_arm64 - case CPU_TYPE_ARM64: - // disabled until tested - break; + case CPU_TYPE_ARM64: + doPass(opts, state); + break; #endif - default: - assert(0 && "unknown objc arch"); - } + default: + assert(0 && "unknown objc arch"); } } diff --git a/ld64/src/ld/passes/stubs/stub_arm64.hpp b/ld64/src/ld/passes/stubs/stub_arm64.hpp index 09de1fa..815e28a 100644 --- a/ld64/src/ld/passes/stubs/stub_arm64.hpp +++ b/ld64/src/ld/passes/stubs/stub_arm64.hpp @@ -406,5 +406,5 @@ class KextStubAtom : public ld::Atom { ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeCode); -} // namespace x86_64 +} // namespace arm64 diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index abed7e3..fb5b9f3 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -213,7 +213,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) return new ld::passes::stubs::arm::StubPICKextAtom(*this, target); } else if ( usingCompressedLINKEDIT() && !forLazyDylib ) { - if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText ) + if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText && !_options.makeEncryptable() ) return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); else if ( _pic ) return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst); diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 867f313..7739986 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -843,6 +843,9 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreARM64PCRelToGOT: printf(", then store as 32-bit delta to GOT entry"); break; + case ld::Fixup::kindStoreARM64PointerToGOT32: + printf(", then store as 32-bit pointer to GOT entry"); + break; case ld::Fixup::kindDtraceExtra: printf("dtrace static probe extra info"); break; diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index f19538f..83d601e 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -236,6 +236,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: + case MH_KEXT_BUNDLE: return true; } return false; @@ -256,6 +257,7 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: + case MH_KEXT_BUNDLE: return true; } return false; @@ -276,12 +278,14 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) case MH_DYLIB: case MH_BUNDLE: case MH_DYLINKER: + case MH_KEXT_BUNDLE: return true; } return false; } #endif + template DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch) : fHeader(NULL), fLength(fileLength), @@ -1652,7 +1656,10 @@ void DyldInfoPrinter::printSharedRegionInfo() uint64_t toOffsetCount = read_uleb128(p, infoEnd); const macho_section

* fromSection = fSections[fromSectionIndex]; const macho_section

* toSection = fSections[toSectionIndex]; - printf("from sect=%s, to sect=%s, count=%lld:\n", fromSection->sectname(), toSection->sectname(), toOffsetCount); + char fromSectionName[20]; + strncpy(fromSectionName, fromSection->sectname(), 16); + fromSectionName[16] = '\0'; + printf("from sect=%s/%s, to sect=%s/%s, count=%lld:\n", fromSection->segname(), fromSectionName, toSection->segname(), toSection->sectname(), toOffsetCount); uint64_t toSectionOffset = 0; const char* lastFromSymbol = NULL; for (uint64_t j=0; j < toOffsetCount; ++j) { @@ -1933,6 +1940,7 @@ arm64::P::uint_t DyldInfoPrinter::relocBase() } #endif + template <> const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) { @@ -1994,6 +2002,7 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) } #endif + template void DyldInfoPrinter::printRelocRebaseInfo() { @@ -2324,7 +2333,7 @@ static void dump(const char* path) if ( DyldInfoPrinter::validFile(p + offset) ) DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); else - throw "in universal file, arm64 slice does not contain arm mach-o"; + throw "in universal file, arm64 slice does not contain arm64 mach-o"; break; #endif default: diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 0465cd5..97138e8 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -258,13 +258,15 @@ bool MachOChecker::validFile(const uint8_t* fileContent) } #endif + +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } - template <> x86::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) { @@ -290,6 +292,13 @@ arm64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_ } +template <> +ppc::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) { @@ -343,7 +352,6 @@ const char* MachOChecker::archName() } - template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path, const char* verifierDstRoot) : fHeader(NULL), fLength(fileLength), fInstallName(NULL), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), @@ -661,8 +669,12 @@ void MachOChecker::checkLoadCommands() // verify encryption info if ( encryption_info != NULL ) { - if ( fHeader->filetype() != MH_EXECUTE ) - throw "LC_ENCRYPTION_INFO load command is only legal in main executables"; + switch ( fHeader->filetype() ) { + case MH_EXECUTE: case MH_DYLIB: case MH_BUNDLE: + break; // okay + default: + throw "LC_ENCRYPTION_INFO load command is not allowed in this file type"; + } if ( encryption_info->cryptoff() < (sizeof(macho_header

) + fHeader->sizeofcmds()) ) throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands"; if ( (encryption_info->cryptoff() % 4096) != 0 ) @@ -712,8 +724,6 @@ void MachOChecker::checkLoadCommands() throw "string pool extends beyond __LINKEDIT"; if ( (symtab->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed throw "string pool start not pointer aligned"; - if ( (symtab->strsize() % sizeof(pint_t)) != 0 ) - throw "string pool size not a multiple of pointer size"; } break; case LC_DYSYMTAB: @@ -1131,6 +1141,7 @@ arm64::P::uint_t MachOChecker::relocBase() return fFirstWritableSegment->vmaddr(); } + template bool MachOChecker::addressInWritableSegment(pint_t address) { @@ -1274,6 +1285,7 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* re } #endif + template void MachOChecker::checkRelocations() { diff --git a/ld64/src/other/objcimageinfo.cpp b/ld64/src/other/objcimageinfo.cpp new file mode 100644 index 0000000..0609d3b --- /dev/null +++ b/ld64/src/other/objcimageinfo.cpp @@ -0,0 +1,414 @@ +/* + * 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@ + */ + +// objcimageinfo.cpp +// Print or edit ObjC image info bits. +// This is used to verify ld's handling of these bits +// for values that are not emitted by current compilers. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" + +#if __BIG_ENDIAN__ + typedef BigEndian CurrentEndian; + typedef LittleEndian OtherEndian; +#elif __LITTLE_ENDIAN__ + typedef LittleEndian CurrentEndian; + typedef BigEndian OtherEndian; +#else +# error unknown endianness +#endif + +static const bool debug = false; + +static bool processFile(const char *filename, uint32_t set, uint32_t clear); + +// fixme use objc/objc-abi.h instead +struct objc_image_info { + uint32_t version; + uint32_t flags; + + enum : uint32_t { + IsReplacement = 1<<0, // used for Fix&Continue, now ignored + SupportsGC = 1<<1, // image supports GC + RequiresGC = 1<<2, // image requires GC + OptimizedByDyld = 1<<3, // image is from an optimized shared cache + CorrectedSynthesize = 1<<4, // used for an old workaround, now ignored + IsSimulated = 1<<5, // image compiled for a simulator platform + HasCategoryClassProperties = 1<<6, // class properties in category_t + + SwiftVersionMask = 0xff << 8 // Swift ABI version + }; +}; + +// objc_image_info flags and their names +static const struct { + const char *name; + uint32_t value; +} Flags[] = { + { "supports-gc", objc_image_info::SupportsGC }, + { "requires-gc", objc_image_info::RequiresGC }, + { "has-category-class-properties", objc_image_info::HasCategoryClassProperties }, + { 0, 0 } +}; + +static void usage(const char *self) +{ + printf("usage: %s [+FLAG|-FLAG ...] file ...\n", self); + printf("Use +FLAG to set and -FLAG to clear.\n"); + printf("Known flags: "); + for (int i = 0; Flags[i].name != 0; i++) { + printf("%s ", Flags[i].name); + } + printf("\n"); +} + +static uint32_t flagForName(const char *name) +{ + for (int i = 0; Flags[i].name != 0; i++) { + if (0 == strcmp(Flags[i].name, name)) { + return Flags[i].value; + } + } + return 0; +} + +static const char *nameForFlag(uint32_t flag) +{ + for (int i = 0; Flags[i].name != 0; i++) { + if (Flags[i].value == flag) { + return Flags[i].name; + } + } + return 0; +} + +static void printFlags(uint32_t flags) +{ + printf("0x%x", flags); + + // Print flags and unknown bits + for (int i = 0; i < 24; i++) { + uint32_t flag = 1<> shift; + if (swift > 0) { + printf(" swift-version=%u", swift); + } +} + +static bool isFlagArgument(const char *arg) +{ + return (arg && (arg[0] == '+' || arg[0] == '-')); +} + +int main(int argc, const char *argv[]) { + uint32_t set = 0; + uint32_t clear = 0; + + // Find flag arguments (which are +FLAG or -FLAG). + int i; + for (i = 1; i < argc && isFlagArgument(argv[i]); i++) { + const char *arg = argv[i]; + uint32_t flag = flagForName(arg+1); + if (flag) { + if (arg[0] == '+') { + set |= flag; + } else { + clear |= flag; + } + } else { + printf("error: unrecognized ObjC flag '%s'\n", arg); + usage(argv[0]); + return 1; + } + } + + // Complain if +FLAG and -FLAG are both set for some flag. + uint32_t overlap = set & clear; + if (overlap) { + printf("error: conflicting changes specified: "); + printFlags(overlap); + printf("\n"); + usage(argv[0]); + return 1; + } + + // Complain if there are no filenames. + if (i == argc) { + printf("error: no files specified\n"); + usage(argv[0]); + return 1; + } + + // Process files. + for (; i < argc; i++) { + if (!processFile(argv[i], set, clear)) return 1; + } + return 0; +} + + +// Segment and section names are 16 bytes and may be un-terminated. +static bool segnameEquals(const char *lhs, const char *rhs) +{ + return 0 == strncmp(lhs, rhs, 16); +} + +static bool segnameStartsWith(const char *segname, const char *prefix) +{ + return 0 == strncmp(segname, prefix, strlen(prefix)); +} + +static bool sectnameEquals(const char *lhs, const char *rhs) +{ + return segnameEquals(lhs, rhs); +} + + +template +static void dosect(const char *filename, uint8_t *start, macho_section

*sect, + uint32_t set, uint32_t clear) +{ + if (debug) printf("section %.16s from segment %.16s\n", + sect->sectname(), sect->segname()); + + if ((segnameStartsWith(sect->segname(), "__DATA") && + sectnameEquals(sect->sectname(), "__objc_imageinfo")) || + (segnameEquals(sect->segname(), "__OBJC") && + sectnameEquals(sect->sectname(), "__image_info"))) + { + objc_image_info *ii = (objc_image_info *)(start + sect->offset()); + uint32_t oldFlags = P::E::get32(ii->flags); + uint32_t newFlags = (oldFlags | set) & ~clear; + if (oldFlags != newFlags) { + P::E::set32(ii->flags, newFlags); + if (debug) printf("changed flags from 0x%x to 0x%x\n", + oldFlags, newFlags); + } + + printf("%s: ", filename); + printFlags(newFlags); + printf("\n"); + } +} + +template +static void doseg(const char *filename, + uint8_t *start, macho_segment_command

*seg, + uint32_t set, uint32_t clear) +{ + if (debug) printf("segment name: %.16s, nsects %u\n", + seg->segname(), seg->nsects()); + macho_section

*sect = (macho_section

*)(seg + 1); + for (uint32_t i = 0; i < seg->nsects(); ++i) { + dosect(filename, start, §[i], set, clear); + } +} + + +template +static bool parse_macho(const char *filename, uint8_t *buffer, + uint32_t set, uint32_t clear) +{ + macho_header

* mh = (macho_header

*)buffer; + uint8_t *cmds = (uint8_t *)(mh + 1); + for (uint32_t c = 0; c < mh->ncmds(); c++) { + macho_load_command

* cmd = (macho_load_command

*)cmds; + cmds += cmd->cmdsize(); + if (cmd->cmd() == LC_SEGMENT || cmd->cmd() == LC_SEGMENT_64) { + doseg(filename, buffer, (macho_segment_command

*)cmd, set, clear); + } + } + + return true; +} + + +static bool parse_macho(const char *filename, uint8_t *buffer, + uint32_t set, uint32_t clear) +{ + uint32_t magic = *(uint32_t *)buffer; + + switch (magic) { + case MH_MAGIC_64: + return parse_macho> + (filename, buffer, set, clear); + case MH_MAGIC: + return parse_macho> + (filename, buffer, set, clear); + case MH_CIGAM_64: + return parse_macho> + (filename, buffer, set, clear); + case MH_CIGAM: + return parse_macho> + (filename, buffer, set, clear); + default: + printf("error: file '%s' is not mach-o (magic %x)\n", filename, magic); + return false; + } +} + + +static bool parse_fat(const char *filename, uint8_t *buffer, size_t size, + uint32_t set, uint32_t clear) +{ + uint32_t magic; + + if (size < sizeof(magic)) { + printf("error: file '%s' is too small\n", filename); + return false; + } + + magic = *(uint32_t *)buffer; + if (magic != FAT_MAGIC && magic != FAT_CIGAM) { + /* Not a fat file */ + return parse_macho(filename, buffer, set, clear); + } else { + struct fat_header *fh; + uint32_t fat_magic, fat_nfat_arch; + struct fat_arch *archs; + + if (size < sizeof(struct fat_header)) { + printf("error: file '%s' is too small\n", filename); + return false; + } + + fh = (struct fat_header *)buffer; + fat_magic = OSSwapBigToHostInt32(fh->magic); + fat_nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch); + + if (size < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) { + printf("error: file '%s' is too small\n", filename); + return false; + } + + archs = (struct fat_arch *)(buffer + sizeof(struct fat_header)); + + /* Special case hidden CPU_TYPE_ARM64 */ + if (size >= (sizeof(struct fat_header) + (fat_nfat_arch + 1) * sizeof(struct fat_arch))) { + if (fat_nfat_arch > 0 + && OSSwapBigToHostInt32(archs[fat_nfat_arch].cputype) == CPU_TYPE_ARM64) { + fat_nfat_arch++; + } + } + /* End special case hidden CPU_TYPE_ARM64 */ + + if (debug) printf("%d fat architectures\n", fat_nfat_arch); + + for (uint32_t i = 0; i < fat_nfat_arch; i++) { + uint32_t arch_cputype = OSSwapBigToHostInt32(archs[i].cputype); + uint32_t arch_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); + uint32_t arch_offset = OSSwapBigToHostInt32(archs[i].offset); + uint32_t arch_size = OSSwapBigToHostInt32(archs[i].size); + + if (debug) printf("cputype %d cpusubtype %d\n", + arch_cputype, arch_cpusubtype); + + /* Check that slice data is after all fat headers and archs */ + if (arch_offset < (sizeof(struct fat_header) + fat_nfat_arch * sizeof(struct fat_arch))) { + printf("error: file is badly formed\n"); + return false; + } + + /* Check that the slice ends before the file does */ + if (arch_offset > size) { + printf("error: file '%s' is badly formed\n", filename); + return false; + } + + if (arch_size > size) { + printf("error: file '%s' is badly formed\n", filename); + return false; + } + + if (arch_offset > (size - arch_size)) { + printf("error: file '%s' is badly formed\n", filename); + return false; + } + + bool ok = parse_macho(filename, buffer + arch_offset, set, clear); + if (!ok) return false; + } + return true; + } +} + +static bool processFile(const char *filename, uint32_t set, uint32_t clear) +{ + if (debug) printf("file %s\n", filename); + int openPerm = O_RDONLY; + int mmapPerm = PROT_READ; + if (set || clear) { + openPerm = O_RDWR; + mmapPerm = PROT_READ | PROT_WRITE; + } + + int fd = open(filename, openPerm); + if (fd < 0) { + printf("error: open %s: %s\n", filename, strerror(errno)); + return false; + } + + struct stat st; + if (fstat(fd, &st) < 0) { + printf("error: fstat %s: %s\n", filename, strerror(errno)); + return false; + } + + void *buffer = mmap(NULL, (size_t)st.st_size, mmapPerm, + MAP_FILE|MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { + printf("error: mmap %s: %s\n", filename, strerror(errno)); + return false; + } + + bool result = + parse_fat(filename, (uint8_t *)buffer, (size_t)st.st_size, set, clear); + munmap(buffer, (size_t)st.st_size); + close(fd); + return result; +} diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index bc925fb..5b264d1 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -163,6 +163,7 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) } #endif + template <> bool UnwindPrinter::validFile(const uint8_t* fileContent) { @@ -756,6 +757,7 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, c } #endif + template <> void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) { @@ -872,6 +874,7 @@ const char* UnwindPrinter::personalityName(const macho_relocation_info const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) { @@ -1138,7 +1141,7 @@ static void dump(const char* path, const std::set& onlyArchs, bool s else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) { UnwindPrinter::make(p, length, path, showFunctionNames); } -#endif +#endif else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) { UnwindPrinter::make(p, length, path, showFunctionNames); } diff --git a/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index f0cbbf1..c3a7864 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -10,24 +10,26 @@ VALID_ARCHS ?= "i386 x86_64 armv6" MYDIR=$(shell cd ../../bin;pwd) -LD = ld -OBJECTDUMP = ObjectDump -MACHOCHECK = machocheck -OTOOL = xcrun otool -REBASE = rebase -DYLDINFO = dyldinfo +LD = ld +OBJECTDUMP = ObjectDump +OBJCIMAGEINFO = objcimageinfo +MACHOCHECK = machocheck +OTOOL = xcrun otool +REBASE = rebase +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}:${COMPILER_PATH} - LD_PATH = ${BUILT_PRODUCTS_DIR} - LD = ${BUILT_PRODUCTS_DIR}/ld - OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump - MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck - REBASE = ${BUILT_PRODUCTS_DIR}/rebase - UNWINDDUMP = ${BUILT_PRODUCTS_DIR}/unwinddump - DYLDINFO = ${BUILT_PRODUCTS_DIR}/dyldinfo + LD_PATH = ${BUILT_PRODUCTS_DIR} + LD = ${BUILT_PRODUCTS_DIR}/ld + OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump + OBJCIMAGEINFO = ${BUILT_PRODUCTS_DIR}/objcimageinfo + MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck + REBASE = ${BUILT_PRODUCTS_DIR}/rebase + UNWINDDUMP = ${BUILT_PRODUCTS_DIR}/unwinddump + DYLDINFO = ${BUILT_PRODUCTS_DIR}/dyldinfo else ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" # if run from Terminal inside unit-test directory @@ -35,13 +37,14 @@ else DEBUGDIR=$(shell cd ../../../build/Debug;pwd) PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${COMPILER_PATH} - LD_PATH = ${DEBUGDIR} - LD = ${DEBUGDIR}/ld - OBJECTDUMP = ${DEBUGDIR}/ObjectDump - MACHOCHECK = ${DEBUGDIR}/machocheck - REBASE = ${DEBUGDIR}/rebase - UNWINDDUMP = ${DEBUGDIR}/unwinddump - DYLDINFO = ${DEBUGDIR}/dyldinfo + LD_PATH = ${DEBUGDIR} + LD = ${DEBUGDIR}/ld + OBJECTDUMP = ${DEBUGDIR}/ObjectDump + OBJCIMAGEINFO = ${DEBUGDIR}/objcimageinfo + MACHOCHECK = ${DEBUGDIR}/machocheck + REBASE = ${DEBUGDIR}/rebase + UNWINDDUMP = ${DEBUGDIR}/unwinddump + DYLDINFO = ${DEBUGDIR}/dyldinfo else PATH := ${MYDIR}:${PATH}: COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: @@ -51,22 +54,23 @@ export PATH export COMPILER_PATH export GCC_EXEC_PREFIX=garbage +IOS_SDK = $(shell xcodebuild -sdk iphoneos.internal -version Path 2>/dev/null) +OSX_SDK = $(shell xcodebuild -sdk macosx.internal -version Path 2>/dev/null) ifeq ($(ARCH),ppc) - SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk + OSX_SDK = /Developer/SDKs/MacOSX10.6.sdk endif -CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} -mmacosx-version-min=10.8 +CC = $(shell xcrun -find clang) -arch ${ARCH} -mmacosx-version-min=10.8 -isysroot $(OSX_SDK) CCFLAGS = -Wall +LDFLAGS = -syslibroot $(OSX_SDK) 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 = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} +CXX = $(shell xcrun -find clang++) -arch ${ARCH} -isysroot $(OSX_SDK) CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos.internal -version Path 2>/dev/null) - ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) override FILEARCH = arm diff --git a/ld64/unit-tests/test-cases/alias-basic/Makefile b/ld64/unit-tests/test-cases/alias-basic/Makefile index e457aeb..74275ac 100644 --- a/ld64/unit-tests/test-cases/alias-basic/Makefile +++ b/ld64/unit-tests/test-cases/alias-basic/Makefile @@ -27,8 +27,6 @@ include ${TESTROOT}/include/common.makefile # Verify that code and data references can be redirected via aliases. # -CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang - run: all all: diff --git a/ld64/unit-tests/test-cases/alias-objects/Makefile b/ld64/unit-tests/test-cases/alias-objects/Makefile index 7b7667d..4f4ec6e 100644 --- a/ld64/unit-tests/test-cases/alias-objects/Makefile +++ b/ld64/unit-tests/test-cases/alias-objects/Makefile @@ -31,7 +31,6 @@ include ${TESTROOT}/include/common.makefile # No differences means this test passes # -CC=/Volumes/my/src/puzzlebox/build/Debug+Asserts/bin/clang run: all diff --git a/ld64/unit-tests/test-cases/archive-image_info/Makefile b/ld64/unit-tests/test-cases/archive-image_info/Makefile index ae87948..a13a089 100644 --- a/ld64/unit-tests/test-cases/archive-image_info/Makefile +++ b/ld64/unit-tests/test-cases/archive-image_info/Makefile @@ -43,7 +43,7 @@ all: ${CC} ${CCFLAGS} main.m -c -o main.o libtool -static main.o -o libmain.a ${CC} ${CCFLAGS} libmain.a -o main -framework Foundation - size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} + size -m -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile b/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile index c8b16ee..2b1df9f 100644 --- a/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile +++ b/ld64/unit-tests/test-cases/cfstring-coalesce/Makefile @@ -43,10 +43,10 @@ run: all all: ${CC} ${CCFLAGS} foo.c bar.c -o foo -framework CoreFoundation -dead_strip - size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} + size -m -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} # now try with -fwritable-strings ${CC} ${CCFLAGS} foo.c bar.c -o foo_writable -framework CoreFoundation -dead_strip -fwritable-strings 2>/dev/null - size -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE}" | ${PASS_IFF_STDIN} + size -m -l foo_writable | grep "__cfstring: ${CFSTRING_SIZE}" | ${PASS_IFF_STDIN} clean: rm -rf foo foo_writable diff --git a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile index 272cb9f..a707ba5 100644 --- a/ld64/unit-tests/test-cases/cfstring-utf16/Makefile +++ b/ld64/unit-tests/test-cases/cfstring-utf16/Makefile @@ -42,8 +42,8 @@ run: all all: ${CC} ${CCFLAGS} foo.m bar.m -o foo -framework CoreFoundation -dead_strip - size -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} - size -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY} + size -m -l foo | grep "__cfstring: ${CFSTRING_SIZE}" | ${FAIL_IF_EMPTY} + size -m -l foo | grep "__ustring: ${USTRING_SIZE}" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} foo clean: diff --git a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile index ff0919b..8679b88 100644 --- a/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile +++ b/ld64/unit-tests/test-cases/cstring-alt-segment/Makefile @@ -32,8 +32,8 @@ run: all all: ${CC} ${CCFLAGS} main.c custom.s -o main - size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} - size -l main | grep -A2 __MYSEG | grep __cstring | grep " 10 " | ${FAIL_IF_EMPTY} + size -m -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} + size -m -l main | grep -A2 __MYSEG | grep __cstring | grep " 10 " | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/cstring-custom-section/Makefile b/ld64/unit-tests/test-cases/cstring-custom-section/Makefile index 628d045..b3e8f41 100644 --- a/ld64/unit-tests/test-cases/cstring-custom-section/Makefile +++ b/ld64/unit-tests/test-cases/cstring-custom-section/Makefile @@ -34,12 +34,12 @@ all: ${CC} ${CCFLAGS} -c foo.s -o foo.o ${CC} ${CCFLAGS} -c bar.s -o bar.o ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o - size -l foobar.o | grep "(__TEXT, __cstring): 13" | ${FAIL_IF_EMPTY} - size -l foobar.o | grep "(__TEXT, __mystring): 15" | ${FAIL_IF_EMPTY} + size -m -l foobar.o | grep "(__TEXT, __cstring): 13" | ${FAIL_IF_EMPTY} + size -m -l foobar.o | grep "(__TEXT, __mystring): 15" | ${FAIL_IF_EMPTY} otool -lv foobar.o | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY} ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib - size -l libfoobar.dylib | grep "__cstring: 13" | ${FAIL_IF_EMPTY} - size -l libfoobar.dylib | grep "__mystring: 15" | ${FAIL_IF_EMPTY} + size -m -l libfoobar.dylib | grep "__cstring: 13" | ${FAIL_IF_EMPTY} + size -m -l libfoobar.dylib | grep "__mystring: 15" | ${FAIL_IF_EMPTY} otool -lv libfoobar.dylib | grep -A10 __mystring | grep S_CSTRING_LITERALS | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} libfoobar.dylib diff --git a/ld64/unit-tests/test-cases/dead_strip/Makefile b/ld64/unit-tests/test-cases/dead_strip/Makefile index a274694..62c1bbe 100644 --- a/ld64/unit-tests/test-cases/dead_strip/Makefile +++ b/ld64/unit-tests/test-cases/dead_strip/Makefile @@ -35,7 +35,7 @@ run: all all: ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} - size -l main-${ARCH} | grep __PAGEZERO | ${FAIL_IF_EMPTY} + size -m -l main-${ARCH} | grep __PAGEZERO | ${FAIL_IF_EMPTY} ${FAIL_IF_BAD_MACHO} main-${ARCH} nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} diff --git a/ld64/unit-tests/test-cases/dirty-data-alt-entry/1.dirty b/ld64/unit-tests/test-cases/dirty-data-alt-entry/1.dirty new file mode 100644 index 0000000..cf6a8eb --- /dev/null +++ b/ld64/unit-tests/test-cases/dirty-data-alt-entry/1.dirty @@ -0,0 +1 @@ +_sym1 diff --git a/ld64/unit-tests/test-cases/dirty-data-alt-entry/2.dirty b/ld64/unit-tests/test-cases/dirty-data-alt-entry/2.dirty new file mode 100644 index 0000000..3da0279 --- /dev/null +++ b/ld64/unit-tests/test-cases/dirty-data-alt-entry/2.dirty @@ -0,0 +1 @@ +_sym2 diff --git a/ld64/unit-tests/test-cases/dirty-data-alt-entry/Makefile b/ld64/unit-tests/test-cases/dirty-data-alt-entry/Makefile new file mode 100644 index 0000000..621a997 --- /dev/null +++ b/ld64/unit-tests/test-cases/dirty-data-alt-entry/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2016 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that -dirty_data_list will move alt-entry symbols +## + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib test.s -o libtest1.dylib -Wl,-dirty_data_list,1.dirty + ${FAIL_IF_BAD_MACHO} libtest1.dylib + nm -nm libtest1.dylib | grep __DATA_DIRTY | grep _sym1 | ${FAIL_IF_EMPTY} + nm -nm libtest1.dylib | grep __DATA_DIRTY | grep _sym2 | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} -dynamiclib test.s -o libtest2.dylib -Wl,-dirty_data_list,2.dirty + ${FAIL_IF_BAD_MACHO} libtest2.dylib + nm -nm libtest2.dylib | grep __DATA_DIRTY | grep _sym1 | ${FAIL_IF_EMPTY} + nm -nm libtest2.dylib | grep __DATA_DIRTY | grep _sym2 | ${FAIL_IF_EMPTY} + ${PASS_IFF} /usr/bin/true + + +clean: + + rm -rf libtest1.dylib libtest2.dylib diff --git a/ld64/unit-tests/test-cases/dirty-data-alt-entry/test.s b/ld64/unit-tests/test-cases/dirty-data-alt-entry/test.s new file mode 100644 index 0000000..abac28d --- /dev/null +++ b/ld64/unit-tests/test-cases/dirty-data-alt-entry/test.s @@ -0,0 +1,23 @@ + + + + + + .data +_foo: .quad 0,0,0 + +# note: the .alt_entry means sym2 needs to stay pinned to sym1 + .globl _sym1 + .globl _sym2 + .alt_entry _sym2 + +_sym1: .quad 0,0 +_sym2: .quad 0,0 + + .globl _sym3 +_sym3: .quad 0,0 + + + .subsections_via_symbols + + diff --git a/ld64/unit-tests/test-cases/dwarf-ignore/Makefile b/ld64/unit-tests/test-cases/dwarf-ignore/Makefile index 94b026c..1427912 100644 --- a/ld64/unit-tests/test-cases/dwarf-ignore/Makefile +++ b/ld64/unit-tests/test-cases/dwarf-ignore/Makefile @@ -33,7 +33,7 @@ run: all all: ${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} + size -m -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} clean: rm -rf dwarf-hello-* diff --git a/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile b/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile index 866d178..cc23634 100644 --- a/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile +++ b/ld64/unit-tests/test-cases/dwarf-strip-objc/Makefile @@ -21,7 +21,7 @@ run: all all: ${CC} ${CCFLAGS} -gdwarf-2 hello.m -c -o hello.o ${LD} -r -S hello.o -o hello-r.o - size -l hello-r.o | grep ${IMAGE_INFO} | ${PASS_IFF_STDIN} + size -m -l hello-r.o | grep ${IMAGE_INFO} | ${PASS_IFF_STDIN} diff --git a/ld64/unit-tests/test-cases/eh-coalescing/Makefile b/ld64/unit-tests/test-cases/eh-coalescing/Makefile index 0c7fb32..7f61116 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing/Makefile +++ b/ld64/unit-tests/test-cases/eh-coalescing/Makefile @@ -44,7 +44,7 @@ all: # verify .eh symbol is missing or is from bar.o (file 3) grep '\[ 2\] __Z4funcv.eh' libfoobar.map | ${FAIL_IF_STDIN} # verify no LSDA - size -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY} + size -m -l libfoobar.dylib | grep __gcc_except_tab | ${PASS_IFF_EMPTY} clean: rm foo.o foo2.o bar.o libfoobar.map libfoobar.dylib diff --git a/ld64/unit-tests/test-cases/lto-archive-objc/Makefile b/ld64/unit-tests/test-cases/lto-archive-objc/Makefile new file mode 100644 index 0000000..419c199 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-archive-objc/Makefile @@ -0,0 +1,23 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that -ObjC works with LTO +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o + ${CC} ${CCFLAGS} -flto bar.m -c -o bar.o + ${CC} ${CCFLAGS} -flto baz.m -c -o baz.o + libtool -static foo.o bar.o baz.o -o libstuff.a + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -o main libstuff.a -framework Foundation -ObjC + nm main | grep _Bar | ${FAIL_IF_EMPTY} + nm main | grep MyCategory | grep doit | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main *.o libstuff.a main.o main diff --git a/ld64/unit-tests/test-cases/lto-archive-objc/bar.m b/ld64/unit-tests/test-cases/lto-archive-objc/bar.m new file mode 100644 index 0000000..0de8ec3 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-archive-objc/bar.m @@ -0,0 +1,9 @@ + +#include + +@interface Bar : NSObject +@end + +@implementation Bar +@end + diff --git a/ld64/unit-tests/test-cases/lto-archive-objc/baz.m b/ld64/unit-tests/test-cases/lto-archive-objc/baz.m new file mode 100644 index 0000000..913e9c2 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-archive-objc/baz.m @@ -0,0 +1,11 @@ +#include + +@interface NSObject(MyCategory) +- (void) doit; +@end + +@implementation NSObject(MyCategory) +- (void) doit +{ +} +@end diff --git a/ld64/unit-tests/test-cases/lto-archive-objc/foo.c b/ld64/unit-tests/test-cases/lto-archive-objc/foo.c new file mode 100644 index 0000000..53d0713 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-archive-objc/foo.c @@ -0,0 +1,8 @@ + +extern void neverFindMe(); + +__attribute__((constructor)) +void myinit() +{ + neverFindMe(); +} diff --git a/ld64/unit-tests/test-cases/lto-archive-objc/main.c b/ld64/unit-tests/test-cases/lto-archive-objc/main.c new file mode 100644 index 0000000..b2ccbdc --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-archive-objc/main.c @@ -0,0 +1,6 @@ + +int main() +{ + return 0; +} + diff --git a/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile b/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile index 1a6f059..2ab56f7 100644 --- a/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile +++ b/ld64/unit-tests/test-cases/lto-objc-image-info/Makefile @@ -43,7 +43,7 @@ run: all all: ${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} + size -m -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/lto-rename_section/Makefile b/ld64/unit-tests/test-cases/lto-rename_section/Makefile index 17ba9c3..8ca0a2a 100644 --- a/ld64/unit-tests/test-cases/lto-rename_section/Makefile +++ b/ld64/unit-tests/test-cases/lto-rename_section/Makefile @@ -39,11 +39,11 @@ all: -Wl,-rename_section,__TEXT,__text,__ROM,__code \ -Wl,-rename_section,__TEXT,__eh_frame,__ROM,__eh_frame \ -Wl,-rename_section,__TEXT,__cstring,__ROM,__const - size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} - size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + size -m -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -m -l main.preload | grep __DATA | ${FAIL_IF_STDIN} nm -m main.preload | grep __ROM | grep __code | grep _entry | ${FAIL_IF_EMPTY} nm -m main.preload | grep __RAM | grep __vars | grep _mystring | ${FAIL_IF_EMPTY} - size -l main.preload | grep __ROM | ${PASS_IFF_STDIN} + size -m -l main.preload | grep __ROM | ${PASS_IFF_STDIN} diff --git a/ld64/unit-tests/test-cases/lto-rename_segment/Makefile b/ld64/unit-tests/test-cases/lto-rename_segment/Makefile index 2f3286c..c3cc884 100644 --- a/ld64/unit-tests/test-cases/lto-rename_segment/Makefile +++ b/ld64/unit-tests/test-cases/lto-rename_segment/Makefile @@ -39,13 +39,13 @@ all: -Wl,-rename_segment,__DATA,__RAM \ -Wl,-rename_section,__DATA,__data_extra,__RAM2,__data \ -Wl,-exported_symbol,_get - size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} - size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + size -m -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -m -l main.preload | grep __DATA | ${FAIL_IF_STDIN} nm -m main.preload | grep __ROM | grep __text | grep _entry | ${FAIL_IF_EMPTY} nm -m main.preload | grep __ROM | grep __text | grep _get | ${FAIL_IF_EMPTY} nm -m main.preload | grep __RAM | grep __data | grep _mystring | ${FAIL_IF_EMPTY} nm -m main.preload | grep __RAM2 | grep __data | grep _param | ${FAIL_IF_EMPTY} - size -l main.preload | grep __ROM | ${PASS_IFF_STDIN} + size -m -l main.preload | grep __ROM | ${PASS_IFF_STDIN} clean: diff --git a/ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile b/ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile index bec9e76..9841cf3 100755 --- a/ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile +++ b/ld64/unit-tests/test-cases/merge_zero_fill_sections/Makefile @@ -31,9 +31,9 @@ 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} + size -m -l main | grep __bss | ${FAIL_IF_STDIN} + size -m -l main | grep __common | ${FAIL_IF_STDIN} + size -m -l main | grep __zerofill | grep "offset 0" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile b/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile index 8db13a7..c876c38 100755 --- a/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile +++ b/ld64/unit-tests/test-cases/no_zero_fill_sections/Makefile @@ -31,8 +31,8 @@ run: all all: ${CC} ${CCFLAGS} main.c -o main -Wl,-no_zero_fill_sections - size -l main | grep __bss | grep "offset 0" | ${FAIL_IF_STDIN} - size -l main | grep __common | grep "offset 0" | ${FAIL_IF_STDIN} + size -m -l main | grep __bss | grep "offset 0" | ${FAIL_IF_STDIN} + size -m -l main | grep __common | grep "offset 0" | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/objc-abi/Makefile b/ld64/unit-tests/test-cases/objc-abi/Makefile index d563cd9..4640112 100644 --- a/ld64/unit-tests/test-cases/objc-abi/Makefile +++ b/ld64/unit-tests/test-cases/objc-abi/Makefile @@ -39,11 +39,10 @@ all: all-i386: ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 -Wno-objc-root-class - size -l test1 | grep __image_info | ${FAIL_IF_EMPTY} + size -m -l test1 | grep __image_info | ${FAIL_IF_EMPTY} ${CC} ${CCFLAGS} test.m -Wno-objc-root-class -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 - size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY} + size -m -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} test2 - - + clean: rm -rf test1 test2 diff --git a/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/Makefile b/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/Makefile new file mode 100644 index 0000000..54697fb --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/Makefile @@ -0,0 +1,132 @@ +## +# 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 +SHELL = bash + +# +# Verify handling of object files with mismatched category class properties. +# + +LD_R = ${LD} -arch ${ARCH} -r + +# check output of objcimageinfo +FAIL_IF_OLD = grep -v has-category-class-properties | ${FAIL_IF_STDIN} +FAIL_IF_NEW = grep has-category-class-properties | ${FAIL_IF_STDIN} +REALLY_FAIL_IF_NEW = grep has-category-class-properties | ${FAIL_IF_STDIN} + +# check diagnostics from ld +FAIL_IF_DIAGNOSTICS = ${FAIL_IF_STDIN} +FAIL_IF_NO_DIAGNOSTICS = egrep 'warning.*category metadata' | ${FAIL_IF_EMPTY} + +# Hack: Old ABI doesn't need the has-category-class-properties bit. +# There should instead be no diagnostics and no "OLD" output anywhere. +ifeq (${ARCH},i386) + FAIL_IF_NEW = ${FAIL_IF_OLD} + FAIL_IF_NO_DIAGNOSTICS = ${FAIL_IF_DIAGNOSTICS} + # REALLY_FAIL_IF_NEW is unchanged to sanity-check objcimageinfo itself. +endif + +all: + # Generate files with has-category-class-properties bit ("NEW"). + ${CC} ${CCFLAGS} -c class.m -o class.o + ${CC} ${CCFLAGS} -c -DCATEGORY=1 -DCLASS_PROPERTY=1 cat.m -o cat-with-class-prop.o + ${CC} ${CCFLAGS} -c -DCATEGORY=1 -DCLASS_PROPERTY=0 cat.m -o cat-without-class-prop.o + ${CC} ${CCFLAGS} -c -DCATEGORY=0 -DCLASS_PROPERTY=0 cat.m -o nocat.o + ${CC} ${CCFLAGS} -c -DCATEGORY=0 -DCLASS_PROPERTY=0 -x c cat.m -o noobjc.o + ${OBJCIMAGEINFO} class.o cat-with-class-prop.o cat-without-class-prop.o nocat.o | ${FAIL_IF_OLD} + # nocat.o must have objc_image_info; noobjc.o must not + size -m -l nocat.o | egrep '(__image_info|__objc_imageinfo)' | ${FAIL_IF_EMPTY} + size -m -l noobjc.o | egrep '(__image_info|__objc_imageinfo)' | ${FAIL_IF_STDIN} + + # Generate files without has-category-class-properties bit ("OLD"). + cp -f cat-without-class-prop.o old-cat.o + cp -f nocat.o old-nocat.o + cp -f class.o old-class.o + ${OBJCIMAGEINFO} -has-category-class-properties old-cat.o old-class.o old-nocat.o | ${REALLY_FAIL_IF_NEW} + + # Link each pair of class and category. + # Verify diagnostics and output's has-category-class-properties bit. + + ############ + # Class: NEW + + # Category: not OLD. No diagnostics. Result should be marked NEW. + # Category: OLD but no category. No diagnostics. Result should be marked NEW. + ${LD_R} class.o cat-with-class-prop.o -o class__cat-with-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} class.o cat-without-class-prop.o -o class__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} class.o nocat.o -o class__nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} class.o noobjc.o -o class__noobjc.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} class.o old-nocat.o -o class__old-nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} class__cat-with-class-prop.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} class__cat-without-class-prop.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} class__nocat.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} class__noobjc.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} class__old-nocat.o | ${FAIL_IF_OLD} + + # Category: OLD. No diagnostics. Result should be marked OLD. + ${LD_R} class.o old-cat.o -o class__old-cat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} class__old-cat.o | ${FAIL_IF_NEW} + + # Two categories: OLD + NEW with class properties. Has diagnostics. Result should be marked OLD. + ${LD_R} class.o old-cat.o cat-with-class-prop.o -o class__old-cat__cat-with-class-prop.o 2>&1 | ${FAIL_IF_NO_DIAGNOSTICS} + ${OBJCIMAGEINFO} class__old-cat__cat-with-class-prop.o | ${FAIL_IF_NEW} + + # Two categories: OLD + NEW without class properties. No diagnostics. Result should be marked OLD. + ${LD_R} class.o old-cat.o cat-without-class-prop.o -o class__old-cat__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} class__old-cat__cat-without-class-prop.o | ${FAIL_IF_NEW} + + + ############ + # Class: OLD + + # Category: OLD. No diagnostics. Result should be marked OLD. + ${LD_R} old-class.o old-cat.o -o old-class__old-cat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} old-class__old-cat.o | ${FAIL_IF_NEW} + + # Category: none. No diagnostics. Result should be marked NEW. + ${LD_R} old-class.o nocat.o -o old-class__nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} old-class.o noobjc.o -o old-class__noobjc.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} old-class.o old-nocat.o -o old-class__old-nocat.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} old-class__nocat.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} old-class__noobjc.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} old-class__old-nocat.o | ${FAIL_IF_OLD} + + # Category: NEW. No diagnostics. Result should be marked NEW. + ${LD_R} old-class.o cat-without-class-prop.o -o old-class__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${LD_R} old-class.o cat-with-class-prop.o -o old-class__cat-with-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} old-class__cat-without-class-prop.o | ${FAIL_IF_OLD} + ${OBJCIMAGEINFO} old-class__cat-with-class-prop.o | ${FAIL_IF_OLD} + + # Two categories: OLD + NEW with class properties. Has diagnostics. Result should be marked OLD. + ${LD_R} old-class.o old-cat.o cat-with-class-prop.o -o old-class__old-cat__cat-with-class-prop.o 2>&1 | ${FAIL_IF_NO_DIAGNOSTICS} + ${OBJCIMAGEINFO} old-class__old-cat__cat-with-class-prop.o | ${FAIL_IF_NEW} + + # Two categories: OLD + NEW without class properties. No diagnostics. Result should be marked OLD. + ${LD_R} old-class.o old-cat.o cat-without-class-prop.o -o old-class__old-cat__cat-without-class-prop.o 2>&1 | ${FAIL_IF_DIAGNOSTICS} + ${OBJCIMAGEINFO} old-class__old-cat__cat-without-class-prop.o | ${FAIL_IF_NEW} + + ${PASS_IFF} true + +clean: + rm -rf *.o diff --git a/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/cat.m b/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/cat.m new file mode 100644 index 0000000..54aa451 --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/cat.m @@ -0,0 +1,24 @@ +#if CATEGORY + +#include + +@interface Foo : NSObject +@end + +@interface Foo(mycat) +@property(readonly) int instanceProperty; +#if CLASS_PROPERTY +@property(class, readonly) int classProperty; +#endif +@end + +@implementation Foo(mycat) +-(int) instanceProperty { return 0; } ++(int) classProperty { return 0; } +@end + +#else + +int x = 0; + +#endif diff --git a/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/class.m b/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/class.m new file mode 100644 index 0000000..065450c --- /dev/null +++ b/ld64/unit-tests/test-cases/objc-category-class-property-mismatch/class.m @@ -0,0 +1,9 @@ +#include + +@interface Foo : NSObject +@end + + +@implementation Foo +@end + diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile index be39fa7..cfd2cc3 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -26,28 +26,24 @@ include ${TESTROOT}/include/common.makefile # # Verify optimization where categories are merged into classes # -OPTIONS = - ifeq ($(ARCH),i386) - OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ + ALL = all-noopt +else + ALL = all-opt endif -all: all-${ARCH} +all: ${ALL} -all-ppc: +# For platforms that do not perform category optimization +all-noopt: ${PASS_IFF} true -all-i386: all-rest -all-x86_64: all-rest -all-armv6: all-rest -all-armv7: all-rest - -all-rest: +# For platforms that optimize categories +all-opt: # check optimization of category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m -framework Foundation -o libfoo.dylib - size -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY} + size -m -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} libfoo.dylib - - + clean: rm -rf libfoo.dylib diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile index 37c69f1..4783f04 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile @@ -26,51 +26,43 @@ include ${TESTROOT}/include/common.makefile # # Verify optimization where categories are merged into classes # -OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ + ALL = all-noopt +else + ALL = all-opt endif -ifeq ($(ARCH),arm) - OPTIONS = -isysroot=${IOS_SDK} -endif - -all: all-${ARCH} +all: ${ALL} -all-ppc: +# For platforms that do not perform category optimization +all-noopt: ${PASS_IFF} true -all-i386: all-rest -all-x86_64: all-rest -all-armv6: all-rest -all-armv7: all-rest - -all-rest: +# For platforms that optimize categories +all-opt: # check optimization can be turned off ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -Wl,-no_objc_category_merging -o libno.dylib - size -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN} + size -m -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN} otool -ov libno.dylib | grep -A17 __objc_classlist | grep -A16 '_OBJC_CLASS_$$_Foo' | grep "count 1" | ${FAIL_IF_EMPTY} # check optimization of category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -o libfoo.dylib - size -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + size -m -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 4" | ${FAIL_IF_EMPTY} # check optimization of protocol and category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROTOCOLS -framework Foundation -o libfoo2.dylib - size -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + size -m -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo2.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} # check optimization of properties and category methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROPERTIES -framework Foundation -o libfoo3.dylib - size -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + size -m -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo3.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} # check optimization of category methods and no base methods ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DNO_BASE_METHODS -framework Foundation -o libfoo4.dylib - size -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + size -m -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} otool -ov libfoo4.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 3" | ${FAIL_IF_EMPTY} otool -ov libfoo4.dylib | grep -A20 "Meta Class" | grep "count 2" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} libfoo3.dylib - - - + clean: rm -rf lib*.dylib diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m b/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m index 11c170b..3652fca 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m +++ b/ld64/unit-tests/test-cases/objc-category-optimize/cat1.m @@ -14,6 +14,8 @@ +(void) class_method_mycat; #if PROPERTIES @property(readonly) int property1; @property(readonly) int property2; + @property(class,readonly) int property3; + @property(class,readonly) int property4; #endif @end @@ -24,6 +26,8 @@ +(void) class_method_mycat {} #if PROPERTIES -(int) property1 { return 0; } -(int) property2 { return 0; } + +(int) property3 { return 0; } + +(int) property4 { return 0; } #endif @end diff --git a/ld64/unit-tests/test-cases/objc-category-warning/Makefile b/ld64/unit-tests/test-cases/objc-category-warning/Makefile index 31d841b..e1835a0 100644 --- a/ld64/unit-tests/test-cases/objc-category-warning/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-warning/Makefile @@ -27,22 +27,20 @@ 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 = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ + ALL = all-noopt +else + ALL = all-opt endif -all: all-${FILEARCH} +all: ${ALL} -all-ppc: +# For platforms that do not perform category optimization +all-noopt: ${PASS_IFF} true -all-i386: all-rest -all-x86_64: all-rest -all-arm: all-rest - -all-rest: +# For platforms that optimize categories +all-opt: ${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} @@ -50,6 +48,6 @@ all-rest: 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 diff --git a/ld64/unit-tests/test-cases/objc-class-alias/Makefile b/ld64/unit-tests/test-cases/objc-class-alias/Makefile index 52840c9..0dda0e2 100644 --- a/ld64/unit-tests/test-cases/objc-class-alias/Makefile +++ b/ld64/unit-tests/test-cases/objc-class-alias/Makefile @@ -26,12 +26,9 @@ include ${TESTROOT}/include/common.makefile # # Verify -alias works with ObjC classes # -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' +CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo' +ifeq (${ARCH},i386) + CLASS_NAME_FOO = .objc_class_name_Foo endif @@ -42,6 +39,6 @@ all: ${DYLDINFO} -export test | grep ${CLASS_NAME_FOO} | awk '{ print $$1}' > foo.addr ${DYLDINFO} -export test | grep _MagicName | awk '{ print $$1}' > magic.addr ${PASS_IFF} diff foo.addr magic.addr - + clean: rm -rf test foo.addr magic.addr diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile index 13d1743..426d9b2 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile @@ -25,42 +25,53 @@ include ${TESTROOT}/include/common.makefile SHELL = bash # use bash shell so we can redirect just stderr +# +# Validate that the linker catches illegal combinations of .o files +# compiled with different GC settings. +# -IMAGE_INFO = "__image_info" +IMAGE_INFO = "__objc_imageinfo" +ifeq ($(ARCH),i386) + IMAGE_INFO = "__image_info" +endif +ifeq ($(ARCH),i386) + ALL = all-gc +else ifeq ($(ARCH),x86_64) - IMAGE_INFO = "__objc_imageinfo" + ALL = all-gc +else + ALL = all-nogc +endif endif -test: test-${FILEARCH} - -test-i386: test-macosx -test-x86_64: test-macosx -test-arm: test-good +all: ${ALL} -# -# Validate that the linker catches illegal combinations of .o files -# compiled with different GC settings. -# +# For platforms that do not support GC. +all-nogc: + ${PASS_IFF} true -test-macosx: +# For platforms that support GC. +all-gc: ${CC} ${CCFLAGS} foo.m -c -o foo.o ${FAIL_IF_BAD_OBJ} foo.o - ${CC} ${CCFLAGS} foo.m -c -o foo-gc.o -fobjc-gc - ${FAIL_IF_BAD_OBJ} foo-gc.o - - ${CC} ${CCFLAGS} foo.m -c -o foo-gc-only.o -fobjc-gc-only - ${FAIL_IF_BAD_OBJ} foo-gc-only.o - ${CC} ${CCFLAGS} bar.m -c -o bar.o ${FAIL_IF_BAD_OBJ} bar.o - ${CC} ${CCFLAGS} bar.m -c -o bar-gc.o -fobjc-gc - ${FAIL_IF_BAD_OBJ} bar-gc.o + # clang no longer builds GC so we create fake GC object files instead. + + cp -f foo.o foo-gc.o + ${OBJCIMAGEINFO} +supports-gc foo-gc.o - ${CC} ${CCFLAGS} bar.m -c -o bar-gc-only.o -fobjc-gc-only - ${FAIL_IF_BAD_OBJ} bar-gc-only.o + cp -f foo.o foo-gc-only.o + ${OBJCIMAGEINFO} +supports-gc +requires-gc foo-gc-only.o + + cp -f bar.o bar-gc.o + ${OBJCIMAGEINFO} +supports-gc bar-gc.o + + cp -f bar.o bar-gc-only.o + ${OBJCIMAGEINFO} +supports-gc +requires-gc bar-gc-only.o # check RR + RR -> RR ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation @@ -69,56 +80,56 @@ test-macosx: # check GC/RR + GC/RR -> GC/RR ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY} + ${OBJCIMAGEINFO} libfoobar.dylib | grep supports-gc | ${FAIL_IF_EMPTY} # check GC + GC -> GC ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} + ${OBJCIMAGEINFO} libfoobar.dylib | grep 'supports-gc requires-gc' | ${FAIL_IF_EMPTY} # check RR + GC/RR -> RR ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + ${OBJCIMAGEINFO} libfoobar.dylib | grep gc | ${FAIL_IF_STDIN} # check GC/RR + RR -> RR ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + ${OBJCIMAGEINFO} libfoobar.dylib | grep gc | ${FAIL_IF_STDIN} # check GC + GC/RR -> GC ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} + ${OBJCIMAGEINFO} libfoobar.dylib | grep 'supports-gc requires-gc' | ${FAIL_IF_EMPTY} # check RR + GC -> error ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log # check cmd line GC/RR, GC/RR + RR -> error ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc -framework Foundation 2> fail.log - + # check GC/RR + compaction ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12 | ${FAIL_IF_EMPTY} + ${OBJCIMAGEINFO} libfoobar.dylib | grep 0x12 | ${FAIL_IF_EMPTY} # check GC + compaction ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation - otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16 | ${FAIL_IF_EMPTY} + ${OBJCIMAGEINFO} libfoobar.dylib | grep 0x16 | ${FAIL_IF_EMPTY} # none + GC/RR-dylib -> none ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation - size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # none + GC-dylib -> none ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation - size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # none + RR-dylib -> none ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation - size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + size -m -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # check RR + GC-dylib -> error ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation @@ -130,8 +141,5 @@ test-macosx: ${PASS_IFF} true -test-good: - ${PASS_IFF} true - clean: rm -rf foo*.o bar*.o libfoobar.dylib fail.log libfoo.dylib libnone.dylib diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile index 76a7347..e51a63a 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/Makefile @@ -31,13 +31,10 @@ include ${TESTROOT}/include/common.makefile # # -SELECTOR_REFS = "__OBJC,__message_refs" +SELECTOR_REFS = "__DATA,__objc_selrefs" -ifeq ($(ARCH),x86_64) - SELECTOR_REFS = "__DATA,__objc_selrefs" -endif -ifeq ($(ARCH),armv6) - SELECTOR_REFS = "__DATA,__objc_selrefs" +ifeq ($(ARCH),i386) + SELECTOR_REFS = "__OBJC,__message_refs" endif @@ -52,6 +49,6 @@ all: ${OBJECTDUMP} -no_content test-r.o | grep -B3 -A6 ${SELECTOR_REFS} > test-r.dump diff test.dump test-r.dump | ${PASS_IFF_EMPTY} - + clean: rm -rf test.o test.dump test-r.o test-r.dump diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile b/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile index 2560a86..0e4d720 100644 --- a/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/Makefile @@ -31,12 +31,14 @@ include ${TESTROOT}/include/common.makefile run: all all: - mkdir -p hide + mkdir -p hide rhide ${CC} ${CCFLAGS} -dynamiclib foo.c -install_name '@loader_path/libfoo.dylib' -o hide/libfoo.dylib ${FAIL_IF_BAD_MACHO} hide/libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib bar.c -o hide/libbar.dylib -install_name '@executable_path/hide/libbar.dylib' ${FAIL_IF_BAD_MACHO} hide/libbar.dylib - ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide + ${CC} ${CCFLAGS} -dynamiclib baz.c -o rhide/libbaz.dylib -install_name '@rpath/libbaz.dylib' + ${FAIL_IF_BAD_MACHO} hide/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o hide/libwrap.dylib -Wl,-reexport-lfoo -Wl,-reexport-lbar -Lhide -Wl,-rpath,@loader_path/../rhide -Wl,-reexport-lbaz -Lrhide ${FAIL_IF_BAD_MACHO} hide/libwrap.dylib ${CC} ${CCFLAGS} main.c -o main hide/libwrap.dylib ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib hide/libwrap.dylib -Wl,-executable_path,`pwd`/main @@ -46,4 +48,4 @@ all: clean: - rm -rf hide libbar.dylib libfoo.dylib libwrap.dylib main libmain.dylib + rm -rf hide rhide main libmain.dylib diff --git a/ld64/unit-tests/test-cases/re-export-relative-paths/baz.c b/ld64/unit-tests/test-cases/re-export-relative-paths/baz.c new file mode 100644 index 0000000..5b832e8 --- /dev/null +++ b/ld64/unit-tests/test-cases/re-export-relative-paths/baz.c @@ -0,0 +1,5 @@ + +int baz(void) +{ + return 1; +} diff --git a/ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile b/ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile index 5b1f246..08613ed 100644 --- a/ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile +++ b/ld64/unit-tests/test-cases/sectcreate-dead_strip/Makefile @@ -32,8 +32,8 @@ run: all all: ${CC} ${CCFLAGS} main.c -o main -sectcreate __MYSEG __mysect sect_content -dead_strip - size -l main | grep __MYSEG | ${FAIL_IF_EMPTY} - size -l main | grep __mysect | ${FAIL_IF_EMPTY} + size -m -l main | grep __MYSEG | ${FAIL_IF_EMPTY} + size -m -l main | grep __mysect | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile b/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile index e9c09c4..9bd4405 100644 --- a/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile +++ b/ld64/unit-tests/test-cases/slow-x86-stubs/Makefile @@ -33,9 +33,9 @@ run: all all: ${CC} ${CCFLAGS} hello.c -o hello - size -l hello | grep __IMPORT | ${FAIL_IF_STDIN} + size -m -l hello | grep __IMPORT | ${FAIL_IF_STDIN} ${CC} ${CCFLAGS} hello.c -dynamiclib -o libhello.dylib - size -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN} + size -m -l libhello.dylib | grep __IMPORT | ${FAIL_IF_STDIN} ${PASS_IFF_GOOD_MACHO} hello clean: diff --git a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile index fcb7e7d..2288cf9 100644 --- a/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile +++ b/ld64/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile @@ -47,8 +47,8 @@ all: ${CC} ${CCFLAGS} all.o -dynamiclib -o dylib2 otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib1 >dylib1.pointers otool -X -s ${POINTER_SEGMENT} ${POINTER_SECTION} dylib2 >dylib2.pointers - size -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN} - size -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN} + size -m -l dylib1 | grep __IMPORT | ${FAIL_IF_STDIN} + size -m -l dylib2 | grep __IMPORT | ${FAIL_IF_STDIN} ${PASS_IFF} diff dylib1.pointers dylib2.pointers clean: diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/Makefile b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/Makefile new file mode 100644 index 0000000..6a5acbf --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2016 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 interaction of -section_rename and -move_to_r[ow]_segment +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo1.c -c -o foo1.o + ${CC} ${CCFLAGS} foo2.c -c -o foo2.o + libtool -static foo1.o foo2.o -o libfoo.a + ${LD} -arch ${ARCH} main.o -force_load libfoo.a \ + -preload -o main.preload \ + -e _main -trace_symbol_layout \ + -move_to_ro_segment __TMP spec.list \ + -rename_section __TMP __text __TEXT spec \ + -move_to_ro_segment __TMP2 foo.list \ + -rename_section __TMP2 __text __TEXT libfoo \ + -move_to_rw_segment __TMPD hot.list \ + -rename_section __TMPD __data __DATA hot \ + -rename_section __TMPD __common __DATA hot \ + > placement.log + nm -m main.preload | grep _s1 | grep __TEXT | grep spec | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _s2 | grep __TEXT | grep spec | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _def | grep __DATA | grep hot | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _com3 | grep __DATA | grep hot | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _foo1 | grep __TEXT | grep libfoo | ${FAIL_IF_EMPTY} + nm -m main.preload | grep _foo2 | grep __TEXT | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm -f main.preload main.o foo1.o foo2.o libfoo.a placement.log diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo.list b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo.list new file mode 100644 index 0000000..28656a5 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo.list @@ -0,0 +1,2 @@ +libfoo.a(*.o):* + diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo1.c b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo1.c new file mode 100644 index 0000000..c6d4367 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo1.c @@ -0,0 +1 @@ +void foo1() {} diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo2.c b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo2.c new file mode 100644 index 0000000..a5b7316 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/foo2.c @@ -0,0 +1 @@ +void foo2() {} diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/hot.list b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/hot.list new file mode 100644 index 0000000..704da52 --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/hot.list @@ -0,0 +1,4 @@ +_def +_com3 + + diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/main.c b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/main.c new file mode 100644 index 0000000..9b080ab --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/main.c @@ -0,0 +1,30 @@ + +void mm() +{ +} + +void s1() { + mm(); +} + +void s2() { + mm(); +} + +int main() +{ + s1(); + s2(); + return 0; +} + + +int abc = 10; +int def = 20; +int ghi = 30; + +int com1; +int com2; +int com3; +int com4; + diff --git a/ld64/unit-tests/test-cases/symbol-move-and-section-rename/spec.list b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/spec.list new file mode 100644 index 0000000..b07733b --- /dev/null +++ b/ld64/unit-tests/test-cases/symbol-move-and-section-rename/spec.list @@ -0,0 +1,4 @@ +_s1 +_s2 + + diff --git a/ld64/unit-tests/test-cases/symbol-section-move/Makefile b/ld64/unit-tests/test-cases/symbol-section-move/Makefile index 32a24b4..1bc26cc 100644 --- a/ld64/unit-tests/test-cases/symbol-section-move/Makefile +++ b/ld64/unit-tests/test-cases/symbol-section-move/Makefile @@ -44,14 +44,14 @@ all: nm -m main.preload | grep _foo | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} nm -m main.preload | grep _s1 | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} nm -m main.preload | grep _mylocal | grep __ROM1 | grep __text | ${FAIL_IF_EMPTY} - size -l main.preload | grep __cstring | ${FAIL_IF_STDIN} - size -l main.preload | grep mycstrings | ${FAIL_IF_EMPTY} - size -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} + size -m -l main.preload | grep __cstring | ${FAIL_IF_STDIN} + size -m -l main.preload | grep mycstrings | ${FAIL_IF_EMPTY} + size -m -l main.preload | grep __TEXT | ${FAIL_IF_STDIN} nm -m main.preload | grep _mm | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY} nm -m main.preload | grep _main | grep __ROM3 | grep __text | ${FAIL_IF_EMPTY} nm -m main.preload | grep _abc | grep __RAM1 | ${FAIL_IF_EMPTY} nm -m main.preload | grep _com | grep __RAM1 | ${FAIL_IF_EMPTY} - size -l main.preload | grep __DATA | ${FAIL_IF_STDIN} + size -m -l main.preload | grep __DATA | ${FAIL_IF_STDIN} nm -m main.preload | grep _def | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY} nm -m main.preload | grep _ghi | grep __RAM2 | grep mydata | ${FAIL_IF_EMPTY} ${PASS_IFF} true diff --git a/ld64/unit-tests/test-cases/tlv-dylib/foo.c b/ld64/unit-tests/test-cases/tlv-dylib/foo.c index a82eafb..034180f 100644 --- a/ld64/unit-tests/test-cases/tlv-dylib/foo.c +++ b/ld64/unit-tests/test-cases/tlv-dylib/foo.c @@ -1,6 +1,6 @@ // foo is an exported thread local variable -__thread int foo = 6; +__thread int foo[1024]; // _bar is an exported regular variable int bar = 5; diff --git a/ld64/unit-tests/test-cases/tlv-dylib/main.c b/ld64/unit-tests/test-cases/tlv-dylib/main.c index 5d0eb88..800356d 100644 --- a/ld64/unit-tests/test-cases/tlv-dylib/main.c +++ b/ld64/unit-tests/test-cases/tlv-dylib/main.c @@ -23,11 +23,11 @@ */ #if USE_FOO_WRONG - extern int foo; + extern int foo[]; #elif USE_BAR_WRONG extern __thread int bar; #else - extern __thread int foo; + extern __thread int foo[]; #endif @@ -38,7 +38,7 @@ int main() #elif USE_BAR_WRONG bar = 1; #else - foo = 1; + foo[0] = 1; #endif return 0; } diff --git a/ld64/unit-tests/test-cases/utf16-nul/Makefile b/ld64/unit-tests/test-cases/utf16-nul/Makefile index 3d845e5..b032fae 100644 --- a/ld64/unit-tests/test-cases/utf16-nul/Makefile +++ b/ld64/unit-tests/test-cases/utf16-nul/Makefile @@ -34,7 +34,7 @@ all: ${CC} ${CCFLAGS} withnul.s -c -o withnul.o ${CC} ${CCFLAGS} other.s -c -o other.o ${LD} -r -arch ${ARCH} withnul.o other.o -o all.o - size -l all.o | grep "__ustring): 44" | ${PASS_IFF_STDIN} + size -m -l all.o | grep "__ustring): 44" | ${PASS_IFF_STDIN} clean: diff --git a/ld64/unit-tests/test-cases/weak_import-disable/Makefile b/ld64/unit-tests/test-cases/weak_import-disable/Makefile new file mode 100644 index 0000000..3e68e08 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-disable/Makefile @@ -0,0 +1,29 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests -no_weak_imports +# + + +run: all + +all: + # check weak_import works and triggers error with -no_weak_imports + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main1a + nm -m main1a | grep _foo | grep weak | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main1b -DMAKE_FOO_WEAK_IMPORT=1 + nm -m main1b | grep _foo | grep weak | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main1c -DMAKE_FOO_WEAK_IMPORT=1 -Wl,-no_weak_imports 2>main1c.errors || true + grep 'ld:' main1c.errors | grep _foo | ${FAIL_IF_EMPTY} + # check $ld$sxx works with -no_weak_imports + ${CC} ${CCFLAGS} main2.c libfoo.dylib -o main2a -Wl,-no_weak_imports + ${CC} ${CCFLAGS} main2.c libfoo.dylib -o main2b -Wl,-no_weak_imports -mmacosx-version-min=10.7 2>main2b.errors || true + grep _bar main2b.errors | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main1a + +clean: + rm -rf libfoo.dylib main1a main1b main1c main1c.errors main2a main2b main2b.errors diff --git a/ld64/unit-tests/test-cases/weak_import-disable/foo.c b/ld64/unit-tests/test-cases/weak_import-disable/foo.c new file mode 100644 index 0000000..260977e --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-disable/foo.c @@ -0,0 +1,12 @@ + + + +void foo() {} + + +#define WEAK_IMPORT_FOR_10_7(sym) \ + extern const char sym##_tmp __asm("$ld$weak$os10.7$_" #sym ); const char sym##_tmp = 0; + +void bar() {} +WEAK_IMPORT_FOR_10_7(bar) + diff --git a/ld64/unit-tests/test-cases/weak_import-disable/main.c b/ld64/unit-tests/test-cases/weak_import-disable/main.c new file mode 100644 index 0000000..c276f1f --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-disable/main.c @@ -0,0 +1,15 @@ + +extern void foo() +#if MAKE_FOO_WEAK_IMPORT +__attribute__((weak_import)) +#endif +; + + +int main() +{ + foo(); + + return 0; +} + diff --git a/ld64/unit-tests/test-cases/weak_import-disable/main2.c b/ld64/unit-tests/test-cases/weak_import-disable/main2.c new file mode 100644 index 0000000..d908e87 --- /dev/null +++ b/ld64/unit-tests/test-cases/weak_import-disable/main2.c @@ -0,0 +1,12 @@ + +extern void bar(); + + + + +int main() +{ + bar(); + return 0; +} + diff --git a/ld64/unit-tests/test-cases/zero-fill3/Makefile b/ld64/unit-tests/test-cases/zero-fill3/Makefile index d16b9d0..b38061b 100644 --- a/ld64/unit-tests/test-cases/zero-fill3/Makefile +++ b/ld64/unit-tests/test-cases/zero-fill3/Makefile @@ -45,4 +45,4 @@ test-arm: ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null clean: - rm -rf test-* *.o *.s *.i + rm -rf test-* *.o *.s *.i *.bc From f722e2aabc2b05545b4085b799af09f4b6cc6010 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 23 Sep 2017 16:30:00 +0100 Subject: [PATCH 24/48] 274.2 --- ld64/src/ld/parsers/lto_file.cpp | 8 +-- .../test-cases/dwarf-debug-notes-lto/Makefile | 54 +++++++++++++++++++ .../test-cases/dwarf-debug-notes-lto/header.h | 8 +++ .../dwarf-debug-notes-lto/hello.cxx | 33 ++++++++++++ .../dwarf-debug-notes-lto/other.cxx | 27 ++++++++++ 5 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-lto/header.h create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-lto/hello.cxx create mode 100644 ld64/unit-tests/test-cases/dwarf-debug-notes-lto/other.cxx diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index a5ab821..650fd4e 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -1307,10 +1307,6 @@ bool Parser::optimizeThinLTO(const std::vector& files, // mach-o parsing is done in-memory, but need path for debug notes std::string tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o"; - // parse generated mach-o file into a MachOReader - ld::relocatable::File* machoFile = parseMachOFile((const uint8_t *)machOFile.Buffer, machOFile.Size, tmp_path, options, ordinal); - ordinal = ordinal.nextFileListOrdinal(); - // if needed, save temp mach-o file to specific location if ( options.tmpObjectFilePath != NULL ) { int fd = ::open(tmp_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); @@ -1323,6 +1319,10 @@ bool Parser::optimizeThinLTO(const std::vector& files, } } + // parse generated mach-o file into a MachOReader + ld::relocatable::File* machoFile = parseMachOFile((const uint8_t *)machOFile.Buffer, machOFile.Size, tmp_path, options, ordinal); + ordinal = ordinal.nextFileListOrdinal(); + // Load the generated MachO file loadMachO(machoFile, options, handler, newAtoms, additionalUndefines, llvmAtoms, deadllvmAtoms); } diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile new file mode 100644 index 0000000..b498430 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2005-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 + +# +# The point of this test is a sanity check that ld +# produces good "debug notes" stabs from dwarf .o files +# Running nm through stabs-filter.pl produces connonical stabs +# that can be diffed against a checked in know good set of stabs +# + +run: all + +all: + ${CXX} ${CCXXFLAGS} -flto -gdwarf-2 hello.cxx -c -o hello.o + ${CXX} ${CCXXFLAGS} -flto -gdwarf-2 other.cxx -c -o other.o + ${CXX} ${CCXXFLAGS} -flto -gdwarf-2 hello.o other.o -o hello -Wl,-object_path_lto,lto.o + ${FAIL_IF_BAD_MACHO} hello + # Check that we have a non zero timestamp in the debug note + nm -ap hello | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*lto.o$$' | ${FAIL_IF_EMPTY} + + # Same for Incremental LTO now + ${CXX} ${CCXXFLAGS} -flto=thin -gdwarf-2 hello.cxx -c -o hello.o + ${CXX} ${CCXXFLAGS} -flto=thin -gdwarf-2 other.cxx -c -o other.o + ${CXX} ${CCXXFLAGS} -flto=thin -gdwarf-2 hello.o other.o -o hello.thin -Wl,-object_path_lto,thinlto.o + ${FAIL_IF_BAD_MACHO} hello.thin + # Check that we have a non zero timestamp in the debug note + nm -ap hello.thin | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*thinlto.o/0.o$$' | ${FAIL_IF_EMPTY} + nm -ap hello.thin | grep '^[0-9a-z]*[1-9a-z][0-9a-z]* .*OSO.*thinlto.o/1.o$$' | ${FAIL_IF_EMPTY} + + +clean: + rm -rf hello hello.thin hello.o other.o lto.o thinlto.o diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/header.h b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/header.h new file mode 100644 index 0000000..aa960dd --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/header.h @@ -0,0 +1,8 @@ + + +inline int foo(int x) +{ + return x + 10; +} + +extern int bar(int x); \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/hello.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/hello.cxx new file mode 100644 index 0000000..0d508e1 --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/hello.cxx @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +#include "header.h" + + +int main() +{ + foo(bar(3)); + fprintf(stdout, "hello\n"); +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/other.cxx b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/other.cxx new file mode 100644 index 0000000..a6b403b --- /dev/null +++ b/ld64/unit-tests/test-cases/dwarf-debug-notes-lto/other.cxx @@ -0,0 +1,27 @@ + +#include "header.h" + +int uninit; +int init = 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; + +int bar(int x) +{ + static int bar_uninit; + static int bar_init=3; + bar_uninit = x; + scustominit = x; + custom = x; + return 20 + suninit + sinit + + bar_init + bar_uninit + foo(x); +} + +extern void disappear() __asm__("lbegone"); +void disappear() {} + +extern void foo() __asm__(".my_non_standard_function_name"); +void foo() { disappear(); } + From b8ca922668ab2c2634fb707ab37404b1d9f5b858 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 May 2014 14:50:50 +0100 Subject: [PATCH 25/48] Some header changes to fix the build with other compilers. --- ld64/src/ld/HeaderAndLoadCommands.hpp | 2 ++ ld64/src/ld/Options.cpp | 9 ++++++++- ld64/src/ld/Snapshot.cpp | 1 + ld64/src/ld/code-sign-blobs/blob.cpp | 2 ++ ld64/src/ld/code-sign-blobs/memutils.h | 6 +++++- ld64/src/ld/ld.hpp | 1 + ld64/src/ld/parsers/lto_file.cpp | 1 + ld64/src/ld/parsers/macho_dylib_file.cpp | 3 +++ ld64/src/ld/parsers/macho_relocatable_file.cpp | 1 + ld64/src/ld/parsers/textstub_dylib_file.cpp | 4 +++- ld64/src/ld/passes/bitcode_bundle.cpp | 8 +++++++- ld64/src/ld/passes/branch_shim.cpp | 1 + ld64/src/ld/passes/compact_unwind.cpp | 2 ++ ld64/src/ld/passes/dtrace_dof.cpp | 1 + ld64/src/ld/passes/dylibs.cpp | 1 + ld64/src/ld/passes/got.cpp | 1 + ld64/src/ld/passes/huge.cpp | 1 + ld64/src/ld/passes/objc.cpp | 1 + ld64/src/ld/passes/order.cpp | 1 + ld64/src/ld/passes/stubs/stubs.cpp | 1 + ld64/src/ld/passes/tlvp.cpp | 1 + ld64/src/other/ObjectDump.cpp | 2 ++ ld64/src/other/PruneTrie.cpp | 1 + ld64/src/other/machochecker.cpp | 2 ++ 24 files changed, 50 insertions(+), 4 deletions(-) diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 454eb1f..10705ce 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include "MachOFileAbstraction.hpp" diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index faf9c16..cd35b45 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -22,7 +22,9 @@ * @APPLE_LICENSE_HEADER_END@ */ - +#include +#include +#include #include #include #include @@ -35,10 +37,15 @@ #include #include #include +#include #include #include #include +#include +#ifndef PATH_MAX +#include +#endif #include "Options.h" #include "Architectures.hpp" diff --git a/ld64/src/ld/Snapshot.cpp b/ld64/src/ld/Snapshot.cpp index 27ce370..b6e1482 100644 --- a/ld64/src/ld/Snapshot.cpp +++ b/ld64/src/ld/Snapshot.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/ld64/src/ld/code-sign-blobs/blob.cpp b/ld64/src/ld/code-sign-blobs/blob.cpp index 0f0567b..a3cd224 100644 --- a/ld64/src/ld/code-sign-blobs/blob.cpp +++ b/ld64/src/ld/code-sign-blobs/blob.cpp @@ -21,7 +21,9 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include +#include // memcpy etc. // // blob - generic extensible binary blob frame diff --git a/ld64/src/ld/code-sign-blobs/memutils.h b/ld64/src/ld/code-sign-blobs/memutils.h index 9e752f4..082fd62 100644 --- a/ld64/src/ld/code-sign-blobs/memutils.h +++ b/ld64/src/ld/code-sign-blobs/memutils.h @@ -28,6 +28,7 @@ #ifndef _H_MEMUTILS #define _H_MEMUTILS +#include //#include #include #include @@ -46,7 +47,7 @@ namespace LowLevelMemoryUtilities { // static const size_t systemAlignment = 4; - +#if 0 // // Get the local alignment for a type, as used by the acting compiler. // @@ -56,6 +57,9 @@ unsigned long myalignof() { return sizeof(s) - sizeof(T); } +template +inline size_t alignof() { struct { char c; T t; } s; return sizeof(s) - sizeof(T); } +#endif // // Get the local offset of a field in a (struct or class) type, as layed out diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 0db1b7e..0bb2a81 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -30,6 +30,7 @@ #include #include #include +#include // strcmp. #include #include diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 650fd4e..5182a3a 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -32,6 +32,7 @@ #include #include #include +#include // std::sort #include #include #include diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 56b327a..8ea0627 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -34,6 +34,9 @@ #include #include #include +#include +#include +#include #include "Architectures.hpp" #include "Bitcode.hpp" diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 776e695..1ffde58 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include "dwarf2.h" #include "debugline.h" diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index 5854462..75cb228 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -27,9 +27,11 @@ #include #include #include +#include +#include #include "Architectures.hpp" -#include "bitcode.hpp" +#include "Bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" #include "generic_dylib_file.hpp" diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp index 527869a..a307876 100644 --- a/ld64/src/ld/passes/bitcode_bundle.cpp +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -25,13 +25,15 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include +#include #include "llvm-c/lto.h" // c header @@ -39,6 +41,10 @@ extern "C" { #include } +#ifndef BITCODE_XAR_VERSION +#define BITCODE_XAR_VERSION 0 +#endif + #include "bitcode_bundle.h" #include "Options.h" diff --git a/ld64/src/ld/passes/branch_shim.cpp b/ld64/src/ld/passes/branch_shim.cpp index 7be33d2..9aae1a3 100644 --- a/ld64/src/ld/passes/branch_shim.cpp +++ b/ld64/src/ld/passes/branch_shim.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index 8b2afbc..cf28ce5 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include +#include #include "ld.hpp" #include "compact_unwind.h" diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index 6f8a544..d685454 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include diff --git a/ld64/src/ld/passes/dylibs.cpp b/ld64/src/ld/passes/dylibs.cpp index 7edc85c..3982ebf 100644 --- a/ld64/src/ld/passes/dylibs.cpp +++ b/ld64/src/ld/passes/dylibs.cpp @@ -29,6 +29,7 @@ #include #include +#include // std::remove_if #include #include diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 01c2e30..b4ca6a4 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -28,6 +28,7 @@ #include #include +#include // std::sort #include #include diff --git a/ld64/src/ld/passes/huge.cpp b/ld64/src/ld/passes/huge.cpp index 3797a23..c5a6c88 100644 --- a/ld64/src/ld/passes/huge.cpp +++ b/ld64/src/ld/passes/huge.cpp @@ -29,6 +29,7 @@ #include #include +#include // std::remove_if #include #include diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index e2afca1..2100479 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -29,6 +29,7 @@ #include #include +#include // std::remove_if std::sort #include #include #include diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index a7d31f3..aec7970 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -29,6 +29,7 @@ #include #include +#include // std::remove_if std::sort #include #include #include diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index fb5b9f3..47a1c45 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -30,6 +30,7 @@ #include #include +#include // std::sort #include #include #include diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index aec9562..65c903c 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/src/ld/passes/tlvp.cpp @@ -28,6 +28,7 @@ #include #include +#include // std::sort #include #include diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 7739986..5c39066 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include "MachOFileAbstraction.hpp" #include "parsers/macho_relocatable_file.h" #include "parsers/lto_file.h" diff --git a/ld64/src/other/PruneTrie.cpp b/ld64/src/other/PruneTrie.cpp index 766ae94..f61301e 100644 --- a/ld64/src/other/PruneTrie.cpp +++ b/ld64/src/other/PruneTrie.cpp @@ -22,6 +22,7 @@ * @APPLE_LICENSE_HEADER_END@ */ #include +#include // asprintf #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 97138e8..60265d7 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include +#include #include #include #include From fd15eddf2df9762db28ade1c625cb80c64f16a35 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 9 Jan 2016 20:27:03 +0000 Subject: [PATCH 26/48] Avoid build warning --- ld64/src/ld/parsers/lto_file.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 5182a3a..34bf4e3 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -47,6 +47,8 @@ #include "lto_file.h" // #defines are a work around for +#undef __STDC_LIMIT_MACROS +#undef __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS 1 #define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" From f16f4ed2ef2defe01330223fae2b986b6cf563d0 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Tue, 19 May 2015 01:19:25 +0100 Subject: [PATCH 27/48] ld64 - Don't try to use Blocks when the compiler doesn't support them. --- ld64/src/ld/Snapshot.cpp | 39 +++++++++++++++++++++++++++++++++++++++ ld64/src/ld/Snapshot.h | 6 +++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ld64/src/ld/Snapshot.cpp b/ld64/src/ld/Snapshot.cpp index b6e1482..8cc3f01 100644 --- a/ld64/src/ld/Snapshot.cpp +++ b/ld64/src/ld/Snapshot.cpp @@ -16,7 +16,9 @@ #include #include #include +#ifdef __BLOCKS__ #include +#endif #include "Snapshot.h" #include "Options.h" @@ -199,9 +201,14 @@ void Snapshot::createSnapshot() SnapshotLog::iterator it; for (it = fLog.begin(); it != fLog.end(); it++) { +#ifdef __BLOCKS__ void (^logItem)(void) = *it; logItem(); Block_release(logItem); +#else + void (*f)(void) = *it; + if (f) f(); +#endif } fLog.erase(fLog.begin(), fLog.end()); @@ -280,7 +287,11 @@ void Snapshot::recordRawArgs(int argc, const char *argv[]) void Snapshot::addSnapshotLinkArg(int argIndex, int argCount, int fileArg) { if (fRootDir == NULL) { +#if __BLOCKS__ fLog.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex, argCount, fileArg); })); +#else + fLog.push_back(nullptr); +#endif } else { char buf[PATH_MAX]; const char *subdir = dataFilesString; @@ -318,7 +329,11 @@ void Snapshot::recordArch(const char *arch) if (!archInArgs) { if (fRootDir == NULL) { +#if __BLOCKS__ fLog.push_back(Block_copy(^{ this->recordArch(arch); })); +#else + fLog.push_back(nullptr); +#endif } else { char path_buf[PATH_MAX]; buildUniquePath(path_buf, NULL, "arch"); @@ -336,7 +351,11 @@ void Snapshot::recordArch(const char *arch) void Snapshot::recordObjectFile(const char *path) { if (fRootDir == NULL) { +#if __BLOCKS__ fLog.push_back(Block_copy(^{ this->recordObjectFile(path); })); +#else + fLog.push_back(nullptr); +#endif } else { if (fRecordObjects) { char path_buf[PATH_MAX]; @@ -398,7 +417,11 @@ void Snapshot::addDylibArg(const char *dylib) void Snapshot::recordDylibSymbol(ld::dylib::File* dylibFile, const char *name) { if (fRootDir == NULL) { +#if __BLOCKS__ fLog.push_back(Block_copy(^{ this->recordDylibSymbol(dylibFile, name); })); +#else + fLog.push_back(nullptr); +#endif } else { if (fRecordDylibSymbols) { // find the dylib in the table @@ -455,8 +478,12 @@ void Snapshot::recordDylibSymbol(ld::dylib::File* dylibFile, const char *name) void Snapshot::recordArchive(const char *archiveFile) { if (fRootDir == NULL) { +#if __BLOCKS__ const char *copy = strdup(archiveFile); fLog.push_back(Block_copy(^{ this->recordArchive(archiveFile); ::free((void *)copy); })); +#else + fLog.push_back(nullptr); +#endif } else { if (fRecordArchiveFiles) { // lazily create a vector of .a files that have been added @@ -487,8 +514,12 @@ void Snapshot::recordArchive(const char *archiveFile) void Snapshot::recordSubUmbrella(const char *frameworkPath) { if (fRootDir == NULL) { +#if __BLOCKS__ const char *copy = strdup(frameworkPath); fLog.push_back(Block_copy(^{ this->recordSubUmbrella(copy); ::free((void *)copy); })); +#else + fLog.push_back(nullptr); +#endif } else { if (fRecordUmbrellaFiles) { const char *framework = basename((char *)frameworkPath); @@ -507,8 +538,12 @@ void Snapshot::recordSubUmbrella(const char *frameworkPath) void Snapshot::recordSubLibrary(const char *dylibPath) { if (fRootDir == NULL) { +#if __BLOCKS__ const char *copy = strdup(dylibPath); fLog.push_back(Block_copy(^{ this->recordSubLibrary(copy); ::free((void *)copy); })); +#else + fLog.push_back(nullptr); +#endif } else { if (fRecordUmbrellaFiles) { copyFileToSnapshot(dylibPath, dylibsString); @@ -526,7 +561,11 @@ void Snapshot::recordAssertionMessage(const char *fmt, ...) va_end(args); if (msg != NULL) { if (fRootDir == NULL) { +#if __BLOCKS__ fLog.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg); free(msg); })); +#else + fLog.push_back(nullptr); +#endif } else { char path[PATH_MAX]; buildPath(path, NULL, assertFileString); diff --git a/ld64/src/ld/Snapshot.h b/ld64/src/ld/Snapshot.h index 8cfd17e..b44083f 100644 --- a/ld64/src/ld/Snapshot.h +++ b/ld64/src/ld/Snapshot.h @@ -87,8 +87,12 @@ class Snapshot { private: friend class SnapshotArchiveFileLog; - + +#ifdef __BLOCKS__ typedef std::vector SnapshotLog; +#else + typedef std::vector SnapshotLog; +#endif struct strcompclass { bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } From 4f612fa1ce81b1096a2ff368ce7ca19b441382d2 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 May 2014 16:08:05 +0100 Subject: [PATCH 28/48] ld64 - ensure var is init to provide a usable default. --- ld64/src/ld/OutputFile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 732eb54..9cf6cd8 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -480,6 +480,7 @@ uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup // For external relocations the classic mach-o format // has addend only stored in the content. That means // that the address of the target is not used. + *target = NULL; if ( fixup->contentAddendOnly ) return 0; } From 12831705861c190fcd2e2b3974814e137139ad61 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 May 2014 21:51:21 +0100 Subject: [PATCH 29/48] ld64 - fix to work with STABS debug. --- ld64/src/ld/parsers/macho_relocatable_file.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 1ffde58..a32a4e2 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -3170,6 +3170,9 @@ uint32_t TentativeDefinitionSection::appendAtoms(class Parser& parser, uin uint32_t count = 0; for (uint32_t i=parser.undefinedStartIndex(); i < parser.undefinedEndIndex(); ++i) { const macho_nlist

& sym = parser.symbolFromIndex(i); + // Don't count STABs. + if ((sym.n_type() & N_STAB) != 0) + continue; if ( ((sym.n_type() & N_TYPE) == N_UNDF) && (sym.n_value() != 0) ) { uint64_t size = sym.n_value(); uint8_t alignP2 = GET_COMM_ALIGN(sym.n_desc()); From cd572ffb8e2234571ae96dfea53857e6f86fb7c6 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Tue, 24 Jun 2014 21:46:04 +0100 Subject: [PATCH 30/48] ld64 - Support building without libLTO. --- ld64/src/ld/InputFiles.cpp | 8 ++++++++ ld64/src/ld/Options.cpp | 8 ++++++-- ld64/src/ld/Resolver.cpp | 10 ++++++++-- ld64/src/ld/Resolver.h | 4 +++- ld64/src/ld/ld.cpp | 3 ++- ld64/src/ld/parsers/archive_file.cpp | 19 +++++++++++++++---- ld64/src/ld/parsers/lto_file.cpp | 3 ++- ld64/src/ld/parsers/lto_file.h | 3 +++ ld64/src/ld/passes/bitcode_bundle.cpp | 3 +++ ld64/src/ld/passes/bitcode_bundle.h | 2 ++ 10 files changed, 52 insertions(+), 11 deletions(-) diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index e8e6bcc..6edf21c 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -60,7 +60,9 @@ #include "macho_dylib_file.h" #include "textstub_dylib_file.hpp" #include "archive_file.h" +#ifdef LTO_SUPPORT #include "lto_file.h" +#endif #include "opaque_section_file.h" #include "MachOFileAbstraction.hpp" #include "Snapshot.h" @@ -188,9 +190,11 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) if ( result != NULL ) return result; +#ifdef LTO_SUPPORT result = lto::archName(p, len); if ( result != NULL ) return result; +#endif if ( strncmp((const char*)p, "!\n", 8) == 0 ) return "archive"; @@ -312,6 +316,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib return objResult; } +#ifdef LTO_SUPPORT // see if it is an llvm object file objResult = lto::parse(p, len, info.path, info.modTime, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles(), _options.verboseOptimizationHints()); if ( objResult != NULL ) { @@ -319,6 +324,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib OSAtomicIncrement32(&_totalObjectLoaded); return objResult; } +#endif // see if it is a dynamic library (or text-based dynamic library) ld::dylib::File* dylibResult; @@ -371,6 +377,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib return archiveResult; } +#ifdef LTO_SUPPORT // 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() ) { @@ -398,6 +405,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); } } +#endif if ( dylibsNotAllowed ) { cpu_type_t dummy1; diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index cd35b45..94abb28 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -52,15 +52,15 @@ #include "MachOFileAbstraction.hpp" #include "Snapshot.h" - // from FunctionNameDemangle.h extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length); - +#ifdef LTO_SUPPORT // upward dependency on lto::version() namespace lto { extern const char* version(); } +#endif // magic to place command line in crash reports const int crashreporterBufferSize = 2000; @@ -3537,11 +3537,13 @@ void Options::parse(int argc, const char* argv[]) fDataInCodeInfoLoadCommandForcedOn = true; fDataInCodeInfoLoadCommandForcedOff = false; } +#ifdef LTO_SUPPORT else if ( strcmp(arg, "-object_path_lto") == 0 ) { fTempLtoObjectPath = argv[++i]; if ( fTempLtoObjectPath == NULL ) throw "missing argument to -object_path_lto"; } +#endif else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) { fObjcCategoryMerging = false; } @@ -3958,9 +3960,11 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fprintf(stderr, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS); // if only -v specified, exit cleanly if ( argc == 2 ) { +#ifdef LTO_SUPPORT const char* ltoVers = lto::version(); if ( ltoVers != NULL ) fprintf(stderr, "LTO support using: %s\n", ltoVers); +#endif fprintf(stderr, "TAPI support using: %s\n", tapi::Version::getFullVersionAsString().c_str()); exit(0); } diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 1c8f16e..c059684 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -57,8 +57,10 @@ #include "InputFiles.h" #include "SymbolTable.h" #include "Resolver.h" -#include "parsers/lto_file.h" +#ifdef LTO_SUPPORT +#include "parsers/lto_file.h" +#endif namespace ld { namespace tool { @@ -1720,6 +1722,7 @@ void Resolver::removeCoalescedAwayAtoms() } } +#ifdef LTO_SUPPORT void Resolver::linkTimeOptimize() { // only do work here if some llvm obj files where loaded @@ -1766,6 +1769,7 @@ void Resolver::linkTimeOptimize() std::vector additionalUndefines; if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) ) return; // if nothing done + _ltoCodeGenFinished = true; // add all newly created atoms to _atoms and update symbol table @@ -1834,7 +1838,7 @@ void Resolver::linkTimeOptimize() this->checkDylibSymbolCollisions(); } } - +#endif void Resolver::tweakWeakness() { @@ -1896,7 +1900,9 @@ void Resolver::resolve() this->syncAliases(); this->removeCoalescedAwayAtoms(); this->fillInEntryPoint(); +#ifdef LTO_SUPPORT this->linkTimeOptimize(); +#endif this->fillInInternalState(); this->tweakWeakness(); _symbolTable.checkDuplicateSymbols(); diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 06b7a8b..fe2c4cf 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -92,8 +92,10 @@ class Resolver : public ld::File::AtomHandler void fillInHelpersInInternalState(); void removeCoalescedAwayAtoms(); void syncAliases(); - void fillInEntryPoint(); + void fillInEntryPoint(); +#ifdef LTO_SUPPORT void linkTimeOptimize(); +#endif void convertReferencesToIndirect(const ld::Atom& atom); const ld::Atom* entryPoint(bool searchArchives); void markLive(const ld::Atom& atom, WhyLiveBackChain* previous); diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index a8d2276..76bb45e 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -1346,8 +1346,9 @@ int main(int argc, const char* argv[]) ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); ld::passes::compact_unwind::doPass(options, state); // must be after order pass +#ifdef LTO_SUPPORT ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib - +#endif // sort final sections state.sortSections(); diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index a1fec1a..ce08e83 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -39,7 +39,9 @@ #include "Architectures.hpp" #include "macho_relocatable_file.h" +#ifdef LTO_SUPPORT #include "lto_file.h" +#endif #include "archive_file.h" @@ -90,8 +92,10 @@ class File : public ld::archive::File private: static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts); +#ifdef LTO_SUPPORT static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts); +#endif static cpu_type_t architecture(); class Entry : ar_hdr @@ -234,13 +238,13 @@ bool File::validMachOFile(const uint8_t* fileContent, uint64_t fileLength, co return mach_o::relocatable::isObjectFile(fileContent, fileLength, opts); } +#ifdef LTO_SUPPORT template bool File::validLTOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) { return lto::isObjectFile(fileContent, fileLength, opts.architecture, opts.subType); } - - +#endif template bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) @@ -263,7 +267,11 @@ bool File::validFile(const uint8_t* fileContent, uint64_t fileLength, const m continue; #endif // archive is valid if first .o file is valid - return (validMachOFile(p->content(), p->contentSize(), opts) || validLTOFile(p->content(), p->contentSize(), opts)); + return (validMachOFile(p->content(), p->contentSize(), opts) +#ifdef LTO_SUPPORT + || validLTOFile(p->content(), p->contentSize(), opts) +#endif + ); } // empty archive return true; @@ -401,6 +409,7 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem _instantiatedEntries[member] = state; return _instantiatedEntries[member]; } +#ifdef LTO_SUPPORT // see if member is llvm bitcode file result = lto::parse(member->content(), member->contentSize(), mPath, member->modificationTime(), ordinal, @@ -410,8 +419,10 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem _instantiatedEntries[member] = state; return _instantiatedEntries[member]; } - throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName, member->contentSize()); +#else + throwf("archive member '%s' with length %d is not mach-o", memberName, member->contentSize()); +#endif } catch (const char* msg) { throwf("in %s, %s", memberPath, msg); diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 34bf4e3..05da701 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -21,6 +21,7 @@ * * @APPLE_LICENSE_HEADER_END@ */ +#ifdef LTO_SUPPORT #ifndef __LTO_READER_H__ #define __LTO_READER_H__ @@ -1600,6 +1601,6 @@ bool optimize( const std::vector& allAtoms, }; // namespace lto - +#endif #endif diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index b760177..d7e09c5 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -22,6 +22,8 @@ * @APPLE_LICENSE_HEADER_END@ */ +#ifdef LTO_SUPPORT + #ifndef __LTO_FILE_H__ #define __LTO_FILE_H__ @@ -88,5 +90,6 @@ extern bool optimize( const std::vector& allAtoms, #endif // __LTO_FILE_H__ +#endif diff --git a/ld64/src/ld/passes/bitcode_bundle.cpp b/ld64/src/ld/passes/bitcode_bundle.cpp index a307876..242c20a 100644 --- a/ld64/src/ld/passes/bitcode_bundle.cpp +++ b/ld64/src/ld/passes/bitcode_bundle.cpp @@ -22,6 +22,8 @@ * @APPLE_LICENSE_HEADER_END@ */ +#ifdef LTO_SUPPORT + #include #include #include @@ -1043,3 +1045,4 @@ void doPass(const Options& opts, ld::Internal& internal) { } // namespace bitcode_bundle } // namespace passes } // namespace ld +#endif diff --git a/ld64/src/ld/passes/bitcode_bundle.h b/ld64/src/ld/passes/bitcode_bundle.h index c87a59e..4a311fc 100644 --- a/ld64/src/ld/passes/bitcode_bundle.h +++ b/ld64/src/ld/passes/bitcode_bundle.h @@ -21,6 +21,7 @@ * * @APPLE_LICENSE_HEADER_END@ */ +#ifdef LTO_SUPPORT #ifndef _BITCODE_BUNDLE_H_ #define _BITCODE_BUNDLE_H_ @@ -41,3 +42,4 @@ extern void doPass(const Options& opts, ld::Internal& internal); } // namespace ld #endif /* defined(_BITCODE_BUNDLE_H_) */ +#endif From a135e0190e85d3c1619a230a5fad1a80af3868d7 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 5 Dec 2015 11:39:06 +0000 Subject: [PATCH 31/48] Allow tool builds without libLTO --- ld64/src/other/ObjectDump.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index 5c39066..e900b49 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -35,7 +35,9 @@ #include "MachOFileAbstraction.hpp" #include "parsers/macho_relocatable_file.h" +#if LTO_SUPPORT #include "parsers/lto_file.h" +#endif static bool sDumpContent= true; static bool sDumpStabs = false; @@ -1265,12 +1267,12 @@ static ld::relocatable::File* createReader(const char* path) ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), objOpts); if ( objResult != NULL ) return objResult; - +#if LTO_SUPPORT // see if it is an llvm object file objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false, true); if ( objResult != NULL ) return objResult; - +#endif throwf("not a mach-o object file: %s", path); #else // for peformance testing From cab837e1e9c4687add221115bb8a9a61c74beee2 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Tue, 19 May 2015 01:31:53 +0100 Subject: [PATCH 32/48] ld64 - Don't try to use Apple private CrashReporter interface unless it's available. --- ld64/src/ld/Options.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 94abb28..acfdbb8 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -65,7 +65,12 @@ namespace lto { // magic to place command line in crash reports const int crashreporterBufferSize = 2000; static char crashreporterBuffer[crashreporterBufferSize]; -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#ifndef __has_include +# define __has_include(x) 0 +#endif +#if __has_include("CrashReporterClient.h") \ + && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + #define USE_CRASHREPORTER_CLIENT 1 #include // hack until ld does not need to build on 10.6 anymore struct crashreporter_annotations_t gCRAnnotations @@ -5708,7 +5713,7 @@ void Options::checkForClassic(int argc, const char* argv[]) bool newLinker = false; // build command line buffer in case ld crashes -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#if USE_CRASHREPORTER_CLIENT CRSetCrashLogMessage(crashreporterBuffer); #endif const char* srcRoot = getenv("SRCROOT"); From 5140eaf839d762a46778388826b67b83df3d90e1 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sun, 17 May 2015 20:25:01 +0100 Subject: [PATCH 33/48] Version and Help output changes. - Add --target-help/--help and a BUGURL - Add GNU-style --version --- ld64/src/ld/Options.cpp | 61 ++++++++++++++++++++++++--------- ld64/src/other/ObjectDump.cpp | 14 +++++++- ld64/src/other/dyldinfo.cpp | 14 +++++++- ld64/src/other/machochecker.cpp | 12 +++++++ ld64/src/other/rebase.cpp | 14 +++++++- ld64/src/other/unwinddump.cpp | 12 +++++++ 6 files changed, 107 insertions(+), 20 deletions(-) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index acfdbb8..f720ac3 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -2365,13 +2365,6 @@ void Options::parse(int argc, const char* argv[]) ++i; // previously handled by buildSearchPaths() } - // The one gnu style option we have to keep compatibility - // with gcc. Might as well have the single hyphen one as well. - else if ( (strcmp(arg, "--help") == 0) - || (strcmp(arg, "-help") == 0)) { - fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n"); - exit (0); - } else if ( strcmp(arg, "-arch") == 0 ) { parseArch(argv[++i]); } @@ -3922,6 +3915,8 @@ void Options::parse(int argc, const char* argv[]) void Options::buildSearchPaths(int argc, const char* argv[]) { bool addStandardLibraryDirectories = true; + bool exitAfterOptionsParsing = false; + bool shouldPrintHelp = false; std::vector libraryPaths; std::vector frameworkPaths; libraryPaths.reserve(10); @@ -3958,22 +3953,35 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } else if ( strcmp(argv[i], "-Z") == 0 ) addStandardLibraryDirectories = false; - else if ( strcmp(argv[i], "-v") == 0 ) { + else if ( strcmp(argv[i], "-v") == 0 || + strcmp(argv[i], "--version") == 0 ) { + bool isVersion = strcmp(argv[i], "--version") == 0; + /* GNU-style --version is supposed to output on stdout, -v uses + stderr. */ + FILE *out = isVersion ? stdout : stderr; fVerbose = true; extern const char ldVersionString[]; - fprintf(stderr, "%s", ldVersionString); - fprintf(stderr, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS); - // if only -v specified, exit cleanly - if ( argc == 2 ) { + fprintf(out, "%s", ldVersionString); + fprintf(out, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS); #ifdef LTO_SUPPORT - const char* ltoVers = lto::version(); - if ( ltoVers != NULL ) - fprintf(stderr, "LTO support using: %s\n", ltoVers); + const char* ltoVers = lto::version(); + if ( ltoVers != NULL ) + fprintf(out, "LTO support using: %s\n", ltoVers); #endif - fprintf(stderr, "TAPI support using: %s\n", tapi::Version::getFullVersionAsString().c_str()); - exit(0); + fprintf(out, "TAPI support using: %s\n", + tapi::Version::getFullVersionAsString().c_str()); + // if only -v specified or --version, exit cleanly + if ( argc == 2 || isVersion ) { + exitAfterOptionsParsing = true; } } + // --help and --target-help for GCC compatibility. + else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-help") == 0 || + strcmp(argv[i], "--target-help") == 0 ) { + shouldPrintHelp = true; + exitAfterOptionsParsing = true; + } else if ( strcmp(argv[i], "-syslibroot") == 0 ) { const char* path = argv[++i]; if ( path == NULL ) @@ -4002,6 +4010,25 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fBundleBitcode = true; } } + if (exitAfterOptionsParsing){ + if (shouldPrintHelp) { + if (! fVerbose) { // We didn't print the version info yet. + extern const char ldVersionString[]; + fprintf(stdout, "%s", ldVersionString); + fprintf(stdout, "configured to support archs: %s\n", ALL_SUPPORTED_ARCHS); +#ifdef LTO_SUPPORT + const char* ltoVers = lto::version(); + if ( ltoVers != NULL ) + fprintf(stdout, "LTO support using: %s\n", ltoVers); +#endif + } + fprintf(stdout, "TODO: implement options summary (look at man ld).\n"); +#ifdef XTOOLS_BUGURL + fprintf(stdout, "Please report bugs to : %s\n", XTOOLS_BUGURL); +#endif + } + exit (0); + } int standardLibraryPathsStartIndex = libraryPaths.size(); int standardFrameworkPathsStartIndex = frameworkPaths.size(); if ( addStandardLibraryDirectories ) { diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index e900b49..e7ab398 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -1288,7 +1288,7 @@ static void usage() { - fprintf(stderr, "ObjectDump options:\n" + fprintf(stdout, "ObjectDump options:\n" "\t-no_content\tdon't dump contents\n" "\t-no_section\tdon't dump section name\n" "\t-no_defintion\tdon't dump definition kind\n" @@ -1312,6 +1312,18 @@ int main(int argc, const char* argv[]) for(int i=1; i < argc; ++i) { const char* arg = argv[i]; if ( arg[0] == '-' ) { + if(strcmp(argv[i], "--version") == 0){ + /* Implement a gnu-style --version. */ + fprintf(stdout, "xtools-%s ObjectDump %s\nBased on Apple Inc. ld64-%s\n", + XTOOLS_VERSION, PACKAGE_VERSION, LD64_VERSION_NUM); + exit(0); + } else if(strcmp(argv[i], "--help") == 0){ + usage(); +#ifdef XTOOLS_BUGURL + fprintf(stdout, "Please report bugs to %s\n", XTOOLS_BUGURL); +#endif + exit(0); + } if ( strcmp(arg, "-no_content") == 0 ) { sDumpContent = false; } diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 83d601e..2d69fcd 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -2375,7 +2375,7 @@ static void dump(const char* path) static void usage() { - fprintf(stderr, "Usage: dyldinfo [-arch ] \n" + fprintf(stdout, "Usage: dyldinfo [-arch ] \n" "\t-dylibs print dependent dylibs\n" "\t-dr print dependent dylibs and show any recorded DR info\n" "\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n" @@ -2403,6 +2403,18 @@ int main(int argc, const char* argv[]) for(int i=1; i < argc; ++i) { const char* arg = argv[i]; if ( arg[0] == '-' ) { + if(strcmp(arg, "--version") == 0){ + /* Implement a gnu-style --version. */ + fprintf(stdout, "xtools-%s dyldinfo %s\nBased on Apple Inc. ld64-%s\n", + XTOOLS_VERSION, PACKAGE_VERSION, LD64_VERSION_NUM); + exit(0); + } else if(strcmp(arg, "--help") == 0){ + usage(); +#ifdef XTOOLS_BUGURL + fprintf(stdout, "Please report bugs to %s\n", XTOOLS_BUGURL); +#endif + exit(0); + } if ( strcmp(arg, "-arch") == 0 ) { const char* arch = ++i& files, uint static void usage() { - fprintf(stderr, "rebase [-low_address] [-high_address] [-v] [-arch ] files...\n"); + fprintf(stdout, "rebase [-low_address] [-high_address] [-v] [-arch ] files...\n"); } @@ -1001,6 +1001,18 @@ int main(int argc, const char* argv[]) for(int i=1; i < argc; ++i) { const char* arg = argv[i]; if ( arg[0] == '-' ) { + if(strcmp(arg, "--version") == 0){ + /* Implement a gnu-style --version. */ + fprintf(stdout, "xtools-%s rebase %s\nBased on Apple Inc. ld64-%s\n", + XTOOLS_VERSION, PACKAGE_VERSION, LD64_VERSION_NUM); + exit(0); + } else if(strcmp(arg, "--help") == 0){ + usage(); +#ifdef XTOOLS_BUGURL + fprintf(stdout, "Please report bugs to %s\n", XTOOLS_BUGURL); +#endif + exit(0); + } if ( strcmp(arg, "-v") == 0 ) { verbose = true; } diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 5b264d1..589eea0 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -1165,6 +1165,18 @@ int main(int argc, const char* argv[]) for(int i=1; i < argc; ++i) { const char* arg = argv[i]; if ( arg[0] == '-' ) { + if(strcmp(arg, "--version") == 0){ + /* Implement a gnu-style --version. */ + fprintf(stdout, "xtools-%s unwinddump %s\nBased on Apple Inc. ld64-%s\n", + XTOOLS_VERSION, PACKAGE_VERSION, LD64_VERSION_NUM); + exit(0); + } else if(strcmp(arg, "--help") == 0){ + fprintf(stdout, "unwinddump: [-arch] [no_symbols] file\n"); +#ifdef XTOOLS_BUGURL + fprintf(stdout, "Please report bugs to %s\n", XTOOLS_BUGURL); +#endif + exit(0); + } if ( strcmp(arg, "-arch") == 0 ) { const char* arch = argv[++i]; if ( strcmp(arch, "i386") == 0 ) From 20b5de384637f50ca112b8ae87f22cb6572dfae2 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Tue, 19 May 2015 13:12:57 +0100 Subject: [PATCH 34/48] Fix errors reported by GCC with const_iterator <=> iterator. --- ld64/src/ld/Options.cpp | 6 +++--- ld64/src/ld/Options.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index f720ac3..02f24eb 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -1212,7 +1212,7 @@ std::vector Options::exportsData() const std::vector Options::SetWithWildcards::data() const { std::vector data; - for (NameSet::iterator it=regularBegin(); it != regularEnd(); ++it) { + for (NameSet::const_iterator it=regularBegin(); it != regularEnd(); ++it) { data.push_back(*it); } for (std::vector::const_iterator it=fWildCard.begin(); it != fWildCard.end(); ++it) { @@ -5482,7 +5482,7 @@ void Options::checkIllegalOptionCombinations() // make sure all required exported symbols exist std::vector impliedExports; - for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); ++it) { + for (NameSet::const_iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); ++it) { const char* name = *it; const int len = strlen(name); if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) { @@ -5514,7 +5514,7 @@ void Options::checkIllegalOptionCombinations() } // make sure all required re-exported symbols exist - for (NameSet::iterator it=fReExportSymbols.regularBegin(); it != fReExportSymbols.regularEnd(); ++it) { + for (NameSet::const_iterator it=fReExportSymbols.regularBegin(); it != fReExportSymbols.regularEnd(); ++it) { fInitialUndefines.push_back(*it); } diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index e257e30..6a4b3de 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -502,8 +502,8 @@ class Options bool containsNonWildcard(const char*) const; bool empty() const { return fRegular.empty() && fWildCard.empty(); } bool hasWildCards() const { return !fWildCard.empty(); } - NameSet::iterator regularBegin() const { return fRegular.begin(); } - NameSet::iterator regularEnd() const { return fRegular.end(); } + NameSet::const_iterator regularBegin() const { return fRegular.begin(); } + NameSet::const_iterator regularEnd() const { return fRegular.end(); } void remove(const NameSet&); std::vector data() const; private: From 9cfeba16fa3436d4b98042913d8ec454ccbebbb5 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 24 May 2014 16:02:49 +0100 Subject: [PATCH 35/48] [ld64] Adjust options to cater for pre-10.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default to 10.5 unless explictly configured differently. Filter options that don’t apply to 10.5. Sort out some spellos at the same time. --- ld64/src/ld/Options.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 02f24eb..d96c538 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -661,6 +661,21 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P fUseSimplifiedDylibReExports = true; return; } + assert(fArchitectureName != NULL); + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { +#if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); +#elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); +#else + warning("-macosx_version_min not specified, assuming 10.5"); + fMacVersionMin = ld::mac10_5; +#endif + } + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; } fArchitectureName = "unknown architecture"; } @@ -3518,8 +3533,12 @@ void Options::parse(int argc, const char* argv[]) cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-function_starts") == 0 ) { - fFunctionStartsForcedOn = true; - fFunctionStartsForcedOff = false; + if (fMacVersionMin >= ld::mac10_6) { + fFunctionStartsForcedOn = true; + fFunctionStartsForcedOff = false; + } else { + warning("-function_starts ignored for OS X < 10.6"); + } } else if ( strcmp(arg, "-no_function_starts") == 0 ) { fFunctionStartsForcedOff = true; @@ -4714,6 +4733,11 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDynamicExecutable: + // No compact unwind for OSX 10.4/5 + if (fMacVersionMin < ld::mac10_6) { + fAddCompactUnwindEncoding = false; + fRemoveDwarfUnwindIfCompactExists = false; + } //if ( fAddCompactUnwindEncoding && (fVersionMin >= ld::mac10_6) ) // fRemoveDwarfUnwindIfCompactExists = true; break; @@ -4957,7 +4981,7 @@ void Options::reconfigureDefaults() case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: - if ( !fVersionLoadCommandForcedOff ) + if ( !fVersionLoadCommandForcedOff && fMacVersionMin >= ld::mac10_6) fVersionLoadCommand = true; break; } From b301f301641d5909f1904dfbd94752ae8bd3e701 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 22 Dec 2016 01:58:31 +0000 Subject: [PATCH 36/48] Alter debug output for dumpAtoms(). --- ld64/src/ld/Resolver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index c059684..5344f53 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -1883,7 +1883,7 @@ void Resolver::dumpAtoms() fprintf(stderr, "Resolver all atoms:\n"); for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { const ld::Atom* atom = *it; - fprintf(stderr, " %p name=%s, def=%d\n", atom, atom->name(), atom->definition()); + fprintf(stderr, " %p sect %32s name=%s, def=%d\n", atom, atom->section().sectionName(), atom->name(), atom->definition()); } } @@ -1891,6 +1891,7 @@ void Resolver::resolve() { this->initializeState(); this->buildAtomList(); + //this->dumpAtoms(); this->addInitialUndefines(); this->fillInHelpersInInternalState(); this->resolveUndefines(); From 07013fca5f5802957ecfac108b4343b8c0bc2d80 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 28 Sep 2017 15:20:13 +0100 Subject: [PATCH 37/48] Account for TV_OS support in the textstub before trying to use it. --- ld64/src/ld/parsers/textstub_dylib_file.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index 75cb228..717125f 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -92,8 +92,10 @@ static Options::Platform mapPlatform(tapi::Platform platform) { return Options::kPlatformiOS; case tapi::Platform::watchOS: return Options::kPlatformWatchOS; +#if SUPPORT_APPLE_TV case tapi::Platform::tvOS: return Options::kPlatform_tvOS; +#endif } return Options::kPlatformUnknown; From 96eb29d84e75f420002a1a7277ce9febb8791327 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Mon, 18 May 2015 20:20:51 +0100 Subject: [PATCH 38/48] [ld64] Restore ppc and ppc64. Based on 127-2 version plus ppc64 additions (also to libunwind). Updated to 241.9 branch islands. - updated for 253.3 - updated for 264.3.102 - updated for 274.1/2 Add missing 'encodingMeansUseDwarf' functions. Add support for textstub dylibs. Add more ppc support functions (ObjC and TAPI). We only call out to ld_classic if specifically asked to. Don't add module tables unless we're targeting < 10.4 since that's what the comment says.. --- ld64/src/abstraction/MachOFileAbstraction.hpp | 12 +- ld64/src/ld/HeaderAndLoadCommands.hpp | 64 ++ ld64/src/ld/InputFiles.cpp | 20 +- ld64/src/ld/LinkEdit.hpp | 40 ++ ld64/src/ld/LinkEditClassic.hpp | 657 +++++++++++++++++ ld64/src/ld/Options.cpp | 64 +- ld64/src/ld/OutputFile.cpp | 287 +++++++- ld64/src/ld/OutputFile.h | 7 +- ld64/src/ld/Resolver.cpp | 49 ++ ld64/src/ld/Resolver.h | 1 + ld64/src/ld/ld.hpp | 7 + ld64/src/ld/parsers/archive_file.cpp | 20 + .../src/ld/parsers/libunwind/AddressSpace.hpp | 10 + .../parsers/libunwind/DwarfInstructions.hpp | 51 +- ld64/src/ld/parsers/libunwind/Registers.hpp | 569 +++++++++++++++ ld64/src/ld/parsers/macho_dylib_file.cpp | 106 +++ .../src/ld/parsers/macho_relocatable_file.cpp | 678 +++++++++++++++++- ld64/src/ld/parsers/textstub_dylib_file.cpp | 10 + ld64/src/ld/passes/branch_island.cpp | 83 ++- ld64/src/ld/passes/dtrace_dof.cpp | 6 + ld64/src/ld/passes/objc.cpp | 14 +- ld64/src/ld/passes/stubs/stub_ppc_classic.hpp | 189 +++++ ld64/src/ld/passes/stubs/stubs.cpp | 20 +- ld64/src/other/ObjectDump.cpp | 36 + ld64/src/other/dyldinfo.cpp | 2 + ld64/src/other/machochecker.cpp | 35 + ld64/src/other/rebase.cpp | 8 +- ld64/src/other/unwinddump.cpp | 2 + 28 files changed, 3013 insertions(+), 34 deletions(-) create mode 100644 ld64/src/ld/passes/stubs/stub_ppc_classic.hpp diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index a417877..1a525b3 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -532,6 +533,16 @@ static const ArchInfo archInfoArray[] = { #if SUPPORT_ARCH_i386 { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, "i386-", "", false, false }, #endif +#if SUPPORT_ARCH_ppc64 + { "ppc64", CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL, "powerpc64-", "ppc64-", false, false }, +#endif +#if SUPPORT_ARCH_ppc + { "ppc750", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_750, "powerpc-", "ppc-", true, false }, + { "ppc7400", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7400, "powerpc-", "ppc-", true, false }, + { "ppc7450", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_7450, "powerpc-", "ppc-", true, false }, + { "ppc970", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_970, "powerpc-", "ppc-", true, false }, + { "ppc", CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL, "powerpc-", "ppc-", false, false }, +#endif #if SUPPORT_ARCH_armv4t { "armv4t", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T, "armv4t-", "", true, false }, #define SUPPORT_ARCH_arm_any 1 @@ -585,7 +596,6 @@ static const ArchInfo archInfoArray[] = { { NULL, 0, 0, NULL, NULL, false, false } }; - // weird, but this include must wait until after SUPPORT_ARCH_arm_any is set up #if SUPPORT_ARCH_arm_any #include diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 10705ce..a948426 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -647,17 +647,36 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const return bits; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_POWERPC; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_POWERPC64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_I386; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_X86_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM64; } +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return _state.cpuSubType; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (CPU_SUBTYPE_POWERPC_ALL | 0x80000000); + else + return CPU_SUBTYPE_POWERPC_ALL; +} + template <> uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const { @@ -1209,6 +1228,51 @@ uint8_t* HeaderAndLoadCommandsAtom::copySourceVersionLoadCommand(uint8_t* p) return p + sizeof(macho_source_version_command

); } +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command* cmd = (macho_thread_command*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_thread_register(0, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(3, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command

* cmd = (macho_thread_command

*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_thread_register(0, start); + //cmd->set_thread_register(0, (start>>32)); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(3, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} template <> uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 6edf21c..f4e3414 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -311,7 +311,12 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { +#ifdef NO_64B_ATOMIC + /* This only applies to ppc-d9, so let's just not do anything multi-threaded there. */ + _totalObjectSize += len; +#else OSAtomicAdd64(len, &_totalObjectSize); +#endif OSAtomicIncrement32(&_totalObjectLoaded); return objResult; } @@ -320,7 +325,12 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib // see if it is an llvm object file objResult = lto::parse(p, len, info.path, info.modTime, info.ordinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles(), _options.verboseOptimizationHints()); if ( objResult != NULL ) { +#ifdef NO_64B_ATOMIC + /* This only applies to ppc-d9, so let's just not do anything multi-threaded there. */ + _totalObjectSize += len; +#else OSAtomicAdd64(len, &_totalObjectSize); +#endif OSAtomicIncrement32(&_totalObjectLoaded); return objResult; } @@ -371,8 +381,12 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts); if ( archiveResult != NULL ) { - +#ifdef NO_64B_ATOMIC + /* This only applies to ppc-d9, so let's just not do anything multi-threaded there. */ + _totalArchiveSize += len; +#else OSAtomicAdd64(len, &_totalArchiveSize); +#endif OSAtomicIncrement32(&_totalArchivesLoaded); return archiveResult; } @@ -885,6 +899,10 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) warning("-arch not specified"); #if __i386__ opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, Options::kPlatformOSX); +#elif __ppc__ + opts.setArchitecture(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL, Options::kPlatformOSX); +#elif __ppc64__ + opts.setArchitecture(CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL, Options::kPlatformOSX); #elif __x86_64__ opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, Options::kPlatformOSX); #elif __arm__ diff --git a/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index c4421ef..8e219c7 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -1098,6 +1098,7 @@ class SplitSegInfoV1Atom : public LinkEditAtom 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; @@ -1213,6 +1214,36 @@ void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kin } #endif +template <> +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +{ + switch (kind) { + case ld::Fixup::kindStorePPCPicHigh16AddLow: + _ppcHi16Locations.push_back(address); + break; + case ld::Fixup::kindStoreBigEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template <> +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +{ + switch (kind) { + case ld::Fixup::kindStorePPCPicHigh16AddLow: + _ppcHi16Locations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + template void SplitSegInfoV1Atom::uleb128EncodeAddresses(const std::vector& locations) const { @@ -1275,6 +1306,14 @@ void SplitSegInfoV1Atom::encode() const this->_encodedData.append_byte(0); // terminator } + if ( _ppcHi16Locations.size() != 0 ) { + this->_encodedData.append_byte(3); + //fprintf(stderr, "type 3:\n"); + std::sort(_ppcHi16Locations.begin(), _ppcHi16Locations.end()); + this->uleb128EncodeAddresses(_ppcHi16Locations); + this->_encodedData.append_byte(0); // terminator + } + if ( _thumbLo16Locations.size() != 0 ) { this->_encodedData.append_byte(5); //fprintf(stderr, "type 5:\n"); @@ -1322,6 +1361,7 @@ void SplitSegInfoV1Atom::encode() const // clean up temporaries _32bitPointerLocations.clear(); _64bitPointerLocations.clear(); + _ppcHi16Locations.clear(); } diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index ce7c820..97ef099 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -837,6 +837,24 @@ uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) throw "writable (__DATA) segment not found"; } +template <> +uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) +{ + if ( _options.outputKind() != Options::kDynamicExecutable ) { + // for things other than a final exec. don't mess with it. + return _options.baseAddress(); + } + + // FIXME: some other tests here to give warnings for 10.4 etc. + // for all other kinds, the ppc64 reloc base address starts at __DATA segment + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + return sect->address; + } + throw "__DATA segment not found"; +} + template uint64_t LocalRelocationsAtom::relocBaseAddress(ld::Internal& state) { @@ -859,6 +877,52 @@ void LocalRelocationsAtom::addPointerReloc(uint64_t addr, uint32_t symNum) template void LocalRelocationsAtom::addTextReloc(uint64_t addr, ld::Fixup::Kind kind, uint64_t targetAddr, uint32_t symNum) { + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + switch ( kind ) { + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( _options.outputSlidable() ) { + reloc1.set_r_address(addr); + reloc1.set_r_symbolnum(symNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + reloc2.set_r_address(targetAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + _relocs.push_back(reloc1); + _relocs.push_back(reloc2); + } + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCAbsHigh16: + if ( _options.outputSlidable() ) { + reloc1.set_r_address(addr); + reloc1.set_r_symbolnum(symNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); + reloc2.set_r_address(targetAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + _relocs.push_back(reloc1); + _relocs.push_back(reloc2); + } + break; + default: + break; + } } @@ -951,6 +1015,20 @@ uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) throw "writable (__DATA) segment not found"; } +template <> +uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) +{ + // FIXME: for ppc64, it's more complicated some extra tests needed here to warn of + // out-of-rage conditions for 10.4 + // ... but, mostly, reloc base address starts at __DATA segment + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + return sect->address; + } + throw "__DATA segment not found"; +} + template uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) { @@ -960,6 +1038,7 @@ uint64_t ExternalRelocationsAtom::relocBaseAddress(ld::Internal& state) template void ExternalRelocationsAtom::addExternalPointerReloc(uint64_t addr, const ld::Atom* target) { +// addr -= relocBaseAddress(_state); _pointerLocations.push_back(LocAndAtom(addr, target)); } @@ -987,7 +1066,9 @@ template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } #endif template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return GENERIC_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return PPC_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X86_64_RELOC_UNSIGNED; } +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return PPC_RELOC_VANILLA; } template <> uint32_t ExternalRelocationsAtom::callReloc() { return X86_64_RELOC_BRANCH; } @@ -1915,6 +1996,582 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSectio } #endif // SUPPORT_ARCH_arm64 +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); + } + uint32_t toAddr; + uint32_t fromAddr; + + switch ( entry.kind ) { + + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStorePPCBranch14: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR14); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + 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); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + if ( external ) + reloc2.set_r_address(entry.toAddend >> 16); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HI16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HA16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicHigh16AddLow: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + macho_scattered_relocation_info

* sreloc1 = (macho_scattered_relocation_info

*)&reloc1; + macho_scattered_relocation_info

* sreloc2 = (macho_scattered_relocation_info

*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); + } + uint32_t toAddr; + uint32_t fromAddr; + + switch ( entry.kind ) { + + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStorePPCBranch14: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR14); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + 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); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreBigEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(3); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + 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(3); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(3); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + if ( external ) + reloc2.set_r_address(entry.toAddend >> 16); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HI16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HA16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicHigh16AddLow: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} template void SectionRelocationsAtom::addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind kind, diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index d96c538..1c1fda3 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -627,6 +627,8 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P switch ( type ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); @@ -1846,12 +1848,18 @@ void Options::parseOrderFile(const char* path, bool cstring) *last = '\0'; --last; } - // if there is an architecture prefix, only use this symbol it if matches current arch if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { - symbolStart = NULL; + if ( fArchitecture == CPU_TYPE_POWERPC ) + symbolStart = &symbolStart[4]; + else + symbolStart = NULL; } + // if there is an architecture prefix, only use this symbol it if matches current arch else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { - symbolStart = NULL; + if ( fArchitecture == CPU_TYPE_POWERPC64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; } else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { if ( fArchitecture == CPU_TYPE_I386 ) @@ -4356,6 +4364,7 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: + case CPU_TYPE_POWERPC: if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); @@ -4399,6 +4408,12 @@ void Options::reconfigureDefaults() fMacVersionMin = ld::mac10_4; } break; + case CPU_TYPE_POWERPC64: + if ( fMacVersionMin < ld::mac10_4 ) { + //warning("-macosx_version_min should be 10.4 or later for ppc64"); + fMacVersionMin = ld::mac10_4; + } + break; case CPU_TYPE_X86_64: if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); @@ -4414,6 +4429,8 @@ void Options::reconfigureDefaults() } // default to adding functions start for dynamic code, static code must opt-in + // FIXME/TODO: decide if these should default off for some set of "earlier" + // OS X versions. switch ( fOutputKind ) { case Options::kPreload: case Options::kStaticExecutable: @@ -4470,6 +4487,7 @@ void Options::reconfigureDefaults() break; } // else use object file + case CPU_TYPE_POWERPC: case CPU_TYPE_I386: // use .o files fOutputKind = kObjectFile; @@ -4512,8 +4530,9 @@ void Options::reconfigureDefaults() // split segs only allowed for dylibs if ( fSplitSegs ) { - // split seg only supported for i386, and arm. + // split seg only supported for ppc, i386, and arm. switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: case CPU_TYPE_I386: if ( fOutputKind != Options::kDynamicLibrary ) fSplitSegs = false; @@ -4540,9 +4559,11 @@ void Options::reconfigureDefaults() // set too-large size switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: case CPU_TYPE_I386: fMaxAddress = 0xFFFFFFFF; break; + case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: break; case CPU_TYPE_ARM: @@ -4576,6 +4597,7 @@ void Options::reconfigureDefaults() // disable prebinding depending on arch and min OS version if ( fPrebind ) { switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: case CPU_TYPE_I386: if ( fMacVersionMin == ld::mac10_4 ) { // in 10.4 only split seg dylibs are prebound @@ -4609,6 +4631,7 @@ void Options::reconfigureDefaults() } } break; + case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: fPrebind = false; break; @@ -4702,6 +4725,10 @@ void Options::reconfigureDefaults() 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 + if ( fMacVersionMin < ld::mac10_4 ) + fNeedsModuleTable = true; + break; case CPU_TYPE_ARM: if ( fPrebind ) fNeedsModuleTable = true; // redo_prebinding requires a module table @@ -4743,6 +4770,8 @@ void Options::reconfigureDefaults() break; } break; + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: case CPU_TYPE_ARM: if ( armUsesZeroCostExceptions() ) { switch ( fOutputKind ) { @@ -4842,6 +4871,13 @@ void Options::reconfigureDefaults() if ( fMakeCompressedDyldInfo ) { if ( !minOS(ld::mac10_6, ld::iOS_3_1) ) fMakeCompressedDyldInfo = false; + switch (fArchitecture) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + fMakeCompressedDyldInfo = false; + default: + break; + } } // only ARM and x86_64 enforces that cpu-sub-types must match @@ -5326,10 +5362,12 @@ void Options::checkIllegalOptionCombinations() if ( fStackAddr != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: + case CPU_TYPE_POWERPC: case CPU_TYPE_ARM: if ( fStackAddr > 0xFFFFFFFF ) throw "-stack_addr must be < 4G for 32-bit processes"; break; + case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: case CPU_TYPE_ARM64: break; @@ -5359,6 +5397,13 @@ void Options::checkIllegalOptionCombinations() fStackAddr = 0xC0000000; } break; + case CPU_TYPE_POWERPC: + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4G for 32-bit processes"; + if ( fStackAddr == 0 ) { + fStackAddr = 0xC0000000; + } + break; case CPU_TYPE_ARM: if ( fStackSize > 0x1F000000 ) throw "-stack_size must be < 496MB"; @@ -5367,6 +5412,7 @@ void Options::checkIllegalOptionCombinations() if ( fStackAddr > 0x20000000) throw "-stack_addr must be < 0x20000000 for arm"; break; + case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: if ( fPlatform == kPlatformOSX ) { if ( fStackSize > 0x10000000000 ) @@ -5497,6 +5543,7 @@ void Options::checkIllegalOptionCombinations() if ( fObjCABIVersion2Override ) alterObjC1ClassNamesToObjC2 = true; break; + case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: case CPU_TYPE_ARM64: @@ -5590,11 +5637,19 @@ void Options::checkIllegalOptionCombinations() if ( fZeroPageSize == ULLONG_MAX ) { // zero page size not specified on command line, set default switch (fArchitecture) { + case CPU_TYPE_POWERPC: case CPU_TYPE_I386: case CPU_TYPE_ARM: // first 4KB for 32-bit architectures fZeroPageSize = 0x1000; break; + case CPU_TYPE_POWERPC64: + // first 4GB for ppc64 on 10.5 + if ( fMacVersionMin >= ld::mac10_5 ) + fZeroPageSize = 0x100000000ULL; + else + fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page + break; case CPU_TYPE_ARM64: case CPU_TYPE_X86_64: // first 4GB for x86_64 on all OS's @@ -5823,6 +5878,7 @@ void Options::checkForClassic(int argc, const char* argv[]) void Options::gotoClassicLinker(int argc, const char* argv[]) { +// FIXME: some of this looks somewhat dodgy argv[0] = "ld_classic"; // ld_classic does not support -iphoneos_version_min, so change for(int j=0; j < argc; ++j) { diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 9cf6cd8..86322ad 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -761,6 +761,38 @@ void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& sta } } +bool OutputFile::checkPPCBranch24Displacement(int64_t displacement) +{ + const int64_t lim = 0x01FFFFFC; + return ( (displacement < lim) && (displacement > (-lim)) ); +} + +void OutputFile::rangeCheckPPCBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if (checkPPCBranch24Displacement (displacement)) + return; + + printSectionLayout(state); + + const ld::Atom* target; + throwf("bl PPC branch out of range (%" PRId64 " max is +/-32MB): from %s (0x%08" PRIX64 ") to %s (0x%08" PRIX64 ")", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); +} + +void OutputFile::rangeCheckPPCBranch14(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; + if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("bcc PPC branch out of range (%lld max is +/-64KB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} void OutputFile::rangeCheckARM64Branch26(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { @@ -1341,6 +1373,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: int64_t delta; uint32_t instruction; uint32_t newInstruction; + uint16_t instructionLowHalf; bool is_bl; bool is_blx; bool is_b; @@ -1536,6 +1569,70 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } set32LE(fixUpLocation, newInstruction); break; + case ld::Fixup::kindStorePPCBranch14: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckPPCBranch14(delta, state, atom, fit); + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)delta & 0x0000FFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + accumulator = addressOf(state, fit, &toTarget); + if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) { + // Branching to island. If ultimate target is in range, branch there directly. + for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); + islandfit != end; ++islandfit) { + if ( islandfit->kind == ld::Fixup::kindIslandTarget ) { + const ld::Atom* islandTarget = NULL; + uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget); + delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4); + if ( checkPPCBranch24Displacement(delta) ) { + toTarget = islandTarget; + accumulator = islandTargetAddress; + thumbTarget = targetIsThumb(state, islandfit); + } + break; + } + } + } + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStorePPCBranch24 case + case ld::Fixup::kindStorePPCBranch24: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckPPCBranch24(delta, state, atom, fit); + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)delta & 0x03FFFFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCAbsLow14: + instruction = get32BE(fixUpLocation); + if ( (accumulator & 0x3) != 0 ) + throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)accumulator); + newInstruction = (instruction & 0xFFFF0003) | (accumulator & 0xFFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsLow16: + case ld::Fixup::kindStorePPCPicLow16: + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | (accumulator & 0xFFFF); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + instructionLowHalf = (accumulator >> 16) & 0xFFFF; + if ( accumulator & 0x00008000 ) + ++instructionLowHalf; + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsHigh16: + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | ((accumulator >> 16) & 0xFFFF); + set32BE(fixUpLocation, newInstruction); + break; case ld::Fixup::kindDtraceExtra: break; case ld::Fixup::kindStoreX86DtraceCallSiteNop: @@ -1558,6 +1655,18 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: fixUpLocation[3] = 0x90; // 1-byte nop } break; + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32BE(fixUpLocation, 0x60000000); + } + break; + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a li r3,0 + set32BE(fixUpLocation, 0x38600000); + } + break; case ld::Fixup::kindStoreARMDtraceCallSiteNop: if ( _options.outputKind() != Options::kObjectFile ) { // change call site to a NOP @@ -2531,12 +2640,16 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } } #endif // SUPPORT_ARCH_arm64 - } void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb) { switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); + break; case CPU_TYPE_I386: case CPU_TYPE_X86_64: for (uint8_t* p=from; p < to; ++p) @@ -3264,6 +3377,66 @@ void OutputFile::addLinkEdit(ld::Internal& state) return addPreloadLinkEdit(state); switch ( _options.architecture() ) { +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + // FIXME: The next three are entered for compatibility, but unless dyld is + // updated are probably useless. + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; +#endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( _hasSectionRelocations ) { @@ -3507,6 +3680,66 @@ void OutputFile::addLinkEdit(ld::Internal& state) stringPoolSection = state.addAtom(*_stringPoolAtom); } break; +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + // FIXME: The next three are entered for compatibility, but unless dyld is + // updated are probably useless. + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 8); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; #endif default: throw "unknown architecture"; @@ -3539,6 +3772,18 @@ void OutputFile::addLoadCommands(ld::Internal& state) _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; +#endif +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; #endif default: throw "unknown architecture"; @@ -3699,6 +3944,11 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreARMLoad12: + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStorePPCBranch14: + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + case ld::Fixup::kindStorePPCPicHigh16AddLow: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: @@ -3730,6 +3980,7 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: #endif + case ld::Fixup::kindStoreTargetAddressPPCBranch24: return true; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: #if SUPPORT_ARCH_arm64 @@ -3797,6 +4048,7 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: #endif + case ld::Fixup::kindStoreTargetAddressPPCBranch24: return true; case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: @@ -3806,6 +4058,8 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: return (_options.outputKind() == Options::kObjectFile); default: break; @@ -4101,7 +4355,8 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } // 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 ( (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) ) { @@ -4109,10 +4364,12 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s return; } // Have direct reference to weak-global. This should be an indrect reference - const char* demangledName = strdup(_options.demangleSymbol(atom->name())); - warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. " + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); + const char* demangledTgt = strdup(_options.demangleSymbol(target->name())); + const char* demangledMin = strdup(_options.demangleSymbol(minusTarget->name())); + warning("direct access in %s to global weak symbol %s (via %s + %lu) means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path()); + demangledName, demangledTgt, demangledMin, minusTargetAddend); } return; } @@ -4427,6 +4684,25 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio sect->hasLocalRelocs = true; } break; + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCAbsHigh16: + { + assert(target != NULL); + if ( target->definition() == ld::Atom::definitionProxy ) + throwf("half word text relocs not supported in %s", atom->name()); + if ( _options.outputSlidable() ) { + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + uint32_t machoSectionIndex = (target->definition() == ld::Atom::definitionAbsolute) + ? R_ABS : target->machoSection(); + _localRelocsAtom->addTextReloc(relocAddress, fixupWithTarget->kind, + target->finalAddress(), machoSectionIndex); + sect->hasLocalRelocs = true; + } + } + break; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: #if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreTargetAddressARM64Branch26: @@ -4651,6 +4927,7 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreX86PCRel32GOT: case ld::Fixup::kindStoreX86PCRel32TLVLoad: case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStorePPCPicHigh16AddLow: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index 81f2cfb..482a771 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -212,7 +212,8 @@ class OutputFile bool hasZeroForFileOffset(const ld::Section* sect); void printSectionLayout(ld::Internal& state); - + + bool checkPPCBranch24Displacement(int64_t displacement); bool checkThumbBranch22Displacement(int64_t displacement); bool checkArmBranch24Displacement(int64_t displacement); @@ -238,6 +239,10 @@ class OutputFile const ld::Fixup* fixup); + void rangeCheckPPCBranch24(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckPPCBranch14(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); void synthesizeDebugNotes(ld::Internal& state); diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index 5344f53..adca3c5 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -308,6 +308,26 @@ void Resolver::buildAtomList() //_symbolTable.printStatistics(); } +unsigned int Resolver::ppcSubTypeIndex(uint32_t subtype) +{ + switch ( subtype ) { + case CPU_SUBTYPE_POWERPC_ALL: + return 0; + case CPU_SUBTYPE_POWERPC_750: + // G3 + return 1; + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + // G4 + return 2; + case CPU_SUBTYPE_POWERPC_970: + // G5 can run everything + return 3; + default: + throw "Unhandled PPC cpu subtype!"; + break; + } +} void Resolver::doLinkerOption(const std::vector& linkerOption, const char* fileName) { @@ -505,6 +525,29 @@ void Resolver::doFile(const ld::File& file) // update cpu-sub-type cpu_subtype_t nextObjectSubType = file.cpuSubType(); switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + // no checking when -force_cpusubtype_ALL is used + if ( _options.forceCpuSubtypeAll() ) + return; + if ( _options.preferSubArchitecture() ) { + // warn if some .o file is not compatible with desired output sub-type + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_options.subArchitecture()) ) { + if ( !_inputFiles.inferredArch() ) + warning("cpu-sub-type of %s is not compatible with command line cpu-sub-type", file.path()); + _internal.cpuSubType = nextObjectSubType; + } + } + } + else { + // command line to linker just had -arch ppc + // figure out final sub-type based on sub-type of all .o files + if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_internal.cpuSubType) ) { + _internal.cpuSubType = nextObjectSubType; + } + } + break; + case CPU_TYPE_ARM: if ( _options.subArchitecture() != nextObjectSubType ) { if ( (_options.subArchitecture() == CPU_SUBTYPE_ARM_ALL) && _options.forceCpuSubtypeAll() ) { @@ -524,6 +567,9 @@ void Resolver::doFile(const ld::File& file) } break; + case CPU_TYPE_POWERPC64: + break; + case CPU_TYPE_I386: _internal.cpuSubType = CPU_SUBTYPE_I386_ALL; break; @@ -814,6 +860,8 @@ bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: case ld::Fixup::kindDtraceExtra: return true; default: @@ -1050,6 +1098,7 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: #endif + case ld::Fixup::kindStoreTargetAddressPPCBranch24: if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() // but a archive loaded .o file may have a forward reference diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index fe2c4cf..7ae7235 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -102,6 +102,7 @@ class Resolver : public ld::File::AtomHandler bool isDtraceProbe(ld::Fixup::Kind kind); void liveUndefines(std::vector&); void remainingUndefines(std::vector&); + static unsigned int ppcSubTypeIndex(uint32_t subtype); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); void tweakWeakness(); void buildArchivesList(); diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 0bb2a81..7dce870 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -436,12 +436,17 @@ struct Fixup kindStoreARM64TLVPLoadNowLeaPage21, kindStoreARM64TLVPLoadNowLeaPageOff12, kindStoreARM64PointerToGOT, kindStoreARM64PCRelToGOT, #endif + // PowerPC specific store kinds + kindStorePPCBranch24, kindStorePPCBranch14, + kindStorePPCPicLow14, kindStorePPCPicLow16, kindStorePPCPicHigh16AddLow, + kindStorePPCAbsLow14, kindStorePPCAbsLow16, kindStorePPCAbsHigh16AddLow, kindStorePPCAbsHigh16, // dtrace probes kindDtraceExtra, kindStoreX86DtraceCallSiteNop, kindStoreX86DtraceIsEnableSiteClear, kindStoreARMDtraceCallSiteNop, kindStoreARMDtraceIsEnableSiteClear, kindStoreARM64DtraceCallSiteNop, kindStoreARM64DtraceIsEnableSiteClear, kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, + kindStorePPCDtraceCallSiteNop, kindStorePPCDtraceIsEnableSiteClear, // lazy binding kindLazyTarget, kindSetLazyOffset, // islands @@ -485,6 +490,8 @@ struct Fixup kindStoreTargetAddressARM64TLVPLoadNowLeaPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPage21 kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPageOff12 #endif + // PowerPC value calculation and store combinations + kindStoreTargetAddressPPCBranch24, // kindSetTargetAddress + kindStorePPCBranch24 }; union { diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index ce08e83..c09c40a 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -227,6 +227,8 @@ const class File::Entry* File::Entry::next() const } +template <> cpu_type_t File::architecture() { return CPU_TYPE_POWERPC; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_POWERPC64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_I386; } template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } @@ -342,6 +344,12 @@ bool File::memberHasObjCCategories(const Entry* member) const } } +template <> +bool File::memberHasObjCCategories(const Entry* member) const +{ + // ppc uses ObjC1 ABI which has .objc_category* global symbols + return false; +} template @@ -671,6 +679,18 @@ ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; #endif } return NULL; diff --git a/ld64/src/ld/parsers/libunwind/AddressSpace.hpp b/ld64/src/ld/parsers/libunwind/AddressSpace.hpp index bebd3e6..8509a76 100644 --- a/ld64/src/ld/parsers/libunwind/AddressSpace.hpp +++ b/ld64/src/ld/parsers/libunwind/AddressSpace.hpp @@ -424,6 +424,16 @@ struct unw_addr_space_ppc : public unw_addr_space OtherAddressSpace > oas; }; +/// +/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points to when examining +/// a 64-bit PowerPC process. +/// +struct unw_addr_space_ppc64 : public unw_addr_space +{ + unw_addr_space_ppc(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + #endif // UNW_REMOTE diff --git a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp index c14c38f..5636993 100644 --- a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -174,7 +174,15 @@ class DwarfInstructions static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, const Registers_arm&, const typename CFI_Parser::PrologInfo& prolog, char warningBuffer[1024]); - + + // ppc64 specific variants + static int lastRestoreReg(const Registers_ppc64&); + static bool isReturnAddressRegister(int regNum, const Registers_ppc64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_ppc64&); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); }; @@ -1778,6 +1786,21 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo } +// +// ppc64 specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_ppc64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR ); + return UNW_PPC_SPEFSCR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_ppc64&) +{ + return (regNum == UNW_PPC_LR); +} // // arm64 specific functions @@ -1840,6 +1863,24 @@ bool DwarfInstructions::checkRegisterPair(uint32_t reg, const typename CFI_ } return false; } +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_ppc64& 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 ppc64 cfa"); +} + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_ppc64&) +{ + return UNWIND_X86_MODE_DWARF; +} template @@ -1968,6 +2009,14 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo return encoding; } +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + return UNWIND_X86_MODE_DWARF; +} diff --git a/ld64/src/ld/parsers/libunwind/Registers.hpp b/ld64/src/ld/parsers/libunwind/Registers.hpp index 66e66cc..ceacc28 100644 --- a/ld64/src/ld/parsers/libunwind/Registers.hpp +++ b/ld64/src/ld/parsers/libunwind/Registers.hpp @@ -1316,6 +1316,405 @@ inline void Registers_arm64::setVectorRegister(int regNum, v128 value) ABORT("no arm64 vector register support yet"); } +/// +/// Registers_ppc holds the register state of a thread in a 64-bit PowerPC process. +/// +class Registers_ppc64 +{ +public: + Registers_ppc64(); + Registers_ppc64(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; + 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_state64_t + { + unsigned long long __srr0; /* Instruction address register (PC) */ + unsigned long long __srr1; /* Machine state register (supervisor) */ + unsigned long long __r0; + unsigned long long __r1; + unsigned long long __r2; + unsigned long long __r3; + unsigned long long __r4; + unsigned long long __r5; + unsigned long long __r6; + unsigned long long __r7; + unsigned long long __r8; + unsigned long long __r9; + unsigned long long __r10; + unsigned long long __r11; + unsigned long long __r12; + unsigned long long __r13; + unsigned long long __r14; + unsigned long long __r15; + unsigned long long __r16; + unsigned long long __r17; + unsigned long long __r18; + unsigned long long __r19; + unsigned long long __r20; + unsigned long long __r21; + unsigned long long __r22; + unsigned long long __r23; + unsigned long long __r24; + unsigned long long __r25; + unsigned long long __r26; + unsigned long long __r27; + unsigned long long __r28; + unsigned long long __r29; + unsigned long long __r30; + unsigned long long __r31; + unsigned int __cr; /* Condition register */ + unsigned long long __xer; /* User's integer exception register */ + unsigned long long __lr; /* Link register */ + unsigned long long __ctr; /* Count register */ + 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_state64_t fRegisters; + ppc_float_state_t fFloatRegisters; + v128 fVectorRegisters[32]; // offset +}; + + + +inline Registers_ppc64::Registers_ppc64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_ppc64) < sizeof(unw_context_t) ); + fRegisters = *((ppc_thread_state64_t*)registers); + fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160)); + memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters)); +} + +inline Registers_ppc64::Registers_ppc64() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fFloatRegisters, sizeof(fFloatRegisters)); + bzero(&fVectorRegisters, sizeof(fVectorRegisters)); +} + + +inline bool Registers_ppc64::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_LR ) + return true; + if ( regNum == UNW_PPC_CTR ) + return true; + if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) ) + return true; + return false; +} + + +inline uint64_t Registers_ppc64::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_ppc64::setRegister(int regNum, uint64_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_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_ppc64::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_PPC_F0 ) + return false; + if ( regNum > UNW_PPC_F31 ) + return false; + return true; +} + +inline double Registers_ppc64::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0]; +} + +inline void Registers_ppc64::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; +} /// /// Registers_arm holds the register state of a thread in a 32-bit arm process. @@ -1350,6 +1749,176 @@ inline Registers_arm::Registers_arm() { } +inline bool Registers_ppc64::validVectorRegister(int regNum) const +{ + if ( regNum < UNW_PPC_V0 ) + return false; + if ( regNum > UNW_PPC_V31 ) + return false; + return true; +} + +v128 Registers_ppc64::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_ppc64::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_ppc64::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 diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index 8ea0627..f8fcb13 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -633,6 +633,60 @@ class Parser +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) +{ + 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_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + template <> bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { @@ -770,9 +824,43 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = CPU_SUBTYPE_ARM64_ALL; return true; } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_POWERPC; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_POWERPC64; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } return false; } +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return NULL; + return "ppc"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return NULL; + return "ppc64"; +} + template <> const char* Parser::fileKind(const uint8_t* fileContent) { @@ -839,6 +927,12 @@ const char* archName(const uint8_t* fileContent) if ( Parser::validFile(fileContent, true) ) { return Parser::fileKind(fileContent); } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } #if SUPPORT_ARCH_arm64 if ( Parser::validFile(fileContent, false) ) { return Parser::fileKind(fileContent); @@ -880,6 +974,18 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; #endif } return nullptr; diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index a32a4e2..1df5fb0 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -192,6 +192,7 @@ class Section : public ld::Section _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } + bool addRelocFixup_powerpc(class Parser& parser,const macho_relocation_info* reloc); Atom* findContentAtomByAddress(pint_t addr, class Atom* start, class Atom* end); uint32_t x86_64PcRelOffset(uint8_t r_type); void addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]); @@ -1303,6 +1304,31 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p { } +template <> +bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} template <> bool Parser::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) @@ -1365,6 +1391,39 @@ bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, return true; } +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_POWERPC_750: + return "ppc750"; + case CPU_SUBTYPE_POWERPC_7400: + return "ppc7400"; + case CPU_SUBTYPE_POWERPC_7450: + return "ppc7450"; + case CPU_SUBTYPE_POWERPC_970: + return "ppc970"; + case CPU_SUBTYPE_POWERPC_ALL: + return "ppc"; + } + return "ppc???"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return NULL; + return "ppc64"; +} template <> const char* Parser::fileKind(const uint8_t* fileContent) @@ -1983,6 +2042,8 @@ static void versionToString(uint32_t value, char buffer[32]) sprintf(buffer, "%d.%d", value >> 16, (value >> 8) & 0xFF); } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } @@ -3079,6 +3140,9 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12; break; #endif + case ld::Fixup::kindStorePPCBranch24: + firstKind = ld::Fixup::kindStoreTargetAddressPPCBranch24; + break; default: combined = false; cl = ld::Fixup::k1of2; @@ -4414,7 +4478,7 @@ bool CFISection::needsRelocating() template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + struct libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { const uint32_t sectionSize = this->_machOSection->size(); @@ -4479,7 +4543,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + struct libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // create ObjectAddressSpace object for use by libunwind @@ -4495,12 +4559,47 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, throwf("malformed __eh_frame section: %s", msg); } +// need to change libunwind parseCFIs() to work for ppc +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + struct libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + struct libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) +{ + // FIXME: libunwind does not support ppc64 - is this OK? + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + struct libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { if ( !parser.armUsesZeroCostExceptions() ) { @@ -4526,7 +4625,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + struct libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // copy __eh_frame data to buffer @@ -4629,6 +4728,8 @@ template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } +template <> bool CFISection::bigEndian() { return true; } +template <> bool CFISection::bigEndian() { return true; } template <> void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) @@ -4724,7 +4825,53 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const C } } +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreBigEndian32); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} + +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); +//FIXME: this is a bit of a bodge - 0x90 implies abs addr, 0x9b implies 4byte offset. + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreBigEndian32); + } +// else if ( personalityEncoding != DW_EH_PE_omit ) { + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) @@ -5064,6 +5211,18 @@ const char* CUSection::personalityName(class Parser& parser, const macho_r return NULL; } +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return true; // Always DWARF +} + +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return true; // Always DWARF +} + template <> bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) { @@ -5635,6 +5794,17 @@ ld::Fixup::Kind NonLazyPointerSection::fixupKind() return ld::Fixup::kindStoreLittleEndian64; } +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreBigEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreBigEndian64; +} template <> void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -6690,6 +6860,406 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati +// +// ppc and ppc64 both use the same relocations, so process them in one common routine +// +template +bool Section::addRelocFixup_powerpc(class Parser& parser, + const macho_relocation_info* reloc) +{ + const macho_section

* sect = this->machoSection(); + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + int16_t lowBits; + pint_t contentValue = 0; + typename Parser::SourceLocation src; + typename Parser::TargetDesc target; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const macho_relocation_info

* nextReloc = &reloc[1]; + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != PPC_RELOC_PAIR ) + instruction = BigEndian::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); + } + switch ( reloc->r_type() ) { + case PPC_RELOC_BR24: + assert((instruction & 0x4C000000) == 0x48000000); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStorePPCDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStorePPCDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + } + break; + case PPC_RELOC_BR14: + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_LO16 missing following pair"; + result = true; + lowBits = (instruction & 0x0000FFFF); + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); + break; + case PPC_RELOC_LO14: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_LO14 missing following pair"; + result = true; + lowBits = (instruction & 0xFFFC); + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); + break; + case PPC_RELOC_HI16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_HI16 missing following pair"; + result = true; + lowBits = (nextReloc->r_address() & 0xFFFF); + dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); + break; + case PPC_RELOC_HA16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_HA16 missing following pair"; + result = true; + lowBits = (nextReloc->r_address() & 0x0000FFFF); + dstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); + break; + case PPC_RELOC_VANILLA: + contentValue = P::getP(*((pint_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + target.addend = contentValue; + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "bad r_length in PPC_RELOC_VANILLA"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); + break; + } + break; + case PPC_RELOC_JBSR: + // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_JBSR missing following pair"; + if ( !parser._hasLongBranchStubs ) + warning("object file compiled with -mlong-branch which is no longer needed. " + "To remove this warning, recompile without -mlong-branch: %s", parser._path); + parser._hasLongBranchStubs = true; + result = true; + if ( reloc->r_extern() ) { + throw "PPC_RELOC_JBSR should not be using an external relocation"; + } + parser.findTargetFromAddressAndSectionNum(nextReloc->r_address(), reloc->r_symbolnum(), target); + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info

* nextSReloc = &sreloc[1]; + const macho_relocation_info

* nextReloc = &reloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + sreloc->r_address()); + instruction = BigEndian::get32(*fixUpPtr); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + typename Parser::TargetDesc picBase; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch ( sreloc->r_type() ) { + case PPC_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + target.atom = parser.findAtomByAddress(sreloc->r_value()); + switch ( sreloc->r_length() ) { + case 0: + case 1: + throw "unsuppored r_length < 2 for scattered PPC_RELOC_VANILLA"; + case 2: + contentValue = BigEndian::get32(*(uint32_t*)fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); + break; + case 3: + contentValue = BigEndian::get64(*(uint64_t*)fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); + break; + } + break; + case PPC_RELOC_BR14: + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (srcAddr + displacement) - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); + break; + case PPC_RELOC_BR24: + assert((instruction & 0x4C000000) == 0x48000000); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (srcAddr + displacement) - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + break; + case PPC_RELOC_LO16_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; + lowBits = (instruction & 0xFFFF); + dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicLow16, target, picBase); + break; + case PPC_RELOC_LO14_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; + lowBits = (instruction & 0xFFFC); + dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicLow14, target, picBase); + break; + case PPC_RELOC_HA16_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; + lowBits = (nextRelocAddress & 0x0000FFFF); + dstAddr = nextRelocValue + (((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicHigh16AddLow, target, picBase); + break; + case PPC_RELOC_LO14: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO14 missing following pair"; + lowBits = (instruction & 0xFFFC); + dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); + break; + case PPC_RELOC_LO16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO16 missing following pair"; + lowBits = (instruction & 0xFFFF); + dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); + break; + case PPC_RELOC_HA16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HA16 missing following pair"; + lowBits = (nextRelocAddress & 0xFFFF); + dstAddr = (((instruction & 0xFFFF) << 16) + (int32_t)lowBits); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); + break; + case PPC_RELOC_HI16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HI16 missing following pair"; + lowBits = (nextRelocAddress & 0xFFFF); + dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_SECTDIFF missing following pair"; + ld::Fixup::Kind kind = ld::Fixup::kindNone; + switch ( sreloc->r_length() ) { + case 0: + throw "bad length for PPC_RELOC_SECTDIFF"; + case 1: + contentValue = (int32_t)(int16_t)BigEndian::get16(*((uint16_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian16; + break; + case 2: + contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian32; + break; + case 3: + contentValue = BigEndian::get64(*((uint64_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian64; + break; + break; + } + Atom* fromAtom = parser.findAtomByAddress(nextRelocValue); + Atom* targetAtom = parser.findAtomByAddress(sreloc->r_value()); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress; + // check for addend encoded in the section content + int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + if ( addend < 0 ) { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + 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); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + else { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + return addRelocFixup_powerpc(parser, reloc); +} + + +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + return addRelocFixup_powerpc(parser, reloc); +} + #if SUPPORT_ARCH_arm_any @@ -7513,6 +8083,41 @@ bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const mach return FixedSizeSection::addRelocFixup(parser, reloc); } +template <> +bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) +{ + // if this is the reloc for the super class name string, add implicit reference to super class + if ( ((reloc->r_address() & R_SCATTERED) == 0) && (reloc->r_type() == PPC_RELOC_VANILLA) ) { + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section

* sect = this->machoSection(); + Parser::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + if ( src.offsetInAtom == 4 ) { + Parser::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* superClassBaseName = (char*)stringTarget.atom->rawContentPointer(); + char* superClassName = new char[strlen(superClassBaseName) + 20]; + strcpy(superClassName, ".objc_class_name_"); + strcat(superClassName, superClassBaseName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, superClassName); + } + } + + // inherited + return FixedSizeSection::addRelocFixup(parser, reloc); +} + + template @@ -7526,6 +8131,38 @@ bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho } +template <> +bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) +{ + // add implict class refs, fixups not usable yet, so look at relocations + assert( (reloc->r_address() & R_SCATTERED) == 0 ); + assert( reloc->r_type() == PPC_RELOC_VANILLA ); + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section

* sect = this->machoSection(); + Parser::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + Parser::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* baseClassName = (char*)stringTarget.atom->rawContentPointer(); + char* objcClassName = new char[strlen(baseClassName) + 20]; + strcpy(objcClassName, ".objc_class_name_"); + strcat(objcClassName, baseClassName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, objcClassName); + + // inherited + return PointerToCStringSection::addRelocFixup(parser, reloc); +} + template <> bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const macho_relocation_info* reloc) @@ -7842,6 +8479,18 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Parser::validFile(fileContent) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Parser::validFile(fileContent) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; #endif } return NULL; @@ -7861,6 +8510,10 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); case CPU_TYPE_ARM64: return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); + case CPU_TYPE_POWERPC: + return ( mach_o::relocatable::Parser::validFile(fileContent) ); + case CPU_TYPE_POWERPC64: + return ( mach_o::relocatable::Parser::validFile(fileContent) ); } return false; } @@ -7898,6 +8551,17 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *platform = Parser::findPlatform(header); return true; } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + *result = CPU_TYPE_POWERPC; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + *result = CPU_TYPE_POWERPC64; + *subResult = CPU_SUBTYPE_POWERPC_ALL; + return true; + } return false; } @@ -7915,6 +8579,12 @@ const char* archName(const uint8_t* fileContent) if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::fileKind(fileContent); } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::fileKind(fileContent); + } return NULL; } diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index 717125f..ca3ca24 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -278,6 +278,16 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch case CPU_TYPE_ARM64: if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); +#endif +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) + return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) + return Parser::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); #endif } return nullptr; diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 65e3cf2..7d2b8c1 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -22,8 +22,10 @@ * @APPLE_LICENSE_HEADER_END@ */ - #include +#define __STDC_FORMAT_MACROS +#include +#include #include #include #include @@ -60,6 +62,37 @@ class TargetAndOffsetComparor static bool _s_log = false; static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); +class PPCBranchIslandAtom : public ld::Atom { +public: + PPCBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressPPCBranch24, target), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%s: PPC jump instruction branch island to final target %s\n", + target->name(), finalTarget.atom->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 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(buffer, 0, 0x48000000); + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const char* _name; + ld::Fixup _fixup1; + ld::Fixup _fixup2; +}; #if SUPPORT_ARCH_arm64 @@ -273,18 +306,27 @@ class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget, const ld::Section& inSect, bool crossSectionBranch) { - char* name; + char *name = NULL; + const char *aname; + if (finalTarget.atom->name() == NULL || finalTarget.atom->name()[0] == 0) + aname = "anon"; + else + aname = finalTarget.atom->name(); if ( finalTarget.offset == 0 ) { if ( islandRegion == 0 ) - asprintf(&name, "%s.island", finalTarget.atom->name()); + asprintf(&name, "%s.island", aname); else - asprintf(&name, "%s.island.%d", finalTarget.atom->name(), islandRegion+1); + asprintf(&name, "%s.island.%d", aname, islandRegion); } else { - asprintf(&name, "%s_plus_%d.island.%d", finalTarget.atom->name(), finalTarget.offset, islandRegion); + asprintf(&name, "%s_plus_%d.island.%d", aname, finalTarget.offset, islandRegion); } switch ( kind ) { + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return new PPCBranchIslandAtom(name, nextTarget, finalTarget); + break; case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: @@ -324,6 +366,10 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool seenThumbBranch) { switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return 32000000; // PPC can branch +/- 32MB + break; case CPU_TYPE_ARM: if ( ! seenThumbBranch ) return 32000000; // ARM can branch +/- 32MB @@ -346,6 +392,10 @@ static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool see static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBranch) { switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return 30*1024*1024; // 2MB of branch islands per 32MB + break; case CPU_TYPE_ARM: if ( ! seenThumbBranch ) return 30*1024*1024; // 2MB of branch islands per 32MB @@ -365,8 +415,8 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra } -// -// PowerPC can do PC relative branches as far as +/-16MB. +// FIXME: comment stuff is out of date. +// PowerPC can do PC relative branches as far as +/-32MB. // If a branch target is >16MB then we insert one or more // "branch islands" between the branch and its target that // allows island hopping to the target. @@ -388,7 +438,6 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra // before any branches could be pushed out of range. // - static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection, unsigned stubCount) { // assign section offsets to each atom in __text section, watch for thumb branches, and find total size @@ -424,10 +473,12 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: // fall into arm branch case case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: haveBranch = true; break; default: - break; + break; } if ( haveBranch && (target->contentType() != ld::Atom::typeStub) ) { // haveCrossSectionBranches only applies to -preload builds @@ -451,7 +502,8 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: uint64_t totalTextSize = offset + stubCount*16; if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches ) return; - if (_s_log) fprintf(stderr, "ld: section %s size=%llu, might need branch islands\n", textSection->sectionName(), totalTextSize); +//if (_s_log) +fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", textSection->sectionName(), totalTextSize); // Figure out how many regions of branch islands will be needed, and their locations. // Construct a vector containing the atoms after which branch islands will be inserted, @@ -528,6 +580,8 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: case ld::Fixup::kindAddAddend: addend = fit->u.addend; break; + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: @@ -552,6 +606,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: if ( target->section().type() == ld::Section::typeStub ) dstAddr = totalTextSize; int64_t displacement = dstAddr - srcAddr; + // FIXME: Do we really mean to truncate? TargetAndOffset finalTargetAndOffset = { target, (uint32_t)addend }; const int64_t kBranchLimit = kBetweenRegions; if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { @@ -707,9 +762,11 @@ void doPass(const Options& opts, ld::Internal& state) // Allow user to disable branch island generation if ( !opts.allowBranchIslands() ) return; - - // only ARM[64] needs branch islands + + // only ARM[64] and PPC need branch islands switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: case CPU_TYPE_ARM: #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: @@ -718,7 +775,7 @@ void doPass(const Options& opts, ld::Internal& state) default: return; } - + if ( opts.outputKind() == Options::kPreload ) { buildAddressMap(opts, state); } diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index d685454..eb86691 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -142,12 +142,14 @@ void doPass(const Options& opts, ld::Internal& internal) for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { switch ( fit->kind ) { case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreARM64DtraceCallSiteNop: probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: @@ -169,6 +171,10 @@ void doPass(const Options& opts, ld::Internal& internal) ld::Fixup::Kind storeKind = ld::Fixup::kindNone; switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + storeKind = ld::Fixup::kindStoreBigEndian32; + break; case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: diff --git a/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index 2100479..2eadebc 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -1357,8 +1357,18 @@ void doPass(const Options& opts, ld::Internal& state) doPass(opts, state); break; #endif - default: - assert(0 && "unknown objc arch"); +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: + doPass(opts, state); + break; +#endif +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + doPass(opts, state); + break; +#endif + default: + assert(0 && "unknown objc arch"); } } diff --git a/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp b/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp new file mode 100644 index 0000000..f780846 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_ppc_classic.hpp @@ -0,0 +1,189 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace ppc { +namespace classic { + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, for64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, + for64 ? ld::Fixup::kindStoreTargetAddressBigEndian64 : ld::Fixup::kindStoreTargetAddressBigEndian32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo), + _for64(for64) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return _for64 ? 8 : 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + const bool _for64; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStorePPCPicHigh16AddLow), + _fixup5(20, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup6(20, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup7(20, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup8(20, ld::Fixup::k4of4, for64 ? ld::Fixup::kindStorePPCPicLow14 : ld::Fixup::kindStorePPCPicLow16), + _for64(for64) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 32; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + if ( _for64 ) + OSWriteBigInt32(&buffer[20], 0, 0xe98b0001);// ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + else + OSWriteBigInt32(&buffer[20], 0, 0x858b0000);// lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + mutable ld::Fixup _fixup4; + mutable ld::Fixup _fixup5; + mutable ld::Fixup _fixup6; + mutable ld::Fixup _fixup7; + mutable ld::Fixup _fixup8; + const bool _for64; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub1", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), + _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStorePPCAbsHigh16AddLow), + _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup4(4, ld::Fixup::k2of2, for64 ? ld::Fixup::kindStorePPCAbsLow14 : ld::Fixup::kindStorePPCAbsLow16), + _for64(for64) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_foo$lazy_ptr) + if ( _for64 ) + OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001);// ldu r12,lo16(L_foo$lazy_ptr)(r11) + else + OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000);// lwzu r12,lo16(L_foo$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + mutable ld::Fixup _fixup4; + const bool _for64; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub1", ld::Section::typeStub); + + +} // namespace classic +} // namespace ppc diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index 47a1c45..2cd92d0 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -89,6 +89,7 @@ class Pass { #include "stub_x86_64_classic.hpp" #include "stub_x86.hpp" #include "stub_x86_classic.hpp" +#include "stub_ppc_classic.hpp" #include "stub_arm.hpp" #include "stub_arm_classic.hpp" #if SUPPORT_ARCH_arm64 @@ -118,6 +119,7 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) if ( fixup->binding == ld::Fixup::bindingsIndirectlyBound ) { const ld::Atom* target = state.indirectBindingTable[fixup->u.bindingIndex]; switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressPPCBranch24: case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: @@ -189,6 +191,20 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) } switch ( _architecture ) { +#if SUPPORT_ARCH_ppc + case CPU_TYPE_POWERPC: + if ( _pic ) + return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, false, weakImport); + else + return new ld::passes::stubs::ppc::classic::StubNoPICAtom(*this, target, forLazyDylib, false, weakImport); + break; +#endif +#if SUPPORT_ARCH_ppc64 + case CPU_TYPE_POWERPC64: +//FIXME: Is PPC64 always PIC? ... I thought it supported mydynamic nopic. + return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, true, weakImport); + break; +#endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: if ( usingCompressedLINKEDIT() && !forLazyDylib ) @@ -332,7 +348,9 @@ void Pass::process(ld::Internal& state) if ( _options.outputKind() != Options::kDynamicLibrary ) throwf("resolver functions (%s) can only be used in dylibs", atom->name()); if ( !_options.makeCompressedDyldInfo() ) { - if ( _options.architecture() == CPU_TYPE_ARM ) + if ( _options.architecture() == CPU_TYPE_POWERPC ) + throwf("resolver functions (%s) not supported for PowerPC", atom->name()); + else if ( _options.architecture() == CPU_TYPE_ARM ) throwf("resolver functions (%s) can only be used when targeting iOS 4.2 or later", atom->name()); else throwf("resolver functions (%s) can only be used when targeting Mac OS X 10.6 or later", atom->name()); diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index e7ab398..b2b9759 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -742,6 +742,33 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreBigEndian64: printf(", then store 64-bit big endian"); break; + case ld::Fixup::kindStorePPCBranch24: + printf(", then store as PPC branch24"); + break; + case ld::Fixup::kindStorePPCBranch14: + printf(", then store as PPC branch14"); + break; + case ld::Fixup::kindStorePPCPicLow14: + printf(", then store as PPC low14 pic"); + break; + case ld::Fixup::kindStorePPCPicLow16: + printf(", then store as PPC low14 pic"); + break; + case ld::Fixup::kindStorePPCPicHigh16AddLow: + printf(", then store as PPC high16 pic"); + break; + case ld::Fixup::kindStorePPCAbsLow14: + printf(", then store as PPC low14 abs"); + break; + case ld::Fixup::kindStorePPCAbsLow16: + printf(", then store as PPC low14 abs"); + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + printf(", then store as PPC high16 abs"); + break; + case ld::Fixup::kindStorePPCAbsHigh16: + printf(", then store as PPC high16 abs, no carry"); + break; case ld::Fixup::kindStoreX86BranchPCRel8: printf(", then store as x86 8-bit pcrel branch"); break; @@ -859,6 +886,12 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: printf("x86 dtrace static is-enabled site"); break; + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + printf("ppc dtrace static probe site"); + break; + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + printf("ppc dtrace static is-enabled site"); + break; case ld::Fixup::kindStoreARMDtraceCallSiteNop: printf("ARM dtrace static probe site"); break; @@ -989,6 +1022,9 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreTargetAddressARMLoad12: printf("ARM store 12-bit pc-rel branch to %s", referenceTargetAtomName(ref)); break; + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + printf("PowerPC store 24-bit pc-rel load of %s", referenceTargetAtomName(ref)); + break; case ld::Fixup::kindSetTargetTLVTemplateOffset: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 2d69fcd..b2eaa11 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -1946,6 +1946,8 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) { if ( r_type == GENERIC_RELOC_VANILLA ) return "pointer"; + else if ( r_type == PPC_RELOC_PB_LA_PTR ) + return "pb pointer"; else return "??"; } diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index dfe8d2e..8f4b678 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -1235,6 +1235,41 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* #endif +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_address() & R_SCATTERED ) { + // scattered + const macho_scattered_relocation_info

* sreloc = (const macho_scattered_relocation_info

*)reloc; + // FIX + + } + else { + // ignore pair relocs + if ( reloc->r_type() == PPC_RELOC_PAIR ) + return; + // FIX + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); + } +} + + +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + if ( reloc->r_length() != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type() != GENERIC_RELOC_VANILLA ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel() != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern() != 0 ) + throw "external relocation found with local relocations"; + if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) + throw "local relocation address not in writable segment"; +} + template <> void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) { diff --git a/ld64/src/other/rebase.cpp b/ld64/src/other/rebase.cpp index 6299510..9007679 100644 --- a/ld64/src/other/rebase.cpp +++ b/ld64/src/other/rebase.cpp @@ -651,7 +651,13 @@ void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) } } else { - throw "cannot rebase final linked image with scattered relocations"; + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + fSlide ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } } } diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 589eea0..2c13b83 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -96,6 +96,8 @@ class UnwindPrinter }; +template <> const char* UnwindPrinter::archName() { return "ppc"; } +template <> const char* UnwindPrinter::archName() { return "ppc64"; } template <> const char* UnwindPrinter::archName() { return "i386"; } template <> const char* UnwindPrinter::archName() { return "x86_64"; } template <> const char* UnwindPrinter::archName() { return "arm"; } From 790ce21bbb445f60a3a60c66e37338c7790b7e8f Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Mon, 26 May 2014 14:19:20 +0100 Subject: [PATCH 39/48] ld64 - add PPC to the list of 32b targets where the checking of 4Gb limit is a warning rather than an error. --- ld64/src/ld/OutputFile.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 86322ad..2633650 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -654,9 +654,13 @@ void OutputFile::rangeCheckAbsolute32(int64_t displacement, ld::Internal& state, // 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 or -static - if ( (_options.outputKind() != Options::kPreload) && (_options.outputKind() != Options::kStaticExecutable) ) { + if ( _options.architecture() == CPU_TYPE_ARM || + _options.architecture() == CPU_TYPE_I386 || + _options.architecture() == CPU_TYPE_POWERPC) { + // Unlikely userland code does funky stuff like this, so warn for them, + // but not warn for -preload or -static + if ( (_options.outputKind() != Options::kPreload) + && (_options.outputKind() != Options::kStaticExecutable)) { 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); } From 291fd86bfde5bb426c80b9a6c38e8faa713faea8 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 25 Jun 2014 01:08:53 +0100 Subject: [PATCH 40/48] Partial fixes for build on Darwin8 + JBSR. - Only a start on dealing with JBSR, not functional yet (but there's a work-around which is not to use -mlong-branch). --- ld64/src/ld/InputFiles.cpp | 1 + ld64/src/ld/OutputFile.cpp | 11 +- ld64/src/ld/ld.cpp | 15 ++- .../src/ld/parsers/macho_relocatable_file.cpp | 113 +++++++++++++----- ld64/src/ld/parsers/macho_relocatable_file.h | 1 + 5 files changed, 110 insertions(+), 31 deletions(-) diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index f4e3414..5d5a968 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -308,6 +308,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.treateBitcodeAsData = _options.bitcodeKind() == Options::kBitcodeAsData; objOpts.usingBitcode = _options.bundleBitcode(); objOpts.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign(); + objOpts.osxMin = _options.macosxVersionMin(); ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 2633650..4ce6912 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -24,6 +24,9 @@ #include +#define __STDC_FORMAT_MACROS +#include +#undef __STDC_FORMAT_MACROS #include #include #include @@ -4694,8 +4697,12 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio case ld::Fixup::kindStorePPCAbsHigh16: { assert(target != NULL); - if ( target->definition() == ld::Atom::definitionProxy ) - throwf("half word text relocs not supported in %s", atom->name()); + if ( target->definition() == ld::Atom::definitionProxy ) { +fprintf(stderr, "bad reloc target: %40s source %40s (offset 0x%" PRIx64 ") final addr 0x%" PRIx64 " atom offset: 0x%x [0x%" PRIx64 "]\n", + target->name(), atom->name(), atom->objectAddress(), atom->finalAddress(), + fixupWithStore->offsetInAtom, (uint64_t)atom->finalAddress()+fixupWithStore->offsetInAtom ); +// throwf("half word text relocs not supported in %s", atom->name()); + } if ( _options.outputSlidable() ) { if ( inReadOnlySeg ) noteTextReloc(atom, target); diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 76bb45e..56882df 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -1401,8 +1401,20 @@ int main(int argc, const char* argv[]) return 0; } - #ifndef NDEBUG +# if defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) \ + && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040 + +/* Don't try a backtrace on earlier systems. */ + +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) +{ + fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + exit (1); +} +# else + +#include // implement assert() function to print out a backtrace before aborting void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) { @@ -1434,6 +1446,7 @@ void __assert_rtn(const char* func, const char* file, int line, const char* fail fprintf(stderr, "ld: Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); exit(1); } +# endif /* 10.4 or earlier. */ #endif diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index 1df5fb0..b6ea7b2 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -53,8 +53,6 @@ #include "ld.hpp" #include "macho_relocatable_file.h" - - extern void throwf(const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); @@ -183,13 +181,14 @@ class Section : public ld::Section virtual bool ignoreLabel(const char* label) const { return false; } static const char* makeSectionName(const macho_section* s); + protected: Section(File& f, const macho_section* s) : ld::Section(makeSegmentName(s), makeSectionName(s), sectionType(s)), _file(f), _machOSection(s), _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } Section(File& f, const char* segName, const char* sectName, ld::Section::Type t, bool hidden=false) : ld::Section(segName, sectName, t, hidden), _file(f), _machOSection(NULL), - _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } + _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false), _hasJBSR(false) { } bool addRelocFixup_powerpc(class Parser& parser,const macho_relocation_info* reloc); @@ -207,6 +206,7 @@ class Section : public ld::Section class Atom* _beginAtoms; class Atom* _endAtoms; bool _hasAliases; + bool _hasJBSR; std::set*> _altEntries; }; @@ -999,7 +999,7 @@ class Parser ordinal, opts.warnUnwindConversionProblems, opts.keepDwarfUnwind, opts.forceDwarfConversion, opts.neverConvertDwarf, opts.verboseOptimizationHints, - opts.ignoreMismatchPlatform); + opts.ignoreMismatchPlatform, opts.osxMin); return p.parse(opts); } @@ -1103,8 +1103,8 @@ class Parser uint32_t absoluteSymbolCount() { return _absoluteSymbolCount; } uint32_t fileLength() const { return _fileLength; } - bool hasStubsSection() { return (_stubsSectionNum != 0); } - unsigned int stubsSectionNum() { return _stubsSectionNum; } + bool hasStubsSection() { return (_numStubsSections > 0); } + unsigned int stubsSectionNum(unsigned n) { return _stubsSectionNums[n]; } void addDtraceExtraInfos(const SourceLocation& src, const char* provider); const char* scanSymbolTableForAddress(uint64_t addr); bool warnUnwindConversionProblems() { return _warnUnwindConversionProblems; } @@ -1200,7 +1200,8 @@ class Parser const char* path, time_t modTime, ld::File::Ordinal ordinal, bool warnUnwindConversionProblems, bool keepDwarfUnwind, bool forceDwarfConversion, bool neverConvertDwarf, - bool verboseOptimizationHints, bool ignoreMismatchPlatform); + bool verboseOptimizationHints, bool ignoreMismatchPlatform, + ld::MacVersionMin OSXmin); ld::relocatable::File* parse(const ParserOptions& opts); static uint8_t loadCommandSizeMask(); bool parseLoadCommands(Options::Platform platform, uint32_t minOSVersion, bool simulator, bool ignoreMismatchPlatform); @@ -1272,8 +1273,10 @@ class Parser bool _treateBitcodeAsData; bool _usingBitcode; uint8_t _maxDefaultCommonAlignment; - unsigned int _stubsSectionNum; - const macho_section

* _stubsMachOSection; + unsigned int _numStubsSections; + unsigned int _stubsSectionNums[3]; + const macho_section

* _stubsMachOSections[3]; + ld::MacVersionMin _osxMin; std::vector _dtraceProviderInfo; std::vector _allFixups; }; @@ -1283,7 +1286,8 @@ class Parser template Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion, - bool neverConvertDwarf, bool verboseOptimizationHints, bool ignoreMismatchPlatform) + bool neverConvertDwarf, bool verboseOptimizationHints, bool ignoreMismatchPlatform, + ld::MacVersionMin OSXmin) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _indirectSymbolCount(0), _strings(NULL), _stringsSize(0), @@ -1299,9 +1303,13 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion), _neverConvertDwarf(neverConvertDwarf), _verboseOptimizationHints(verboseOptimizationHints), - _ignoreMismatchPlatform(ignoreMismatchPlatform), - _stubsSectionNum(0), _stubsMachOSection(NULL) + _ignoreMismatchPlatform(ignoreMismatchPlatform), _numStubsSections(0), + _osxMin(OSXmin) { + for (unsigned s=0; s<3; s++) { + _stubsSectionNums[s] = 0; + _stubsMachOSections[s] = NULL; + } } template <> @@ -2626,12 +2634,17 @@ void Parser::makeSections() machOSects[count].sect = sect; switch ( sect->flags() & SECTION_TYPE ) { case S_SYMBOL_STUBS: - if ( _stubsSectionNum == 0 ) { - _stubsSectionNum = i+1; - _stubsMachOSection = sect; + // Cater for the wacky darwin8 crt with three versions of the symbol + // stub. Probably we just dump two of them (TODO figure out how). + if ( _numStubsSections < 3 ) { + _stubsSectionNums[_numStubsSections] = i+1; + _stubsMachOSections[_numStubsSections] = sect; + _numStubsSections++; } else - assert(1 && "multiple S_SYMBOL_STUBS sections"); + warning("%s has more than three symbol stubs sections?", _file->path()); +// assert(0 && "more than three S_SYMBOL_STUBS sections"); + break; case S_LAZY_SYMBOL_POINTERS: break; case S_4BYTE_LITERALS: @@ -2893,17 +2906,31 @@ Atom* Parser::findAtomByAddress(pint_t addr) template Atom* Parser::findAtomByAddressOrNullIfStub(pint_t addr) { - if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) + if (hasStubsSection()) { + for (unsigned sts = 0; sts < 3; sts++) { + if (_stubsMachOSections[sts] == NULL || stubsSectionNum(sts) == 0) + break; + if ( (_stubsMachOSections[sts]->addr() <= addr) + && (addr < (_stubsMachOSections[sts]->addr()+_stubsMachOSections[sts]->size()))) return NULL; + } + } + return findAtomByAddress(addr); } template Atom* Parser::findAtomByAddressOrLocalTargetOfStub(pint_t addr, uint32_t* offsetInAtom) { - if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) { + if (hasStubsSection()) { + for (unsigned sts = 0; sts < 3; sts++) { + if (_stubsMachOSections[sts] == NULL || stubsSectionNum(sts) == 0) + break; + + if ((_stubsMachOSections[sts]->addr() <= addr) + && (addr < (_stubsMachOSections[sts]->addr()+_stubsMachOSections[sts]->size()))) { // target is a stub, remove indirection - uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSections[sts]); assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); // can't be to external weak symbol @@ -2911,6 +2938,9 @@ Atom* Parser::findAtomByAddressOrLocalTargetOfStub(pint_t addr, uint32_t* *offsetInAtom = 0; return this->findAtomByName(this->nameFromSymbol(sym)); } + } + } + Atom* target = this->findAtomByAddress(addr); *offsetInAtom = addr - target->_objAddress; return target; @@ -2932,9 +2962,15 @@ Atom* Parser::findAtomByName(const char* name) template void Parser::findTargetFromAddress(pint_t addr, TargetDesc& target) { - if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) { + if (hasStubsSection()) { + for (unsigned sts = 0; sts < 3; sts++) { + if (_stubsMachOSections[sts] == NULL || stubsSectionNum(sts) == 0) + break; + + if ( (_stubsMachOSections[sts]->addr() <= addr) + && (addr < (_stubsMachOSections[sts]->addr()+_stubsMachOSections[sts]->size())) ) { // target is a stub, remove indirection - uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSections[sts]); assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); target.atom = NULL; @@ -2943,6 +2979,9 @@ void Parser::findTargetFromAddress(pint_t addr, TargetDesc& target) target.addend = 0; return; } + } + } + Section* section = this->sectionForAddress(addr); target.atom = section->findAtomByAddress(addr); target.addend = addr - target.atom->_objAddress; @@ -2974,13 +3013,19 @@ void Parser::findTargetFromAddressAndSectionNum(pint_t addr, unsigned int sec throwf("R_ABS reloc but no absolute symbol at target address"); } - if ( hasStubsSection() && (stubsSectionNum() == sectNum) ) { + if (hasStubsSection()) { + for (unsigned sts = 0; sts < 3; sts++) { + if (_stubsMachOSections[sts] == NULL || stubsSectionNum(sts) == 0) + break; + + if ( (stubsSectionNum(sts) == sectNum) ) { // target is a stub, remove indirection - uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSections[sts]); assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); // use direct reference when stub is to a static function - if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (this->nameFromSymbol(sym)[0] == 'L')) ) { + if ( ((sym.n_type() & N_TYPE) == N_SECT) + && (((sym.n_type() & N_EXT) == 0) || (this->nameFromSymbol(sym)[0] == 'L')) ) { this->findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); } else { @@ -2991,6 +3036,9 @@ void Parser::findTargetFromAddressAndSectionNum(pint_t addr, unsigned int sec } return; } + } + } + Section* section = this->sectionForNum(sectNum); target.atom = section->findAtomByAddress(addr); if ( target.atom == NULL ) { @@ -4691,7 +4739,6 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, throwf("malformed __eh_frame section: %s", msg); } - template uint32_t CFISection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, @@ -4701,7 +4748,6 @@ uint32_t CFISection::computeAtomCount(class Parser& parser, } - template uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, struct Parser::LabelAndCFIBreakIterator& it, @@ -7014,18 +7060,29 @@ bool Section::addRelocFixup_powerpc(class Parser& parser, } break; case PPC_RELOC_JBSR: - // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target + // This is from -mlong-branch codegen. + // We ignore the jump island and make reference to the real target. + // + // FIXME: We then have, effectively, dead branch island code but it + // (the island code) contains relocs and probably can't be easily + // proved dead?? ... this causes problems when we come to emit. if ( nextReloc->r_type() != PPC_RELOC_PAIR ) throw "PPC_RELOC_JBSR missing following pair"; - if ( !parser._hasLongBranchStubs ) + if ( !parser._hasLongBranchStubs && parser._osxMin >= ld::mac10_5) warning("object file compiled with -mlong-branch which is no longer needed. " "To remove this warning, recompile without -mlong-branch: %s", parser._path); parser._hasLongBranchStubs = true; + this->_hasJBSR = true; result = true; if ( reloc->r_extern() ) { throw "PPC_RELOC_JBSR should not be using an external relocation"; } parser.findTargetFromAddressAndSectionNum(nextReloc->r_address(), reloc->r_symbolnum(), target); + // Check the branch and displacement. + assert(((instruction & 0x4C000000) == 0x48000000) && "must be a 24bit branch"); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); break; default: diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index e31f7f9..615639f 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -50,6 +50,7 @@ struct ParserOptions { bool treateBitcodeAsData; bool usingBitcode; uint8_t maxDefaultCommonAlignment; + ld::MacVersionMin osxMin; }; extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, From 89bef8623ccbd808f6ae41dec8c30b2c26c9051d Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 23 May 2015 14:54:57 +0100 Subject: [PATCH 41/48] Revised branch islands algorithm. Debug stuff still in place, and only tested/implemented for PPC. It's possible that the fix for symbol+offset could be needed for ARM too. - updated for 253.3 and 264.3.102 --- ld64/src/ld/OutputFile.cpp | 19 +- ld64/src/ld/ld.cpp | 22 +- ld64/src/ld/ld.hpp | 8 +- ld64/src/ld/passes/branch_island.cpp | 556 ++++++++++++++++----------- 4 files changed, 384 insertions(+), 221 deletions(-) diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 4ce6912..8c7e8a9 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -1386,6 +1386,12 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: bool is_b; bool thumbTarget = false; std::map usedByHints; + bool printStuff = false; + if (atom->contentType() == ld::Atom::typeBranchIsland) { + //fprintf(stderr, "applyFixUps() on %s ", atom->name()); + //printStuff = true; + } + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { uint8_t* fixUpLocation = &buffer[fit->offsetInAtom]; ld::Fixup::LOH_arm64 lohExtra; @@ -1404,6 +1410,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: accumulator |= 1; if ( fit->contentAddendOnly || fit->contentDetlaToAddendOnly ) accumulator = 0; +if (printStuff) fprintf(stderr, ":kindSetTargetAddress accum 0x%lx %s ", accumulator, toTarget?toTarget->name():"anon"); break; case ld::Fixup::kindSubtractTargetAddress: delta = addressOf(state, fit, &fromTarget); @@ -1423,6 +1430,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } accumulator += fit->u.addend; } +if (printStuff) fprintf(stderr, ":kindAddAddend accum 0x%lx addend %u ", accumulator, fit->u.addend); break; case ld::Fixup::kindSubtractAddend: accumulator -= fit->u.addend; @@ -1585,6 +1593,9 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: break; case ld::Fixup::kindStoreTargetAddressPPCBranch24: accumulator = addressOf(state, fit, &toTarget); +if (printStuff) fprintf(stderr, ":kindStoreTargetAddressPPCBranch24 "); +#if 0 +//FIXME: reinstate this optimisation - and make sure it applies to the addend cases too. if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) { // Branching to island. If ultimate target is in range, branch there directly. for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); @@ -1602,10 +1613,12 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } } } +#endif if ( fit->contentDetlaToAddendOnly ) accumulator = 0; // fall into kindStorePPCBranch24 case case ld::Fixup::kindStorePPCBranch24: +if (printStuff) fprintf(stderr, ":kindStorePPCBranch24 "); delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); rangeCheckPPCBranch24(delta, state, atom, fit); instruction = get32BE(fixUpLocation); @@ -1711,7 +1724,11 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: } break; case ld::Fixup::kindLazyTarget: + break; case ld::Fixup::kindIslandTarget: + if ( fit->clusterSize == ld::Fixup::k1of2 ) { // had an addend saved. + ++fit; // skip it. + } break; case ld::Fixup::kindSetLazyOffset: assert(fit->binding == ld::Fixup::bindingDirectlyBound); @@ -2198,7 +2215,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: #endif } } - +if (printStuff) fprintf(stderr, "\n"); #if SUPPORT_ARCH_arm64 // after all fixups are done on atom, if there are potential optimizations, do those if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) { diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index 56882df..2250089 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -112,7 +112,7 @@ class InternalState : public ld::Internal uint64_t assignFileOffsets(); void setSectionSizesAndAlignments(); - void sortSections(); + void sortSections(bool useQuick); void markAtomsOrdered() { _atomsOrderedInSections = true; } bool hasReferenceToWeakExternal(const ld::Atom& atom); @@ -829,13 +829,19 @@ int InternalState::FinalSection::sectionComparer(const void* l, const void* r) return (left->_sectionOrder - right->_sectionOrder); } -void InternalState::sortSections() +void InternalState::sortSections(bool useQuick) { //fprintf(stderr, "UNSORTED final sections:\n"); //for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); //} - qsort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer); + + // Once we've applied branch islands, we must ensure that sections in the __TEXT segment that compare + // equal are stably ordered. + if (useQuick) + qsort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer); + else + mergesort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer); //fprintf(stderr, "SORTED final sections:\n"); //for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); @@ -1329,7 +1335,7 @@ int main(int argc, const char* argv[]) inputFiles.dylibs(state); // do initial section sorting so passes have rough idea of the layout - state.sortSections(); + state.sortSections(/* useQuick */true); // run passes statistics.startPasses = mach_absolute_time(); @@ -1343,6 +1349,10 @@ int main(int argc, const char* argv[]) state.markAtomsOrdered(); ld::passes::dedup::doPass(options, state); ld::passes::branch_shim::doPass(options, state); // must be after stubs + // We have multiple code sections and these might require intermediate branch + // islands, we need to ensure that the layout of the __TEXT segment is known + // before we do this. + state.sortSections(/* useQuick */true); ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); ld::passes::compact_unwind::doPass(options, state); // must be after order pass @@ -1350,7 +1360,9 @@ int main(int argc, const char* argv[]) ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib #endif // sort final sections - state.sortSections(); + // Use a stable sort so that assumptions made in branch islanding don't get + // broken. + state.sortSections(/* useQuick */false); // write output file statistics.startOutput = mach_absolute_time(); diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 7dce870..2015984 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -866,7 +866,9 @@ class Internal fileOffset(0), size(0), alignment(0), indirectSymTabStartIndex(0), indirectSymTabElementSize(0), relocStart(0), relocCount(0), - hasLocalRelocs(false), hasExternalRelocs(false) {} + hasLocalRelocs(false), hasExternalRelocs(false), + hasBranches(false), hasCrossSectionBranches(false), + hasThumbBranches(false), needsIslands(false) {} std::vector atoms; uint64_t address; uint64_t fileOffset; @@ -879,6 +881,10 @@ class Internal uint32_t relocCount; bool hasLocalRelocs; bool hasExternalRelocs; + bool hasBranches; + bool hasCrossSectionBranches; + bool hasThumbBranches; + bool needsIslands; }; typedef std::map AtomToSection; diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 7d2b8c1..45a9a03 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -42,10 +42,16 @@ namespace ld { namespace passes { namespace branch_island { +static bool seenCrossSectBr; +static bool seenThumbBr; +static uint64_t lowestTextAddr; +static uint64_t furthestStubSect; +static uint64_t furthestCodeOrStubSect; +static uint64_t sizeOfTEXTSeg; +static unsigned sectionsWithBanches; static std::map sAtomToAddress; - struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; }; class TargetAndOffsetComparor { @@ -60,19 +66,39 @@ class TargetAndOffsetComparor static bool _s_log = false; -static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); +//static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); class PPCBranchIslandAtom : public ld::Atom { public: - PPCBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + PPCBranchIslandAtom(const char* nm, const ld::Section& inSect, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, - ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), - _name(nm), - _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressPPCBranch24, target), - _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { - if (_s_log) fprintf(stderr, "%s: PPC jump instruction branch island to final target %s\n", - target->name(), finalTarget.atom->name()); + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm) + { + _fxups.clear(); + if (finalTarget.offset == 0) + hasAddend = false; + else + hasAddend = true; + // We are doing the final branch, which might need the addend from the original fixups. + if ((target == finalTarget.atom) && hasAddend) { + _fxups.push_back(Fixup(0, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, target)); + _fxups.push_back(Fixup(0, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, finalTarget.offset)); + _fxups.push_back(Fixup(0, ld::Fixup::k3of3, ld::Fixup::kindStorePPCBranch24)); + } else { + _fxups.push_back(Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressPPCBranch24, target)); + } + // Record a shorthand version of the final destination, so that OutputFile can optimize the + // intermediate islands away where the final target turns out to be reachable. + if (hasAddend) { + _fxups.push_back(Fixup(0, ld::Fixup::k1of2, ld::Fixup::kindIslandTarget, finalTarget.atom)); + _fxups.push_back(Fixup(0, ld::Fixup::k2of2, ld::Fixup::kindAddAddend, finalTarget.offset)); + } else { + _fxups.push_back(Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom)); + } + if (_s_log) fprintf(stderr, " %s: PPC jump instruction branch island to %s (final target %s%s) %d fixups\n", + nm, target->name(), finalTarget.atom->name(), (hasAddend?" has addend":""), + _fxups.size()); } virtual const ld::File* file() const { return NULL; } @@ -85,21 +111,21 @@ class PPCBranchIslandAtom : public ld::Atom { OSWriteBigInt32(buffer, 0, 0x48000000); } virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } - virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fxups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fxups[_fxups.size()]; } private: const char* _name; - ld::Fixup _fixup1; - ld::Fixup _fixup2; + mutable std::vector _fxups; + bool hasAddend; }; #if SUPPORT_ARCH_arm64 class ARM64BranchIslandAtom : public ld::Atom { public: - ARM64BranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ARM64BranchIslandAtom(const char* nm, const ld::Section& inSect, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), @@ -130,8 +156,8 @@ class ARM64BranchIslandAtom : public ld::Atom { class ARMtoARMBranchIslandAtom : public ld::Atom { public: - ARMtoARMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ARMtoARMBranchIslandAtom(const char* nm, const ld::Section& inSect, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), @@ -162,8 +188,8 @@ class ARMtoARMBranchIslandAtom : public ld::Atom { class ARMtoThumb1BranchIslandAtom : public ld::Atom { public: - ARMtoThumb1BranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ARMtoThumb1BranchIslandAtom(const char* nm, const ld::Section& inSect, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), @@ -199,8 +225,8 @@ class ARMtoThumb1BranchIslandAtom : public ld::Atom { class Thumb2toThumbBranchIslandAtom : public ld::Atom { public: - Thumb2toThumbBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + Thumb2toThumbBranchIslandAtom(const char* nm, const ld::Section& inSect, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), _name(nm), @@ -231,7 +257,7 @@ class Thumb2toThumbBranchIslandAtom : public ld::Atom { class Thumb2toThumbBranchAbsoluteIslandAtom : public ld::Atom { public: - Thumb2toThumbBranchAbsoluteIslandAtom(const char* nm, const ld::Section& inSect, TargetAndOffset finalTarget) + Thumb2toThumbBranchAbsoluteIslandAtom(const char* nm, const ld::Section& inSect, TargetAndOffset finalTarget) : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), @@ -271,8 +297,8 @@ class Thumb2toThumbBranchAbsoluteIslandAtom : public ld::Atom { class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { public: - NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) - : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Section& inSect, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), @@ -325,7 +351,7 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int switch ( kind ) { case ld::Fixup::kindStorePPCBranch24: case ld::Fixup::kindStoreTargetAddressPPCBranch24: - return new PPCBranchIslandAtom(name, nextTarget, finalTarget); + return new PPCBranchIslandAtom(name, inSect, nextTarget, finalTarget); break; case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: @@ -336,23 +362,23 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int } else if ( finalTarget.atom->isThumb() ) { if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) { - return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget); + return new Thumb2toThumbBranchIslandAtom(name, inSect, nextTarget, finalTarget); } else if ( opts.outputSlidable() ) { - return new ARMtoThumb1BranchIslandAtom(name, nextTarget, finalTarget); + return new ARMtoThumb1BranchIslandAtom(name, inSect, nextTarget, finalTarget); } else { - return new NoPicARMtoThumbMBranchIslandAtom(name, nextTarget, finalTarget); + return new NoPicARMtoThumbMBranchIslandAtom(name, inSect, nextTarget, finalTarget); } } else { - return new ARMtoARMBranchIslandAtom(name, nextTarget, finalTarget); + return new ARMtoARMBranchIslandAtom(name, inSect, nextTarget, finalTarget); } break; #if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreARM64Branch26: case ld::Fixup::kindStoreTargetAddressARM64Branch26: - return new ARM64BranchIslandAtom(name, nextTarget, finalTarget); + return new ARM64BranchIslandAtom(name, inSect, nextTarget, finalTarget); break; #endif default: @@ -415,173 +441,80 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra } -// FIXME: comment stuff is out of date. -// PowerPC can do PC relative branches as far as +/-32MB. -// If a branch target is >16MB then we insert one or more -// "branch islands" between the branch and its target that -// allows island hopping to the target. -// // Branch Island Algorithm // -// If the __TEXT segment < 16MB, then no branch islands needed -// Otherwise, every 14MB into the __TEXT segment a region is -// added which can contain branch islands. Every out-of-range -// bl instruction is checked. If it crosses a region, an island -// is added to that region with the same target and the bl is -// adjusted to target the island instead. -// -// In theory, if too many islands are added to one region, it -// could grow the __TEXT enough that other previously in-range -// bl branches could be pushed out of range. We reduce the -// probability this could happen by placing the ranges every -// 14MB which means the region would have to be 2MB (512,000 islands) -// before any branches could be pushed out of range. +// FIXME: write a comment describing the revised algorithm. + // -static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection, unsigned stubCount) +typedef std::map AtomToIsland; + +static unsigned numberOfIslandRegions; +static std::vector branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted +static std::vector branchIslandInsertionSections; // Section containing +static int kIslandRegionsCount; +static uint64_t kBetweenRegions; +static AtomToIsland* *regionsMap; +static uint64_t *regionAddresses; +static std::vector* *regionsIslands; +static unsigned int islandCount; + +static void makeIslandsForSection(const Options& opts, ld::Internal& state, + ld::Internal::FinalSection* sect, unsigned stubCount) { - // assign section offsets to each atom in __text section, watch for thumb branches, and find total size - bool hasThumbBranches = false; - bool haveCrossSectionBranches = false; + bool hasThumbBranches = sect->hasThumbBranches; + bool haveCrossSectionBranches = sect->hasCrossSectionBranches; const bool preload = (opts.outputKind() == Options::kPreload); - uint64_t offset = 0; - for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - // check for thumb branches and cross section branches - const ld::Atom* target = NULL; - for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { - if ( fit->firstInCluster() ) { - target = NULL; - } - switch ( fit->binding ) { - case ld::Fixup::bindingNone: - case ld::Fixup::bindingByNameUnbound: - break; - case ld::Fixup::bindingByContentBound: - case ld::Fixup::bindingDirectlyBound: - target = fit->u.target; - break; - case ld::Fixup::bindingsIndirectlyBound: - target = state.indirectBindingTable[fit->u.bindingIndex]; - break; - } - bool haveBranch = false; - switch (fit->kind) { - case ld::Fixup::kindStoreThumbBranch22: - case ld::Fixup::kindStoreTargetAddressThumbBranch22: - hasThumbBranches = true; - // fall into arm branch case - case ld::Fixup::kindStoreARMBranch24: - case ld::Fixup::kindStoreTargetAddressARMBranch24: - case ld::Fixup::kindStorePPCBranch24: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: - haveBranch = true; - break; - default: - break; - } - if ( haveBranch && (target->contentType() != ld::Atom::typeStub) ) { - // haveCrossSectionBranches only applies to -preload builds - if ( preload && (atom->section() != target->section()) ) - haveCrossSectionBranches = true; - } - } - // align atom - ld::Atom::Alignment atomAlign = atom->alignment(); - uint64_t atomAlignP2 = (1 << atomAlign.powerOf2); - uint64_t currentModulus = (offset % atomAlignP2); - if ( currentModulus != atomAlign.modulus ) { - if ( atomAlign.modulus > currentModulus ) - offset += atomAlign.modulus-currentModulus; - else - offset += atomAlign.modulus+atomAlignP2-currentModulus; - } - (const_cast(atom))->setSectionOffset(offset); - offset += atom->size(); - } - uint64_t totalTextSize = offset + stubCount*16; - if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches ) - return; -//if (_s_log) -fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", textSection->sectionName(), totalTextSize); - - // Figure out how many regions of branch islands will be needed, and their locations. - // Construct a vector containing the atoms after which branch islands will be inserted, - // taking into account follow on fixups. No atom run without an island can exceed kBetweenRegions. - const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section - std::vector branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted - uint64_t previousIslandEndAddr = 0; - const ld::Atom *insertionPoint = NULL; - branchIslandInsertionPoints.reserve(totalTextSize/kBetweenRegions*2); - for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { - const ld::Atom* atom = *it; - // if we move past the next atom, will the run length exceed kBetweenRegions? - if ( atom->sectionOffset() + atom->size() > previousIslandEndAddr + kBetweenRegions ) { - // yes. Add the last known good location (atom) for inserting a branch island. - if ( insertionPoint == NULL ) - throwf("Unable to insert branch island. No insertion point available."); - branchIslandInsertionPoints.push_back(insertionPoint); - previousIslandEndAddr = insertionPoint->sectionOffset()+insertionPoint->size(); - insertionPoint = NULL; - } - // Can we insert an island after this atom? If so then keep track of it. - if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) ) - insertionPoint = atom; - } - // add one more island after the last atom if close to limit - if ( (insertionPoint != NULL) && (insertionPoint->sectionOffset() + insertionPoint->size() > previousIslandEndAddr + (kBetweenRegions-0x100000)) ) - branchIslandInsertionPoints.push_back(insertionPoint); - if ( haveCrossSectionBranches && branchIslandInsertionPoints.empty() ) { - branchIslandInsertionPoints.push_back(textSection->atoms.back()); - } - const int kIslandRegionsCount = branchIslandInsertionPoints.size(); - if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); - typedef std::map AtomToIsland; - AtomToIsland* regionsMap[kIslandRegionsCount]; - uint64_t regionAddresses[kIslandRegionsCount]; - std::vector* regionsIslands[kIslandRegionsCount]; - for(int i=0; i < kIslandRegionsCount; ++i) { - regionsMap[i] = new AtomToIsland(); - regionsIslands[i] = new std::vector(); - regionAddresses[i] = branchIslandInsertionPoints[i]->sectionOffset() + branchIslandInsertionPoints[i]->size(); - if (_s_log) fprintf(stderr, "ld: branch islands will be inserted at 0x%08llX after %s\n", regionAddresses[i], branchIslandInsertionPoints[i]->name()); - } - unsigned int islandCount = 0; +if (_s_log) fprintf(stderr, "ld: checking section %s\n", sect->sectionName()); - // create islands for branches in __text that are out of range - for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { + // create islands for branches in sect that are out of range + for (std::vector::iterator ait=sect->atoms.begin(); + ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; const ld::Atom* target = NULL; - uint64_t addend = 0; + uint32_t addend = 0; ld::Fixup* fixupWithTarget = NULL; - for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + ld::Fixup* fixupWithAddend = NULL; + int fu = 0; +//if (_s_log) fprintf(stderr, "atom : %s \n", atom->name()); + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); + fit != end; ++fit) { +//if (_s_log) fprintf(stderr, "fu %d %s : %s : ", fu++, (fit->firstInCluster()?"is first":"not first"), (fit->lastInCluster()?"is last":"not last")); if ( fit->firstInCluster() ) { target = NULL; fixupWithTarget = NULL; + fixupWithAddend = NULL; addend = 0; } switch ( fit->binding ) { case ld::Fixup::bindingNone: case ld::Fixup::bindingByNameUnbound: +//if (_s_log) fprintf(stderr, "Binding none / unbound " ); break; case ld::Fixup::bindingByContentBound: case ld::Fixup::bindingDirectlyBound: target = fit->u.target; fixupWithTarget = fit; +//if (_s_log) fprintf(stderr, " target : %s ", target->name()); break; case ld::Fixup::bindingsIndirectlyBound: target = state.indirectBindingTable[fit->u.bindingIndex]; fixupWithTarget = fit; +//if (_s_log) fprintf(stderr, "indir target : %s ", target->name()); break; } bool haveBranch = false; switch (fit->kind) { case ld::Fixup::kindAddAddend: addend = fit->u.addend; + fixupWithAddend = fit; +//if (_s_log) fprintf(stderr, " addend : %u\n", addend); break; - case ld::Fixup::kindStorePPCBranch24: case ld::Fixup::kindStoreTargetAddressPPCBranch24: +//if (_s_log) fprintf(stderr, " StoreTargetAddress", addend); + case ld::Fixup::kindStorePPCBranch24: +//if (_s_log) fprintf(stderr, " branch\n", addend); case ld::Fixup::kindStoreARMBranch24: case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: @@ -593,23 +526,25 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", haveBranch = true; break; default: - break; +//if (_s_log) fprintf(stderr, " [%u] def\n", fit->kind); + break; } if ( haveBranch ) { - bool crossSectionBranch = ( preload && (atom->section() != target->section()) ); + bool crossSectionBranch = ( /*preload && */(atom->section() != target->section()) ); int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom; int64_t dstAddr = target->sectionOffset() + addend; - if ( preload ) { + if ( seenCrossSectBr || preload ) { srcAddr = sAtomToAddress[atom] + fit->offsetInAtom; dstAddr = sAtomToAddress[target] + addend; } if ( target->section().type() == ld::Section::typeStub ) - dstAddr = totalTextSize; + dstAddr = furthestStubSect; int64_t displacement = dstAddr - srcAddr; - // FIXME: Do we really mean to truncate? - TargetAndOffset finalTargetAndOffset = { target, (uint32_t)addend }; +if (_s_log && (abs(displacement) > kBetweenRegions) ) fprintf(stderr, "from %s to %s delta : 0x%0" PRIx64 " in section %s\n", + atom->name(), target->name(), displacement, sect->sectionName()); + TargetAndOffset finalTargetAndOffset = { target, addend }; const int64_t kBranchLimit = kBetweenRegions; - if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { + if ( crossSectionBranch && preload && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { const ld::Atom* island; AtomToIsland* region = regionsMap[0]; AtomToIsland::iterator pos = region->find(finalTargetAndOffset); @@ -620,7 +555,7 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", island, island->name(), displacement); ++islandCount; regionsIslands[0]->push_back(island); - state.atomToSection[island] = textSection; + state.atomToSection[island] = sect; } else { island = pos->second; @@ -628,11 +563,13 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", island, island->name(), target->name(), atom->name()); fixupWithTarget->u.target = island; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + if (fixupWithAddend) + fixupWithAddend->u.addend = 0; } else if ( displacement > kBranchLimit ) { // create forward branch chain const ld::Atom* nextTarget = target; - if (_s_log) fprintf(stderr, "need forward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", + if (_s_log) fprintf(stderr, " +need forward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", srcAddr, dstAddr, target->name()); for (int i=kIslandRegionsCount-1; i >=0 ; --i) { AtomToIsland* region = regionsMap[i]; @@ -640,11 +577,11 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", if ( (srcAddr < islandRegionAddr) && ((islandRegionAddr <= dstAddr)) ) { AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset, atom->section(), false); + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset, *branchIslandInsertionSections[i], false); (*region)[finalTargetAndOffset] = island; - if (_s_log) fprintf(stderr, "added forward branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); + if (_s_log) fprintf(stderr, " +added forward branching island %p %s to region %d (in section %s) for %s\n", island, island->name(), i, branchIslandInsertionSections[i]->sectionName(), atom->name()); regionsIslands[i]->push_back(island); - state.atomToSection[island] = textSection; + state.atomToSection[island] = sect; ++islandCount; nextTarget = island; } @@ -653,9 +590,11 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", } } } - if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", nextTarget, nextTarget->name(), target->name(), atom->name()); + if (_s_log) fprintf(stderr, " +using island %p %s for branch to %s from %s\n", nextTarget, nextTarget->name(), target->name(), atom->name()); fixupWithTarget->u.target = nextTarget; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + if (fixupWithAddend) + fixupWithAddend->u.addend = 0; } else if ( displacement < (-kBranchLimit) ) { // create back branching chain @@ -664,14 +603,14 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", AtomToIsland* region = regionsMap[i]; int64_t islandRegionAddr = regionAddresses[i]; if ( (dstAddr < islandRegionAddr) && (islandRegionAddr <= srcAddr) ) { - if (_s_log) fprintf(stderr, "need backward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", srcAddr, dstAddr, target->name()); + if (_s_log) fprintf(stderr, " -need backward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", srcAddr, dstAddr, target->name()); AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset, atom->section(), false); + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset, *branchIslandInsertionSections[i], false); (*region)[finalTargetAndOffset] = island; - if (_s_log) fprintf(stderr, "added back branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); + if (_s_log) fprintf(stderr, " -added back branching island %p %s to region %d (in section %s) for %s\n", island, island->name(), i, branchIslandInsertionSections[i]->sectionName(), atom->name()); regionsIslands[i]->push_back(island); - state.atomToSection[island] = textSection; + state.atomToSection[island] = sect; ++islandCount; prevTarget = island; } @@ -680,53 +619,32 @@ fprintf(stderr, "ld: section %s size=%" PRIu64 ", might need branch islands\n", } } } - if (_s_log) fprintf(stderr, "using back island %p %s for %s\n", prevTarget, prevTarget->name(), atom->name()); + if (_s_log) fprintf(stderr, " -using back island %p %s for %s\n", prevTarget, prevTarget->name(), atom->name()); fixupWithTarget->u.target = prevTarget; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + if (fixupWithAddend) + fixupWithAddend->u.addend = 0; } } } } - - - // insert islands into __text section and adjust section offsets - if ( islandCount > 0 ) { - if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); - std::vector newAtomList; - newAtomList.reserve(textSection->atoms.size()+islandCount); - - int regionIndex = 0; - for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ait++) { - const ld::Atom* atom = *ait; - newAtomList.push_back(atom); - if ( (regionIndex < kIslandRegionsCount) && (atom == branchIslandInsertionPoints[regionIndex]) ) { - std::vector* islands = regionsIslands[regionIndex]; - newAtomList.insert(newAtomList.end(), islands->begin(), islands->end()); - ++regionIndex; - } - } - // swap in new list of atoms for __text section - textSection->atoms.clear(); - textSection->atoms = newAtomList; - } - } - +// Layout the atoms well enough to determine where to insert static void buildAddressMap(const Options& opts, ld::Internal& state) { - // Assign addresses to sections - state.setSectionSizesAndAlignments(); - state.assignFileOffsets(); // Assign addresses to atoms in a side table const bool log = false; if ( log ) fprintf(stderr, "buildAddressMap()\n"); - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + for (std::vector::iterator sit = state.sections.begin(); + sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; uint16_t maxAlignment = 0; uint64_t offset = 0; - if ( log ) fprintf(stderr, " section=%s/%s, address=0x%08llX\n", sect->segmentName(), sect->sectionName(), sect->address); - for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + if ( log ) fprintf(stderr, " section=%s/%s, address=0x%08llX\n", + sect->segmentName(), sect->sectionName(), sect->address); + for (std::vector::iterator ait = sect->atoms.begin(); + ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; uint32_t atomModulus = atom->alignment().modulus; @@ -749,8 +667,164 @@ static void buildAddressMap(const Options& opts, ld::Internal& state) { offset += atom->size(); } } +} - +// Initial (conservative) check as to whether islands might be required. +// If the total size of sections containing code exceeds that reachable by a +// branch instruction (including ability to reach a stubs section), then we +// assume conservatively that branch islands could be needed. We assume that +// the lowest address that might need to reach somewhere is 0. +static bool mightNeedBranchIslands(const Options& opts, ld::Internal& state) { + bool anySectNeedsIslands = false; + furthestStubSect = 0; + furthestCodeOrStubSect = 0; + sizeOfTEXTSeg = 0; + sectionsWithBanches = 0; + + // Assign addresses to sections + state.setSectionSizesAndAlignments(); + state.assignFileOffsets(); + + for (std::vector::iterator sit = state.sections.begin(); + sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if (strncmp(sect->segmentName(), "__TEXT", 6) == 0) { + lowestTextAddr = lowestTextAddr > sect->address ? sect->address : lowestTextAddr; + sizeOfTEXTSeg += sect->size; + } + // To a first approximation, the longest distance is from 0 to the end of the + // section we're currently looking at. + if ( sect->type() == ld::Section::typeStub) { + furthestStubSect = sect->address + sect->size; + furthestCodeOrStubSect = sect->address + sect->size; + } else if (sect->type() == ld::Section::typeCode) { + // So now see whether we have branches, cross-section branches and/or + // thumb ones. + furthestCodeOrStubSect = sect->address + sect->size; + for (std::vector::iterator ait=sect->atoms.begin(); + ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // check for thumb branches and cross section branches + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); + fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + bool haveBranch = false; + switch (fit->kind) { + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + sect->hasThumbBranches = seenThumbBr = true; + // fall into arm branch case + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + sect->hasBranches = true; + haveBranch = true; + break; + default: + break; + } + // We will count branches to stubs as cross-section since we + // don't know what funky User-specific sections might get made. + if ( haveBranch && (atom->section() != target->section()) ) + sect->hasCrossSectionBranches = seenCrossSectBr = true; + } // for each fixup. + } // for each atom. + // So if we have branches, see if the section needs islands (on its own) + if ( sect->hasBranches && + (sect->size > textSizeWhenMightNeedBranchIslands(opts, sect->hasThumbBranches)) ) + sect->needsIslands = anySectNeedsIslands = true; + if (sect->hasBranches) + sectionsWithBanches++; + } // in code sections. + } // for each section. + // If the codesize > smallest reachable and there are inter-section branches, assume that + // we need islands. + if (((furthestCodeOrStubSect-lowestTextAddr) > textSizeWhenMightNeedBranchIslands(opts, seenThumbBr)) + && seenCrossSectBr) + anySectNeedsIslands = true; +if (_s_log) +fprintf (stderr, "TEXT seg size %" PRIu64 "M lowest Addr 0x%08" PRIx64 " furthest stub 0x%08" PRIx64 " furthest code or stub 0x%08" PRIx64 " %s islands\n", + sizeOfTEXTSeg/(1024*1024), lowestTextAddr, furthestStubSect, furthestCodeOrStubSect, (anySectNeedsIslands? "needs" : "no")); + kBetweenRegions = maxDistanceBetweenIslands(opts, seenThumbBr); + return anySectNeedsIslands; +} + +// Figure out how many regions of branch islands will be needed, and their locations. +// Construct a vector containing the atoms after which branch islands will be inserted, +// taking into account follow on fixups. No atom run without an island can exceed kBetweenRegions. +void findIslandInsertionPoints(const Options& opts, ld::Internal& state) { + + uint64_t previousIslandEndAddr = lowestTextAddr; + branchIslandInsertionPoints.reserve(furthestCodeOrStubSect/kBetweenRegions*2); + + for (std::vector::iterator sit = state.sections.begin(); + sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + const ld::Atom *insertionPoint = NULL; + if ( previousIslandEndAddr + kBetweenRegions > sect->address + sect->size ) + continue; // No Islands in this section. + + if ( previousIslandEndAddr + kBetweenRegions > furthestCodeOrStubSect ) + break; // Done. + + if (sect->type() != ld::Section::typeCode) { +fprintf(stderr, "**Want to insert branch island into non-code section %s/%s, wanted this address=0x%08" PRIu64 "\n", + sect->segmentName(), sect->sectionName(), previousIslandEndAddr + kBetweenRegions); + } + + // We expect one of more islands in this section. + for (std::vector::iterator it=sect->atoms.begin(); + it != sect->atoms.end(); it++) { + const ld::Atom* atom = *it; + // if we move past the next atom, will the run length exceed kBetweenRegions? + if ( sect->address + atom->sectionOffset() + atom->size() + > previousIslandEndAddr + kBetweenRegions ) { + // yes. Add the last known good location (atom) for inserting a branch island. + if ( insertionPoint == NULL ) + throwf("Unable to insert branch island. No insertion point available."); + branchIslandInsertionPoints.push_back(insertionPoint); + branchIslandInsertionSections.push_back(sect); + previousIslandEndAddr = sect->address + insertionPoint->sectionOffset() + insertionPoint->size(); + insertionPoint = NULL; + } + // Can we insert an island after this atom? If so then keep track of it. + if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) ) + insertionPoint = atom; + } + } // until we pass the endpoint. + + kIslandRegionsCount = branchIslandInsertionPoints.size(); + if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + + typedef AtomToIsland* AtomToIsland_p; + regionsMap = new AtomToIsland_p[kIslandRegionsCount]; + regionAddresses = new uint64_t[kIslandRegionsCount]; + typedef std::vector* region_p; + regionsIslands = new region_p[kIslandRegionsCount]; + for(int i=0; i < kIslandRegionsCount; ++i) { + const ld::Internal::FinalSection* sect = branchIslandInsertionSections[i]; + regionsMap[i] = new AtomToIsland(); + regionsIslands[i] = new std::vector(); + regionAddresses[i] = branchIslandInsertionPoints[i]->sectionOffset() + branchIslandInsertionPoints[i]->size(); + if (_s_log) fprintf(stderr, "ld: branch islands will be inserted at 0x%08llX after %s in section %s/%s\n", + regionAddresses[i], branchIslandInsertionPoints[i]->name(), sect->segmentName(), sect->sectionName()); + } } void doPass(const Options& opts, ld::Internal& state) @@ -776,10 +850,21 @@ void doPass(const Options& opts, ld::Internal& state) return; } - if ( opts.outputKind() == Options::kPreload ) { + seenCrossSectBr = false; + seenThumbBr = false; + lowestTextAddr = 0xFFFFFFFFFFFFFFFFULL; + // First do a rough check to see if we think that any branch isl. are needed. +if ( _s_log ) fprintf(stderr, "ld: checking for poss branch isl.\n"); + if (! mightNeedBranchIslands(opts, state)) + return; + + // If we've seen branches between sections (or even across them), then we need + // insert island regions globally to the __TEXT segment. Otherwise, it's + // enough to make island regions local to the section that requires them. + if ( seenCrossSectBr || (opts.outputKind() == Options::kPreload) ) { buildAddressMap(opts, state); } - + // scan sections for number of stubs unsigned stubCount = 0; for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { @@ -788,12 +873,55 @@ void doPass(const Options& opts, ld::Internal& state) stubCount = sect->atoms.size(); } + // Build a list of regions into which branch islands can be inserted as required. + findIslandInsertionPoints(opts, state); + // scan sections and add island to each code section - for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + islandCount = 0; + for (std::vector::iterator sit=state.sections.begin(); + sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; if ( sect->type() == ld::Section::typeCode ) makeIslandsForSection(opts, state, sect, stubCount); } + + int regionIndex = 0; + if ( islandCount == 0 ) { + if ( _s_log ) fprintf(stderr, "ld: a bit surprising that we didn't need any branch islands after all\n"); + return; + } + + // insert islands into their sections and adjust section offsets + if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", + islandCount, kIslandRegionsCount); + + while (regionIndex < kIslandRegionsCount) { + // Accumulate the atoms to be added to a section. + ld::Internal::FinalSection* sect = branchIslandInsertionSections[regionIndex]; + std::vector newAtomList; + newAtomList.clear(); + newAtomList.reserve(sect->atoms.size()+islandCount); + + for (std::vector::iterator ait=sect->atoms.begin(); + ait != sect->atoms.end(); ait++) { + const ld::Atom* atom = *ait; + newAtomList.push_back(atom); + if ( (regionIndex < kIslandRegionsCount) + && (atom == branchIslandInsertionPoints[regionIndex]) ) { + assert(branchIslandInsertionSections[regionIndex] == sect && "wrong section seen?"); + std::vector* islands = regionsIslands[regionIndex]; + if ( _s_log ) fprintf(stderr, "ld: inserted %d islands after %s (0x%08llx)\n", + islands->size(), atom->name(), sAtomToAddress[atom]+atom->size()); + newAtomList.insert(newAtomList.end(), islands->begin(), islands->end()); + ++regionIndex; + } + } + // swap in new list of atoms for __text section + if (!newAtomList.empty()) { + sect->atoms.clear(); + sect->atoms = newAtomList; + } + } } From cac44c79c315c6ee65ff3085fd657f6c07f3374a Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 22 Dec 2016 01:53:57 +0000 Subject: [PATCH 42/48] Fix build for casting ints to pint_t in make_pair. --- ld64/src/other/machochecker.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index 8f4b678..ba3a00d 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -531,7 +531,8 @@ void MachOChecker::checkLoadCommands() else { throw "overlapping segment vm addresses"; } - segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); + //segmentAddressRanges.push_back(std::make_pair(startAddr, endAddr)); + segmentAddressRanges.push_back(std::make_pair((pint_t)startAddr, (pint_t)endAddr)); } // see if this overlaps another segment file offset range uint64_t startOffset = segCmd->fileoff(); @@ -548,7 +549,8 @@ void MachOChecker::checkLoadCommands() else { throw "overlapping segment file data"; } - segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); + //segmentFileOffsetRanges.push_back(std::make_pair(startOffset, endOffset)); + segmentFileOffsetRanges.push_back(std::make_pair((pint_t)startOffset, (pint_t)endOffset)); // check is within file bounds if ( (startOffset > fLength) || (endOffset > fLength) ) throw "segment file data is past end of file"; From 7d10d416f3ac6881bba16f41d3354f4cf95ffe7f Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 22 Dec 2016 00:48:11 +0000 Subject: [PATCH 43/48] PPC Additions for 264.3.102. --- ld64/src/other/machochecker.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ld64/src/other/machochecker.cpp b/ld64/src/other/machochecker.cpp index ba3a00d..fa12071 100644 --- a/ld64/src/other/machochecker.cpp +++ b/ld64/src/other/machochecker.cpp @@ -1113,6 +1113,23 @@ void MachOChecker::checkInitTerms() } +template <> +ppc::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} + +template <> +ppc64::P::uint_t MachOChecker::relocBase() +{ + if ( fHeader->flags() & MH_SPLIT_SEGS ) + return fFirstWritableSegment->vmaddr(); + else + return fFirstSegment->vmaddr(); +} template <> x86::P::uint_t MachOChecker::relocBase() From 3794f5739d9a22b69e3f5a025b03023489361d0b Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 28 Sep 2017 15:23:22 +0100 Subject: [PATCH 44/48] [ObjectDump] Comment out a fixup type that doesn't exist in the sources. --- ld64/src/other/ObjectDump.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index b2b9759..01d02f8 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -874,9 +874,9 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreARM64PCRelToGOT: printf(", then store as 32-bit delta to GOT entry"); break; - case ld::Fixup::kindStoreARM64PointerToGOT32: - printf(", then store as 32-bit pointer to GOT entry"); - break; +// case ld::Fixup::kindStoreARM64PointerToGOT32: +// printf(", then store as 32-bit pointer to GOT entry"); +// break; case ld::Fixup::kindDtraceExtra: printf("dtrace static probe extra info"); break; From d591198d710ba4e6e331c963c9b5d06d210266bb Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Mon, 3 Sep 2018 13:27:43 +0100 Subject: [PATCH 45/48] [ld64] Add -compact_unwind to allow it to be forced on. --- ld64/src/ld/Options.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 1c1fda3..a32e3a4 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -3421,6 +3421,10 @@ void Options::parse(int argc, const char* argv[]) fEncryptableForceOn = true; cannotBeUsedWithBitcode(arg); } + else if ( strcmp(arg, "-compact_unwind") == 0 ) { + fAddCompactUnwindEncoding = true; + cannotBeUsedWithBitcode(arg); + } else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { fAddCompactUnwindEncoding = false; cannotBeUsedWithBitcode(arg); From 6249b56a9bd1f50db9e57e8528abdee6943e363b Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sun, 24 Mar 2019 13:00:54 +0000 Subject: [PATCH 46/48] [ld64] Fix dsymutil warning about not finding symbol file symbol objects. When the same source file is used to generate multiple objects (with conditional compilation), currently ld64 is only emitting an OSO for the first one (since it's assuming 1:1 mapping of source:object). This patch causes it to emit a new OSO for each object encountered with the same source file. --- ld64/src/ld/OutputFile.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 8c7e8a9..f9e5cf0 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -5580,13 +5580,13 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) // synthesize "debug notes" and add them to master stabs vector const char* dirPath = NULL; const char* filename = NULL; + const ld::relocatable::File* atomObjFile = NULL; bool wroteStartSO = false; state.stabs.reserve(atomsNeedingDebugNotes.size()*4); std::unordered_set seenFiles; for (std::vector::iterator it=atomsNeedingDebugNotes.begin(); it != atomsNeedingDebugNotes.end(); it++) { const ld::Atom* atom = *it; const ld::File* atomFile = atom->file(); - const ld::relocatable::File* atomObjFile = dynamic_cast(atomFile); //fprintf(stderr, "debug note for %s\n", atom->name()); const char* newPath = atom->translationUnitSource(); if ( newPath != NULL ) { @@ -5600,8 +5600,15 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) newDirPath = temp; // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' temp[lastSlash-newPath+1] = '\0'; - // need SO's whenever the translation unit source file changes - if ( (filename == NULL) || (strcmp(newFilename,filename) != 0) || (strcmp(newDirPath,dirPath) != 0)) { + // We need SO's whenever the translation unit source file changes + // We also need a new OSO every time the object file changes (which can be + // more often than the source file change). In particular, when + // multiple entries in a convenience lib are built from the same + // source file (using some conditional compilation). + const ld::relocatable::File* newAtomObjFile = dynamic_cast(atomFile); + if ( (filename == NULL) || (strcmp(newFilename,filename) != 0) + || (strcmp(newDirPath,dirPath) != 0) + || newAtomObjFile && newAtomObjFile != atomObjFile ) { if ( filename != NULL ) { // translation unit change, emit ending SO ld::relocatable::File::Stab endFileStab; @@ -5630,7 +5637,8 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) fileStab.value = 0; fileStab.string = newFilename; state.stabs.push_back(fileStab); - // Synthesize OSO for start of file + wroteStartSO = true; + // Synthesize for this new object. ld::relocatable::File::Stab objStab; objStab.atom = NULL; objStab.type = N_OSO; @@ -5638,15 +5646,14 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) objStab.other = atomFile->cpuSubType(); objStab.desc = 1; if ( atomObjFile != NULL ) { - objStab.string = assureFullPath(atomObjFile->debugInfoPath()); - objStab.value = atomObjFile->debugInfoModificationTime(); + objStab.string = assureFullPath(newAtomObjFile->debugInfoPath()); + objStab.value = newAtomObjFile->debugInfoModificationTime(); } else { objStab.string = assureFullPath(atomFile->path()); objStab.value = atomFile->modificationTime(); } state.stabs.push_back(objStab); - wroteStartSO = true; // add the source file path to seenFiles so it does not show up in SOLs seenFiles.insert(newFilename); char* fullFilePath; @@ -5656,6 +5663,7 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) } filename = newFilename; dirPath = newDirPath; + atomObjFile = newAtomObjFile; if ( atom->section().type() == ld::Section::typeCode ) { // Synthesize BNSYM and start FUN stabs ld::relocatable::File::Stab beginSym; From e67dc495c121192b3ff5f2b41a23ab8a063660a3 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Wed, 3 Jul 2019 12:55:41 +0100 Subject: [PATCH 47/48] [ld64] Just some FIXMEs. --- ld64/src/ld/Options.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index a32e3a4..0ab643d 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -5648,7 +5648,7 @@ void Options::checkIllegalOptionCombinations() fZeroPageSize = 0x1000; break; case CPU_TYPE_POWERPC64: - // first 4GB for ppc64 on 10.5 + // first 4GB for ppc64 on 10.5 FIXME: Check about mdynamic-no-pic if ( fMacVersionMin >= ld::mac10_5 ) fZeroPageSize = 0x100000000ULL; else @@ -5813,7 +5813,7 @@ void Options::checkIllegalOptionCombinations() void Options::checkForClassic(int argc, const char* argv[]) { - // scan options + // scan options FIXME: we scan them but then ignore the result? bool archFound = false; bool staticFound = false; bool dtraceFound = false; From b852e0d4347b76b54f1b8059895a588bb7a2110e Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sat, 6 Apr 2019 11:36:19 +0100 Subject: [PATCH 48/48] [ld64] Hack around the 'zippered' change. --- ld64/src/ld/Options.h | 6 ++++-- ld64/src/ld/parsers/macho_dylib_file.cpp | 9 +++++++++ ld64/src/ld/parsers/textstub_dylib_file.cpp | 7 +++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 6a4b3de..3041837 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -89,9 +89,9 @@ class Options enum BitcodeMode { kBitcodeProcess, kBitcodeAsData, kBitcodeMarker, kBitcodeStrip }; enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; #if SUPPORT_APPLE_TV - enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS, kPlatform_tvOS }; + enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS, kPlatform_tvOS, kPlatformZippered }; #else - enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS }; + enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS, kPlatformZippered }; #endif static Platform platformForLoadCommand(uint32_t lc) { @@ -123,6 +123,8 @@ class Options case kPlatform_tvOS: return "tvOS"; #endif + case kPlatformZippered: + return "zippered"; case kPlatformUnknown: default: return "(unknown)"; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index f8fcb13..6a486ea 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -261,6 +261,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, (std::is_same::value || std::is_same::value)) lcPlatform = Options::kPlatformiOS; + if (lcPlatform == Options::kPlatformUnknown && + (std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value)) + lcPlatform = Options::kPlatformOSX; + // check cross-linking if ( lcPlatform != platform ) { this->_wrongOS = true; @@ -309,6 +314,10 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, switch (platform) { case Options::kPlatformOSX: case Options::kPlatformiOS: + case Options::kPlatformZippered: + // it's an OK combination + if ( lcPlatform == Options::kPlatformZippered ) + break; if ( lcPlatform == Options::kPlatformUnknown ) break; // fall through if the Platform is not Unknown diff --git a/ld64/src/ld/parsers/textstub_dylib_file.cpp b/ld64/src/ld/parsers/textstub_dylib_file.cpp index ca3ca24..63858ba 100644 --- a/ld64/src/ld/parsers/textstub_dylib_file.cpp +++ b/ld64/src/ld/parsers/textstub_dylib_file.cpp @@ -88,6 +88,8 @@ static Options::Platform mapPlatform(tapi::Platform platform) { return Options::kPlatformUnknown; case tapi::Platform::OSX: return Options::kPlatformOSX; + case tapi::Platform::zippered: + return Options::kPlatformZippered; case tapi::Platform::iOS: return Options::kPlatformiOS; case tapi::Platform::watchOS: @@ -165,7 +167,8 @@ template this->_allowableClients.emplace_back(strdup(client.c_str())); auto dylibPlatform = mapPlatform(file->getPlatform()); - if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) { + if ( (dylibPlatform != platform) && (dylibPlatform != Options::kPlatformZippered) + && (platform != Options::kPlatformUnknown) ) { this->_wrongOS = true; if ( this->_addVersionLoadCommand && !indirectDylib ) { if ( buildingForSimulator ) { @@ -173,7 +176,7 @@ template throwf("building for %s simulator, but linking against dylib built for %s (%s).", Options::platformName(platform), Options::platformName(dylibPlatform), path); } else { - throwf("building for %s, but linking against dylib built for %s (%s).", + throwf("building for %s, but linking against dylib made for %s (%s).", Options::platformName(platform), Options::platformName(dylibPlatform), path); } }